diff --git a/etc/http-server.api.md b/etc/http-server.api.md index 0b7c90a..8fe3cce 100644 --- a/etc/http-server.api.md +++ b/etc/http-server.api.md @@ -4,8 +4,6 @@ ```ts -/// - import type * as http from 'http'; import type * as https from 'https'; import { IBaseComponent } from '@well-known-components/interfaces'; @@ -153,7 +151,7 @@ export type ServerComponents = { // @beta (undocumented) export type StandardStatusResponse = { - status: "pass" | "fail" | "warn"; + status: 'pass' | 'fail' | 'warn'; version?: string; releaseId?: string; notes?: string[]; @@ -165,7 +163,7 @@ export type StandardStatusResponse = { // @beta (undocumented) export type StandardStatusResponseDetail = { - status: "pass" | "fail" | "warn"; + status: 'pass' | 'fail' | 'warn'; componentType?: string; componentId?: string; }; @@ -184,8 +182,8 @@ export interface WebSocketServer { // Warnings were encountered during analysis: // // dist/router.d.ts:20:5 - (ae-forgotten-export) The symbol "Layer" needs to be exported by the entry point index.d.ts -// dist/types.d.ts:23:5 - (ae-incompatible-release-tags) The symbol "ws" is marked as @public, but its signature references "WebSocketServer" which is marked as @alpha -// dist/types.d.ts:29:5 - (ae-forgotten-export) The symbol "CorsOptions" needs to be exported by the entry point index.d.ts +// dist/types.d.ts:19:5 - (ae-incompatible-release-tags) The symbol "ws" is marked as @public, but its signature references "WebSocketServer" which is marked as @alpha +// dist/types.d.ts:25:5 - (ae-forgotten-export) The symbol "CorsOptions" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/package-lock.json b/package-lock.json index 296d1bb..ac11d36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "path-to-regexp": "^6.2.1" }, "devDependencies": { - "@microsoft/api-extractor": "^7.34.4", + "@microsoft/api-extractor": "^7.47.7", "@types/busboy": "^1.5.0", "@types/destroy": "^1.0.0", "@types/node": "^20.3.2", @@ -30,7 +30,7 @@ "@well-known-components/logger": "^3.1.2", "@well-known-components/test-helpers": "^1.3.0", "busboy": "^1.6.0", - "typescript": "^4.9.5", + "typescript": "^5.6.2", "undici": "^5.19.1", "ws": "^8.12.1" } @@ -998,96 +998,97 @@ "dev": true }, "node_modules/@microsoft/api-extractor": { - "version": "7.34.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.34.4.tgz", - "integrity": "sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ==", - "dev": true, - "dependencies": { - "@microsoft/api-extractor-model": "7.26.4", - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.55.2", - "@rushstack/rig-package": "0.3.18", - "@rushstack/ts-command-line": "4.13.2", - "colors": "~1.2.1", + "version": "7.47.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.47.7.tgz", + "integrity": "sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==", + "dev": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.29.6", + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.0", + "@rushstack/ts-command-line": "4.22.6", "lodash": "~4.17.15", + "minimatch": "~3.0.3", "resolve": "~1.22.1", - "semver": "~7.3.0", + "semver": "~7.5.4", "source-map": "~0.6.1", - "typescript": "~4.8.4" + "typescript": "5.4.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "node_modules/@microsoft/api-extractor-model": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.26.4.tgz", - "integrity": "sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ==", + "version": "7.29.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.29.6.tgz", + "integrity": "sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==", "dev": true, "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.55.2" + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", "dev": true }, "node_modules/@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", "dev": true, "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", "jju": "~1.4.0", - "resolve": "~1.19.0" - } - }, - "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "resolve": "~1.22.2" } }, "node_modules/@rushstack/node-core-library": { - "version": "3.55.2", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.55.2.tgz", - "integrity": "sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.7.0.tgz", + "integrity": "sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==", "dev": true, "dependencies": { - "colors": "~1.2.1", + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.3.0", - "z-schema": "~5.0.2" + "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" @@ -1098,25 +1099,74 @@ } } }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@rushstack/rig-package": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.18.tgz", - "integrity": "sha512-SGEwNTwNq9bI3pkdd01yCaH+gAsHqs0uxfGvtw9b0LJXH52qooWXnrFTRRLG1aL9pf+M2CARdrA9HLHJys3jiQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", "dev": true, "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, + "node_modules/@rushstack/terminal": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.0.tgz", + "integrity": "sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==", + "dev": true, + "dependencies": { + "@rushstack/node-core-library": "5.7.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/@rushstack/ts-command-line": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.13.2.tgz", - "integrity": "sha512-bCU8qoL9HyWiciltfzg7GqdfODUeda/JpI0602kbN5YH22rzTxyqYvv7aRLENCM7XCQ1VRs7nMkEqgJUOU8Sag==", + "version": "4.22.6", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.22.6.tgz", + "integrity": "sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==", "dev": true, "dependencies": { + "@rushstack/terminal": "0.14.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", - "colors": "~1.2.1", "string-argv": "~0.3.1" } }, @@ -1405,14 +1455,14 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -1420,6 +1470,37 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1809,15 +1890,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1830,16 +1902,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2178,10 +2240,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -2257,18 +2322,6 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2278,6 +2331,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -2367,12 +2432,15 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2965,21 +3033,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-util": { "version": "29.6.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", @@ -3119,9 +3172,9 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "node_modules/json5": { @@ -3199,12 +3252,6 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -3663,9 +3710,9 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -3702,13 +3749,22 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3750,9 +3806,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3884,9 +3940,9 @@ } }, "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { "node": ">=0.6.19" @@ -4077,21 +4133,6 @@ } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -4120,16 +4161,16 @@ "dev": true }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici": { @@ -4212,15 +4253,6 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -4355,26 +4387,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dev": true, - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } } }, "dependencies": { @@ -5122,108 +5134,142 @@ } }, "@microsoft/api-extractor": { - "version": "7.34.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.34.4.tgz", - "integrity": "sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ==", - "dev": true, - "requires": { - "@microsoft/api-extractor-model": "7.26.4", - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.55.2", - "@rushstack/rig-package": "0.3.18", - "@rushstack/ts-command-line": "4.13.2", - "colors": "~1.2.1", + "version": "7.47.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.47.7.tgz", + "integrity": "sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==", + "dev": true, + "requires": { + "@microsoft/api-extractor-model": "7.29.6", + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.0", + "@rushstack/ts-command-line": "4.22.6", "lodash": "~4.17.15", + "minimatch": "~3.0.3", "resolve": "~1.22.1", - "semver": "~7.3.0", + "semver": "~7.5.4", "source-map": "~0.6.1", - "typescript": "~4.8.4" + "typescript": "5.4.2" }, "dependencies": { + "minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true } } }, "@microsoft/api-extractor-model": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.26.4.tgz", - "integrity": "sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ==", + "version": "7.29.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.29.6.tgz", + "integrity": "sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.55.2" + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0" } }, "@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", "dev": true }, "@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", "jju": "~1.4.0", - "resolve": "~1.19.0" - }, - "dependencies": { - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - } + "resolve": "~1.22.2" } }, "@rushstack/node-core-library": { - "version": "3.55.2", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.55.2.tgz", - "integrity": "sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.7.0.tgz", + "integrity": "sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==", "dev": true, "requires": { - "colors": "~1.2.1", + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.3.0", - "z-schema": "~5.0.2" + "semver": "~7.5.4" + }, + "dependencies": { + "ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + } + } } }, "@rushstack/rig-package": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.18.tgz", - "integrity": "sha512-SGEwNTwNq9bI3pkdd01yCaH+gAsHqs0uxfGvtw9b0LJXH52qooWXnrFTRRLG1aL9pf+M2CARdrA9HLHJys3jiQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", "dev": true, "requires": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, + "@rushstack/terminal": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.0.tgz", + "integrity": "sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==", + "dev": true, + "requires": { + "@rushstack/node-core-library": "5.7.0", + "supports-color": "~8.1.1" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@rushstack/ts-command-line": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.13.2.tgz", - "integrity": "sha512-bCU8qoL9HyWiciltfzg7GqdfODUeda/JpI0602kbN5YH22rzTxyqYvv7aRLENCM7XCQ1VRs7nMkEqgJUOU8Sag==", + "version": "4.22.6", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.22.6.tgz", + "integrity": "sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==", "dev": true, "requires": { + "@rushstack/terminal": "0.14.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", - "colors": "~1.2.1", "string-argv": "~0.3.1" } }, @@ -5509,17 +5555,33 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, + "ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "requires": {} + }, + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5793,12 +5855,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", - "dev": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5808,13 +5864,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "optional": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6067,9 +6116,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "gensync": { @@ -6122,21 +6171,21 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6205,12 +6254,12 @@ "dev": true }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.2" } }, "is-fullwidth-code-point": { @@ -6658,17 +6707,6 @@ "natural-compare": "^1.4.0", "pretty-format": "^29.6.1", "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "jest-util": { @@ -6781,9 +6819,9 @@ "dev": true }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "json5": { @@ -6846,12 +6884,6 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7210,9 +7242,9 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "pure-rand": { @@ -7233,13 +7265,19 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -7266,9 +7304,9 @@ "dev": true }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7369,9 +7407,9 @@ "dev": true }, "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true }, "string-length": { @@ -7488,17 +7526,6 @@ "make-error": "1.x", "semver": "^7.5.3", "yargs-parser": "^21.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "type-detect": { @@ -7520,9 +7547,9 @@ "dev": true }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true }, "undici": { @@ -7578,12 +7605,6 @@ } } }, - "validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true - }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -7674,18 +7695,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true - }, - "z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dev": true, - "requires": { - "commander": "^9.4.1", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - } } } } diff --git a/package.json b/package.json index abcd3e1..f5f7f9e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "homepage": "https://github.com/well-known-components/http-server#readme", "devDependencies": { - "@microsoft/api-extractor": "^7.34.4", + "@microsoft/api-extractor": "^7.47.7", "@types/busboy": "^1.5.0", "@types/destroy": "^1.0.0", "@types/node": "^20.3.2", @@ -35,7 +35,7 @@ "@well-known-components/logger": "^3.1.2", "@well-known-components/test-helpers": "^1.3.0", "busboy": "^1.6.0", - "typescript": "^4.9.5", + "typescript": "^5.6.2", "undici": "^5.19.1", "ws": "^8.12.1" }, diff --git a/src/benchmark.ts b/src/benchmark.ts index 64068a6..3927806 100644 --- a/src/benchmark.ts +++ b/src/benchmark.ts @@ -1,10 +1,10 @@ // this server shuts down after 10100 requests. -import { IConfigComponent, IHttpServerComponent, ILoggerComponent, Lifecycle } from "@well-known-components/interfaces" -import { createConfigComponent } from "@well-known-components/env-config-provider" -import { createServerComponent } from "./index" -import { createLogComponent } from "@well-known-components/logger" -import { readFileSync } from "fs" +import { IConfigComponent, IHttpServerComponent, ILoggerComponent, Lifecycle } from '@well-known-components/interfaces' +import { createConfigComponent } from '@well-known-components/env-config-provider' +import { createServerComponent } from './index' +import { createLogComponent } from '@well-known-components/logger' +import { readFileSync } from 'fs' // Record of components type Components = { @@ -44,7 +44,7 @@ async function main({ components, startComponents, stop }: Lifecycle.EntryPointP const staticBuffer = Buffer.from(new Array(10000).fill(0).map(() => Math.floor(Math.random() * 256))) const staticArrayBuffer = new Uint8Array(staticBuffer) - const packageJsonString = readFileSync("package.json").toString() + const packageJsonString = readFileSync('package.json').toString() const packageJson = JSON.parse(packageJsonString) const TOTAL_REQUESTS = 10100 @@ -65,25 +65,25 @@ async function main({ components, startComponents, stop }: Lifecycle.EntryPointP return { headers, status: 200, - body: staticBuffer, + body: staticBuffer } case 2: return { headers, status: 200, - body: staticArrayBuffer, + body: staticArrayBuffer } case 3: return { headers, status: 200, - body: packageJsonString, + body: packageJsonString } case 4: return { headers, status: 200, - body: packageJson, + body: packageJson } } @@ -93,8 +93,8 @@ async function main({ components, startComponents, stop }: Lifecycle.EntryPointP status: 200, body: { json: true, - text: "Hello world", - }, + text: 'Hello world' + } } }) @@ -108,8 +108,8 @@ async function initComponents(): Promise { const logs = await createLogComponent({}) const config = createConfigComponent({ - HTTP_SERVER_PORT: "5000", - HTTP_SERVER_HOST: "0.0.0.0", + HTTP_SERVER_PORT: '5000', + HTTP_SERVER_HOST: '0.0.0.0' }) const server = await createServerComponent({ logs, config }, {}) @@ -117,6 +117,6 @@ async function initComponents(): Promise { return /*components*/ { logs, config, - server, + server } } diff --git a/src/index.ts b/src/index.ts index e4bcd50..9979168 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ -export * from "./server" -export * from "./injectors" -export * from "./test-component" -export * from "./types" -export * from "./status-checks" -export * from "./helpers" -export * from "./router" -export * from "./metrics" +export * from './server' +export * from './injectors' +export * from './test-component' +export * from './types' +export * from './status-checks' +export * from './helpers' +export * from './router' +export * from './metrics' diff --git a/src/injectors.ts b/src/injectors.ts index f885aca..06df907 100644 --- a/src/injectors.ts +++ b/src/injectors.ts @@ -1,6 +1,6 @@ -import type { IHttpServerComponent } from "@well-known-components/interfaces" +import type { IHttpServerComponent } from '@well-known-components/interfaces' -const underlyingServerKey = Symbol("real-server") +const underlyingServerKey = Symbol('real-server') /** * @public @@ -8,16 +8,13 @@ const underlyingServerKey = Symbol("real-server") export async function getUnderlyingServer(server: IHttpServerComponent): Promise { const getListener: () => Promise = (server as any)[underlyingServerKey] if (!getListener) - throw new Error("The provided server does not have an underlying http or https server implementation") + throw new Error('The provided server does not have an underlying http or https server implementation') return getListener() } /** * @internal */ -export function _setUnderlyingServer( - server: IHttpServerComponent, - getter: () => Promise -) { +export function _setUnderlyingServer(server: IHttpServerComponent, getter: () => Promise) { ;(server as any)[underlyingServerKey] = getter -} \ No newline at end of file +} diff --git a/src/layer.ts b/src/layer.ts index 20b4bdb..246d0d5 100644 --- a/src/layer.ts +++ b/src/layer.ts @@ -1,7 +1,7 @@ -import { IHttpServerComponent as http } from "@well-known-components/interfaces" -import { pathToRegexp, Key } from "path-to-regexp" -import { Middleware } from "./middleware" -import { RoutedContext } from "./router" +import { IHttpServerComponent as http } from '@well-known-components/interfaces' +import { pathToRegexp, Key } from 'path-to-regexp' +import { Middleware } from './middleware' +import { RoutedContext } from './router' export type LayerOptions = Partial<{ name: string @@ -47,14 +47,14 @@ export class Layer { for (let i = 0; i < methods.length; i++) { const l = this.methods.push(methods[i].toUpperCase() as http.HTTPMethod) - if (this.methods[l - 1] === "GET") this.methods.unshift("HEAD") + if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD') } // ensure middleware is a function for (let i = 0; i < this.stack.length; i++) { const fn = this.stack[i] const type = typeof fn - if (type !== "function") + if (type !== 'function') throw new Error( `${methods.toString()} \`${this.opts.name || path}\`: \`middleware\` must be a function, not \`${type}\`` ) @@ -115,7 +115,7 @@ export class Layer { setPrefix(prefix: string): this { if (this.path) { - this.path = this.path !== "/" || this.opts.strict === true ? `${prefix}${this.path}` : prefix + this.path = this.path !== '/' || this.opts.strict === true ? `${prefix}${this.path}` : prefix this.paramNames = [] this.regexp = pathToRegexp(this.path, this.paramNames, this.opts) } diff --git a/src/logic.ts b/src/logic.ts index d6fe467..e27d883 100644 --- a/src/logic.ts +++ b/src/logic.ts @@ -1,14 +1,14 @@ -import * as fetch from "node-fetch" -import { Stream } from "stream" -import * as http from "http" -import * as https from "https" -import destroy from "destroy" -import onFinished from "on-finished" -import type { IHttpServerComponent } from "@well-known-components/interfaces" -import type { IHttpServerOptions } from "./types" -import { HttpError } from "http-errors" -import { Middleware } from "./middleware" -import { getWebSocketCallback, upgradeWebSocketResponse, withWebSocketCallback } from "./ws" +import * as fetch from 'node-fetch' +import { Stream } from 'stream' +import * as http from 'http' +import * as https from 'https' +import destroy from 'destroy' +import onFinished from 'on-finished' +import type { IHttpServerComponent } from '@well-known-components/interfaces' +import type { IHttpServerOptions } from './types' +import { HttpError } from 'http-errors' +import { Middleware } from './middleware' +import { getWebSocketCallback, upgradeWebSocketResponse, withWebSocketCallback } from './ws' /** * @internal @@ -17,8 +17,8 @@ export function getServer( options: Partial, listener: http.RequestListener ): http.Server | https.Server { - if ("https" in options && options.https) return https.createServer(options.https, listener) - if ("http" in options && options.http) return http.createServer(options.http, listener) + if ('https' in options && options.https) return https.createServer(options.https, listener) + if ('http' in options && options.http) return http.createServer(options.http, listener) return http.createServer(listener) } @@ -31,11 +31,11 @@ const NAME = Symbol.toStringTag export const isBlob = (object: any): object is Blob => { return ( object !== null && - typeof object === "object" && - typeof object.arrayBuffer === "function" && - typeof object.type === "string" && - typeof object.stream === "function" && - typeof object.constructor === "function" && + typeof object === 'object' && + typeof object.arrayBuffer === 'function' && + typeof object.type === 'string' && + typeof object.stream === 'function' && + typeof object.constructor === 'function' && /^(Blob|File)$/.test(object[NAME]) ) } @@ -68,14 +68,14 @@ export function success(data: fetch.Response, res: http.ServerResponse) { // } else { // ;(blob.stream() as any).pipe(res) // } - throw new Error("Unknown response body (Blob)") + throw new Error('Unknown response body (Blob)') } else if (body && body.pipe) { body.pipe(res) // Note: for context about why this is necessary, check https://github.com/nodejs/node/issues/1180 onFinished(res, () => destroy(body)) } else if (body !== undefined && body !== null) { - throw new Error("Unknown response body") + throw new Error('Unknown response body') } else { res.end() } @@ -95,7 +95,7 @@ export const getRequestFromNodeMessage = headers.append(key, $)) @@ -105,15 +105,15 @@ export const getRequestFromNodeMessage = = async (_, next) => { } catch (e: any) { if ( e instanceof HttpError || - (("status" in e || "statusCode" in e) && (typeof e.status == "number" || typeof e.statusCode == "number")) + (('status' in e || 'statusCode' in e) && (typeof e.status == 'number' || typeof e.statusCode == 'number')) ) { return { status: e.status || e.statusCode, body: e.body || e.message, - headers: e.headers, + headers: e.headers } } throw e @@ -152,10 +152,10 @@ function respondBuffer( mutableHeaders: fetch.Headers ): fetch.Response { // TODO: test - mutableHeaders.set("Content-Length", buffer.byteLength.toFixed()) + mutableHeaders.set('Content-Length', buffer.byteLength.toFixed()) return new fetch.Response(buffer, { ...(response as fetch.ResponseInit), - headers: mutableHeaders, + headers: mutableHeaders }) } @@ -165,8 +165,8 @@ function respondJson( mutableHeaders: fetch.Headers ): fetch.Response { // TODO: test - if (!mutableHeaders.has("content-type")) { - mutableHeaders.set("content-type", "application/json") + if (!mutableHeaders.has('content-type')) { + mutableHeaders.set('content-type', 'application/json') } return respondString(JSON.stringify(json), response, mutableHeaders) } @@ -178,11 +178,11 @@ function respondString( ): fetch.Response { // TODO: test // TODO: accept encoding - const returnEncoding = "utf-8" + const returnEncoding = 'utf-8' const retBuffer = Buffer.from(txt, returnEncoding) - if (!mutableHeaders.has("content-type")) { - mutableHeaders.set("content-type", `text/plain; charset=${returnEncoding}`) + if (!mutableHeaders.has('content-type')) { + mutableHeaders.set('content-type', `text/plain; charset=${returnEncoding}`) } return respondBuffer(retBuffer, response, mutableHeaders) @@ -190,7 +190,7 @@ function respondString( const initialResponse: IHttpServerComponent.IResponse = { status: 404, - body: "Not found", + body: 'Not found' } /** @@ -208,7 +208,7 @@ export function normalizeResponseBody( ): fetch.Response { if (!response) { // Not Implemented - return new fetch.Response(undefined, { status: 501, statusText: "Server did not produce a valid response" }) + return new fetch.Response(undefined, { status: 501, statusText: 'Server did not produce a valid response' }) } if (response.status == 101) { @@ -220,22 +220,22 @@ export function normalizeResponseBody( return new fetch.Response(response.body, { headers: response.headers, status: response.status, - statusText: response.statusText, + statusText: response.statusText }) } const is1xx = response.status && response.status >= 100 && response.status < 200 const is204 = response.status == 204 const is304 = response.status == 304 - const isHEAD = request.method == "HEAD" + const isHEAD = request.method == 'HEAD' const mutableHeaders = new fetch.Headers(response.headers as fetch.HeadersInit) if (is204 || is304) { // TODO: TEST this code path - mutableHeaders.delete("Content-Type") - mutableHeaders.delete("Content-Length") - mutableHeaders.delete("Transfer-Encoding") + mutableHeaders.delete('Content-Type') + mutableHeaders.delete('Content-Length') + mutableHeaders.delete('Transfer-Encoding') } // https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 @@ -249,7 +249,7 @@ export function normalizeResponseBody( return respondBuffer(response.body, response, mutableHeaders) } else if (response.body instanceof ArrayBuffer || response.body instanceof Uint8Array) { return respondBuffer(response.body, response, mutableHeaders) - } else if (typeof response.body == "string") { + } else if (typeof response.body == 'string') { return respondString(response.body, response, mutableHeaders) } else if (response.body instanceof Stream) { return new fetch.Response(response.body, response as fetch.ResponseInit) @@ -261,8 +261,8 @@ export function normalizeResponseBody( // Applications SHOULD use this field to indicate the transfer-length of the // message-body, unless this is prohibited by the rules in section 4.4. // (https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4) - if (!mutableHeaders.has("content-length")) { - mutableHeaders.set("content-length", "0") + if (!mutableHeaders.has('content-length')) { + mutableHeaders.set('content-length', '0') } return new fetch.Response(undefined, { ...(response as fetch.ResponseInit), headers: mutableHeaders }) diff --git a/src/methods.ts b/src/methods.ts index 9edfac8..88cf3b9 100644 --- a/src/methods.ts +++ b/src/methods.ts @@ -1,19 +1,19 @@ -import { IHttpServerComponent as http } from "@well-known-components/interfaces" +import { IHttpServerComponent as http } from '@well-known-components/interfaces' export type MethodsMapType = { [key in http.HTTPMethod]: key } export const methodsMap: MethodsMapType = Object.seal({ - CONNECT: "CONNECT", - DELETE: "DELETE", - GET: "GET", - HEAD: "HEAD", - OPTIONS: "OPTIONS", - PATCH: "PATCH", - POST: "POST", - PUT: "PUT", - TRACE: "TRACE", + CONNECT: 'CONNECT', + DELETE: 'DELETE', + GET: 'GET', + HEAD: 'HEAD', + OPTIONS: 'OPTIONS', + PATCH: 'PATCH', + POST: 'POST', + PUT: 'PUT', + TRACE: 'TRACE' }) export const methodsList: ReadonlyArray = Object.seal( diff --git a/src/middleware.ts b/src/middleware.ts index c1d925a..656593a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,4 +1,4 @@ -import { IHttpServerComponent as http, IMiddlewareAdapterHandler } from "@well-known-components/interfaces" +import { IHttpServerComponent as http, IMiddlewareAdapterHandler } from '@well-known-components/interfaces' /** * @public @@ -9,10 +9,10 @@ export type Middleware = IMiddlewareAdapterHandler * @public */ export function compose(...middlewares: Middleware[]): Middleware { - if (!Array.isArray(middlewares)) throw new TypeError("Middleware stack must be an array!") + if (!Array.isArray(middlewares)) throw new TypeError('Middleware stack must be an array!') for (const fn of middlewares) { - if (typeof fn !== "function") throw new TypeError("Middleware must be composed of functions!") + if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } if (middlewares.length == 1) { @@ -25,7 +25,7 @@ export function compose(...middlewares: Middleware[]): Middleware return dispatch(0) async function dispatch(i: number): Promise { if (i <= index) { - throw new Error("next() called multiple times") + throw new Error('next() called multiple times') } index = i let fn: Middleware | undefined = middlewares[i] diff --git a/src/router.ts b/src/router.ts index 2e31fba..dd91570 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,9 +1,9 @@ -import HttpError from "http-errors" -import { Layer, LayerOptions } from "./layer" -import { Key, pathToRegexp } from "path-to-regexp" -import type { IHttpServerComponent, IMiddlewareAdapterHandler } from "@well-known-components/interfaces" -import { compose, Middleware } from "./middleware" -import { methodsList } from "./methods" +import HttpError from 'http-errors' +import { Layer, LayerOptions } from './layer' +import { Key, pathToRegexp } from 'path-to-regexp' +import type { IHttpServerComponent, IMiddlewareAdapterHandler } from '@well-known-components/interfaces' +import { compose, Middleware } from './middleware' +import { methodsList } from './methods' /** @public */ export type RouterOptions = Partial<{ @@ -23,7 +23,7 @@ export type AllowedMethodOptions = Partial<{ methodNotAllowed: NewableFunction }> -const injectedMiddlewareRouterSymbol = Symbol("injected-router") +const injectedMiddlewareRouterSymbol = Symbol('injected-router') /** @internal */ function getInjectedRouter(middleware: Middleware): Router | null { @@ -97,25 +97,25 @@ export class Router implements IHttpServerComponent.MethodHa constructor(opts?: RouterOptions) { this.opts = opts || {} this.methods = this.opts?.methods?.map(($) => $.toUpperCase()) || [ - "HEAD", - "OPTIONS", - "GET", - "PUT", - "PATCH", - "POST", - "DELETE", + 'HEAD', + 'OPTIONS', + 'GET', + 'PUT', + 'PATCH', + 'POST', + 'DELETE' ] } - connect = createMethodHandler(this, "CONNECT") - delete = createMethodHandler(this, "DELETE") - get = createMethodHandler(this, "GET") - head = createMethodHandler(this, "HEAD") - options = createMethodHandler(this, "OPTIONS") - patch = createMethodHandler(this, "PATCH") - post = createMethodHandler(this, "POST") - put = createMethodHandler(this, "PUT") - trace = createMethodHandler(this, "TRACE") + connect = createMethodHandler(this, 'CONNECT') + delete = createMethodHandler(this, 'DELETE') + get = createMethodHandler(this, 'GET') + head = createMethodHandler(this, 'HEAD') + options = createMethodHandler(this, 'OPTIONS') + patch = createMethodHandler(this, 'PATCH') + post = createMethodHandler(this, 'POST') + put = createMethodHandler(this, 'PUT') + trace = createMethodHandler(this, 'TRACE') /** * Use given middleware. @@ -155,7 +155,7 @@ export class Router implements IHttpServerComponent.MethodHa let path: string | undefined let router = this - const hasPath = typeof middleware[0] === "string" + const hasPath = typeof middleware[0] === 'string' if (hasPath) path = middleware.shift() as any as string for (let i = 0; i < middleware.length; i++) { @@ -163,7 +163,7 @@ export class Router implements IHttpServerComponent.MethodHa const injectedRouter = getInjectedRouter(m) if (injectedRouter) { const cloneRouter = Object.assign(Object.create(Router.prototype), injectedRouter, { - stack: injectedRouter.stack.slice(0), + stack: injectedRouter.stack.slice(0) }) for (let j = 0; j < cloneRouter.stack.length; j++) { @@ -177,9 +177,9 @@ export class Router implements IHttpServerComponent.MethodHa } } else { const keys: Key[] = [] - pathToRegexp(router.opts.prefix || "", keys) + pathToRegexp(router.opts.prefix || '', keys) const routerPrefixHasParam = router.opts.prefix && keys.length - router.register(path || "([^/]*)", [], m, { end: false, ignoreCaptures: !hasPath && !routerPrefixHasParam }) + router.register(path || '([^/]*)', [], m, { end: false, ignoreCaptures: !hasPath && !routerPrefixHasParam }) } } @@ -199,7 +199,7 @@ export class Router implements IHttpServerComponent.MethodHa */ prefix(prefix: string): this { - prefix = prefix.replace(/\/$/, "") + prefix = prefix.replace(/\/$/, '') this.opts.prefix = prefix @@ -240,20 +240,23 @@ export class Router implements IHttpServerComponent.MethodHa ctx._matchedRouteName = mostSpecificLayer.name } - layerChain = matchedLayers.reduce(function (memo, layer) { - memo.push(async function (ctx, next) { - ctx.captures = layer.captures(path) - ctx.params = ctx.params = layer.params(ctx.captures, ctx.params) - ctx.routerPath = layer.path - // ctx.routerName = layer.name || undefined - ctx._matchedRoute = layer.path - if (layer.name) { - ctx._matchedRouteName = layer.name - } - return await next() - }) - return memo.concat(layer.stack) - }, [] as typeof layerChain) + layerChain = matchedLayers.reduce( + function (memo, layer) { + memo.push(async function (ctx, next) { + ctx.captures = layer.captures(path) + ctx.params = ctx.params = layer.params(ctx.captures, ctx.params) + ctx.routerPath = layer.path + // ctx.routerName = layer.name || undefined + ctx._matchedRoute = layer.path + if (layer.name) { + ctx._matchedRouteName = layer.name + } + return await next() + }) + return memo.concat(layer.stack) + }, + [] as typeof layerChain + ) return compose(...layerChain)(ctx, next) } @@ -308,7 +311,7 @@ export class Router implements IHttpServerComponent.MethodHa const allowed: Partial> = {} if (!response.status || response.status === 404) { - if ("matched" in ctx && ctx.matched) { + if ('matched' in ctx && ctx.matched) { for (let i = 0; i < ctx.matched.length; i++) { const route: any = ctx.matched[i] for (let j = 0; j < route.methods.length; j++) { @@ -324,7 +327,7 @@ export class Router implements IHttpServerComponent.MethodHa if (!~implemented.indexOf(currentMethod)) { if (options.throw) { let notImplementedThrowable = - typeof options.notImplemented === "function" + typeof options.notImplemented === 'function' ? options.notImplemented() // set whatever the user returns from their function : new HttpError.NotImplemented() @@ -332,19 +335,19 @@ export class Router implements IHttpServerComponent.MethodHa } else { return { status: 501, - headers: { Allow: allowedArr.join(", ") }, + headers: { Allow: allowedArr.join(', ') } } } } else if (allowedArr.length) { - if (currentMethod === "OPTIONS") { + if (currentMethod === 'OPTIONS') { return { status: 200, - headers: { Allow: allowedArr.join(", ") }, + headers: { Allow: allowedArr.join(', ') } } } else if (!allowed[currentMethod]) { if (options.throw) { let notAllowedThrowable = - typeof options.methodNotAllowed === "function" + typeof options.methodNotAllowed === 'function' ? options.methodNotAllowed() // set whatever the user returns from their function : new HttpError.MethodNotAllowed() @@ -352,7 +355,7 @@ export class Router implements IHttpServerComponent.MethodHa } else { return { status: 405, - headers: { Allow: allowedArr.join(", ") }, + headers: { Allow: allowedArr.join(', ') } } } } @@ -403,10 +406,10 @@ export class Router implements IHttpServerComponent.MethodHa redirect(source: string, destination: string, code: number = 301): this { // lookup source route by name - if (source[0] !== "/") throw new Error(`Relative URL must start with / got ${JSON.stringify(source)} instead`) + if (source[0] !== '/') throw new Error(`Relative URL must start with / got ${JSON.stringify(source)} instead`) // lookup destination route by name - if (destination[0] !== "/" && !destination.includes("://")) + if (destination[0] !== '/' && !destination.includes('://')) throw new Error( `Can't resolve target URL, it is neither a relative or absolute URL. Got ${JSON.stringify(source)}` ) @@ -449,8 +452,8 @@ export class Router implements IHttpServerComponent.MethodHa name: opts.name, sensitive: opts.sensitive || this.opts.sensitive || false, strict: opts.strict || this.opts.strict || false, - prefix: opts.prefix || this.opts.prefix || "", - ignoreCaptures: opts.ignoreCaptures, + prefix: opts.prefix || this.opts.prefix || '', + ignoreCaptures: opts.ignoreCaptures }) if (this.opts.prefix) { @@ -478,7 +481,7 @@ export class Router implements IHttpServerComponent.MethodHa const matched = { path: [] as Layer[], pathAndMethod: [] as Layer[], - route: false, + route: false } for (let len = layers.length, i = 0; i < len; i++) { diff --git a/src/server-handler.ts b/src/server-handler.ts index 8321031..dd16f40 100644 --- a/src/server-handler.ts +++ b/src/server-handler.ts @@ -1,7 +1,7 @@ -import { contextFromRequest, defaultHandler, getDefaultMiddlewares, normalizeResponseBody } from "./logic" -import { Middleware, compose } from "./middleware" -import * as fetch from "node-fetch" -import { IHttpServerComponent as http } from "@well-known-components/interfaces" +import { contextFromRequest, defaultHandler, getDefaultMiddlewares, normalizeResponseBody } from './logic' +import { Middleware, compose } from './middleware' +import * as fetch from 'node-fetch' +import { IHttpServerComponent as http } from '@well-known-components/interfaces' // @internal export function createServerHandler() { @@ -20,7 +20,7 @@ export function createServerHandler() { // initialize default middleware resetMiddlewares() - const use: http["use"] = async (handler) => { + const use: http['use'] = async (handler) => { middlewares.push(handler) doMiddlewareComposition() } @@ -34,6 +34,6 @@ export function createServerHandler() { return { resetMiddlewares, use, - processRequest, + processRequest } } diff --git a/src/server.ts b/src/server.ts index 7ec5443..7ad6f5c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,18 +1,18 @@ import type { IBaseComponent, IHttpServerComponent, - IStatusCheckCapableComponent, -} from "@well-known-components/interfaces" -import { _setUnderlyingServer } from "./injectors" -import { getServer, success, getRequestFromNodeMessage } from "./logic" -import type { ServerComponents, IHttpServerOptions } from "./types" -import { createServerHandler } from "./server-handler" -import * as http from "http" -import { createServerTerminator } from "./terminator" -import { Socket } from "net" -import { getWebSocketCallback } from "./ws" -import destroy from "destroy" -import { createCorsMiddleware } from "./cors" + IStatusCheckCapableComponent +} from '@well-known-components/interfaces' +import { _setUnderlyingServer } from './injectors' +import { getServer, success, getRequestFromNodeMessage } from './logic' +import type { ServerComponents, IHttpServerOptions } from './types' +import { createServerHandler } from './server-handler' +import * as http from 'http' +import { createServerTerminator } from './terminator' +import { Socket } from 'net' +import { getWebSocketCallback } from './ws' +import destroy from 'destroy' +import { createCorsMiddleware } from './cors' /** * @public @@ -36,16 +36,14 @@ export async function createServerComponent( options: Partial ): Promise> { const { config, logs, ws } = components - const logger = logs.getLogger("http-server") + const logger = logs.getLogger('http-server') // config - const port = await config.requireNumber("HTTP_SERVER_PORT") - const host = await config.requireString("HTTP_SERVER_HOST") + const port = await config.requireNumber('HTTP_SERVER_PORT') + const host = await config.requireString('HTTP_SERVER_HOST') let handlerFn: http.RequestListener = handler - - const server = getServer(options, handlerFn) let listen: Promise | undefined @@ -54,7 +52,7 @@ export async function createServerComponent( async function start(): Promise { if (listen) { - logger.error("start() called more than once") + logger.error('start() called more than once') await listen return } @@ -65,16 +63,16 @@ export async function createServerComponent( reject(err) } - server.once("error", errorHandler).listen(port, host, () => { + server.once('error', errorHandler).listen(port, host, () => { // logger.log(`Listening ${host}:${port}`) // resolve(server) // server!.off("error", errorHandler) }) - server.once("listening", () => { + server.once('listening', () => { logger.log(`Listening ${host}:${port}`) resolve(server) - server!.off("error", errorHandler) + server!.off('error', errorHandler) }) }) @@ -109,7 +107,7 @@ export async function createServerComponent( }, // extra - resetMiddlewares: serverHandler.resetMiddlewares, + resetMiddlewares: serverHandler.resetMiddlewares } async function asyncHandle(req: http.IncomingMessage, res: http.ServerResponse) { @@ -121,7 +119,7 @@ export async function createServerComponent( async function handleUpgrade(req: http.IncomingMessage, socket: Socket, head: Buffer) { if (!ws) { - throw new Error("No WebSocketServer present") + throw new Error('No WebSocketServer present') } const request = getRequestFromNodeMessage(req, host) @@ -141,7 +139,7 @@ export async function createServerComponent( } else { if (response.status) { const statusCode = isNaN(response.status) ? 404 : response.status - const statusText = http.STATUS_CODES[statusCode] || "Not Found" + const statusText = http.STATUS_CODES[statusCode] || 'Not Found' socket.end(`HTTP/${req.httpVersion} ${statusCode} ${statusText}\r\n\r\n`) } else { socket.end() @@ -150,7 +148,7 @@ export async function createServerComponent( } if (ws) { - server.on("upgrade", (req: http.IncomingMessage, socket: Socket, head: Buffer) => { + server.on('upgrade', (req: http.IncomingMessage, socket: Socket, head: Buffer) => { return handleUpgrade(req, socket, head).catch((err) => { logger.error(err) destroy(socket) @@ -162,7 +160,7 @@ export async function createServerComponent( asyncHandle(request, response).catch((error) => { logger.error(error) - if (error.code == "ERR_INVALID_URL") { + if (error.code == 'ERR_INVALID_URL') { response.statusCode = 404 response.end() } else { @@ -173,7 +171,7 @@ export async function createServerComponent( } _setUnderlyingServer(ret, async () => { - if (!server) throw new Error("The server is stopped") + if (!server) throw new Error('The server is stopped') return (await listen) || server! }) diff --git a/src/status-checks.ts b/src/status-checks.ts index 5249814..b492953 100644 --- a/src/status-checks.ts +++ b/src/status-checks.ts @@ -2,15 +2,15 @@ import { IBaseComponent, IConfigComponent, IHttpServerComponent, - IStatusCheckCapableComponent, -} from "@well-known-components/interfaces" -import { Router } from "./router" + IStatusCheckCapableComponent +} from '@well-known-components/interfaces' +import { Router } from './router' /** * @beta */ export type StandardStatusResponse = { - status: "pass" | "fail" | "warn" + status: 'pass' | 'fail' | 'warn' version?: string releaseId?: string notes?: string[] @@ -24,7 +24,7 @@ export type StandardStatusResponse = { * @beta */ export type StandardStatusResponseDetail = { - status: "pass" | "fail" | "warn" + status: 'pass' | 'fail' | 'warn' componentType?: string componentId?: string } @@ -48,7 +48,7 @@ export async function createStatusCheckComponent(co const SUCCESSFUL_STATUS = 200 const FAILED_STATUS = 503 - const MIME = "application/health+json; charset=utf-8" + const MIME = 'application/health+json; charset=utf-8' async function getDetails(startup: boolean): Promise { if (!mutStartOptions) { @@ -58,17 +58,17 @@ export async function createStatusCheckComponent(co const probes: { name: string; promise: Promise }[] = [] - let functionName: "startupProbe" | "readynessProbe" = startup ? "startupProbe" : "readynessProbe" + let functionName: 'startupProbe' | 'readynessProbe' = startup ? 'startupProbe' : 'readynessProbe' for (let c in components) { - if (typeof components[c][functionName] == "function") { + if (typeof components[c][functionName] == 'function') { probes.push({ name: c, promise: new Promise((ok) => { components[c][functionName]!() .then(ok) .catch(() => ok(false)) - }), + }) }) } } @@ -77,12 +77,12 @@ export async function createStatusCheckComponent(co const content: StandardStatusResponse = { details: {}, - status: results.some(($) => $ === false) ? "fail" : "pass", + status: results.some(($) => $ === false) ? 'fail' : 'pass' } for (let it of probes) { content.details[it.name] = { - status: (await it.promise) ? "pass" : "fail", + status: (await it.promise) ? 'pass' : 'fail' } } @@ -98,41 +98,41 @@ export async function createStatusCheckComponent(co * associated service's "pool" of pods that are handling requests, * by marking the pod as "Unready". */ - routes.get("/health/ready", async () => { + routes.get('/health/ready', async () => { if (!mutStartOptions) { return { - body: { status: "initializing" }, + body: { status: 'initializing' }, status: FAILED_STATUS, headers: { - "content-type": MIME, - }, + 'content-type': MIME + } } } if (mutStartOptions.started()) { const content: StandardStatusResponse = (await getDetails(false))! return { - status: content.status == "pass" ? SUCCESSFUL_STATUS : FAILED_STATUS, + status: content.status == 'pass' ? SUCCESSFUL_STATUS : FAILED_STATUS, body: content, headers: { - "content-type": MIME, - }, + 'content-type': MIME + } } } else if (mutStartOptions.live()) { return { - body: "unready", + body: 'unready', status: FAILED_STATUS, headers: { - "content-type": MIME, - }, + 'content-type': MIME + } } } return { - body: "waiting", + body: 'waiting', status: FAILED_STATUS, headers: { - "content-type": MIME, - }, + 'content-type': MIME + } } }) @@ -146,37 +146,37 @@ export async function createStatusCheckComponent(co * process has finished, you can switch to returning a success * res (200) for the startup probe. */ - routes.get("/health/startup", async () => { + routes.get('/health/startup', async () => { if (!mutStartOptions) { return { body: { - status: "bootstrapping", + status: 'bootstrapping' }, headers: { - "content-type": MIME, + 'content-type': MIME }, - status: FAILED_STATUS, + status: FAILED_STATUS } } else if (!mutStartOptions.started()) { return { body: { - status: "starting", + status: 'starting' }, headers: { - "content-type": MIME, + 'content-type': MIME }, - status: FAILED_STATUS, + status: FAILED_STATUS } } const content: StandardStatusResponse = (await getDetails(true))! return { - status: content.status == "pass" ? SUCCESSFUL_STATUS : FAILED_STATUS, + status: content.status == 'pass' ? SUCCESSFUL_STATUS : FAILED_STATUS, body: content, headers: { - "content-type": MIME, - }, + 'content-type': MIME + } } }) @@ -185,8 +185,8 @@ export async function createStatusCheckComponent(co * the container is alive or not. If a container fails its liveness * probe, Kubernetes will kill the pod and restart another. */ - routes.get("/health/live", async () => { - return { status: SUCCESSFUL_STATUS, body: "alive" } + routes.get('/health/live', async () => { + return { status: SUCCESSFUL_STATUS, body: 'alive' } }) const middleware = routes.middleware() @@ -195,6 +195,6 @@ export async function createStatusCheckComponent(co return { async start(opt) { mutStartOptions = opt - }, + } } } diff --git a/src/terminator.ts b/src/terminator.ts index 51ec7d4..951e508 100644 --- a/src/terminator.ts +++ b/src/terminator.ts @@ -1,13 +1,13 @@ // Based on work of https://github.com/gajus/http-terminator -import type { ILoggerComponent } from "@well-known-components/interfaces" -import future, { IFuture } from "fp-future" -import http from "http" -import https from "https" -import type { Duplex } from "stream" +import type { ILoggerComponent } from '@well-known-components/interfaces' +import future, { IFuture } from 'fp-future' +import http from 'http' +import https from 'https' +import type { Duplex } from 'stream' const configurationDefaults: HttpTerminatorConfigurationInput = { - gracefulTerminationTimeout: 1_000, + gracefulTerminationTimeout: 1_000 } function delay(ms: number) { @@ -31,7 +31,7 @@ export function createServerTerminator( const configuration: HttpTerminatorConfigurationInput = { ...configurationDefaults, - ...configurationInput, + ...configurationInput } const sockets = new Set() @@ -39,25 +39,25 @@ export function createServerTerminator( let terminating: IFuture | undefined - server.on("connection", (socket) => { + server.on('connection', (socket) => { if (terminating) { socket.destroy() } else { sockets.add(socket) - socket.once("close", () => { + socket.once('close', () => { sockets.delete(socket) }) } }) - server.on("secureConnection", (socket) => { + server.on('secureConnection', (socket) => { if (terminating) { socket.destroy() } else { secureSockets.add(socket) - socket.once("close", () => { + socket.once('close', () => { secureSockets.delete(socket) }) } @@ -80,16 +80,16 @@ export function createServerTerminator( const terminate = async () => { if (terminating) { - logger.warn("Already terminating HTTP server") + logger.warn('Already terminating HTTP server') return terminating } terminating = future() - server.on("request", (incomingMessage, outgoingMessage) => { + server.on('request', (incomingMessage, outgoingMessage) => { if (!outgoingMessage.headersSent) { - outgoingMessage.setHeader("connection", "close") + outgoingMessage.setHeader('connection', 'close') } }) @@ -105,7 +105,7 @@ export function createServerTerminator( if (serverResponse) { if (!serverResponse.headersSent) { - serverResponse.setHeader("connection", "close") + serverResponse.setHeader('connection', 'close') } continue @@ -120,7 +120,7 @@ export function createServerTerminator( if (serverResponse) { if (!serverResponse.headersSent) { - serverResponse.setHeader("connection", "close") + serverResponse.setHeader('connection', 'close') } continue @@ -159,6 +159,6 @@ export function createServerTerminator( return { secureSockets, sockets, - terminate, + terminate } } diff --git a/src/test-component.ts b/src/test-component.ts index 7177f50..6566074 100644 --- a/src/test-component.ts +++ b/src/test-component.ts @@ -1,8 +1,8 @@ -import type { IHttpServerComponent } from "@well-known-components/interfaces" -import * as fetch from "node-fetch" -import { createServerHandler } from "./server-handler" -import { PassThrough, pipeline, Stream } from "stream" -import { IFetchComponent } from "@well-known-components/interfaces" +import type { IHttpServerComponent } from '@well-known-components/interfaces' +import * as fetch from 'node-fetch' +import { createServerHandler } from './server-handler' +import { PassThrough, pipeline, Stream } from 'stream' +import { IFetchComponent } from '@well-known-components/interfaces' /** @alpha */ export type IWebSocketComponent = { @@ -39,11 +39,11 @@ export function createTestServerComponent(): ITestH req = url } else { const tempHeaders = new fetch.Headers(initRequest?.headers) - const hostname = tempHeaders.get("X-Forwarded-Host") || tempHeaders.get("host") || "0.0.0.0" - const protocol = tempHeaders.get("X-Forwarded-Proto") == "https" ? "https" : "http" - let newUrl = new URL(protocol + "://" + hostname + url) + const hostname = tempHeaders.get('X-Forwarded-Host') || tempHeaders.get('host') || '0.0.0.0' + const protocol = tempHeaders.get('X-Forwarded-Proto') == 'https' ? 'https' : 'http' + let newUrl = new URL(protocol + '://' + hostname + url) try { - newUrl = new URL(url, protocol + "://" + hostname) + newUrl = new URL(url, protocol + '://' + hostname) } catch {} req = new fetch.Request(newUrl.toString(), initRequest) } @@ -62,14 +62,14 @@ export function createTestServerComponent(): ITestH return res } catch (error: any) { console.error(error) - return new fetch.Response("DEV-SERVER-ERROR: " + (error.stack || error.toString()), { status: 500 }) + return new fetch.Response('DEV-SERVER-ERROR: ' + (error.stack || error.toString()), { status: 500 }) } }, use: serverHandler.use, setContext(ctx) { currentContext = Object.create(ctx) }, - resetMiddlewares: serverHandler.resetMiddlewares, + resetMiddlewares: serverHandler.resetMiddlewares } return ret } diff --git a/src/test-server.ts b/src/test-server.ts index c075f3e..d4f571a 100644 --- a/src/test-server.ts +++ b/src/test-server.ts @@ -72,7 +72,7 @@ export async function wireTestServerComponents(context: TestServerAppContext< * Creates a test server and returns all the components and helpers */ export async function initTestServerComponents(): Promise> { - const config = createConfigComponent({...defaultServerConfig(), LOG_LEVEL: "INFO"}) + const config = createConfigComponent({ ...defaultServerConfig(), LOG_LEVEL: 'INFO' }) const logs = await createLogComponent({ config }) @@ -85,7 +85,7 @@ export async function initTestServerComponents(): Promise { - return await getBaseUrl() + url + return (await getBaseUrl()) + url } return { @@ -94,6 +94,6 @@ export async function initTestServerComponents(): Promise void, + callback: (client: any, request: http.IncomingMessage) => void ): void } diff --git a/src/ws.ts b/src/ws.ts index de8535a..f48edd5 100644 --- a/src/ws.ts +++ b/src/ws.ts @@ -1,6 +1,6 @@ -import { IHttpServerComponent } from "@well-known-components/interfaces" +import { IHttpServerComponent } from '@well-known-components/interfaces' -const wsSymbol = Symbol("WebSocketResponse") +const wsSymbol = Symbol('WebSocketResponse') export type WebSocketCallback = (ws: WebSocket) => Promise | void @@ -11,7 +11,7 @@ export type WebSocketCallback = (ws: WebSocket) => Promise | void export function upgradeWebSocketResponse(cb: WebSocketCallback): IHttpServerComponent.IResponse { return withWebSocketCallback( { - status: 101, + status: 101 }, cb ) diff --git a/test/busboy.ts b/test/busboy.ts index ae20a32..1cea5c0 100644 --- a/test/busboy.ts +++ b/test/busboy.ts @@ -1,6 +1,6 @@ -import { IHttpServerComponent } from "@well-known-components/interfaces" -import busboy, { FieldInfo, FileInfo } from "busboy" -import { Readable } from "stream" +import { IHttpServerComponent } from '@well-known-components/interfaces' +import busboy, { FieldInfo, FileInfo } from 'busboy' +import { Readable } from 'stream' export type FormDataContext = IHttpServerComponent.DefaultContext & { formData: { @@ -27,38 +27,38 @@ export function multipartParserWrapper { const formDataParser = busboy({ headers: { - "content-type": ctx.request.headers.get("content-type") || undefined, - }, + 'content-type': ctx.request.headers.get('content-type') || undefined + } }) - const fields: FormDataContext["formData"]["fields"] = {} - const files: FormDataContext["formData"]["files"] = {} + const fields: FormDataContext['formData']['fields'] = {} + const files: FormDataContext['formData']['files'] = {} const finished = new Promise((ok, err) => { - formDataParser.on("error", err) - formDataParser.on("finish", ok) + formDataParser.on('error', err) + formDataParser.on('finish', ok) }) /** * Emitted for each new non-file field found. */ - formDataParser.on("field", function (name: string, value: string, info: FieldInfo): void { + formDataParser.on('field', function (name: string, value: string, info: FieldInfo): void { fields[name] = { fieldname: name, value, - ...info, + ...info } }) - formDataParser.on("file", function (name: string, stream: Readable, info: FileInfo) { + formDataParser.on('file', function (name: string, stream: Readable, info: FileInfo) { const chunks: any[] = [] - stream.on("data", function (data) { + stream.on('data', function (data) { chunks.push(data) }) - stream.on("end", function () { + stream.on('end', function () { files[name] = { ...info, fieldname: name, - value: Buffer.concat(chunks), + value: Buffer.concat(chunks) } }) }) @@ -71,4 +71,4 @@ export function multipartParserWrapper { - const content = Buffer.from("holacaracola", "utf-8") +testWithServer('fetch suite', ({ components, stubComponents }) => { + const content = Buffer.from('holacaracola', 'utf-8') let infiniteRedirectCount = 0 - it("prepares the endpoints", () => { + it('prepares the endpoints', () => { components.router.get(`/working`, async () => { return { - body: content.toString(), + body: content.toString() } }) components.router.get(`/working-redirected-302`, async () => { return { status: 302, headers: { - location: "/working", - }, + location: '/working' + } } }) components.router.get(`/working-redirected-301`, async () => { return { status: 301, headers: { - location: "/working", - }, + location: '/working' + } } }) components.router.get(`/forever-redirecting-301`, async () => { @@ -36,8 +36,8 @@ testWithServer("fetch suite", ({ components, stubComponents }) => { return { status: 301, headers: { - location: "/forever-redirecting-301", - }, + location: '/forever-redirecting-301' + } } }) @@ -47,69 +47,69 @@ testWithServer("fetch suite", ({ components, stubComponents }) => { function* streamContent() { // sleep to fool the nagle algorithm chunk++ - yield "a" + yield 'a' if (chunk == 100) { - console.log("Closing stream") - throw new Error("Closing stream") + console.log('Closing stream') + throw new Error('Closing stream') } } - const headers: Record = ctx.url.searchParams.has("connection") + const headers: Record = ctx.url.searchParams.has('connection') ? { - connection: ctx.url.searchParams.get("connection")!, + connection: ctx.url.searchParams.get('connection')! } : {} return { headers: { ...headers, - "content-length": "100000", + 'content-length': '100000' }, - body: Readable.from(streamContent(), { encoding: "utf-8" }), + body: Readable.from(streamContent(), { encoding: 'utf-8' }) } }) }) - test("undici", Undici.fetch) - test("node-fetch", NodeFetch) + test('undici', Undici.fetch) + test('node-fetch', NodeFetch) function test(name: string, fetch: typeof Undici.fetch | typeof NodeFetch) { describe(name, () => { - it("/wroking", async () => { - const res = await fetch(await components.getUrl("/working")) - expect(await res.text()).toEqual("holacaracola") + it('/wroking', async () => { + const res = await fetch(await components.getUrl('/working')) + expect(await res.text()).toEqual('holacaracola') expect(res.status).toEqual(200) }) - it("/working-redirected-301", async () => { - const res = await fetch(await components.getUrl("/working-redirected-301")) - expect(await res.text()).toEqual("holacaracola") + it('/working-redirected-301', async () => { + const res = await fetch(await components.getUrl('/working-redirected-301')) + expect(await res.text()).toEqual('holacaracola') expect(res.status).toEqual(200) }) - it("/working-redirected-302", async () => { - const res = await fetch(await components.getUrl("/working-redirected-302")) - expect(await res.text()).toEqual("holacaracola") + it('/working-redirected-302', async () => { + const res = await fetch(await components.getUrl('/working-redirected-302')) + expect(await res.text()).toEqual('holacaracola') expect(res.status).toEqual(200) }) - it("/forever-redirecting-301", async () => { + it('/forever-redirecting-301', async () => { infiniteRedirectCount = 0 - await expect(fetch(await components.getUrl("/forever-redirecting-301"))).rejects.toThrow() + await expect(fetch(await components.getUrl('/forever-redirecting-301'))).rejects.toThrow() expect(infiniteRedirectCount).toBeGreaterThan(1) - }); + }) - (name == "node-fetch" ? it : xit)("/fails?connection=keep-alive", async () => { - const res = await fetch(await components.getUrl("/fails?connection=keep-alive"), { timeout: 1000 }) + ;(name == 'node-fetch' ? it : xit)('/fails?connection=keep-alive', async () => { + const res = await fetch(await components.getUrl('/fails?connection=keep-alive'), { timeout: 1000 }) expect(res.status).toEqual(200) await expect(res.arrayBuffer()).rejects.toThrowError() }) - it("/fails?connection=close", async () => { - const res = await fetch(await components.getUrl("/fails?connection=close"), { timeout: 1000 }) + it('/fails?connection=close', async () => { + const res = await fetch(await components.getUrl('/fails?connection=close'), { timeout: 1000 }) expect(res.status).toEqual(200) await expect(res.arrayBuffer()).rejects.toThrowError() - }); + }) }) } }) diff --git a/test/integration.spec.ts b/test/integration.spec.ts index 077eaaf..9c9a4cc 100644 --- a/test/integration.spec.ts +++ b/test/integration.spec.ts @@ -1,20 +1,20 @@ -import { Stream } from "stream" -import { createReadStream, readFileSync } from "fs" -import { getUnderlyingServer, Router } from "../src" -import { describeE2E } from "./test-e2e-harness" -import { describeTestE2E } from "./test-e2e-test-server" -import { TestComponents } from "./test-helpers" -import FormData from "form-data" -import * as undici from "undici" -import nodeFetch from "node-fetch" -import Sinon from "sinon" -import { multipartParserWrapper } from "./busboy" - -describeE2E("integration sanity tests using http backend", integrationSuite) -describeTestE2E("integration sanity tests using test server", integrationSuite) - -describeTestE2E("underlying server", function ({ components }: { components: TestComponents }) { - it("gets the underlying http server", async () => { +import { Stream } from 'stream' +import { createReadStream, readFileSync } from 'fs' +import { getUnderlyingServer, Router } from '../src' +import { describeE2E } from './test-e2e-harness' +import { describeTestE2E } from './test-e2e-test-server' +import { TestComponents } from './test-helpers' +import FormData from 'form-data' +import * as undici from 'undici' +import nodeFetch from 'node-fetch' +import Sinon from 'sinon' +import { multipartParserWrapper } from './busboy' + +describeE2E('integration sanity tests using http backend', integrationSuite) +describeTestE2E('integration sanity tests using test server', integrationSuite) + +describeTestE2E('underlying server', function ({ components }: { components: TestComponents }) { + it('gets the underlying http server', async () => { const { server } = components const http = getUnderlyingServer(server) await expect(http).rejects.toThrow() @@ -22,7 +22,7 @@ describeTestE2E("underlying server", function ({ components }: { components: Tes }) function integrationSuite({ components }: { components: TestComponents }) { - it("empty server returns 404", async () => { + it('empty server returns 404', async () => { const { fetch, server } = components server.resetMiddlewares() @@ -30,7 +30,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(404) }) - it("bypass middleware returns 404", async () => { + it('bypass middleware returns 404', async () => { const { fetch, server } = components server.resetMiddlewares() server.use((_, next) => next()) @@ -38,7 +38,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(404) }) - it("empty return middleware returns 501", async () => { + it('empty return middleware returns 501', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async () => { @@ -48,31 +48,31 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(501) }) - it("url must use X-Forwarded-Host if available", async () => { + it('url must use X-Forwarded-Host if available', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (ctx) => { return { body: ctx.url.toString() } }) const res = await fetch.fetch(`/test?a=true`, { - headers: { "X-Forwarded-Host": "google.com", host: "arduz.com.ar" }, + headers: { 'X-Forwarded-Host': 'google.com', host: 'arduz.com.ar' } }) const url = await res.text() - expect(url).toEqual("http://google.com/test?a=true") + expect(url).toEqual('http://google.com/test?a=true') }) - it("url must use host if available", async () => { + it('url must use host if available', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (ctx) => { return { body: ctx.url.toString() } }) - const res = await fetch.fetch(`/test?a=true`, { headers: { host: "arduz.com.ar" } }) + const res = await fetch.fetch(`/test?a=true`, { headers: { host: 'arduz.com.ar' } }) const url = await res.text() - expect(url).toEqual("http://arduz.com.ar/test?a=true") + expect(url).toEqual('http://arduz.com.ar/test?a=true') }) - it("calling multiple next fails", async () => { + it('calling multiple next fails', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (_, next) => { @@ -83,7 +83,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(500) }) - it("calling multiple next if there is no next returns 404", async () => { + it('calling multiple next if there is no next returns 404', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (_, next) => { @@ -93,7 +93,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(404) }) - it("unmatched routes return 501", async () => { + it('unmatched routes return 501', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() @@ -103,23 +103,23 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(404) }) - it("context is passed to handlers", async () => { + it('context is passed to handlers', async () => { const { fetch, server } = components server.resetMiddlewares() - server.setContext({ a: { value: "test" } }) + server.setContext({ a: { value: 'test' } }) server.use(async (ctx) => { return { body: { value: (ctx as any).a.value, isStillInjectingHttpContext: !!ctx.url } } }) const res = await fetch.fetch(`/`) - expect(await res.json()).toEqual({ value: "test", isStillInjectingHttpContext: true }) + expect(await res.json()).toEqual({ value: 'test', isStillInjectingHttpContext: true }) }) - it("return fetch should be legal google.com", async () => { + it('return fetch should be legal google.com', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (ctx) => { - const googlePromise = await nodeFetch("https://google.com", { - compress: false, + const googlePromise = await nodeFetch('https://google.com', { + compress: false }) return googlePromise as any }) @@ -127,12 +127,12 @@ function integrationSuite({ components }: { components: TestComponents }) { const res = await fetch.fetch(`/`) // console.log("res " + inspect(res, false, 3, true)) expect(res.ok).toEqual(true) - expect(res.headers.get("alt-svc")).not.toBeNull() + expect(res.headers.get('alt-svc')).not.toBeNull() const text = await res.text() - expect(text).toMatch("goog") + expect(text).toMatch('goog') }) - it("return readStream of file can be piped as text", async () => { + it('return readStream of file can be piped as text', async () => { const { fetch, server } = components server.resetMiddlewares() // const stream = createReadStream(import.meta.url.replace('file://', '')) @@ -152,7 +152,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(stream.destroyed).toEqual(true) }) - it("return Buffer works", async () => { + it('return Buffer works', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async () => { @@ -163,7 +163,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(Buffer.from(await res.arrayBuffer())).toEqual(Buffer.from([33, 22, 33])) }) - it("return Uint8Array works", async () => { + it('return Uint8Array works', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async () => { @@ -174,7 +174,7 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(new Uint8Array(await res.arrayBuffer())).toEqual(Uint8Array.from([33, 22, 33])) }) - it("return ArrayBuffer works", async () => { + it('return ArrayBuffer works', async () => { const { fetch, server } = components server.resetMiddlewares() const body = new ArrayBuffer(3) @@ -190,20 +190,20 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(new Uint8Array(await res.arrayBuffer())).toEqual(Uint8Array.from([33, 22, 66])) }) - it("returns a stream", async () => { + it('returns a stream', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() function* generator() { - yield "One line\n" - yield "Another line\n" + yield 'One line\n' + yield 'Another line\n' } - routes.get("/", async (ctx) => ({ + routes.get('/', async (ctx) => ({ status: 201, - body: Stream.Readable.from(generator(), { encoding: "utf-8" }), + body: Stream.Readable.from(generator(), { encoding: 'utf-8' }) })) server.use(routes.middleware()) @@ -211,7 +211,7 @@ function integrationSuite({ components }: { components: TestComponents }) { { const res = await fetch.fetch(`/`) expect(res.status).toEqual(201) - expect(await res.text()).toEqual("One line\nAnother line\n") + expect(await res.text()).toEqual('One line\nAnother line\n') } }) @@ -238,7 +238,7 @@ function integrationSuite({ components }: { components: TestComponents }) { // } // }) - it("send and read form data using FormData", async () => { + it('send and read form data using FormData', async () => { const { fetch, server, config } = components // TODO: undici doesn't work with FormData yet if (fetch.isUndici) return @@ -248,13 +248,13 @@ function integrationSuite({ components }: { components: TestComponents }) { const routes = new Router() routes.post( - "/", + '/', multipartParserWrapper(async (ctx) => { return { status: 201, body: { - fields: ctx.formData.fields, - }, + fields: ctx.formData.fields + } } }) ) @@ -263,52 +263,52 @@ function integrationSuite({ components }: { components: TestComponents }) { { const data = fetch.isUndici ? new undici.FormData() : new FormData() - data.append("username", "menduz") - data.append("username2", "cazala") - const res = await fetch.fetch(`/`, { body: data as any, method: "POST" }) + data.append('username', 'menduz') + data.append('username2', 'cazala') + const res = await fetch.fetch(`/`, { body: data as any, method: 'POST' }) expect(res.status).toEqual(201) expect(await res.json()).toEqual({ fields: { username: { - encoding: "7bit", - fieldname: "username", - mimeType: "text/plain", + encoding: '7bit', + fieldname: 'username', + mimeType: 'text/plain', nameTruncated: false, - value: "menduz", - valueTruncated: false, + value: 'menduz', + valueTruncated: false }, username2: { - encoding: "7bit", - fieldname: "username2", - mimeType: "text/plain", + encoding: '7bit', + fieldname: 'username2', + mimeType: 'text/plain', nameTruncated: false, - value: "cazala", - valueTruncated: false, - }, - }, + value: 'cazala', + valueTruncated: false + } + } }) } }) - it("unknown route should yield 404", async () => { + it('unknown route should yield 404', async () => { const { fetch, server } = components server.resetMiddlewares() const res = await fetch.fetch(`/test-${Math.random()}`) expect(res.status).toEqual(404) - expect(await res.text()).toEqual("Not found") + expect(await res.text()).toEqual('Not found') }) - it("GET / json response", async () => { + it('GET / json response', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() - routes.get("/", async () => ({ + routes.get('/', async () => ({ status: 200, - body: { hi: true }, + body: { hi: true } })) server.use(routes.middleware()) @@ -324,53 +324,53 @@ function integrationSuite({ components }: { components: TestComponents }) { } }) - it("custom headers reach the handlers", async () => { + it('custom headers reach the handlers', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() - routes.get("/users/:user", async (ctx) => ({ - body: ctx.request.headers.get("x-a") as any, + routes.get('/users/:user', async (ctx) => ({ + body: ctx.request.headers.get('x-a') as any })) server.use(routes.middleware()) { const val = Math.random().toString() - const res = await fetch.fetch(`/users/test`, { headers: { "X-A": val } }) + const res = await fetch.fetch(`/users/test`, { headers: { 'X-A': val } }) expect(await res.text()).toEqual(val) } }) - it("custom headers in the response", async () => { + it('custom headers in the response', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() - routes.get("/users/:user", async (ctx) => ({ - headers: { "X-b": "asd" }, + routes.get('/users/:user', async (ctx) => ({ + headers: { 'X-b': 'asd' } })) server.use(routes.middleware()) { const res = await fetch.fetch(`/users/test`) - expect(res.headers.get("X-b")).toEqual("asd") + expect(res.headers.get('X-b')).toEqual('asd') expect(res.status).toEqual(200) } }) - it("params are parsed (smoke)", async () => { + it('params are parsed (smoke)', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() - routes.get("/users/:user", async (ctx) => ({ + routes.get('/users/:user', async (ctx) => ({ status: 200, - body: ctx.params, + body: ctx.params })) server.use(routes.middleware()) @@ -378,19 +378,19 @@ function integrationSuite({ components }: { components: TestComponents }) { { const res = await fetch.fetch(`/users/test`) expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ user: "test" }) + expect(await res.json()).toEqual({ user: 'test' }) } }) - it("params are parsed with query string (smoke)", async () => { + it('params are parsed with query string (smoke)', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() - routes.get("/users/:user", async (ctx) => ({ + routes.get('/users/:user', async (ctx) => ({ status: 200, - body: ctx.params, + body: ctx.params })) server.use(routes.middleware()) @@ -398,11 +398,11 @@ function integrationSuite({ components }: { components: TestComponents }) { { const res = await fetch.fetch(`/users/xyz?query1=2`) expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ user: "xyz" }) + expect(await res.json()).toEqual({ user: 'xyz' }) } }) - it("context always returns a new object", async () => { + it('context always returns a new object', async () => { const { fetch, server } = components server.resetMiddlewares() const results = new Set<{ id: number }>() @@ -427,21 +427,21 @@ function integrationSuite({ components }: { components: TestComponents }) { // list of offensive endpoints taken from a real world attack to one of the maintainer's servers const offensiveEndpoints: Record = { - "//%5Cinteract.sh": 404, - "//%01%02%03%04%0a%0d%0a/admin/": 404, - "//..%25%35%63/admin/": 404, - "//..%255c/admin/": 404, - "//%3C%3E//interact.sh": 404, - "//%3C%3F/admin/": 404, - "//..%5c/admin/": 404, - "////interact.sh@/": 404, - "///%5C/interact.sh/": 404, - "///interact.sh@/": 404, - "///%5Ctinteract.sh/": 404, - "//https:interact.sh": 404, + '//%5Cinteract.sh': 404, + '//%01%02%03%04%0a%0d%0a/admin/': 404, + '//..%25%35%63/admin/': 404, + '//..%255c/admin/': 404, + '//%3C%3E//interact.sh': 404, + '//%3C%3F/admin/': 404, + '//..%5c/admin/': 404, + '////interact.sh@/': 404, + '///%5C/interact.sh/': 404, + '///interact.sh@/': 404, + '///%5Ctinteract.sh/': 404, + '//https:interact.sh': 404 } - describe("offensive endpoints", () => { + describe('offensive endpoints', () => { Object.entries(offensiveEndpoints).forEach(([endpoint, status]) => { it(endpoint, async () => { const { fetch, server } = components @@ -449,7 +449,7 @@ function integrationSuite({ components }: { components: TestComponents }) { server.use(async (ctx) => { return { status: 201, - body: ctx.url.toJSON(), + body: ctx.url.toJSON() } }) @@ -460,7 +460,7 @@ function integrationSuite({ components }: { components: TestComponents }) { }) }) - xit("xss sanity", async () => { + xit('xss sanity', async () => { const { fetch, server } = components server.resetMiddlewares() @@ -469,7 +469,7 @@ function integrationSuite({ components }: { components: TestComponents }) { server.use(async (ctx) => { return { status: 200, - body: ctx.url.toJSON(), + body: ctx.url.toJSON() } }) @@ -479,7 +479,7 @@ function integrationSuite({ components }: { components: TestComponents }) { ) expect(res.status).toEqual(200) expect(await res.text()).toContain( - "/%1B%5D8%3B%3Bhttps%3A%2F%2Fexample.com%22%2Fonmouseover%3D%22alert(1)%07example%1B%5D8%3B%3B%07" + '/%1B%5D8%3B%3Bhttps%3A%2F%2Fexample.com%22%2Fonmouseover%3D%22alert(1)%07example%1B%5D8%3B%3B%07' ) } @@ -488,7 +488,7 @@ function integrationSuite({ components }: { components: TestComponents }) { `/\u001B]8;;https://example.com\"/onmouseover=\"alert(1)\u0007example\u001B]8;;\u0007` ) expect(res.status).toEqual(200) - expect(await res.text()).toContain("/%1B]8;;https://example.com%22/onmouseover=%22alert(1)%07example%1B]8;;") + expect(await res.text()).toContain('/%1B]8;;https://example.com%22/onmouseover=%22alert(1)%07example%1B]8;;') } { @@ -499,25 +499,25 @@ function integrationSuite({ components }: { components: TestComponents }) { } }) - it("gracefully fail with exceptions (async)", async () => { + it('gracefully fail with exceptions (async)', async () => { const { fetch, server } = components server.resetMiddlewares() server.use(async (ctx) => { - throw new Error("some exception") + throw new Error('some exception') }) const res = await fetch.fetch(`/hola`) expect(res.status).toEqual(500) }) - it("gracefully fail with exceptions (async router)", async () => { + it('gracefully fail with exceptions (async router)', async () => { const { fetch, server } = components server.resetMiddlewares() const r = new Router() - r.get("/hola", async () => { - throw new Error("some exception") + r.get('/hola', async () => { + throw new Error('some exception') }) server.use(r.middleware()) @@ -526,13 +526,13 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(500) }) - it("gracefully fail with exceptions (sync router)", async () => { + it('gracefully fail with exceptions (sync router)', async () => { const { fetch, server } = components server.resetMiddlewares() const r = new Router() - r.get("/hola", () => { - throw new Error("some exception") + r.get('/hola', () => { + throw new Error('some exception') }) server.use(r.middleware()) @@ -541,25 +541,25 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(500) }) - it("gracefully fail with exceptions (sync)", async () => { + it('gracefully fail with exceptions (sync)', async () => { const { fetch, server } = components server.resetMiddlewares() server.use((ctx) => { - throw new Error("some exception") + throw new Error('some exception') }) const res = await fetch.fetch(`/hola`) expect(res.status).toEqual(500) }) - it("gracefully fail with sinon", async () => { + it('gracefully fail with sinon', async () => { const { fetch, server } = components server.resetMiddlewares() const module = { - fn() {}, + fn() {} } - Sinon.stub(module).fn.throwsException("some exception") + Sinon.stub(module).fn.throwsException('some exception') server.use(async (ctx) => { module.fn() return {} @@ -569,23 +569,23 @@ function integrationSuite({ components }: { components: TestComponents }) { expect(res.status).toEqual(500) }) - describe("failures inside router", () => { - it("gracefully fail with exceptions (async)", async () => { + describe('failures inside router', () => { + it('gracefully fail with exceptions (async)', async () => { const { fetch, server } = components server.resetMiddlewares() const routes = new Router() server.use(routes.middleware()) server.use(routes.allowedMethods()) - routes.get("/hola", async (ctx) => { - throw new Error("some exception") + routes.get('/hola', async (ctx) => { + throw new Error('some exception') }) const res = await fetch.fetch(`/hola`) expect(res.status).toEqual(500) }) - it("gracefully fail with exceptions (sync)", async () => { + it('gracefully fail with exceptions (sync)', async () => { const { fetch, server } = components server.resetMiddlewares() @@ -593,26 +593,26 @@ function integrationSuite({ components }: { components: TestComponents }) { server.use(routes.middleware()) server.use(routes.allowedMethods()) - routes.get("/hola", (ctx) => { - throw new Error("some exception") + routes.get('/hola', (ctx) => { + throw new Error('some exception') }) const res = await fetch.fetch(`/hola`) expect(res.status).toEqual(500) }) - it("gracefully fail with sinon", async () => { + it('gracefully fail with sinon', async () => { const { fetch, server } = components server.resetMiddlewares() const module = { - fn() {}, + fn() {} } - Sinon.stub(module).fn.throwsException("some exception") + Sinon.stub(module).fn.throwsException('some exception') const routes = new Router() server.use(routes.middleware()) server.use(routes.allowedMethods()) - routes.get("/hola", async (ctx) => { + routes.get('/hola', async (ctx) => { module.fn() return {} }) diff --git a/test/layer.spec.ts b/test/layer.spec.ts index 04dd3b0..2f5f59c 100644 --- a/test/layer.spec.ts +++ b/test/layer.spec.ts @@ -1,153 +1,153 @@ -import { createTestServerComponent } from "../src" -import { Layer } from "../src/layer" -import { Router } from "../src/router" +import { createTestServerComponent } from '../src' +import { Layer } from '../src/layer' +import { Router } from '../src/router' -describe("Layer", function () { - it("composes multiple callbacks/middlware", async function () { +describe('Layer', function () { + it('composes multiple callbacks/middlware', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/:category/:title", async function (ctx, next) { + router.get('/:category/:title', async function (ctx, next) { return { status: 500, ...(await next()) } }) app.use(async function (ctx, next) { return { status: 204 } }) - const res = await app.fetch("/programming/how-to-node") + const res = await app.fetch('/programming/how-to-node') expect(res.status).toEqual(204) }) - describe("Layer#match()", function () { - it("captures URL path parameters", async function () { + describe('Layer#match()', function () { + it('captures URL path parameters', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/:category/:title", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "match") - expect(ctx.params).toHaveProperty("title", "this") + router.get('/:category/:title', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', 'match') + expect(ctx.params).toHaveProperty('title', 'this') return { status: 204 } }) - const res = await app.fetch("/match/this") + const res = await app.fetch('/match/this') expect(res.status).toEqual(204) }) - it("return original path parameters when decodeURIComponent throw error", async function () { + it('return original path parameters when decodeURIComponent throw error', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/:category/:title", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "100%") - expect(ctx.params).toHaveProperty("title", "101%") + router.get('/:category/:title', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', '100%') + expect(ctx.params).toHaveProperty('title', '101%') return { status: 204 } }) - const res = await app.fetch("/100%/101%") + const res = await app.fetch('/100%/101%') expect(res.status).toEqual(204) }) - it("should throw friendly error message when handle not exists", function () { + it('should throw friendly error message when handle not exists', function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) const notexistHandle: any = undefined expect(function () { - router.get("/foo", notexistHandle) - }).toThrow("Middleware must be composed of functions!") + router.get('/foo', notexistHandle) + }).toThrow('Middleware must be composed of functions!') }) }) - describe("Layer#param()", function () { - it("composes middleware for param fn", async function () { + describe('Layer#param()', function () { + it('composes middleware for param fn', async function () { const app = createTestServerComponent() const router = new Router() const route = new Layer( - "/users/:user", - ["GET"], + '/users/:user', + ['GET'], [ async function (ctx) { return { body: ctx.params } - }, + } ] ) router.stack.push(route) app.use(router.middleware()) - const res = await app.fetch("/users/3") + const res = await app.fetch('/users/3') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("user", "3") + expect(await res.json()).toHaveProperty('user', '3') }) - it("param with paramNames positive check", function () { + it('param with paramNames positive check', function () { const route = new Layer( - "/:category/:title", - ["GET"], + '/:category/:title', + ['GET'], [ async function (_, next) { return next() - }, + } ], - { name: "books" } + { name: 'books' } ) route.paramNames = [ { - name: "category", - } as any, + name: 'category' + } as any ] - const paramSet = route.params(["programming", "ydkjs"], { title: "how-to-code" }) - expect(paramSet).toHaveProperty("title", "how-to-code") - expect(paramSet).toHaveProperty("category", "programming") + const paramSet = route.params(['programming', 'ydkjs'], { title: 'how-to-code' }) + expect(paramSet).toHaveProperty('title', 'how-to-code') + expect(paramSet).toHaveProperty('category', 'programming') }) }) - describe("Layer#url()", function () { - it("setPrefix method checks Layer for path", function () { + describe('Layer#url()', function () { + it('setPrefix method checks Layer for path', function () { const route = new Layer( - "/category", - ["GET"], + '/category', + ['GET'], [ async function (_, next) { return next() - }, + } ], - { name: "books" } + { name: 'books' } ) - route.path = "/hunter2" - const prefix = route.setPrefix("TEST") - expect(prefix.path).toEqual("TEST/hunter2") + route.path = '/hunter2' + const prefix = route.setPrefix('TEST') + expect(prefix.path).toEqual('TEST/hunter2') }) }) - describe("Layer#prefix", () => { - it("setPrefix method passes check Layer for path", function () { + describe('Layer#prefix', () => { + it('setPrefix method passes check Layer for path', function () { const route = new Layer( - "/category", - ["GET"], + '/category', + ['GET'], [ async function (_, next) { return next() - }, + } ], - { name: "books" } + { name: 'books' } ) - route.path = "/hunter2" - const prefix = route.setPrefix("/TEST") - expect(prefix.path).toEqual("/TEST/hunter2") + route.path = '/hunter2' + const prefix = route.setPrefix('/TEST') + expect(prefix.path).toEqual('/TEST/hunter2') }) - it("setPrefix method fails check Layer for path", function () { + it('setPrefix method fails check Layer for path', function () { const route = new Layer( false as any, - ["GET"], + ['GET'], [ async function (_, next) { return next() - }, + } ], - { name: "books" } + { name: 'books' } ) ;(route as any).path = false - const prefix = route.setPrefix("/TEST") + const prefix = route.setPrefix('/TEST') expect(prefix.path).toEqual(false) }) }) diff --git a/test/mockedLifecycleComponent.ts b/test/mockedLifecycleComponent.ts index 6411dcd..b5a2233 100644 --- a/test/mockedLifecycleComponent.ts +++ b/test/mockedLifecycleComponent.ts @@ -1,5 +1,5 @@ -import future from "fp-future" -import { MockedLifecycleComponent } from "./test-helpers" +import future from 'fp-future' +import { MockedLifecycleComponent } from './test-helpers' export function createMockedLifecycleComponent(): MockedLifecycleComponent { const startupProbe = future() @@ -33,6 +33,6 @@ export function createMockedLifecycleComponent(): MockedLifecycleComponent { }, setStartupProbe(p) { p.then(startupProbe.resolve) - }, + } } } diff --git a/test/router.spec.ts b/test/router.spec.ts index 66d150c..15604b1 100644 --- a/test/router.spec.ts +++ b/test/router.spec.ts @@ -1,129 +1,129 @@ /** * Router tests */ -import HttpError from "http-errors" -import fs from "fs" -import path from "path" -import { createTestServerComponent, Router } from "../src" -import { Layer } from "../src/layer" -import { methodsList } from "../src/methods" -import { IHttpServerComponent } from "@well-known-components/interfaces" +import HttpError from 'http-errors' +import fs from 'fs' +import path from 'path' +import { createTestServerComponent, Router } from '../src' +import { Layer } from '../src/layer' +import { methodsList } from '../src/methods' +import { IHttpServerComponent } from '@well-known-components/interfaces' const methods: Lowercase[] = methodsList.map(($) => $.toLowerCase()) as any -describe("Router", function () { - it("shares context between routers (gh-205)", async function () { +describe('Router', function () { + it('shares context between routers (gh-205)', async function () { const app = createTestServerComponent() const router1 = new Router<{ foo?: any }>() const router2 = new Router<{ baz?: any; foo?: any }>() - router1.get("/", async function (ctx, next) { - ctx.foo = "bar" + router1.get('/', async function (ctx, next) { + ctx.foo = 'bar' return next() }) - router2.get("/", async function (ctx, next) { - ctx.baz = "qux" + router2.get('/', async function (ctx, next) { + ctx.baz = 'qux' return { body: { foo: ctx.foo } } }) app.use(router1.middleware()) app.use(router2.middleware()) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foo", "bar") + expect(await res.json()).toHaveProperty('foo', 'bar') }) - it("nested routes", async function () { + it('nested routes', async function () { const app = createTestServerComponent() const parentRouter = new Router<{ n?: number }>() const nestedRouter = new Router<{ n?: number }>() - parentRouter.use("/a", async function (ctx, next) { + parentRouter.use('/a', async function (ctx, next) { ctx.n = ctx.n ? ctx.n + 1 : 1 return next() }) - nestedRouter.get("/b", async function (ctx, next) { + nestedRouter.get('/b', async function (ctx, next) { return { body: { n: ctx.n }, status: 300 } }) - parentRouter.use("/a", nestedRouter.middleware()) + parentRouter.use('/a', nestedRouter.middleware()) app.use(parentRouter.middleware()) - const res = await app.fetch("/a/b") + const res = await app.fetch('/a/b') expect(res.status).toEqual(300) - expect(await res.json()).toHaveProperty("n", 1) + expect(await res.json()).toHaveProperty('n', 1) }) - it("does not register middleware more than once (gh-184)", async function () { + it('does not register middleware more than once (gh-184)', async function () { const app = createTestServerComponent() const parentRouter = new Router<{ n?: number }>() const nestedRouter = new Router<{ n?: number }>() - nestedRouter.get("/first-nested-route", async function (ctx, next) { + nestedRouter.get('/first-nested-route', async function (ctx, next) { return { body: { n: ctx.n } } }) - nestedRouter.get("/second-nested-route", async function (ctx, next) { + nestedRouter.get('/second-nested-route', async function (ctx, next) { return next() }) - nestedRouter.get("/third-nested-route", async function (ctx, next) { + nestedRouter.get('/third-nested-route', async function (ctx, next) { return next() }) - parentRouter.use("/parent-route", async function (ctx, next) { + parentRouter.use('/parent-route', async function (ctx, next) { ctx.n = ctx.n ? ctx.n + 1 : 1 return next() }) - parentRouter.use("/parent-route", nestedRouter.middleware()) + parentRouter.use('/parent-route', nestedRouter.middleware()) app.use(parentRouter.middleware()) - const res = await app.fetch("/parent-route/first-nested-route") + const res = await app.fetch('/parent-route/first-nested-route') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("n", 1) + expect(await res.json()).toHaveProperty('n', 1) }) function sleep(n: number) { return new Promise((resolve) => setTimeout(resolve, n)) } - it("registers multiple middleware for one route", async function () { + it('registers multiple middleware for one route', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/double", async function (ctx, next) { + router.get('/double', async function (ctx, next) { await sleep(1) const n = await next() - return { ...n, body: { message: "Hello" + (n.body as any).message } } + return { ...n, body: { message: 'Hello' + (n.body as any).message } } }) - router.get("/double", async function (ctx, next) { + router.get('/double', async function (ctx, next) { await sleep(1) const n = await next() - return { ...n, body: { message: " World" + (n.body as any).message } } + return { ...n, body: { message: ' World' + (n.body as any).message } } }) - router.get("/double", async function (ctx, next) { - return { body: { message: "!" } } + router.get('/double', async function (ctx, next) { + return { body: { message: '!' } } }) app.use(router.middleware()) - const res = await app.fetch("/double") + const res = await app.fetch('/double') expect(res.status).toEqual(200) - expect(((await res.json()) as any).message).toEqual("Hello World!") + expect(((await res.json()) as any).message).toEqual('Hello World!') }) - it("supports promises for async/await", async function () { + it('supports promises for async/await', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/async", async function (_, next) { + router.get('/async', async function (_, next) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve({ body: { - msg: "promises!", - }, + msg: 'promises!' + } }) }, 1) }) @@ -131,9 +131,9 @@ describe("Router", function () { app.use(router.middleware()) app.use(router.allowedMethods()) - const res = await app.fetch("/async") + const res = await app.fetch('/async') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("msg", "promises!") + expect(await res.json()).toHaveProperty('msg', 'promises!') }) // it("matches middleware only if route was matched (gh-182)", async function () { @@ -160,364 +160,364 @@ describe("Router", function () { // expect(res.body['bar']).toBeFalsy() // }) - it("matches first to last", async function () { + it('matches first to last', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/user/(.*).jsx", async function (ctx) { + router.get('/user/(.*).jsx', async function (ctx) { return { body: { order: 1 } } }) - router.all("/app/(.*).jsx", async function (ctx) { + router.all('/app/(.*).jsx', async function (ctx) { return { body: { order: 2 } } }) - router.all("(.*).jsx", async function (ctx) { + router.all('(.*).jsx', async function (ctx) { return { body: { order: 3 } } }) app.use(router.middleware()) - const res = await app.fetch("/user/account.jsx") + const res = await app.fetch('/user/account.jsx') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("order", 1) + expect(await res.json()).toHaveProperty('order', 1) }) - it("does not run subsequent middleware without calling next", async function () { + it('does not run subsequent middleware without calling next', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/user/(.*).jsx", async function (ctx) { + router.get('/user/(.*).jsx', async function (ctx) { // no next() return { status: 404 } }) - router.get("/user/(.*).jsx", async function (ctx) { + router.get('/user/(.*).jsx', async function (ctx) { return { body: { order: 1 } } }) app.use(router.middleware()) - const res = await app.fetch("/user/account.jsx") + const res = await app.fetch('/user/account.jsx') expect(res.status).toEqual(404) }) - it("nests routers with prefixes at root", async function () { + it('nests routers with prefixes at root', async function () { const app = createTestServerComponent() const forums = new Router({ - prefix: "/forums", + prefix: '/forums' }) const posts = new Router({ - prefix: "/:fid/posts", + prefix: '/:fid/posts' }) - posts.get("/", async function (ctx, next) { + posts.get('/', async function (ctx, next) { return { status: 204 } }) - posts.get("/:pid", async function (ctx, next) { + posts.get('/:pid', async function (ctx, next) { return { body: ctx.params, status: 301 } }) forums.use(posts.middleware()) app.use(forums.middleware()) { - const res = await app.fetch("/forums/1/posts") + const res = await app.fetch('/forums/1/posts') expect(res.status).toEqual(204) } { - const res = await app.fetch("/forums/1") + const res = await app.fetch('/forums/1') expect(res.status).toEqual(404) } { - const res = await app.fetch("/forums/1/posts/2") + const res = await app.fetch('/forums/1/posts/2') expect(res.status).toEqual(301) const json = await res.json() - expect(json).toEqual({ fid: "1", pid: "2" }) + expect(json).toEqual({ fid: '1', pid: '2' }) } }) - it("nests routers with prefixes at path", async function () { + it('nests routers with prefixes at path', async function () { const app = createTestServerComponent() const forums = new Router({ - prefix: "/api", + prefix: '/api' }) const posts = new Router({ - prefix: "/posts", + prefix: '/posts' }) - posts.get("/", async function (ctx, next) { + posts.get('/', async function (ctx, next) { return { ...(await next()), status: 204 } }) - posts.get("/:pid", async function (ctx, next) { + posts.get('/:pid', async function (ctx, next) { return { body: ctx.params, status: 301 } }) - forums.use("/forums/:fid", posts.middleware()) + forums.use('/forums/:fid', posts.middleware()) app.use(forums.middleware()) { - const res = await app.fetch("/api/forums/1/posts") + const res = await app.fetch('/api/forums/1/posts') expect(res.status).toEqual(204) } { - const res = await app.fetch("/api/forums/1") + const res = await app.fetch('/api/forums/1') expect(res.status).toEqual(404) } { - const res = await app.fetch("/api/forums/1/posts/2") + const res = await app.fetch('/api/forums/1/posts/2') expect(res.status).toEqual(301) const json = await res.json() - expect(json).toEqual({ fid: "1", pid: "2" }) + expect(json).toEqual({ fid: '1', pid: '2' }) } }) - it("runs subrouter middleware after parent", async function () { + it('runs subrouter middleware after parent', async function () { const app = createTestServerComponent() const subrouter = new Router<{ msg?: any }>() subrouter .use(function (ctx, next) { - ctx.msg = "subrouter" + ctx.msg = 'subrouter' return next() }) - .get("/", async function (ctx) { + .get('/', async function (ctx) { return { body: { msg: ctx.msg } } }) const router = new Router<{ msg?: any }>() router .use(function (ctx, next) { - ctx.msg = "router" + ctx.msg = 'router' return next() }) .use(subrouter.middleware()) app.use(router.middleware()) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("msg", "subrouter") + expect(await res.json()).toHaveProperty('msg', 'subrouter') }) - it("runs parent middleware for subrouter routes", async function () { + it('runs parent middleware for subrouter routes', async function () { const app = createTestServerComponent() const subrouter = new Router<{ msg?: any }>() - subrouter.get("/sub", async function (ctx) { + subrouter.get('/sub', async function (ctx) { return { body: { msg: ctx.msg } } }) const router = new Router<{ msg?: any }>() router .use(function (ctx, next) { - ctx.msg = "router" + ctx.msg = 'router' return next() }) - .use("/parent", subrouter.middleware()) + .use('/parent', subrouter.middleware()) app.use(router.middleware()) - const res = await app.fetch("/parent/sub") + const res = await app.fetch('/parent/sub') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("msg", "router") + expect(await res.json()).toHaveProperty('msg', 'router') }) - it("matches corresponding requests", async function () { + it('matches corresponding requests', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/:category/:title", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "programming") - expect(ctx.params).toHaveProperty("title", "how-to-node") + router.get('/:category/:title', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', 'programming') + expect(ctx.params).toHaveProperty('title', 'how-to-node') return { status: 204 } }) - router.post("/:category", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "programming") + router.post('/:category', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', 'programming') return { status: 204 } }) - router.put("/:category/not-a-title", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "programming") + router.put('/:category/not-a-title', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', 'programming') // ctx.params.should.not.have.property("title") return { status: 204 } }) { - const res = await app.fetch("/programming/how-to-node") + const res = await app.fetch('/programming/how-to-node') expect(res.status).toEqual(204) } { - const res = await app.fetch("/programming", { method: "post" }) + const res = await app.fetch('/programming', { method: 'post' }) expect(res.status).toEqual(204) } { - const res = await app.fetch("/programming/not-a-title", { method: "put" }) + const res = await app.fetch('/programming/not-a-title', { method: 'put' }) expect(res.status).toEqual(204) } }) - it("matches corresponding requests with optional route parameter", async function () { + it('matches corresponding requests with optional route parameter', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/resources", async function (ctx) { - expect(ctx).toHaveProperty("params") + router.get('/resources', async function (ctx) { + expect(ctx).toHaveProperty('params') expect(ctx.params).toEqual({}) return { status: 204 } }) - const id = "10" - const ext = ".json" - router.get("/resources/:id{.:ext}?", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("id", id) + const id = '10' + const ext = '.json' + router.get('/resources/:id{.:ext}?', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('id', id) // if (ctx.params.ext) ctx.params.ext.should.be.equal(ext.substring(1)) return { status: 204 } }) { - const res = await app.fetch("/resources") + const res = await app.fetch('/resources') expect(res.status).toEqual(204) } { - const res = await app.fetch("/resources/" + id) + const res = await app.fetch('/resources/' + id) expect(res.status).toEqual(204) } { - const res = await app.fetch("/resources/" + id + ext) + const res = await app.fetch('/resources/' + id + ext) expect(res.status).toEqual(204) } }) - it("executes route middleware using `app.context`", async function () { + it('executes route middleware using `app.context`', async function () { const app = createTestServerComponent() const router = new Router<{ bar?: any; foo?: any }>() app.use(router.middleware()) router.use(function (ctx, next) { - ctx.bar = "baz" + ctx.bar = 'baz' return next() }) - router.get("/:category/:title", function (ctx, next) { - ctx.foo = "bar" + router.get('/:category/:title', function (ctx, next) { + ctx.foo = 'bar' return next() }) - router.get("/:category/:title", async function (ctx) { - expect(ctx).toHaveProperty("bar", "baz") - expect(ctx).toHaveProperty("foo", "bar") - expect(ctx).toHaveProperty("app", true) + router.get('/:category/:title', async function (ctx) { + expect(ctx).toHaveProperty('bar', 'baz') + expect(ctx).toHaveProperty('foo', 'bar') + expect(ctx).toHaveProperty('app', true) return { status: 204 } }) app.setContext({ - app: true, + app: true }) - const res = await app.fetch("/match/this") + const res = await app.fetch('/match/this') expect(res.status).toEqual(204) }) }) -it("does not match after ctx.throw()", async function () { +it('does not match after ctx.throw()', async function () { const app = createTestServerComponent() let counter = 0 const router = new Router() app.use(router.middleware()) - router.get("/", async function (ctx) { + router.get('/', async function (ctx) { counter++ throw new HttpError[403]() }) - router.get("/", async function (_, n) { + router.get('/', async function (_, n) { counter++ return n() }) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(counter).toEqual(1) expect(res.status).toEqual(403) }) -it("supports promises for route middleware", async function () { +it('supports promises for route middleware', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) const readVersion = function () { return new Promise(function (resolve, reject) { // const packagePath = path.join(import.meta.url, "..", "..", "package.json").replace(/^file:/, '') - const packagePath = path.join(__dirname, "..", "package.json").replace(/^file:/, "") - fs.readFile(packagePath, "utf8", function (err, data) { + const packagePath = path.join(__dirname, '..', 'package.json').replace(/^file:/, '') + fs.readFile(packagePath, 'utf8', function (err, data) { if (err) return reject(err) resolve(JSON.parse(data).version) }) }) } - router.get("/", function (ctx, next) { + router.get('/', function (ctx, next) { return next() }) - router.get("/", async function (ctx) { + router.get('/', async function (ctx) { await readVersion() return { status: 204 } }) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(res.status).toEqual(204) }) -it("routes registered after middleware creation must work", async function () { +it('routes registered after middleware creation must work', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.get("/", async function (ctx, next) { + router.get('/', async function (ctx, next) { return { status: 201 } }) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(res.status).toEqual(201) }) -it("routes registered before middleware creation must work", async function () { +it('routes registered before middleware creation must work', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/", async function (ctx, next) { + router.get('/', async function (ctx, next) { return { status: 201 } }) app.use(router.middleware()) - const res = await app.fetch("/") + const res = await app.fetch('/') expect(res.status).toEqual(201) }) -it("returning json should include the content-length", async function () { +it('returning json should include the content-length', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/", async function (ctx, next) { + router.get('/', async function (ctx, next) { return { status: 201, body: {} } }) app.use(router.middleware()) - const res = await app.fetch("/") - expect(res.headers.get("content-length")).toEqual("2") + const res = await app.fetch('/') + expect(res.headers.get('content-length')).toEqual('2') }) -describe("Router#allowedMethods()", function () { - it("responds to OPTIONS requests", async function () { +describe('Router#allowedMethods()', function () { + it('responds to OPTIONS requests', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) app.use(router.allowedMethods()) - router.get("/users", async function (ctx, next) { + router.get('/users', async function (ctx, next) { return next() }) - router.put("/users", async function (ctx, next) { + router.put('/users', async function (ctx, next) { return next() }) - const res = await app.fetch("/users", { method: "options" }) + const res = await app.fetch('/users', { method: 'options' }) expect(res.status).toEqual(200) - expect(res.headers.get("content-length")).toEqual("0") - expect(res.headers.get("allow")).toEqual("HEAD, GET, PUT") + expect(res.headers.get('content-length')).toEqual('0') + expect(res.headers.get('allow')).toEqual('HEAD, GET, PUT') }) - it("responds with 405 Method Not Allowed", async function () { + it('responds with 405 Method Not Allowed', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - router.post("/events", function (_, next) { + router.post('/events', function (_, next) { return next() }) app.use(router.middleware()) app.use(router.allowedMethods()) - const res = await app.fetch("/users", { method: "post" }) + const res = await app.fetch('/users', { method: 'post' }) expect(res.status).toEqual(405) - expect(res.headers.get("allow")).toEqual("HEAD, GET, PUT") + expect(res.headers.get('allow')).toEqual('HEAD, GET, PUT') }) it('responds with 405 Method Not Allowed using the "throw" option', async function () { @@ -529,27 +529,27 @@ describe("Router#allowedMethods()", function () { return await next() } catch (err: any) { // assert that the correct HTTPError was thrown - expect(err.name).toEqual("MethodNotAllowedError") + expect(err.name).toEqual('MethodNotAllowedError') expect(err.status).toEqual(405) return { body: err.name, status: err.status } } }) app.use(router.allowedMethods({ throw: true })) - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - router.post("/events", function (_, next) { + router.post('/events', function (_, next) { return next() }) - const res = await app.fetch("/users", { method: "post" }) + const res = await app.fetch('/users', { method: 'post' }) expect(res.status).toEqual(405) // the 'Allow' header is not set when throwing - expect(res.headers.get("allow")).toBeNull() - expect(res.headers.get("Allow")).toBeNull() + expect(res.headers.get('allow')).toBeNull() + expect(res.headers.get('Allow')).toBeNull() }) it('responds with user-provided throwable using the "throw" and "methodNotAllowed" options', async function () { @@ -561,7 +561,7 @@ describe("Router#allowedMethods()", function () { return await next() } catch (err: any) { // assert that the correct HTTPError was thrown - expect(err.message).toEqual("Custom Not Allowed Error") + expect(err.message).toEqual('Custom Not Allowed Error') expect(err.status).toEqual(405) return { body: err.body, status: err.status } } @@ -570,47 +570,47 @@ describe("Router#allowedMethods()", function () { router.allowedMethods({ throw: true, methodNotAllowed: function () { - const notAllowedErr: any = new Error("Custom Not Allowed Error") - notAllowedErr.type = "custom" + const notAllowedErr: any = new Error('Custom Not Allowed Error') + notAllowedErr.type = 'custom' notAllowedErr.status = 405 notAllowedErr.body = { - error: "Custom Not Allowed Error", + error: 'Custom Not Allowed Error', status: 405, - otherStuff: true, + otherStuff: true } return notAllowedErr - } as any, + } as any }) ) - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - router.post("/events", function (_, next) { + router.post('/events', function (_, next) { return next() }) - const res = await app.fetch("/users", { method: "post" }) + const res = await app.fetch('/users', { method: 'post' }) expect(res.status).toEqual(405) // the 'Allow' header is not set when throwing // res.header.should.not.have.property("allow") - expect(await res.json()).toEqual({ error: "Custom Not Allowed Error", status: 405, otherStuff: true }) + expect(await res.json()).toEqual({ error: 'Custom Not Allowed Error', status: 405, otherStuff: true }) }) - it("responds with 501 Not Implemented", async function () { + it('responds with 501 Not Implemented', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) app.use(router.allowedMethods()) - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - const res = await app.fetch("/users", { method: "search" }) + const res = await app.fetch('/users', { method: 'search' }) expect(res.status).toEqual(501) }) @@ -623,25 +623,25 @@ describe("Router#allowedMethods()", function () { return next() } catch (err: any) { // assert that the correct HTTPError was thrown - expect(err.name).toEqual("NotImplementedError") + expect(err.name).toEqual('NotImplementedError') expect(err.status).toEqual(501) return { body: err.name, status: err.status } } }) app.use(router.allowedMethods({ throw: true })) - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - const res = await app.fetch("/users", { method: "search" }) + const res = await app.fetch('/users', { method: 'search' }) expect(res.status).toEqual(501) // the 'Allow' header is not set when throwing // res.header.should.not.have.property("allow") - expect(res.headers.get("allow")).toBeNull() - expect(res.headers.get("Allow")).toBeNull() + expect(res.headers.get('allow')).toBeNull() + expect(res.headers.get('Allow')).toBeNull() }) it('responds with user-provided throwable using the "throw" and "notImplemented" options', async function () { @@ -653,12 +653,12 @@ describe("Router#allowedMethods()", function () { return next() } catch (err: any) { // assert that our custom error was thrown - expect(err.message).toEqual("Custom Not Implemented Error") - expect(err.type).toEqual("custom") + expect(err.message).toEqual('Custom Not Implemented Error') + expect(err.type).toEqual('custom') expect(err.status).toEqual(501) return { body: err.body, - status: err.status, + status: err.status } } }) @@ -666,411 +666,411 @@ describe("Router#allowedMethods()", function () { router.allowedMethods({ throw: true, notImplemented: function () { - const notImplementedErr: any = new Error("Custom Not Implemented Error") - notImplementedErr.type = "custom" + const notImplementedErr: any = new Error('Custom Not Implemented Error') + notImplementedErr.type = 'custom' notImplementedErr.status = 501 notImplementedErr.body = { - error: "Custom Not Implemented Error", + error: 'Custom Not Implemented Error', status: 501, - otherStuff: true, + otherStuff: true } return notImplementedErr - } as any, + } as any }) ) - router.get("/users", function (_, next) { + router.get('/users', function (_, next) { return next() }) - router.put("/users", function (_, next) { + router.put('/users', function (_, next) { return next() }) - const res = await app.fetch("/users", { method: "search" }) + const res = await app.fetch('/users', { method: 'search' }) expect(res.status).toEqual(501) // the 'Allow' header is not set when throwing - expect(res.headers.has("allow")).toBeFalsy() - expect(await res.json()).toEqual({ error: "Custom Not Implemented Error", status: 501, otherStuff: true }) + expect(res.headers.has('allow')).toBeFalsy() + expect(await res.json()).toEqual({ error: 'Custom Not Implemented Error', status: 501, otherStuff: true }) }) - it("does not send 405 if route matched but status is 404", async function () { + it('does not send 405 if route matched but status is 404', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) app.use(router.allowedMethods()) - router.get("/users", async function (ctx, next) { + router.get('/users', async function (ctx, next) { return { status: 404 } }) - const res = await app.fetch("/users") + const res = await app.fetch('/users') expect(res.status).toEqual(404) }) - it("sets the allowed methods to a single Allow header #273", async function () { + it('sets the allowed methods to a single Allow header #273', async function () { // https://tools.ietf.org/html/rfc7231#section-7.4.1 const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) app.use(router.allowedMethods()) - router.get("/", async function (ctx, next) { + router.get('/', async function (ctx, next) { return next() }) - const res = await app.fetch("/", { method: "options" }) + const res = await app.fetch('/', { method: 'options' }) expect(res.status).toEqual(200) - expect(res.headers.get("allow")).toEqual("HEAD, GET") + expect(res.headers.get('allow')).toEqual('HEAD, GET') }) }) -it("allowedMethods check if flow (allowedArr.length)", async function () { +it('allowedMethods check if flow (allowedArr.length)', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) app.use(router.allowedMethods()) - const res = await app.fetch("/users") + const res = await app.fetch('/users') }) -it("supports custom routing detect path: ctx.routerPath", async function () { +it('supports custom routing detect path: ctx.routerPath', async function () { const app = createTestServerComponent<{ routerPath: string }>() const router = new Router() app.use(function (ctx, next) { // bind helloworld.example.com/users => example.com/helloworld/users - const appname = ctx.url.hostname.split(".", 1)[0] - ctx.routerPath = "/" + appname + ctx.url.pathname + const appname = ctx.url.hostname.split('.', 1)[0] + ctx.routerPath = '/' + appname + ctx.url.pathname return next() }) app.use(router.middleware()) - router.get("/helloworld/users", async function (ctx) { - return { body: ctx.request.method + " " + ctx.url.pathname } + router.get('/helloworld/users', async function (ctx) { + return { body: ctx.request.method + ' ' + ctx.url.pathname } }) - const res = await app.fetch("/users", { headers: { Host: "helloworld.example.com" } }) + const res = await app.fetch('/users', { headers: { Host: 'helloworld.example.com' } }) expect(res.status).toEqual(200) - expect(await res.text()).toEqual("GET /users") + expect(await res.text()).toEqual('GET /users') }) -it("parameter added to request in ctx", async function () { +it('parameter added to request in ctx', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/echo/:saying", async function (ctx) { + router.get('/echo/:saying', async function (ctx) { try { - expect(ctx.params.saying).toEqual("helloWorld") + expect(ctx.params.saying).toEqual('helloWorld') return { body: { echo: ctx.params.saying } } } catch (err: any) { return { status: 500, body: err.message } } }) app.use(router.middleware()) - const res = await app.fetch("/echo/helloWorld") + const res = await app.fetch('/echo/helloWorld') expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ echo: "helloWorld" }) + expect(await res.json()).toEqual({ echo: 'helloWorld' }) }) -it("parameter added to request in ctx with sub router", async function () { +it('parameter added to request in ctx with sub router', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any }>() const subrouter = new Router<{ foo?: any }>() router.use(function (ctx, next) { - ctx.foo = "boo" + ctx.foo = 'boo' return next() }) - subrouter.get("/:saying", async function (ctx) { + subrouter.get('/:saying', async function (ctx) { try { - expect(ctx.params.saying).toEqual("helloWorld") + expect(ctx.params.saying).toEqual('helloWorld') return { body: { echo: ctx.params.saying } } } catch (err: any) { return { status: 500, body: err.message } } }) - router.use("/echo", subrouter.middleware()) + router.use('/echo', subrouter.middleware()) app.use(router.middleware()) - const res = await app.fetch("/echo/helloWorld") + const res = await app.fetch('/echo/helloWorld') expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ echo: "helloWorld" }) + expect(await res.json()).toEqual({ echo: 'helloWorld' }) }) -describe("Router#[verb]()", function () { - it("registers route specific to HTTP verb", function () { +describe('Router#[verb]()', function () { + it('registers route specific to HTTP verb', function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) methods.forEach(function (method) { expect(router).toHaveProperty(method) - expect(typeof router[method]).toEqual("function") - router[method]("/", function (_, next) { + expect(typeof router[method]).toEqual('function') + router[method]('/', function (_, next) { return next() }) }) expect(router.stack.length).toEqual(methods.length) }) - it("registers route with a given name", function () { + it('registers route with a given name', function () { const router = new Router() methods.forEach(function (method) { expect( - router[method]("/", function (_, next) { + router[method]('/', function (_, next) { return next() }) ).toEqual(router) }) }) - it("enables route chaining", function () { + it('enables route chaining', function () { const router = new Router() methods.forEach(function (method) { expect( - router[method]("/", function (_, next) { + router[method]('/', function (_, next) { return next() }) ).toEqual(router) }) }) - it("registers array of paths (gh-203)", function () { + it('registers array of paths (gh-203)', function () { const router = new Router() - router.get("/one", async function (ctx, next) { + router.get('/one', async function (ctx, next) { return next() }) - router.get("/two", async function (ctx, next) { + router.get('/two', async function (ctx, next) { return next() }) - expect(router.stack).toHaveProperty("length", 2) - expect(router.stack[0]).toHaveProperty("path", "/one") - expect(router.stack[1]).toHaveProperty("path", "/two") + expect(router.stack).toHaveProperty('length', 2) + expect(router.stack[0]).toHaveProperty('path', '/one') + expect(router.stack[1]).toHaveProperty('path', '/two') }) - it("resolves non-parameterized routes without attached parameters", async function () { + it('resolves non-parameterized routes without attached parameters', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/notparameter", async function (ctx, next) { + router.get('/notparameter', async function (ctx, next) { return { body: { - param: (ctx.params as any).parameter, - }, + param: (ctx.params as any).parameter + } } }) - router.get("/:parameter", async function (ctx, next) { + router.get('/:parameter', async function (ctx, next) { return { body: { - param: ctx.params.parameter, - }, + param: ctx.params.parameter + } } }) app.use(router.middleware()) - const res = await app.fetch("/notparameter") + const res = await app.fetch('/notparameter') expect(res.status).toEqual(200) const b: any = await res.json() expect(b.param).toEqual(undefined) }) }) -describe("Router#use()", function () { - it("uses router middleware without path", async function () { +describe('Router#use()', function () { + it('uses router middleware without path', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any }>() router.use(function (ctx, next) { - ctx.foo = "baz" + ctx.foo = 'baz' return next() }) router.use(function (ctx, next) { - ctx.foo = "foo" + ctx.foo = 'foo' return next() }) - router.get("/foo/bar", async function (ctx) { + router.get('/foo/bar', async function (ctx) { return { body: { - foobar: ctx.foo + "bar", - }, + foobar: ctx.foo + 'bar' + } } }) app.use(router.middleware()) - const res = await app.fetch("/foo/bar") + const res = await app.fetch('/foo/bar') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') }) - it("uses router middleware at given path", async function () { + it('uses router middleware at given path', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any }>() - router.use("/foo/bar", async function (ctx, next) { - ctx.foo = "foo" + router.use('/foo/bar', async function (ctx, next) { + ctx.foo = 'foo' return next() }) - router.get("/foo/bar", async function (ctx) { + router.get('/foo/bar', async function (ctx) { return { body: { - foobar: ctx.foo + "bar", - }, + foobar: ctx.foo + 'bar' + } } }) app.use(router.middleware()) - const res = await app.fetch("/foo/bar") + const res = await app.fetch('/foo/bar') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') }) - it("runs router middleware before subrouter middleware", async function () { + it('runs router middleware before subrouter middleware', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any }>() const subrouter = new Router<{ foo?: any }>() router.use(function (ctx, next) { - ctx.foo = "boo" + ctx.foo = 'boo' return next() }) subrouter .use(function (ctx, next) { - ctx.foo = "foo" + ctx.foo = 'foo' return next() }) - .get("/bar", async function (ctx) { + .get('/bar', async function (ctx) { return { body: { - foobar: ctx.foo + "bar", - }, + foobar: ctx.foo + 'bar' + } } }) - router.use("/foo", subrouter.middleware()) + router.use('/foo', subrouter.middleware()) app.use(router.middleware()) - const res = await app.fetch("/foo/bar") + const res = await app.fetch('/foo/bar') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') }) - it("assigns middleware to array of paths", async function () { + it('assigns middleware to array of paths', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any; bar?: any }>() - router.use("/foo", async function (ctx, next) { - ctx.foo = "foo" - ctx.bar = "bar" + router.use('/foo', async function (ctx, next) { + ctx.foo = 'foo' + ctx.bar = 'bar' return next() }) - router.use("/bar", async function (ctx, next) { - ctx.foo = "foo" - ctx.bar = "bar" + router.use('/bar', async function (ctx, next) { + ctx.foo = 'foo' + ctx.bar = 'bar' return next() }) - router.get("/foo", async function (ctx, next) { + router.get('/foo', async function (ctx, next) { return { body: { - foobar: ctx.foo + "bar", - }, + foobar: ctx.foo + 'bar' + } } }) - router.get("/bar", async function (ctx) { + router.get('/bar', async function (ctx) { return { body: { - foobar: "foo" + ctx.bar, - }, + foobar: 'foo' + ctx.bar + } } }) app.use(router.middleware()) { - const res = await app.fetch("/foo") + const res = await app.fetch('/foo') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') } { - const res = await app.fetch("/bar") + const res = await app.fetch('/bar') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') } }) - it("multiple middlewares work .use", async function () { + it('multiple middlewares work .use', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any; bar?: any }>() router.use( - "/foo", + '/foo', async function (ctx, next) { - ctx.foo = "foo" + ctx.foo = 'foo' return next() }, async function (ctx, next) { - ctx.bar = "bar" + ctx.bar = 'bar' return next() } ) - router.get("/foo", async function (ctx, next) { + router.get('/foo', async function (ctx, next) { return { body: { - foobar: ctx.foo + ctx.bar, - }, + foobar: ctx.foo + ctx.bar + } } }) app.use(router.middleware()) { - const res = await app.fetch("/foo") + const res = await app.fetch('/foo') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') } }) - it("multiple middlewares work .get", async function () { + it('multiple middlewares work .get', async function () { const app = createTestServerComponent() const router = new Router<{ foo?: any; bar?: any }>() router.get( - "/foo", + '/foo', async function (ctx, next) { - ctx.foo = "foo" + ctx.foo = 'foo' return next() }, async function (ctx, next) { - ctx.bar = "bar" + ctx.bar = 'bar' return next() } ) - router.get("/foo", async function (ctx, next) { + router.get('/foo', async function (ctx, next) { return { body: { - foobar: ctx.foo + ctx.bar, - }, + foobar: ctx.foo + ctx.bar + } } }) app.use(router.middleware()) { - const res = await app.fetch("/foo") + const res = await app.fetch('/foo') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("foobar", "foobar") + expect(await res.json()).toHaveProperty('foobar', 'foobar') } }) }) -it("without path, does not set params.0 to the matched path - gh-247", async function () { +it('without path, does not set params.0 to the matched path - gh-247', async function () { const app = createTestServerComponent() const router = new Router() @@ -1078,55 +1078,55 @@ it("without path, does not set params.0 to the matched path - gh-247", async fun return next() }) - router.get("/foo/:id", async function (ctx) { + router.get('/foo/:id', async function (ctx) { return { body: ctx.params } }) app.use(router.middleware()) - const res = await app.fetch("/foo/815") + const res = await app.fetch('/foo/815') expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ id: "815" }) + expect(await res.json()).toEqual({ id: '815' }) // expect(res.body).toNotHaveProperty("0") }) -it("does not add an erroneous (.*) to unprefiexed nested routers - gh-369 gh-410", async function () { +it('does not add an erroneous (.*) to unprefiexed nested routers - gh-369 gh-410', async function () { const app = createTestServerComponent() const router = new Router() const nested = new Router() let called = 0 - nested.get("/", async (ctx, next) => { + nested.get('/', async (ctx, next) => { called += 1 - return { body: "root", ...(await next()) } + return { body: 'root', ...(await next()) } }) - nested.get("/test", async (ctx, next) => { + nested.get('/test', async (ctx, next) => { called += 1 - return { body: { hello: "test" } } + return { body: { hello: 'test' } } }) router.use(nested.middleware()) app.use(router.middleware()) - const res = await app.fetch("/test", { method: "get" }) + const res = await app.fetch('/test', { method: 'get' }) expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ hello: "test" }) + expect(await res.json()).toEqual({ hello: 'test' }) expect(called).toEqual(1) }) -it("assigns middleware to array of paths with function middleware and router need to nest. - gh-22", async function () { +it('assigns middleware to array of paths with function middleware and router need to nest. - gh-22', async function () { const app = createTestServerComponent() - const base = new Router<{ foo?: any; bar?: any }>({ prefix: "/api" }) - const nested = new Router<{ foo?: any; bar?: any }>({ prefix: "/qux" }) - const pathList = ["/foo", "/bar"] + const base = new Router<{ foo?: any; bar?: any }>({ prefix: '/api' }) + const nested = new Router<{ foo?: any; bar?: any }>({ prefix: '/qux' }) + const pathList = ['/foo', '/bar'] - nested.get("/baz", async (ctx) => { + nested.get('/baz', async (ctx) => { return { body: { foo: ctx.foo, bar: ctx.bar, - baz: "baz", - }, + baz: 'baz' + } } }) @@ -1134,8 +1134,8 @@ it("assigns middleware to array of paths with function middleware and router nee base.use( path, (ctx, next) => { - ctx.foo = "foo" - ctx.bar = "bar" + ctx.foo = 'foo' + ctx.bar = 'bar' return next() }, @@ -1149,41 +1149,41 @@ it("assigns middleware to array of paths with function middleware and router nee pathList.map(async (pathname) => { const res = await app.fetch(`/api${pathname}/qux/baz`) expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ foo: "foo", bar: "bar", baz: "baz" }) + expect(await res.json()).toEqual({ foo: 'foo', bar: 'bar', baz: 'baz' }) }) ) }) -it("uses a same router middleware at given paths continuously - ZijianHe/koa-router#gh-244 gh-18", async function () { +it('uses a same router middleware at given paths continuously - ZijianHe/koa-router#gh-244 gh-18', async function () { const app = createTestServerComponent() - const base = new Router<{ foo?: any; bar?: any }>({ prefix: "/api" }) - const nested = new Router<{ foo?: any; bar?: any }>({ prefix: "/qux" }) + const base = new Router<{ foo?: any; bar?: any }>({ prefix: '/api' }) + const nested = new Router<{ foo?: any; bar?: any }>({ prefix: '/qux' }) - nested.get("/baz", async (ctx) => { + nested.get('/baz', async (ctx) => { return { body: { foo: ctx.foo, bar: ctx.bar, - baz: "baz", - }, + baz: 'baz' + } } }) base .use( - "/foo", + '/foo', (ctx, next) => { - ctx.foo = "foo" - ctx.bar = "bar" + ctx.foo = 'foo' + ctx.bar = 'bar' return next() }, nested.middleware() ) .use( - "/bar", + '/bar', (ctx, next) => { - ctx.foo = "foo" - ctx.bar = "bar" + ctx.foo = 'foo' + ctx.bar = 'bar' return next() }, @@ -1193,150 +1193,150 @@ it("uses a same router middleware at given paths continuously - ZijianHe/koa-rou app.use(base.middleware()) await Promise.all( - ["/foo", "/bar"].map(async (pathname) => { + ['/foo', '/bar'].map(async (pathname) => { const res = await app.fetch(`/api${pathname}/qux/baz`) expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ foo: "foo", bar: "bar", baz: "baz" }) + expect(await res.json()).toEqual({ foo: 'foo', bar: 'bar', baz: 'baz' }) }) ) }) -describe("Router#register()", function () { - it("registers new routes", async function () { +describe('Router#register()', function () { + it('registers new routes', async function () { const app = createTestServerComponent() const router = new Router() - expect(router).toHaveProperty("register") - expect(typeof router.register).toEqual("function") - const route = router.register("/", ["GET", "POST"], function (_, next) { + expect(router).toHaveProperty('register') + expect(typeof router.register).toEqual('function') + const route = router.register('/', ['GET', 'POST'], function (_, next) { return next() }) app.use(router.middleware()) expect(Array.isArray(router.stack)).toEqual(true) - expect(router.stack).toHaveProperty("length", 1) - expect(router.stack[0]).toHaveProperty("path", "/") + expect(router.stack).toHaveProperty('length', 1) + expect(router.stack[0]).toHaveProperty('path', '/') }) - describe("Router#redirect()", function () { - it("registers redirect routes", async function () { + describe('Router#redirect()', function () { + it('registers redirect routes', async function () { const app = createTestServerComponent() const router = new Router() - expect(router).toHaveProperty("redirect") - expect(typeof router.redirect).toEqual("function") - router.redirect("/source", "/destination", 302) + expect(router).toHaveProperty('redirect') + expect(typeof router.redirect).toEqual('function') + router.redirect('/source', '/destination', 302) app.use(router.middleware()) - expect(router.stack).toHaveProperty("length", 1) + expect(router.stack).toHaveProperty('length', 1) expect(router.stack[0]).toBeInstanceOf(Layer) - expect(router.stack[0]).toHaveProperty("path", "/source") + expect(router.stack[0]).toHaveProperty('path', '/source') }) - it("redirects to external sites", async function () { + it('redirects to external sites', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.redirect("/", "https://www.example.com") - const res = await app.fetch("/", { method: "post" }) + router.redirect('/', 'https://www.example.com') + const res = await app.fetch('/', { method: 'post' }) expect(res.status).toEqual(301) - expect(res.headers.get("location")).toEqual("https://www.example.com") + expect(res.headers.get('location')).toEqual('https://www.example.com') }) - it("redirects to any external protocol", async function () { + it('redirects to any external protocol', async function () { const app = createTestServerComponent() const router = new Router() app.use(router.middleware()) - router.redirect("/", "my-custom-app-protocol://www.example.com/foo") - const res = await app.fetch("/", { method: "post" }) + router.redirect('/', 'my-custom-app-protocol://www.example.com/foo') + const res = await app.fetch('/', { method: 'post' }) expect(res.status).toEqual(301) - expect(res.headers.get("location")).toEqual("my-custom-app-protocol://www.example.com/foo") + expect(res.headers.get('location')).toEqual('my-custom-app-protocol://www.example.com/foo') }) - describe("Router#route()", function () { - it("inherits routes from nested router", function () { + describe('Router#route()', function () { + it('inherits routes from nested router', function () { const subrouter = new Router() - subrouter.get("/hello", async function (ctx) { - return { body: { hello: "world" } } + subrouter.get('/hello', async function (ctx) { + return { body: { hello: 'world' } } }) const router = new Router().use(subrouter.middleware()) - expect(router.match("/hello", "GET").path).toHaveLength(1) + expect(router.match('/hello', 'GET').path).toHaveLength(1) }) }) - describe("Router#opts", function () { - it("responds with 200", async function () { + describe('Router#opts', function () { + it('responds with 200', async function () { const app = createTestServerComponent() const router = new Router({ - strict: true, + strict: true }) - router.get("/info", async function (ctx) { - return { body: { a: "hello" } } + router.get('/info', async function (ctx) { + return { body: { a: 'hello' } } }) app.use(router.middleware()) - const res = await app.fetch("/info") + const res = await app.fetch('/info') expect(res.status).toEqual(200) - expect(await res.json()).toEqual({ a: "hello" }) + expect(await res.json()).toEqual({ a: 'hello' }) }) - it("should allow setting a prefix", async function () { + it('should allow setting a prefix', async function () { const app = createTestServerComponent() - const routes = new Router({ prefix: "/things/:thing_id" }) + const routes = new Router({ prefix: '/things/:thing_id' }) - routes.get("/list", async function (ctx) { + routes.get('/list', async function (ctx) { return { body: ctx.params } }) app.use(routes.middleware()) - const res = await app.fetch("/things/1/list") + const res = await app.fetch('/things/1/list') expect(res.status).toEqual(200) - expect(((await res.json()) as any).thing_id).toEqual("1") + expect(((await res.json()) as any).thing_id).toEqual('1') }) - it("responds with 404 when has a trailing slash", async function () { + it('responds with 404 when has a trailing slash', async function () { const app = createTestServerComponent() const router = new Router({ - strict: true, + strict: true }) - router.get("/info", async function (ctx) { - return { body: { hello: "hello" } } + router.get('/info', async function (ctx) { + return { body: { hello: 'hello' } } }) app.use(router.middleware()) - const res = await app.fetch("/info/") + const res = await app.fetch('/info/') expect(res.status).toEqual(404) }) }) - describe("use middleware with opts", function () { - it("responds with 200", async function () { + describe('use middleware with opts', function () { + it('responds with 200', async function () { const app = createTestServerComponent() const router = new Router({ - strict: true, + strict: true }) - router.get("/info", async function (ctx) { - return { body: { hello: "hello" } } + router.get('/info', async function (ctx) { + return { body: { hello: 'hello' } } }) app.use(router.middleware()) - const res = await app.fetch("/info") + const res = await app.fetch('/info') expect(res.status).toEqual(200) - expect(await res.text()).toEqual(JSON.stringify({ hello: "hello" })) + expect(await res.text()).toEqual(JSON.stringify({ hello: 'hello' })) }) - it("responds with 404 when has a trailing slash", async function () { + it('responds with 404 when has a trailing slash', async function () { const app = createTestServerComponent() const router = new Router({ - strict: true, + strict: true }) - router.get("/info", async function (ctx) { - return { body: { hello: "hello" } } + router.get('/info', async function (ctx) { + return { body: { hello: 'hello' } } }) app.use(router.middleware()) - const res = await app.fetch("/info/") + const res = await app.fetch('/info/') expect(res.status).toEqual(404) }) }) - describe("router.routes()", function () { - it("should return composed middleware", async function () { + describe('router.routes()', function () { + it('should return composed middleware', async function () { const app = createTestServerComponent() const router = new Router() let middlewareCount = 0 @@ -1351,194 +1351,194 @@ describe("Router#register()", function () { return next() } ) - router.get("/users/:id", async function (ctx) { - expect(ctx.params).toHaveProperty("id") - return { body: { hello: "world" } } + router.get('/users/:id', async function (ctx) { + expect(ctx.params).toHaveProperty('id') + return { body: { hello: 'world' } } }) const routerMiddleware = router.middleware() - expect(typeof routerMiddleware).toEqual("function") + expect(typeof routerMiddleware).toEqual('function') app.use(routerMiddleware) - const res = await app.fetch("/users/1", { method: "get" }) + const res = await app.fetch('/users/1', { method: 'get' }) expect(res.status).toEqual(200) const json = await res.json() - expect(json).toHaveProperty("hello", "world") + expect(json).toHaveProperty('hello', 'world') expect(middlewareCount).toEqual(2) }) - it("places a `_matchedRoute` value on context", async function () { + it('places a `_matchedRoute` value on context', async function () { const app = createTestServerComponent() const router = new Router() router.use(async function middleware(ctx, next) { await next() - expect(ctx._matchedRoute).toEqual("/users/:id") + expect(ctx._matchedRoute).toEqual('/users/:id') return {} }) - router.get("/users/:id", async function (ctx, next) { - expect(ctx._matchedRoute).toEqual("/users/:id") - expect(ctx.params).toHaveProperty("id") - return { body: { hello: "world" } } + router.get('/users/:id', async function (ctx, next) { + expect(ctx._matchedRoute).toEqual('/users/:id') + expect(ctx.params).toHaveProperty('id') + return { body: { hello: 'world' } } }) const routerMiddleware = router.middleware() app.use(routerMiddleware) - const res = await app.fetch("/users/1", { method: "get" }) + const res = await app.fetch('/users/1', { method: 'get' }) expect(res.status).toEqual(200) }) - it("places a `routerPath` value on the context for current route", async function () { + it('places a `routerPath` value on the context for current route', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/users/:id", async function (ctx) { - expect(ctx.routerPath).toEqual("/users/:id") + router.get('/users/:id', async function (ctx) { + expect(ctx.routerPath).toEqual('/users/:id') return { status: 200 } }) app.use(router.middleware()) - const res = await app.fetch("/users/1") + const res = await app.fetch('/users/1') expect(res.status).toEqual(200) }) - it("places a `_matchedRoute` value on the context for current route", async function () { + it('places a `_matchedRoute` value on the context for current route', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/users/list", async function (ctx) { - expect(ctx._matchedRoute).toEqual("/users/list") + router.get('/users/list', async function (ctx) { + expect(ctx._matchedRoute).toEqual('/users/list') return { status: 200 } }) - router.get("/users/:id", async function (ctx) { - expect(ctx._matchedRoute).toEqual("/users/:id") + router.get('/users/:id', async function (ctx) { + expect(ctx._matchedRoute).toEqual('/users/:id') return { status: 200 } }) app.use(router.middleware()) - const res = await app.fetch("/users/list") + const res = await app.fetch('/users/list') expect(res.status).toEqual(200) }) }) - describe("If no HEAD method, default to GET", function () { - it("should default to GET", async function () { + describe('If no HEAD method, default to GET', function () { + it('should default to GET', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/users/:id", async function (ctx) { - expect(ctx.params).toHaveProperty("id") + router.get('/users/:id', async function (ctx) { + expect(ctx.params).toHaveProperty('id') return { body: { hello: true } } }) app.use(router.middleware()) - const res = await app.fetch("/users/1", { method: "head" }) + const res = await app.fetch('/users/1', { method: 'head' }) expect(res.status).toEqual(200) expect(await res.text()).toBeFalsy() - expect(res.headers.get("content-length")).toBeFalsy() + expect(res.headers.get('content-length')).toBeFalsy() }) - it("should work with middleware", async function () { + it('should work with middleware', async function () { const app = createTestServerComponent() const router = new Router() - router.get("/users/:id", async function (ctx) { - expect(ctx.params).toHaveProperty("id") + router.get('/users/:id', async function (ctx) { + expect(ctx.params).toHaveProperty('id') return { body: { hello: true } } }) app.use(router.middleware()) - const res = await app.fetch("/users/1", { method: "head" }) + const res = await app.fetch('/users/1', { method: 'head' }) expect(res.status).toEqual(200) expect(await res.text()).toBeFalsy() }) }) - describe("Router#prefix", function () { - it("should set opts.prefix", function () { + describe('Router#prefix', function () { + it('should set opts.prefix', function () { const router = new Router() // expect(router.opts).to.not.have.key("prefix") - router.prefix("/things/:thing_id") - expect(router.opts.prefix).toEqual("/things/:thing_id") + router.prefix('/things/:thing_id') + expect(router.opts.prefix).toEqual('/things/:thing_id') }) - it("should prefix existing routes", function () { + it('should prefix existing routes', function () { const router = new Router() - router.get("/users/:id", async function (ctx) { - return { body: { hello: "test" } } + router.get('/users/:id', async function (ctx) { + return { body: { hello: 'test' } } }) - router.prefix("/things/:thing_id") + router.prefix('/things/:thing_id') const route = router.stack[0] - expect(route.path).toEqual("/things/:thing_id/users/:id") + expect(route.path).toEqual('/things/:thing_id/users/:id') expect(route.paramNames).toHaveLength(2) - expect(route.paramNames[0]).toHaveProperty("name", "thing_id") - expect(route.paramNames[1]).toHaveProperty("name", "id") + expect(route.paramNames[0]).toHaveProperty('name', 'thing_id') + expect(route.paramNames[1]).toHaveProperty('name', 'id') }) - it("populates ctx.params correctly for router prefix (including use)", async function () { + it('populates ctx.params correctly for router prefix (including use)', async function () { var app = createTestServerComponent() - var router = new Router({ prefix: "/:category" }) + var router = new Router({ prefix: '/:category' }) app.use(router.middleware()) router .use((ctx, next) => { - expect(ctx).toHaveProperty("params") - expect(typeof ctx.params).toEqual("object") - expect(ctx.params).toHaveProperty("category", "cats") + expect(ctx).toHaveProperty('params') + expect(typeof ctx.params).toEqual('object') + expect(ctx.params).toHaveProperty('category', 'cats') return next() }) - .get("/suffixHere", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(typeof ctx.params).toEqual("object") - expect(ctx.params).toHaveProperty("category", "cats") + .get('/suffixHere', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(typeof ctx.params).toEqual('object') + expect(ctx.params).toHaveProperty('category', 'cats') return { status: 204 } }) - const res = await app.fetch("/cats/suffixHere") + const res = await app.fetch('/cats/suffixHere') expect(res.status).toEqual(204) }) - it("populates ctx.params correctly for more complex router prefix (including use)", async function () { + it('populates ctx.params correctly for more complex router prefix (including use)', async function () { var app = createTestServerComponent() - var router = new Router({ prefix: "/:category/:color" }) + var router = new Router({ prefix: '/:category/:color' }) app.use(router.middleware()) router .use((ctx, next) => { - expect(ctx).toHaveProperty("params") - expect(typeof ctx.params).toEqual("object") - expect(ctx.params).toHaveProperty("category", "cats") - expect(ctx.params).toHaveProperty("color", "gray") + expect(ctx).toHaveProperty('params') + expect(typeof ctx.params).toEqual('object') + expect(ctx.params).toHaveProperty('category', 'cats') + expect(ctx.params).toHaveProperty('color', 'gray') return next() }) - .get("/:active/suffixHere", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("category", "cats") - expect(ctx.params).toHaveProperty("color", "gray") - expect(ctx.params).toHaveProperty("active", "true") + .get('/:active/suffixHere', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('category', 'cats') + expect(ctx.params).toHaveProperty('color', 'gray') + expect(ctx.params).toHaveProperty('active', 'true') return { status: 204 } }) - const res = await app.fetch("/cats/gray/true/suffixHere") + const res = await app.fetch('/cats/gray/true/suffixHere') expect(res.status).toEqual(204) }) - it("populates ctx.params correctly for static prefix", async function () { + it('populates ctx.params correctly for static prefix', async function () { var app = createTestServerComponent() - var router = new Router({ prefix: "/all" }) + var router = new Router({ prefix: '/all' }) app.use(router.middleware()) router .use((ctx, next) => { - expect(ctx).toHaveProperty("params") - expect(typeof ctx.params).toEqual("object") + expect(ctx).toHaveProperty('params') + expect(typeof ctx.params).toEqual('object') expect(ctx.params).toEqual({}) return next() }) - .get("/:active/suffixHere", async function (ctx) { - expect(ctx).toHaveProperty("params") - expect(ctx.params).toHaveProperty("active", "true") + .get('/:active/suffixHere', async function (ctx) { + expect(ctx).toHaveProperty('params') + expect(ctx.params).toHaveProperty('active', 'true') return { status: 204 } }) - const res = await app.fetch("/all/true/suffixHere") + const res = await app.fetch('/all/true/suffixHere') expect(res.status).toEqual(204) }) - describe("when used with .use(fn) - gh-247", function () { - it("does not set params.0 to the matched path", async function () { + describe('when used with .use(fn) - gh-247', function () { + it('does not set params.0 to the matched path', async function () { const app = createTestServerComponent() const router = new Router() @@ -1546,22 +1546,22 @@ describe("Router#register()", function () { return next() }) - router.get("/foo/:id", async function (ctx) { + router.get('/foo/:id', async function (ctx) { return { body: ctx.params } }) - router.prefix("/things") + router.prefix('/things') app.use(router.middleware()) - const res = await app.fetch("/things/foo/108") + const res = await app.fetch('/things/foo/108') expect(res.status).toEqual(200) - expect(await res.json()).toHaveProperty("id", "108") + expect(await res.json()).toHaveProperty('id', '108') }) }) - describe("with trailing slash", testPrefix("/admin/")) - describe("without trailing slash", testPrefix("/admin")) + describe('with trailing slash', testPrefix('/admin/')) + describe('without trailing slash', testPrefix('/admin')) function testPrefix(prefix: string) { return function () { @@ -1573,11 +1573,11 @@ describe("Router#register()", function () { router.use(function (ctx, next) { middlewareCount++ - ctx.thing = "worked" + ctx.thing = 'worked' return next() }) - router.get("/", async function (ctx) { + router.get('/', async function (ctx) { middlewareCount++ return { body: { name: ctx.thing } } }) @@ -1590,35 +1590,35 @@ describe("Router#register()", function () { middlewareCount = 0 }) - it("should support root level router middleware", async function () { + it('should support root level router middleware', async function () { const res = await app.fetch(prefix) expect(res.status).toEqual(200) expect(middlewareCount).toEqual(2) const b = await res.json() - expect(typeof b).toEqual("object") - expect(b).toHaveProperty("name", "worked") + expect(typeof b).toEqual('object') + expect(b).toHaveProperty('name', 'worked') }) - it("should support requests with a trailing path slash", async function () { - const res = await app.fetch("/admin/") + it('should support requests with a trailing path slash', async function () { + const res = await app.fetch('/admin/') expect(res.status).toEqual(200) expect(middlewareCount).toEqual(2) const b = await res.json() - expect(typeof b).toEqual("object") - expect(b).toHaveProperty("name", "worked") + expect(typeof b).toEqual('object') + expect(b).toHaveProperty('name', 'worked') }) - it("should support requests without a trailing path slash", async function () { - const res = await app.fetch("/admin") + it('should support requests without a trailing path slash', async function () { + const res = await app.fetch('/admin') expect(res.status).toEqual(200) expect(middlewareCount).toEqual(2) const b = await res.json() - expect(typeof b).toEqual("object") - expect(b).toHaveProperty("name", "worked") + expect(typeof b).toEqual('object') + expect(b).toHaveProperty('name', 'worked') }) } } @@ -1628,19 +1628,19 @@ describe("Router#register()", function () { const app = createTestServerComponent() const router = new Router({ strict: false, - prefix: "/foo", + prefix: '/foo' }) const strictRouter = new Router({ strict: true, - prefix: "/bar", + prefix: '/bar' }) - router.get("/", async function (ctx) { + router.get('/', async function (ctx) { return {} }) - strictRouter.get("/", async function (ctx) { + strictRouter.get('/', async function (ctx) { return {} }) @@ -1648,40 +1648,40 @@ describe("Router#register()", function () { app.use(strictRouter.middleware()) { - const res = await app.fetch("/foo") + const res = await app.fetch('/foo') expect(res.status).toEqual(200) } { - const res = await app.fetch("/foo/") + const res = await app.fetch('/foo/') expect(res.status).toEqual(200) } { - const res = await app.fetch("/bar") + const res = await app.fetch('/bar') expect(res.status).toEqual(404) } { - const res = await app.fetch("/bar/") + const res = await app.fetch('/bar/') expect(res.status).toEqual(200) } }) }) }) -it("a", async () => { +it('a', async () => { const testServer = createTestServerComponent() const router = new Router<{}>() - router.post("/users/:userIda", async (ctx) => { + router.post('/users/:userIda', async (ctx) => { return { - status: 1, + status: 1 } }) testServer.use(router.middleware()) - const response = await testServer.fetch("/users/1", { - method: "post", + const response = await testServer.fetch('/users/1', { + method: 'post' }) expect(response.status).toEqual(1) diff --git a/test/statusChecks.spec.ts b/test/statusChecks.spec.ts index 18eaecb..8f92c44 100644 --- a/test/statusChecks.spec.ts +++ b/test/statusChecks.spec.ts @@ -1,140 +1,140 @@ -import { describeE2EWithStatusChecks } from "./test-e2e-harness" -import { TestComponents, TestComponentsWithStatus } from "./test-helpers" +import { describeE2EWithStatusChecks } from './test-e2e-harness' +import { TestComponents, TestComponentsWithStatus } from './test-helpers' -describeE2EWithStatusChecks("statusChecks", function ({ components }: { components: TestComponentsWithStatus }) { - it("start was called", async () => { +describeE2EWithStatusChecks('statusChecks', function ({ components }: { components: TestComponentsWithStatus }) { + it('start was called', async () => { const { kafka } = components expect(kafka.didStart).toEqual(true) }) - it("/health/live", async () => { + it('/health/live', async () => { const { fetch } = components - const res = await fetch.fetch("/health/live") + const res = await fetch.fetch('/health/live') expect(res.ok).toEqual(true) }) - it("/health/ready", async () => { + it('/health/ready', async () => { const { fetch, kafka, database } = components - const res = await fetch.fetch("/health/ready") + const res = await fetch.fetch('/health/ready') expect(res.status).toEqual(503) expect(await res.json()).toEqual({ details: { database: { - status: "fail", + status: 'fail' }, kafka: { - status: "fail", + status: 'fail' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "fail", + status: 'fail' }) kafka.setReadynessProbe(Promise.resolve(true)) { - const res = await fetch.fetch("/health/ready") + const res = await fetch.fetch('/health/ready') expect(res.ok).toEqual(false) expect(await res.json()).toEqual({ details: { database: { - status: "fail", + status: 'fail' }, kafka: { - status: "pass", + status: 'pass' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "fail", + status: 'fail' }) } database.setReadynessProbe(Promise.resolve(true)) { - const res = await fetch.fetch("/health/ready") + const res = await fetch.fetch('/health/ready') expect(res.ok).toEqual(true) expect(res.status).toEqual(200) expect(await res.json()).toEqual({ details: { database: { - status: "pass", + status: 'pass' }, kafka: { - status: "pass", + status: 'pass' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "pass", + status: 'pass' }) } }) - it("/health/startup", async () => { + it('/health/startup', async () => { const { fetch, kafka, database } = components { - const res = await fetch.fetch("/health/startup") + const res = await fetch.fetch('/health/startup') expect(res.ok).toEqual(false) expect(await res.json()).toEqual({ details: { database: { - status: "fail", + status: 'fail' }, kafka: { - status: "fail", + status: 'fail' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "fail", + status: 'fail' }) } kafka.setStartupProbe(Promise.resolve(true)) { - const res = await fetch.fetch("/health/startup") + const res = await fetch.fetch('/health/startup') expect(res.ok).toEqual(false) expect(await res.json()).toEqual({ details: { database: { - status: "fail", + status: 'fail' }, kafka: { - status: "pass", + status: 'pass' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "fail", + status: 'fail' }) } database.setStartupProbe(Promise.resolve(true)) { - const res = await fetch.fetch("/health/startup") + const res = await fetch.fetch('/health/startup') expect(res.ok).toEqual(true) expect(await res.json()).toEqual({ details: { database: { - status: "pass", + status: 'pass' }, kafka: { - status: "pass", + status: 'pass' }, server: { - status: "pass", - }, + status: 'pass' + } }, - status: "pass", + status: 'pass' }) } }) diff --git a/test/test-e2e-harness.ts b/test/test-e2e-harness.ts index 0c738d5..4f23f21 100644 --- a/test/test-e2e-harness.ts +++ b/test/test-e2e-harness.ts @@ -1,13 +1,13 @@ -import { createConfigComponent } from "@well-known-components/env-config-provider" -import { createLogComponent } from "@well-known-components/logger" -import { createRunner } from "@well-known-components/test-helpers" -import nodeFetch from "node-fetch" -import { createServerComponent, createStatusCheckComponent, IWebSocketComponent } from "../src" -import { createMockedLifecycleComponent } from "./mockedLifecycleComponent" -import { TestComponents, TestComponentsWithStatus } from "./test-helpers" -import wsLib, { WebSocketServer } from "ws" -import * as undici from "undici" -import { IFetchComponent } from "@well-known-components/interfaces" +import { createConfigComponent } from '@well-known-components/env-config-provider' +import { createLogComponent } from '@well-known-components/logger' +import { createRunner } from '@well-known-components/test-helpers' +import nodeFetch from 'node-fetch' +import { createServerComponent, createStatusCheckComponent, IWebSocketComponent } from '../src' +import { createMockedLifecycleComponent } from './mockedLifecycleComponent' +import { TestComponents, TestComponentsWithStatus } from './test-helpers' +import wsLib, { WebSocketServer } from 'ws' +import * as undici from 'undici' +import { IFetchComponent } from '@well-known-components/interfaces' let currentPort = 19000 @@ -15,14 +15,14 @@ const describeE2ETest = createRunner({ async main(program) { await program.startComponents() }, - initComponents: createInitComponents({ undici: false }), + initComponents: createInitComponents({ undici: false }) }) // creates a "mocha-like" describe function to run tests using the test components export const describeE2E: typeof describeE2ETest = (name, fn) => { - describeE2ETest("(http) " + name, fn) - describeE2EWithStatusChecks("(http status) " + name, fn) - describeE2EWithStatusChecksAndUndici("(http undici) " + name, fn) + describeE2ETest('(http) ' + name, fn) + describeE2EWithStatusChecks('(http status) ' + name, fn) + describeE2EWithStatusChecksAndUndici('(http undici) ' + name, fn) } export const describeE2EWithStatusChecks = createRunner({ @@ -31,7 +31,7 @@ export const describeE2EWithStatusChecks = createRunner({ @@ -40,7 +40,7 @@ export const describeE2EWithStatusChecksAndUndici = createRunner({ logs, config, ws: new WebSocketServer({ noServer: true }) }, {}) const fetch: IFetchComponent & { isUndici: boolean } = { async fetch(url: any, initRequest?: any) { - if (typeof url == "string" && url.startsWith("/")) { + if (typeof url == 'string' && url.startsWith('/')) { return (options.undici ? undici.fetch : nodeFetch)(protocolHostAndProtocol + url, { ...initRequest }) as any } else { return (options.undici ? undici.fetch : nodeFetch)(url, { ...initRequest }) as any } }, - isUndici: !!options.undici, + isUndici: !!options.undici } const ws: IWebSocketComponent = { createWebSocket(url: string, protocols?: string | string[]) { - if (typeof url == "string" && url.startsWith("/")) { - return new wsLib.WebSocket(protocolHostAndProtocol.replace(/^http/, "ws") + url, protocols) + if (typeof url == 'string' && url.startsWith('/')) { + return new wsLib.WebSocket(protocolHostAndProtocol.replace(/^http/, 'ws') + url, protocols) } else { return new wsLib.WebSocket(url, protocols) } - }, + } } return { logs, config, server, fetch, ws } @@ -93,6 +93,6 @@ async function initComponentsWithStatus(undici: boolean): Prom ...components, status, database: createMockedLifecycleComponent(), - kafka: createMockedLifecycleComponent(), + kafka: createMockedLifecycleComponent() } } diff --git a/test/test-e2e-test-server.ts b/test/test-e2e-test-server.ts index 3bd32e6..642936a 100644 --- a/test/test-e2e-test-server.ts +++ b/test/test-e2e-test-server.ts @@ -1,16 +1,16 @@ -import { createConfigComponent } from "@well-known-components/env-config-provider" -import { createLogComponent } from "@well-known-components/logger" -import { createRunner } from "@well-known-components/test-helpers" -import { createTestServerComponent, IWebSocketComponent } from "../src" -import { TestComponents } from "./test-helpers" -import wsLib from "ws" +import { createConfigComponent } from '@well-known-components/env-config-provider' +import { createLogComponent } from '@well-known-components/logger' +import { createRunner } from '@well-known-components/test-helpers' +import { createTestServerComponent, IWebSocketComponent } from '../src' +import { TestComponents } from './test-helpers' +import wsLib from 'ws' // creates a "mocha-like" describe function to run tests using the test components export const describeTestE2E = createRunner({ async main(program) { await program.startComponents() }, - initComponents, + initComponents }) async function initComponents(): Promise { @@ -24,8 +24,8 @@ async function initComponents(): Promise { const ws: IWebSocketComponent = { createWebSocket(url: string, protocols?: string | string[]) { - throw new Error("Not implemented") - }, + throw new Error('Not implemented') + } } return { logs, config, server, fetch, ws } diff --git a/test/test-helpers.ts b/test/test-helpers.ts index 9b48e23..741640f 100644 --- a/test/test-helpers.ts +++ b/test/test-helpers.ts @@ -4,10 +4,10 @@ import { IFetchComponent, IHttpServerComponent, ILoggerComponent, - IStatusCheckCapableComponent, -} from "@well-known-components/interfaces" -import { IWebSocketComponent } from "../src" -import wsLib from "ws" + IStatusCheckCapableComponent +} from '@well-known-components/interfaces' +import { IWebSocketComponent } from '../src' +import wsLib from 'ws' export type TestComponents = { server: IHttpServerComponent<{}> & { resetMiddlewares(): void } diff --git a/test/test-server-runner.ts b/test/test-server-runner.ts index 4773a52..090e9c6 100644 --- a/test/test-server-runner.ts +++ b/test/test-server-runner.ts @@ -1,5 +1,5 @@ -import { createRunner } from "@well-known-components/test-helpers" -import { initTestServerComponents, TestServerComponents, wireTestServerComponents } from "../src/test-server" +import { createRunner } from '@well-known-components/test-helpers' +import { initTestServerComponents, TestServerComponents, wireTestServerComponents } from '../src/test-server' export const testWithServer = createRunner>({ async main(program) { @@ -9,4 +9,4 @@ export const testWithServer = createRunner>({ async initComponents() { return initTestServerComponents() } -}) \ No newline at end of file +}) diff --git a/test/ws.spec.ts b/test/ws.spec.ts index 393fe7d..0be4d84 100644 --- a/test/ws.spec.ts +++ b/test/ws.spec.ts @@ -1,20 +1,20 @@ -import future from "fp-future" -import { Router } from "../src/router" -import { createTestServerComponent } from "../src/test-component" -import { upgradeWebSocketResponse } from "../src/ws" -import { describeE2E } from "./test-e2e-harness" -import { timeout } from "./test-helpers" - -describe("upgrade requests with router", () => { - it("responds 201 to endpoint without Upgrade header and .get", async () => { +import future from 'fp-future' +import { Router } from '../src/router' +import { createTestServerComponent } from '../src/test-component' +import { upgradeWebSocketResponse } from '../src/ws' +import { describeE2E } from './test-e2e-harness' +import { timeout } from './test-helpers' + +describe('upgrade requests with router', () => { + it('responds 201 to endpoint without Upgrade header and .get', async () => { const server = createTestServerComponent() const router = new Router() - router.get("/ws", async (ctx) => { - if (ctx.request.headers.get("upgrade") == "websocket") { + router.get('/ws', async (ctx) => { + if (ctx.request.headers.get('upgrade') == 'websocket') { return upgradeWebSocketResponse((sock) => { - sock.send("hola vite") + sock.send('hola vite') sock.close() }) } @@ -30,14 +30,14 @@ describe("upgrade requests with router", () => { } { - const res = await server.fetch(`/ws`, { headers: { Upgrade: "websocket" } }) + const res = await server.fetch(`/ws`, { headers: { Upgrade: 'websocket' } }) expect(res.status).toEqual(101) } }) }) -describeE2E("with real websockets (ws)", ({ components }) => { - it("awaits for WebSocketResult", async () => { +describeE2E('with real websockets (ws)', ({ components }) => { + it('awaits for WebSocketResult', async () => { const { server, ws } = components const didReturnWebSocket = future() @@ -46,12 +46,12 @@ describeE2E("with real websockets (ws)", ({ components }) => { const router = new Router() - router.get("/ws", async (ctx) => { - if (ctx.request.headers.get("upgrade") == "websocket") { + router.get('/ws', async (ctx) => { + if (ctx.request.headers.get('upgrade') == 'websocket') { return upgradeWebSocketResponse((sock) => { sock.onclose = didCloseServerWebSocket.resolve - console.log("Got server socket in state: " + sock.readyState) - sock.send("hello") + console.log('Got server socket in state: ' + sock.readyState) + sock.send('hello') }) } return { status: 201 } @@ -61,13 +61,13 @@ describeE2E("with real websockets (ws)", ({ components }) => { server.use(router.middleware()) server.use(router.allowedMethods()) - const sock = ws.createWebSocket("/ws") + const sock = ws.createWebSocket('/ws') sock.onopen = (x) => { - console.log("client socket open") + console.log('client socket open') didReturnWebSocket.resolve(sock) } sock.onmessage = (x) => { - console.log("received message", x.data) + console.log('received message', x.data) didReceiveMessageFromServer.resolve(x.data) } sock.onerror = (x) => { @@ -77,7 +77,7 @@ describeE2E("with real websockets (ws)", ({ components }) => { await Promise.race([didReturnWebSocket, timeout(1500)]) - expect(await didReceiveMessageFromServer).toEqual("hello") + expect(await didReceiveMessageFromServer).toEqual('hello') // close from client sock.close() @@ -86,43 +86,43 @@ describeE2E("with real websockets (ws)", ({ components }) => { await didCloseServerWebSocket }) - it("rejects using middleware", async () => { + it('rejects using middleware', async () => { const { server, ws } = components const didReturnWebSocket = future() const router = new Router() - router.get("/ws", async (ctx) => { - return { status: 401, statusText: "Unauthorized" } + router.get('/ws', async (ctx) => { + return { status: 401, statusText: 'Unauthorized' } }) server.resetMiddlewares() server.use(router.middleware()) server.use(router.allowedMethods()) - const sock = ws.createWebSocket("/ws") + const sock = ws.createWebSocket('/ws') sock.onerror = (x) => didReturnWebSocket.reject(x.error) - await expect(didReturnWebSocket).rejects.toThrow("Unexpected server response: 401") + await expect(didReturnWebSocket).rejects.toThrow('Unexpected server response: 401') }) - it("rejects using middleware, close connection directly", async () => { + it('rejects using middleware, close connection directly', async () => { const { server, ws } = components const didReturnWebSocket = future() const router = new Router() - router.get("/ws", async (ctx) => { - throw new Error("asd") + router.get('/ws', async (ctx) => { + throw new Error('asd') }) server.resetMiddlewares() server.use(router.middleware()) server.use(router.allowedMethods()) - const sock = ws.createWebSocket("/ws") + const sock = ws.createWebSocket('/ws') sock.onerror = (x) => didReturnWebSocket.reject(x.error) await expect(didReturnWebSocket).rejects.toThrow()