mirror of https://github.com/186526/handlers.js
Add CI & Deno Supported & Fix a lot of bugs & Webpack for building package
This commit is contained in:
parent
b3552cb6a2
commit
057c9b9cb5
|
@ -0,0 +1,14 @@
|
||||||
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
|
ARG VARIANT=16-bullseye
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
|
||||||
|
|
||||||
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||||
|
|
||||||
|
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
||||||
|
# ARG EXTRA_NODE_VERSION=10
|
||||||
|
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
||||||
|
|
||||||
|
# [Optional] Uncomment if you want to install more global node packages
|
||||||
|
# RUN su node -c "npm install -g <your-package-list -here>"
|
|
@ -0,0 +1,17 @@
|
||||||
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
|
ARG VARIANT=16-bullseye
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||||
|
|
||||||
|
# Install tslint, typescript. eslint is installed by javascript image
|
||||||
|
ARG NODE_MODULES="tslint-to-eslint-config typescript"
|
||||||
|
COPY library-scripts/meta.env /usr/local/etc/vscode-dev-containers
|
||||||
|
RUN su node -c "umask 0002 && npm install -g ${NODE_MODULES}" \
|
||||||
|
&& npm cache clean --force > /dev/null 2>&1
|
||||||
|
|
||||||
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||||
|
|
||||||
|
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
||||||
|
# ARG EXTRA_NODE_VERSION=10
|
||||||
|
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
|
@ -0,0 +1,39 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/typescript-node
|
||||||
|
{
|
||||||
|
"name": "Node.js & TypeScript",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
|
||||||
|
// Append -bullseye or -buster to pin to an OS version.
|
||||||
|
// Use -bullseye variants on local on arm64/Apple Silicon.
|
||||||
|
"args": {
|
||||||
|
"VARIANT": "16-bullseye"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
"customizations": {
|
||||||
|
// Configure properties specific to VS Code.
|
||||||
|
"vscode": {
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"ms-vscode.vscode-typescript-next",
|
||||||
|
"oderwat.indent-rainbow",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "yarn install",
|
||||||
|
|
||||||
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
"remoteUser": "node",
|
||||||
|
"features": {
|
||||||
|
"git": "latest"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [14.x, 16.x, 18.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Test handlers.js
|
||||||
|
run: yarn test
|
|
@ -1,3 +1,5 @@
|
||||||
{
|
{
|
||||||
"compile-hero.disable-compile-files-on-did-save-code": false
|
"compile-hero.disable-compile-files-on-did-save-code": true,
|
||||||
|
"deno.enable": false,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
}
|
}
|
|
@ -4,7 +4,6 @@ import {
|
||||||
handler,
|
handler,
|
||||||
route,
|
route,
|
||||||
response,
|
response,
|
||||||
ChainInterrupted,
|
|
||||||
} from "../index";
|
} from "../index";
|
||||||
import errorHandler from "./errorHandler";
|
import errorHandler from "./errorHandler";
|
||||||
|
|
||||||
|
@ -25,10 +24,18 @@ App.binding(
|
||||||
App.create(
|
App.create(
|
||||||
"ANY",
|
"ANY",
|
||||||
(): Promise<string> =>
|
(): Promise<string> =>
|
||||||
new Promise((resolve) => {
|
new Promise(() => {
|
||||||
console.log("Hello World!");
|
console.log("Hello World!");
|
||||||
resolve("Hello World!");
|
throw new response("Hello World!");
|
||||||
throw ChainInterrupted;
|
})
|
||||||
|
)
|
||||||
|
).binding(
|
||||||
|
"/(.*)",
|
||||||
|
App.create(
|
||||||
|
"ANY",
|
||||||
|
(): Promise<string> =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
resolve("Hello World?")
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
1
index.ts
1
index.ts
|
@ -6,7 +6,6 @@ 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 { ChainInterrupted } from "./src/interface/index";
|
|
||||||
export * as platformAdapater from "./src/platform/export";
|
export * as platformAdapater from "./src/platform/export";
|
||||||
|
|
||||||
export { rootRouter };
|
export { rootRouter };
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
export default {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
forceExit: true
|
||||||
|
};
|
23
package.json
23
package.json
|
@ -20,22 +20,35 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^3.13.0",
|
"@cloudflare/workers-types": "^3.13.0",
|
||||||
|
"@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",
|
||||||
|
"jest": "^28.1.2",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
|
"ts-jest": "^28.0.5",
|
||||||
"ts-loader": "^9.3.1",
|
"ts-loader": "^9.3.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 build:node && yarn build:webworker",
|
"build": "yarn clean && yarn build:node && yarn build:serviceworker && yarn build:cfworker && yarn build:deno",
|
||||||
"build:node": "TARGET=node webpack",
|
"build:node": "BUILD_TARGET=node webpack",
|
||||||
"build:webworker": "TARGET=webworker webpack",
|
"build:serviceworker": "BUILD_TARGET=serviceworker webpack",
|
||||||
"watch": "webpack --watch"
|
"build:cfworker": "BUILD_TARGET=cfworker webpack",
|
||||||
|
"build:deno": "BUILD_TARGET=deno webpack",
|
||||||
|
"watch": "webpack --watch",
|
||||||
|
"clean": "rm -rf ./dist",
|
||||||
|
"demo": "env NODE_ENV=development yarn build:node && node ./dist/main.node.js",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"test": "jest",
|
||||||
|
"coverage": "jest --collectCoverage --"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
},
|
||||||
|
"type": "module"
|
||||||
}
|
}
|
|
@ -1 +0,0 @@
|
||||||
console.log("Hello World!");
|
|
|
@ -3,6 +3,5 @@ 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 ChainInterrupted = new Error("ChainInterrupted");
|
|
||||||
export const AllMismatchInterrupted = new Error("AllMismatchInterrupted");
|
export const AllMismatchInterrupted = new Error("AllMismatchInterrupted");
|
||||||
export type path = string | RegExp;
|
export type path = string | RegExp;
|
||||||
|
|
|
@ -56,6 +56,6 @@ export enum methodENUM {
|
||||||
ANY = "ANY",
|
ANY = "ANY",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type method = methodENUM | string;
|
export type method = "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | "ANY" | methodENUM;
|
||||||
|
|
||||||
export default method;
|
export default method;
|
16
src/lib.ts
16
src/lib.ts
|
@ -4,17 +4,23 @@ export const platform = (() => {
|
||||||
if (typeof process != "undefined") {
|
if (typeof process != "undefined") {
|
||||||
return "Node.js";
|
return "Node.js";
|
||||||
}
|
}
|
||||||
if (typeof self != "undefined") {
|
if (typeof Deno != "undefined") {
|
||||||
return "Web Worker";
|
return "Deno";
|
||||||
}
|
}
|
||||||
|
if (typeof self != "undefined") {
|
||||||
return "Unknown";
|
return "Service Worker";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
})();
|
})();
|
||||||
export const version = (() => {
|
export const version = (() => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case "Node.js":
|
case "Node.js":
|
||||||
return process.version;
|
return process.version;
|
||||||
|
case "Deno":
|
||||||
|
return Deno.version.deno;
|
||||||
|
case "Service Worker":
|
||||||
|
return undefined;
|
||||||
default:
|
default:
|
||||||
return "Unknown";
|
return undefined;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { SWPlatformAdapter } from "./serviceworker";
|
||||||
|
import { platformAdapater } from "./index";
|
||||||
|
|
||||||
|
import { request } from "../interface/request";
|
||||||
|
import { headers } from "../interface/headers";
|
||||||
|
import { methodENUM } from "src/interface/method";
|
||||||
|
|
||||||
|
|
||||||
|
const DefaultConn: Deno.Conn = {
|
||||||
|
localAddr: {
|
||||||
|
transport: "tcp",
|
||||||
|
hostname: "0.0.0.0",
|
||||||
|
port: 80,
|
||||||
|
},
|
||||||
|
remoteAddr: {
|
||||||
|
transport: "tcp",
|
||||||
|
hostname: "0.0.0.0",
|
||||||
|
port: 80,
|
||||||
|
},
|
||||||
|
rid: 0,
|
||||||
|
closeWrite: async () => undefined,
|
||||||
|
readable: "",
|
||||||
|
writable: "",
|
||||||
|
read: async (p: Uint8Array) => null,
|
||||||
|
write: async (p: Uint8Array) => 0,
|
||||||
|
close: () => undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DenoPlatformAdapter<T = any, K = any>
|
||||||
|
extends SWPlatformAdapter<T, K>
|
||||||
|
implements platformAdapater<T, K>
|
||||||
|
{
|
||||||
|
async listen(port: number): Promise<void> {
|
||||||
|
const Server: Deno.Listener = Deno.listen({ port });
|
||||||
|
|
||||||
|
for await (const connection of Server) {
|
||||||
|
const httpConnection = Deno.serveHttp(connection);
|
||||||
|
|
||||||
|
for await (const requestEvent of httpConnection) {
|
||||||
|
requestEvent.respondWith(this.handler(requestEvent, connection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleRequest(nativeRequest: Request, connection: Deno.Conn = DefaultConn): Promise<request<T>> {
|
||||||
|
const requestHeaders = new headers(
|
||||||
|
Object.fromEntries(nativeRequest.headers.entries())
|
||||||
|
);
|
||||||
|
const requestMessage: request<T> = new request(
|
||||||
|
<methodENUM>nativeRequest.method,
|
||||||
|
new URL(nativeRequest.url),
|
||||||
|
requestHeaders,
|
||||||
|
await nativeRequest.text(),
|
||||||
|
{},
|
||||||
|
`${connection.remoteAddr.hostname}:${connection.remoteAddr.port}` || ""
|
||||||
|
);
|
||||||
|
return requestMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handler(event: FetchEvent, connection: Deno.Conn = DefaultConn): Promise<Response> {
|
||||||
|
return await this.handleResponse(
|
||||||
|
await this.handleRequest(event.request, connection).then((request) =>
|
||||||
|
this.router.respond(request)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
import { NodePlatformAdapter } from "./node";
|
import { NodePlatformAdapter } from "./node";
|
||||||
import { SWPlatformAdapter } from "./serviceworker";
|
import { SWPlatformAdapter } from "./serviceworker";
|
||||||
|
import { DenoPlatformAdapter } from "./deno";
|
||||||
|
|
||||||
export const platformAdapaterMapping = {
|
export const platformAdapaterMapping = {
|
||||||
"Node.js": NodePlatformAdapter,
|
"Node.js": NodePlatformAdapter,
|
||||||
"Web Worker": SWPlatformAdapter,
|
"Service Worker": SWPlatformAdapter,
|
||||||
|
"Deno": DenoPlatformAdapter,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { NodePlatformAdapter, SWPlatformAdapter };
|
export { NodePlatformAdapter, SWPlatformAdapter, DenoPlatformAdapter };
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { request, response } from "../interface";
|
import { request, response } from "../interface/index";
|
||||||
import { router } from "../../index";
|
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>;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { platformAdapater } from "./index";
|
import { platformAdapater } from "./index";
|
||||||
import { request, response } from "../interface";
|
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";
|
||||||
|
|
||||||
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>;
|
||||||
|
@ -54,7 +55,7 @@ export class NodePlatformAdapter<T = any, K = any> implements platformAdapater {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new request<T>(
|
return new request<T>(
|
||||||
nativeRequest.method,
|
<methodENUM>nativeRequest.method,
|
||||||
new URL(
|
new URL(
|
||||||
nativeRequest.url,
|
nativeRequest.url,
|
||||||
`http://${requestHeaders.get("host") ?? "localhost"}`
|
`http://${requestHeaders.get("host") ?? "localhost"}`
|
||||||
|
|
|
@ -4,6 +4,8 @@ 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";
|
||||||
|
|
||||||
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>;
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ export class SWPlatformAdapter<T = any, K = any> implements platformAdapater {
|
||||||
Object.fromEntries(nativeRequest.headers.entries())
|
Object.fromEntries(nativeRequest.headers.entries())
|
||||||
);
|
);
|
||||||
const requestMessage: request<T> = new request(
|
const requestMessage: request<T> = new request(
|
||||||
nativeRequest.method,
|
<methodENUM>nativeRequest.method,
|
||||||
new URL(nativeRequest.url),
|
new URL(nativeRequest.url),
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
await nativeRequest.text(),
|
await nativeRequest.text(),
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
path,
|
path,
|
||||||
response,
|
response,
|
||||||
request,
|
request,
|
||||||
ChainInterrupted,
|
|
||||||
AllMismatchInterrupted,
|
AllMismatchInterrupted,
|
||||||
responder,
|
responder,
|
||||||
method,
|
method,
|
||||||
|
@ -132,8 +131,8 @@ export class router<K = any, V = any> {
|
||||||
throw AllMismatchInterrupted;
|
throw AllMismatchInterrupted;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e === ChainInterrupted) {
|
if (e instanceof response) {
|
||||||
return e.response;
|
throw e;
|
||||||
}
|
}
|
||||||
if (e === AllMismatchInterrupted) mismatchCount++;
|
if (e === AllMismatchInterrupted) mismatchCount++;
|
||||||
else {
|
else {
|
||||||
|
@ -191,8 +190,8 @@ export class rootRouter<K = any, V = any> extends router<K, V> {
|
||||||
try {
|
try {
|
||||||
responseMessage = await this._respond(request, responseMessage);
|
responseMessage = await this._respond(request, responseMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e === ChainInterrupted) {
|
if (e instanceof response) {
|
||||||
return responseMessage;
|
return e;
|
||||||
} else if (e === AllMismatchInterrupted) {
|
} else if (e === AllMismatchInterrupted) {
|
||||||
responseMessage =
|
responseMessage =
|
||||||
(await this.errorResponder(404, "404 Not Found\n")(request)) ??
|
(await this.errorResponder(404, "404 Not Found\n")(request)) ??
|
||||||
|
@ -214,6 +213,7 @@ export class rootRouter<K = any, V = any> extends router<K, V> {
|
||||||
useMappingAdapter(
|
useMappingAdapter(
|
||||||
mapping: { [platform: string]: platformAdapaterConstructor } = platformAdapaterMapping
|
mapping: { [platform: string]: platformAdapaterConstructor } = platformAdapaterMapping
|
||||||
): this {
|
): this {
|
||||||
|
if (typeof platform == "undefined") throw new Error("Cannot detect platform");
|
||||||
if (mapping[platform] == undefined) throw new Error("Platform not found in mapping");
|
if (mapping[platform] == undefined) throw new Error("Platform not found in mapping");
|
||||||
else this.useAdapater(mapping[platform]);
|
else this.useAdapater(mapping[platform]);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import _ from "./test-server";
|
||||||
|
|
||||||
|
import Axios from "axios";
|
||||||
|
|
||||||
|
_.listen(3000);
|
||||||
|
|
||||||
|
const Instance = Axios.create({
|
||||||
|
baseURL: "http://localhost:3000"
|
||||||
|
})
|
||||||
|
|
||||||
|
const randomString = () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
|
describe("Test server", () => {
|
||||||
|
test("normal 200 response", async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
|
||||||
|
const { data, status } = await Instance.get("/");
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(data).toEqual("200 OK");
|
||||||
|
})
|
||||||
|
|
||||||
|
test("post response", async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
const string = randomString();
|
||||||
|
|
||||||
|
const { data, status } = await Instance.post("/post", string);
|
||||||
|
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(data).toEqual(string);
|
||||||
|
})
|
||||||
|
|
||||||
|
test("change header and status code", async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
|
||||||
|
const { data, status, headers } = await Instance.get("/header");
|
||||||
|
|
||||||
|
expect(status).toEqual(204);
|
||||||
|
expect(headers["itis"]).toEqual("work");
|
||||||
|
expect(data).toEqual("");
|
||||||
|
})
|
||||||
|
|
||||||
|
test("get param", async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
|
||||||
|
const string = randomString();
|
||||||
|
const { data, status } = await Instance.get(`/info/${string}`);
|
||||||
|
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(data).toEqual(string);
|
||||||
|
})
|
||||||
|
|
||||||
|
test("chain interrupted", async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
|
||||||
|
const { data, status } = await Instance.get(`/info/foo`);
|
||||||
|
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(data).toEqual("hit");
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,25 @@
|
||||||
|
import * as handlersJS from '../index';
|
||||||
|
|
||||||
|
const App = new handlersJS.rootRouter();
|
||||||
|
|
||||||
|
App.binding("/", App.create("GET", async () => "200 OK"));
|
||||||
|
|
||||||
|
App.binding("/post", App.create("POST", async (request: handlersJS.request<any>) => request.body));
|
||||||
|
|
||||||
|
App.binding("/header", App.create("GET", async () => {
|
||||||
|
const response = new handlersJS.response<any>("");
|
||||||
|
response.status = 204;
|
||||||
|
response.headers.set("itis", "work");
|
||||||
|
return response;
|
||||||
|
}));
|
||||||
|
|
||||||
|
App
|
||||||
|
.route("/info/(.*)")
|
||||||
|
.binding("/foo", App.create("GET", (): 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();
|
||||||
|
|
||||||
|
export default App;
|
|
@ -1,14 +1,9 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"module": "commonjs",
|
"module": "esnext",
|
||||||
"lib": [
|
"lib": ["ESNext"],
|
||||||
"ESNext",
|
"types": ["@cloudflare/workers-types", "@types/node", "@types/jest"],
|
||||||
],
|
|
||||||
"types": [
|
|
||||||
"@cloudflare/workers-types",
|
|
||||||
"@types/node",
|
|
||||||
],
|
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
|
@ -28,18 +23,12 @@
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"plugins": [
|
"plugins": [{
|
||||||
{
|
"transform": "@zerollup/ts-transform-paths"
|
||||||
"transform": "@zerollup/ts-transform-paths",
|
}],
|
||||||
}
|
"declaration": true
|
||||||
],
|
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["node_modules"],
|
||||||
"node_modules"
|
"include": ["index.ts", "src/**/*.ts", "demo/**/*.ts", "types/deno.d.ts"],
|
||||||
],
|
// "esm": true
|
||||||
"include": [
|
|
||||||
"index.ts",
|
|
||||||
"src/**/*.ts",
|
|
||||||
"demo/**/*.ts",
|
|
||||||
],
|
|
||||||
}
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
declare namespace Deno {
|
||||||
|
export const version: {
|
||||||
|
/** Deno's version. For example: `"1.0.0"` */
|
||||||
|
deno: string;
|
||||||
|
/** The V8 version used by Deno. For example: `"8.0.0.0"` */
|
||||||
|
v8: string;
|
||||||
|
/** The TypeScript version used by Deno. For example: `"4.0.0"` */
|
||||||
|
typescript: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface NetAddr {
|
||||||
|
transport: "tcp" | "udp";
|
||||||
|
hostname: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Addr = NetAddr;
|
||||||
|
|
||||||
|
export interface Closer {
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Reader {
|
||||||
|
/** 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
|
||||||
|
* 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
|
||||||
|
* available but not `p.byteLength` bytes, `read()` conventionally resolves
|
||||||
|
* to what is available instead of waiting for more.
|
||||||
|
*
|
||||||
|
* When `read()` encounters end-of-file condition, it resolves to EOF
|
||||||
|
* (`null`).
|
||||||
|
*
|
||||||
|
* When `read()` encounters an error, it rejects with an error.
|
||||||
|
*
|
||||||
|
* Callers should always process the `n` > `0` bytes returned before
|
||||||
|
* considering the EOF (`null`). Doing so correctly handles I/O errors that
|
||||||
|
* happen after reading some bytes and also both of the allowed EOF
|
||||||
|
* behaviors.
|
||||||
|
*
|
||||||
|
* Implementations should not retain a reference to `p`.
|
||||||
|
*
|
||||||
|
* Use `itereateReader` from from https://deno.land/std/streams/conversion.ts to
|
||||||
|
* turn a Reader into an AsyncIterator.
|
||||||
|
*/
|
||||||
|
read(p: Uint8Array): Promise<number | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Writer {
|
||||||
|
/** Writes `p.byteLength` bytes from `p` to the underlying data stream. It
|
||||||
|
* resolves to the number of bytes written from `p` (`0` <= `n` <=
|
||||||
|
* `p.byteLength`) or reject with the error encountered that caused the
|
||||||
|
* write to stop early. `write()` must reject with a non-null error if
|
||||||
|
* would resolve to `n` < `p.byteLength`. `write()` must not modify the
|
||||||
|
* slice data, even temporarily.
|
||||||
|
*
|
||||||
|
* Implementations should not retain a reference to `p`.
|
||||||
|
*/
|
||||||
|
write(p: Uint8Array): Promise<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Conn extends Reader, Writer, Closer {
|
||||||
|
/** The local address of the connection. */
|
||||||
|
readonly localAddr: Addr;
|
||||||
|
/** The remote address of the connection. */
|
||||||
|
readonly remoteAddr: Addr;
|
||||||
|
/** The resource ID of the connection. */
|
||||||
|
readonly rid: number;
|
||||||
|
/** Shuts down (`shutdown(2)`) the write side of the connection. Most
|
||||||
|
* callers should just use `close()`. */
|
||||||
|
closeWrite(): Promise<void>;
|
||||||
|
|
||||||
|
readonly readable: ReadableStream<Uint8Array>;
|
||||||
|
readonly writable: WritableStream<Uint8Array>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A generic network listener for stream-oriented protocols. */
|
||||||
|
export interface Listener extends AsyncIterable<Conn> {
|
||||||
|
/** Waits for and resolves to the next connection to the `Listener`. */
|
||||||
|
accept(): Promise<Conn>;
|
||||||
|
/** Close closes the listener. Any pending accept promises will be rejected
|
||||||
|
* with errors. */
|
||||||
|
close(): void;
|
||||||
|
/** Return the address of the `Listener`. */
|
||||||
|
readonly addr: Addr;
|
||||||
|
|
||||||
|
/** Return the rid of the `Listener`. */
|
||||||
|
readonly rid: number;
|
||||||
|
|
||||||
|
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListenOptions {
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listen(
|
||||||
|
options: ListenOptions & { transport?: "tcp" }
|
||||||
|
): Listener;
|
||||||
|
|
||||||
|
export function serveHttp(conn: Conn): HttpConn;
|
||||||
|
|
||||||
|
export interface HttpConn extends AsyncIterable<FetchEvent> {
|
||||||
|
readonly rid: number;
|
||||||
|
|
||||||
|
nextRequest(): Promise<FetchEvent | null>;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,20 @@
|
||||||
// Generated using webpack-cli https://github.com/webpack/webpack-cli
|
// Generated using webpack-cli https://github.com/webpack/webpack-cli
|
||||||
|
|
||||||
const path = require("path");
|
import path from "path";
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV == "production";
|
const isProduction = process.env.NODE_ENV == "production";
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
chunkFormat: "module",
|
||||||
|
library: {
|
||||||
|
type: 'module',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// Add your plugins here
|
// Add your plugins here
|
||||||
|
@ -30,9 +38,12 @@ const config = {
|
||||||
"http": false
|
"http": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
experiments: {
|
||||||
|
outputModule: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = () => {
|
export default () => {
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
config.mode = "production";
|
config.mode = "production";
|
||||||
config.entry = "./index.ts";
|
config.entry = "./index.ts";
|
||||||
|
@ -40,14 +51,23 @@ module.exports = () => {
|
||||||
config.mode = "development";
|
config.mode = "development";
|
||||||
config.entry = "./demo/index.ts";
|
config.entry = "./demo/index.ts";
|
||||||
}
|
}
|
||||||
switch(process.env.TARGET) {
|
switch (process.env.BUILD_TARGET) {
|
||||||
case "node":
|
case "node":
|
||||||
config.target = "node14";
|
config.target = "node12";
|
||||||
config.output.filename = "main.node.js";
|
config.output.filename = "main.node.js";
|
||||||
break;
|
break;
|
||||||
case "webworker":
|
case "serviceworker":
|
||||||
config.target = "webworker";
|
config.target = "webworker";
|
||||||
config.output.filename = "main.webworker.js";
|
config.output.filename = "main.serviceworker.js";
|
||||||
|
break;
|
||||||
|
case "deno":
|
||||||
|
config.target = "webworker";
|
||||||
|
config.output.filename = "main.deno.js";
|
||||||
|
break;
|
||||||
|
case "cfworker":
|
||||||
|
config.mode = "production";
|
||||||
|
config.target = "webworker";
|
||||||
|
config.output.filename = "main.cfworker.js";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
config.target = "es6";
|
config.target = "es6";
|
||||||
|
|
Loading…
Reference in New Issue