diff --git a/jest.config.ts b/jest.config.ts index 8c78e79..32a40c2 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,3 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + /* * For a detailed explanation regarding each configuration property and type check, visit: * https://jestjs.io/docs/configuration @@ -34,7 +59,7 @@ export default { ], // Indicates which provider should be used to instrument code for coverage - // coverageProvider: "babel", + coverageProvider: "v8", // A list of reporter names that Jest uses when writing coverage reports // coverageReporters: [ @@ -188,7 +213,7 @@ export default { "^.+\\.tsx?$": [ "ts-jest", { - tsconfig: "/tsconfig.json", + tsconfig : "/tsconfig.json", isolatedModules: true, }, ], diff --git a/package.json b/package.json index 4dbc33b..e6ebcb6 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typedoc": "^0.24.8", - "typescript": "^5.1.3" + "typescript": "^5.1.6" }, "workspaces": [ "benchmarks/*", diff --git a/packages/config/package.json b/packages/config/package.json index ec59e93..5cf1ada 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -46,7 +46,7 @@ "node": ">=16" }, "imports": { - "#src/config": { + "#src/config-content": { "import": "./src/config.esm.ts", "default": "./src/config.cjs.ts" }, @@ -61,7 +61,15 @@ "@types/node": ">=16" }, "dependencies": { - "joi": "^17.9.2" + "etag": "^1.8.1", + "joi": "^17.9.2", + "proxy-addr": "^2.0.7", + "qs": "^6.11.2" + }, + "devDependencies": { + "@types/etag": "^1.8.1", + "@types/proxy-addr": "^2.0.0", + "@types/qs": "^6.9.7" }, "publishConfig": { "access": "public", @@ -82,7 +90,7 @@ "./package.json": "./package.json" }, "imports": { - "#src/config": { + "#src/config-content": { "import": { "types": "./.build/esm/config.esm.d.ts", "default": "./.build/esm/config.esm.js" diff --git a/packages/config/src/config.cjs.ts b/packages/config/src/config.cjs.ts index 3d5974b..b8f44f8 100644 --- a/packages/config/src/config.cjs.ts +++ b/packages/config/src/config.cjs.ts @@ -1,4 +1,4 @@ -import { CONFIG_FILEPATH, ConfigI } from "#src/constants"; +import { CONFIG_FILEPATH } from "#src/constants/index"; /* ------------------------- Read the config file ------------------------- */ @@ -14,6 +14,6 @@ try { resolved = {}; } -const content: ConfigI = resolved.default ?? {}; +const content: any = resolved.default ?? null; export default content; diff --git a/packages/config/src/config.esm.ts b/packages/config/src/config.esm.ts index 5c6ba6d..9d5dc22 100644 --- a/packages/config/src/config.esm.ts +++ b/packages/config/src/config.esm.ts @@ -1,13 +1,12 @@ -import { CONFIG_FILEPATH, ConfigI } from "#src/constants"; +import { CONFIG_FILEPATH } from "#src/constants/index"; /* ------------------------- Read the config file ------------------------- */ // TODO: Just to avoid the CommonJS build issue // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error,@typescript-eslint/ban-ts-comment // @ts-ignore -const content: ConfigI = await import(CONFIG_FILEPATH) +const content: any = await import(CONFIG_FILEPATH) .then(resolved => resolved.default) - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - .catch(() => ({}) as never); + .catch(() => null); export default content; diff --git a/packages/config/src/config/config.ts b/packages/config/src/config/config.ts new file mode 100644 index 0000000..cdd44bc --- /dev/null +++ b/packages/config/src/config/config.ts @@ -0,0 +1,183 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import { cpus } from "node:os"; +import Joi from "joi"; +import content from "#src/config-content"; +import { ENV } from "#src/constants/index"; +import { Node, type Schema } from "#src/utils/index"; +import { JsonConfig, type JsonConfigI } from "./json.config.js"; +import { JsonpConfig, type JsonpConfigI } from "./jsonp.config.js"; +import { ProxyConfig, type ProxyConfigI } from "./proxy.config.js"; +import { QueryConfig, type QueryConfigI } from "./query.config.js"; +import { RouterConfig, type RouterConfigI } from "./router.config.js"; +import { ServerConfig, type ServerConfigI } from "./server.config.js"; +import { SubdomainConfig, type SubdomainConfigI } from "./subdomain.config.js"; +import { ViewConfig, type ViewConfigI } from "./view.config.js"; + +export interface ConfigI { + + /** + * Node.js environment. + * @default process.env.NODE_ENV ?? "development" + */ + env?: ENV; + + /** + * JSON config. + */ + readonly json?: JsonConfigI; + + /** + * JSONP config. + */ + readonly jsonp?: JsonpConfigI; + + /** + * Proxy config. + */ + readonly proxy?: ProxyConfigI; + + /** + * Request query string config. + */ + readonly query?: QueryConfigI; + + /** + * Router config. + */ + readonly router?: RouterConfigI; + + /** + * Server config. + */ + readonly server?: ServerConfigI; + + /** + * Subdomain config. + */ + readonly subdomain?: SubdomainConfigI; + + /** + * View config. + */ + readonly view?: ViewConfigI; + + /** + * Number of Node.js cluster workers to be created. + * In case of `1` Node.js cluster workers won't be used. + * @default 1 + */ + workers?: number; + + /** + * Indicates whether the "X-Powered-By" header should be present or not. + * @default true + */ + xPoweredBy?: boolean; +} + +export class Config extends Node { + + public static SCHEMA: Schema = { + env: Joi.string().valid(...Object.values(ENV)) + .default(process.env.NODE_ENV as ENV | undefined ?? ENV.DEVELOPMENT), + workers: Joi.number().integer() + .min(1) + .max(cpus().length) + .default(1), + xPoweredBy: Joi.boolean().default(true), + }; + + /** + * Node.js environment. + * @default process.env.NODE_ENV ?? "development" + */ + public env: ENV; + + /** + * JSON config. + */ + public json = new JsonConfig; + + /** + * JSONP config. + */ + public jsonp = new JsonpConfig; + + /** + * Proxy config. + */ + public proxy = new ProxyConfig; + + /** + * Request query string config. + */ + public query = new QueryConfig; + + /** + * Router config. + */ + public router = new RouterConfig; + + /** + * Server config. + */ + public server = new ServerConfig; + + /** + * Subdomain config. + */ + public subdomain = new SubdomainConfig; + + /** + * View config. + */ + public view = new ViewConfig; + + /** + * Number of Node.js cluster workers to be created. + * In case of `1` Node.js cluster workers won't be used. + * @default 1 + */ + public workers: number; + + /** + * Indicates whether the "X-Powered-By" header should be present or not. + * @default true + */ + public xPoweredBy: boolean; + + public constructor(config: Partial = content ?? {}) { + super(); + + const { env, workers, xPoweredBy } = config as Required; + + this.env = env; + this.workers = workers; + this.xPoweredBy = xPoweredBy; + } + +} diff --git a/packages/config/src/config/index.ts b/packages/config/src/config/index.ts new file mode 100644 index 0000000..4a42dd9 --- /dev/null +++ b/packages/config/src/config/index.ts @@ -0,0 +1,32 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import { Config, ConfigI } from "./config.js"; + +export { type ViewRendererCallbackT, type ViewRendererT } from "./view.config.js"; + +export type { Config, ConfigI }; + +export const config = new Config; diff --git a/packages/config/src/config/json.config.ts b/packages/config/src/config/json.config.ts new file mode 100644 index 0000000..7d97168 --- /dev/null +++ b/packages/config/src/config/json.config.ts @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface JsonConfigI { + + /** + * Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. + * @default false + */ + escape?: boolean; + + /** + * The `space` argument used by `JSON.stringify`. + * This is typically set to the number of spaces to use to indent prettified JSON. + * @default 0 + */ + spaces?: number; + + /** + * The `replacer` argument used by `JSON.stringify`. + */ + replacer?(key: string, value: unknown): unknown; +} + +export class JsonConfig extends Node { + + public static SCHEMA: Schema = { + escape : Joi.boolean().default(false), + replacer: Joi.function(), + spaces : Joi.number().integer() + .min(0) + .default(0), + }; + + /** + * Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. + * @default false + */ + public escape: boolean; + + /** + * The `replacer` argument used by `JSON.stringify`. + */ + public replacer?: (key: string, value: unknown) => unknown; + + /** + * The `space` argument used by `JSON.stringify`. + * This is typically set to the number of spaces to use to indent prettified JSON. + * @default 0 + */ + public spaces: number; + + public constructor(config: Partial = content?.json ?? {}) { + super("json"); + + const { escape, replacer, spaces } = config as Required; + + this.escape = escape; + this.replacer = replacer; + this.spaces = spaces; + } + +} diff --git a/packages/config/src/config/jsonp.config.ts b/packages/config/src/config/jsonp.config.ts new file mode 100644 index 0000000..3ea482d --- /dev/null +++ b/packages/config/src/config/jsonp.config.ts @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface JsonpConfigI { + + /** + * The JSONP callback name. + * @default "callback" + */ + callback?: string; +} + + +export class JsonpConfig extends Node { + + public static SCHEMA: Schema = { + callback: Joi.string().default("callback"), + }; + + /** + * The JSONP callback name. + * @default "callback" + */ + public callback: string; + + + public constructor(config: Partial = content?.jsonp ?? {}) { + super("jsonp"); + + const { callback } = config as Required; + + this.callback = callback; + } + +} diff --git a/packages/config/src/config/proxy.config.ts b/packages/config/src/config/proxy.config.ts new file mode 100644 index 0000000..75aa0f0 --- /dev/null +++ b/packages/config/src/config/proxy.config.ts @@ -0,0 +1,81 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import { compile } from "proxy-addr"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface ProxyConfigI { + + /** + * Indicates whether the app is behind a front-facing proxy, + * and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. + * @default () => false + */ + trust?: boolean | number | string | ((ip: string, hopIndex: number) => boolean); +} + +export interface ProxyConfig { + + /** + * Indicates whether the app is behind a front-facing proxy, + * and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. + * @default () => false + */ + get trust(): (ip: string, hopIndex: number) => boolean; + + /** + * Indicates whether the app is behind a front-facing proxy, + * and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. + * @default () => false + */ + set trust(value: boolean | number | string | ((ip: string, hopIndex: number) => boolean)); +} + +export class ProxyConfig extends Node { + + public static SCHEMA: Schema = { + trust: Joi.alternatives().try( + Joi.function(), + Joi.boolean().custom(value => ((): boolean => value)), + Joi.string().custom(value => compile(value.split(/ *, */))), + Joi.number().integer() + .positive() + .custom(value => ((ip: string, hopIndex: number): boolean => hopIndex < value)), + ) + .default(() => ((): boolean => false)), + }; + + + public constructor(config: Partial = content?.proxy ?? {}) { + super("proxy"); + + const { trust } = config as Required; + + this.trust = trust; + } + +} diff --git a/packages/config/src/config/query.config.ts b/packages/config/src/config/query.config.ts new file mode 100644 index 0000000..91e95d1 --- /dev/null +++ b/packages/config/src/config/query.config.ts @@ -0,0 +1,91 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import { parse, ParsedQs } from "qs"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface QueryConfigI { + + /** + * A custom query string parsing function will receive the complete query string, + * and must return an object of query keys and their values. + * @default qs.parse + */ + parser?: boolean | "extended" | "simple" | ((str: string) => ParsedQs); +} + +export interface QueryConfig { + + /** + * A custom query string parsing function will receive the complete query string, + * and must return an object of query keys and their values. + * @default qs.parse + */ + get parser(): (str: string) => ParsedQs; + + /** + * A custom query string parsing function will receive the complete query string, + * and must return an object of query keys and their values. + * @default qs.parse + */ + set parser(parser: boolean | "extended" | "simple" | ((str: string) => ParsedQs)); +} + +export class QueryConfig extends Node { + + public static SCHEMA: Schema = { + parser: Joi.alternatives().try( + Joi.function(), + Joi.boolean().custom((value) => { + if (value) return parse; + + return (): ParsedQs => ({}); + }), + Joi.string().custom((value) => { + switch (value) { + case "simple": + return parse; + case "extended": + return (str: string): ParsedQs => parse(str, { allowPrototypes: true }); + default: + throw new TypeError(`Unexpected value: ${ value }`); + } + }), + ) + .default(() => parse), + }; + + + public constructor(config: Partial = content?.query ?? {}) { + super("query"); + + const { parser } = config as Required; + + this.parser = parser; + } + +} diff --git a/packages/config/src/config/router.config.ts b/packages/config/src/config/router.config.ts new file mode 100644 index 0000000..f471ad6 --- /dev/null +++ b/packages/config/src/config/router.config.ts @@ -0,0 +1,103 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface RouterConfigI { + + /** + * Indicates whether the router should allow unsafe regex or not. + * @default false + */ + allowUnsafeRegex?: boolean; + + /** + * Indicates whether the router paths should be case-sensitive or not. + * @default true + */ + caseSensitive?: boolean; + + /** + * Indicates whether the router should ignore trailing slashes or not. + * @default false + */ + ignoreTrailingSlash?: boolean; + + /** + * Maximum allowed length for router parameter values. + * @default 100 + */ + maxParamLength?: number; +} + +export class RouterConfig extends Node { + + public static SCHEMA: Schema = { + allowUnsafeRegex : Joi.boolean().default(false), + caseSensitive : Joi.boolean().default(true), + ignoreTrailingSlash: Joi.boolean().default(false), + maxParamLength : Joi.number().integer() + .positive() + .default(100), + }; + + /** + * Indicates whether the router should allow unsafe regex or not. + * @default false + */ + public allowUnsafeRegex: boolean; + + /** + * Indicates whether the router paths should be case-sensitive or not. + * @default true + */ + public caseSensitive: boolean; + + /** + * Indicates whether the router should ignore trailing slashes or not. + * @default false + */ + public ignoreTrailingSlash: boolean; + + /** + * Maximum allowed length for router parameter values. + * @default 100 + */ + public maxParamLength: number; + + public constructor(config: Partial = content?.subdomain ?? {}) { + super("router"); + + const { allowUnsafeRegex, caseSensitive, ignoreTrailingSlash, maxParamLength } = config as Required; + + this.allowUnsafeRegex = allowUnsafeRegex; + this.caseSensitive = caseSensitive; + this.ignoreTrailingSlash = ignoreTrailingSlash; + this.maxParamLength = maxParamLength; + } + +} diff --git a/packages/config/src/config/server.config.ts b/packages/config/src/config/server.config.ts new file mode 100644 index 0000000..e737684 --- /dev/null +++ b/packages/config/src/config/server.config.ts @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import createETag from "etag"; +import Joi from "joi"; +import content from "#src/config-content"; +import { SERVER_PROTOCOL } from "#src/constants/index"; +import { Node, Schema } from "#src/utils/index"; + +const createETagGenerator = (weak: boolean) => function generateETag( + body: Buffer | string, + encoding?: BufferEncoding, +): string { + return createETag(Buffer.isBuffer(body) ? body : Buffer.from(body, encoding), { + weak, + }); +}; + +export interface ServerConfigI { + + /** + * HTTP2/HTTPS cert chain in PEM format. + */ + cert?: string; + + /** + * ETag response header value generator. + */ + etag?: boolean | "strong" | "weak" | ((body: Buffer | string, encoding?: BufferEncoding) => string); + + /** + * Server hostname. + * @default "localhost" + */ + hostname?: string; + + /** + * HTTP2/HTTPS private key in PEM format. + */ + key?: string; + + /** + * Server port. + * @default 3000 + */ + port?: number; + + /** + * Server protocol. + * @default "http" + */ + protocol?: SERVER_PROTOCOL; +} + +export interface ServerConfig { + + /** + * ETag response header value generator. + */ + get etag(): ((body: Buffer | string, encoding?: BufferEncoding) => string) | undefined; + + /** + * ETag response header value generator. + */ + set etag( + etag: boolean | "strong" | "weak" | ((body: Buffer | string, encoding?: BufferEncoding) => string) | undefined, + ); +} + +export class ServerConfig extends Node { + + public static SCHEMA: Schema = { + cert: Joi.string(), + etag: Joi.alternatives( + Joi.function(), + Joi.boolean().custom((value) => { + if (value) return createETagGenerator(true); + + // eslint-disable-next-line no-undefined + return undefined; + }), + Joi.string().custom((value) => { + switch (value) { + case "weak": + return createETagGenerator(true); + case "strong": + return createETagGenerator(false); + default: + throw new TypeError(`Unexpected value: ${ value }`); + } + }), + ), + hostname: Joi.string().hostname() + .default("localhost"), + key : Joi.string(), + port: Joi.number().port() + .default(3_000), + protocol: Joi.string().valid(...Object.values(SERVER_PROTOCOL)) + .default(SERVER_PROTOCOL.HTTP), + }; + + /** + * HTTP2/HTTPS cert chain in PEM format. + */ + public cert?: string; + + /** + * Server hostname. + * @default "localhost" + */ + public hostname: string; + + /** + * HTTP2/HTTPS private key in PEM format. + */ + public key?: string; + + /** + * Server port. + * @default 3000 + */ + public port: number; + + /** + * Server protocol. + * @default "http" + */ + public protocol: SERVER_PROTOCOL; + + public constructor(config: Partial = content?.server ?? {}) { + super("server"); + + const { cert, etag, hostname, key, port, protocol } = config as Required; + + this.cert = cert; + this.etag = etag; + this.hostname = hostname; + this.key = key; + this.port = port; + this.protocol = protocol; + } + +} diff --git a/packages/config/src/config/subdomain.config.ts b/packages/config/src/config/subdomain.config.ts new file mode 100644 index 0000000..28a173b --- /dev/null +++ b/packages/config/src/config/subdomain.config.ts @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export interface SubdomainConfigI { + + /** + * The number of dot-separated parts of the host to remove to access subdomain. + * @default 2 + */ + offset?: number; +} + +export class SubdomainConfig extends Node { + + public static SCHEMA: Schema = { + offset: Joi.number().integer() + .min(0) + .default(2), + }; + + /** + * The number of dot-separated parts of the host to remove to access subdomain. + * @default 2 + */ + public offset: number; + + public constructor(config: Partial = content?.subdomain ?? {}) { + super("subdomain"); + + const { offset } = config as Required; + + this.offset = offset; + } + +} diff --git a/packages/config/src/config/view.config.ts b/packages/config/src/config/view.config.ts new file mode 100644 index 0000000..e10a62a --- /dev/null +++ b/packages/config/src/config/view.config.ts @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import { statSync } from "node:fs"; +import Joi from "joi"; +import content from "#src/config-content"; +import { Node, Schema } from "#src/utils/index"; + +export type ViewRendererCallbackT = (error: Error, result: string) => void; + +export type ViewRendererT = ( + filepath: string, + options: Record, + callback?: ViewRendererCallbackT, +) => void; + +export interface ViewConfigI { + + /** + * The directory containing view templates. + */ + directory?: string; + + /** + * The view template file extension. + */ + extension?: string; + + /** + * The function to render the view templates. + */ + renderer?: ViewRendererT; +} + +export class ViewConfig extends Node { + + public static SCHEMA: Schema = { + directory: Joi.string().custom((value) => { + if (statSync(value).isDirectory()) return value; + + throw new TypeError(`Unexpected value: ${ value }`); + }), + extension: Joi.string(), + renderer : Joi.function(), + }; + + /** + * The directory containing view templates. + */ + public directory: string; + + /** + * The view template file extension. + */ + public extension?: string; + + /** + * The function to render the view templates. + */ + public renderer: ViewRendererT; + + public constructor(config: Partial = content?.view ?? {}) { + super("view"); + + const { directory, extension, renderer } = config as Required; + + this.directory = directory; + this.extension = extension; + this.renderer = renderer; + } + +} diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts deleted file mode 100644 index c5e32d6..0000000 --- a/packages/config/src/constants.ts +++ /dev/null @@ -1,173 +0,0 @@ -import qs from "node:querystring"; - -export const CONFIG_FILEPATH = `${ process.cwd() }/foxify.config.js`; - -export enum ENV { - DEVELOPMENT = "development", - PRODUCTION = "production", - TEST = "test", -} - -export enum SERVER_PROTOCOL { - HTTP = "http", - HTTPS = "https", -} - -export const DEFAULT: ConfigI = { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - env : process.env.NODE_ENV as ENV ?? ENV.DEVELOPMENT, - xPoweredBy: true, - workers : 1, - server : { - protocol: SERVER_PROTOCOL.HTTP, - hostname: "localhost", - port : 3000, - }, - subdomain: { - offset: 2, - }, - json: { - escape: false, - spaces: 0, - }, - jsonp: { - callback: "callback", - }, - query: { - parser: qs.parse, - }, - proxy: { - trust: () => false, - }, -}; - -/* ------------------------- Interfaces ------------------------- */ - -export interface ConfigI { - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - readonly [config: string]: any; - - /** - * Node.js environment. - */ - readonly env: ENV; - - /** - * JSON config. - */ - readonly json: JsonConfigI; - - /** - * JSONP config. - */ - readonly jsonp: JsonpConfigI; - - /** - * Proxy config. - */ - readonly proxy: ProxyConfigI; - - /** - * Request query string config. - */ - readonly query: QueryConfigI; - - /** - * Server config. - */ - readonly server: ServerConfigI; - - /** - * Subdomain config. - */ - readonly subdomain: SubdomainConfigI; - - /** - * Number of Node.js cluster workers to be created. - * In case of `1` Node.js cluster workers won't be used. - */ - readonly workers: number; - - /** - * Indicates whether the "X-Powered-By" header should be present or not. - */ - readonly xPoweredBy: boolean; - - /** - * ETag response header value generator. - */ - etag?(body: Buffer | string, encoding?: BufferEncoding): string; -} - -export interface ServerConfigI { - - /** - * Server hostname. - */ - readonly hostname: string; - - /** - * Server port. - */ - readonly port: number; - - /** - * Server protocol. - */ - readonly protocol: SERVER_PROTOCOL; -} - -export interface SubdomainConfigI { - - /** - * The number of dot-separated parts of the host to remove to access subdomain. - */ - readonly offset: number; -} - -export interface JsonConfigI { - - /** - * Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. - */ - readonly escape: boolean; - - /** - * The `space` argument used by `JSON.stringify`. - * This is typically set to the number of spaces to use to indent prettified JSON. - */ - readonly spaces: number; - - /** - * The `replacer` argument used by `JSON.stringify`. - */ - replacer?(key: string, value: unknown): unknown; -} - -export interface JsonpConfigI { - - /** - * The JSONP callback name. - */ - readonly callback: string; -} - -export interface QueryConfigI { - - /** - * A custom query string parsing function will receive the complete query string, - * and must return an object of query keys and their values. - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - parser(str: string): Record; -} - -export interface ProxyConfigI { - - /** - * Indicates whether the app is behind a front-facing proxy, - * and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. - */ - trust(ip: string, hopIndex: number): boolean; -} diff --git a/packages/config/src/constants/CONFIG_FILEPATH.ts b/packages/config/src/constants/CONFIG_FILEPATH.ts new file mode 100644 index 0000000..40fb255 --- /dev/null +++ b/packages/config/src/constants/CONFIG_FILEPATH.ts @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import { cwd } from "node:process"; + +export const CONFIG_FILEPATH = `${ cwd() }/foxify.config.js`; diff --git a/packages/config/src/constants/ENV.ts b/packages/config/src/constants/ENV.ts new file mode 100644 index 0000000..f14af86 --- /dev/null +++ b/packages/config/src/constants/ENV.ts @@ -0,0 +1,30 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +export enum ENV { + DEVELOPMENT = "development", + PRODUCTION = "production", + TEST = "test", +} diff --git a/packages/config/src/constants/SERVER_PROTOCOL.ts b/packages/config/src/constants/SERVER_PROTOCOL.ts new file mode 100644 index 0000000..a5a3fde --- /dev/null +++ b/packages/config/src/constants/SERVER_PROTOCOL.ts @@ -0,0 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +export enum SERVER_PROTOCOL { + HTTP = "http", + HTTPS = "https", +} diff --git a/packages/config/src/constants/index.ts b/packages/config/src/constants/index.ts new file mode 100644 index 0000000..2c5904a --- /dev/null +++ b/packages/config/src/constants/index.ts @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +export * from "./CONFIG_FILEPATH.js"; +export * from "./ENV.js"; +export * from "./SERVER_PROTOCOL.js"; diff --git a/packages/config/src/events.ts b/packages/config/src/events.ts new file mode 100644 index 0000000..e668ece --- /dev/null +++ b/packages/config/src/events.ts @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import { EventEmitter } from "node:events"; + +export const events = new EventEmitter; diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index a7769df..eb63afe 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -1,116 +1,4 @@ -import os from "node:os"; -import Joi from "joi"; -import content from "#src/config"; -import { - ConfigI, - DEFAULT, - ENV, - JsonConfigI, - JsonpConfigI, - ProxyConfigI, - QueryConfigI, - SERVER_PROTOCOL, - ServerConfigI, - SubdomainConfigI, -} from "#src/constants"; - -/* ------------------------- Validate the config data ------------------------- */ - -const { value: config, error } = Joi - .object() - .keys({ - env: Joi.string().valid(...Object.values(ENV)) - .default(DEFAULT.env), - xPoweredBy: Joi.boolean().default(DEFAULT.xPoweredBy), - workers : Joi.number().integer() - .min(1) - .max(os.cpus().length) - .default(DEFAULT.workers), - etag : Joi.function(), - server: Joi - .object() - .keys({ - protocol: Joi.string().valid(...Object.values(SERVER_PROTOCOL)) - .default(DEFAULT.server.protocol), - hostname: Joi.string().hostname() - .default(DEFAULT.server.hostname), - port: Joi.number().port() - .default(DEFAULT.server.port), - }) - .default(DEFAULT.server), - subdomain: Joi - .object() - .keys({ - offset: Joi.number().integer() - .min(0) - .default(DEFAULT.subdomain.offset), - }) - .default(DEFAULT.subdomain), - json: Joi - .object() - .keys({ - escape : Joi.boolean().default(DEFAULT.json.escape), - replacer: Joi.function(), - spaces : Joi.number().integer() - .min(0) - .default(DEFAULT.json.spaces), - }) - .default(DEFAULT.json), - jsonp: Joi - .object() - .keys({ - callback: Joi.string().default(DEFAULT.jsonp.callback), - }) - .default(DEFAULT.jsonp), - query: Joi - .object() - .keys({ - parser: Joi.function().default(DEFAULT.query.parser), - }) - .default(DEFAULT.query), - proxy: Joi - .object() - .keys({ - trust: Joi.function().default(DEFAULT.proxy.trust), - }) - .default(DEFAULT.proxy), - }) - .unknown() - .validate(content); - -if (error != null) throw error; - -/* ------------------------- Freeze the config data ------------------------- */ - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function freeze>(obj: T): T { - for (const key in obj) { - if (!Object.hasOwn(obj, key)) continue; - - const value = obj[key]; - - if (typeof value !== "object" || value == null) continue; - - obj[key] = freeze(value); - } - - return Object.freeze(obj); -} - -const CONFIG: ConfigI = config.env === ENV.TEST ? config : freeze(config); - -/* ------------------------- Exports ------------------------- */ - -export default CONFIG; - -export { ENV, SERVER_PROTOCOL }; - -export type { - ConfigI, - ServerConfigI, - SubdomainConfigI, - JsonConfigI, - JsonpConfigI, - QueryConfigI, - ProxyConfigI, -}; +export { config as default } from "#src/config/index"; +export * from "#src/config/index"; +export * from "#src/constants/index"; +export * from "#src/events"; diff --git a/packages/config/src/utils/Node.ts b/packages/config/src/utils/Node.ts new file mode 100644 index 0000000..80f0a9a --- /dev/null +++ b/packages/config/src/utils/Node.ts @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +import Joi from "joi"; +import { events } from "#src/events"; + +export type DirectConfigKey = { + [K in keyof T]: T[K] extends object ? T[K] extends (...args: any[]) => any ? K : never : K; +}[keyof T]; + +export type Schema = Record, Joi.Schema>; + +export abstract class Node { + + public static SCHEMA: Record; + + protected constructor(prefix = "") { + const SCHEMA = (this.constructor as typeof Node).SCHEMA; + + if (prefix !== "") prefix += "."; + + // eslint-disable-next-line no-constructor-return + return new Proxy(this, { + // eslint-disable-next-line max-params + set(target: Node, p: string, newValue: unknown, receiver: unknown): boolean { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const oldValue = (target as any)[p]; + + if (oldValue instanceof Node) throw new TypeError(`Config override for "${ p }" is not allowed.`); + + if (p in SCHEMA) { + const { value, error } = SCHEMA[p].validate(newValue); + + if (error != null) throw error; + + newValue = value; + } + + const result = Reflect.set(target, p, newValue, receiver); + + if (result) events.emit("change", `${ prefix }${ p }`, newValue, oldValue); + + return result; + }, + }); + } + +} diff --git a/packages/config/src/utils/index.ts b/packages/config/src/utils/index.ts new file mode 100644 index 0000000..329bd8c --- /dev/null +++ b/packages/config/src/utils/index.ts @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2023 FoxifyJS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +export * from "./Node.js"; diff --git a/packages/config/tests/index.test.ts b/packages/config/tests/index.test.ts index a86c08b..e042207 100644 --- a/packages/config/tests/index.test.ts +++ b/packages/config/tests/index.test.ts @@ -1,6 +1,81 @@ -import { DEFAULT } from "#src/constants"; -import CONFIG from "@foxify/config"; +import { parse } from "qs"; +import { config, ConfigI, ENV, events, SERVER_PROTOCOL } from "@foxify/config"; it("should use default config", () => { - expect(CONFIG).toEqual(DEFAULT); + expect(config).toMatchObject({ + env : "test" as ENV, + json: { + escape : false, + // eslint-disable-next-line no-undefined + replacer: undefined, + spaces : 0, + }, + jsonp: { + callback: "callback", + }, + proxy: { + }, + query: { + parser: parse, + }, + server: { + // eslint-disable-next-line no-undefined + etag : undefined, + hostname: "localhost", + port : 3000, + protocol: "http" as SERVER_PROTOCOL, + }, + subdomain: { + offset: 2, + }, + router: { + allowUnsafeRegex : false, + caseSensitive : true, + ignoreTrailingSlash: false, + maxParamLength : 100, + }, + workers : 1, + xPoweredBy: true, + } satisfies ConfigI); + + expect(config.proxy.trust).toBeInstanceOf(Function); + expect(config.proxy.trust.toString()).toBe("() => false"); +}); + +it("should update json.escape", () => { + const listener = jest.fn(); + + events.on("change", listener); + + expect(config.json.escape).toBe(false); + + config.json.escape = true; + + expect(listener).toBeCalledTimes(1); + expect(listener).toBeCalledWith("json.escape", true, false); + + listener.mockReset(); + + expect(config.json.escape).toBe(true); + + config.json.escape = false; + + expect(listener).toBeCalledTimes(1); + expect(listener).toBeCalledWith("json.escape", false, true); + + events.off("change", listener); + + expect(config.json).toEqual({ + escape : false, + // eslint-disable-next-line no-undefined + replacer: undefined, + spaces : 0, + }); +}); + +it("shouldn't allow sub-config updates", () => { + expect(() => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + config.json = {} as never; + }).toThrowError("Config override for \"json\" is not allowed."); }); diff --git a/packages/foxify/package.json b/packages/foxify/package.json index f93c190..ccfeaf2 100644 --- a/packages/foxify/package.json +++ b/packages/foxify/package.json @@ -68,6 +68,7 @@ "./package.json": "./package.json" }, "peerDependencies": { + "@foxify/config": "^1", "@foxify/http": "^1", "@foxify/router": "^1", "@types/node": ">=16", @@ -85,6 +86,7 @@ "serve-static": "^1.15.0" }, "devDependencies": { + "@foxify/config": "workspace:^", "@foxify/http": "workspace:^", "@foxify/router": "workspace:^", "@types/cookie": "^0.5.1", diff --git a/packages/foxify/src/Foxify.ts b/packages/foxify/src/Foxify.ts index a019815..83136bf 100644 --- a/packages/foxify/src/Foxify.ts +++ b/packages/foxify/src/Foxify.ts @@ -1,10 +1,5 @@ import assert from "node:assert"; -import { - Request, - requestSettings, - Response, - responseSettings, -} from "@foxify/http"; +import { Request, Response } from "@foxify/http"; import inject, { InjectResultI, OptionsI as InjectOptionsI } from "@foxify/inject"; import Router from "@foxify/router"; import * as proxyAddr from "proxy-addr"; @@ -12,7 +7,6 @@ import * as qs from "qs"; import serveStatic from "serve-static"; import Server from "./Server.js"; import * as utils from "./utils/index.js"; -import { Engine } from "./view/index.js"; const SETTINGS: Array = [ "env", @@ -120,8 +114,6 @@ class Foxify extends Router { etag : undefined as any, }; - private _view?: Engine; - public constructor() { super(); @@ -129,6 +121,7 @@ class Foxify extends Router { this.disable("trust.proxy"); } + // TODO: remove in favor of @foxify/config public static dotenv(path: string): void { utils.assertType("path", "string", path); @@ -136,22 +129,27 @@ class Foxify extends Router { require("dotenv").config({ path }); } + // TODO: remove in favor of @foxify/config public disable(setting: keyof Foxify.Settings): this { return this.set(setting, false); } + // TODO: remove in favor of @foxify/config public disabled(setting: keyof Foxify.Settings): boolean { return !this.setting(setting); } + // TODO: remove in favor of @foxify/config public enable(setting: keyof Foxify.Settings): this { return this.set(setting, true); } + // TODO: remove in favor of @foxify/config public enabled(setting: keyof Foxify.Settings): boolean { return !this.disabled(setting); } + // TODO: remove in favor of @foxify/config /** * Handle view * @param extension view template file extension @@ -159,8 +157,6 @@ class Foxify extends Router { * @param handler */ public engine(extension: string, path: string, handler: () => void): this { - this._view = new Engine(path, extension, handler); - return this; } @@ -172,12 +168,6 @@ class Foxify extends Router { if (typeof options === "string") options = { url: options }; - requestSettings(this._settings as any); - responseSettings({ - ...this._settings, - view: this._view, - } as any); - // TODO: fix typescript issues return inject(this.lookup.bind(this), { ...options, @@ -190,6 +180,7 @@ class Foxify extends Router { }); } + // TODO: remove in favor of @foxify/config public set( setting: T, value: Foxify.UserSettings[T], @@ -285,6 +276,7 @@ class Foxify extends Router { return this; } + // TODO: remove in favor of @foxify/config public setting(setting: T): Foxify.Settings[T] { assert( SETTINGS.includes(setting), @@ -297,17 +289,10 @@ class Foxify extends Router { public start(callback?: Server.Callback): Server { if (callback != null) utils.assertType("callback", "function", callback); - /* Set node env */ process.env.NODE_ENV = this.setting("env"); - const server = new Server( - { - ...this._settings, - view: this._view, - }, - this.lookup.bind(this), - ); + const server = new Server(this.lookup.bind(this)); return server.start(callback); } diff --git a/packages/foxify/src/Server.ts b/packages/foxify/src/Server.ts index 87932fd..9fd29a5 100644 --- a/packages/foxify/src/Server.ts +++ b/packages/foxify/src/Server.ts @@ -1,21 +1,11 @@ import cluster from "node:cluster"; import http from "node:http"; import https from "node:https"; -import { - Request, - requestSettings, - Response, - responseSettings, -} from "@foxify/http"; -import { Engine } from "./view/index.js"; -// eslint-disable-next-line @typescript-eslint/consistent-type-imports -import type Foxify from "./index.js"; +import { config, SERVER_PROTOCOL } from "@foxify/config"; +import { Request, Response } from "@foxify/http"; // eslint-disable-next-line @typescript-eslint/no-namespace namespace Server { - export interface Settings extends Foxify.Settings { - view?: Engine; - } export type Listener = (request: Request, response: Response) => void; @@ -24,35 +14,25 @@ namespace Server { class Server { - protected _host: string; - protected _listening = false; - protected _port: number; - private readonly _instance?: http.Server | https.Server; - public constructor(settings: Server.Settings, listener: Server.Listener) { - this._host = settings.url; - this._port = settings.port; - - const isHttps = settings.https; + public constructor(listener: Server.Listener) { + const isHttps = config.server.protocol === SERVER_PROTOCOL.HTTPS; const SERVER: any = isHttps ? https : http; - requestSettings(settings as any); - responseSettings(settings as any); - const OPTIONS: any = { IncomingMessage: Request, ServerResponse : Response, }; if (isHttps) { - OPTIONS.cert = settings["https.cert"]; - OPTIONS.key = settings["https.key"]; + OPTIONS.cert = config.server.cert; + OPTIONS.key = config.server.key; } - const workers = settings.workers; + const workers = config.workers; if (workers > 1) { if (cluster.isPrimary) { @@ -86,8 +66,8 @@ class Server { if (instance) { instance.listen( - this._port, - this._host, + config.server.port, + config.server.hostname, callback && ((): unknown => callback(this)), ); } diff --git a/packages/foxify/src/view/Engine.ts b/packages/foxify/src/view/Engine.ts deleted file mode 100644 index 145f8ca..0000000 --- a/packages/foxify/src/view/Engine.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { join } from "node:path"; - -// eslint-disable-next-line @typescript-eslint/no-namespace -namespace Engine { - export type Callback = (err: Error, str: string) => any; -} - -class Engine { - - protected _ext: string; - - protected _handler: (...args: any[]) => void; - - protected _path: string; - - public constructor(path: string, ext: string, handler: () => void) { - this._path = path; - this._ext = ext; - this._handler = handler; - } - - public render( - filename: string, - opts: Record = {}, - cb?: Engine.Callback, - ): void { - this._handler(join(this._path, `${ filename }.${ this._ext }`), opts, cb); - } - -} - -export default Engine; diff --git a/packages/foxify/src/view/index.ts b/packages/foxify/src/view/index.ts deleted file mode 100644 index d6c6206..0000000 --- a/packages/foxify/src/view/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Engine } from "./Engine.js"; diff --git a/packages/http/package.json b/packages/http/package.json index 98bc76d..f7ea60b 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -56,6 +56,7 @@ "./package.json": "./package.json" }, "peerDependencies": { + "@foxify/config": "workspace:^", "@types/node": ">=16" }, "dependencies": { @@ -75,6 +76,7 @@ "type-is": "^1.6.18" }, "devDependencies": { + "@foxify/config": "workspace:^", "@foxify/inject": "workspace:^", "@types/content-disposition": "^0.5.5", "@types/content-type": "^1.1.5", diff --git a/packages/http/src/Request.ts b/packages/http/src/Request.ts index 9ea0414..8dfed72 100644 --- a/packages/http/src/Request.ts +++ b/packages/http/src/Request.ts @@ -1,26 +1,11 @@ import { IncomingHttpHeaders, IncomingMessage } from "node:http"; import { isIP } from "node:net"; import { UrlWithStringQuery } from "node:url"; -import proxyAddr, { compile as proxyAddrCompile, all as proxyAddrAll } from "proxy-addr"; -import * as qs from "qs"; +import { config } from "@foxify/config"; +import proxyAddr, { all as proxyAddrAll } from "proxy-addr"; import typeIs from "type-is"; import { MethodT, ProtocolT } from "./constants/index.js"; -import { - Accepts, - parseUrl, - RANGE_PARSER_RESULT, - rangeParser, - RangeParserRangesI, -} from "./utils/index.js"; - -// eslint-disable-next-line import/exports-last -export const DEFAULT_SETTINGS: SettingsI = { - "query.parser" : qs.parse, - "trust.proxy" : proxyAddrCompile([]), - "subdomain.offset": 2, -}; - -const SETTINGS: SettingsI = { ...DEFAULT_SETTINGS }; +import { Accepts, parseUrl, RANGE_PARSER_RESULT, rangeParser, RangeParserRangesI } from "./utils/index.js"; export default class Request extends IncomingMessage { @@ -52,7 +37,7 @@ export default class Request extends IncomingMessage { public get hostname(): string | undefined { let host = this.get("x-forwarded-host"); - if (!host || !SETTINGS["trust.proxy"](this.socket.remoteAddress!, 0)) { + if (!host || !config.proxy.trust(this.socket.remoteAddress!, 0)) { host = this.get("host")!; } else if (host.includes(",")) { // Note: X-Forwarded-Host is normally only ever a @@ -77,7 +62,7 @@ export default class Request extends IncomingMessage { * "trust.proxy" is set. */ public get ip(): string { - return proxyAddr(this, SETTINGS["trust.proxy"]); + return proxyAddr(this, config.proxy.trust); } /** @@ -89,7 +74,7 @@ export default class Request extends IncomingMessage { * "proxy2" were trusted. */ public get ips(): string[] { - const addresses = proxyAddrAll(this, SETTINGS["trust.proxy"]); + const addresses = proxyAddrAll(this, config.proxy.trust); // Reverse the order (to farthest -> closest) // and remove socket address @@ -118,8 +103,7 @@ export default class Request extends IncomingMessage { public get protocol(): ProtocolT { const proto = (this.socket as any).encrypted ? "https" : "http"; - if (!SETTINGS["trust.proxy"](this.socket.remoteAddress!, 0)) return proto; - + if (!config.proxy.trust(this.socket.remoteAddress!, 0)) return proto; // Note: X-Forwarded-Proto is normally only ever a // single value, but this is to be safe. @@ -132,7 +116,7 @@ export default class Request extends IncomingMessage { public get query(): Record { return ( - this._queryCache ??= SETTINGS["query.parser"]((this._parsedUrl ??= parseUrl(this.url)).query!) + this._queryCache ??= config.query.parser((this._parsedUrl ??= parseUrl(this.url)).query!) ); } @@ -161,7 +145,7 @@ export default class Request extends IncomingMessage { if (!hostname) return []; - return (isIP(hostname) ? [hostname] : hostname.split(".").reverse()).slice(SETTINGS["subdomain.offset"]); + return (isIP(hostname) ? [hostname] : hostname.split(".").reverse()).slice(config.subdomain.offset); } /** @@ -318,20 +302,9 @@ export default class Request extends IncomingMessage { } -// eslint-disable-next-line @typescript-eslint/no-shadow -export function settings(settings: Partial = DEFAULT_SETTINGS): void { - Object.assign(SETTINGS, settings); -} - export interface HeadersI extends IncomingHttpHeaders { referrer: IncomingHttpHeaders["referer"]; "x-forwarded-host"?: string; "x-forwarded-proto"?: string; "x-requested-with"?: string; } - -export interface SettingsI { - "subdomain.offset": number; - "query.parser"(str: string): Record; - "trust.proxy"(ip: string, hopIndex: number): boolean; -} diff --git a/packages/http/src/Response.ts b/packages/http/src/Response.ts index 58eaaa6..65e4a13 100644 --- a/packages/http/src/Response.ts +++ b/packages/http/src/Response.ts @@ -1,6 +1,7 @@ import assert from "node:assert"; import { OutgoingHttpHeaders, ServerResponse, STATUS_CODES } from "node:http"; import { extname, resolve } from "node:path"; +import { config, type ViewRendererCallbackT } from "@foxify/config"; import fresh from "@foxify/fresh"; import contentDisposition from "content-disposition"; import * as contentType from "content-type"; @@ -11,8 +12,7 @@ import onFinished from "on-finished"; import send, { mime as sendMime } from "send"; import Request from "./Request.js"; import { ENCODING_UTF8, JsonT, METHOD, STATUS, StatusT, StringifyT } from "./constants/index.js"; -import { createETagGenerator, encodeUrl, vary } from "./utils/index.js"; -import { CallbackT as EngineCallbackT, Engine } from "./view/index.js"; +import { encodeUrl, vary } from "./utils/index.js"; /** * Set the charset in a given Content-Type string. @@ -278,15 +278,6 @@ const normalizeTypes = (types: string[]): any[] => { return ret; }; -// eslint-disable-next-line import/exports-last -export const DEFAULT_SETTINGS: SettingsI = { - etag : createETagGenerator(true), - "json.escape" : false, - "jsonp.callback": "callback", -}; - -const SETTINGS: SettingsI = { ...DEFAULT_SETTINGS }; - const hasOwnProperty = Object.prototype.hasOwnProperty; const charsetRegExp = /;\s*charset\s*=/; @@ -744,9 +735,9 @@ class Response extends ServerResponse { stringify( this.stringify[this.statusCode], body, - SETTINGS["json.replacer"], - SETTINGS["json.spaces"], - SETTINGS["json.escape"], + config.json.replacer, + config.json.spaces, + config.json.escape, ), ENCODING_UTF8, ); @@ -762,11 +753,11 @@ class Response extends ServerResponse { let str = stringify( this.stringify[this.statusCode], body, - SETTINGS["json.replacer"], - SETTINGS["json.spaces"], - SETTINGS["json.escape"], + config.json.replacer, + config.json.spaces, + config.json.escape, ); - let callback = this.req.query[SETTINGS["jsonp.callback"]]; + let callback = this.req.query[config.jsonp.callback]; // Content-type if (!this.get("Content-Type")) { @@ -886,31 +877,30 @@ class Response extends ServerResponse { public render( view: string, - data?: EngineCallbackT | Record, - callback?: EngineCallbackT, + data: Record | ViewRendererCallbackT = {}, + callback?: ViewRendererCallbackT, ): void { - const { view: engine } = SETTINGS; - - assert(engine, "View engine is not specified"); + assert(config.view.renderer, "View renderer is not specified"); if (typeof data === "function") { callback = data; - // eslint-disable-next-line no-undefined - data = undefined; + data = {}; } - callback ??= (err, str): void => { + callback ??= (error: Error, result: string): void => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (err != null) { - this.next(err); + if (error != null) { + this.next(error); return; } - this.send(str); + this.send(result); }; - engine.render(view, data, callback); + if (config.view.extension) view += `.${ config.view.extension }`; + + config.view.renderer(resolve(config.view.directory, view), data, callback); } /** @@ -1082,10 +1072,8 @@ class Response extends ServerResponse { private $end(body?: Buffer | string, encoding?: BufferEncoding): this { // eslint-disable-next-line no-undefined if (body !== undefined) { - const { etag } = SETTINGS; - - if (etag && !this.hasHeader("ETag")) { - const generatedETag = etag(body, encoding); + if (config.server.etag && !this.hasHeader("ETag")) { + const generatedETag = config.server.etag(body, encoding); if (generatedETag) this.setHeader("ETag", generatedETag); } @@ -1123,26 +1111,6 @@ Response.prototype.get = Response.prototype.getHeader; export default Response; -// eslint-disable-next-line @typescript-eslint/no-shadow -export function settings(settings: Partial = DEFAULT_SETTINGS): void { - Object.assign(SETTINGS, settings); -} - export interface HeadersI extends OutgoingHttpHeaders { "Content-Type"?: string; } - -export interface SettingsI { - "json.escape": boolean; - "json.spaces"?: number; - "jsonp.callback": string; - view?: Engine; - - etag?( - body: Buffer | string, - encoding?: BufferEncoding, - ): string | undefined; - - "json.replacer"?(key: string, value: any): any; - -} diff --git a/packages/http/src/index.ts b/packages/http/src/index.ts index f0343e9..a9b2ab1 100644 --- a/packages/http/src/index.ts +++ b/packages/http/src/index.ts @@ -2,15 +2,9 @@ export * from "./constants/index.js"; export * from "./errors/index.js"; export { default as Request, - settings as requestSettings, - DEFAULT_SETTINGS as REQUEST_DEFAULT_SETTINGS, type HeadersI as RequestHeadersI, - type SettingsI as RequestSettingsI, } from "./Request.js"; export { default as Response, - settings as responseSettings, - DEFAULT_SETTINGS as RESPONSE_DEFAULT_SETTINGS, type HeadersI as ResponseHeadersI, - type SettingsI as ResponseSettingsI, } from "./Response.js"; diff --git a/packages/http/src/view/Engine.ts b/packages/http/src/view/Engine.ts deleted file mode 100644 index d4a0037..0000000 --- a/packages/http/src/view/Engine.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { join } from "node:path"; - -export default class Engine { - - protected _ext: string; - - protected _handler: (...args: any[]) => void; - - protected _path: string; - - public constructor(path: string, ext: string, handler: () => void) { - this._path = path; - this._ext = ext; - this._handler = handler; - } - - public render( - filename: string, - opts: Record = {}, - cb?: CallbackT, - ): void { - this._handler(join(this._path, `${ filename }.${ this._ext }`), opts, cb); - } - -} - -export type CallbackT = (err: Error, str: string) => void; diff --git a/packages/http/src/view/index.ts b/packages/http/src/view/index.ts deleted file mode 100644 index 3eace5a..0000000 --- a/packages/http/src/view/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Engine, type CallbackT } from "./Engine.js"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a527ee4..488777e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,10 +22,10 @@ importers: version: 17.6.5 '@nrwl/jest': specifier: ^16.3.2 - version: 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.3) + version: 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.6) '@nrwl/linter': specifier: ^16.3.2 - version: 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.3) + version: 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.6) '@nrwl/workspace': specifier: ^16.3.2 version: 16.3.2 @@ -37,10 +37,10 @@ importers: version: 20.3.1 '@typescript-eslint/eslint-plugin': specifier: ^5.59.11 - version: 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.3) + version: 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^5.59.11 - version: 5.59.11(eslint@8.43.0)(typescript@5.1.3) + version: 5.59.11(eslint@8.43.0)(typescript@5.1.6) eslint: specifier: ^8.43.0 version: 8.43.0 @@ -73,16 +73,16 @@ importers: version: 16.0.5 ts-jest: specifier: ^29.1.0 - version: 29.1.0(@babel/core@7.19.6)(jest@29.5.0)(typescript@5.1.3) + version: 29.1.0(@babel/core@7.19.6)(jest@29.5.0)(typescript@5.1.6) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.3.1)(typescript@5.1.3) + version: 10.9.1(@types/node@20.3.1)(typescript@5.1.6) typedoc: specifier: ^0.24.8 - version: 0.24.8(typescript@5.1.3) + version: 0.24.8(typescript@5.1.6) typescript: - specifier: ^5.1.3 - version: 5.1.3 + specifier: ^5.1.6 + version: 5.1.6 benchmarks/benchmark: dependencies: @@ -325,9 +325,28 @@ importers: '@types/node': specifier: '>=16' version: 18.11.9 + etag: + specifier: ^1.8.1 + version: 1.8.1 joi: specifier: ^17.9.2 version: 17.9.2 + proxy-addr: + specifier: ^2.0.7 + version: 2.0.7 + qs: + specifier: ^6.11.2 + version: 6.11.2 + devDependencies: + '@types/etag': + specifier: ^1.8.1 + version: 1.8.1 + '@types/proxy-addr': + specifier: ^2.0.0 + version: 2.0.0 + '@types/qs': + specifier: ^6.9.7 + version: 6.9.7 packages/foxify: dependencies: @@ -359,6 +378,9 @@ importers: specifier: ^1.15.0 version: 1.15.0 devDependencies: + '@foxify/config': + specifier: workspace:^ + version: link:../config '@foxify/http': specifier: workspace:^ version: link:../http @@ -453,6 +475,9 @@ importers: specifier: ^1.6.18 version: 1.6.18 devDependencies: + '@foxify/config': + specifier: workspace:^ + version: link:../config '@foxify/inject': specifier: workspace:^ version: link:../inject @@ -2368,13 +2393,13 @@ packages: '@types/node': 20.3.1 chalk: 4.1.2 cosmiconfig: 8.2.0 - cosmiconfig-typescript-loader: 4.3.0(@types/node@20.3.1)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.3) + cosmiconfig-typescript-loader: 4.3.0(@types/node@20.3.1)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.3) - typescript: 5.1.3 + ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -3112,10 +3137,10 @@ packages: - nx dev: true - /@nrwl/jest@16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.3): + /@nrwl/jest@16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.6): resolution: {integrity: sha512-vhwrgjIn1XG3zDSlc6CSfCKBtgDEYQUWG69MdfaqrNInmmsiPkspv7eM99Xh8MGN5HMC2Epzy2todD3J2zZZuQ==} dependencies: - '@nx/jest': 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.3) + '@nx/jest': 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.6) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -3130,10 +3155,10 @@ packages: - verdaccio dev: true - /@nrwl/js@16.3.2(nx@16.3.2)(typescript@5.1.3): + /@nrwl/js@16.3.2(nx@16.3.2)(typescript@5.1.6): resolution: {integrity: sha512-UMmdA4vXy2/VWNMlpBDruT9XwGmLw/MpUaKoN2KLkai/fYN6MvB3mabc9WQ8qsNvDWshmOJ6TqAHReR25BjugQ==} dependencies: - '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.3) + '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.6) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -3145,10 +3170,10 @@ packages: - verdaccio dev: true - /@nrwl/linter@16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.3): + /@nrwl/linter@16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.6): resolution: {integrity: sha512-sUDQNlmRIGQnhdDmpQkJgpF9LZWKBoqXr2g9Y4yq0QlpTamxTbx8/GxMICotA52kayEx1cKbU1xvjJWPchSrlw==} dependencies: - '@nx/linter': 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.3) + '@nx/linter': 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.6) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -3204,15 +3229,15 @@ packages: tslib: 2.4.1 dev: true - /@nx/jest@16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.3): + /@nx/jest@16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.6): resolution: {integrity: sha512-aO8Rc+wwSXLh1jJYd2cxOT5R9BQfqjAXWZOPcvAQQonFNNfwMHrw0+YsqjWgiFtFrxzSX5RrhzVG44cOWpAdqQ==} dependencies: '@jest/reporters': 29.5.0 '@jest/test-result': 29.5.0 - '@nrwl/jest': 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.3) + '@nrwl/jest': 16.3.2(@types/node@20.3.1)(nx@16.3.2)(ts-node@10.9.1)(typescript@5.1.6) '@nx/devkit': 16.3.2(nx@16.3.2) - '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.3) - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.3) + '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.6) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.6) chalk: 4.1.2 dotenv: 10.0.0 identity-obj-proxy: 3.0.0 @@ -3235,7 +3260,7 @@ packages: - verdaccio dev: true - /@nx/js@16.3.2(nx@16.3.2)(typescript@5.1.3): + /@nx/js@16.3.2(nx@16.3.2)(typescript@5.1.6): resolution: {integrity: sha512-bumLGMduNm221Sh3/wkEMEkJOC1kTlqmpx6wamDSsPlAFq0ePgoaNJjoYqC9XH7n7wXtgy9bgKhHJPnek8NKow==} peerDependencies: verdaccio: ^5.0.4 @@ -3250,10 +3275,10 @@ packages: '@babel/preset-env': 7.22.5(@babel/core@7.19.6) '@babel/preset-typescript': 7.22.5(@babel/core@7.19.6) '@babel/runtime': 7.20.1 - '@nrwl/js': 16.3.2(nx@16.3.2)(typescript@5.1.3) + '@nrwl/js': 16.3.2(nx@16.3.2)(typescript@5.1.6) '@nx/devkit': 16.3.2(nx@16.3.2) '@nx/workspace': 16.3.2 - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.3) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.6) babel-plugin-const-enum: 1.2.0(@babel/core@7.19.6) babel-plugin-macros: 2.8.0 babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.19.6) @@ -3276,7 +3301,7 @@ packages: - typescript dev: true - /@nx/linter@16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.3): + /@nx/linter@16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.6): resolution: {integrity: sha512-hVCU6ZIMd+yTMLrC3PbjaHuD3yU+sB/lABTaWuUx2klT0cqKhiTp0KnDLcFWtzQmnNtGEaUjfPKxvA92xon0CA==} peerDependencies: eslint: ^8.0.0 @@ -3284,10 +3309,10 @@ packages: eslint: optional: true dependencies: - '@nrwl/linter': 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.3) + '@nrwl/linter': 16.3.2(eslint@8.43.0)(nx@16.3.2)(typescript@5.1.6) '@nx/devkit': 16.3.2(nx@16.3.2) - '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.3) - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.3) + '@nx/js': 16.3.2(nx@16.3.2)(typescript@5.1.6) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.1.6) eslint: 8.43.0 tmp: 0.2.1 tslib: 2.4.1 @@ -3430,13 +3455,13 @@ packages: node-gyp-build: 4.5.0 dev: true - /@phenomnomnominal/tsquery@5.0.1(typescript@5.1.3): + /@phenomnomnominal/tsquery@5.0.1(typescript@5.1.6): resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==} peerDependencies: typescript: ^3 || ^4 || ^5 dependencies: esquery: 1.4.0 - typescript: 5.1.3 + typescript: 5.1.6 dev: true /@pkgr/utils@2.3.1: @@ -3921,7 +3946,7 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin@5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.3): + /@typescript-eslint/eslint-plugin@5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.6): resolution: {integrity: sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3933,23 +3958,23 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.6) '@typescript-eslint/scope-manager': 5.59.11 - '@typescript-eslint/type-utils': 5.59.11(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/type-utils': 5.59.11(eslint@8.43.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@5.1.6) debug: 4.3.4 eslint: 8.43.0 grapheme-splitter: 1.0.4 ignore: 5.2.0 natural-compare-lite: 1.4.0 semver: 7.3.8 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.59.11(eslint@8.43.0)(typescript@5.1.3): + /@typescript-eslint/parser@5.59.11(eslint@8.43.0)(typescript@5.1.6): resolution: {integrity: sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3961,10 +3986,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.59.11 '@typescript-eslint/types': 5.59.11 - '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.6) debug: 4.3.4 eslint: 8.43.0 - typescript: 5.1.3 + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true @@ -3977,7 +4002,7 @@ packages: '@typescript-eslint/visitor-keys': 5.59.11 dev: true - /@typescript-eslint/type-utils@5.59.11(eslint@8.43.0)(typescript@5.1.3): + /@typescript-eslint/type-utils@5.59.11(eslint@8.43.0)(typescript@5.1.6): resolution: {integrity: sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3987,12 +4012,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.3) - '@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.6) + '@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@5.1.6) debug: 4.3.4 eslint: 8.43.0 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true @@ -4002,7 +4027,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.59.11(typescript@5.1.3): + /@typescript-eslint/typescript-estree@5.59.11(typescript@5.1.6): resolution: {integrity: sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -4017,13 +4042,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.59.11(eslint@8.43.0)(typescript@5.1.3): + /@typescript-eslint/utils@5.59.11(eslint@8.43.0)(typescript@5.1.6): resolution: {integrity: sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -4034,7 +4059,7 @@ packages: '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.59.11 '@typescript-eslint/types': 5.59.11 - '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.1.6) eslint: 8.43.0 eslint-scope: 5.1.1 semver: 7.3.8 @@ -5050,7 +5075,7 @@ packages: vary: 1.1.2 dev: true - /cosmiconfig-typescript-loader@4.3.0(@types/node@20.3.1)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.3): + /cosmiconfig-typescript-loader@4.3.0(@types/node@20.3.1)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6): resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -5061,8 +5086,8 @@ packages: dependencies: '@types/node': 20.3.1 cosmiconfig: 8.2.0 - ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.3) - typescript: 5.1.3 + ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.6) + typescript: 5.1.6 dev: true /cosmiconfig@6.0.0: @@ -5527,7 +5552,7 @@ packages: eslint-plugin-import: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/eslint-plugin': 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@5.1.6) eslint: 8.43.0 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.43.0) eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) @@ -5588,7 +5613,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.6) debug: 3.2.7(supports-color@5.5.0) eslint: 8.43.0 eslint-import-resolver-node: 0.3.7 @@ -5607,7 +5632,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@5.1.6) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 @@ -7060,7 +7085,7 @@ packages: pretty-format: 29.5.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.3) + ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.1.6) transitivePeerDependencies: - supports-color dev: true @@ -8984,7 +9009,7 @@ packages: once: 1.4.0 pidusage: 3.0.2 pino: 8.7.0 - qs: 6.11.0 + qs: 6.11.2 restify-errors: 8.0.2 semver: 7.3.8 send: 0.18.0 @@ -9811,7 +9836,7 @@ packages: matchit: 1.1.0 dev: false - /ts-jest@29.1.0(@babel/core@7.19.6)(jest@29.5.0)(typescript@5.1.3): + /ts-jest@29.1.0(@babel/core@7.19.6)(jest@29.5.0)(typescript@5.1.6): resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -9841,11 +9866,11 @@ packages: lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.3.8 - typescript: 5.1.3 + typescript: 5.1.6 yargs-parser: 21.1.1 dev: true - /ts-node@10.9.1(@types/node@20.3.1)(typescript@5.1.3): + /ts-node@10.9.1(@types/node@20.3.1)(typescript@5.1.6): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -9871,7 +9896,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.1.3 + typescript: 5.1.6 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -9910,14 +9935,14 @@ packages: engines: {node: '>=0.6.x'} dev: false - /tsutils@3.21.0(typescript@5.1.3): + /tsutils@3.21.0(typescript@5.1.6): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.1.3 + typescript: 5.1.6 dev: true /tty-table@4.1.6: @@ -9998,7 +10023,7 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: false - /typedoc@0.24.8(typescript@5.1.3): + /typedoc@0.24.8(typescript@5.1.6): resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==} engines: {node: '>= 14.14'} hasBin: true @@ -10009,11 +10034,11 @@ packages: marked: 4.3.0 minimatch: 9.0.1 shiki: 0.14.2 - typescript: 5.1.3 + typescript: 5.1.6 dev: true - /typescript@5.1.3: - resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} hasBin: true dev: true