diff --git a/.github/workflows/publish-pre.yml b/.github/workflows/publish-pre.yml index ae41e5f..469eece 100644 --- a/.github/workflows/publish-pre.yml +++ b/.github/workflows/publish-pre.yml @@ -8,11 +8,12 @@ jobs: publish-npm: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 12 + node-version: 16 registry-url: https://registry.npmjs.org/ + - run: npm run build - run: npm publish --tag pre --access public env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} @@ -23,8 +24,9 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 12 + node-version: 16 registry-url: https://npm.pkg.github.com/ + - run: npm run build - run: npm publish --tag pre --access public env: NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..5b679ff --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +.github/ +src/ +test/ +MIGRATION.md +tsconfig.json \ No newline at end of file diff --git a/README.md b/README.md index b5c25cb..bb8c822 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ See [Migration Guide](https://github.com/tsndr/cloudflare-worker-router/blob/v2- --- -Cloudflare Workers Router is a super lightweight router (3.6 kB) with middleware support and ZERO dependencies for Cloudflare Workers. +Cloudflare Workers Router is a super lightweight router (2.30 KiB) with middleware support and **ZERO dependencies** for [Cloudflare Workers](https://workers.cloudflare.com/). When I was trying out Cloudflare Workers I almost immediately noticed how fast it was compared to other serverless offerings. So I wanted to build a full-fledged API to see how it performs doing real work, but since I wasn't able to find a router that suited my needs I created my own. @@ -91,7 +91,7 @@ router.delete('/user/:id', ({ req, res, next }) => { // Listen Cloudflare Workers Fetch Event export default { - async fetch(request, env, ctx) { + async fetch(request, env) { return router.handle(env, request) } } diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 5922146..0000000 --- a/index.d.ts +++ /dev/null @@ -1,372 +0,0 @@ -export = Router -/** - * Router - * - * @class - * @constructor - * @public - */ -declare class Router { - /** - * Router Array - * - * @protected - * @type {Route[]} - */ - protected routes: Route[] - /** - * Global Handlers - * - * @protected - * @type {RouterHandler[]} - */ - protected globalHandlers: RouterHandler[] - /** - * Debug Mode - * - * @protected - * @type {boolean=false} - */ - protected debugMode: boolean = false - /** - * CORS Config - * - * @protected - * @type {RouterCorsConfig} - */ - protected corsConfig: RouterCorsConfig - /** - * Route Object - * - * @typedef Route - * @property {string} method HTTP request method - * @property {string} url URL String - * @property {RouterHandler[]} handlers Array of handler functions - */ - - /** - * Router Context - * - * @typedef RouterContext - * @property {Object} env Environment - * @property {RouterRequest} req Request Object - * @property {RouterResponse} res Response Object - * @property {RouterNext} next Next Handler - */ - /** - * Request Object - * - * @typedef RouterRequest - * @property {string} method HTTP request method - * @property {Object} params Object containing all parameters defined in the url string - * @property {Object} query Object containing all query parameters - * @property {Headers} headers Request headers object - * @property {Object | string} body Only available if method is `POST`, `PUT`, `PATCH` or `DELETE`. Contains either the received body string or a parsed object if valid JSON was sent. - * @property {Object} cf object containing custom Cloudflare properties. (https://developers.cloudflare.com/workers/examples/accessing-the-cloudflare-object) - */ - /** - * Response Object - * - * @typedef RouterResponse - * @property {Headers} headers Response headers object - * @property {number} status Return status code (default: `204`) - * @property {Object | string} body Either an `object` (will be converted to JSON) or a string - * @property {Response} raw A response object that is to be returned, this will void all other res properties and return this as is. - */ - /** - * Next Function - * - * @callback RouterNext - * @returns {Promise} - */ - /** - * Handler Function - * - * @callback RouterHandler - * @param {RouterContext} ctx - */ - /** - * Register global handler - * - * @param {RouterHandler} handler - * @param handlers - */ - use(handler: RouterHandler): Router - /** - * Register CONNECT route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - connect(url: string, ...handlers: RouterHandler[]): Router - /** - * Register DELETE route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - delete(url: string, ...handlers: RouterHandler[]): Router - /** - * Register GET route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - get(url: string, ...handlers: RouterHandler[]): Router - /** - * Register HEAD route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - head(url: string, ...handlers: RouterHandler[]): Router - /** - * Register OPTIONS route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - options(url: string, ...handlers: RouterHandler[]): Router - /** - * Register PATCH route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - patch(url: string, ...handlers: RouterHandler[]): Router - /** - * Register POST route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - post(url: string, ...handlers: RouterHandler[]): Router - /** - * Register PUT route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - put(url: string, ...handlers: RouterHandler[]): Router - /** - * Register TRACE route - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - trace(url: string, ...handlers: RouterHandler[]): Router - /** - * Register route, ignoring method - * - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - any(url: string, ...handlers: RouterHandler[]): Router - /** - * Register route, ignoring method - * - * @deprecated since version 1.0.2, use .any(url, ...handlers) instead. - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - all(url: string, ...handlers: RouterHandler[]): Router - /** - * CORS Config - * - * @typedef RouterCorsConfig - * @property {string} allowOrigin Access-Control-Allow-Origin (default: `*`) - * @property {string} allowMethods Access-Control-Allow-Methods (default: `*`) - * @property {string} allowHeaders Access-Control-Allow-Headers (default: `*`) - * @property {number} maxAge Access-Control-Max-Age (default: `86400`) - * @property {number} optionsSuccessStatus Return status code for OPTIONS request (default: `204`) - */ - /** - * Debug Mode - * - * @param {boolean} [state=true] Whether to turn on or off debug mode (default: true) - */ - debug(state?: boolean): void - /** - * Enable CORS support - * - * @param {RouterCorsConfig} config - * @returns {Router} - */ - cors(config?: RouterCorsConfig): Router - /** - * Register route - * - * @private - * @param {string} method HTTP request method - * @param {string} url URL String - * @param {RouterHandler[]} handlers Arrar of handler functions - * @returns {Router} - */ - private register(method: string, url: string, handlers: RouterHandler[]): Router - /** - * Get Route by request - * - * @private - * @param {Request} request - * @returns {Route | undefined} - */ - private getRoute(request: Request): Route | undefined - /** - * Handle requests - * - * @param {any} env - * @param {Request} request - * @param {any=} extend - * @returns {Response} - */ - handle(env: any, request: Request, extend?: any): Response -} -declare namespace Router { - export { Route, RouterCorsConfig, RouterHandler, RouterContext, RouterRequest, RouterResponse, RouterNext } -} -/** - * Route Object - */ -type Route = { - /** - * HTTP request method - */ - method: string - /** - * URL String - */ - url: string - /** - * Array of handler functions - */ - handlers: RouterHandler[] -} -/** - * CORS Config - */ -type RouterCorsConfig = { - /** - * Access-Control-Allow-Origin (default: `*`) - */ - allowOrigin: string - /** - * Access-Control-Allow-Methods (default: `*`) - */ - allowMethods: string - /** - * Access-Control-Allow-Headers (default: `*`) - */ - allowHeaders: string - /** - * Access-Control-Max-Age (default: `86400`) - */ - maxAge: number - /** - * Return status code for OPTIONS request (default: `204`) - */ - optionsSuccessStatus: number -} -/** - * Handler Function - */ -type RouterHandler = (ctx: RouterContext) => any - -/** - * Router Context - */ -type RouterContext = { - /** - * Environment - */ - env: Object - - /** - * Request Object - */ - req: RouterRequest - - /** - * Response Object - */ - res: RouterResponse - - /** - * Next Handler - */ - next: RouterNext -} - -/** - * Request Object - */ -type RouterRequest = { - /** - * HTTP request method - */ - method: string - /** - * Object containing request headers - */ - headers: Headers - /** - * URL String - */ - url: string - /** - * Object containing all parameters defined in the url string - */ - params: { - [key: string]: string - } - /** - * Only available if method is `POST`, `PUT` or `PATCH`. Contains either the received body string or a parsed object if valid JSON was sent. - */ - body: any - - /** - * Make it user extendable - */ - [key: string]: any -} -/** - * Response Object - */ -type RouterResponse = { - /** - * Object you can set response headers in - */ - headers: Headers - /** - * Return status code (default: `204`) - */ - status: number - /** - * A response object to be directly returned to the client - */ - response: Response - /** - * Either an `object` (will be converted to JSON) or a string - */ - body: { - [key: string]: any - } | string - /** - * Upgraded websocket connection - */ - webSocket?: WebSocket -} -/** - * Next Function - */ -type RouterNext = () => Promise diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..df99b22 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,50 @@ +{ + "name": "@tsndr/cloudflare-worker-router", + "version": "2.0.0-pre.7", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@tsndr/cloudflare-worker-router", + "version": "2.0.0-pre.7", + "license": "MIT", + "devDependencies": { + "@cloudflare/workers-types": "^3.13.0", + "typescript": "^4.7.4" + } + }, + "node_modules/@cloudflare/workers-types": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-3.13.0.tgz", + "integrity": "sha512-oyhzfYlWBLgd9odJ/WHcsD/8B+IaAjSD+OcPEGLzX5kGRONjwcW3NY0WQfsVIhQzZ6AbPzjwkmj4D2VFwU1xRQ==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "@cloudflare/workers-types": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-3.13.0.tgz", + "integrity": "sha512-oyhzfYlWBLgd9odJ/WHcsD/8B+IaAjSD+OcPEGLzX5kGRONjwcW3NY0WQfsVIhQzZ6AbPzjwkmj4D2VFwU1xRQ==", + "dev": true + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json index a02a4e3..87e71b4 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,11 @@ "name": "@tsndr/cloudflare-worker-router", "version": "2.0.0-pre.7", "description": "", - "main": "index.js", - "types": "index.d.ts", - "scripts": {}, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "rm -rf dist/* && tsc" + }, "repository": { "type": "git", "url": "git+https://github.com/tsndr/cloudflare-worker-router.git" @@ -27,5 +29,9 @@ "bugs": { "url": "https://github.com/tsndr/cloudflare-worker-router/issues" }, - "homepage": "https://github.com/tsndr/cloudflare-worker-router#readme" + "homepage": "https://github.com/tsndr/cloudflare-worker-router#readme", + "devDependencies": { + "@cloudflare/workers-types": "^3.13.0", + "typescript": "^4.7.4" + } } diff --git a/index.js b/src/index.ts similarity index 54% rename from index.js rename to src/index.ts index 783333e..bfed327 100644 --- a/index.js +++ b/src/index.ts @@ -1,3 +1,120 @@ +/** + * Route Object + * + * @typedef Route + * @property {string} method HTTP request method + * @property {string} url URL String + * @property {RouterHandler[]} handlers Array of handler functions + */ + interface Route { + method: string + url: string + handlers: RouterHandler[] +} + +/** + * Router Context + * + * @typedef RouterContext + * @property {Object} env Environment + * @property {RouterRequest} req Request Object + * @property {RouterResponse} res Response Object + * @property {RouterNext} next Next Handler + */ +interface RouterContext { + env: any + req: RouterRequest + res: RouterResponse + next: RouterNext +} + +/** + * Request Object + * + * @typedef RouterRequest + * @property {string} url URL + * @property {string} method HTTP request method + * @property {RouterRequestParams} params Object containing all parameters defined in the url string + * @property {RouterRequestQuery} query Object containing all query parameters + * @property {Headers} headers Request headers object + * @property {any} body Only available if method is `POST`, `PUT`, `PATCH` or `DELETE`. Contains either the received body string or a parsed object if valid JSON was sent. + * @property {IncomingRequestCfProperties=} cf object containing custom Cloudflare properties. (https://developers.cloudflare.com/workers/examples/accessing-the-cloudflare-object) + */ +interface RouterRequest { + url: string + method: string + params: RouterRequestParams + query: RouterRequestQuery + headers: Headers + body: any + cf?: IncomingRequestCfProperties +} + +interface RouterRequestParams { + [key: string]: string +} + +interface RouterRequestQuery { + [key: string]: string +} + +/** + * Response Object + * + * @typedef RouterResponse + * @property {Headers} headers Response headers object + * @property {number=204} status Return status code (default: `204`) + * @property {any=} body Either an `object` (will be converted to JSON) or a string + * @property {Response=} raw A response object that is to be returned, this will void all other res properties and return this as is. + */ +interface RouterResponse { + headers: Headers + status?: number + body?: any + raw?: Response, + webSocket?: WebSocket +} + +/** + * Next Function + * + * @callback RouterNext + * @returns {Promise} + */ +interface RouterNext { + (): Promise +} + +/** + * Handler Function + * + * @callback RouterHandler + * @param {RouterContext} ctx + * @returns {Promise | void} + */ +interface RouterHandler { + (ctx: RouterContext): Promise | void +} + +/** + * CORS Config + * + * @typedef RouterCorsConfig + * @property {string} allowOrigin Access-Control-Allow-Origin (default: `*`) + * @property {string} allowMethods Access-Control-Allow-Methods (default: `*`) + * @property {string} allowHeaders Access-Control-Allow-Headers (default: `*`) + * @property {number} maxAge Access-Control-Max-Age (default: `86400`) + * @property {number} optionsSuccessStatus Return status code for OPTIONS request (default: `204`) + */ +interface RouterCorsConfig { + allowOrigin: string + allowMethods: string + allowHeaders: string + maxAge: number + optionsSuccessStatus: number +} + + /** * Router * @@ -7,99 +124,51 @@ */ class Router { - constructor() { - /** - * Router Array - * - * @protected - * @type {Route[]} - */ - this.routes = [] + /** + * Router Array + * + * @protected + * @type {Route[]} + */ + protected routes: Route[] = [] - /** - * Global Handlers - */ - this.globalHandlers = [] + /** + * Global Handlers + */ + protected globalHandlers: RouterHandler[] = [] - /** - * Debug Mode - * - * @protected - * @type {boolean} - */ - this.debugMode = false + /** + * Debug Mode + * + * @protected + * @type {boolean} + */ + protected debugMode: boolean = false - /** - * CORS Config - * - * @protected - * @type {RouterCorsConfig} - */ - this.corsConfig = {} + /** + * CORS Config + * + * @protected + * @type {RouterCorsConfig} + */ + protected corsConfig: RouterCorsConfig = { + allowOrigin: '*', + allowMethods: '*', + allowHeaders: '*', + maxAge: 86400, + optionsSuccessStatus: 204 } /** - * Route Object + * Register global handlers * - * @typedef Route - * @property {string} method HTTP request method - * @property {string} url URL String - * @property {RouterHandler[]} handlers Array of handler functions + * @param {RouterHandler[]} handlers + * @returns {Router} */ - - /** - * Router Context - * - * @typedef RouterContext - * @property {Object} env Environment - * @property {RouterRequest} req Request Object - * @property {RouterResponse} res Response Object - * @property {RouterNext} next Next Handler - */ - - /** - * Request Object - * - * @typedef RouterRequest - * @property {string} method HTTP request method - * @property {Object} params Object containing all parameters defined in the url string - * @property {Object} query Object containing all query parameters - * @property {Headers} headers Request headers object - * @property {Object | string} body Only available if method is `POST`, `PUT`, `PATCH` or `DELETE`. Contains either the received body string or a parsed object if valid JSON was sent. - * @property {Object} cf object containing custom Cloudflare properties. (https://developers.cloudflare.com/workers/examples/accessing-the-cloudflare-object) - */ - - /** - * Response Object - * - * @typedef RouterResponse - * @property {Headers} headers Response headers object - * @property {number} status Return status code (default: `204`) - * @property {Object | string} body Either an `object` (will be converted to JSON) or a string - * @property {Response} raw A response object that is to be returned, this will void all other res properties and return this as is. - */ - - /** - * Next Function - * - * @callback RouterNext - * @returns {Promise} - */ - - /** - * Handler Function - * - * @callback RouterHandler - * @param {RouterContext} ctx - */ - - /** - * Register global handler - * - * @param {RouterHandler} handler - */ - use(handlers) { - this.globalHandlers.push(handlers) + public use(...handlers: RouterHandler[]): Router { + for (let handler of handlers) { + this.globalHandlers.push(handler) + } return this } @@ -107,10 +176,10 @@ class Router { * Register CONNECT route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - connect(url, ...handlers) { + public connect(url: string, ...handlers: RouterHandler[]): Router { return this.register('CONNECT', url, handlers) } @@ -118,10 +187,10 @@ class Router { * Register DELETE route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - delete(url, ...handlers) { + public delete(url: string, ...handlers: RouterHandler[]): Router { return this.register('DELETE', url, handlers) } @@ -129,10 +198,10 @@ class Router { * Register GET route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - get(url, ...handlers) { + public get(url: string, ...handlers: RouterHandler[]): Router { return this.register('GET', url, handlers) } @@ -140,10 +209,10 @@ class Router { * Register HEAD route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - head(url, ...handlers) { + public head(url: string, ...handlers: RouterHandler[]): Router { return this.register('HEAD', url, handlers) } @@ -151,10 +220,10 @@ class Router { * Register OPTIONS route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - options(url, ...handlers) { + public options(url: string, ...handlers: RouterHandler[]): Router { return this.register('OPTIONS', url, handlers) } @@ -162,10 +231,10 @@ class Router { * Register PATCH route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - patch(url, ...handlers) { + public patch(url: string, ...handlers: RouterHandler[]): Router { return this.register('PATCH', url, handlers) } @@ -173,10 +242,10 @@ class Router { * Register POST route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - post(url, ...handlers) { + public post(url: string, ...handlers: RouterHandler[]): Router { return this.register('POST', url, handlers) } @@ -184,10 +253,10 @@ class Router { * Register PUT route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - put(url, ...handlers) { + public put(url: string, ...handlers: RouterHandler[]): Router { return this.register('PUT', url, handlers) } @@ -195,10 +264,10 @@ class Router { * Register TRACE route * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - trace(url, ...handlers) { + public trace(url: string, ...handlers: RouterHandler[]): Router { return this.register('TRACE', url, handlers) } @@ -206,53 +275,29 @@ class Router { * Register route, ignoring method * * @param {string} url - * @param {...RouterHandler} handlers + * @param {RouterHandler[]} handlers * @returns {Router} */ - any(url, ...handlers) { + public any(url: string, ...handlers: RouterHandler[]): Router { return this.register('*', url, handlers) } - /** - * Register route, ignoring method - * - * @deprecated since version 1.0.2, use .any(url, ...handlers) instead. - * @param {string} url - * @param {...RouterHandler} handlers - * @returns {Router} - */ - all(url, ...handlers) { - console.warn('WARNING: This function is deprecated and will be removed in a future release, please use .any(url, ...handlers) instead.') - return this.any(url, handlers) - } - /** * Debug Mode * * @param {boolean} [state=true] Whether to turn on or off debug mode (default: true) */ - debug(state = true) { + public debug(state = true) { this.debugMode = state } - /** - * CORS Config - * - * @typedef RouterCorsConfig - * @property {string} allowOrigin Access-Control-Allow-Origin (default: `*`) - * @property {string} allowMethods Access-Control-Allow-Methods (default: `*`) - * @property {string} allowHeaders Access-Control-Allow-Headers (default: `*`) - * @property {number} maxAge Access-Control-Max-Age (default: `86400`) - * @property {number} optionsSuccessStatus Return status code for OPTIONS request (default: `204`) - */ - /** * Enable CORS support * * @param {RouterCorsConfig} config * @returns {Router} */ - cors(config) { + public cors(config: RouterCorsConfig): Router { config = config || {} this.corsConfig = { allowOrigin: config.allowOrigin || '*', @@ -273,7 +318,7 @@ class Router { * @param {RouterHandler[]} handlers Arrar of handler functions * @returns {Router} */ - register(method, url, handlers) { + private register(method: string, url: string, handlers: RouterHandler[]): Router { this.routes.push({ method, url, @@ -287,16 +332,16 @@ class Router { * * @private * @param {Request} request - * @returns {Route | undefined} + * @returns {RouterRequest | undefined} */ - getRoute(request) { + private getRoute(request: RouterRequest): Route | undefined { const url = new URL(request.url) const pathArr = url.pathname.split('/').filter(i => i) return this.routes.find(r => { const routeArr = r.url.split('/').filter(i => i) if (![request.method, '*'].includes(r.method) || routeArr.length !== pathArr.length) return false - const params = {} + const params: RouterRequestParams = {} for (let i = 0; i < routeArr.length; i++) { if (routeArr[i] !== pathArr[i] && routeArr[i][0] !== ':') return false @@ -304,7 +349,7 @@ class Router { params[routeArr[i].substring(1)] = pathArr[i] } request.params = params - const query = {} + const query: any = {} for (const [k, v] of url.searchParams.entries()) { query[k] = v } @@ -321,18 +366,15 @@ class Router { * @param {any=} extend * @returns {Response} */ - async handle(env, request, extend = {}) { + public async handle(env: any, request: Request, extend: any = {}) { try { - if (request instanceof Event) { - request = request.request - console.warn("Warning: Using `event` on `router.handle()` is deprecated and might go away in future versions, please use `event.request` instead.") - } - const req = { + const req: RouterRequest = { ...extend, method: request.method, headers: request.headers, url: request.url, - params: [], + cf: request.cf, + params: {}, query: {}, body: '' } @@ -342,13 +384,13 @@ class Router { 'Access-Control-Allow-Origin': this.corsConfig.allowOrigin, 'Access-Control-Allow-Methods': this.corsConfig.allowMethods, 'Access-Control-Allow-Headers': this.corsConfig.allowHeaders, - 'Access-Control-Max-Age': this.corsConfig.maxAge + 'Access-Control-Max-Age': this.corsConfig.maxAge.toString() }, status: this.corsConfig.optionsSuccessStatus }) } if (['POST', 'PUT', 'PATCH'].includes(req.method)) { - if (req.headers.has('Content-Type') && req.headers.get('Content-Type').includes('json')) { + if (req.headers.has('Content-Type') && req.headers.get('Content-Type')!.includes('json')) { try { req.body = await request.json() } catch { @@ -363,21 +405,18 @@ class Router { } } const route = this.getRoute(req) - if (!route) { - return new Response(this.debugMode ? 'Route not found!' : null, { - status: 404 - }) - } - const res = { headers: new Headers() } + if (!route) + return new Response(this.debugMode ? 'Route not found!' : null, { status: 404 }) + const res: RouterResponse = { headers: new Headers() } if (Object.keys(this.corsConfig).length) { res.headers.set('Access-Control-Allow-Origin', this.corsConfig.allowOrigin) res.headers.set('Access-Control-Allow-Methods', this.corsConfig.allowMethods) res.headers.set('Access-Control-Allow-Headers', this.corsConfig.allowHeaders) - res.headers.set('Access-Control-Max-Age', this.corsConfig.maxAge) + res.headers.set('Access-Control-Max-Age', this.corsConfig.maxAge.toString()) } const handlers = [...this.globalHandlers, ...route.handlers] let prevIndex = -1 - const runner = async index => { + const runner = async (index: number) => { if (index === prevIndex) throw new Error('next() called multiple times') prevIndex = index @@ -390,25 +429,14 @@ class Router { res.headers.set('Content-Type', 'application/json') res.body = JSON.stringify(res.body) } - if (res.raw) { + if (res.raw) return res.raw - } - - const resOpts = { - status: res.status || (res.body ? 200 : 204), - headers: res.headers - } - - if (res.webSocket) { - resOpts.webSocket = res.webSocket - } - - return new Response([101, 204, 205, 304].includes(resOpts.status) ? null : res.body, resOpts) + return new Response([101, 204, 205, 304].includes(res.status || (res.body ? 200 : 204)) ? null : res.body, { status: res.status, headers: res.headers, webSocket: res.webSocket || null }) } catch(err) { console.error(err) - return new Response(this.debugMode ? err.stack : '', { status: 500 }) + return new Response(this.debugMode && err instanceof Error ? err.stack : '', { status: 500 }) } } } -module.exports = Router +export default Router diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..afd7594 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "module": "commonjs", + "target": "esnext", + "lib": ["esnext"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "preserveConstEnums": true, + "moduleResolution": "node", + "types": ["@cloudflare/workers-types"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "test"] +} \ No newline at end of file