mirror of
https://github.com/186526/handlers.js
synced 2024-10-13 00:29:43 +00:00
format
This commit is contained in:
@ -1,42 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "Handlers.js Devlopment Container",
|
"name": "Handlers.js Devlopment Container",
|
||||||
"build": {
|
"build": {
|
||||||
"dockerfile": "Dockerfile",
|
"dockerfile": "Dockerfile",
|
||||||
"args": {
|
"args": {
|
||||||
"VARIANT": "16-bullseye"
|
"VARIANT": "16-bullseye"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Configure tool-specific properties.
|
// Configure tool-specific properties.
|
||||||
"customizations": {
|
"customizations": {
|
||||||
// Configure properties specific to VS Code.
|
// Configure properties specific to VS Code.
|
||||||
"vscode": {
|
"vscode": {
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"ms-vscode.vscode-typescript-next",
|
"ms-vscode.vscode-typescript-next",
|
||||||
"oderwat.indent-rainbow",
|
"oderwat.indent-rainbow",
|
||||||
"GitHub.copilot",
|
"GitHub.copilot",
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"ms-azuretools.vscode-docker"
|
"ms-azuretools.vscode-docker"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
// "forwardPorts": [],
|
// "forwardPorts": [],
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": "yarn install",
|
"postCreateCommand": "yarn install",
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
"remoteUser": "node",
|
"remoteUser": "node",
|
||||||
"features": {
|
"features": {
|
||||||
"git": "latest",
|
"git": "latest",
|
||||||
"docker-in-docker": {
|
"docker-in-docker": {
|
||||||
"version": "latest",
|
"version": "latest",
|
||||||
"moby": true,
|
"moby": true,
|
||||||
"dockerDashComposeVersion": "v1"
|
"dockerDashComposeVersion": "v1"
|
||||||
},
|
},
|
||||||
"rust": {
|
"rust": {
|
||||||
"version": "latest",
|
"version": "latest",
|
||||||
"profile": "minimal"
|
"profile": "minimal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 4
|
||||||
|
}
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compile-hero.disable-compile-files-on-did-save-code": true,
|
"compile-hero.disable-compile-files-on-did-save-code": true,
|
||||||
"deno.enable": false,
|
"deno.enable": false,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
@ -3,13 +3,13 @@
|
|||||||
> Handlers.js is a unified and lightweight web application framework for multiple platforms.
|
> Handlers.js is a unified and lightweight web application framework for multiple platforms.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import handlerJS from "handlers.js";
|
import handlerJS from 'handlers.js';
|
||||||
|
|
||||||
const App = new handlerJS();
|
const App = new handlerJS();
|
||||||
|
|
||||||
App.binding(
|
App.binding(
|
||||||
"/",
|
'/',
|
||||||
App.create("ANY", async () => "Hello World!")
|
App.create('ANY', async () => 'Hello World!'),
|
||||||
);
|
);
|
||||||
|
|
||||||
App.useMappingAdapter();
|
App.useMappingAdapter();
|
||||||
|
@ -1,141 +1,141 @@
|
|||||||
// From https://gist.github.com/186526/82b7372619fb4b029568040ee4a4aa44, Open source with MIT license
|
// From https://gist.github.com/186526/82b7372619fb4b029568040ee4a4aa44, Open source with MIT license
|
||||||
|
|
||||||
import { response, request } from "../index";
|
import { response, request } from '../index';
|
||||||
import { defaultHeaders } from "../src/interface/response";
|
import { defaultHeaders } from '../src/interface/response';
|
||||||
|
|
||||||
function uuid(): string {
|
function uuid(): string {
|
||||||
const s: any[] = [];
|
const s: any[] = [];
|
||||||
const hexDigits = "0123456789abcdef";
|
const hexDigits = '0123456789abcdef';
|
||||||
|
|
||||||
for (let i = 0; i < 36; i++) {
|
for (let i = 0; i < 36; i++) {
|
||||||
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
|
s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
|
||||||
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||||||
s[8] = s[13] = s[18] = s[23] = "-";
|
s[8] = s[13] = s[18] = s[23] = '-';
|
||||||
|
|
||||||
const uuid = s.join("");
|
const uuid = s.join('');
|
||||||
|
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
const codeAlternative:{[index: number]:string} = {
|
const codeAlternative: { [index: number]: string } = {
|
||||||
100: "Continue",
|
100: 'Continue',
|
||||||
101: "Switching Protocols",
|
101: 'Switching Protocols',
|
||||||
102: "Processing",
|
102: 'Processing',
|
||||||
103: "Early Hints",
|
103: 'Early Hints',
|
||||||
200: "OK",
|
200: 'OK',
|
||||||
201: "Created",
|
201: 'Created',
|
||||||
202: "Accepted",
|
202: 'Accepted',
|
||||||
203: "Non-Authoritative Information",
|
203: 'Non-Authoritative Information',
|
||||||
204: "No Content",
|
204: 'No Content',
|
||||||
205: "Reset Content",
|
205: 'Reset Content',
|
||||||
206: "Partial Content",
|
206: 'Partial Content',
|
||||||
207: "Multi Status",
|
207: 'Multi Status',
|
||||||
208: "Already Reported",
|
208: 'Already Reported',
|
||||||
226: "IM Used",
|
226: 'IM Used',
|
||||||
300: "Multiple Choices",
|
300: 'Multiple Choices',
|
||||||
301: "Moved Permanently",
|
301: 'Moved Permanently',
|
||||||
302: "Found",
|
302: 'Found',
|
||||||
303: "See Other",
|
303: 'See Other',
|
||||||
304: "Not Modified",
|
304: 'Not Modified',
|
||||||
305: "Use Proxy",
|
305: 'Use Proxy',
|
||||||
306: "Switch Proxy",
|
306: 'Switch Proxy',
|
||||||
307: "Temporary Redirect",
|
307: 'Temporary Redirect',
|
||||||
308: "Permanent Redirect",
|
308: 'Permanent Redirect',
|
||||||
400: "Bad Request",
|
400: 'Bad Request',
|
||||||
401: "Unauthorized",
|
401: 'Unauthorized',
|
||||||
402: "Payment Required",
|
402: 'Payment Required',
|
||||||
403: "Forbidden",
|
403: 'Forbidden',
|
||||||
404: "Not Found",
|
404: 'Not Found',
|
||||||
405: "Method Not Allowed",
|
405: 'Method Not Allowed',
|
||||||
406: "Not Acceptable",
|
406: 'Not Acceptable',
|
||||||
407: "Proxy Authentication Required",
|
407: 'Proxy Authentication Required',
|
||||||
408: "Request Time-out",
|
408: 'Request Time-out',
|
||||||
409: "Conflict",
|
409: 'Conflict',
|
||||||
410: "Gone",
|
410: 'Gone',
|
||||||
411: "Length Required",
|
411: 'Length Required',
|
||||||
412: "Precondition Failed",
|
412: 'Precondition Failed',
|
||||||
413: "Request Entity Too Large",
|
413: 'Request Entity Too Large',
|
||||||
414: "Request-URI Too Large",
|
414: 'Request-URI Too Large',
|
||||||
415: "Unsupported Media Type",
|
415: 'Unsupported Media Type',
|
||||||
416: "Requested Range not Satisfiable",
|
416: 'Requested Range not Satisfiable',
|
||||||
417: "Expectation Failed",
|
417: 'Expectation Failed',
|
||||||
418: "I'm a teapot",
|
418: "I'm a teapot",
|
||||||
421: "Misdirected Request",
|
421: 'Misdirected Request',
|
||||||
422: "Unprocessable Entity",
|
422: 'Unprocessable Entity',
|
||||||
423: "Locked",
|
423: 'Locked',
|
||||||
424: "Failed Dependency",
|
424: 'Failed Dependency',
|
||||||
426: "Upgrade Required",
|
426: 'Upgrade Required',
|
||||||
428: "Precondition Required", // RFC 6585
|
428: 'Precondition Required', // RFC 6585
|
||||||
429: "Too Many Requests",
|
429: 'Too Many Requests',
|
||||||
431: "Request Header Fields Too Large", // RFC 6585
|
431: 'Request Header Fields Too Large', // RFC 6585
|
||||||
451: "Unavailable For Legal Reasons",
|
451: 'Unavailable For Legal Reasons',
|
||||||
500: "Internal Server Error",
|
500: 'Internal Server Error',
|
||||||
501: "Not Implemented",
|
501: 'Not Implemented',
|
||||||
502: "Bad Gateway",
|
502: 'Bad Gateway',
|
||||||
503: "Service Unavailable",
|
503: 'Service Unavailable',
|
||||||
504: "Gateway Time-out",
|
504: 'Gateway Time-out',
|
||||||
505: "HTTP Version not Supported",
|
505: 'HTTP Version not Supported',
|
||||||
506: "Variant Also Negotiates",
|
506: 'Variant Also Negotiates',
|
||||||
507: "Insufficient Storage",
|
507: 'Insufficient Storage',
|
||||||
508: "Loop Detected",
|
508: 'Loop Detected',
|
||||||
510: "Not Extended",
|
510: 'Not Extended',
|
||||||
511: "Network Authentication Required",
|
511: 'Network Authentication Required',
|
||||||
};
|
};
|
||||||
|
|
||||||
const template: string =
|
const template: string =
|
||||||
'<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /><meta name="renderer" content="webkit" /><meta name="force-rendering" content="webkit" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><link rel="preconnect" href="https://fonts.gstatic.com"><link href="https://fonts.googleapis.com/css2?family=Google+Sans&family=Fira+Mono&family=Ubuntu&display=swap" rel="stylesheet"><title>${statusCode} | ${codeAlternative}</title><link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"><style type="text/css">:root{ --background-color: #fff; --font-color: #000; --font-color-lighter: rgb(87, 89, 88); --font-size-main: 3.545rem; --font-size-description: 1.245rem; --box-color: #F2F2F2; --working-color: #137333; --working-color-background: #e6f4ea; --error-color-background: #fce8e6; --error-color: #c5221f; --working-with-error-color: #b05a00; --working-with-error-color-background: #fef7e0; --icon-size: 48px;} body{ margin: 2rem 2rem; font-family: Google Sans, Ubuntu, Roboto, Noto Sans SC, sans-serif; color: var(--font-color); background-color: var(--background-color);} nav{ margin-left: 1rem;} nav description{ font-family: Ubuntu, Roboto, Noto Sans SC, sans-serif; font-size: var(--font-size-description); line-height: var(--fonr-size-description); color: var(--font-color-lighter);} nav main{ font-size: var(--font-size-main); line-height: var(--font-size-main); font-family: Fira Mono, Ubuntu, monospace;} code{ font-family: Fira Mono, monospace;} status{ margin-top: 2.5rem; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center;} status block.status-block{ background-color: var(--box-color); padding: 2rem; margin: 1rem 1rem; min-height: 3rem; border-radius: 9px; flex-grow: 1;} status block.status-block.working-block{ background-color: var(--working-color-background);} status block.status-block.error-block{ background-color: var(--error-color-background);} status block.status-block.working-with-error-block{ background-color: var(--working-with-error-color-background);} .status-block main{ font-size: calc(var(--font-size-description) + 0.1rem);} .status-working{ color: var(--working-color);} .status-error{ color: var(--error-color);} .status-working-with-error{ color: var(--working-with-error-color);} icon{ font-size: var(--icon-size) !important;} a{ text-decoration: none; color: #1967d2;} reason{ display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: center;} reason>*{ display: block; margin: 1rem; flex-grow: 1; max-width: 40%;} reason main{ font-size: calc(var(--font-size-description) + 0.2rem); font-weight: 550;} footer{ margin: 1rem; color: var(--font-color-lighter); font-size: calc(var(--font-size-description) - 0.4rem);} footer>request-status{ font-size: calc(var(--font-size-description) - 0.6rem);} footer>*{ display: block;} @media screen and (max-width:480px){ body{ margin: 6rem 2rem;} :root{ --font-size-main: 3.0rem; --font-size-description: 1.045rem;} reason>*{ max-width: 100%;} footer{ font-size: calc(var(--font-size-description) - 0.2rem);} footer>request-status{ font-size: calc(var(--font-size-description) - 0.4rem);}} @media screen and (min-width: 768px){ body{ margin: 8% 10%;} nav *{ display: inline-block; margin-left: 1%;}} @media (prefers-color-scheme: dark){ :root{ --font-color: rgba(255, 255, 255, 0.86); --font-color-lighter: rgba(255, 255, 255, 0.4); --background-color: rgb(0, 0, 0); --box-color: rgb(40 40 40 / 73%); --working-color-background: #07220f; --error-color-background: #270501; --working-with-error-color-background: #392605;}} </style></head><body><nav><main>${statusCode}</main><description>${codeAlternative}</description></nav><status><block class="status-block ${clientStatusShort}-block" id="client-status-block"><icon class="material-icons-outlined status-${clientStatusShort}">web</icon><main>Your Client</main><status-text class="status-${clientStatusShort}">${clientStatus}</status-text></block><block class="status-block working-block" id="edge-status-block"><icon class="material-icons-outlined status-working">alt_route</icon><main>Handlers.js</main><status-text class="status-working">Working </block><block class="status-block ${ServerStatusLowerCase}-block" id="website-status-block"><icon class="material-icons-outlined status-${ServerStatusLowerCase}">widgets</icon><main>Route</main><status-text class="status-${ServerStatusLowerCase}">${ServerStatus}</status-text></block></status><reason><explain><main>${codeAlternative}</main><p>${explain} </p></explain><howto><main>What can I do?</main><p>${howto} </p></howto></reason><footer><provider>Running with <a href="https://git.186526.xyz/186526/Handlers.js">Handlers.js</a>.</provider><br><request-status>Your IP is <code>${ip}</code><br>Request ID is <code>${id}</code></request-status></footer></body></html>';
|
'<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /><meta name="renderer" content="webkit" /><meta name="force-rendering" content="webkit" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><link rel="preconnect" href="https://fonts.gstatic.com"><link href="https://fonts.googleapis.com/css2?family=Google+Sans&family=Fira+Mono&family=Ubuntu&display=swap" rel="stylesheet"><title>${statusCode} | ${codeAlternative}</title><link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"><style type="text/css">:root{ --background-color: #fff; --font-color: #000; --font-color-lighter: rgb(87, 89, 88); --font-size-main: 3.545rem; --font-size-description: 1.245rem; --box-color: #F2F2F2; --working-color: #137333; --working-color-background: #e6f4ea; --error-color-background: #fce8e6; --error-color: #c5221f; --working-with-error-color: #b05a00; --working-with-error-color-background: #fef7e0; --icon-size: 48px;} body{ margin: 2rem 2rem; font-family: Google Sans, Ubuntu, Roboto, Noto Sans SC, sans-serif; color: var(--font-color); background-color: var(--background-color);} nav{ margin-left: 1rem;} nav description{ font-family: Ubuntu, Roboto, Noto Sans SC, sans-serif; font-size: var(--font-size-description); line-height: var(--fonr-size-description); color: var(--font-color-lighter);} nav main{ font-size: var(--font-size-main); line-height: var(--font-size-main); font-family: Fira Mono, Ubuntu, monospace;} code{ font-family: Fira Mono, monospace;} status{ margin-top: 2.5rem; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center;} status block.status-block{ background-color: var(--box-color); padding: 2rem; margin: 1rem 1rem; min-height: 3rem; border-radius: 9px; flex-grow: 1;} status block.status-block.working-block{ background-color: var(--working-color-background);} status block.status-block.error-block{ background-color: var(--error-color-background);} status block.status-block.working-with-error-block{ background-color: var(--working-with-error-color-background);} .status-block main{ font-size: calc(var(--font-size-description) + 0.1rem);} .status-working{ color: var(--working-color);} .status-error{ color: var(--error-color);} .status-working-with-error{ color: var(--working-with-error-color);} icon{ font-size: var(--icon-size) !important;} a{ text-decoration: none; color: #1967d2;} reason{ display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: center;} reason>*{ display: block; margin: 1rem; flex-grow: 1; max-width: 40%;} reason main{ font-size: calc(var(--font-size-description) + 0.2rem); font-weight: 550;} footer{ margin: 1rem; color: var(--font-color-lighter); font-size: calc(var(--font-size-description) - 0.4rem);} footer>request-status{ font-size: calc(var(--font-size-description) - 0.6rem);} footer>*{ display: block;} @media screen and (max-width:480px){ body{ margin: 6rem 2rem;} :root{ --font-size-main: 3.0rem; --font-size-description: 1.045rem;} reason>*{ max-width: 100%;} footer{ font-size: calc(var(--font-size-description) - 0.2rem);} footer>request-status{ font-size: calc(var(--font-size-description) - 0.4rem);}} @media screen and (min-width: 768px){ body{ margin: 8% 10%;} nav *{ display: inline-block; margin-left: 1%;}} @media (prefers-color-scheme: dark){ :root{ --font-color: rgba(255, 255, 255, 0.86); --font-color-lighter: rgba(255, 255, 255, 0.4); --background-color: rgb(0, 0, 0); --box-color: rgb(40 40 40 / 73%); --working-color-background: #07220f; --error-color-background: #270501; --working-with-error-color-background: #392605;}} </style></head><body><nav><main>${statusCode}</main><description>${codeAlternative}</description></nav><status><block class="status-block ${clientStatusShort}-block" id="client-status-block"><icon class="material-icons-outlined status-${clientStatusShort}">web</icon><main>Your Client</main><status-text class="status-${clientStatusShort}">${clientStatus}</status-text></block><block class="status-block working-block" id="edge-status-block"><icon class="material-icons-outlined status-working">alt_route</icon><main>Handlers.js</main><status-text class="status-working">Working </block><block class="status-block ${ServerStatusLowerCase}-block" id="website-status-block"><icon class="material-icons-outlined status-${ServerStatusLowerCase}">widgets</icon><main>Route</main><status-text class="status-${ServerStatusLowerCase}">${ServerStatus}</status-text></block></status><reason><explain><main>${codeAlternative}</main><p>${explain} </p></explain><howto><main>What can I do?</main><p>${howto} </p></howto></reason><footer><provider>Running with <a href="https://git.186526.xyz/186526/Handlers.js">Handlers.js</a>.</provider><br><request-status>Your IP is <code>${ip}</code><br>Request ID is <code>${id}</code></request-status></footer></body></html>';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
errorCode: number,
|
errorCode: number,
|
||||||
errorMessage: string = `The route you are trying access is error.`
|
errorMessage: string = `The route you are trying access is error.`,
|
||||||
) =>
|
) =>
|
||||||
async (request: request<any>) => {
|
async (request: request<any>) => {
|
||||||
return new response(
|
return new response(
|
||||||
template
|
template
|
||||||
.replaceAll("${statusCode}", errorCode.toString())
|
.replaceAll('${statusCode}', errorCode.toString())
|
||||||
.replaceAll("${codeAlternative}", codeAlternative[errorCode])
|
.replaceAll('${codeAlternative}', codeAlternative[errorCode])
|
||||||
.replaceAll("${explain}", errorMessage)
|
.replaceAll('${explain}', errorMessage)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"${howto}",
|
'${howto}',
|
||||||
`You can go to the <a href="/">homepage</a> of the website.`
|
`You can go to the <a href="/">homepage</a> of the website.`,
|
||||||
)
|
)
|
||||||
.replaceAll("${id}", uuid())
|
.replaceAll('${id}', uuid())
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"${ServerStatus}",
|
'${ServerStatus}',
|
||||||
errorCode >= 500
|
errorCode >= 500
|
||||||
? "Error"
|
? 'Error'
|
||||||
: errorCode <= 500 && errorCode >= 400
|
: errorCode <= 500 && errorCode >= 400
|
||||||
? `Working but ${codeAlternative[errorCode]}`
|
? `Working but ${codeAlternative[errorCode]}`
|
||||||
: "Working"
|
: 'Working',
|
||||||
)
|
)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"${ServerStatusLowerCase}",
|
'${ServerStatusLowerCase}',
|
||||||
errorCode >= 500
|
errorCode >= 500
|
||||||
? "error"
|
? 'error'
|
||||||
: errorCode <= 500 && errorCode >= 400
|
: errorCode <= 500 && errorCode >= 400
|
||||||
? `working-with-error`
|
? `working-with-error`
|
||||||
: "working"
|
: 'working',
|
||||||
)
|
)
|
||||||
.replaceAll("${ip}", request.ip)
|
.replaceAll('${ip}', request.ip)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"${clientStatus}",
|
'${clientStatus}',
|
||||||
errorCode <= 500 && errorCode >= 400
|
errorCode <= 500 && errorCode >= 400
|
||||||
? `Working but receive ${codeAlternative[errorCode]}`
|
? `Working but receive ${codeAlternative[errorCode]}`
|
||||||
: "Working"
|
: 'Working',
|
||||||
)
|
)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"${clientStatusShort}",
|
'${clientStatusShort}',
|
||||||
errorCode <= 500 && errorCode >= 400
|
errorCode <= 500 && errorCode >= 400
|
||||||
? `working-with-error`
|
? `working-with-error`
|
||||||
: "working"
|
: 'working',
|
||||||
),
|
),
|
||||||
errorCode,
|
errorCode,
|
||||||
(() => {
|
(() => {
|
||||||
const Headers = new defaultHeaders();
|
const Headers = new defaultHeaders();
|
||||||
Headers.set("Content-Type", "text/html; charset=utf-8");
|
Headers.set('Content-Type', 'text/html; charset=utf-8');
|
||||||
return Headers;
|
return Headers;
|
||||||
})()
|
})(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
118
demo/index.ts
118
demo/index.ts
@ -1,75 +1,69 @@
|
|||||||
import {
|
import { rootRouter, method, handler, route, response } from '../index';
|
||||||
rootRouter,
|
import errorHandler from './errorHandler';
|
||||||
method,
|
|
||||||
handler,
|
|
||||||
route,
|
|
||||||
response,
|
|
||||||
} from "../index";
|
|
||||||
import errorHandler from "./errorHandler";
|
|
||||||
|
|
||||||
const App = new rootRouter<any, any>();
|
const App = new rootRouter<any, any>();
|
||||||
|
|
||||||
App.binding(
|
App.binding(
|
||||||
"/(.*)",
|
'/(.*)',
|
||||||
new handler("ANY", [
|
new handler('ANY', [
|
||||||
async (request, response) => {
|
async (request, response) => {
|
||||||
console.log(request);
|
console.log(request);
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
App.binding(
|
App.binding(
|
||||||
"/",
|
'/',
|
||||||
App.create(
|
App.create(
|
||||||
"ANY",
|
'ANY',
|
||||||
(): Promise<string> =>
|
(): Promise<string> =>
|
||||||
new Promise(() => {
|
new Promise(() => {
|
||||||
console.log("Hello World!");
|
console.log('Hello World!');
|
||||||
throw new response("Hello World!");
|
throw new response('Hello World!');
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).binding(
|
).binding(
|
||||||
"/(.*)",
|
'/(.*)',
|
||||||
App.create(
|
App.create(
|
||||||
"ANY",
|
'ANY',
|
||||||
(): Promise<string> =>
|
(): Promise<string> =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
resolve("Hello World?")
|
resolve('Hello World?');
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
App.route("/v1/(.*)")
|
App.route('/v1/(.*)')
|
||||||
.add(
|
.add(
|
||||||
new route(
|
new route(
|
||||||
["/echo", "/echo/(.*)"],
|
['/echo', '/echo/(.*)'],
|
||||||
[
|
[
|
||||||
new handler(method["GET"], [
|
new handler(method['GET'], [
|
||||||
async (requestMessage, responseMessage) => {
|
async (requestMessage, responseMessage) => {
|
||||||
responseMessage = responseMessage ?? new response("");
|
responseMessage = responseMessage ?? new response('');
|
||||||
responseMessage?.headers.set("Hello", "World");
|
responseMessage?.headers.set('Hello', 'World');
|
||||||
responseMessage.body = requestMessage.url.pathname;
|
responseMessage.body = requestMessage.url.pathname;
|
||||||
return responseMessage;
|
return responseMessage;
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
new handler(method["POST"], [
|
new handler(method['POST'], [
|
||||||
async (requestMessage, responseMessage) => {
|
async (requestMessage, responseMessage) => {
|
||||||
responseMessage = responseMessage ?? new response("");
|
responseMessage = responseMessage ?? new response('');
|
||||||
responseMessage.body = requestMessage.body;
|
responseMessage.body = requestMessage.body;
|
||||||
return responseMessage;
|
return responseMessage;
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.binding(
|
.binding(
|
||||||
"/error",
|
'/error',
|
||||||
App.create(method["ANY"], async () => {
|
App.create(method['ANY'], async () => {
|
||||||
throw new Error("Nothing will happen here.");
|
throw new Error('Nothing will happen here.');
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.useErrorResponder(errorHandler);
|
.useErrorResponder(errorHandler);
|
||||||
|
|
||||||
App.useMappingAdapter();
|
App.useMappingAdapter();
|
||||||
App.listen(8080);
|
App.listen(8080);
|
||||||
|
16
index.ts
16
index.ts
@ -1,12 +1,12 @@
|
|||||||
import { rootRouter } from "./src/router";
|
import { rootRouter } from './src/router';
|
||||||
|
|
||||||
export { handler } from "./src/handler";
|
export { handler } from './src/handler';
|
||||||
export { route } from "./src/route";
|
export { route } from './src/route';
|
||||||
export { router } from "./src/router";
|
export { router } from './src/router';
|
||||||
export { methodENUM as method } from "./src/interface/method";
|
export { methodENUM as method } from './src/interface/method';
|
||||||
export { response } from "./src/interface/response";
|
export { response } from './src/interface/response';
|
||||||
export { request } from "./src/interface/request";
|
export { request } from './src/interface/request';
|
||||||
export * as platformAdapater from "./src/platform/export";
|
export * as platformAdapater from './src/platform/export';
|
||||||
|
|
||||||
export { rootRouter };
|
export { rootRouter };
|
||||||
export default rootRouter;
|
export default rootRouter;
|
||||||
|
134
package.json
134
package.json
@ -1,68 +1,70 @@
|
|||||||
{
|
{
|
||||||
"name": "handlers.js",
|
"name": "handlers.js",
|
||||||
"description": "Handlers.js is a unified and lightweight web application framework for multiple platforms.",
|
"description": "Handlers.js is a unified and lightweight web application framework for multiple platforms.",
|
||||||
"version": "0.1.1-2",
|
"version": "0.1.1-2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"webpack": "./dist/index.js",
|
"webpack": "./dist/index.js",
|
||||||
"browser": "./dist/main.serviceworker.js",
|
"browser": "./dist/main.serviceworker.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"author": "186526 <i@186526.xyz>",
|
"author": "186526 <i@186526.xyz>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/186526/handlers.js"
|
"url": "https://github.com/186526/handlers.js"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"web framework",
|
"web framework",
|
||||||
"lightweight",
|
"lightweight",
|
||||||
"cross-platform",
|
"cross-platform",
|
||||||
"unified"
|
"unified"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-to-regexp": "^6.2.1"
|
"path-to-regexp": "^6.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^3.13.0",
|
"@cloudflare/workers-types": "^3.13.0",
|
||||||
"@types/jest": "^28.1.4",
|
"@types/jest": "^28.1.4",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@webpack-cli/generators": "^2.5.0",
|
"@webpack-cli/generators": "^2.5.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"bun-types": "^0.1.4",
|
"bun-types": "^0.1.4",
|
||||||
"jest": "^28.1.2",
|
"jest": "^28.1.2",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"ts-jest": "^28.0.5",
|
"ts-jest": "^28.0.5",
|
||||||
"ts-loader": "^9.3.1",
|
"ts-loader": "^9.3.1",
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.8.1",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.10.0"
|
"webpack-cli": "^4.10.0"
|
||||||
},
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn clean && yarn build:node && yarn build:serviceworker && yarn build:cfworker && yarn build:deno && yarn build:txiki && yarn build:bun",
|
"build": "yarn clean && yarn build:node && yarn build:serviceworker && yarn build:cfworker && yarn build:deno && yarn build:txiki && yarn build:bun",
|
||||||
"build:node": "BUILD_TARGET=node webpack",
|
"build:node": "BUILD_TARGET=node webpack",
|
||||||
"build:serviceworker": "BUILD_TARGET=serviceworker webpack",
|
"build:serviceworker": "BUILD_TARGET=serviceworker webpack",
|
||||||
"build:cfworker": "BUILD_TARGET=cfworker webpack",
|
"build:cfworker": "BUILD_TARGET=cfworker webpack",
|
||||||
"build:deno": "BUILD_TARGET=deno webpack",
|
"build:deno": "BUILD_TARGET=deno webpack",
|
||||||
"build:txiki": "BUILD_TARGET=txiki webpack",
|
"build:txiki": "BUILD_TARGET=txiki webpack",
|
||||||
"build:bun": "BUILD_TARGET=bun webpack",
|
"build:bun": "BUILD_TARGET=bun webpack",
|
||||||
"watch": "webpack --watch",
|
"watch": "webpack --watch",
|
||||||
"clean": "rm -rf ./dist",
|
"clean": "rm -rf ./dist",
|
||||||
"demo": "env NODE_ENV=development yarn build:node && node ./dist/main.node.js",
|
"demo": "env NODE_ENV=development yarn build:node && node ./dist/main.node.js",
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"test:node": "jest ./test/node.test.ts",
|
"test:node": "jest ./test/node.test.ts",
|
||||||
"test:deno": "BUILD_TARGET=deno:test webpack && jest ./test/deno.test.ts",
|
"test:deno": "BUILD_TARGET=deno:test webpack && jest ./test/deno.test.ts",
|
||||||
"coverage": "jest --collectCoverage --",
|
"coverage": "jest --collectCoverage --",
|
||||||
"prepublish": "env NODE_ENV=production yarn build && yarn tsc"
|
"prepublish": "env NODE_ENV=production yarn build && yarn tsc",
|
||||||
},
|
"format": "prettier --write \"**/*.{ts,json,md}\" "
|
||||||
"engines": {
|
},
|
||||||
"node": ">=18.0.0"
|
"engines": {
|
||||||
},
|
"node": ">=18.0.0"
|
||||||
"type": "module"
|
},
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "yarn@1.22.22"
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,46 @@
|
|||||||
import { method, request, responder, response } from "./interface/index";
|
import { method, request, responder, response } from './interface/index';
|
||||||
|
|
||||||
export class handler<RequestCustomType, ResponseCustomType> {
|
export class handler<RequestCustomType, ResponseCustomType> {
|
||||||
public responders: responder<RequestCustomType, ResponseCustomType>[];
|
public responders: responder<RequestCustomType, ResponseCustomType>[];
|
||||||
public method: method;
|
public method: method;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
method: method,
|
method: method,
|
||||||
responders: responder<RequestCustomType, ResponseCustomType>[]
|
responders: responder<RequestCustomType, ResponseCustomType>[],
|
||||||
) {
|
) {
|
||||||
this.responders = responders;
|
this.responders = responders;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(responder: responder<RequestCustomType, ResponseCustomType>) {
|
add(responder: responder<RequestCustomType, ResponseCustomType>) {
|
||||||
this.responders.push(responder);
|
this.responders.push(responder);
|
||||||
}
|
}
|
||||||
|
|
||||||
async respond(
|
async respond(
|
||||||
request: request<RequestCustomType>,
|
request: request<RequestCustomType>,
|
||||||
responseMessage: response<ResponseCustomType> = new response<ResponseCustomType>(
|
responseMessage: response<ResponseCustomType> = new response<ResponseCustomType>(
|
||||||
""
|
'',
|
||||||
)
|
),
|
||||||
): Promise<response<ResponseCustomType> | void> {
|
): Promise<response<ResponseCustomType> | void> {
|
||||||
switch (this.responders.length) {
|
switch (this.responders.length) {
|
||||||
case 0:
|
case 0:
|
||||||
Promise.reject("No responders found in this handler.");
|
Promise.reject('No responders found in this handler.');
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
return this.responders[0](request, responseMessage);
|
return this.responders[0](request, responseMessage);
|
||||||
default:
|
default:
|
||||||
for (let responder of this.responders) {
|
for (let responder of this.responders) {
|
||||||
let thisResponse = await responder(request, responseMessage);
|
let thisResponse = await responder(
|
||||||
if (thisResponse instanceof response) {
|
request,
|
||||||
responseMessage = thisResponse;
|
responseMessage,
|
||||||
}
|
);
|
||||||
}
|
if (thisResponse instanceof response) {
|
||||||
return responseMessage;
|
responseMessage = thisResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return responseMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default handler;
|
export default handler;
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import * as lib from "../lib";
|
import * as lib from '../lib';
|
||||||
export class headers {
|
export class headers {
|
||||||
public headers: { [key: string]: string } = {};
|
public headers: { [key: string]: string } = {};
|
||||||
constructor(headers: { [key: string]: string }) {
|
constructor(headers: { [key: string]: string }) {
|
||||||
this.headers = {};
|
this.headers = {};
|
||||||
Object.keys(headers).forEach((key) => {
|
Object.keys(headers).forEach((key) => {
|
||||||
this.headers[lib.firstUpperCase(key)] = headers[key];
|
this.headers[lib.firstUpperCase(key)] = headers[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
delete(key: string) {
|
delete(key: string) {
|
||||||
delete this.headers[lib.firstUpperCase(key)];
|
delete this.headers[lib.firstUpperCase(key)];
|
||||||
}
|
}
|
||||||
get(key: string): string | undefined {
|
get(key: string): string | undefined {
|
||||||
return this.headers[lib.firstUpperCase(key)];
|
return this.headers[lib.firstUpperCase(key)];
|
||||||
}
|
}
|
||||||
has(key: string): boolean {
|
has(key: string): boolean {
|
||||||
return this.headers.hasOwnProperty(lib.firstUpperCase(key));
|
return this.headers.hasOwnProperty(lib.firstUpperCase(key));
|
||||||
}
|
}
|
||||||
set(key: string, value: string) {
|
set(key: string, value: string) {
|
||||||
this.headers[lib.firstUpperCase(key)] = value;
|
this.headers[lib.firstUpperCase(key)] = value;
|
||||||
}
|
}
|
||||||
toObject() {
|
toObject() {
|
||||||
return this.headers;
|
return this.headers;
|
||||||
}
|
}
|
||||||
forEach(func: (key: string, value: string) => any) {
|
forEach(func: (key: string, value: string) => any) {
|
||||||
Object.keys(this.headers).forEach((key) => {
|
Object.keys(this.headers).forEach((key) => {
|
||||||
func(key, this.headers[key]);
|
func(key, this.headers[key]);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default headers;
|
export default headers;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export { request } from "./request";
|
export { request } from './request';
|
||||||
export { response } from "./response";
|
export { response } from './response';
|
||||||
export { method } from "./method";
|
export { method } from './method';
|
||||||
export { headers } from "./headers";
|
export { headers } from './headers';
|
||||||
export { responder } from "./responder";
|
export { responder } from './responder';
|
||||||
export const AllMismatchInterrupted = new Error("AllMismatchInterrupted");
|
export const AllMismatchInterrupted = new Error('AllMismatchInterrupted');
|
||||||
export type path = string | RegExp;
|
export type path = string | RegExp;
|
||||||
|
@ -1,61 +1,72 @@
|
|||||||
export enum methodENUM {
|
export enum methodENUM {
|
||||||
/**
|
/**
|
||||||
* The `CONNECT` method establishes a tunnel to the server identified by the
|
* The `CONNECT` method establishes a tunnel to the server identified by the
|
||||||
* target resource.
|
* target resource.
|
||||||
*/
|
*/
|
||||||
CONNECT = "CONNECT",
|
CONNECT = 'CONNECT',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `DELETE` method deletes the specified resource.
|
* The `DELETE` method deletes the specified resource.
|
||||||
*/
|
*/
|
||||||
DELETE = "DELETE",
|
DELETE = 'DELETE',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `GET` method requests a representation of the specified resource.
|
* The `GET` method requests a representation of the specified resource.
|
||||||
* Requests using GET should only retrieve data.
|
* Requests using GET should only retrieve data.
|
||||||
*/
|
*/
|
||||||
GET = "GET",
|
GET = 'GET',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `HEAD` method asks for a response identical to that of a GET request,
|
* The `HEAD` method asks for a response identical to that of a GET request,
|
||||||
* but without the response body.
|
* but without the response body.
|
||||||
*/
|
*/
|
||||||
HEAD = "HEAD",
|
HEAD = 'HEAD',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `OPTIONS` method is used to describe the communication options for the
|
* The `OPTIONS` method is used to describe the communication options for the
|
||||||
* target resource.
|
* target resource.
|
||||||
*/
|
*/
|
||||||
OPTIONS = "OPTIONS",
|
OPTIONS = 'OPTIONS',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PATCH method is used to apply partial modifications to a resource.
|
* The PATCH method is used to apply partial modifications to a resource.
|
||||||
*/
|
*/
|
||||||
PATCH = "PATCH",
|
PATCH = 'PATCH',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `POST` method is used to submit an entity to the specified resource,
|
* The `POST` method is used to submit an entity to the specified resource,
|
||||||
* often causing a change in state or side effects on the server.
|
* often causing a change in state or side effects on the server.
|
||||||
*/
|
*/
|
||||||
POST = "POST",
|
POST = 'POST',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `PUT` method replaces all current representations of the target
|
* The `PUT` method replaces all current representations of the target
|
||||||
* resource with the request payload.
|
* resource with the request payload.
|
||||||
*/
|
*/
|
||||||
PUT = "PUT",
|
PUT = 'PUT',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `TRACE` method performs a message loop-back test along the path to the
|
* The `TRACE` method performs a message loop-back test along the path to the
|
||||||
* target resource.
|
* target resource.
|
||||||
*/
|
*/
|
||||||
TRACE = "TRACE",
|
TRACE = 'TRACE',
|
||||||
/**
|
/**
|
||||||
* The `ANY` method will match any method.
|
* The `ANY` method will match any method.
|
||||||
*/
|
*/
|
||||||
ANY = "ANY",
|
ANY = 'ANY',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type method = "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | "ANY" | methodENUM;
|
export type method =
|
||||||
|
| 'CONNECT'
|
||||||
|
| 'DELETE'
|
||||||
|
| 'GET'
|
||||||
|
| 'HEAD'
|
||||||
|
| 'OPTIONS'
|
||||||
|
| 'PATCH'
|
||||||
|
| 'POST'
|
||||||
|
| 'PUT'
|
||||||
|
| 'TRACE'
|
||||||
|
| 'ANY'
|
||||||
|
| methodENUM;
|
||||||
|
|
||||||
export default method;
|
export default method;
|
@ -1,34 +1,37 @@
|
|||||||
import method from "./method";
|
import method from './method';
|
||||||
import headers from "./headers";
|
import headers from './headers';
|
||||||
export class request<RequestCustomType> {
|
export class request<RequestCustomType> {
|
||||||
public readonly method: method;
|
public readonly method: method;
|
||||||
public readonly url: URL;
|
public readonly url: URL;
|
||||||
public originURL?: URL;
|
public originURL?: URL;
|
||||||
public readonly headers: headers;
|
public readonly headers: headers;
|
||||||
public readonly body: any;
|
public readonly body: any;
|
||||||
public readonly query: URLSearchParams;
|
public readonly query: URLSearchParams;
|
||||||
public params: { [key: string]: string | undefined };
|
public params: { [key: string]: string | undefined };
|
||||||
public custom: RequestCustomType;
|
public custom: RequestCustomType;
|
||||||
public ip: string;
|
public ip: string;
|
||||||
public constructor(
|
public constructor(
|
||||||
method: method,
|
method: method,
|
||||||
url: URL,
|
url: URL,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: any,
|
body: any,
|
||||||
params: { [key: string]: string },
|
params: { [key: string]: string },
|
||||||
ip: string = "0.0.0.0"
|
ip: string = '0.0.0.0',
|
||||||
) {
|
) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.query = new URLSearchParams(url.search);
|
this.query = new URLSearchParams(url.search);
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.ip = headers.get("X-REAL-IP") ?? headers.get("X-Forwarded-For")?.split(" ")[0] ?? ip;
|
this.ip =
|
||||||
}
|
headers.get('X-REAL-IP') ??
|
||||||
public extends(custom: RequestCustomType): request<RequestCustomType> {
|
headers.get('X-Forwarded-For')?.split(' ')[0] ??
|
||||||
this.custom = custom;
|
ip;
|
||||||
return this;
|
}
|
||||||
}
|
public extends(custom: RequestCustomType): request<RequestCustomType> {
|
||||||
|
this.custom = custom;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default request;
|
export default request;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { request, response } from "./index";
|
import { request, response } from './index';
|
||||||
export interface responder<RequestCustomType, ResponseCustomType> {
|
export interface responder<RequestCustomType, ResponseCustomType> {
|
||||||
(
|
(
|
||||||
request: request<RequestCustomType>,
|
request: request<RequestCustomType>,
|
||||||
reponse?: response<ResponseCustomType>
|
reponse?: response<ResponseCustomType>,
|
||||||
): Promise<response<ResponseCustomType>> | Promise<void> | void;
|
): Promise<response<ResponseCustomType>> | Promise<void> | void;
|
||||||
}
|
}
|
||||||
export default responder;
|
export default responder;
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
import headers from "./headers";
|
import headers from './headers';
|
||||||
import packageJSON from "../../package.json";
|
import packageJSON from '../../package.json';
|
||||||
import { platform, version } from "../lib";
|
import { platform, version } from '../lib';
|
||||||
|
|
||||||
export class defaultHeaders extends headers {
|
export class defaultHeaders extends headers {
|
||||||
constructor(headers: { [key: string]: string } = {}) {
|
constructor(headers: { [key: string]: string } = {}) {
|
||||||
super(headers);
|
super(headers);
|
||||||
if (!this.has("Content-Type"))
|
if (!this.has('Content-Type'))
|
||||||
this.set("Content-Type", "text/plain; charset=utf-8");
|
this.set('Content-Type', 'text/plain; charset=utf-8');
|
||||||
this.set(
|
this.set(
|
||||||
"Server",
|
'Server',
|
||||||
`Handlers.js/${packageJSON.version} ${platform}/${version}`
|
`Handlers.js/${packageJSON.version} ${platform}/${version}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class response<ResponseCustomType> {
|
export class response<ResponseCustomType> {
|
||||||
public status: number;
|
public status: number;
|
||||||
public headers: headers;
|
public headers: headers;
|
||||||
public body: any;
|
public body: any;
|
||||||
public custom: ResponseCustomType;
|
public custom: ResponseCustomType;
|
||||||
public constructor(
|
public constructor(
|
||||||
body: any,
|
body: any,
|
||||||
status: number = 200,
|
status: number = 200,
|
||||||
headers: headers = new defaultHeaders()
|
headers: headers = new defaultHeaders(),
|
||||||
) {
|
) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
public extends(custom: ResponseCustomType): response<ResponseCustomType> {
|
public extends(custom: ResponseCustomType): response<ResponseCustomType> {
|
||||||
this.custom = custom;
|
this.custom = custom;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default response;
|
export default response;
|
||||||
|
58
src/lib.ts
58
src/lib.ts
@ -1,34 +1,34 @@
|
|||||||
export const firstUpperCase = ([first, ...rest]: string) =>
|
export const firstUpperCase = ([first, ...rest]: string) =>
|
||||||
first?.toUpperCase() + rest.map((e) => e.toLowerCase()).join("");
|
first?.toUpperCase() + rest.map((e) => e.toLowerCase()).join('');
|
||||||
export const platform = (() => {
|
export const platform = (() => {
|
||||||
if (typeof process != "undefined") {
|
if (typeof process != 'undefined') {
|
||||||
return "Node.js";
|
return 'Node.js';
|
||||||
}
|
}
|
||||||
if (typeof Deno != "undefined") {
|
if (typeof Deno != 'undefined') {
|
||||||
return "Deno";
|
return 'Deno';
|
||||||
}
|
}
|
||||||
if (typeof Bun != "undefined") {
|
if (typeof Bun != 'undefined') {
|
||||||
return "Bun";
|
return 'Bun';
|
||||||
}
|
}
|
||||||
if (typeof tjs != "undefined") {
|
if (typeof tjs != 'undefined') {
|
||||||
return "txiki.js";
|
return 'txiki.js';
|
||||||
}
|
}
|
||||||
if (typeof self != "undefined") {
|
if (typeof self != 'undefined') {
|
||||||
return "Service Worker";
|
return 'Service Worker';
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
})();
|
})();
|
||||||
export const version = (() => {
|
export const version = (() => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case "Node.js" || "Bun":
|
case 'Node.js' || 'Bun':
|
||||||
return process.version;
|
return process.version;
|
||||||
case "Deno":
|
case 'Deno':
|
||||||
return Deno.version.deno;
|
return Deno.version.deno;
|
||||||
case "txiki.js":
|
case 'txiki.js':
|
||||||
return tjs.versions.tjs;
|
return tjs.versions.tjs;
|
||||||
case "Service Worker":
|
case 'Service Worker':
|
||||||
return undefined;
|
return undefined;
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { SWPlatformAdapter } from "./serviceworker";
|
import { SWPlatformAdapter } from './serviceworker';
|
||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from './index';
|
||||||
|
|
||||||
export class BunPlatformAdapter<T = any, K = any>
|
export class BunPlatformAdapter<T = any, K = any>
|
||||||
extends SWPlatformAdapter<T, K>
|
extends SWPlatformAdapter<T, K>
|
||||||
implements platformAdapater<T, K>
|
implements platformAdapater<T, K>
|
||||||
{
|
{
|
||||||
async listen(port: number): Promise<void> {
|
async listen(port: number): Promise<void> {
|
||||||
Bun.serve({
|
Bun.serve({
|
||||||
fetch: async (request: Request): Promise<Response> => {
|
fetch: async (request: Request): Promise<Response> => {
|
||||||
return await this.handleResponse(
|
return await this.handleResponse(
|
||||||
await this.handleRequest(request).then((request) =>
|
await this.handleRequest(request).then((request) =>
|
||||||
this.router.respond(request)
|
this.router.respond(request),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
port,
|
port,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,25 @@
|
|||||||
import { SWPlatformAdapter } from "./serviceworker";
|
import { SWPlatformAdapter } from './serviceworker';
|
||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from './index';
|
||||||
|
|
||||||
import { request } from "../interface/request";
|
|
||||||
import { headers } from "../interface/headers";
|
|
||||||
import { methodENUM } from "src/interface/method";
|
|
||||||
|
|
||||||
|
import { request } from '../interface/request';
|
||||||
|
import { headers } from '../interface/headers';
|
||||||
|
import { methodENUM } from 'src/interface/method';
|
||||||
|
|
||||||
const DefaultConn: Deno.Conn = {
|
const DefaultConn: Deno.Conn = {
|
||||||
localAddr: {
|
localAddr: {
|
||||||
transport: "tcp",
|
transport: 'tcp',
|
||||||
hostname: "0.0.0.0",
|
hostname: '0.0.0.0',
|
||||||
port: 80,
|
port: 80,
|
||||||
},
|
},
|
||||||
remoteAddr: {
|
remoteAddr: {
|
||||||
transport: "tcp",
|
transport: 'tcp',
|
||||||
hostname: "0.0.0.0",
|
hostname: '0.0.0.0',
|
||||||
port: 80,
|
port: 80,
|
||||||
},
|
},
|
||||||
rid: 0,
|
rid: 0,
|
||||||
closeWrite: async () => undefined,
|
closeWrite: async () => undefined,
|
||||||
readable: "",
|
readable: '',
|
||||||
writable: "",
|
writable: '',
|
||||||
read: async (p: Uint8Array) => null,
|
read: async (p: Uint8Array) => null,
|
||||||
write: async (p: Uint8Array) => 0,
|
write: async (p: Uint8Array) => 0,
|
||||||
close: () => undefined,
|
close: () => undefined,
|
||||||
@ -37,14 +36,19 @@ export class DenoPlatformAdapter<T = any, K = any>
|
|||||||
const httpConnection = Deno.serveHttp(connection);
|
const httpConnection = Deno.serveHttp(connection);
|
||||||
|
|
||||||
for await (const requestEvent of httpConnection) {
|
for await (const requestEvent of httpConnection) {
|
||||||
requestEvent.respondWith(this.handler(requestEvent, connection));
|
requestEvent.respondWith(
|
||||||
|
this.handler(requestEvent, connection),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRequest(nativeRequest: Request, connection: Deno.Conn = DefaultConn): Promise<request<T>> {
|
async handleRequest(
|
||||||
|
nativeRequest: Request,
|
||||||
|
connection: Deno.Conn = DefaultConn,
|
||||||
|
): Promise<request<T>> {
|
||||||
const requestHeaders = new headers(
|
const requestHeaders = new headers(
|
||||||
Object.fromEntries(nativeRequest.headers.entries())
|
Object.fromEntries(nativeRequest.headers.entries()),
|
||||||
);
|
);
|
||||||
const requestMessage: request<T> = new request(
|
const requestMessage: request<T> = new request(
|
||||||
<methodENUM>nativeRequest.method,
|
<methodENUM>nativeRequest.method,
|
||||||
@ -52,16 +56,20 @@ export class DenoPlatformAdapter<T = any, K = any>
|
|||||||
requestHeaders,
|
requestHeaders,
|
||||||
await nativeRequest.text(),
|
await nativeRequest.text(),
|
||||||
{},
|
{},
|
||||||
`${connection.remoteAddr.hostname}:${connection.remoteAddr.port}` || ""
|
`${connection.remoteAddr.hostname}:${connection.remoteAddr.port}` ||
|
||||||
|
'',
|
||||||
);
|
);
|
||||||
return requestMessage;
|
return requestMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handler(event: FetchEvent, connection: Deno.Conn = DefaultConn): Promise<Response> {
|
async handler(
|
||||||
|
event: FetchEvent,
|
||||||
|
connection: Deno.Conn = DefaultConn,
|
||||||
|
): Promise<Response> {
|
||||||
return await this.handleResponse(
|
return await this.handleResponse(
|
||||||
await this.handleRequest(event.request, connection).then((request) =>
|
await this.handleRequest(event.request, connection).then(
|
||||||
this.router.respond(request)
|
(request) => this.router.respond(request),
|
||||||
)
|
),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { NodePlatformAdapter } from "./node";
|
import { NodePlatformAdapter } from './node';
|
||||||
import { SWPlatformAdapter } from "./serviceworker";
|
import { SWPlatformAdapter } from './serviceworker';
|
||||||
import { DenoPlatformAdapter } from "./deno";
|
import { DenoPlatformAdapter } from './deno';
|
||||||
import { TxikiPlatformAdapter } from "./txiki";
|
import { TxikiPlatformAdapter } from './txiki';
|
||||||
|
|
||||||
export const platformAdapaterMapping = {
|
export const platformAdapaterMapping = {
|
||||||
"Node.js": NodePlatformAdapter,
|
'Node.js': NodePlatformAdapter,
|
||||||
"Service Worker": SWPlatformAdapter,
|
'Service Worker': SWPlatformAdapter,
|
||||||
"Deno": DenoPlatformAdapter,
|
Deno: DenoPlatformAdapter,
|
||||||
"txiki.js": TxikiPlatformAdapter,
|
'txiki.js': TxikiPlatformAdapter,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { NodePlatformAdapter, SWPlatformAdapter, DenoPlatformAdapter, TxikiPlatformAdapter };
|
export {
|
||||||
|
NodePlatformAdapter,
|
||||||
|
SWPlatformAdapter,
|
||||||
|
DenoPlatformAdapter,
|
||||||
|
TxikiPlatformAdapter,
|
||||||
|
};
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
import { request, response } from "../interface/index";
|
import { request, response } from '../interface/index';
|
||||||
import { router } from "../router";
|
import { router } from '../router';
|
||||||
|
|
||||||
export interface platformAdapater<T = any, K = any> {
|
export interface platformAdapater<T = any, K = any> {
|
||||||
router: router<T, K>;
|
router: router<T, K>;
|
||||||
listen(port: number): void;
|
listen(port: number): void;
|
||||||
handleRequest(nativeRequest: any): Promise<request<T>>;
|
handleRequest(nativeRequest: any): Promise<request<T>>;
|
||||||
handleResponse(response: response<K> | Promise<response<K>>, nativeResponse?: any): any;
|
handleResponse(
|
||||||
|
response: response<K> | Promise<response<K>>,
|
||||||
|
nativeResponse?: any,
|
||||||
|
): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface platformAdapaterConstructor<T = any, K = any> {
|
export interface platformAdapaterConstructor<T = any, K = any> {
|
||||||
new (router: router<T, K>): platformAdapater<T, K>;
|
new (router: router<T, K>): platformAdapater<T, K>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPlatformAdapater(
|
export function createPlatformAdapater(
|
||||||
adapater: platformAdapaterConstructor,
|
adapater: platformAdapaterConstructor,
|
||||||
router: router
|
router: router,
|
||||||
): platformAdapater {
|
): platformAdapater {
|
||||||
return new adapater(router);
|
return new adapater(router);
|
||||||
}
|
}
|
||||||
|
@ -1,77 +1,81 @@
|
|||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from './index';
|
||||||
import { request, response } from "../interface/index";
|
import { request, response } from '../interface/index';
|
||||||
import { router } from "../router";
|
import { router } from '../router';
|
||||||
import { headers } from "../interface/headers";
|
import { headers } from '../interface/headers';
|
||||||
|
|
||||||
import http from "http";
|
import http from 'http';
|
||||||
import { methodENUM } from "src/interface/method";
|
import { methodENUM } from 'src/interface/method';
|
||||||
|
|
||||||
export class NodePlatformAdapter<T = any, K = any> implements platformAdapater {
|
export class NodePlatformAdapter<T = any, K = any> implements platformAdapater {
|
||||||
public router: router<T, K>;
|
public router: router<T, K>;
|
||||||
|
|
||||||
constructor(router: router<T, K>) {
|
constructor(router: router<T, K>) {
|
||||||
this.router = router;
|
this.router = router;
|
||||||
}
|
}
|
||||||
|
|
||||||
async listen(port: number): Promise<void> {
|
async listen(port: number): Promise<void> {
|
||||||
const server = http.createServer();
|
const server = http.createServer();
|
||||||
server.on(
|
server.on(
|
||||||
"request",
|
'request',
|
||||||
async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||||
const request = await this.handleRequest(req);
|
const request = await this.handleRequest(req);
|
||||||
const response = await this.router.respond(request);
|
const response = await this.router.respond(request);
|
||||||
this.handleResponse(response, res);
|
this.handleResponse(response, res);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
server.listen(port);
|
server.listen(port);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRequest(
|
async handleRequest(
|
||||||
nativeRequest: http.IncomingMessage
|
nativeRequest: http.IncomingMessage,
|
||||||
): Promise<request<T>> {
|
): Promise<request<T>> {
|
||||||
if (
|
if (
|
||||||
typeof nativeRequest.method != "string" ||
|
typeof nativeRequest.method != 'string' ||
|
||||||
typeof nativeRequest.url != "string" ||
|
typeof nativeRequest.url != 'string' ||
|
||||||
typeof nativeRequest.headers != "object"
|
typeof nativeRequest.headers != 'object'
|
||||||
) {
|
) {
|
||||||
throw new Error("Invalid request");
|
throw new Error('Invalid request');
|
||||||
}
|
}
|
||||||
|
|
||||||
let body: string = "";
|
let body: string = '';
|
||||||
const ip: string = nativeRequest.socket.remoteAddress?.replace("::ffff:", "") ?? "0.0.0.0";
|
const ip: string =
|
||||||
const requestHeaders = new headers(<any>nativeRequest.headers);
|
nativeRequest.socket.remoteAddress?.replace('::ffff:', '') ??
|
||||||
|
'0.0.0.0';
|
||||||
|
const requestHeaders = new headers(<any>nativeRequest.headers);
|
||||||
|
|
||||||
if (!["GET", "HEAD", "DELETE", "OPTIONS"].includes(nativeRequest.method)) {
|
if (
|
||||||
nativeRequest.on("data", (data: string) => {
|
!['GET', 'HEAD', 'DELETE', 'OPTIONS'].includes(nativeRequest.method)
|
||||||
body += data;
|
) {
|
||||||
});
|
nativeRequest.on('data', (data: string) => {
|
||||||
|
body += data;
|
||||||
|
});
|
||||||
|
|
||||||
await new Promise((resolve) =>
|
await new Promise((resolve) =>
|
||||||
nativeRequest.on("end", () => {
|
nativeRequest.on('end', () => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new request<T>(
|
return new request<T>(
|
||||||
<methodENUM>nativeRequest.method,
|
<methodENUM>nativeRequest.method,
|
||||||
new URL(
|
new URL(
|
||||||
nativeRequest.url,
|
nativeRequest.url,
|
||||||
`http://${requestHeaders.get("host") ?? "localhost"}`
|
`http://${requestHeaders.get('host') ?? 'localhost'}`,
|
||||||
),
|
),
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
body,
|
body,
|
||||||
{},
|
{},
|
||||||
ip
|
ip,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResponse(response: response<K>, nativeResponse: http.ServerResponse) {
|
handleResponse(response: response<K>, nativeResponse: http.ServerResponse) {
|
||||||
nativeResponse.statusCode = response.status;
|
nativeResponse.statusCode = response.status;
|
||||||
response.headers.forEach((key, value) => {
|
response.headers.forEach((key, value) => {
|
||||||
nativeResponse.setHeader(key, value);
|
nativeResponse.setHeader(key, value);
|
||||||
});
|
});
|
||||||
nativeResponse.end(response.body);
|
nativeResponse.end(response.body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,55 @@
|
|||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from './index';
|
||||||
import { request } from "../interface/request";
|
import { request } from '../interface/request';
|
||||||
import { response } from "../interface/response";
|
import { response } from '../interface/response';
|
||||||
import { router } from "../router";
|
import { router } from '../router';
|
||||||
import { headers } from "../interface/headers";
|
import { headers } from '../interface/headers';
|
||||||
|
|
||||||
import { methodENUM } from "src/interface/method";
|
import { methodENUM } from 'src/interface/method';
|
||||||
|
|
||||||
export class SWPlatformAdapter<T = any, K = any> implements platformAdapater {
|
export class SWPlatformAdapter<T = any, K = any> implements platformAdapater {
|
||||||
public router: router<T, K>;
|
public router: router<T, K>;
|
||||||
|
|
||||||
constructor(router: router<T, K>) {
|
constructor(router: router<T, K>) {
|
||||||
this.router = router;
|
this.router = router;
|
||||||
}
|
}
|
||||||
|
|
||||||
async listen(_port?: number): Promise<void> {
|
async listen(_port?: number): Promise<void> {
|
||||||
self.addEventListener("fetch", (event: FetchEvent) => {
|
self.addEventListener('fetch', (event: FetchEvent) => {
|
||||||
event.respondWith(this.handler(event));
|
event.respondWith(this.handler(event));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRequest(nativeRequest: Request): Promise<request<T>> {
|
async handleRequest(nativeRequest: Request): Promise<request<T>> {
|
||||||
const requestHeaders = new headers(
|
const requestHeaders = new headers(
|
||||||
Object.fromEntries(nativeRequest.headers.entries())
|
Object.fromEntries(nativeRequest.headers.entries()),
|
||||||
);
|
);
|
||||||
const requestMessage: request<T> = new request(
|
const requestMessage: request<T> = new request(
|
||||||
<methodENUM>nativeRequest.method,
|
<methodENUM>nativeRequest.method,
|
||||||
new URL(nativeRequest.url),
|
new URL(nativeRequest.url),
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
await nativeRequest.text(),
|
await nativeRequest.text(),
|
||||||
{},
|
{},
|
||||||
requestHeaders.get("CF-Connecting-IP") || ""
|
requestHeaders.get('CF-Connecting-IP') || '',
|
||||||
);
|
);
|
||||||
return requestMessage;
|
return requestMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleResponse(response: response<K>): Promise<Response> {
|
async handleResponse(response: response<K>): Promise<Response> {
|
||||||
if (response.status === 204) { response.body = null; }
|
if (response.status === 204) {
|
||||||
const nativResponse = new Response(response.body, {
|
response.body = null;
|
||||||
status: response.status,
|
}
|
||||||
headers: response.headers.headers,
|
const nativResponse = new Response(response.body, {
|
||||||
});
|
status: response.status,
|
||||||
return nativResponse;
|
headers: response.headers.headers,
|
||||||
}
|
});
|
||||||
|
return nativResponse;
|
||||||
|
}
|
||||||
|
|
||||||
async handler(event: FetchEvent): Promise<Response> {
|
async handler(event: FetchEvent): Promise<Response> {
|
||||||
return await this.handleResponse(
|
return await this.handleResponse(
|
||||||
await this.handleRequest(event.request).then((request) =>
|
await this.handleRequest(event.request).then((request) =>
|
||||||
this.router.respond(request)
|
this.router.respond(request),
|
||||||
)
|
),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { request } from "../../interface/request";
|
import { request } from '../../interface/request';
|
||||||
import { response } from "../../interface";
|
import { response } from '../../interface';
|
||||||
import { headers } from "../../interface/headers";
|
import { headers } from '../../interface/headers';
|
||||||
import { methodENUM } from "../../interface/method";
|
import { methodENUM } from '../../interface/method';
|
||||||
|
|
||||||
import statusCode from "./statusCode.json";
|
import statusCode from './statusCode.json';
|
||||||
|
|
||||||
export class HttpConn {
|
export class HttpConn {
|
||||||
private closed: boolean = false;
|
private closed: boolean = false;
|
||||||
@ -16,44 +16,63 @@ export class HttpConn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readMessage(httpMessage: string): request<any> {
|
private readMessage(httpMessage: string): request<any> {
|
||||||
const lines = httpMessage.split("\n");
|
const lines = httpMessage.split('\n');
|
||||||
const firstLine = lines[0];
|
const firstLine = lines[0];
|
||||||
const dividingIndex = lines.indexOf("\r") ?? lines.indexOf("");
|
const dividingIndex = lines.indexOf('\r') ?? lines.indexOf('');
|
||||||
const rawHeaders = lines.slice(1, dividingIndex);
|
const rawHeaders = lines.slice(1, dividingIndex);
|
||||||
|
|
||||||
const [method, path, version] = firstLine.split(" ");
|
const [method, path, version] = firstLine.split(' ');
|
||||||
|
|
||||||
|
if (version in ['HTTP/1.1', 'HTTP/1.0', 'HTTP/0.9']) {
|
||||||
|
this.conn.close();
|
||||||
|
}
|
||||||
|
|
||||||
const requestHeaders = new headers({});
|
const requestHeaders = new headers({});
|
||||||
for (const header of rawHeaders) {
|
for (const header of rawHeaders) {
|
||||||
const [key, value] = header.split(": ");
|
const [key, value] = header.split(': ');
|
||||||
requestHeaders.set(key, value);
|
requestHeaders.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(path, `http://${requestHeaders.get("Host")}/` ?? `http://${this.conn.localAddress.ip}:${this.conn.localAddress.port}/`);
|
const url = new URL(
|
||||||
|
path,
|
||||||
|
`http://${requestHeaders.get('Host')}/` ??
|
||||||
|
`http://${this.conn.localAddress.ip}:${this.conn.localAddress.port}/`,
|
||||||
|
);
|
||||||
|
|
||||||
const body = lines.slice(dividingIndex + 1).join("\n");
|
const body = lines.slice(dividingIndex + 1).join('\n');
|
||||||
|
|
||||||
const requestMessage = new request<any>(<methodENUM>method, url, requestHeaders, body, {}, this.conn.remoteAddress.ip);
|
const requestMessage = new request<any>(
|
||||||
|
<methodENUM>method,
|
||||||
|
url,
|
||||||
|
requestHeaders,
|
||||||
|
body,
|
||||||
|
{},
|
||||||
|
this.conn.remoteAddress.ip,
|
||||||
|
);
|
||||||
return requestMessage;
|
return requestMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleResponse(response: response<any>) {
|
private handleResponse(response: response<any>) {
|
||||||
let responseMessage: string = "";
|
let responseMessage: string = '';
|
||||||
responseMessage += "HTTP/1.1 " + response.status + " " + statusCode[<"100">response.status.toString()] ?? "";
|
responseMessage +=
|
||||||
|
'HTTP/1.1 ' +
|
||||||
|
response.status +
|
||||||
|
' ' +
|
||||||
|
statusCode[<'100'>response.status.toString()] ?? '';
|
||||||
|
|
||||||
response.headers.forEach((key, value) => {
|
response.headers.forEach((key, value) => {
|
||||||
responseMessage += "\n" + key + ": " + value;
|
responseMessage += '\n' + key + ': ' + value;
|
||||||
});
|
});
|
||||||
|
|
||||||
responseMessage += "\n\n" + response.body;
|
responseMessage += '\n\n' + response.body;
|
||||||
|
|
||||||
this.conn.write(new TextEncoder().encode(responseMessage));
|
this.conn.write(new TextEncoder().encode(responseMessage));
|
||||||
this.conn.shutdown();
|
this.conn.close();
|
||||||
this.closed = true;
|
this.closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async read(): Promise<request<any> | undefined> {
|
private async read(): Promise<request<any> | undefined> {
|
||||||
let message = "";
|
let message = '';
|
||||||
const { done, value } = await this.reader.read();
|
const { done, value } = await this.reader.read();
|
||||||
|
|
||||||
if (done || this.closed) {
|
if (done || this.closed) {
|
||||||
@ -61,9 +80,15 @@ export class HttpConn {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
message += String.fromCharCode(...Object.values(<{
|
message += String.fromCharCode(
|
||||||
[key: string]: number
|
...Object.values(
|
||||||
}>value));
|
<
|
||||||
|
{
|
||||||
|
[key: string]: number;
|
||||||
|
}
|
||||||
|
>value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const requestMessage = this.readMessage(message);
|
const requestMessage = this.readMessage(message);
|
||||||
return requestMessage;
|
return requestMessage;
|
||||||
@ -82,10 +107,10 @@ export class HttpConn {
|
|||||||
respondWith: (response: response<any>) => {
|
respondWith: (response: response<any>) => {
|
||||||
httpConn.handleResponse(response);
|
httpConn.handleResponse(response);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from './index';
|
||||||
import { request } from "../interface/request";
|
import { request } from '../interface/request';
|
||||||
import { response } from "../interface/response";
|
import { response } from '../interface/response';
|
||||||
import { router } from "../router";
|
import { router } from '../router';
|
||||||
import serveHttp from "./txiki-js/serveHttp";
|
import serveHttp from './txiki-js/serveHttp';
|
||||||
|
|
||||||
export class TxikiPlatformAdapter<T = any, K = any> implements platformAdapater {
|
export class TxikiPlatformAdapter<T = any, K = any>
|
||||||
|
implements platformAdapater
|
||||||
|
{
|
||||||
public router: router<T, K>;
|
public router: router<T, K>;
|
||||||
|
|
||||||
constructor(router: router<T, K>) {
|
constructor(router: router<T, K>) {
|
||||||
@ -12,13 +14,16 @@ export class TxikiPlatformAdapter<T = any, K = any> implements platformAdapater
|
|||||||
}
|
}
|
||||||
|
|
||||||
async listen(port?: number): Promise<void> {
|
async listen(port?: number): Promise<void> {
|
||||||
const Server = await tjs.listen("tcp", "0.0.0.0", port);
|
const Server = await tjs.listen('tcp', '0.0.0.0', port);
|
||||||
|
|
||||||
for await (const conn of Server) {
|
for await (const conn of Server) {
|
||||||
const httpConn = serveHttp(conn);
|
const httpConn = serveHttp(conn);
|
||||||
|
|
||||||
for await (const conn of httpConn) {
|
for await (const conn of httpConn) {
|
||||||
if (typeof conn == "undefined" || typeof conn.request == "undefined") {
|
if (
|
||||||
|
typeof conn == 'undefined' ||
|
||||||
|
typeof conn.request == 'undefined'
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conn.respondWith(await this.router.respond(conn.request));
|
conn.respondWith(await this.router.respond(conn.request));
|
||||||
|
111
src/route.ts
111
src/route.ts
@ -1,71 +1,70 @@
|
|||||||
import { path } from "./interface";
|
import { path } from './interface';
|
||||||
import handler from "./handler";
|
import handler from './handler';
|
||||||
import { pathToRegexp } from "path-to-regexp";
|
import { pathToRegexp } from 'path-to-regexp';
|
||||||
|
|
||||||
interface matchedStatus {
|
interface matchedStatus {
|
||||||
matched: boolean;
|
matched: boolean;
|
||||||
attributes: {
|
attributes: {
|
||||||
name: string;
|
name: string;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface regExpKey {
|
interface regExpKey {
|
||||||
name: string;
|
name: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
suffix: string;
|
suffix: string;
|
||||||
pattern: string;
|
pattern: string;
|
||||||
modifier: string;
|
modifier: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export class route {
|
export class route {
|
||||||
private paths: path[];
|
private paths: path[];
|
||||||
public handlers: handler<any, any>[];
|
public handlers: handler<any, any>[];
|
||||||
private regExps: { regExp: RegExp, keys: regExpKey[] }[] = [];
|
private regExps: { regExp: RegExp; keys: regExpKey[] }[] = [];
|
||||||
|
|
||||||
constructor(paths: path[], handlers: handler<any, any>[]) {
|
constructor(paths: path[], handlers: handler<any, any>[]) {
|
||||||
this.paths = paths;
|
this.paths = paths;
|
||||||
this.handlers = handlers;
|
this.handlers = handlers;
|
||||||
|
|
||||||
this.paths.forEach(path => {
|
this.paths.forEach((path) => {
|
||||||
const keys: regExpKey[] = [];
|
const keys: regExpKey[] = [];
|
||||||
this.regExps.push({ regExp: pathToRegexp(path, keys), keys });
|
this.regExps.push({ regExp: pathToRegexp(path, keys), keys });
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
async exec(path: string): Promise<matchedStatus> {
|
async exec(path: string): Promise<matchedStatus> {
|
||||||
let Answer = await Promise.all<Promise<matchedStatus>>(
|
let Answer = await Promise.all<Promise<matchedStatus>>(
|
||||||
this.regExps.map(async (it) => {
|
this.regExps.map(async (it) => {
|
||||||
|
const answer = it.regExp.exec(path);
|
||||||
|
if (answer === null)
|
||||||
|
return {
|
||||||
|
matched: false,
|
||||||
|
attributes: [],
|
||||||
|
};
|
||||||
|
|
||||||
const answer = it.regExp.exec(path);
|
let attributes: matchedStatus['attributes'] = [];
|
||||||
if (answer === null)
|
|
||||||
return {
|
|
||||||
matched: false,
|
|
||||||
attributes: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
let attributes: matchedStatus["attributes"] = [];
|
it.keys.forEach((key, index) => {
|
||||||
|
attributes.push({
|
||||||
|
name: key.name,
|
||||||
|
value: answer[index + 1],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it.keys.forEach((key, index) => {
|
return {
|
||||||
attributes.push({
|
matched: true,
|
||||||
name: key.name,
|
attributes: attributes,
|
||||||
value: answer[index + 1],
|
};
|
||||||
});
|
}),
|
||||||
});
|
);
|
||||||
|
Answer = Answer.filter((it) => it.matched);
|
||||||
return {
|
if (Answer.length === 0)
|
||||||
matched: true,
|
return {
|
||||||
attributes: attributes,
|
matched: false,
|
||||||
};
|
attributes: [],
|
||||||
})
|
};
|
||||||
);
|
else return Answer[0];
|
||||||
Answer = Answer.filter((it) => it.matched);
|
}
|
||||||
if (Answer.length === 0)
|
|
||||||
return {
|
|
||||||
matched: false,
|
|
||||||
attributes: [],
|
|
||||||
};
|
|
||||||
else return Answer[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default route;
|
export default route;
|
||||||
|
403
src/router.ts
403
src/router.ts
@ -1,225 +1,238 @@
|
|||||||
import handler from "./handler";
|
import handler from './handler';
|
||||||
import {
|
import {
|
||||||
path,
|
path,
|
||||||
response,
|
response,
|
||||||
request,
|
request,
|
||||||
AllMismatchInterrupted,
|
AllMismatchInterrupted,
|
||||||
responder,
|
responder,
|
||||||
method,
|
method,
|
||||||
} from "./interface/index";
|
} from './interface/index';
|
||||||
import { defaultHeaders } from "./interface/response";
|
import { defaultHeaders } from './interface/response';
|
||||||
import route from "./route";
|
import route from './route';
|
||||||
import { methodENUM } from "./interface/method";
|
import { methodENUM } from './interface/method';
|
||||||
import {
|
import {
|
||||||
createPlatformAdapater,
|
createPlatformAdapater,
|
||||||
platformAdapaterConstructor,
|
platformAdapaterConstructor,
|
||||||
platformAdapater,
|
platformAdapater,
|
||||||
} from "./platform/index";
|
} from './platform/index';
|
||||||
import { platformAdapaterMapping } from "./platform/export";
|
import { platformAdapaterMapping } from './platform/export';
|
||||||
import { platform } from "./lib";
|
import { platform } from './lib';
|
||||||
|
|
||||||
export class router<K = any, V = any> {
|
export class router<K = any, V = any> {
|
||||||
public routes: route[];
|
public routes: route[];
|
||||||
|
|
||||||
public errorResponder: (
|
public errorResponder: (
|
||||||
errorCode: number,
|
errorCode: number,
|
||||||
errorMessage?: string
|
errorMessage?: string,
|
||||||
) => responder<K, V>;
|
) => responder<K, V>;
|
||||||
|
|
||||||
constructor(routes: route[] = []) {
|
constructor(routes: route[] = []) {
|
||||||
this.routes = routes;
|
this.routes = routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(route: route) {
|
add(route: route) {
|
||||||
this.routes.push(route);
|
this.routes.push(route);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
binding(path: path, handler: handler<K, V>) {
|
binding(path: path, handler: handler<K, V>) {
|
||||||
this.add(new route([path], [handler]));
|
this.add(new route([path], [handler]));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
create(
|
create(
|
||||||
method: method,
|
method: method,
|
||||||
responder: (
|
responder: (
|
||||||
request: request<K>
|
request: request<K>,
|
||||||
) =>
|
) =>
|
||||||
| Promise<response<V>>
|
| Promise<response<V>>
|
||||||
| Promise<string>
|
| Promise<string>
|
||||||
| Promise<object>
|
| Promise<object>
|
||||||
| Promise<number>
|
| Promise<number>
|
||||||
| Promise<void>
|
| Promise<void>,
|
||||||
) {
|
) {
|
||||||
return new handler<K, V>(method, [
|
return new handler<K, V>(method, [
|
||||||
async (request: request<K>) => {
|
async (request: request<K>) => {
|
||||||
const answer = await responder(request);
|
const answer = await responder(request);
|
||||||
if (answer instanceof response) {
|
if (answer instanceof response) {
|
||||||
return answer;
|
return answer;
|
||||||
} else if (typeof answer == "string") {
|
} else if (typeof answer == 'string') {
|
||||||
return new response(answer);
|
return new response(answer);
|
||||||
} else if (typeof answer == "number") {
|
} else if (typeof answer == 'number') {
|
||||||
return new response(answer.toString());
|
return new response(answer.toString());
|
||||||
} else if (typeof answer == "object") {
|
} else if (typeof answer == 'object') {
|
||||||
return new response(
|
return new response(
|
||||||
JSON.stringify(answer),
|
JSON.stringify(answer),
|
||||||
200,
|
200,
|
||||||
new defaultHeaders({
|
new defaultHeaders({
|
||||||
"Content-Type": "application/json; charset=utf-8",
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return new response("", 204);
|
return new response('', 204);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
use(routers: router[], path: path): void {
|
use(routers: router[], path: path): void {
|
||||||
routers.forEach((router) => {
|
routers.forEach((router) => {
|
||||||
this.binding(path, router.toHandler());
|
this.binding(path, router.toHandler());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
route(path: path): router {
|
route(path: path): router {
|
||||||
const Router = new router([]);
|
const Router = new router([]);
|
||||||
this.use([Router], path);
|
this.use([Router], path);
|
||||||
return Router;
|
return Router;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _respond(
|
async _respond(
|
||||||
request: request<K>,
|
request: request<K>,
|
||||||
responseMessage: response<V> = new response<V>("")
|
responseMessage: response<V> = new response<V>(''),
|
||||||
): Promise<response<V>> {
|
): Promise<response<V>> {
|
||||||
request.originURL = request.url;
|
request.originURL = request.url;
|
||||||
request.url.pathname = request.params["0"]
|
request.url.pathname = request.params['0']
|
||||||
? "/" + request.params["0"]
|
? '/' + request.params['0']
|
||||||
: request.originURL.pathname;
|
: request.originURL.pathname;
|
||||||
|
|
||||||
let mismatchCount = 0;
|
let mismatchCount = 0;
|
||||||
|
|
||||||
for (let route of this.routes) {
|
for (let route of this.routes) {
|
||||||
const isMatched = await route.exec(request.url.pathname);
|
const isMatched = await route.exec(request.url.pathname);
|
||||||
|
|
||||||
if (!isMatched.matched) {
|
if (!isMatched.matched) {
|
||||||
mismatchCount++;
|
mismatchCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
isMatched.attributes.forEach((e) => {
|
isMatched.attributes.forEach((e) => {
|
||||||
request.params[e.name] = e.value;
|
request.params[e.name] = e.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let thisResponse: response<V> | void = responseMessage;
|
let thisResponse: response<V> | void = responseMessage;
|
||||||
for (let handler of route.handlers) {
|
for (let handler of route.handlers) {
|
||||||
if (
|
if (
|
||||||
handler.method != request.method &&
|
handler.method != request.method &&
|
||||||
handler.method != methodENUM.ANY
|
handler.method != methodENUM.ANY
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
thisResponse = await handler.respond(
|
thisResponse = await handler.respond(
|
||||||
request,
|
request,
|
||||||
thisResponse ?? responseMessage
|
thisResponse ?? responseMessage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (thisResponse instanceof response) {
|
if (thisResponse instanceof response) {
|
||||||
responseMessage = thisResponse;
|
responseMessage = thisResponse;
|
||||||
} else {
|
} else {
|
||||||
// means that the handler is a middleware that doesn't change the response
|
// means that the handler is a middleware that doesn't change the response
|
||||||
throw AllMismatchInterrupted;
|
throw AllMismatchInterrupted;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof response) {
|
if (e instanceof response) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (e === AllMismatchInterrupted) mismatchCount++;
|
if (e === AllMismatchInterrupted) mismatchCount++;
|
||||||
else {
|
else {
|
||||||
if (typeof this.errorResponder == "function") {
|
if (typeof this.errorResponder == 'function') {
|
||||||
responseMessage =
|
responseMessage =
|
||||||
(await this.errorResponder(500, e.toString() + "\n")(request)) ??
|
(await this.errorResponder(
|
||||||
new response(e.toString(), 500);
|
500,
|
||||||
console.log(e);
|
e.toString() + '\n',
|
||||||
} else {
|
)(request)) ?? new response(e.toString(), 500);
|
||||||
throw e;
|
console.log(e);
|
||||||
}
|
} else {
|
||||||
}
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mismatchCount == this.routes.length) {
|
if (mismatchCount == this.routes.length) {
|
||||||
throw AllMismatchInterrupted;
|
throw AllMismatchInterrupted;
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseMessage;
|
return responseMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public respond = this._respond;
|
public respond = this._respond;
|
||||||
|
|
||||||
toHandler(): handler<K, V> {
|
toHandler(): handler<K, V> {
|
||||||
return new handler(methodENUM.ANY, [
|
return new handler(methodENUM.ANY, [
|
||||||
(request: request<K>, responseMessage?: response<V>) => {
|
(request: request<K>, responseMessage?: response<V>) => {
|
||||||
return this.respond(request, responseMessage ?? new response(""));
|
return this.respond(
|
||||||
},
|
request,
|
||||||
]);
|
responseMessage ?? new response(''),
|
||||||
}
|
);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
useErrorResponder(
|
useErrorResponder(
|
||||||
errorResponder: (
|
errorResponder: (
|
||||||
errorCode: number,
|
errorCode: number,
|
||||||
errorMessage?: string
|
errorMessage?: string,
|
||||||
) => responder<K, V>
|
) => responder<K, V>,
|
||||||
): this {
|
): this {
|
||||||
this.errorResponder = errorResponder;
|
this.errorResponder = errorResponder;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
||||||
export class rootRouter<K = any, V = any> extends router<K, V> {
|
export class rootRouter<K = any, V = any> extends router<K, V> {
|
||||||
public adapater: platformAdapater<K, V>;
|
public adapater: platformAdapater<K, V>;
|
||||||
errorResponder =
|
errorResponder =
|
||||||
(errorCode: number, errorMessage?: string) =>
|
(errorCode: number, errorMessage?: string) =>
|
||||||
async (_request: request<K>): Promise<response<V>> =>
|
async (_request: request<K>): Promise<response<V>> =>
|
||||||
new response(errorMessage ?? "", errorCode);
|
new response(errorMessage ?? '', errorCode);
|
||||||
|
|
||||||
respond = async (request: request<K>): Promise<response<V>> => {
|
respond = async (request: request<K>): Promise<response<V>> => {
|
||||||
let responseMessage: response<V> = new response("");
|
let responseMessage: response<V> = new response('');
|
||||||
try {
|
try {
|
||||||
responseMessage = await this._respond(request, responseMessage);
|
responseMessage = await this._respond(request, responseMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof response) {
|
if (e instanceof response) {
|
||||||
return e;
|
return e;
|
||||||
} else if (e === AllMismatchInterrupted) {
|
} else if (e === AllMismatchInterrupted) {
|
||||||
responseMessage =
|
responseMessage =
|
||||||
(await this.errorResponder(404, "404 Not Found\n")(request)) ??
|
(await this.errorResponder(
|
||||||
new response("404 Not Found\n", 404);
|
404,
|
||||||
} else {
|
'404 Not Found\n',
|
||||||
responseMessage =
|
)(request)) ?? new response('404 Not Found\n', 404);
|
||||||
(await this.errorResponder(500, e.toString() + "\n")(request)) ??
|
} else {
|
||||||
new response(e.toString(), 500);
|
responseMessage =
|
||||||
console.log(e);
|
(await this.errorResponder(
|
||||||
}
|
500,
|
||||||
}
|
e.toString() + '\n',
|
||||||
|
)(request)) ?? new response(e.toString(), 500);
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return responseMessage;
|
return responseMessage;
|
||||||
};
|
};
|
||||||
useAdapater(adapater: platformAdapaterConstructor): this {
|
useAdapater(adapater: platformAdapaterConstructor): this {
|
||||||
this.adapater = createPlatformAdapater(adapater, this);
|
this.adapater = createPlatformAdapater(adapater, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
useMappingAdapter(
|
useMappingAdapter(
|
||||||
mapping: { [platform: string]: platformAdapaterConstructor } = platformAdapaterMapping
|
mapping: {
|
||||||
): this {
|
[platform: string]: platformAdapaterConstructor;
|
||||||
if (typeof platform == "undefined") throw new Error("Cannot detect platform");
|
} = platformAdapaterMapping,
|
||||||
if (mapping[platform] == undefined) throw new Error("Platform not found in mapping");
|
): this {
|
||||||
else this.useAdapater(mapping[platform]);
|
if (typeof platform == 'undefined')
|
||||||
return this;
|
throw new Error('Cannot detect platform');
|
||||||
}
|
if (mapping[platform] == undefined)
|
||||||
listen(port: number): void {
|
throw new Error('Platform not found in mapping');
|
||||||
if (this.adapater == null) throw new Error("No platform adapter set");
|
else this.useAdapater(mapping[platform]);
|
||||||
this.adapater.listen(port);
|
return this;
|
||||||
}
|
}
|
||||||
|
listen(port: number): void {
|
||||||
|
if (this.adapater == null) throw new Error('No platform adapter set');
|
||||||
|
this.adapater.listen(port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
import { spawn } from "child_process";
|
import { spawn } from 'child_process';
|
||||||
|
|
||||||
import Axios from "axios";
|
import Axios from 'axios';
|
||||||
|
|
||||||
const Instance = Axios.create({
|
const Instance = Axios.create({
|
||||||
baseURL: "http://localhost:3000"
|
baseURL: 'http://localhost:3000',
|
||||||
})
|
});
|
||||||
|
|
||||||
spawn(`deno`, [
|
spawn(`deno`, ['run', '--allow-net', `${__dirname}/../dist/test.deno.js`]);
|
||||||
"run", "--allow-net", `${__dirname}/../dist/test.deno.js`
|
|
||||||
]);
|
|
||||||
|
|
||||||
const randomString = () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
const randomString = () =>
|
||||||
|
Math.random().toString(36).substring(2, 15) +
|
||||||
|
Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
describe("Test server", () => {
|
describe('Test server', () => {
|
||||||
test("normal 200 response", async () => {
|
test('normal 200 response', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 200));
|
await new Promise((r) => setTimeout(r, 200));
|
||||||
|
|
||||||
const { data, status } = await Instance.get("/");
|
const { data, status } = await Instance.get('/');
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual("200 OK");
|
expect(data).toEqual('200 OK');
|
||||||
})
|
});
|
||||||
|
|
||||||
test("post response", async () => {
|
test('post response', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const string = randomString();
|
const string = randomString();
|
||||||
|
|
||||||
const { data, status } = await Instance.post("/post", string);
|
const { data, status } = await Instance.post('/post', string);
|
||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual(string);
|
expect(data).toEqual(string);
|
||||||
})
|
});
|
||||||
|
|
||||||
test("change header and status code", async () => {
|
test('change header and status code', async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
|
|
||||||
const { data, status, headers } = await Instance.get("/header");
|
const { data, status, headers } = await Instance.get('/header');
|
||||||
|
|
||||||
expect(status).toEqual(204);
|
expect(status).toEqual(204);
|
||||||
expect(headers["itis"]).toEqual("work");
|
expect(headers['itis']).toEqual('work');
|
||||||
expect(data).toEqual("");
|
expect(data).toEqual('');
|
||||||
})
|
});
|
||||||
|
|
||||||
test("get param", async () => {
|
test('get param', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
const string = randomString();
|
const string = randomString();
|
||||||
@ -51,14 +51,14 @@ describe("Test server", () => {
|
|||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual(string);
|
expect(data).toEqual(string);
|
||||||
})
|
});
|
||||||
|
|
||||||
test("chain interrupted", async () => {
|
test('chain interrupted', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
const { data, status } = await Instance.get(`/info/foo`);
|
const { data, status } = await Instance.get(`/info/foo`);
|
||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual("hit");
|
expect(data).toEqual('hit');
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,45 +1,47 @@
|
|||||||
import _ from "./test-server";
|
import _ from './test-server';
|
||||||
|
|
||||||
import Axios from "axios";
|
import Axios from 'axios';
|
||||||
|
|
||||||
_.listen(3000);
|
_.listen(3000);
|
||||||
|
|
||||||
const Instance = Axios.create({
|
const Instance = Axios.create({
|
||||||
baseURL: "http://localhost:3000"
|
baseURL: 'http://localhost:3000',
|
||||||
})
|
});
|
||||||
|
|
||||||
const randomString = () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
const randomString = () =>
|
||||||
|
Math.random().toString(36).substring(2, 15) +
|
||||||
|
Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
describe("Test server", () => {
|
describe('Test server', () => {
|
||||||
test("normal 200 response", async () => {
|
test('normal 200 response', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
const { data, status } = await Instance.get("/");
|
const { data, status } = await Instance.get('/');
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual("200 OK");
|
expect(data).toEqual('200 OK');
|
||||||
})
|
});
|
||||||
|
|
||||||
test("post response", async () => {
|
test('post response', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const string = randomString();
|
const string = randomString();
|
||||||
|
|
||||||
const { data, status } = await Instance.post("/post", string);
|
const { data, status } = await Instance.post('/post', string);
|
||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual(string);
|
expect(data).toEqual(string);
|
||||||
})
|
});
|
||||||
|
|
||||||
test("change header and status code", async () => {
|
test('change header and status code', async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
|
|
||||||
const { data, status, headers } = await Instance.get("/header");
|
const { data, status, headers } = await Instance.get('/header');
|
||||||
|
|
||||||
expect(status).toEqual(204);
|
expect(status).toEqual(204);
|
||||||
expect(headers["itis"]).toEqual("work");
|
expect(headers['itis']).toEqual('work');
|
||||||
expect(data).toEqual("");
|
expect(data).toEqual('');
|
||||||
})
|
});
|
||||||
|
|
||||||
test("get param", async () => {
|
test('get param', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
const string = randomString();
|
const string = randomString();
|
||||||
@ -47,14 +49,14 @@ describe("Test server", () => {
|
|||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual(string);
|
expect(data).toEqual(string);
|
||||||
})
|
});
|
||||||
|
|
||||||
test("chain interrupted", async () => {
|
test('chain interrupted', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
const { data, status } = await Instance.get(`/info/foo`);
|
const { data, status } = await Instance.get(`/info/foo`);
|
||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
expect(data).toEqual("hit");
|
expect(data).toEqual('hit');
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import App from "./test-server";
|
import App from './test-server';
|
||||||
|
|
||||||
App.listen(3000);
|
App.listen(3000);
|
@ -2,23 +2,48 @@ import * as handlersJS from '../index';
|
|||||||
|
|
||||||
const App = new handlersJS.rootRouter();
|
const App = new handlersJS.rootRouter();
|
||||||
|
|
||||||
App.binding("/", App.create("GET", async () => "200 OK"));
|
App.binding(
|
||||||
|
'/',
|
||||||
|
App.create('GET', async () => '200 OK'),
|
||||||
|
);
|
||||||
|
|
||||||
App.binding("/post", App.create("POST", async (request: handlersJS.request<any>) => request.body));
|
App.binding(
|
||||||
|
'/post',
|
||||||
|
App.create(
|
||||||
|
'POST',
|
||||||
|
async (request: handlersJS.request<any>) => request.body,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
App.binding("/header", App.create("GET", async () => {
|
App.binding(
|
||||||
const response = new handlersJS.response<any>("");
|
'/header',
|
||||||
response.status = 204;
|
App.create('GET', async () => {
|
||||||
response.headers.set("itis", "work");
|
const response = new handlersJS.response<any>('');
|
||||||
return response;
|
response.status = 204;
|
||||||
}));
|
response.headers.set('itis', 'work');
|
||||||
|
return response;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
App
|
App.route('/info/(.*)')
|
||||||
.route("/info/(.*)")
|
.binding(
|
||||||
.binding("/foo", App.create("GET", (): Promise<handlersJS.response<any>> => new Promise(resolve => {
|
'/foo',
|
||||||
throw new handlersJS.response("hit")
|
App.create(
|
||||||
})))
|
'GET',
|
||||||
.binding("/(.*)", App.create("GET", async (request: handlersJS.request<any>) => request.params[0] ?? "not found"));
|
(): Promise<handlersJS.response<any>> =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
throw new handlersJS.response('hit');
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.binding(
|
||||||
|
'/(.*)',
|
||||||
|
App.create(
|
||||||
|
'GET',
|
||||||
|
async (request: handlersJS.request<any>) =>
|
||||||
|
request.params[0] ?? 'not found',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
App.useMappingAdapter();
|
App.useMappingAdapter();
|
||||||
|
|
||||||
|
@ -1,50 +1,41 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"lib": [
|
"lib": ["ESNext"],
|
||||||
"ESNext"
|
"types": [
|
||||||
],
|
"@cloudflare/workers-types",
|
||||||
"types": [
|
"@types/node",
|
||||||
"@cloudflare/workers-types",
|
"@types/jest",
|
||||||
"@types/node",
|
"bun-types"
|
||||||
"@types/jest",
|
],
|
||||||
"bun-types"
|
"skipLibCheck": true,
|
||||||
],
|
"sourceMap": true,
|
||||||
"skipLibCheck": true,
|
"outDir": "./dist",
|
||||||
"sourceMap": true,
|
"moduleResolution": "node",
|
||||||
"outDir": "./dist",
|
"removeComments": true,
|
||||||
"moduleResolution": "node",
|
"noImplicitAny": true,
|
||||||
"removeComments": true,
|
"strictNullChecks": true,
|
||||||
"noImplicitAny": true,
|
"strictFunctionTypes": true,
|
||||||
"strictNullChecks": true,
|
"noImplicitThis": true,
|
||||||
"strictFunctionTypes": true,
|
"noUnusedLocals": false,
|
||||||
"noImplicitThis": true,
|
"noUnusedParameters": false,
|
||||||
"noUnusedLocals": false,
|
"noImplicitReturns": true,
|
||||||
"noUnusedParameters": false,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitReturns": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"emitDecoratorMetadata": true,
|
||||||
"esModuleInterop": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"resolveJsonModule": true,
|
||||||
"experimentalDecorators": true,
|
"baseUrl": ".",
|
||||||
"resolveJsonModule": true,
|
"plugins": [
|
||||||
"baseUrl": ".",
|
{
|
||||||
"plugins": [
|
"transform": "@zerollup/ts-transform-paths"
|
||||||
{
|
}
|
||||||
"transform": "@zerollup/ts-transform-paths"
|
],
|
||||||
}
|
"declaration": true
|
||||||
],
|
},
|
||||||
"declaration": true
|
"exclude": ["node_modules"],
|
||||||
},
|
"include": ["index.ts", "src/**/*.ts", "demo/**/*.ts", "types/*.d.ts"]
|
||||||
"exclude": [
|
// "esm": true
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"index.ts",
|
|
||||||
"src/**/*.ts",
|
|
||||||
"demo/**/*.ts",
|
|
||||||
"types/*.d.ts"
|
|
||||||
],
|
|
||||||
// "esm": true
|
|
||||||
}
|
}
|
184
types/deno.d.ts
vendored
184
types/deno.d.ts
vendored
@ -1,109 +1,109 @@
|
|||||||
declare namespace Deno {
|
declare namespace Deno {
|
||||||
export const version: {
|
export const version: {
|
||||||
/** Deno's version. For example: `"1.0.0"` */
|
/** Deno's version. For example: `"1.0.0"` */
|
||||||
deno: string;
|
deno: string;
|
||||||
/** The V8 version used by Deno. For example: `"8.0.0.0"` */
|
/** The V8 version used by Deno. For example: `"8.0.0.0"` */
|
||||||
v8: string;
|
v8: string;
|
||||||
/** The TypeScript version used by Deno. For example: `"4.0.0"` */
|
/** The TypeScript version used by Deno. For example: `"4.0.0"` */
|
||||||
typescript: string;
|
typescript: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface NetAddr {
|
export interface NetAddr {
|
||||||
transport: "tcp" | "udp";
|
transport: 'tcp' | 'udp';
|
||||||
hostname: string;
|
hostname: string;
|
||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Addr = NetAddr;
|
export type Addr = NetAddr;
|
||||||
|
|
||||||
export interface Closer {
|
export interface Closer {
|
||||||
close(): void;
|
close(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Reader {
|
export interface Reader {
|
||||||
/** Reads up to `p.byteLength` bytes into `p`. It resolves to the number of
|
/** Reads up to `p.byteLength` bytes into `p`. It resolves to the number of
|
||||||
* bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error
|
* bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error
|
||||||
* encountered. Even if `read()` resolves to `n` < `p.byteLength`, it may
|
* encountered. Even if `read()` resolves to `n` < `p.byteLength`, it may
|
||||||
* use all of `p` as scratch space during the call. If some data is
|
* use all of `p` as scratch space during the call. If some data is
|
||||||
* available but not `p.byteLength` bytes, `read()` conventionally resolves
|
* available but not `p.byteLength` bytes, `read()` conventionally resolves
|
||||||
* to what is available instead of waiting for more.
|
* to what is available instead of waiting for more.
|
||||||
*
|
*
|
||||||
* When `read()` encounters end-of-file condition, it resolves to EOF
|
* When `read()` encounters end-of-file condition, it resolves to EOF
|
||||||
* (`null`).
|
* (`null`).
|
||||||
*
|
*
|
||||||
* When `read()` encounters an error, it rejects with an error.
|
* When `read()` encounters an error, it rejects with an error.
|
||||||
*
|
*
|
||||||
* Callers should always process the `n` > `0` bytes returned before
|
* Callers should always process the `n` > `0` bytes returned before
|
||||||
* considering the EOF (`null`). Doing so correctly handles I/O errors that
|
* considering the EOF (`null`). Doing so correctly handles I/O errors that
|
||||||
* happen after reading some bytes and also both of the allowed EOF
|
* happen after reading some bytes and also both of the allowed EOF
|
||||||
* behaviors.
|
* behaviors.
|
||||||
*
|
*
|
||||||
* Implementations should not retain a reference to `p`.
|
* Implementations should not retain a reference to `p`.
|
||||||
*
|
*
|
||||||
* Use `itereateReader` from from https://deno.land/std/streams/conversion.ts to
|
* Use `itereateReader` from from https://deno.land/std/streams/conversion.ts to
|
||||||
* turn a Reader into an AsyncIterator.
|
* turn a Reader into an AsyncIterator.
|
||||||
*/
|
*/
|
||||||
read(p: Uint8Array): Promise<number | null>;
|
read(p: Uint8Array): Promise<number | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Writer {
|
export interface Writer {
|
||||||
/** Writes `p.byteLength` bytes from `p` to the underlying data stream. It
|
/** Writes `p.byteLength` bytes from `p` to the underlying data stream. It
|
||||||
* resolves to the number of bytes written from `p` (`0` <= `n` <=
|
* resolves to the number of bytes written from `p` (`0` <= `n` <=
|
||||||
* `p.byteLength`) or reject with the error encountered that caused the
|
* `p.byteLength`) or reject with the error encountered that caused the
|
||||||
* write to stop early. `write()` must reject with a non-null error if
|
* write to stop early. `write()` must reject with a non-null error if
|
||||||
* would resolve to `n` < `p.byteLength`. `write()` must not modify the
|
* would resolve to `n` < `p.byteLength`. `write()` must not modify the
|
||||||
* slice data, even temporarily.
|
* slice data, even temporarily.
|
||||||
*
|
*
|
||||||
* Implementations should not retain a reference to `p`.
|
* Implementations should not retain a reference to `p`.
|
||||||
*/
|
*/
|
||||||
write(p: Uint8Array): Promise<number>;
|
write(p: Uint8Array): Promise<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Conn extends Reader, Writer, Closer {
|
export interface Conn extends Reader, Writer, Closer {
|
||||||
/** The local address of the connection. */
|
/** The local address of the connection. */
|
||||||
readonly localAddr: Addr;
|
readonly localAddr: Addr;
|
||||||
/** The remote address of the connection. */
|
/** The remote address of the connection. */
|
||||||
readonly remoteAddr: Addr;
|
readonly remoteAddr: Addr;
|
||||||
/** The resource ID of the connection. */
|
/** The resource ID of the connection. */
|
||||||
readonly rid: number;
|
readonly rid: number;
|
||||||
/** Shuts down (`shutdown(2)`) the write side of the connection. Most
|
/** Shuts down (`shutdown(2)`) the write side of the connection. Most
|
||||||
* callers should just use `close()`. */
|
* callers should just use `close()`. */
|
||||||
closeWrite(): Promise<void>;
|
closeWrite(): Promise<void>;
|
||||||
|
|
||||||
readonly readable: ReadableStream<Uint8Array>;
|
readonly readable: ReadableStream<Uint8Array>;
|
||||||
readonly writable: WritableStream<Uint8Array>;
|
readonly writable: WritableStream<Uint8Array>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A generic network listener for stream-oriented protocols. */
|
/** A generic network listener for stream-oriented protocols. */
|
||||||
export interface Listener extends AsyncIterable<Conn> {
|
export interface Listener extends AsyncIterable<Conn> {
|
||||||
/** Waits for and resolves to the next connection to the `Listener`. */
|
/** Waits for and resolves to the next connection to the `Listener`. */
|
||||||
accept(): Promise<Conn>;
|
accept(): Promise<Conn>;
|
||||||
/** Close closes the listener. Any pending accept promises will be rejected
|
/** Close closes the listener. Any pending accept promises will be rejected
|
||||||
* with errors. */
|
* with errors. */
|
||||||
close(): void;
|
close(): void;
|
||||||
/** Return the address of the `Listener`. */
|
/** Return the address of the `Listener`. */
|
||||||
readonly addr: Addr;
|
readonly addr: Addr;
|
||||||
|
|
||||||
/** Return the rid of the `Listener`. */
|
/** Return the rid of the `Listener`. */
|
||||||
readonly rid: number;
|
readonly rid: number;
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
|
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListenOptions {
|
export interface ListenOptions {
|
||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listen(
|
export function listen(
|
||||||
options: ListenOptions & { transport?: "tcp" }
|
options: ListenOptions & { transport?: 'tcp' },
|
||||||
): Listener;
|
): Listener;
|
||||||
|
|
||||||
export function serveHttp(conn: Conn): HttpConn;
|
export function serveHttp(conn: Conn): HttpConn;
|
||||||
|
|
||||||
export interface HttpConn extends AsyncIterable<FetchEvent> {
|
export interface HttpConn extends AsyncIterable<FetchEvent> {
|
||||||
readonly rid: number;
|
readonly rid: number;
|
||||||
|
|
||||||
nextRequest(): Promise<FetchEvent | null>;
|
nextRequest(): Promise<FetchEvent | null>;
|
||||||
close(): void;
|
close(): void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
types/txiki.d.ts
vendored
30
types/txiki.d.ts
vendored
@ -1,7 +1,7 @@
|
|||||||
type Transport = "tcp" | "udp" | "pipe";
|
type Transport = 'tcp' | 'udp' | 'pipe';
|
||||||
|
|
||||||
interface ListenOptions {
|
interface ListenOptions {
|
||||||
backlog?: number
|
backlog?: number;
|
||||||
// Disables dual stack mode.
|
// Disables dual stack mode.
|
||||||
ipv6Only?: boolean;
|
ipv6Only?: boolean;
|
||||||
// Used on UDP only. Enable address reusing (when binding). What that means is that multiple threads or processes can bind to the same address without error (provided they all set the flag) but only the last one to bind will receive any traffic, in effect "stealing" the port from the previous listener.
|
// Used on UDP only. Enable address reusing (when binding). What that means is that multiple threads or processes can bind to the same address without error (provided they all set the flag) but only the last one to bind will receive any traffic, in effect "stealing" the port from the previous listener.
|
||||||
@ -9,7 +9,13 @@ interface ListenOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare namespace tjs {
|
declare namespace tjs {
|
||||||
const versions: { curl: string; quickjs: string; tjs: string; uv: string; wasm3: string };
|
const versions: {
|
||||||
|
curl: string;
|
||||||
|
quickjs: string;
|
||||||
|
tjs: string;
|
||||||
|
uv: string;
|
||||||
|
wasm3: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface Address {
|
export interface Address {
|
||||||
readonly family: number;
|
readonly family: number;
|
||||||
@ -24,15 +30,15 @@ declare namespace tjs {
|
|||||||
readonly readable: ReadableStream<Uint8Array>;
|
readonly readable: ReadableStream<Uint8Array>;
|
||||||
|
|
||||||
readonly remoteAddress: Address;
|
readonly remoteAddress: Address;
|
||||||
readonly writeable: WritableStream<Uint8Array>
|
readonly writeable: WritableStream<Uint8Array>;
|
||||||
|
|
||||||
close(): void
|
close(): void;
|
||||||
read(buf: Uint8Array): Promise<number>
|
read(buf: Uint8Array): Promise<number>;
|
||||||
write(buf: Uint8Array): Promise<number>
|
write(buf: Uint8Array): Promise<number>;
|
||||||
|
|
||||||
setKeepAlive(enable?: boolean): void
|
setKeepAlive(enable?: boolean): void;
|
||||||
setNoDelay(enable?: boolean): void
|
setNoDelay(enable?: boolean): void;
|
||||||
shutdown(): void
|
shutdown(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Listener extends AsyncIterable<Connection> {
|
export interface Listener extends AsyncIterable<Connection> {
|
||||||
@ -48,6 +54,6 @@ declare namespace tjs {
|
|||||||
transport: Transport,
|
transport: Transport,
|
||||||
host: string,
|
host: string,
|
||||||
port?: string | number,
|
port?: string | number,
|
||||||
options?: ListenOptions
|
options?: ListenOptions,
|
||||||
): Promise<Listener>
|
): Promise<Listener>;
|
||||||
}
|
}
|
Reference in New Issue
Block a user