@kosmojs/api 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/package.json +11 -18
- package/pkg/debug.d.ts +16 -0
- package/pkg/errors/index.d.ts +21 -0
- package/pkg/errors/index.js +8 -4
- package/pkg/errors/index.js.map +2 -2
- package/pkg/errors/types.d.ts +43 -0
- package/pkg/index.d.ts +3 -0
- package/pkg/index.js +49 -225
- package/pkg/index.js.map +4 -4
- package/pkg/routes.d.ts +5 -0
- package/pkg/types.d.ts +285 -0
- package/pkg/bodyparser/index.js +0 -111
- package/pkg/bodyparser/index.js.map +0 -7
- package/pkg/queryparser/index.js +0 -37
- package/pkg/queryparser/index.js.map +0 -7
- package/pkg/src/app.d.ts +0 -3
- package/pkg/src/bodyparser/config.d.ts +0 -10
- package/pkg/src/bodyparser/index.d.ts +0 -14
- package/pkg/src/bodyparser/types.d.ts +0 -39
- package/pkg/src/debug.d.ts +0 -10
- package/pkg/src/errors/index.d.ts +0 -18
- package/pkg/src/index.d.ts +0 -5
- package/pkg/src/queryparser/index.d.ts +0 -5
- package/pkg/src/router.d.ts +0 -7
- package/pkg/src/types.d.ts +0 -133
- package/pkg/src/use.d.ts +0 -2
- package/pkg/test/createRouterRoutes/params.test.d.ts +0 -1
- package/pkg/test/createRouterRoutes/routeMiddleware.test.d.ts +0 -1
- package/pkg/test/createRouterRoutes/useWrappers.test.d.ts +0 -1
- package/pkg/test/defineRoute.test.d.ts +0 -1
- package/pkg/test/index.d.ts +0 -7
- package/pkg/test/use.test.d.ts +0 -1
package/pkg/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/
|
|
4
|
-
"sourcesContent": ["import Koa from \"koa\";\n\nimport withQueryparser from \"@/queryparser\";\n\nimport type { AppOptions } from \"./types\";\n\nexport const createApp = (options?: AppOptions) => {\n return withQueryparser(new Koa(options));\n};\n", "import type Koa from \"koa\";\nimport type { IParseOptions, IStringifyOptions } from \"qs\";\nimport { parse, stringify } from \"qs\";\n\nimport type { DefaultContext, DefaultState } from \"@/types\";\n\nexport default <\n T extends InstanceType<typeof Koa<DefaultState, DefaultContext>> = never,\n>(\n app: T,\n _parseOptions: IParseOptions = {},\n _stringifyOptions: IStringifyOptions = {},\n) => {\n const parseOptions = {\n ignoreQueryPrefix: true,\n parseArrays: true,\n arrayLimit: 100,\n parameterLimit: 100,\n depth: 5,\n ..._parseOptions,\n };\n\n const stringifyOptions = {\n encodeValuesOnly: true,\n arrayFormat: \"brackets\",\n ..._stringifyOptions,\n } as const;\n\n const obj = {\n get query() {\n return parse((this as Koa.Request).querystring || \"\", parseOptions);\n },\n\n set query(obj: object) {\n (this as Koa.Request).querystring = stringify(obj, stringifyOptions);\n },\n };\n\n const entries = Object.getOwnPropertyNames(obj).map((name) => [\n name,\n Object.getOwnPropertyDescriptor(obj, name),\n ]) as [name: string, desc: PropertyDescriptor][];\n\n for (const [name, desc] of entries) {\n Object.defineProperty(app.request, name, desc);\n }\n\n return app;\n};\n", "import type {\n ValidationErrorData,\n ValidationErrorEntry,\n ValidationErrorScope,\n} from \"@/types\";\n\n/**\n * Standardized error wrapper used by validation generators.\n *\n * Instances of this class are thrown whenever validation fails,\n * carrying both the error scope (e.g. `\"params\"`, `\"payload\"`)\n * and the list of validation error details.\n * */\nexport class ValidationError extends Error {\n public scope: ValidationErrorScope;\n public errors: Array<ValidationErrorEntry> = [];\n public errorMessage: string;\n public errorSummary: string;\n\n constructor([scope, { errors, errorMessage, errorSummary }]: [\n ValidationErrorScope,\n ValidationErrorData,\n ]) {\n super(JSON.stringify(errors, null, 2));\n this.name = `${scope}ValidationError`;\n this.scope = scope;\n this.errors = errors;\n this.errorMessage = errorMessage;\n this.errorSummary = errorSummary;\n }\n}\n", "import Router, { type RouterMiddleware } from \"@koa/router\";\n\nimport debugRouteEntry from \"./debug\";\nimport type {\n DefineRoute,\n HandlerDefinition,\n MiddlewareDefinition,\n RouterOptions,\n RouterRoute,\n RouterRouteSource,\n UseSlots,\n ValidationSchemas,\n} from \"./types\";\nimport { use } from \"./use\";\n\nexport const defineRoute: DefineRoute = (factory) => {\n return factory({\n use(middleware, options) {\n return {\n kind: \"middleware\",\n middleware: [middleware as never].flat(),\n options,\n };\n },\n HEAD(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"HEAD\",\n };\n },\n OPTIONS(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"OPTIONS\",\n };\n },\n GET(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"GET\",\n };\n },\n POST(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"POST\",\n };\n },\n PUT(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"PUT\",\n };\n },\n PATCH(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"PATCH\",\n };\n },\n DELETE(middleware) {\n return {\n kind: \"handler\",\n middleware: [middleware as never].flat(),\n method: \"DELETE\",\n };\n },\n });\n};\n\nexport const createRouter = (options?: RouterOptions) => {\n return new Router(options);\n};\n\nexport const createRouterRoutes = (\n routeSources: Array<RouterRouteSource>,\n {\n // Global middleware applied to every route (e.g., logging)\n coreMiddleware,\n }: {\n coreMiddleware: Array<MiddlewareDefinition>;\n },\n): Array<RouterRoute> => {\n // NOTE:: prioritized middleware must run in this exact order!\n const prioritizedSlots: Array<keyof UseSlots> = [\n \"errorHandler\",\n \"params\",\n \"validateParams\",\n \"bodyparser\",\n \"payload\",\n \"validatePayload\",\n \"validateResponse\",\n ];\n\n const stack: Array<RouterRoute> = [];\n\n // Iterate over each route definition\n for (const { name, path, file, ...rest } of routeSources) {\n // Include both middleware and HTTP method handlers\n const definitionItems = [\n ...rest.useWrappers,\n ...rest.definitionItems,\n ].flat();\n\n const routeMiddleware: Array<MiddlewareDefinition> = definitionItems.filter(\n (e) => e.kind === \"middleware\",\n );\n\n const middlewareStack: Array<MiddlewareDefinition> = [\n ...createParamsMiddleware(rest.params, rest.numericParams),\n ...createValidationMiddleware(rest.validationSchemas),\n // core middleware overrides builtin middleware (of same slot)\n ...coreMiddleware,\n // route middleware overrides core middleware (of same slot)\n ...routeMiddleware,\n ];\n\n // NOTE: later defined middleware should override previous middleware of same slot\n const routeStack: Array<MiddlewareDefinition | HandlerDefinition> = [\n ...prioritizedSlots.flatMap((slot) => {\n const middleware = middlewareStack.findLast(\n // Using findLast to pick the latest entry,\n // ensuring later entries override earlier ones.\n (e) => e.options?.slot === slot,\n );\n return middleware //\n ? [middleware]\n : [];\n }),\n\n ...coreMiddleware.flatMap((entry) => {\n if (!entry.options?.slot) {\n // no slot, including regardless\n return [entry];\n }\n if (prioritizedSlots.includes(entry.options?.slot)) {\n // already picked when inserted prioritized middleware, excluding\n return [];\n }\n const override = routeMiddleware.findLast(\n // Using findLast to pick the latest entry,\n // ensuring later entries override earlier ones.\n (e) => e.options?.slot === entry.options?.slot,\n );\n return [override || entry];\n }),\n\n ...definitionItems.flatMap((entry) => {\n const slot =\n entry.kind === \"middleware\" //\n ? entry.options?.slot\n : undefined;\n\n if (slot) {\n if (prioritizedSlots.includes(slot)) {\n // already picked when inserted prioritized middleware, excluding\n return [];\n }\n if (coreMiddleware.some((e) => e.options?.slot === slot)) {\n // already picked when inserted core middleware, excluding\n return [];\n }\n }\n\n return [entry];\n }),\n ];\n\n for (const entry of routeStack) {\n if (entry.kind === \"handler\") {\n const middleware = routeStack.flatMap((e) => {\n if (e.kind === \"middleware\") {\n return !e.options?.on || e.options.on.includes(entry.method)\n ? [e]\n : [];\n }\n return [];\n });\n stack.push({\n name,\n path,\n file,\n methods: [entry.method],\n middleware: [\n ...middleware.flatMap((e) => e.middleware),\n ...entry.middleware,\n ] as unknown as Array<RouterMiddleware>,\n debug: debugRouteEntry({\n name,\n path,\n file,\n methods: [entry.method],\n middleware,\n handler: entry,\n }),\n });\n }\n }\n }\n\n return stack;\n};\n\nconst createParamsMiddleware = (\n params: RouterRouteSource[\"params\"],\n numericParams: RouterRouteSource[\"numericParams\"],\n) => [\n use(\n function useParams(ctx, next) {\n ctx.typedParams = params.reduce(\n (map: Record<string, unknown>, [name, isRest]) => {\n const value = ctx.params[name];\n if (value) {\n if (isRest) {\n map[name] = numericParams.includes(name)\n ? value.split(\"/\").map(Number)\n : value.split(\"/\");\n } else {\n map[name] = numericParams.includes(name) ? Number(value) : value;\n }\n } else {\n map[name] = value;\n }\n return map;\n },\n {},\n ) as never;\n return next();\n },\n { slot: \"params\" },\n ),\n];\n\nconst createValidationMiddleware = (validationSchemas: ValidationSchemas) => [\n use(\n function useValidateParams(ctx, next) {\n validationSchemas.params?.validate(ctx.typedParams);\n return next();\n },\n { slot: \"validateParams\" },\n ),\n\n use(\n function useValidatePayload(ctx, next) {\n validationSchemas.payload?.[ctx.method]?.validate(ctx.payload);\n return next();\n },\n {\n slot: \"validatePayload\",\n on: Object.keys(validationSchemas.payload || {}) as never,\n },\n ),\n\n use(\n async function useValidateResponse(ctx, next) {\n if (validationSchemas.response?.[ctx.method]) {\n await next();\n validationSchemas.response?.[ctx.method]?.validate(ctx.body);\n } else {\n return next();\n }\n },\n {\n slot: \"validateResponse\",\n on: Object.keys(validationSchemas.response || {}) as never,\n },\n ),\n];\n", "import { styleText } from \"node:util\";\n\nimport stringWidth from \"string-width\";\n\nimport type {\n HandlerDefinition,\n MiddlewareDefinition,\n RouterRoute,\n} from \"./types\";\n\nconst colorizeMethod = (method: string): string => {\n const color = (\n {\n HEAD: \"gray\",\n GET: \"green\",\n POST: \"blue\",\n PATCH: \"blue\",\n PUT: \"blue\",\n DELETE: \"red\",\n } as const\n )[method];\n return color ? styleText(color, method) : method;\n};\n\nexport default (entry: {\n name: string;\n path: string;\n file: string;\n methods: Array<string>;\n middleware: Array<MiddlewareDefinition>;\n handler: HandlerDefinition;\n}): RouterRoute[\"debug\"] => {\n const { path, file } = entry;\n\n const methodLines = entry.methods.flatMap((method) => {\n const coloredMethod = colorizeMethod(method);\n return method === \"GET\"\n ? [coloredMethod + styleText(\"gray\", \"|HEAD\")]\n : [coloredMethod];\n });\n\n const middlewareLines = entry.middleware\n .map(({ options, middleware }) => {\n const lines: Array<string> = [];\n\n if (options?.slot) {\n lines.push(\n `${styleText(\"dim\", \"slot:\")} ${styleText(\"blue\", options.slot)};`,\n );\n }\n\n const funcNames = middleware.map((fn) => {\n return styleText(\"magenta\", funcName(fn));\n });\n\n lines.push(`${styleText(\"dim\", \"exec:\")} ${funcNames.join(\"; \")}`);\n\n return lines.join(\" \");\n })\n .join(`\\n${Array(12).fill(\" \").join(\"\")}`);\n\n const handlerLines = entry.handler.middleware.map((fn) => {\n return styleText(\"yellow\", funcName(fn));\n });\n\n const headline = `${styleText(\"bgBlue\", styleText(\"black\", ` ${path} `))} ${styleText(\"gray\", `[ ${file} ]`)}`;\n const methods = `${styleText(\"dim\", \" methods:\")} ${methodLines.join(\" \")}`;\n const middleware = `${styleText(\"dim\", \"middleware:\")} ${middlewareLines}`;\n const handler = `${styleText(\"dim\", \" handler:\")} ${handlerLines.join(Array(7).fill(\" \").join(\"\"))}`;\n\n const maxColumns = process.stdout.isTTY\n ? Number(process.stdout.columns || 80)\n : 80;\n\n const debugEntries = [\n [\"headline\", headline],\n [\"methods\", methods],\n [\"middleware\", middleware],\n [\"handler\", handler],\n ] as const;\n\n const lineMapper = (line: string) => {\n const freeColumns = maxColumns - stringWidth(line);\n return freeColumns > 0\n ? [\n line,\n styleText(\n \"dim\",\n styleText(\"gray\", Array(freeColumns).fill(\"\u00B7\").join(\"\")),\n ),\n ].join(\"\")\n : line;\n };\n\n const debug = debugEntries.reduce(\n (map, [key, line]) => {\n map[key] = line.split(\"\\n\").map(lineMapper).join(\"\\n\");\n return map;\n },\n {} as RouterRoute[\"debug\"],\n );\n\n return {\n ...debug,\n full: Object.values(debug).join(\"\\n\"),\n };\n};\n\nconst funcName = (fn: Function) => {\n return fn.name || fn.toString().split(\"\\n\")[0].slice(0, 30);\n};\n", "import type { Use } from \"./types\";\n\nexport const use: Use = (middleware, options) => {\n return {\n kind: \"middleware\",\n middleware: [middleware].flat() as never,\n options,\n };\n};\n", "import type { RouterContext, RouterMiddleware } from \"@koa/router\";\nimport type { Next } from \"koa\";\n\ndeclare module \"koa\" {\n interface Request {\n body?: unknown;\n rawBody: string;\n }\n}\n\nexport interface DefaultState {}\nexport interface DefaultContext {}\n\nexport enum HTTPMethods {\n HEAD = \"HEAD\",\n OPTIONS = \"OPTIONS\",\n GET = \"GET\",\n PUT = \"PUT\",\n PATCH = \"PATCH\",\n POST = \"POST\",\n DELETE = \"DELETE\",\n}\n\nexport type HTTPMethod = keyof typeof HTTPMethods;\n\nexport type ParameterizedContext<\n ParamsT,\n StateT,\n ContextT,\n PayloadT = unknown,\n ResponseT = unknown,\n> = RouterContext<\n DefaultState & StateT,\n DefaultContext &\n ContextT & {\n typedParams: ParamsT;\n payload: PayloadT;\n },\n ResponseT\n>;\n\nexport type ParameterizedMiddleware<\n ParamsT = {},\n StateT = {},\n ContextT = {},\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT>,\n next: Next,\n) => Promise<void> | void;\n\nexport type RouteHandler<\n ParamsT,\n StateT,\n ContextT,\n PayloadT = unknown,\n ResponseT = unknown,\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT, PayloadT, ResponseT>,\n next: Next,\n) => Promise<void> | void;\n\nexport type MiddlewareDefinition = {\n kind: \"middleware\";\n middleware: Array<ParameterizedMiddleware>;\n options?: UseOptions | undefined;\n};\n\nexport type HandlerDefinition = {\n kind: \"handler\";\n middleware: Array<ParameterizedMiddleware>;\n method: HTTPMethod;\n};\n\nexport type RouteDefinitionItem = MiddlewareDefinition | HandlerDefinition;\n\nexport type DefineRouteHelpers<\n ParamsT,\n StateT,\n ContextT,\n OptionalHandlers = undefined,\n> = {\n // INFO: The `use` helper intentionally does not accept type parameters.\n // PayloadT and ResponseT are only relevant to route handlers,\n // as different request methods receive different payloads and return different responses.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, StateT, ContextT>\n | Array<ParameterizedMiddleware<ParamsT, StateT, ContextT>>,\n options?: UseOptions,\n ) => RouteDefinitionItem;\n} & {\n [M in HTTPMethod]: M extends OptionalHandlers\n ? <PayloadT = unknown, ResponseT = unknown>(\n handler?:\n | RouteHandler<ParamsT, StateT, ContextT, PayloadT, ResponseT>\n | Array<RouteHandler<ParamsT, StateT, ContextT, PayloadT, ResponseT>>,\n ) => RouteDefinitionItem\n : <PayloadT = unknown, ResponseT = unknown>(\n handler:\n | RouteHandler<ParamsT, StateT, ContextT, PayloadT, ResponseT>\n | Array<RouteHandler<ParamsT, StateT, ContextT, PayloadT, ResponseT>>,\n ) => RouteDefinitionItem;\n};\n\nexport type DefineRoute = <\n ParamsT = Record<string, string>,\n StateT = object,\n ContextT = object,\n>(\n factory: (\n helpers: DefineRouteHelpers<ParamsT, StateT, ContextT>,\n ) => Array<RouteDefinitionItem>,\n) => Array<RouteDefinitionItem>;\n\nexport interface UseSlots {\n errorHandler: string;\n params: string;\n validateParams: string;\n bodyparser: string;\n payload: string;\n validatePayload: string;\n validateResponse: string;\n}\n\nexport type UseOptions = {\n on?: Array<HTTPMethod>;\n slot?: keyof UseSlots;\n debug?: string | undefined;\n};\n\nexport type Use = <StateT = DefaultState, ContextT = DefaultContext>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, StateT, ContextT>\n | Array<ParameterizedMiddleware<Record<string, string>, StateT, ContextT>>,\n options?: UseOptions,\n) => MiddlewareDefinition;\n\nexport type RouterRouteSource = {\n name: string;\n path: string;\n file: string;\n // useWrappers is same as defining middleware inside route definition,\n // just automatically imported from use.ts files\n useWrappers: [...a: Array<MiddlewareDefinition>];\n definitionItems: Array<RouteDefinitionItem>;\n params: Array<[name: string, isRest?: boolean]>;\n numericParams: Array<string>;\n validationSchemas: ValidationSchemas;\n meta?: Record<string, unknown>;\n};\n\nexport type RouterRoute = {\n name: string;\n path: string;\n file: string;\n methods: Array<string>;\n middleware: Array<RouterMiddleware>;\n debug: {\n headline: string;\n methods: string;\n middleware: string;\n handler: string;\n full: string;\n };\n};\n\nimport type Koa from \"koa\";\nexport type App = Koa<DefaultState, DefaultContext>;\nexport type AppOptions = ConstructorParameters<typeof import(\"koa\")>[0];\n\nexport type Router = import(\"@koa/router\").Router<DefaultState, DefaultContext>;\nexport type RouterOptions = import(\"@koa/router\").RouterOptions;\n\nexport type DevMiddlewareFactory = (\n app: App,\n) => (\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n next: () => Promise<void>,\n) => Promise<void>;\nexport type TeardownHandler = (app: App) => void | Promise<void>;\n\nexport type ValidationSchema = {\n check: (data: unknown) => boolean;\n errors: (data: unknown) => Array<ValidationErrorEntry>;\n errorMessage: (data: unknown) => string;\n errorSummary: (data: unknown) => string;\n validate: (data: unknown) => void;\n};\n\nexport type ValidationSchemas<Extend = object> = {\n params?: ValidationSchema & Extend;\n payload?: Record<string, ValidationSchema & Extend>;\n response?: Record<string, ValidationSchema & Extend>;\n};\n\nexport type ValidationErrorScope = \"params\" | \"payload\" | \"response\";\n\n/**\n * Shape of individual validation errors emitted by generators.\n */\nexport type ValidationErrorEntry = {\n /** JSON Schema keyword that triggered the error (e.g. `format`, `maxItems`, `maxLength`). */\n keyword: string;\n /** JSON Pointer\u2013style path to the invalid field (matches JSON Schema `instancePath`). */\n path: string;\n /** Human-readable error message. */\n message: string;\n /** Constraint parameters (e.g. `{ limit: 5 }`, `{ format: \"email\" }`). */\n params?: Record<string, unknown>;\n /** Optional error code for i18n/l10n or custom handling. */\n code?: string;\n};\n\nexport type ValidationErrorData = {\n errors: Array<ValidationErrorEntry>;\n /**\n * Formats errors into a single human-readable message.\n * @example: Validation failed: user: missing required properties: \"email\", \"name\"; password: must be at least 8 characters long\n */\n errorMessage: string;\n /**\n * Gets a simple error summary for quick feedback.\n * @example: 2 validation errors found across 2 fields\n */\n errorSummary: string;\n};\n"],
|
|
5
|
-
"mappings": ";AAAA,
|
|
6
|
-
"names": ["
|
|
3
|
+
"sources": ["../src/debug.ts", "../src/routes.ts", "../src/types.ts"],
|
|
4
|
+
"sourcesContent": ["import { styleText } from \"node:util\";\n\nimport stringWidth from \"string-width\";\n\nimport type { HTTPMethod, Route, UseOptions } from \"./types\";\n\nconst colorizeMethod = (method: string): string => {\n const color = (\n {\n HEAD: \"gray\",\n GET: \"green\",\n POST: \"blue\",\n PATCH: \"blue\",\n PUT: \"blue\",\n DELETE: \"red\",\n } as const\n )[method];\n return color ? styleText(color, method) : method;\n};\n\nexport const debugRouteEntry = <MiddlewareT>(entry: {\n name: string;\n path: string;\n file: string;\n methods: Array<string>;\n middleware: Array<{\n middleware: Array<MiddlewareT>;\n options?: UseOptions | undefined;\n }>;\n handler: {\n kind: \"handler\";\n middleware: Array<MiddlewareT>;\n method: HTTPMethod;\n };\n}): Route<MiddlewareT>[\"debug\"] => {\n const { path, file } = entry;\n\n const methodLines = entry.methods.flatMap((method) => {\n const coloredMethod = colorizeMethod(method);\n return method === \"GET\"\n ? [coloredMethod + styleText(\"gray\", \"|HEAD\")]\n : [coloredMethod];\n });\n\n const middlewareLines = entry.middleware\n .map(({ options, middleware }) => {\n const lines: Array<string> = [];\n\n if (options?.slot) {\n lines.push(\n `${styleText(\"dim\", \"slot:\")} ${styleText(\"blue\", options.slot)}`,\n );\n }\n\n lines.push(\n styleText(\"dim\", middleware.map(funcName as never).join(\"; \")),\n );\n\n return lines.join(\" \");\n })\n .join(`\\n${Array(12).fill(\" \").join(\"\")}`);\n\n const handlerLines = entry.handler.middleware.map((fn) => {\n return styleText(\"yellow\", funcName(fn as Function));\n });\n\n const headline = `${styleText(\"bgBlue\", styleText(\"black\", ` ${path} `))} ${styleText(\"gray\", `[ ${file} ]`)}`;\n const methods = `${styleText(\"dim\", \" methods:\")} ${methodLines.join(\" \")}`;\n const middleware = `${styleText(\"dim\", \"middleware:\")} ${middlewareLines}`;\n const handler = `${styleText(\"dim\", \" handler:\")} ${handlerLines.join(Array(7).fill(\" \").join(\"\"))}`;\n\n const maxColumns = process.stdout.isTTY\n ? Number(process.stdout.columns || 80)\n : 80;\n\n const debugEntries = [\n [\"headline\", headline],\n [\"methods\", methods],\n [\"middleware\", middleware],\n [\"handler\", handler],\n ] as const;\n\n const lineMapper = (line: string) => {\n const freeColumns = maxColumns - stringWidth(line);\n return freeColumns > 0\n ? [\n line,\n styleText(\n \"dim\",\n styleText(\"gray\", Array(freeColumns).fill(\"\u00B7\").join(\"\")),\n ),\n ].join(\"\")\n : line;\n };\n\n const debug = debugEntries.reduce(\n (map, [key, line]) => {\n map[key] = line.split(\"\\n\").map(lineMapper).join(\"\\n\");\n return map;\n },\n {} as Route<MiddlewareT>[\"debug\"],\n );\n\n return {\n ...debug,\n full: Object.values(debug).join(\"\\n\"),\n };\n};\n\nconst funcName = (fn: Function) => {\n return fn.name || fn.toString().split(\"\\n\")[0].slice(0, 30);\n};\n", "import { debugRouteEntry } from \"./debug\";\nimport type {\n CreateRouteMiddleware,\n HandlerDefinition,\n MiddlewareDefinition,\n Route,\n RouteSource,\n UseSlots,\n} from \"./types\";\n\nexport const createRoutes = <MiddlewareT, MiddlewareR>(\n routeSources: Array<RouteSource<MiddlewareT>>,\n {\n globalMiddleware,\n createRouteMiddleware,\n }: {\n // Global middleware applied to every route (e.g., logging)\n globalMiddleware: Array<MiddlewareDefinition<MiddlewareT>>;\n // route-specific middlware\n createRouteMiddleware: CreateRouteMiddleware<MiddlewareT>;\n },\n): Array<Route<MiddlewareR>> => {\n // NOTE:: prioritized middleware must run in this exact order!\n const prioritizedSlots: Array<keyof UseSlots> = [\n \"errorHandler\",\n \"extendContext\",\n \"bodyparser\",\n \"validate:params\",\n \"validate:query\",\n \"validate:headers\",\n \"validate:cookies\",\n \"validate:json\",\n \"validate:form\",\n \"validate:raw\",\n \"validate:response\",\n ];\n\n const stack: Array<Route<MiddlewareR>> = [];\n\n // Iterate over each route definition\n for (const routeSource of routeSources) {\n const { name, path, file } = routeSource;\n\n // Include both middleware and HTTP method handlers\n const definitionItems = [\n ...routeSource.cascadingMiddleware,\n ...routeSource.definitionItems,\n ].flat();\n\n const routeMiddleware: Array<MiddlewareDefinition<MiddlewareT>> =\n definitionItems.filter((e) => e.kind === \"middleware\");\n\n // NOTE: should be built in exactly this order\n const middlewareStack: Array<MiddlewareDefinition<MiddlewareT>> = [\n ...createRouteMiddleware(routeSource),\n ...globalMiddleware,\n ...routeMiddleware,\n ];\n\n // NOTE: later defined middleware should override previous middleware of same slot\n const routeStack: Array<\n MiddlewareDefinition<MiddlewareT> | HandlerDefinition<MiddlewareT>\n > = [\n ...prioritizedSlots.flatMap((slot) => {\n const middleware = middlewareStack.findLast(\n // Using findLast to pick the latest entry,\n // ensuring later entries override earlier ones.\n (e) => e.options?.slot === slot,\n );\n return middleware //\n ? [middleware]\n : [];\n }),\n\n ...globalMiddleware.flatMap((entry) => {\n if (!entry.options?.slot) {\n // no slot, including regardless\n return [entry];\n }\n if (prioritizedSlots.includes(entry.options?.slot)) {\n // already picked when inserted prioritized middleware, excluding\n return [];\n }\n const override = routeMiddleware.findLast(\n // Using findLast to pick the latest entry,\n // ensuring later entries override earlier ones.\n (e) => e.options?.slot === entry.options?.slot,\n );\n return [override || entry];\n }),\n\n ...definitionItems.flatMap((entry) => {\n const slot =\n entry.kind === \"middleware\" //\n ? entry.options?.slot\n : undefined;\n\n if (slot) {\n if (prioritizedSlots.includes(slot)) {\n // already picked when inserted prioritized middleware, excluding\n return [];\n }\n if (globalMiddleware.some((e) => e.options?.slot === slot)) {\n // already picked when inserted core middleware, excluding\n return [];\n }\n }\n\n return [entry];\n }),\n ];\n\n for (const entry of routeStack) {\n if (entry.kind === \"handler\") {\n const middleware = routeStack.flatMap((e) => {\n if (e.kind === \"middleware\") {\n return !e.options?.on || e.options.on.includes(entry.method)\n ? [e]\n : [];\n }\n return [];\n });\n stack.push({\n name,\n path,\n file,\n methods: [entry.method],\n middleware: [\n ...middleware.flatMap((e) => e.middleware),\n ...entry.middleware,\n ] as Array<never>,\n debug: debugRouteEntry<MiddlewareT>({\n name,\n path,\n file,\n methods: [entry.method],\n middleware,\n handler: entry,\n }),\n });\n }\n }\n }\n\n return stack;\n};\n", "/// <reference types=\"@types/bun\" />\n\nimport type { ValidationErrorEntry } from \"./errors/types\";\n\nexport enum HTTPMethods {\n HEAD = \"HEAD\",\n OPTIONS = \"OPTIONS\",\n GET = \"GET\",\n PUT = \"PUT\",\n PATCH = \"PATCH\",\n POST = \"POST\",\n DELETE = \"DELETE\",\n}\n\nexport type HTTPMethod = keyof typeof HTTPMethods;\n\nexport type MiddlewareDefinition<MiddlewareT> = {\n kind: \"middleware\";\n middleware: Array<MiddlewareT>;\n options?: UseOptions | undefined;\n};\n\nexport type HandlerDefinition<MiddlewareT> = {\n kind: \"handler\";\n middleware: Array<MiddlewareT>;\n method: HTTPMethod;\n};\n\nexport type RouteDefinitionItem<MiddlewareT> =\n | MiddlewareDefinition<MiddlewareT>\n | HandlerDefinition<MiddlewareT>;\n\nexport interface UseSlots {\n errorHandler: string;\n extendContext: string;\n bodyparser: string;\n \"validate:params\": string;\n \"validate:query\": string;\n \"validate:headers\": string;\n \"validate:cookies\": string;\n \"validate:json\": string;\n \"validate:form\": string;\n \"validate:raw\": string;\n \"validate:response\": string;\n}\n\nexport type UseOptions = {\n on?: Array<HTTPMethod>;\n slot?: keyof UseSlots;\n debug?: string | undefined;\n};\n\nexport type RouteSource<MiddlewareT> = {\n name: string;\n path: string;\n // path-to-regexp pattern\n pathPattern: string;\n file: string;\n // same as inline middleware inside route definition,\n // just automatically imported from use.ts files\n cascadingMiddleware: [...a: Array<MiddlewareDefinition<MiddlewareT>>];\n definitionItems: Array<RouteDefinitionItem<MiddlewareT>>;\n params: Array<string>;\n numericParams: Array<string>;\n validationSchemas: ValidationSchemas;\n meta?: Record<string, unknown>;\n};\n\nexport type Route<MiddlewareT> = {\n name: string;\n path: string;\n file: string;\n methods: Array<string>;\n middleware: Array<MiddlewareT>;\n debug: {\n headline: string;\n methods: string;\n middleware: string;\n handler: string;\n full: string;\n };\n};\n\nexport type DevSetup = {\n /**\n * API request handler for development mode.\n *\n * In dev mode, incoming requests are routed based on URL:\n * - Requests matching apiurl routed to this handler (your API)\n * - All other requests routed to Vite dev server (pages/assets)\n *\n * Returns a function that processes API requests.\n * */\n requestHandler: () => (\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n ) => Promise<void>;\n\n /**\n * Custom function to determine if a request should be handled by the API.\n *\n * By default, requests are routed to the API handler if their URL starts with `apiurl`.\n * Use this to implement custom heuristics for detecting API requests.\n * */\n requestMatcher?: (req: import(\"node:http\").IncomingMessage) => boolean;\n\n /**\n * In dev mode, perform cleanup operations before reloading the API handler.\n * */\n teardownHandler?: () => void | Promise<void>;\n};\n\nexport type AppFactory<App, AppOptions = unknown> = (\n factory: (a: { createApp: (o?: AppOptions) => App }) => App,\n) => App;\n\nexport type RouterFactory<Router, RouterOptions = unknown> = (\n factory: (a: { createRouter: (o?: RouterOptions) => Router }) => Router,\n) => Router;\n\nexport type CreateRouteMiddleware<MiddlewareT> = (\n routeSource: RouteSource<MiddlewareT>,\n) => Array<MiddlewareDefinition<MiddlewareT>>;\n\nexport type CreateServer<App, Server> = (\n app: App,\n opt?: {\n port?: number;\n sock?: string;\n callback?: () => void | Promise<void>;\n },\n) => Promise<Server>;\n\nexport type ServerFactory<App, Server> = (\n factory: (a: { createServer: CreateServer<App, Server> }) => void,\n) => void;\n\n/**\n * Request metadata validation targets.\n * */\nexport const RequestMetadataTargets = {\n query: \"URL query parameters\",\n headers: \"HTTP request headers\",\n cookies: \"HTTP cookies\",\n} as const;\n\n/**\n * Request body validation targets.\n *\n * Body formats are mutually exclusive - only one should be specified per handler.\n *\n * **Development behavior:**\n * - If multiple formats are defined, the builder displays a warning and\n * disables validation schemas for the affected handler.\n * - If an unsuitable target is defined (e.g., `json`, `form`)\n * for a method without a body like GET, HEAD), a warning is displayed and\n * validation schemas are disabled for that handler.\n *\n * This ensures misconfigurations are detected during development\n * for runtime to execute without false positive validation failures.\n *\n * Always define exactly one target that is suitable for current handler.\n * */\nexport const RequestBodyTargets = {\n json: \"JSON request body\",\n form: \"URL-encoded or Multipart form\",\n raw: \"Raw body format (string/Buffer/ArrayBuffer/Blob)\",\n} as const;\n\nexport const RequestValidationTargets = {\n ...RequestMetadataTargets,\n ...RequestBodyTargets,\n} as const;\n\nexport type RequestMetadataTarget = keyof typeof RequestMetadataTargets;\nexport type RequestBodyTarget = keyof typeof RequestBodyTargets;\nexport type RequestValidationTarget = keyof typeof RequestValidationTargets;\n\nexport type ValidationTarget = RequestValidationTarget | \"params\" | \"response\";\n\nexport type ValidationDefmap = Partial<{\n /**\n * Request metadata targets.\n * */\n query: Record<string, unknown>;\n headers: Record<string, string>;\n cookies: Record<string, unknown>;\n\n /**\n * Request body targets. One target per handler.\n *\n * POST<\n * json: { id: number }\n * // or form/raw\n * >((ctx) => {})\n * */\n json: unknown;\n form: Record<string, unknown>;\n raw: string | Buffer | ArrayBuffer | Blob;\n\n /**\n * Response variants.\n * Multiple variants can be specified via unions.\n *\n * POST<\n * response:\n * | [200, \"json\", User]\n * | [201, \"json\"]\n * | [301]\n * >((ctx) => {})\n * */\n response: [\n /**\n * HTTP status code to send with the response.\n * Common values: 200 (OK), 400 (Bad Request), 404 (Not Found), 500 (Internal Server Error)\n * */\n status: number,\n /**\n * Content-Type header for the response. Supports shorthand notation that gets\n * resolved via mime-types lookup (e.g., \"json\" becomes \"application/json\",\n * \"html\" becomes \"text/html\", \"png\" becomes \"image/png\")\n * */\n contentType?: string | undefined,\n /** The response body schema */\n body?: unknown,\n ];\n}>;\n\nexport type ValidationCustomErrors = {\n /**\n * Custom error messages for validation failures.\n *\n * Use `error` to set a general error message for the entire validation target.\n * Use `error.<fieldName>` to set specific error messages for individual fields.\n *\n * @example Override validation error messages\n * POST<{\n * json: {\n * id: number;\n * email: string;\n * age: number;\n * }\n * }, {\n * json: {\n * error: \"Invalid user data provided\",\n * \"error.id\": \"User ID must be a valid number\",\n * \"error.email\": \"Please provide a valid email address\",\n * \"error.age\": \"Age must be a number\"\n * }\n * }>\n * */\n error?: string;\n} & {\n [E in `error.${string}`]?: string;\n};\n\nexport type ValidationOptions = {\n /**\n * Controls runtime validation for this target.\n *\n * By default, all validation targets are validated at runtime. Set this to\n * `false` if you only need compile-time type checking without runtime validation.\n *\n * @example Disable runtime validation for JSON payload\n * POST<{\n * json: Payload<User>\n * }, {\n * json: {\n * runtimeValidation: false\n * }\n * }>\n * */\n runtimeValidation?: boolean | undefined;\n\n /**\n * Specifies the request Content-Type for OpenAPI schema generation.\n *\n * When the validation target is `form`, the OpenAPI generator will include\n * both `application/x-www-form-urlencoded` and `multipart/form-data` in the\n * request body content types by default. This indicates the handler accepts\n * either format, which may not be accurate for your use case.\n *\n * Use this option to explicitly declare which content type your handler expects.\n * */\n contentType?: string;\n} & ValidationCustomErrors;\n\nexport type ValidationOptmap = {\n [K in ValidationTarget]?: ValidationOptions;\n};\n\nexport const StateKey: unique symbol = Symbol(\"kosmo.state\");\n\nexport type ExtendContext<\n ParamsT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap,\n BodyparserOptions extends Record<RequestBodyTarget, unknown>,\n> = {\n [StateKey]: Map<ValidationTarget, unknown>;\n bodyparser: {\n [T in RequestBodyTarget]: <R = unknown>(\n opts?: BodyparserOptions[T],\n ) => Promise<R>;\n };\n validated: {\n // Only iterate over defined keys\n [K in keyof VDefs as K extends RequestValidationTarget\n ? VOpts[K] extends { runtimeValidation: false }\n ? never\n : K\n : never]: VDefs[K];\n } & { params: ParamsT };\n};\n\nexport type ValidationSchema = {\n check: (data: unknown) => boolean;\n errors: (data: unknown) => Array<ValidationErrorEntry>;\n errorMessage: (data: unknown) => string;\n errorSummary: (data: unknown) => string;\n validate: (data: unknown) => void;\n};\n\nexport type ValidationSchemas<Extend = object> = {\n [T in RequestValidationTarget]?: Record<\n // http method\n string,\n ValidationSchema &\n Extend & {\n runtimeValidation?: boolean;\n customErrors?: ValidationCustomErrors;\n }\n >;\n} & {\n params?: ValidationSchema & Extend;\n response?: Record<\n // http method\n string,\n Array<\n ValidationSchema &\n Extend & {\n status: number;\n contentType?: string;\n runtimeValidation?: boolean;\n customErrors?: ValidationCustomErrors;\n }\n >\n >;\n};\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,iBAAiB;AAE1B,OAAO,iBAAiB;AAIxB,IAAM,iBAAiB,CAAC,WAA2B;AACjD,QAAM,QACJ;AAAA,IACE,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,EACA,MAAM;AACR,SAAO,QAAQ,UAAU,OAAO,MAAM,IAAI;AAC5C;AAEO,IAAM,kBAAkB,CAAc,UAcV;AACjC,QAAM,EAAE,MAAM,KAAK,IAAI;AAEvB,QAAM,cAAc,MAAM,QAAQ,QAAQ,CAAC,WAAW;AACpD,UAAM,gBAAgB,eAAe,MAAM;AAC3C,WAAO,WAAW,QACd,CAAC,gBAAgB,UAAU,QAAQ,OAAO,CAAC,IAC3C,CAAC,aAAa;AAAA,EACpB,CAAC;AAED,QAAM,kBAAkB,MAAM,WAC3B,IAAI,CAAC,EAAE,SAAS,YAAAA,YAAW,MAAM;AAChC,UAAM,QAAuB,CAAC;AAE9B,QAAI,SAAS,MAAM;AACjB,YAAM;AAAA,QACJ,GAAG,UAAU,OAAO,OAAO,CAAC,IAAI,UAAU,QAAQ,QAAQ,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,UAAM;AAAA,MACJ,UAAU,OAAOA,YAAW,IAAI,QAAiB,EAAE,KAAK,IAAI,CAAC;AAAA,IAC/D;AAEA,WAAO,MAAM,KAAK,GAAG;AAAA,EACvB,CAAC,EACA,KAAK;AAAA,EAAK,MAAM,EAAE,EAAE,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE;AAE3C,QAAM,eAAe,MAAM,QAAQ,WAAW,IAAI,CAAC,OAAO;AACxD,WAAO,UAAU,UAAU,SAAS,EAAc,CAAC;AAAA,EACrD,CAAC;AAED,QAAM,WAAW,GAAG,UAAU,UAAU,UAAU,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,UAAU,QAAQ,KAAK,IAAI,IAAI,CAAC;AAC5G,QAAM,UAAU,GAAG,UAAU,OAAO,aAAa,CAAC,IAAI,YAAY,KAAK,GAAG,CAAC;AAC3E,QAAM,aAAa,GAAG,UAAU,OAAO,aAAa,CAAC,IAAI,eAAe;AACxE,QAAM,UAAU,GAAG,UAAU,OAAO,aAAa,CAAC,IAAI,aAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;AAEpG,QAAM,aAAa,QAAQ,OAAO,QAC9B,OAAO,QAAQ,OAAO,WAAW,EAAE,IACnC;AAEJ,QAAM,eAAe;AAAA,IACnB,CAAC,YAAY,QAAQ;AAAA,IACrB,CAAC,WAAW,OAAO;AAAA,IACnB,CAAC,cAAc,UAAU;AAAA,IACzB,CAAC,WAAW,OAAO;AAAA,EACrB;AAEA,QAAM,aAAa,CAAC,SAAiB;AACnC,UAAM,cAAc,aAAa,YAAY,IAAI;AACjD,WAAO,cAAc,IACjB;AAAA,MACE;AAAA,MACA;AAAA,QACE;AAAA,QACA,UAAU,QAAQ,MAAM,WAAW,EAAE,KAAK,MAAG,EAAE,KAAK,EAAE,CAAC;AAAA,MACzD;AAAA,IACF,EAAE,KAAK,EAAE,IACT;AAAA,EACN;AAEA,QAAM,QAAQ,aAAa;AAAA,IACzB,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM;AACpB,UAAI,GAAG,IAAI,KAAK,MAAM,IAAI,EAAE,IAAI,UAAU,EAAE,KAAK,IAAI;AACrD,aAAO;AAAA,IACT;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,OAAO,OAAO,KAAK,EAAE,KAAK,IAAI;AAAA,EACtC;AACF;AAEA,IAAM,WAAW,CAAC,OAAiB;AACjC,SAAO,GAAG,QAAQ,GAAG,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE;AAC5D;;;ACrGO,IAAM,eAAe,CAC1B,cACA;AAAA,EACE;AAAA,EACA;AACF,MAM8B;AAE9B,QAAM,mBAA0C;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,QAAmC,CAAC;AAG1C,aAAW,eAAe,cAAc;AACtC,UAAM,EAAE,MAAM,MAAM,KAAK,IAAI;AAG7B,UAAM,kBAAkB;AAAA,MACtB,GAAG,YAAY;AAAA,MACf,GAAG,YAAY;AAAA,IACjB,EAAE,KAAK;AAEP,UAAM,kBACJ,gBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY;AAGvD,UAAM,kBAA4D;AAAA,MAChE,GAAG,sBAAsB,WAAW;AAAA,MACpC,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAGA,UAAM,aAEF;AAAA,MACF,GAAG,iBAAiB,QAAQ,CAAC,SAAS;AACpC,cAAM,aAAa,gBAAgB;AAAA;AAAA;AAAA,UAGjC,CAAC,MAAM,EAAE,SAAS,SAAS;AAAA,QAC7B;AACA,eAAO,aACH,CAAC,UAAU,IACX,CAAC;AAAA,MACP,CAAC;AAAA,MAED,GAAG,iBAAiB,QAAQ,CAAC,UAAU;AACrC,YAAI,CAAC,MAAM,SAAS,MAAM;AAExB,iBAAO,CAAC,KAAK;AAAA,QACf;AACA,YAAI,iBAAiB,SAAS,MAAM,SAAS,IAAI,GAAG;AAElD,iBAAO,CAAC;AAAA,QACV;AACA,cAAM,WAAW,gBAAgB;AAAA;AAAA;AAAA,UAG/B,CAAC,MAAM,EAAE,SAAS,SAAS,MAAM,SAAS;AAAA,QAC5C;AACA,eAAO,CAAC,YAAY,KAAK;AAAA,MAC3B,CAAC;AAAA,MAED,GAAG,gBAAgB,QAAQ,CAAC,UAAU;AACpC,cAAM,OACJ,MAAM,SAAS,eACX,MAAM,SAAS,OACf;AAEN,YAAI,MAAM;AACR,cAAI,iBAAiB,SAAS,IAAI,GAAG;AAEnC,mBAAO,CAAC;AAAA,UACV;AACA,cAAI,iBAAiB,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,IAAI,GAAG;AAE1D,mBAAO,CAAC;AAAA,UACV;AAAA,QACF;AAEA,eAAO,CAAC,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AAEA,eAAW,SAAS,YAAY;AAC9B,UAAI,MAAM,SAAS,WAAW;AAC5B,cAAM,aAAa,WAAW,QAAQ,CAAC,MAAM;AAC3C,cAAI,EAAE,SAAS,cAAc;AAC3B,mBAAO,CAAC,EAAE,SAAS,MAAM,EAAE,QAAQ,GAAG,SAAS,MAAM,MAAM,IACvD,CAAC,CAAC,IACF,CAAC;AAAA,UACP;AACA,iBAAO,CAAC;AAAA,QACV,CAAC;AACD,cAAM,KAAK;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,CAAC,MAAM,MAAM;AAAA,UACtB,YAAY;AAAA,YACV,GAAG,WAAW,QAAQ,CAAC,MAAM,EAAE,UAAU;AAAA,YACzC,GAAG,MAAM;AAAA,UACX;AAAA,UACA,OAAO,gBAA6B;AAAA,YAClC;AAAA,YACA;AAAA,YACA;AAAA,YACA,SAAS,CAAC,MAAM,MAAM;AAAA,YACtB;AAAA,YACA,SAAS;AAAA,UACX,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AC7IO,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,aAAA,UAAO;AACP,EAAAA,aAAA,aAAU;AACV,EAAAA,aAAA,SAAM;AACN,EAAAA,aAAA,SAAM;AACN,EAAAA,aAAA,WAAQ;AACR,EAAAA,aAAA,UAAO;AACP,EAAAA,aAAA,YAAS;AAPC,SAAAA;AAAA,GAAA;AAwIL,IAAM,yBAAyB;AAAA,EACpC,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AACX;AAmBO,IAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AACP;AAEO,IAAM,2BAA2B;AAAA,EACtC,GAAG;AAAA,EACH,GAAG;AACL;AAuHO,IAAM,WAA0B,uBAAO,aAAa;",
|
|
6
|
+
"names": ["middleware", "HTTPMethods"]
|
|
7
7
|
}
|
package/pkg/routes.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CreateRouteMiddleware, MiddlewareDefinition, Route, RouteSource } from "./types";
|
|
2
|
+
export declare const createRoutes: <MiddlewareT, MiddlewareR>(routeSources: Array<RouteSource<MiddlewareT>>, { globalMiddleware, createRouteMiddleware, }: {
|
|
3
|
+
globalMiddleware: Array<MiddlewareDefinition<MiddlewareT>>;
|
|
4
|
+
createRouteMiddleware: CreateRouteMiddleware<MiddlewareT>;
|
|
5
|
+
}) => Array<Route<MiddlewareR>>;
|
package/pkg/types.d.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { ValidationErrorEntry } from "./errors/types";
|
|
2
|
+
export declare enum HTTPMethods {
|
|
3
|
+
HEAD = "HEAD",
|
|
4
|
+
OPTIONS = "OPTIONS",
|
|
5
|
+
GET = "GET",
|
|
6
|
+
PUT = "PUT",
|
|
7
|
+
PATCH = "PATCH",
|
|
8
|
+
POST = "POST",
|
|
9
|
+
DELETE = "DELETE"
|
|
10
|
+
}
|
|
11
|
+
export type HTTPMethod = keyof typeof HTTPMethods;
|
|
12
|
+
export type MiddlewareDefinition<MiddlewareT> = {
|
|
13
|
+
kind: "middleware";
|
|
14
|
+
middleware: Array<MiddlewareT>;
|
|
15
|
+
options?: UseOptions | undefined;
|
|
16
|
+
};
|
|
17
|
+
export type HandlerDefinition<MiddlewareT> = {
|
|
18
|
+
kind: "handler";
|
|
19
|
+
middleware: Array<MiddlewareT>;
|
|
20
|
+
method: HTTPMethod;
|
|
21
|
+
};
|
|
22
|
+
export type RouteDefinitionItem<MiddlewareT> = MiddlewareDefinition<MiddlewareT> | HandlerDefinition<MiddlewareT>;
|
|
23
|
+
export interface UseSlots {
|
|
24
|
+
errorHandler: string;
|
|
25
|
+
extendContext: string;
|
|
26
|
+
bodyparser: string;
|
|
27
|
+
"validate:params": string;
|
|
28
|
+
"validate:query": string;
|
|
29
|
+
"validate:headers": string;
|
|
30
|
+
"validate:cookies": string;
|
|
31
|
+
"validate:json": string;
|
|
32
|
+
"validate:form": string;
|
|
33
|
+
"validate:raw": string;
|
|
34
|
+
"validate:response": string;
|
|
35
|
+
}
|
|
36
|
+
export type UseOptions = {
|
|
37
|
+
on?: Array<HTTPMethod>;
|
|
38
|
+
slot?: keyof UseSlots;
|
|
39
|
+
debug?: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
export type RouteSource<MiddlewareT> = {
|
|
42
|
+
name: string;
|
|
43
|
+
path: string;
|
|
44
|
+
pathPattern: string;
|
|
45
|
+
file: string;
|
|
46
|
+
cascadingMiddleware: [...a: Array<MiddlewareDefinition<MiddlewareT>>];
|
|
47
|
+
definitionItems: Array<RouteDefinitionItem<MiddlewareT>>;
|
|
48
|
+
params: Array<string>;
|
|
49
|
+
numericParams: Array<string>;
|
|
50
|
+
validationSchemas: ValidationSchemas;
|
|
51
|
+
meta?: Record<string, unknown>;
|
|
52
|
+
};
|
|
53
|
+
export type Route<MiddlewareT> = {
|
|
54
|
+
name: string;
|
|
55
|
+
path: string;
|
|
56
|
+
file: string;
|
|
57
|
+
methods: Array<string>;
|
|
58
|
+
middleware: Array<MiddlewareT>;
|
|
59
|
+
debug: {
|
|
60
|
+
headline: string;
|
|
61
|
+
methods: string;
|
|
62
|
+
middleware: string;
|
|
63
|
+
handler: string;
|
|
64
|
+
full: string;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
export type DevSetup = {
|
|
68
|
+
/**
|
|
69
|
+
* API request handler for development mode.
|
|
70
|
+
*
|
|
71
|
+
* In dev mode, incoming requests are routed based on URL:
|
|
72
|
+
* - Requests matching apiurl routed to this handler (your API)
|
|
73
|
+
* - All other requests routed to Vite dev server (pages/assets)
|
|
74
|
+
*
|
|
75
|
+
* Returns a function that processes API requests.
|
|
76
|
+
* */
|
|
77
|
+
requestHandler: () => (req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse) => Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Custom function to determine if a request should be handled by the API.
|
|
80
|
+
*
|
|
81
|
+
* By default, requests are routed to the API handler if their URL starts with `apiurl`.
|
|
82
|
+
* Use this to implement custom heuristics for detecting API requests.
|
|
83
|
+
* */
|
|
84
|
+
requestMatcher?: (req: import("node:http").IncomingMessage) => boolean;
|
|
85
|
+
/**
|
|
86
|
+
* In dev mode, perform cleanup operations before reloading the API handler.
|
|
87
|
+
* */
|
|
88
|
+
teardownHandler?: () => void | Promise<void>;
|
|
89
|
+
};
|
|
90
|
+
export type AppFactory<App, AppOptions = unknown> = (factory: (a: {
|
|
91
|
+
createApp: (o?: AppOptions) => App;
|
|
92
|
+
}) => App) => App;
|
|
93
|
+
export type RouterFactory<Router, RouterOptions = unknown> = (factory: (a: {
|
|
94
|
+
createRouter: (o?: RouterOptions) => Router;
|
|
95
|
+
}) => Router) => Router;
|
|
96
|
+
export type CreateRouteMiddleware<MiddlewareT> = (routeSource: RouteSource<MiddlewareT>) => Array<MiddlewareDefinition<MiddlewareT>>;
|
|
97
|
+
export type CreateServer<App, Server> = (app: App, opt?: {
|
|
98
|
+
port?: number;
|
|
99
|
+
sock?: string;
|
|
100
|
+
callback?: () => void | Promise<void>;
|
|
101
|
+
}) => Promise<Server>;
|
|
102
|
+
export type ServerFactory<App, Server> = (factory: (a: {
|
|
103
|
+
createServer: CreateServer<App, Server>;
|
|
104
|
+
}) => void) => void;
|
|
105
|
+
/**
|
|
106
|
+
* Request metadata validation targets.
|
|
107
|
+
* */
|
|
108
|
+
export declare const RequestMetadataTargets: {
|
|
109
|
+
readonly query: "URL query parameters";
|
|
110
|
+
readonly headers: "HTTP request headers";
|
|
111
|
+
readonly cookies: "HTTP cookies";
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Request body validation targets.
|
|
115
|
+
*
|
|
116
|
+
* Body formats are mutually exclusive - only one should be specified per handler.
|
|
117
|
+
*
|
|
118
|
+
* **Development behavior:**
|
|
119
|
+
* - If multiple formats are defined, the builder displays a warning and
|
|
120
|
+
* disables validation schemas for the affected handler.
|
|
121
|
+
* - If an unsuitable target is defined (e.g., `json`, `form`)
|
|
122
|
+
* for a method without a body like GET, HEAD), a warning is displayed and
|
|
123
|
+
* validation schemas are disabled for that handler.
|
|
124
|
+
*
|
|
125
|
+
* This ensures misconfigurations are detected during development
|
|
126
|
+
* for runtime to execute without false positive validation failures.
|
|
127
|
+
*
|
|
128
|
+
* Always define exactly one target that is suitable for current handler.
|
|
129
|
+
* */
|
|
130
|
+
export declare const RequestBodyTargets: {
|
|
131
|
+
readonly json: "JSON request body";
|
|
132
|
+
readonly form: "URL-encoded or Multipart form";
|
|
133
|
+
readonly raw: "Raw body format (string/Buffer/ArrayBuffer/Blob)";
|
|
134
|
+
};
|
|
135
|
+
export declare const RequestValidationTargets: {
|
|
136
|
+
readonly json: "JSON request body";
|
|
137
|
+
readonly form: "URL-encoded or Multipart form";
|
|
138
|
+
readonly raw: "Raw body format (string/Buffer/ArrayBuffer/Blob)";
|
|
139
|
+
readonly query: "URL query parameters";
|
|
140
|
+
readonly headers: "HTTP request headers";
|
|
141
|
+
readonly cookies: "HTTP cookies";
|
|
142
|
+
};
|
|
143
|
+
export type RequestMetadataTarget = keyof typeof RequestMetadataTargets;
|
|
144
|
+
export type RequestBodyTarget = keyof typeof RequestBodyTargets;
|
|
145
|
+
export type RequestValidationTarget = keyof typeof RequestValidationTargets;
|
|
146
|
+
export type ValidationTarget = RequestValidationTarget | "params" | "response";
|
|
147
|
+
export type ValidationDefmap = Partial<{
|
|
148
|
+
/**
|
|
149
|
+
* Request metadata targets.
|
|
150
|
+
* */
|
|
151
|
+
query: Record<string, unknown>;
|
|
152
|
+
headers: Record<string, string>;
|
|
153
|
+
cookies: Record<string, unknown>;
|
|
154
|
+
/**
|
|
155
|
+
* Request body targets. One target per handler.
|
|
156
|
+
*
|
|
157
|
+
* POST<
|
|
158
|
+
* json: { id: number }
|
|
159
|
+
* // or form/raw
|
|
160
|
+
* >((ctx) => {})
|
|
161
|
+
* */
|
|
162
|
+
json: unknown;
|
|
163
|
+
form: Record<string, unknown>;
|
|
164
|
+
raw: string | Buffer | ArrayBuffer | Blob;
|
|
165
|
+
/**
|
|
166
|
+
* Response variants.
|
|
167
|
+
* Multiple variants can be specified via unions.
|
|
168
|
+
*
|
|
169
|
+
* POST<
|
|
170
|
+
* response:
|
|
171
|
+
* | [200, "json", User]
|
|
172
|
+
* | [201, "json"]
|
|
173
|
+
* | [301]
|
|
174
|
+
* >((ctx) => {})
|
|
175
|
+
* */
|
|
176
|
+
response: [
|
|
177
|
+
/**
|
|
178
|
+
* HTTP status code to send with the response.
|
|
179
|
+
* Common values: 200 (OK), 400 (Bad Request), 404 (Not Found), 500 (Internal Server Error)
|
|
180
|
+
* */
|
|
181
|
+
status: number,
|
|
182
|
+
/**
|
|
183
|
+
* Content-Type header for the response. Supports shorthand notation that gets
|
|
184
|
+
* resolved via mime-types lookup (e.g., "json" becomes "application/json",
|
|
185
|
+
* "html" becomes "text/html", "png" becomes "image/png")
|
|
186
|
+
* */
|
|
187
|
+
contentType?: string | undefined,
|
|
188
|
+
/** The response body schema */
|
|
189
|
+
body?: unknown
|
|
190
|
+
];
|
|
191
|
+
}>;
|
|
192
|
+
export type ValidationCustomErrors = {
|
|
193
|
+
/**
|
|
194
|
+
* Custom error messages for validation failures.
|
|
195
|
+
*
|
|
196
|
+
* Use `error` to set a general error message for the entire validation target.
|
|
197
|
+
* Use `error.<fieldName>` to set specific error messages for individual fields.
|
|
198
|
+
*
|
|
199
|
+
* @example Override validation error messages
|
|
200
|
+
* POST<{
|
|
201
|
+
* json: {
|
|
202
|
+
* id: number;
|
|
203
|
+
* email: string;
|
|
204
|
+
* age: number;
|
|
205
|
+
* }
|
|
206
|
+
* }, {
|
|
207
|
+
* json: {
|
|
208
|
+
* error: "Invalid user data provided",
|
|
209
|
+
* "error.id": "User ID must be a valid number",
|
|
210
|
+
* "error.email": "Please provide a valid email address",
|
|
211
|
+
* "error.age": "Age must be a number"
|
|
212
|
+
* }
|
|
213
|
+
* }>
|
|
214
|
+
* */
|
|
215
|
+
error?: string;
|
|
216
|
+
} & {
|
|
217
|
+
[E in `error.${string}`]?: string;
|
|
218
|
+
};
|
|
219
|
+
export type ValidationOptions = {
|
|
220
|
+
/**
|
|
221
|
+
* Controls runtime validation for this target.
|
|
222
|
+
*
|
|
223
|
+
* By default, all validation targets are validated at runtime. Set this to
|
|
224
|
+
* `false` if you only need compile-time type checking without runtime validation.
|
|
225
|
+
*
|
|
226
|
+
* @example Disable runtime validation for JSON payload
|
|
227
|
+
* POST<{
|
|
228
|
+
* json: Payload<User>
|
|
229
|
+
* }, {
|
|
230
|
+
* json: {
|
|
231
|
+
* runtimeValidation: false
|
|
232
|
+
* }
|
|
233
|
+
* }>
|
|
234
|
+
* */
|
|
235
|
+
runtimeValidation?: boolean | undefined;
|
|
236
|
+
/**
|
|
237
|
+
* Specifies the request Content-Type for OpenAPI schema generation.
|
|
238
|
+
*
|
|
239
|
+
* When the validation target is `form`, the OpenAPI generator will include
|
|
240
|
+
* both `application/x-www-form-urlencoded` and `multipart/form-data` in the
|
|
241
|
+
* request body content types by default. This indicates the handler accepts
|
|
242
|
+
* either format, which may not be accurate for your use case.
|
|
243
|
+
*
|
|
244
|
+
* Use this option to explicitly declare which content type your handler expects.
|
|
245
|
+
* */
|
|
246
|
+
contentType?: string;
|
|
247
|
+
} & ValidationCustomErrors;
|
|
248
|
+
export type ValidationOptmap = {
|
|
249
|
+
[K in ValidationTarget]?: ValidationOptions;
|
|
250
|
+
};
|
|
251
|
+
export declare const StateKey: unique symbol;
|
|
252
|
+
export type ExtendContext<ParamsT, VDefs extends ValidationDefmap, VOpts extends ValidationOptmap, BodyparserOptions extends Record<RequestBodyTarget, unknown>> = {
|
|
253
|
+
[StateKey]: Map<ValidationTarget, unknown>;
|
|
254
|
+
bodyparser: {
|
|
255
|
+
[T in RequestBodyTarget]: <R = unknown>(opts?: BodyparserOptions[T]) => Promise<R>;
|
|
256
|
+
};
|
|
257
|
+
validated: {
|
|
258
|
+
[K in keyof VDefs as K extends RequestValidationTarget ? VOpts[K] extends {
|
|
259
|
+
runtimeValidation: false;
|
|
260
|
+
} ? never : K : never]: VDefs[K];
|
|
261
|
+
} & {
|
|
262
|
+
params: ParamsT;
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
export type ValidationSchema = {
|
|
266
|
+
check: (data: unknown) => boolean;
|
|
267
|
+
errors: (data: unknown) => Array<ValidationErrorEntry>;
|
|
268
|
+
errorMessage: (data: unknown) => string;
|
|
269
|
+
errorSummary: (data: unknown) => string;
|
|
270
|
+
validate: (data: unknown) => void;
|
|
271
|
+
};
|
|
272
|
+
export type ValidationSchemas<Extend = object> = {
|
|
273
|
+
[T in RequestValidationTarget]?: Record<string, ValidationSchema & Extend & {
|
|
274
|
+
runtimeValidation?: boolean;
|
|
275
|
+
customErrors?: ValidationCustomErrors;
|
|
276
|
+
}>;
|
|
277
|
+
} & {
|
|
278
|
+
params?: ValidationSchema & Extend;
|
|
279
|
+
response?: Record<string, Array<ValidationSchema & Extend & {
|
|
280
|
+
status: number;
|
|
281
|
+
contentType?: string;
|
|
282
|
+
runtimeValidation?: boolean;
|
|
283
|
+
customErrors?: ValidationCustomErrors;
|
|
284
|
+
}>>;
|
|
285
|
+
};
|
package/pkg/bodyparser/index.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
// src/bodyparser/index.ts
|
|
2
|
-
import zlib from "node:zlib";
|
|
3
|
-
import IncomingForm from "formidable";
|
|
4
|
-
import rawParser from "raw-body";
|
|
5
|
-
|
|
6
|
-
// src/bodyparser/config.ts
|
|
7
|
-
var json = {
|
|
8
|
-
limit: 1024 ** 2,
|
|
9
|
-
trim: ["*"]
|
|
10
|
-
};
|
|
11
|
-
var form = {
|
|
12
|
-
limit: 1024 ** 2
|
|
13
|
-
};
|
|
14
|
-
var raw = {
|
|
15
|
-
limit: 1024 ** 2
|
|
16
|
-
};
|
|
17
|
-
var config_default = {
|
|
18
|
-
json,
|
|
19
|
-
form,
|
|
20
|
-
raw
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// src/bodyparser/index.ts
|
|
24
|
-
var bodyparser_default = { json: json2, form: form2, raw: raw2 };
|
|
25
|
-
function json2(opts = {}) {
|
|
26
|
-
return [
|
|
27
|
-
async function useJSONBodyparser(ctx, next) {
|
|
28
|
-
const form3 = IncomingForm({
|
|
29
|
-
maxFieldsSize: opts.limit || config_default.json.limit,
|
|
30
|
-
...opts
|
|
31
|
-
});
|
|
32
|
-
const trimmer = trimmerFactory(opts.trim);
|
|
33
|
-
ctx.request.body = await new Promise((resolve, reject) => {
|
|
34
|
-
form3.parse(ctx.request.req, (err, fields) => {
|
|
35
|
-
if (err) {
|
|
36
|
-
return reject(err);
|
|
37
|
-
}
|
|
38
|
-
resolve(trimmer ? trimmer(fields) : fields);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
return next();
|
|
42
|
-
}
|
|
43
|
-
];
|
|
44
|
-
}
|
|
45
|
-
function form2(opts = {}) {
|
|
46
|
-
return [
|
|
47
|
-
async function useFormBodyparser(ctx, next) {
|
|
48
|
-
const form3 = IncomingForm({
|
|
49
|
-
maxFieldsSize: opts.limit || config_default.form.limit,
|
|
50
|
-
maxFileSize: opts.limit || config_default.form.limit,
|
|
51
|
-
...opts
|
|
52
|
-
});
|
|
53
|
-
let trimmer = trimmerFactory(opts.trim);
|
|
54
|
-
if (opts.multipart || opts.urlencoded) {
|
|
55
|
-
trimmer = void 0;
|
|
56
|
-
}
|
|
57
|
-
ctx.request.body = await new Promise((resolve, reject) => {
|
|
58
|
-
form3.parse(ctx.request.req, (err, fields, files) => {
|
|
59
|
-
if (err) {
|
|
60
|
-
return reject(err);
|
|
61
|
-
}
|
|
62
|
-
resolve({
|
|
63
|
-
fields: trimmer ? trimmer(fields) : fields,
|
|
64
|
-
files
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
return next();
|
|
69
|
-
}
|
|
70
|
-
];
|
|
71
|
-
}
|
|
72
|
-
function raw2(opts = {}) {
|
|
73
|
-
return [
|
|
74
|
-
async function useRawBodyparser(ctx, next) {
|
|
75
|
-
const { chunkSize, ...rawParserOptions } = { ...config_default.raw, ...opts };
|
|
76
|
-
const stream = ctx.request.req.pipe(zlib.createUnzip({ chunkSize }));
|
|
77
|
-
ctx.request.body = await rawParser(stream, rawParserOptions);
|
|
78
|
-
return next();
|
|
79
|
-
}
|
|
80
|
-
];
|
|
81
|
-
}
|
|
82
|
-
function trimmerFactory(trimOption) {
|
|
83
|
-
if (!Array.isArray(trimOption) || !trimOption.length) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const trimableKeys = trimOption.reduce((m, k) => {
|
|
87
|
-
m[k] = true;
|
|
88
|
-
return m;
|
|
89
|
-
}, {});
|
|
90
|
-
const trim = (key, val) => {
|
|
91
|
-
return typeof val === "string" ? trimableKeys[key] || trimableKeys["*"] ? val.trim() : val : val;
|
|
92
|
-
};
|
|
93
|
-
const reducer = (memo, [key, val]) => {
|
|
94
|
-
memo[key] = trim(key, val);
|
|
95
|
-
return memo;
|
|
96
|
-
};
|
|
97
|
-
return (payload) => Object.entries(payload).reduce(
|
|
98
|
-
reducer,
|
|
99
|
-
// accumulator is set to payload intentionally, to avoid duplication of big strings.
|
|
100
|
-
// if using a new object for accumulator then trimmed strings will be duplicated?
|
|
101
|
-
payload
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
export {
|
|
105
|
-
config_default as config,
|
|
106
|
-
bodyparser_default as default,
|
|
107
|
-
form2 as form,
|
|
108
|
-
json2 as json,
|
|
109
|
-
raw2 as raw
|
|
110
|
-
};
|
|
111
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/bodyparser/index.ts", "../../src/bodyparser/config.ts"],
|
|
4
|
-
"sourcesContent": ["import zlib from \"node:zlib\";\n\nimport IncomingForm from \"formidable\";\nimport rawParser from \"raw-body\";\n\nimport type { ParameterizedMiddleware } from \"@kosmojs/api\";\n\nimport config from \"./config\";\nimport type {\n FormOptions,\n JsonOptions,\n RawOptions,\n Trimmer,\n TrimOption,\n} from \"./types\";\n\nexport * from \"./types\";\nexport { config };\nexport default { json, form, raw };\n\nexport function json(opts: JsonOptions = {}): Array<ParameterizedMiddleware> {\n return [\n async function useJSONBodyparser(ctx, next) {\n const form = IncomingForm({\n maxFieldsSize: opts.limit || config.json.limit,\n ...opts,\n });\n\n const trimmer = trimmerFactory(opts.trim);\n\n ctx.request.body = await new Promise((resolve, reject) => {\n form.parse(ctx.request.req, (err, fields) => {\n if (err) {\n return reject(err);\n }\n\n resolve(trimmer ? trimmer(fields) : fields);\n });\n });\n\n return next();\n },\n ];\n}\n\nexport function form(opts: FormOptions = {}): Array<ParameterizedMiddleware> {\n return [\n async function useFormBodyparser(ctx, next) {\n const form = IncomingForm({\n maxFieldsSize: opts.limit || config.form.limit,\n maxFileSize: opts.limit || config.form.limit,\n ...opts,\n });\n\n let trimmer = trimmerFactory(opts.trim);\n\n if (opts.multipart || opts.urlencoded) {\n trimmer = undefined;\n }\n\n ctx.request.body = await new Promise((resolve, reject) => {\n form.parse(ctx.request.req, (err, fields, files) => {\n if (err) {\n return reject(err);\n }\n\n resolve({\n fields: trimmer ? trimmer(fields) : fields,\n files,\n });\n });\n });\n\n return next();\n },\n ];\n}\n\nexport function raw(opts: RawOptions = {}): Array<ParameterizedMiddleware> {\n return [\n async function useRawBodyparser(ctx, next) {\n const { chunkSize, ...rawParserOptions } = { ...config.raw, ...opts };\n\n const stream = ctx.request.req.pipe(zlib.createUnzip({ chunkSize }));\n ctx.request.body = await rawParser(stream, rawParserOptions);\n\n return next();\n },\n ];\n}\n\nfunction trimmerFactory(\n trimOption: TrimOption | undefined,\n): Trimmer | undefined {\n if (!Array.isArray(trimOption) || !trimOption.length) {\n return;\n }\n\n const trimableKeys: {\n [key: string]: boolean;\n } = trimOption.reduce((m: Record<string, boolean>, k) => {\n m[k] = true;\n return m;\n }, {});\n\n const trim = (key: string, val: unknown) => {\n return typeof val === \"string\"\n ? trimableKeys[key] || trimableKeys[\"*\"]\n ? val.trim()\n : val\n : val;\n };\n\n const reducer = (\n memo: Record<string, unknown>,\n [key, val]: [string, unknown],\n ) => {\n memo[key] = trim(key, val);\n return memo;\n };\n\n return (payload) =>\n Object.entries(payload).reduce(\n reducer,\n // accumulator is set to payload intentionally, to avoid duplication of big strings.\n // if using a new object for accumulator then trimmed strings will be duplicated?\n payload,\n );\n}\n", "import type { FormOptions, JsonOptions, RawOptions } from \"./types\";\n\nexport const json: JsonOptions = {\n limit: 1024 ** 2,\n trim: [\"*\"],\n};\n\nexport const form: FormOptions = {\n limit: 1024 ** 2,\n};\n\nexport const raw: RawOptions = {\n limit: 1024 ** 2,\n};\n\nexport default {\n json,\n form,\n raw,\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["json", "form", "raw"]
|
|
7
|
-
}
|
package/pkg/queryparser/index.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// src/queryparser/index.ts
|
|
2
|
-
import { parse, stringify } from "qs";
|
|
3
|
-
var queryparser_default = (app, _parseOptions = {}, _stringifyOptions = {}) => {
|
|
4
|
-
const parseOptions = {
|
|
5
|
-
ignoreQueryPrefix: true,
|
|
6
|
-
parseArrays: true,
|
|
7
|
-
arrayLimit: 100,
|
|
8
|
-
parameterLimit: 100,
|
|
9
|
-
depth: 5,
|
|
10
|
-
..._parseOptions
|
|
11
|
-
};
|
|
12
|
-
const stringifyOptions = {
|
|
13
|
-
encodeValuesOnly: true,
|
|
14
|
-
arrayFormat: "brackets",
|
|
15
|
-
..._stringifyOptions
|
|
16
|
-
};
|
|
17
|
-
const obj = {
|
|
18
|
-
get query() {
|
|
19
|
-
return parse(this.querystring || "", parseOptions);
|
|
20
|
-
},
|
|
21
|
-
set query(obj2) {
|
|
22
|
-
this.querystring = stringify(obj2, stringifyOptions);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
const entries = Object.getOwnPropertyNames(obj).map((name) => [
|
|
26
|
-
name,
|
|
27
|
-
Object.getOwnPropertyDescriptor(obj, name)
|
|
28
|
-
]);
|
|
29
|
-
for (const [name, desc] of entries) {
|
|
30
|
-
Object.defineProperty(app.request, name, desc);
|
|
31
|
-
}
|
|
32
|
-
return app;
|
|
33
|
-
};
|
|
34
|
-
export {
|
|
35
|
-
queryparser_default as default
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/queryparser/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type Koa from \"koa\";\nimport type { IParseOptions, IStringifyOptions } from \"qs\";\nimport { parse, stringify } from \"qs\";\n\nimport type { DefaultContext, DefaultState } from \"@/types\";\n\nexport default <\n T extends InstanceType<typeof Koa<DefaultState, DefaultContext>> = never,\n>(\n app: T,\n _parseOptions: IParseOptions = {},\n _stringifyOptions: IStringifyOptions = {},\n) => {\n const parseOptions = {\n ignoreQueryPrefix: true,\n parseArrays: true,\n arrayLimit: 100,\n parameterLimit: 100,\n depth: 5,\n ..._parseOptions,\n };\n\n const stringifyOptions = {\n encodeValuesOnly: true,\n arrayFormat: \"brackets\",\n ..._stringifyOptions,\n } as const;\n\n const obj = {\n get query() {\n return parse((this as Koa.Request).querystring || \"\", parseOptions);\n },\n\n set query(obj: object) {\n (this as Koa.Request).querystring = stringify(obj, stringifyOptions);\n },\n };\n\n const entries = Object.getOwnPropertyNames(obj).map((name) => [\n name,\n Object.getOwnPropertyDescriptor(obj, name),\n ]) as [name: string, desc: PropertyDescriptor][];\n\n for (const [name, desc] of entries) {\n Object.defineProperty(app.request, name, desc);\n }\n\n return app;\n};\n"],
|
|
5
|
-
"mappings": ";AAEA,SAAS,OAAO,iBAAiB;AAIjC,IAAO,sBAAQ,CAGb,KACA,gBAA+B,CAAC,GAChC,oBAAuC,CAAC,MACrC;AACH,QAAM,eAAe;AAAA,IACnB,mBAAmB;AAAA,IACnB,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,GAAG;AAAA,EACL;AAEA,QAAM,mBAAmB;AAAA,IACvB,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,GAAG;AAAA,EACL;AAEA,QAAM,MAAM;AAAA,IACV,IAAI,QAAQ;AACV,aAAO,MAAO,KAAqB,eAAe,IAAI,YAAY;AAAA,IACpE;AAAA,IAEA,IAAI,MAAMA,MAAa;AACrB,MAAC,KAAqB,cAAc,UAAUA,MAAK,gBAAgB;AAAA,IACrE;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,oBAAoB,GAAG,EAAE,IAAI,CAAC,SAAS;AAAA,IAC5D;AAAA,IACA,OAAO,yBAAyB,KAAK,IAAI;AAAA,EAC3C,CAAC;AAED,aAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,WAAO,eAAe,IAAI,SAAS,MAAM,IAAI;AAAA,EAC/C;AAEA,SAAO;AACT;",
|
|
6
|
-
"names": ["obj"]
|
|
7
|
-
}
|
package/pkg/src/app.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { FormOptions, JsonOptions, RawOptions } from "./types";
|
|
2
|
-
export declare const json: JsonOptions;
|
|
3
|
-
export declare const form: FormOptions;
|
|
4
|
-
export declare const raw: RawOptions;
|
|
5
|
-
declare const _default: {
|
|
6
|
-
json: JsonOptions;
|
|
7
|
-
form: FormOptions;
|
|
8
|
-
raw: RawOptions;
|
|
9
|
-
};
|
|
10
|
-
export default _default;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ParameterizedMiddleware } from "@kosmojs/api";
|
|
2
|
-
import config from "./config";
|
|
3
|
-
import type { FormOptions, JsonOptions, RawOptions } from "./types";
|
|
4
|
-
export * from "./types";
|
|
5
|
-
export { config };
|
|
6
|
-
declare const _default: {
|
|
7
|
-
json: typeof json;
|
|
8
|
-
form: typeof form;
|
|
9
|
-
raw: typeof raw;
|
|
10
|
-
};
|
|
11
|
-
export default _default;
|
|
12
|
-
export declare function json(opts?: JsonOptions): Array<ParameterizedMiddleware>;
|
|
13
|
-
export declare function form(opts?: FormOptions): Array<ParameterizedMiddleware>;
|
|
14
|
-
export declare function raw(opts?: RawOptions): Array<ParameterizedMiddleware>;
|