@forinda/kickjs 5.7.0 → 5.8.0
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/dist/{adapter-CoJp35rz.d.mts → adapter-RzfEzd-c.d.mts} +46 -11
- package/dist/adapter-RzfEzd-c.d.mts.map +1 -0
- package/dist/app-module-BE7hAASB.d.mts.map +1 -1
- package/dist/{application-DmekGKp7.d.mts → application-CwLcDTyg.d.mts} +3 -3
- package/dist/application-CwLcDTyg.d.mts.map +1 -0
- package/dist/application-h14rczhJ.mjs +12 -0
- package/dist/application-h14rczhJ.mjs.map +1 -0
- package/dist/{bootstrap-CwP2GGu0.d.mts → bootstrap-B8y526yc.d.mts} +2 -2
- package/dist/bootstrap-B8y526yc.d.mts.map +1 -0
- package/dist/{bootstrap-D86s_Aac.mjs → bootstrap-V0q7pgA4.mjs} +3 -3
- package/dist/{bootstrap-D86s_Aac.mjs.map → bootstrap-V0q7pgA4.mjs.map} +1 -1
- package/dist/cache-XrNWj1KQ.d.mts.map +1 -1
- package/dist/config/index.d.mts +1 -1
- package/dist/config/index.mjs +2 -2
- package/dist/{config-BVvCHSX2.mjs → config-C_oycBvB.mjs} +3 -3
- package/dist/{config-BVvCHSX2.mjs.map → config-C_oycBvB.mjs.map} +1 -1
- package/dist/container-BZKeHc5X.d.mts.map +1 -1
- package/dist/{container-COmj1vM3.mjs → container-BvRsVif5.mjs} +3 -3
- package/dist/{container-COmj1vM3.mjs.map → container-BvRsVif5.mjs.map} +1 -1
- package/dist/context-ByhFuf19.mjs +12 -0
- package/dist/context-ByhFuf19.mjs.map +1 -0
- package/dist/context-aocg9gO-.d.mts.map +1 -1
- package/dist/context-decorator-BYf8HdMl.d.mts.map +1 -1
- package/dist/core/adapter.d.mts +2 -2
- package/dist/core/adapter.mjs +1 -1
- package/dist/core/app-module.mjs +1 -1
- package/dist/core/cache.mjs +1 -1
- package/dist/core/cache.mjs.map +1 -1
- package/dist/core/container.mjs +2 -2
- package/dist/core/cron.mjs +2 -2
- package/dist/core/cron.mjs.map +1 -1
- package/dist/core/decorators.d.mts +1 -1
- package/dist/core/decorators.mjs +2 -2
- package/dist/core/decorators.mjs.map +1 -1
- package/dist/core/errors.mjs +2 -2
- package/dist/core/errors.mjs.map +1 -1
- package/dist/core/interfaces.mjs +1 -1
- package/dist/core/interfaces.mjs.map +1 -1
- package/dist/core/logger.mjs +1 -1
- package/dist/core/logger.mjs.map +1 -1
- package/dist/core/path.mjs +1 -1
- package/dist/core/path.mjs.map +1 -1
- package/dist/core/plugin.d.mts +1 -1
- package/dist/core/plugin.mjs +1 -1
- package/dist/core/reactivity.mjs +1 -1
- package/dist/core/reactivity.mjs.map +1 -1
- package/dist/{core-CJKmg8QR.mjs → core-BQHQsimv.mjs} +3 -3
- package/dist/{core-CJKmg8QR.mjs.map → core-BQHQsimv.mjs.map} +1 -1
- package/dist/cors-BfgqfLPd.d.mts.map +1 -1
- package/dist/cron-GNoYydfg.d.mts.map +1 -1
- package/dist/csrf-DiK9zept.d.mts.map +1 -1
- package/dist/{decorators-ZcjsV1O0.d.mts → decorators-BYi8lB37.d.mts} +7 -1
- package/dist/decorators-BYi8lB37.d.mts.map +1 -0
- package/dist/{env-DfB1WNRf.mjs → env-D3Q6sGZP.mjs} +3 -3
- package/dist/{env-DfB1WNRf.mjs.map → env-D3Q6sGZP.mjs.map} +1 -1
- package/dist/errors-uaHCeXRz.d.mts.map +1 -1
- package/dist/execution-context-DKnt61pY.d.mts.map +1 -1
- package/dist/helmet-CaMbjzBG.d.mts.map +1 -1
- package/dist/http/application.d.mts +1 -1
- package/dist/http/application.mjs +2 -2
- package/dist/http/bootstrap.d.mts +1 -1
- package/dist/http/bootstrap.mjs +2 -2
- package/dist/http/context.mjs +2 -2
- package/dist/http/middleware/cors.mjs +1 -1
- package/dist/http/middleware/cors.mjs.map +1 -1
- package/dist/http/middleware/csrf.mjs +1 -1
- package/dist/http/middleware/csrf.mjs.map +1 -1
- package/dist/http/middleware/error-handler.mjs +2 -2
- package/dist/http/middleware/error-handler.mjs.map +1 -1
- package/dist/http/middleware/helmet.mjs +1 -1
- package/dist/http/middleware/helmet.mjs.map +1 -1
- package/dist/http/middleware/rate-limit.mjs +2 -2
- package/dist/http/middleware/rate-limit.mjs.map +1 -1
- package/dist/http/middleware/request-id.mjs +1 -1
- package/dist/http/middleware/request-id.mjs.map +1 -1
- package/dist/http/middleware/request-logger.mjs +2 -2
- package/dist/http/middleware/request-logger.mjs.map +1 -1
- package/dist/http/middleware/session.mjs +1 -1
- package/dist/http/middleware/session.mjs.map +1 -1
- package/dist/http/middleware/spa.d.mts +1 -1
- package/dist/http/middleware/spa.mjs +2 -2
- package/dist/http/middleware/spa.mjs.map +1 -1
- package/dist/http/middleware/upload.d.mts +1 -1
- package/dist/http/middleware/upload.mjs +1 -1
- package/dist/http/middleware/upload.mjs.map +1 -1
- package/dist/http/middleware/validate.mjs +2 -2
- package/dist/http/middleware/validate.mjs.map +1 -1
- package/dist/http/middleware/views.d.mts +1 -1
- package/dist/http/middleware/views.mjs +2 -2
- package/dist/http/middleware/views.mjs.map +1 -1
- package/dist/http/query/index.mjs +2 -2
- package/dist/http/router-builder.d.mts +1 -1
- package/dist/http/router-builder.mjs +2 -2
- package/dist/http/router-builder.mjs.map +1 -1
- package/dist/index-B9cyOQAe.d.mts.map +1 -1
- package/dist/{index-CkKLXZHg.d.mts → index-C5aLtJkk.d.mts} +3 -3
- package/dist/index-C5aLtJkk.d.mts.map +1 -0
- package/dist/{index-oB1CDq5i.d.mts → index-DM1PteZT.d.mts} +2 -2
- package/dist/index-DM1PteZT.d.mts.map +1 -0
- package/dist/index.d.mts +12 -12
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/interfaces-B50n7dwl.d.mts.map +1 -1
- package/dist/logger-CLVRZf2N.d.mts.map +1 -1
- package/dist/{metadata-D9F5Lm-z.mjs → metadata-DN0sBHmY.mjs} +2 -2
- package/dist/{metadata-D9F5Lm-z.mjs.map → metadata-DN0sBHmY.mjs.map} +1 -1
- package/dist/path-Diviu6hv.d.mts.map +1 -1
- package/dist/{plugin-NMHVmDRF.d.mts → plugin-BGK3PJOJ.d.mts} +2 -2
- package/dist/plugin-BGK3PJOJ.d.mts.map +1 -0
- package/dist/{query-Di7R8qNX.mjs → query-BuX74AmB.mjs} +2 -2
- package/dist/{query-Di7R8qNX.mjs.map → query-BuX74AmB.mjs.map} +1 -1
- package/dist/reactivity-CfJceo4H.d.mts.map +1 -1
- package/dist/request-id-BgglvZHN.d.mts.map +1 -1
- package/dist/request-logger-CWHbAzTE.d.mts.map +1 -1
- package/dist/{router-builder-BfCCeQB-.d.mts → router-builder-QNfwAjQ8.d.mts} +2 -2
- package/dist/router-builder-QNfwAjQ8.d.mts.map +1 -0
- package/dist/session-N0hActOO.d.mts.map +1 -1
- package/dist/{spa-DIXtRBj-.d.mts → spa-BlW5xxU5.d.mts} +2 -2
- package/dist/spa-BlW5xxU5.d.mts.map +1 -0
- package/dist/{upload-DqRwWhX0.d.mts → upload-5goxbO9A.d.mts} +2 -2
- package/dist/upload-5goxbO9A.d.mts.map +1 -0
- package/dist/validate-HwAyWTwH.d.mts.map +1 -1
- package/dist/{views-DDT9vghc.d.mts → views-CNCTSpcw.d.mts} +2 -2
- package/dist/views-CNCTSpcw.d.mts.map +1 -0
- package/package.json +4 -4
- package/dist/adapter-CoJp35rz.d.mts.map +0 -1
- package/dist/application-Bg_-CIPK.mjs +0 -12
- package/dist/application-Bg_-CIPK.mjs.map +0 -1
- package/dist/application-DmekGKp7.d.mts.map +0 -1
- package/dist/bootstrap-CwP2GGu0.d.mts.map +0 -1
- package/dist/context-CvJQzbBF.mjs +0 -12
- package/dist/context-CvJQzbBF.mjs.map +0 -1
- package/dist/decorators-ZcjsV1O0.d.mts.map +0 -1
- package/dist/index-CkKLXZHg.d.mts.map +0 -1
- package/dist/index-oB1CDq5i.d.mts.map +0 -1
- package/dist/plugin-NMHVmDRF.d.mts.map +0 -1
- package/dist/router-builder-BfCCeQB-.d.mts.map +0 -1
- package/dist/spa-DIXtRBj-.d.mts.map +0 -1
- package/dist/upload-DqRwWhX0.d.mts.map +0 -1
- package/dist/views-DDT9vghc.d.mts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs v5.
|
|
2
|
+
* @forinda/kickjs v5.8.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
const FILTER_OPERATORS=new Set([`eq`,`neq`,`gt`,`gte`,`lt`,`lte`,`between`,`in`,`contains`,`starts`,`ends`]);function parseFilters(filterParam,allowedFields){if(!filterParam)return[];let items=Array.isArray(filterParam)?filterParam:[filterParam],results=[],allowed=allowedFields?new Set(allowedFields):null;for(let item of items){let firstColon=item.indexOf(`:`);if(firstColon===-1)continue;let secondColon=item.indexOf(`:`,firstColon+1);if(secondColon===-1)continue;let field=item.slice(0,firstColon),operator=item.slice(firstColon+1,secondColon),value=item.slice(secondColon+1);!field||!value||FILTER_OPERATORS.has(operator)&&(allowed&&!allowed.has(field)||results.push({field,operator,value}))}return results}function parseSort(sortParam,allowedFields){if(!sortParam)return[];let items=Array.isArray(sortParam)?sortParam:[sortParam],results=[],allowed=allowedFields?new Set(allowedFields):null;for(let item of items){let lastColon=item.lastIndexOf(`:`);if(lastColon===-1)continue;let field=item.slice(0,lastColon),dir=item.slice(lastColon+1).toLowerCase();field&&(dir!==`asc`&&dir!==`desc`||allowed&&!allowed.has(field)||results.push({field,direction:dir}))}return results}function parsePagination(params){let page=typeof params.page==`string`?Number.parseInt(params.page,10):params.page??1,limit=typeof params.limit==`string`?Number.parseInt(params.limit,10):params.limit??20;return(Number.isNaN(page)||page<1)&&(page=1),(Number.isNaN(limit)||limit<1)&&(limit=20),limit>100&&(limit=100),{page,limit,offset:(page-1)*limit}}function parseSearchQuery(q){return q?q.trim().slice(0,200):``}function normalizeFieldConfig(config){if(config)return`columns`in config&&config.columns&&typeof config.columns==`object`?{filterable:Object.keys(config.columns),sortable:config.sortable?Object.keys(config.sortable):void 0,searchable:void 0}:config}function parseQuery(query,fieldConfig){let normalized=normalizeFieldConfig(fieldConfig);return{filters:parseFilters(query.filter,normalized?.filterable),sort:parseSort(query.sort,normalized?.sortable),pagination:parsePagination({page:query.page,limit:query.limit}),search:parseSearchQuery(query.q)}}function buildQueryParams(parsed){let params={};return parsed.filters?.length&&(params.filter=parsed.filters.map(f=>`${f.field}:${f.operator}:${f.value}`)),parsed.sort?.length&&(params.sort=parsed.sort.map(s=>`${s.field}:${s.direction}`)),parsed.pagination&&(params.page=parsed.pagination.page,params.limit=parsed.pagination.limit),parsed.search&&(params.q=parsed.search),params}export{parseSearchQuery as a,parseQuery as i,parseFilters as n,parseSort as o,parsePagination as r,FILTER_OPERATORS as s,buildQueryParams as t};
|
|
12
|
-
//# sourceMappingURL=query-
|
|
12
|
+
//# sourceMappingURL=query-BuX74AmB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-Di7R8qNX.mjs","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"sourcesContent":["/** Supported filter operators for query string parsing */\nexport type FilterOperator =\n | 'eq'\n | 'neq'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'between'\n | 'in'\n | 'contains'\n | 'starts'\n | 'ends'\n\nexport const FILTER_OPERATORS = new Set<string>([\n 'eq',\n 'neq',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'in',\n 'contains',\n 'starts',\n 'ends',\n])\n\nexport interface FilterItem {\n field: string\n operator: FilterOperator\n value: string\n}\n\nexport interface SortItem {\n field: string\n direction: 'asc' | 'desc'\n}\n\nexport interface PaginationParams {\n page: number\n limit: number\n offset: number\n}\n\n/**\n * The result of parsing a query string. ORM-agnostic — pass this to\n * a query builder adapter (Drizzle, Prisma, Sequelize, etc.) to produce\n * database-specific query objects.\n */\nexport interface ParsedQuery {\n filters: FilterItem[]\n sort: SortItem[]\n pagination: PaginationParams\n search: string\n}\n\n/**\n * Helper that extracts the literal field union from a `QueryFieldConfig`\n * key. When the user passes `{ filterable: ['status', 'category'] } as const`,\n * `FieldsOf<typeof config, 'filterable'>` resolves to `'status' | 'category'`.\n *\n * Without `as const`, the arrays widen to `string[]` and the union falls\n * back to `string`. This is the documented escape hatch.\n */\nexport type FieldsOf<\n TConfig extends QueryFieldConfig | undefined,\n K extends 'filterable' | 'sortable' | 'searchable',\n> = TConfig extends { readonly [P in K]: readonly (infer F)[] }\n ? F extends string\n ? F\n : string\n : TConfig extends { columns: infer C }\n ? K extends 'filterable'\n ? keyof C & string\n : string\n : string\n\n/**\n * Narrowed `ParsedQuery` whose `filters[].field` and `sort[].field`\n * unions are constrained to the literal whitelist passed to\n * `ctx.qs()` / `parseQuery()`. Extends `ParsedQuery`, so any function\n * accepting `ParsedQuery` accepts a `TypedParsedQuery<X>` automatically\n * (subtype assignment, no codegen, backward compatible).\n *\n * @example\n * ```ts\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt'],\n * } as const)\n * parsed.filters[0]?.field // 'status' | 'priority'\n * parsed.sort[0]?.field // 'createdAt'\n * ```\n */\nexport interface TypedParsedQuery<\n TConfig extends QueryFieldConfig | undefined,\n> extends ParsedQuery {\n filters: Array<FilterItem & { field: FieldsOf<TConfig, 'filterable'> }>\n sort: Array<SortItem & { field: FieldsOf<TConfig, 'sortable'> }>\n}\n\n/**\n * Restrict which fields can be filtered, sorted, or searched.\n * Fields not in the allow-list are silently ignored.\n *\n * Accepts two forms:\n * 1. **String arrays** (standard): `{ filterable: ['name', 'status'], sortable: ['createdAt'] }`\n * 2. **Column-object maps** (Drizzle-style): `{ columns: { name: col, status: col }, sortable: { createdAt: col } }`\n *\n * When column-object maps are provided, `Object.keys()` is used to derive the field names.\n */\nexport type QueryFieldConfig = StringQueryFieldConfig | ColumnQueryFieldConfig\n\nexport interface StringQueryFieldConfig {\n filterable?: string[]\n sortable?: string[]\n searchable?: string[]\n}\n\n/**\n * Column-object-based field config (e.g., from DrizzleQueryParamsConfig).\n * Keys are the field names used in query strings; values are ORM column references.\n */\nexport interface ColumnQueryFieldConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/** Standardized paginated response shape */\nexport interface PaginatedResponse<T = any> {\n data: T[]\n meta: {\n page: number\n limit: number\n total: number\n totalPages: number\n hasNext: boolean\n hasPrev: boolean\n }\n}\n\n/**\n * Interface for ORM-specific query builder adapters.\n * Implement this to translate a `ParsedQuery` into your ORM's query format.\n *\n * @example\n * ```ts\n * // Drizzle adapter\n * class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult> {\n * build(parsed: ParsedQuery, config: DrizzleConfig): DrizzleQueryResult {\n * // Convert filters → Drizzle SQL conditions\n * // Convert sort → Drizzle orderBy\n * return { where, orderBy, limit, offset }\n * }\n * }\n *\n * // Prisma adapter\n * class PrismaQueryAdapter implements QueryBuilderAdapter<PrismaQueryResult> {\n * build(parsed: ParsedQuery, config: PrismaConfig): PrismaQueryResult {\n * return { where, orderBy, skip, take }\n * }\n * }\n * ```\n */\nexport interface QueryBuilderAdapter<TResult = any, TConfig = any> {\n /** Human-readable name for debugging */\n readonly name: string\n\n /**\n * Convert a ParsedQuery into an ORM-specific query object.\n * @param parsed - The ORM-agnostic parsed query\n * @param config - ORM-specific configuration (column maps, search columns, etc.)\n */\n build(parsed: ParsedQuery, config: TConfig): TResult\n}\n","import {\n FILTER_OPERATORS,\n type FilterItem,\n type FilterOperator,\n type SortItem,\n type PaginationParams,\n type ParsedQuery,\n type QueryFieldConfig,\n type StringQueryFieldConfig,\n type TypedParsedQuery,\n} from './types'\n\nconst MAX_SEARCH_LENGTH = 200\nconst DEFAULT_PAGE = 1\nconst DEFAULT_LIMIT = 20\nconst MAX_LIMIT = 100\n\n// ── Individual parsers ──────────────────────────────────────────────────\n\n/** Parse filter strings like \"status:eq:active\" into structured objects */\nexport function parseFilters(\n filterParam: string | string[] | undefined,\n allowedFields?: string[],\n): FilterItem[] {\n if (!filterParam) return []\n\n const items = Array.isArray(filterParam) ? filterParam : [filterParam]\n const results: FilterItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on first two colons only — value may contain colons (e.g. timestamps)\n const firstColon = item.indexOf(':')\n if (firstColon === -1) continue\n\n const secondColon = item.indexOf(':', firstColon + 1)\n if (secondColon === -1) continue\n\n const field = item.slice(0, firstColon)\n const operator = item.slice(firstColon + 1, secondColon)\n const value = item.slice(secondColon + 1)\n\n if (!field || !value) continue\n if (!FILTER_OPERATORS.has(operator)) continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, operator: operator as FilterOperator, value })\n }\n\n return results\n}\n\n/** Parse sort strings like \"firstName:asc\" into structured objects */\nexport function parseSort(\n sortParam: string | string[] | undefined,\n allowedFields?: string[],\n): SortItem[] {\n if (!sortParam) return []\n\n const items = Array.isArray(sortParam) ? sortParam : [sortParam]\n const results: SortItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on last colon so field names with colons work\n const lastColon = item.lastIndexOf(':')\n if (lastColon === -1) continue\n\n const field = item.slice(0, lastColon)\n const dir = item.slice(lastColon + 1).toLowerCase()\n\n if (!field) continue\n if (dir !== 'asc' && dir !== 'desc') continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, direction: dir })\n }\n\n return results\n}\n\n/** Parse page/limit into pagination with computed offset */\nexport function parsePagination(params: {\n page?: string | number\n limit?: string | number\n}): PaginationParams {\n let page =\n typeof params.page === 'string'\n ? Number.parseInt(params.page, 10)\n : (params.page ?? DEFAULT_PAGE)\n let limit =\n typeof params.limit === 'string'\n ? Number.parseInt(params.limit, 10)\n : (params.limit ?? DEFAULT_LIMIT)\n\n if (Number.isNaN(page) || page < 1) page = DEFAULT_PAGE\n if (Number.isNaN(limit) || limit < 1) limit = DEFAULT_LIMIT\n if (limit > MAX_LIMIT) limit = MAX_LIMIT\n\n return { page, limit, offset: (page - 1) * limit }\n}\n\n/** Sanitize and truncate search query */\nexport function parseSearchQuery(q: string | undefined): string {\n if (!q) return ''\n return q.trim().slice(0, MAX_SEARCH_LENGTH)\n}\n\n// ── Config normalizer ───────────────────────────────────────────────────\n\n/**\n * Normalize a QueryFieldConfig into the string-based form.\n * Detects column-object-based configs (DrizzleQueryParamsConfig) by checking\n * for a `columns` property and extracts `Object.keys()`.\n */\nfunction normalizeFieldConfig(config?: QueryFieldConfig): StringQueryFieldConfig | undefined {\n if (!config) return undefined\n\n // Column-object-based config (e.g., DrizzleQueryParamsConfig)\n if ('columns' in config && config.columns && typeof config.columns === 'object') {\n return {\n filterable: Object.keys(config.columns),\n sortable: config.sortable ? Object.keys(config.sortable) : undefined,\n searchable: undefined, // searchColumns are Column objects, not used for string filtering\n }\n }\n\n // Already string-based\n return config as StringQueryFieldConfig\n}\n\n// ── Combined parser ─────────────────────────────────────────────────────\n\n/**\n * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.\n *\n * @param query - Raw query string object from `req.query` or Zod-validated object\n * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable).\n * Accepts both string-based configs (`{ filterable, sortable, searchable }`) and\n * column-object configs (`{ columns, sortable, searchColumns }`).\n *\n * @example\n * ```ts\n * // String-based config\n * const parsed = parseQuery(ctx.query, {\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * })\n *\n * // Column-object config (DrizzleQueryParamsConfig)\n * const parsed = parseQuery(ctx.query, TASK_QUERY_CONFIG)\n * ```\n *\n * Query string format:\n * - Filters: `?filter=field:operator:value` (repeatable)\n * - Sort: `?sort=field:asc|desc` (repeatable)\n * - Pagination: `?page=1&limit=20`\n * - Search: `?q=search+term`\n *\n * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends\n */\nexport function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(\n query: Record<string, any>,\n fieldConfig?: TConfig,\n): TypedParsedQuery<TConfig> {\n const normalized = normalizeFieldConfig(fieldConfig)\n return {\n filters: parseFilters(query.filter, normalized?.filterable),\n sort: parseSort(query.sort, normalized?.sortable),\n pagination: parsePagination({ page: query.page, limit: query.limit }),\n search: parseSearchQuery(query.q),\n } as TypedParsedQuery<TConfig>\n}\n\n// ── Query URL builder (for client-side or testing) ──────────────────────\n\n/** Convert ParsedQuery back into query string parameters */\nexport function buildQueryParams(\n parsed: Partial<ParsedQuery>,\n): Record<string, string | string[] | number> {\n const params: Record<string, string | string[] | number> = {}\n\n if (parsed.filters?.length) {\n params.filter = parsed.filters.map((f) => `${f.field}:${f.operator}:${f.value}`)\n }\n\n if (parsed.sort?.length) {\n params.sort = parsed.sort.map((s) => `${s.field}:${s.direction}`)\n }\n\n if (parsed.pagination) {\n params.page = parsed.pagination.page\n params.limit = parsed.pagination.limit\n }\n\n if (parsed.search) {\n params.q = parsed.search\n }\n\n return params\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-BuX74AmB.mjs","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"sourcesContent":["/** Supported filter operators for query string parsing */\nexport type FilterOperator =\n | 'eq'\n | 'neq'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'between'\n | 'in'\n | 'contains'\n | 'starts'\n | 'ends'\n\nexport const FILTER_OPERATORS = new Set<string>([\n 'eq',\n 'neq',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'in',\n 'contains',\n 'starts',\n 'ends',\n])\n\nexport interface FilterItem {\n field: string\n operator: FilterOperator\n value: string\n}\n\nexport interface SortItem {\n field: string\n direction: 'asc' | 'desc'\n}\n\nexport interface PaginationParams {\n page: number\n limit: number\n offset: number\n}\n\n/**\n * The result of parsing a query string. ORM-agnostic — pass this to\n * a query builder adapter (Drizzle, Prisma, Sequelize, etc.) to produce\n * database-specific query objects.\n */\nexport interface ParsedQuery {\n filters: FilterItem[]\n sort: SortItem[]\n pagination: PaginationParams\n search: string\n}\n\n/**\n * Helper that extracts the literal field union from a `QueryFieldConfig`\n * key. When the user passes `{ filterable: ['status', 'category'] } as const`,\n * `FieldsOf<typeof config, 'filterable'>` resolves to `'status' | 'category'`.\n *\n * Without `as const`, the arrays widen to `string[]` and the union falls\n * back to `string`. This is the documented escape hatch.\n */\nexport type FieldsOf<\n TConfig extends QueryFieldConfig | undefined,\n K extends 'filterable' | 'sortable' | 'searchable',\n> = TConfig extends { readonly [P in K]: readonly (infer F)[] }\n ? F extends string\n ? F\n : string\n : TConfig extends { columns: infer C }\n ? K extends 'filterable'\n ? keyof C & string\n : string\n : string\n\n/**\n * Narrowed `ParsedQuery` whose `filters[].field` and `sort[].field`\n * unions are constrained to the literal whitelist passed to\n * `ctx.qs()` / `parseQuery()`. Extends `ParsedQuery`, so any function\n * accepting `ParsedQuery` accepts a `TypedParsedQuery<X>` automatically\n * (subtype assignment, no codegen, backward compatible).\n *\n * @example\n * ```ts\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt'],\n * } as const)\n * parsed.filters[0]?.field // 'status' | 'priority'\n * parsed.sort[0]?.field // 'createdAt'\n * ```\n */\nexport interface TypedParsedQuery<\n TConfig extends QueryFieldConfig | undefined,\n> extends ParsedQuery {\n filters: Array<FilterItem & { field: FieldsOf<TConfig, 'filterable'> }>\n sort: Array<SortItem & { field: FieldsOf<TConfig, 'sortable'> }>\n}\n\n/**\n * Restrict which fields can be filtered, sorted, or searched.\n * Fields not in the allow-list are silently ignored.\n *\n * Accepts two forms:\n * 1. **String arrays** (standard): `{ filterable: ['name', 'status'], sortable: ['createdAt'] }`\n * 2. **Column-object maps** (Drizzle-style): `{ columns: { name: col, status: col }, sortable: { createdAt: col } }`\n *\n * When column-object maps are provided, `Object.keys()` is used to derive the field names.\n */\nexport type QueryFieldConfig = StringQueryFieldConfig | ColumnQueryFieldConfig\n\nexport interface StringQueryFieldConfig {\n filterable?: string[]\n sortable?: string[]\n searchable?: string[]\n}\n\n/**\n * Column-object-based field config (e.g., from DrizzleQueryParamsConfig).\n * Keys are the field names used in query strings; values are ORM column references.\n */\nexport interface ColumnQueryFieldConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/** Standardized paginated response shape */\nexport interface PaginatedResponse<T = any> {\n data: T[]\n meta: {\n page: number\n limit: number\n total: number\n totalPages: number\n hasNext: boolean\n hasPrev: boolean\n }\n}\n\n/**\n * Interface for ORM-specific query builder adapters.\n * Implement this to translate a `ParsedQuery` into your ORM's query format.\n *\n * @example\n * ```ts\n * // Drizzle adapter\n * class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult> {\n * build(parsed: ParsedQuery, config: DrizzleConfig): DrizzleQueryResult {\n * // Convert filters → Drizzle SQL conditions\n * // Convert sort → Drizzle orderBy\n * return { where, orderBy, limit, offset }\n * }\n * }\n *\n * // Prisma adapter\n * class PrismaQueryAdapter implements QueryBuilderAdapter<PrismaQueryResult> {\n * build(parsed: ParsedQuery, config: PrismaConfig): PrismaQueryResult {\n * return { where, orderBy, skip, take }\n * }\n * }\n * ```\n */\nexport interface QueryBuilderAdapter<TResult = any, TConfig = any> {\n /** Human-readable name for debugging */\n readonly name: string\n\n /**\n * Convert a ParsedQuery into an ORM-specific query object.\n * @param parsed - The ORM-agnostic parsed query\n * @param config - ORM-specific configuration (column maps, search columns, etc.)\n */\n build(parsed: ParsedQuery, config: TConfig): TResult\n}\n","import {\n FILTER_OPERATORS,\n type FilterItem,\n type FilterOperator,\n type SortItem,\n type PaginationParams,\n type ParsedQuery,\n type QueryFieldConfig,\n type StringQueryFieldConfig,\n type TypedParsedQuery,\n} from './types'\n\nconst MAX_SEARCH_LENGTH = 200\nconst DEFAULT_PAGE = 1\nconst DEFAULT_LIMIT = 20\nconst MAX_LIMIT = 100\n\n// ── Individual parsers ──────────────────────────────────────────────────\n\n/** Parse filter strings like \"status:eq:active\" into structured objects */\nexport function parseFilters(\n filterParam: string | string[] | undefined,\n allowedFields?: string[],\n): FilterItem[] {\n if (!filterParam) return []\n\n const items = Array.isArray(filterParam) ? filterParam : [filterParam]\n const results: FilterItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on first two colons only — value may contain colons (e.g. timestamps)\n const firstColon = item.indexOf(':')\n if (firstColon === -1) continue\n\n const secondColon = item.indexOf(':', firstColon + 1)\n if (secondColon === -1) continue\n\n const field = item.slice(0, firstColon)\n const operator = item.slice(firstColon + 1, secondColon)\n const value = item.slice(secondColon + 1)\n\n if (!field || !value) continue\n if (!FILTER_OPERATORS.has(operator)) continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, operator: operator as FilterOperator, value })\n }\n\n return results\n}\n\n/** Parse sort strings like \"firstName:asc\" into structured objects */\nexport function parseSort(\n sortParam: string | string[] | undefined,\n allowedFields?: string[],\n): SortItem[] {\n if (!sortParam) return []\n\n const items = Array.isArray(sortParam) ? sortParam : [sortParam]\n const results: SortItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on last colon so field names with colons work\n const lastColon = item.lastIndexOf(':')\n if (lastColon === -1) continue\n\n const field = item.slice(0, lastColon)\n const dir = item.slice(lastColon + 1).toLowerCase()\n\n if (!field) continue\n if (dir !== 'asc' && dir !== 'desc') continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, direction: dir })\n }\n\n return results\n}\n\n/** Parse page/limit into pagination with computed offset */\nexport function parsePagination(params: {\n page?: string | number\n limit?: string | number\n}): PaginationParams {\n let page =\n typeof params.page === 'string'\n ? Number.parseInt(params.page, 10)\n : (params.page ?? DEFAULT_PAGE)\n let limit =\n typeof params.limit === 'string'\n ? Number.parseInt(params.limit, 10)\n : (params.limit ?? DEFAULT_LIMIT)\n\n if (Number.isNaN(page) || page < 1) page = DEFAULT_PAGE\n if (Number.isNaN(limit) || limit < 1) limit = DEFAULT_LIMIT\n if (limit > MAX_LIMIT) limit = MAX_LIMIT\n\n return { page, limit, offset: (page - 1) * limit }\n}\n\n/** Sanitize and truncate search query */\nexport function parseSearchQuery(q: string | undefined): string {\n if (!q) return ''\n return q.trim().slice(0, MAX_SEARCH_LENGTH)\n}\n\n// ── Config normalizer ───────────────────────────────────────────────────\n\n/**\n * Normalize a QueryFieldConfig into the string-based form.\n * Detects column-object-based configs (DrizzleQueryParamsConfig) by checking\n * for a `columns` property and extracts `Object.keys()`.\n */\nfunction normalizeFieldConfig(config?: QueryFieldConfig): StringQueryFieldConfig | undefined {\n if (!config) return undefined\n\n // Column-object-based config (e.g., DrizzleQueryParamsConfig)\n if ('columns' in config && config.columns && typeof config.columns === 'object') {\n return {\n filterable: Object.keys(config.columns),\n sortable: config.sortable ? Object.keys(config.sortable) : undefined,\n searchable: undefined, // searchColumns are Column objects, not used for string filtering\n }\n }\n\n // Already string-based\n return config as StringQueryFieldConfig\n}\n\n// ── Combined parser ─────────────────────────────────────────────────────\n\n/**\n * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.\n *\n * @param query - Raw query string object from `req.query` or Zod-validated object\n * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable).\n * Accepts both string-based configs (`{ filterable, sortable, searchable }`) and\n * column-object configs (`{ columns, sortable, searchColumns }`).\n *\n * @example\n * ```ts\n * // String-based config\n * const parsed = parseQuery(ctx.query, {\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * })\n *\n * // Column-object config (DrizzleQueryParamsConfig)\n * const parsed = parseQuery(ctx.query, TASK_QUERY_CONFIG)\n * ```\n *\n * Query string format:\n * - Filters: `?filter=field:operator:value` (repeatable)\n * - Sort: `?sort=field:asc|desc` (repeatable)\n * - Pagination: `?page=1&limit=20`\n * - Search: `?q=search+term`\n *\n * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends\n */\nexport function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(\n query: Record<string, any>,\n fieldConfig?: TConfig,\n): TypedParsedQuery<TConfig> {\n const normalized = normalizeFieldConfig(fieldConfig)\n return {\n filters: parseFilters(query.filter, normalized?.filterable),\n sort: parseSort(query.sort, normalized?.sortable),\n pagination: parsePagination({ page: query.page, limit: query.limit }),\n search: parseSearchQuery(query.q),\n } as TypedParsedQuery<TConfig>\n}\n\n// ── Query URL builder (for client-side or testing) ──────────────────────\n\n/** Convert ParsedQuery back into query string parameters */\nexport function buildQueryParams(\n parsed: Partial<ParsedQuery>,\n): Record<string, string | string[] | number> {\n const params: Record<string, string | string[] | number> = {}\n\n if (parsed.filters?.length) {\n params.filter = parsed.filters.map((f) => `${f.field}:${f.operator}:${f.value}`)\n }\n\n if (parsed.sort?.length) {\n params.sort = parsed.sort.map((s) => `${s.field}:${s.direction}`)\n }\n\n if (parsed.pagination) {\n params.page = parsed.pagination.page\n params.limit = parsed.pagination.limit\n }\n\n if (parsed.search) {\n params.q = parsed.search\n }\n\n return params\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactivity-CfJceo4H.d.mts","names":[],"sources":["../src/core/reactivity.ts"],"mappings":";;;AAuCA;;;;UAAiB,GAAA;EACf,KAAA,EAAO,CAAA;EAEgC;EAAvC,SAAA,CAAU,EAAA,GAAK,QAAA,EAAU,CAAA,EAAG,QAAA,EAAU,CAAA;AAAA
|
|
1
|
+
{"version":3,"file":"reactivity-CfJceo4H.d.mts","names":[],"sources":["../src/core/reactivity.ts"],"mappings":";;;AAuCA;;;;UAAiB,GAAA;EACf,KAAA,EAAO,CAAA;EAEgC;EAAvC,SAAA,CAAU,EAAA,GAAK,QAAA,EAAU,CAAA,EAAG,QAAA,EAAU,CAAA;AAAA;;;;;;;;;;AAAU;iBAalC,GAAA,GAAA,CAAO,YAAA,EAAc,CAAA,GAAI,GAAA,CAAI,CAAA;AAAA,UA2B5B,WAAA;EAAA,SACN,KAAA,EAAO,CAAC;AAAA;;;;;;;;;;;AA5B2B;AA2B9C;iBAgBgB,QAAA,GAAA,CAAY,MAAA,QAAc,CAAA,GAAI,WAAA,CAAY,CAAA;AAAA,UA+BzC,YAAA;EA9CE;EAgDjB,SAAS;AAAA;AAAA,KAGN,WAAA,MAAiB,GAAA,CAAI,CAAA,IAAK,WAAA,CAAY,CAAA,WAAY,CAAA;;AAnDpC;AAenB;;;;;;;;;;;;iBAoDgB,KAAA,GAAA,CACd,MAAA,EAAQ,WAAA,CAAY,CAAA,GACpB,QAAA,GAAW,QAAA,EAAU,CAAA,EAAG,QAAA,EAAU,CAAA,WAClC,OAAA,GAAU,YAAA;;AAvD+C;AA+B3D;;;;AAEW;AACV;;;;;;;;iBAwEe,QAAA,WAAmB,MAAA,cAAA,CAAqB,MAAA,EAAQ,CAAA,GAAI,CAAA;;iBA0BpD,KAAA,CAAM,KAAA,QAAa,KAAA,IAAS,GAAG;;iBAK/B,KAAA,GAAA,CAAS,KAAA,EAAO,GAAA,CAAI,CAAA,IAAK,CAAA,GAAI,CAAA;;iBAK7B,MAAA,WAAiB,MAAA,cAAA,CAAqB,GAAA,EAAK,CAAA,iBAAkB,CAAA,GAAI,GAAA,CAAI,CAAA,CAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-id-BgglvZHN.d.mts","names":[],"sources":["../src/http/middleware/request-id.ts"],"mappings":";;;;cAGa,iBAAA;;AAAb
|
|
1
|
+
{"version":3,"file":"request-id-BgglvZHN.d.mts","names":[],"sources":["../src/http/middleware/request-id.ts"],"mappings":";;;;cAGa,iBAAA;;AAAb;;;;AAA8B;AAY9B;;;;iBAAgB,SAAA,CAAA,IACN,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-logger-CWHbAzTE.d.mts","names":[],"sources":["../src/http/middleware/request-logger.ts"],"mappings":";;;;UAGiB,oBAAA;;EAEf,IAAA;EAFmC;EAInC,KAAA;EAJmC;EAMnC,IAAA;AAAA
|
|
1
|
+
{"version":3,"file":"request-logger-CWHbAzTE.d.mts","names":[],"sources":["../src/http/middleware/request-logger.ts"],"mappings":";;;;UAGiB,oBAAA;;EAEf,IAAA;EAFmC;EAInC,KAAA;EAJmC;EAMnC,IAAA;AAAA;;;AAAI;AAmBN;;;;;;;;;;;;;iBAAgB,aAAA,CAAc,OAAA,GAAS,oBAAA,IAK7B,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { S as SourcedRegistration } from "./index-
|
|
2
|
+
import { S as SourcedRegistration } from "./index-C5aLtJkk.mjs";
|
|
3
3
|
import { Router } from "express";
|
|
4
4
|
|
|
5
5
|
//#region src/http/router-builder.d.ts
|
|
@@ -28,4 +28,4 @@ interface BuildRoutesOptions {
|
|
|
28
28
|
declare function buildRoutes(controllerClass: any, options?: BuildRoutesOptions): Router;
|
|
29
29
|
//#endregion
|
|
30
30
|
export { _setExternalContributorSources as n, buildRoutes as r, BuildRoutesOptions as t };
|
|
31
|
-
//# sourceMappingURL=router-builder-
|
|
31
|
+
//# sourceMappingURL=router-builder-QNfwAjQ8.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-builder-QNfwAjQ8.d.mts","names":[],"sources":["../src/http/router-builder.ts"],"mappings":";;;;;;iBAoCgB,8BAAA,CAA+B,OAAuC,WAArB,mBAAmB;AAAA,UAInE,kBAAA;EAJ6B;;;AAAwC;AAItF;;EAOE,eAAA,YAA2B,mBAAmB;AAAA;AAAA;AAchD;;;;;;;;;AAA2F;AAd3C,iBAchC,WAAA,CAAY,eAAA,OAAsB,OAAA,GAAS,kBAAA,GAA0B,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-N0hActOO.d.mts","names":[],"sources":["../src/http/middleware/session.ts"],"mappings":";;;;UAGiB,WAAA;EAAA,CACd,
|
|
1
|
+
{"version":3,"file":"session-N0hActOO.d.mts","names":[],"sources":["../src/http/middleware/session.ts"],"mappings":";;;;UAGiB,WAAA;EAAA,CACd,GAAW;AAAA;AAAA,UAGG,YAAA;EACf,GAAA,CAAI,GAAA,WAAc,OAAA,CAAQ,WAAA;EAC1B,GAAA,CAAI,GAAA,UAAa,IAAA,EAAM,WAAA,EAAa,MAAA,WAAiB,OAAA;EACrD,OAAA,CAAQ,GAAA,WAAc,OAAA;EACtB,KAAA,EAAO,GAAA,UAAa,MAAA,WAAiB,OAAA;AAAA;AAAA,UAGtB,OAAA;EACf,EAAA;EACA,IAAA,EAAM,WAAA;EACN,UAAA,IAAc,OAAA;EACd,OAAA,IAAW,OAAA;EACX,IAAA,IAAQ,OAAA;AAAA;AAAA,UAGO,cAAA;EAX6B;EAa5C,MAAA;EAhBI;EAkBJ,UAAA;EAlB0B;EAoB1B,MAAA;EAnBI;EAqBJ,OAAA;EArBiB;EAuBjB,iBAAA;EAvBqD;EAyBrD,MAAA;IACE,QAAA;IACA,MAAA;IACA,QAAA;IACA,IAAA;IACA,MAAA;EAAA;EA5B0C;EA+B5C,KAAA,GAAQ,YAAY;AAAA;;;;;;;;;;;;;;;;;;;AAvBL;AAGjB;;;;;iBAiJgB,OAAA,CAAQ,OAAA,EAAS,cAAA,IAoBjB,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA,KAAY,OAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { r as AppAdapter, t as AdapterContext } from "./adapter-
|
|
2
|
+
import { r as AppAdapter, t as AdapterContext } from "./adapter-RzfEzd-c.mjs";
|
|
3
3
|
//#region src/http/middleware/spa.d.ts
|
|
4
4
|
interface SpaAdapterOptions {
|
|
5
5
|
/**
|
|
@@ -98,4 +98,4 @@ declare class SpaAdapter implements AppAdapter {
|
|
|
98
98
|
}
|
|
99
99
|
//#endregion
|
|
100
100
|
export { SpaAdapterOptions as n, SpaAdapter as t };
|
|
101
|
-
//# sourceMappingURL=spa-
|
|
101
|
+
//# sourceMappingURL=spa-BlW5xxU5.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spa-BlW5xxU5.d.mts","names":[],"sources":["../src/http/middleware/spa.ts"],"mappings":";;;UAMiB,iBAAA;;;AAAjB;;;;;;;;;;AAiDmB;EAnCjB,SAAA;EAqEsB;;;;;;;;;;EAzDtB,SAAA;EAkEoB;;;;;;;;;EAvDpB,OAAA;EAuDoB;;;;EAjDpB,YAAA;EAoGA;;;;EA9FA,iBAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkCW,UAAA,YAAsB,UAAA;EAAA,QASb,OAAA;EARpB,IAAA;EAAA,QACQ,SAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,QACA,YAAA;EAAA,QACA,iBAAA;EAAA,QACA,SAAA;cAEY,OAAA,GAAS,iBAAA;EAU7B,WAAA,CAAA;IAAc;EAAA,GAAO,cAAA;EAyCrB,WAAA,CAAA;IAAc;EAAA,GAAO,cAAA;AAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { g as FileUploadConfig, o as BaseUploadOptions } from "./decorators-
|
|
2
|
+
import { g as FileUploadConfig, o as BaseUploadOptions } from "./decorators-BYi8lB37.mjs";
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from "express";
|
|
4
4
|
import { Options } from "multer";
|
|
5
5
|
|
|
@@ -76,4 +76,4 @@ declare const upload: {
|
|
|
76
76
|
};
|
|
77
77
|
//#endregion
|
|
78
78
|
export { upload as a, resolveMimeTypes as i, buildUploadMiddleware as n, cleanupFiles as r, UploadOptions as t };
|
|
79
|
-
//# sourceMappingURL=upload-
|
|
79
|
+
//# sourceMappingURL=upload-5goxbO9A.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-5goxbO9A.d.mts","names":[],"sources":["../src/http/middleware/upload.ts"],"mappings":";;;;;;;;;AAsEA;;;;AAAgD;AAYhD;;iBAZgB,gBAAA,CAAiB,KAAe;;;;;;UAY/B,aAAA,SAAsB,iBAAiB;EAIlD;EAFJ,OAAA,GAAU,OAAA;EAmEG;EAjEb,IAAA;AAAA;;;;;;AAiE6E;AAAA;;;;;;iBAAtE,MAAA,CAAO,SAAA,UAAmB,OAAA,GAAS,aAAA,GAAqB,cAAc;;;;iBAQtE,KAAA,CAAM,SAAA,UAAmB,QAAA,WAAe,OAAA,GAAS,aAAA,GAAqB,cAAc;AAAA;;;AAAA,iBAQpF,IAAA,CAAK,OAAA,GAAS,aAAA,GAAqB,cAAc;;;;;AAAA;AAmB1D;;;;;;;;;iBAAgB,YAAA,CAAA,IACN,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;;;;iBA+B7B,qBAAA,CAAsB,MAAA,EAAQ,gBAAA,GAAmB,cAAc;AAA/E;AAAA,cAmBa,MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-HwAyWTwH.d.mts","names":[],"sources":["../src/http/middleware/validate.ts"],"mappings":";;;;UAGiB,gBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA
|
|
1
|
+
{"version":3,"file":"validate-HwAyWTwH.d.mts","names":[],"sources":["../src/http/middleware/validate.ts"],"mappings":";;;;UAGiB,gBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;;;;;;AAAM;AA8CR;;;;iBAAgB,QAAA,CAAS,MAAA,EAAQ,gBAAA,IACvB,GAAA,EAAK,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,IAAA,EAAM,YAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { r as MetaValue } from "./execution-context-DKnt61pY.mjs";
|
|
3
3
|
import { c as DepValue, n as ContextDecorator, r as ContextDecoratorSpec } from "./context-decorator-BYf8HdMl.mjs";
|
|
4
|
-
import { c as AdapterFactory } from "./index-
|
|
4
|
+
import { c as AdapterFactory } from "./index-C5aLtJkk.mjs";
|
|
5
5
|
import { r as RequestContext } from "./context-aocg9gO-.mjs";
|
|
6
6
|
import { NextFunction, Request, Response } from "express";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -214,4 +214,4 @@ interface ViewAdapterOptions {
|
|
|
214
214
|
declare const ViewAdapter: AdapterFactory<ViewAdapterOptions, unknown>;
|
|
215
215
|
//#endregion
|
|
216
216
|
export { TraceContextOptions as a, requestScopeMiddleware as c, getRequestValue as d, requestStore as f, isClusterPrimary as h, TraceContext as i, RequestStore as l, ClusterOptions as m, ViewAdapterOptions as n, parseTraceparent as o, defineHttpContextDecorator as p, TRACEPARENT_HEADER as r, traceContext as s, ViewAdapter as t, getRequestStore as u };
|
|
217
|
-
//# sourceMappingURL=views-
|
|
217
|
+
//# sourceMappingURL=views-CNCTSpcw.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"views-CNCTSpcw.d.mts","names":[],"sources":["../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts"],"mappings":";;;;;;;;;UAOiB,cAAA;;EAEf,OAAA;;EAEA,uBAAA;;EAEA,YAAA;;EAEA,WAAA;;EAEA,aAAA;AAAA;;iBAIc,gBAAA,CAAA;;;iBCoCA,0BAAA,6BAEJ,MAAA,SAAe,QAAA,IAAY,MAAA,2BAC3B,MAAA,oBAA0B,MAAA,gBAAA,CACpC,IAAA,EAAM,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA,IAAkB,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA;;;;UCzDjE,YAAA;;EAEf,SAAA;;;;;;EAMA,SAAA,EAAW,GAAA;;;;;;;;;;;;AFLb;;;;;;;;;EE0BE,MAAA,EAAQ,GAAG;AAAA;AFZb;AAAA,cEgBa,YAAA,EAAY,iBAAA,CAAA,YAAA;;;AFhBO;;iBEsBhB,eAAA,CAAA,GAAmB,YAAY;;ADc/C;;;;;;;;;;;;;;;;;;;;;;;;;iBCsBgB,eAAA,kBAAA,CAAkC,GAAA,EAAK,CAAA,GAAI,SAAA,CAAU,CAAA;;;;;;;;;;;iBCrDrD,sBAAA,CAAA,IAAsB,GAAA,EACnB,OAAA,EAAO,IAAA,EAAQ,QAAA,EAAQ,IAAA,EAAQ,YAAA;;;;;;;cCnBrC,kBAAA;AAAA,UAKI,mBAAA;;;;;EAKf,iBAAiB;AAAA;;;;UAMF,YAAA;;EAEf,OAAA;;EAEA,OAAA;;EAEA,YAAA;EJvBe;EIyBf,KAAA;AAAA;;;;;iBAqBc,gBAAA,CAAiB,MAAA,WAAiB,YAAY;;;AJpC/C;AAIf;;;;AAAgC;;;;ACoChC;;;;;;;;;;iBGsCgB,YAAA,CAAa,OAAA,GAAS,mBAAA,IAC5B,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;UC3F5B,kBAAA;;;;;;;;;;;;;;;EAef,MAAA;;EAGA,GAAA;;EAGA,QAAA;;EAGA,MAAA;AAAA;ALtBF;;;;;;;;;;AAUe;AAIf;;;;AAAgC;;;;ACoChC;;;;;;;;;;ADlDA,cKsDa,WAAA,EAAW,cAAA,CAAA,kBAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forinda/kickjs",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "A production-grade, decorator-driven Node.js framework built on Express 5 and TypeScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kickjs",
|
|
@@ -145,10 +145,10 @@
|
|
|
145
145
|
"@types/cookie-parser": "^1.4.10",
|
|
146
146
|
"@types/express": "^5.0.6",
|
|
147
147
|
"@types/multer": "^2.1.0",
|
|
148
|
-
"@types/node": "^25.
|
|
148
|
+
"@types/node": "^25.7.0",
|
|
149
149
|
"typescript": "^6.0.3",
|
|
150
|
-
"vite": "^8.0.
|
|
151
|
-
"vitest": "^4.1.
|
|
150
|
+
"vite": "^8.0.12",
|
|
151
|
+
"vitest": "^4.1.6"
|
|
152
152
|
},
|
|
153
153
|
"publishConfig": {
|
|
154
154
|
"access": "public"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-CoJp35rz.d.mts","names":[],"sources":["../src/core/augmentation.ts","../src/core/adapter.ts"],"mappings":";;;;;;;;;;;;;;;AAqBA;;;;;AAUA;;;;;AAcA;;;UAxBiB,oBAAA;AA+EjB;;;;;;;;AAAA,KArEY,gBAAA,SAAyB,oBAAA,gCAE3B,oBAAA;;;;AClBV;;;;;AAGA;;UD2BiB,gBAAA;ECvBQ;;;;ED4BvB,WAAA;EC1BI;;AAgBN;;;;EDiBE,OAAA;AAAA;;;;;;;;;;;;;;ACwBF;;;;;;;;;;;;;;;;;;;;;;;;;;;iBDmBgB,kBAAA,CAAmB,IAAA,UAAc,IAAA,GAAO,gBAAA;;;;AA/ExD;;;;;AAUA;;KChBY,eAAA;;UAGK,iBAAA;ED2BA;ECzBf,OAAA;;EAEA,KAAA,GAAQ,eAAA;EDmCD;ECjCP,IAAA;AAAA;;;;;;;;;;;AATF;;;UAyBiB,cAAA;EAzBU;EA2BzB,GAAA,EAAK,OAAA;EAxB2B;EA0BhC,SAAA,EAAW,SAAA;EAtBY;EAwBvB,MAAA,GAAS,IAAA,CAAK,MAAA;EAxBd;EA0BA,GAAA;EAxBA;EA0BA,YAAA;AAAA;AAVF;;;;;;;;;;;;;;;;;;;AAyCA;;;;;;;;;AAzCA,UAyCiB,UAAA;EAsE2B;;;;;EAhE1C,IAAA;EAuGgC;;;;;;;;;;;;;;;;;;;EAlFhC,SAAA,YAAqB,gBAAA;EA2CqB;;;;EArC1C,UAAA,KAAe,iBAAA;EA2Df;;;;;;;;;EAhDA,YAAA,KAAiB,wBAAA;EA6EL;AAId;;;EA3EE,WAAA,EAAa,GAAA,EAAK,cAAA,UAAwB,OAAA;EA2EM;;;;EArEhD,YAAA,EAAc,eAAA,EAAiB,WAAA,EAAa,SAAA;;;;;;;;;;;;;EAc5C,WAAA,EAAa,GAAA,EAAK,cAAA,UAAwB,OAAA;;;;;;;;;;;;;;;EAgB1C,UAAA,EAAY,GAAA,EAAK,cAAA,UAAwB,OAAA;;;;;EAMzC,aAAA,KAAkB,OAAA;IAAU,IAAA;IAAc,MAAA;EAAA;;EAG1C,QAAA,KAAa,YAAA;;;;;;;;;;;;;EAcb,UAAA,eAAyB,OAAA;;;;;;;;;;;EAYzB,YAAA;AAAA;;KAIU,eAAA,aAA4B,UAAA"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @forinda/kickjs v5.7.0
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) Felix Orinda
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* LICENSE file in the root directory of this source tree.
|
|
8
|
-
*
|
|
9
|
-
* @license MIT
|
|
10
|
-
*/
|
|
11
|
-
import{n as tokenName,t as Container}from"./container-COmj1vM3.mjs";import{METADATA}from"./core/interfaces.mjs";import{Logger,createLogger}from"./core/logger.mjs";import{t as getClassMeta}from"./metadata-D9F5Lm-z.mjs";import{g as MutableModuleRegistry,s as mountSort}from"./core-CJKmg8QR.mjs";import{normalizePath}from"./core/path.mjs";import{requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore}from"./context-CvJQzbBF.mjs";import{_setExternalContributorSources,buildRoutes}from"./http/router-builder.mjs";import http from"node:http";import express from"express";import crypto from"node:crypto";const REQUEST_SCOPE_MIDDLEWARE_MARKER=Symbol.for(`@kickjs/requestScopeMiddleware`);function requestScopeMiddleware(){let mw=(req,_res,next)=>{let requestIdHeader=req.headers[`x-request-id`],requestId=(Array.isArray(requestIdHeader)?requestIdHeader[0]:requestIdHeader)||crypto.randomUUID(),store={requestId,instances:new Map,values:new Map};req.requestId=requestId,requestStore.run(store,()=>next())};return mw[REQUEST_SCOPE_MIDDLEWARE_MARKER]=!0,mw}function isRequestScopeMiddleware(value){return typeof value==`function`?value[REQUEST_SCOPE_MIDDLEWARE_MARKER]===!0:!1}const log=createLogger(`Application`);var Application=class{app;container;httpServer=null;adapters;plugins;_inFlightRequests=0;_draining=!1;_shutdownInitiated=!1;_drainResolvers=[];constructor(options){this.options=options,this.app=express(),this.container=Container.getInstance(),this.plugins=mountSort(options.plugins??[],`plugin`);let allAdapters=[...this.plugins.flatMap(p=>p.adapters?.()??[]),...options.adapters??[]],anonAdapterCount=0,namedAdapters=allAdapters.map(adapter=>{if(!adapter.name){let ctorName=adapter.constructor?.name,fallback=ctorName&&ctorName!==`Object`?ctorName:`AnonymousAdapter#${anonAdapterCount++}`;return Object.assign(adapter,{name:fallback})}return adapter});this.adapters=mountSort(namedAdapters,`adapter`),Container._requestStoreProvider=()=>requestStore.getStore()??null,Logger._contextProvider=()=>{let store=requestStore.getStore();if(!store)return null;let ctx={requestId:store.requestId},traceId=store.values.get(`traceId`);traceId&&(ctx.traceId=traceId);let spanId=store.values.get(`spanId`);return spanId&&(ctx.spanId=spanId),ctx}}get isDraining(){return this._draining}get inFlightRequests(){return this._inFlightRequests}getContainer(){return this.container}handle(req,res,next){next?this.app(req,res,next):this.app(req,res)}adapterCtx(server){let env=process.env.NODE_ENV??`development`;return{app:this.app,container:this.container,server,env,isProduction:env===`production`}}async callHook(hook,ctx){if(hook)try{let result=hook(ctx);result&&typeof result.then==`function`&&await result}catch(err){log.error(err,`Adapter hook failed`)}}async setup(){log.debug(`Bootstrapping application...`);let adapterMw=this.collectAdapterMiddleware();this.app.__kickApp=this;let ctx=this.adapterCtx();for(let adapter of this.adapters)await this.callHook(adapter.beforeMount?.bind(adapter),ctx);this.app.disable(`x-powered-by`),this.app.set(`trust proxy`,this.options.trustProxy??!1),this.app.use(this.requestTrackingMiddleware()),this.mountHealthEndpoints(),this.shouldAutoMountRequestScope()&&this.app.use(requestScopeMiddleware()),this.mountMiddlewareList(adapterMw.beforeGlobal);for(let plugin of this.plugins)plugin.register?.(this.container);for(let plugin of this.plugins)try{let mw=plugin.middleware?.()??[];for(let handler of mw)this.app.use(handler)}catch(err){log.error(err,`Plugin middleware failed: ${plugin.name??`unknown`}`)}if(this.options.security?.helmet!==!1)try{let{helmet:helmetFn}=await import(`./http/middleware/helmet.mjs`);this.app.use(helmetFn())}catch{}if(this.options.middleware)for(let entry of this.options.middleware)this.mountMiddlewareEntry(entry);else this.app.use(requestId()),this.app.use(express.json({limit:this.options.jsonLimit??`1mb`}));this.mountMiddlewareList(adapterMw.afterGlobal);let moduleRegistry=new MutableModuleRegistry;for(let plugin of this.plugins){for(let m of plugin.modules?.()??[])moduleRegistry.mount(m);plugin.setup?.(moduleRegistry)}for(let m of this.options.modules)moduleRegistry.mount(m);this.options.setup?.(moduleRegistry);let modules=moduleRegistry.entries.map(entry=>{let mod=typeof entry==`function`?new entry:entry;return mod.register?.(this.container),mod});this.container.bootstrap(),this.mountMiddlewareList(adapterMw.beforeRoutes);let apiPrefix=this.options.apiPrefix??`/api`,defaultVersion=this.options.defaultVersion??1,shouldLogRoutes=this.options.logRoutesTable??process.env.NODE_ENV!==`production`,mountedRoutes=[],adapterSources=[];for(let adapter of this.adapters){let adapterContribs=adapter.contributors?.()??[],adapterLabel=adapter.name??adapter.constructor.name??`adapter`;for(let registration of adapterContribs)adapterSources.push({source:`adapter`,registration,label:adapterLabel})}for(let plugin of this.plugins){let pluginContribs=plugin.contributors?.()??[],pluginLabel=plugin.name??plugin.constructor?.name??`plugin`;for(let registration of pluginContribs)adapterSources.push({source:`adapter`,registration,label:pluginLabel})}let globalSources=(this.options.contributors??[]).map(registration=>({source:`global`,registration,label:`bootstrap`}));for(let mod of modules){_setExternalContributorSources([...(mod.contributors?.()??[]).map(registration=>({source:`module`,registration,label:mod.constructor?.name??`module`})),...adapterSources,...globalSources]);let result;try{result=mod.routes()}finally{_setExternalContributorSources([])}if(!result)continue;let routeSets=Array.isArray(result)?result:[result];for(let route of routeSets){let mountPath=`${apiPrefix}/v${route.version??defaultVersion}${normalizePath(route.path)}`,router=route.router??buildRoutes(route.controller);if(!router)throw Error(`Module route at ${mountPath} requires either 'controller' or 'router'. Pass \`controller: SomeController\` for the common case (the framework calls buildRoutes() internally) or \`router: yourRouter\` to hand-build it.`);if(this.app.use(mountPath,router),route.controller){for(let adapter of this.adapters)try{adapter.onRouteMount?.(route.controller,mountPath)}catch(err){log.error(err,`adapter.onRouteMount() failed for ${mountPath}`)}shouldLogRoutes&&mountedRoutes.push({controller:route.controller,mountPath})}}}if(shouldLogRoutes&&mountedRoutes.length>0){let methodOrder=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],methodRank=m=>{let i=methodOrder.indexOf(m);return i===-1?99:i},totalRoutes=0;log.debug(`Routes:`);for(let{controller,mountPath}of mountedRoutes){let defs=getClassMeta(METADATA.ROUTES,controller,[]);if(defs.length===0)continue;let counts={};for(let def of defs){let m=def.method.toUpperCase();counts[m]=(counts[m]||0)+1}totalRoutes+=defs.length;let methods=Object.entries(counts).toSorted(([a],[b])=>methodRank(a)-methodRank(b)).map(([m,n])=>`${n} ${m}`).join(`, `),name=controller.name||`Controller`;log.debug(` ${name.padEnd(30)} ${mountPath.padEnd(25)} ${defs.length} routes (${methods})`)}log.debug(` Total: ${totalRoutes} routes`)}this.mountMiddlewareList(adapterMw.afterRoutes),this.app.use(this.options.onNotFound??notFoundHandler()),this.app.use(this.options.onError??errorHandler());for(let adapter of this.adapters)await this.callHook(adapter.beforeStart?.bind(adapter),ctx)}async registerOnly(){await this.setup()}async start(){await this.setup();let g=globalThis;if(g.__kickjs_httpServer){this.httpServer=g.__kickjs_httpServer,log.debug(`Attached to Vite dev server`);for(let adapter of this.adapters){let ctx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),ctx)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);return}let port=this.options.port??parseInt(process.env.PORT||`3000`,10);this.httpServer=http.createServer(this.app),this.httpServer.on(`error`,err=>{throw err.code===`EADDRINUSE`&&(log.error(`Port ${port} is already in use. Kill the existing process or use a different port:\n PORT=${port+1} kick dev\n lsof -i :${port} # find what's using it\n kill <PID> # stop it`),process.exit(1)),err}),await new Promise((resolve,reject)=>{this.httpServer.listen(port,async()=>{try{log.info(`Server running on http://localhost:${port}`);for(let adapter of this.adapters){let afterCtx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),afterCtx)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);resolve()}catch(err){reject(err)}})})}async rebuild(){let prevApp=this.app,prevContainer=this.container;try{Container.reset(),this.container=Container.getInstance(),this.app=express(),await this.setup()}catch(err){log.error(err,`HMR rebuild failed, keeping previous app`),this.app=prevApp,this.container=prevContainer;return}this.httpServer&&(this.httpServer.removeAllListeners(`request`),this.httpServer.on(`request`,this.app),log.debug(`HMR: Express app rebuilt and swapped`))}async shutdown(){if(this._shutdownInitiated){log.debug(`Shutdown already in progress, skipping duplicate call`);return}this._shutdownInitiated=!0,this._draining=!0,log.debug(`Shutting down — draining in-flight requests...`);let timeoutMs=this.options.shutdownTimeout??3e4,timer,forceExitPromise=timeoutMs>0?new Promise(resolve=>{timer=setTimeout(()=>resolve(`timeout`),timeoutMs),timer.unref()}):new Promise(()=>{});try{if(this.httpServer&&this.httpServer.close(()=>{}),this._inFlightRequests>0){log.debug(`Waiting for ${this._inFlightRequests} in-flight request(s) to complete...`);let drainPromise=new Promise(resolve=>{this._drainResolvers.push(()=>resolve(`drained`))});await Promise.race([drainPromise,forceExitPromise])===`timeout`?log.warn(`Shutdown timeout (${timeoutMs}ms) reached with ${this._inFlightRequests} request(s) still in-flight, forcing shutdown`):log.debug(`All in-flight requests completed`)}let results=await Promise.allSettled([...this.plugins.map(plugin=>Promise.resolve(plugin.shutdown?.())),...this.adapters.map(adapter=>Promise.resolve(adapter.shutdown?.()))]);for(let result of results)result.status===`rejected`&&log.error({err:result.reason},`Adapter shutdown failed`)}finally{timer&&clearTimeout(timer)}}getExpressApp(){return this.app}getAdapters(){return this.adapters}getPlugins(){return this.plugins}getContributors(){let out=[],ingest=(list,source,label)=>{if(list)for(let reg of list){let r=reg;if(r.key===void 0||r.key===null)continue;let key=typeof r.key==`string`?r.key:tokenName(r.key);out.push({key,source,label,dependsOn:Array.isArray(r.dependsOn)?r.dependsOn:[]})}};for(let adapter of this.adapters)ingest(adapter.contributors?.(),`adapter`,adapter.name??`adapter`);for(let plugin of this.plugins)ingest(plugin.contributors?.(),`plugin`,plugin.name??`plugin`);return ingest(this.options.contributors,`global`,`bootstrap`),out}getHttpServer(){return this.httpServer}shouldAutoMountRequestScope(){if(this.options.contextStore===`manual`)return!1;let userEntries=this.options.middleware??[];for(let entry of userEntries)if(isRequestScopeMiddleware(typeof entry==`function`?entry:entry.handler))return!1;return!0}collectAdapterMiddleware(){let result={beforeGlobal:[],afterGlobal:[],beforeRoutes:[],afterRoutes:[]};for(let adapter of this.adapters){let entries=adapter.middleware?.()??[];for(let entry of entries)result[entry.phase??`afterGlobal`].push(entry)}return result}mountMiddlewareList(entries){for(let entry of entries)entry.path?this.app.use(entry.path,entry.handler):this.app.use(entry.handler)}mountMiddlewareEntry(entry){typeof entry==`function`?this.app.use(entry):this.app.use(entry.path,entry.handler)}requestTrackingMiddleware(){return(_req,res,next)=>{this._inFlightRequests++;let onFinish=()=>{if(res.removeListener(`finish`,onFinish),res.removeListener(`close`,onFinish),this._inFlightRequests--,this._draining&&this._inFlightRequests===0){for(let resolve of this._drainResolvers)resolve();this._drainResolvers=[]}};res.on(`finish`,onFinish),res.on(`close`,onFinish),next()}}mountHealthEndpoints(){this.app.get(`/health/live`,(_req,res)=>{this._draining?res.status(503).json({status:`draining`,uptime:process.uptime()}):res.json({status:`ok`,uptime:process.uptime()})}),this.app.get(`/health/ready`,async(_req,res)=>{if(this._draining){res.status(503).json({status:`draining`,checks:[]});return}let adaptersWithHealth=this.adapters.filter(a=>a.onHealthCheck),results=(await Promise.allSettled(adaptersWithHealth.map(a=>a.onHealthCheck()))).map((c,i)=>c.status===`fulfilled`?c.value:{name:adaptersWithHealth[i].name??`unknown`,status:`down`}),healthy=results.every(r=>r.status===`up`);res.status(healthy?200:503).json({status:healthy?`ready`:`degraded`,checks:results})})}};export{requestScopeMiddleware as n,Application as t};
|
|
12
|
-
//# sourceMappingURL=application-Bg_-CIPK.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"application-Bg_-CIPK.mjs","names":[],"sources":["../src/http/middleware/request-scope.ts","../src/http/application.ts"],"sourcesContent":["import crypto from 'node:crypto'\nimport type { Request, Response, NextFunction } from 'express'\nimport { requestStore, type RequestStore } from '../request-store'\n\n/**\n * Marker symbol stamped on the function returned by {@link requestScopeMiddleware}.\n *\n * `Application.setup()` uses {@link isRequestScopeMiddleware} to detect whether\n * the user-supplied middleware list already contains the request-scope wrapper,\n * and skips its automatic auto-mount when one is found. Lets adopters control\n * the exact position of the ALS frame in their pipeline (e.g., after a custom\n * tracing wrapper) without ending up with two nested frames.\n */\n// `Symbol.for(...)` returns a plain `symbol` (not `unique symbol`), but the\n// runtime detection marker doesn't need the unique nominal property — it only\n// needs the interned-slot reference equality that `Symbol.for` already provides.\nconst REQUEST_SCOPE_MIDDLEWARE_MARKER = Symbol.for('@kickjs/requestScopeMiddleware')\n\n/**\n * Wraps each request in an AsyncLocalStorage context.\n * Enables REQUEST-scoped DI and automatic requestId propagation to logs.\n *\n * Should be mounted early in the middleware pipeline (before route handlers).\n * `Application` mounts this automatically unless `contextStore: 'manual'` is set\n * or the user already includes one in their middleware list.\n */\nexport function requestScopeMiddleware() {\n const mw = (req: Request, _res: Response, next: NextFunction) => {\n const requestIdHeader = req.headers['x-request-id']\n const requestId =\n (Array.isArray(requestIdHeader) ? requestIdHeader[0] : requestIdHeader) || crypto.randomUUID()\n const store: RequestStore = {\n requestId,\n instances: new Map(),\n // Explicit generic locks the \"string keys only\" decision\n // (architecture.md §20.12 #4) at construction time, so accidental\n // non-string keys can't slip in via the inferred Map<any, any>.\n values: new Map<string, unknown>(),\n }\n // Surface the requestId on req so the standalone requestId() middleware\n // (when also mounted) reuses this value instead of generating a divergent\n // second one. Without this, log lines that read from the ALS store would\n // disagree with the X-Request-Id response header for any request that\n // didn't carry an inbound x-request-id header.\n ;(req as Request & { requestId?: string }).requestId = requestId\n requestStore.run(store, () => next())\n }\n ;(mw as unknown as Record<symbol, unknown>)[REQUEST_SCOPE_MIDDLEWARE_MARKER] = true\n return mw\n}\n\n/**\n * Returns `true` when `value` is the function produced by {@link requestScopeMiddleware}.\n *\n * Detection uses a `Symbol.for(...)` marker so the check survives different\n * copies of `@forinda/kickjs` loaded under different module identities (e.g.,\n * a published package alongside a workspace-linked one during local dev).\n */\nexport function isRequestScopeMiddleware(value: unknown): boolean {\n if (typeof value !== 'function') return false\n return (value as unknown as Record<symbol, unknown>)[REQUEST_SCOPE_MIDDLEWARE_MARKER] === true\n}\n","import http from 'node:http'\nimport express, { type Express, type RequestHandler } from 'express'\nimport {\n Container,\n createLogger,\n Logger,\n normalizePath,\n tokenName,\n METADATA,\n type AppModule,\n type AppModuleClass,\n type AppModuleEntry,\n type AppAdapter,\n type AdapterContext,\n type AdapterMiddleware,\n type ContributorRegistrations,\n type KickPlugin,\n type ModuleRegistry,\n type RouteDefinition,\n type SourcedRegistration,\n mountSort,\n MutableModuleRegistry,\n} from '../core'\nimport { getClassMeta } from '../core/metadata'\nimport { requestId } from './middleware/request-id'\nimport { notFoundHandler, errorHandler } from './middleware/error-handler'\nimport { requestScopeMiddleware, isRequestScopeMiddleware } from './middleware/request-scope'\nimport { _setExternalContributorSources, buildRoutes } from './router-builder'\nimport { requestStore } from './request-store'\n\nconst log = createLogger('Application')\n\n/**\n * A middleware entry in the declarative pipeline.\n * Can be a bare handler or an object with path scoping.\n */\nexport type MiddlewareEntry = RequestHandler | { path: string; handler: RequestHandler }\n\nexport interface ApplicationOptions {\n /**\n * Feature modules to load. Accepts both class form\n * (`SomeModule extends AppModule`, instantiated via `new`) and\n * instance form (output of {@link defineModule}'s factory call,\n * e.g. `TasksModule({ scope: 'admin' })`) — the loader discriminates\n * and either `new`-s the class or uses the instance directly.\n *\n * Static path — for conditional / dynamic registration, use the\n * {@link ApplicationOptions.setup} callback instead.\n */\n modules: AppModuleEntry[]\n\n /**\n * Imperative module registration callback. Receives a\n * {@link ModuleRegistry} you call `.mount(module)` on to register\n * modules conditionally — based on env flags, runtime config, a\n * tenant list, etc. Runs after every plugin's `setup()` hook and\n * after the static `modules: [...]` array is collected.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules: [HelloModule()],\n * setup(registry) {\n * if (env.ENABLE_ADMIN) registry.mount(AdminModule())\n * },\n * })\n * ```\n */\n setup?(registry: ModuleRegistry): void\n /** Adapters that hook into the lifecycle (DB, Redis, Swagger, etc.) */\n adapters?: AppAdapter[]\n /** Server port (falls back to PORT env var, then 3000) */\n port?: number\n /** Global API prefix (default: '/api') */\n apiPrefix?: string\n /** Default API version (default: 1) — routes become /{prefix}/v{version}/{path} */\n defaultVersion?: number\n\n /**\n * Global middleware pipeline. Declared in order.\n * Replaces the hardcoded middleware stack — you control exactly what runs.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * middleware: [\n * helmet(),\n * cors(),\n * compression(),\n * morgan('dev'),\n * express.json({ limit: '1mb' }),\n * ],\n * })\n * ```\n *\n * If omitted, a sensible default is applied:\n * requestId(), express.json({ limit: '100kb' })\n */\n middleware?: MiddlewareEntry[]\n\n /** Plugins that bundle modules, adapters, middleware, and DI bindings */\n plugins?: KickPlugin[]\n\n /**\n * Global Context Contributors (#107) that apply to every route in the\n * application. Merge into the per-route pipeline at the `'global'`\n * precedence level — they lose to module, adapter, class, and method\n * contributors with the same key, providing app-wide defaults that any\n * narrower scope can override.\n *\n * @example\n * ```ts\n * const StartedAt = defineContextDecorator({\n * key: 'requestStartedAt',\n * resolve: () => Date.now(),\n * })\n *\n * bootstrap({\n * modules,\n * contributors: [StartedAt.registration],\n * })\n * ```\n */\n contributors?: ContributorRegistrations\n\n /**\n * Backing store strategy for {@link RequestContext} `set/get` and the\n * Context Contributor pipeline (#107).\n *\n * - `'auto'` (default) — Application mounts {@link requestScopeMiddleware}\n * automatically before any user middleware. If the user-supplied\n * `middleware` list already includes `requestScopeMiddleware()`,\n * detection skips the auto-mount so adopters can control its position.\n * - `'manual'` — Application never mounts the wrapper. The Context\n * Contributor pipeline still runs on every route — the runner is\n * inserted by `router-builder` regardless of ALS state. What\n * degrades without an ALS frame is the *backing store*:\n * REQUEST-scoped DI throws (no `requestStore.getStore()` to read\n * from), `Logger` loses its requestId context, and\n * `RequestContext.set/get` throws. Use `'manual'` only when you\n * genuinely intend to wrap requests in your own ALS frame (rare —\n * multi-tenant adapters used to do this; post-Phase 3 they share\n * the framework's frame instead).\n */\n contextStore?: 'auto' | 'manual'\n\n /**\n * Controls whether KickJS registers process-level handlers at bootstrap.\n *\n * - `'auto'` (default) — register `uncaughtException` /\n * `unhandledRejection` loggers AND `SIGINT` / `SIGTERM` →\n * `app.shutdown()` → `process.exit(0)`.\n * - `'errors-only'` — register the error loggers; skip the signal\n * handlers. Use this when you have your own shutdown choreographer\n * (OpenTelemetry SDK, Sentry, custom orchestrator) that calls\n * `app.shutdown()` itself and you don't want two listeners racing\n * to call `process.exit`.\n * - `'manual'` — skip everything. Adopter is responsible for both\n * error logging and signal-driven shutdown.\n *\n * Background: many observability SDKs install their own SIGTERM\n * handler to flush spans/metrics before the process exits. Two\n * separate handlers calling `process.exit(0)` race each other and\n * can truncate the in-flight flush. Setting this to `'errors-only'`\n * or `'manual'` lets the SDK own the lifecycle while still letting\n * KickJS surface uncaught exceptions / rejections via the logger.\n *\n * The dev mode auto-detect (`__kickjs_httpServer` set by the Vite\n * plugin) suppresses signal registration regardless of this option,\n * since Vite owns the lifecycle in development.\n */\n processHooks?: 'auto' | 'errors-only' | 'manual'\n\n /** Express `trust proxy` setting */\n trustProxy?: boolean | number | string | ((ip: string, hopIndex: number) => boolean)\n /** Maximum JSON body size (only used when middleware is not provided) */\n jsonLimit?: string | number\n /**\n * Log route summary on startup. Default: true in dev, false in production.\n * Set to `true` to always log, `false` to always suppress.\n */\n logRoutesTable?: boolean\n /**\n * Maximum time (ms) to wait for graceful shutdown before forcing exit.\n * Default: 30000 (30 seconds). Set to 0 to disable forced exit.\n */\n shutdownTimeout?: number\n\n /**\n * Enable cluster mode for multi-core utilization.\n *\n * When enabled, the primary process forks worker processes that share the\n * same port via Node's built-in `cluster` module (OS load balancing).\n *\n * - `true` — use all available CPU cores\n * - `{ workers: N }` — use exactly N workers\n *\n * Workers are auto-restarted on crash. SIGTERM/SIGINT on the primary is\n * forwarded to all workers.\n *\n * @example\n * ```ts\n * bootstrap({ modules, cluster: true })\n * bootstrap({ modules, cluster: { workers: 4 } })\n * ```\n */\n cluster?: boolean | { workers?: number }\n\n /**\n * Custom 404 handler for unmatched routes. Receives the raw Express\n * `(req, res, next)` args. When omitted, the built-in handler returns\n * `{ message: 'Not Found' }` with status 404.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * onNotFound: (req, res) => {\n * res.status(404).json({ error: 'Route not found', path: req.originalUrl })\n * },\n * })\n * ```\n */\n onNotFound?: (req: any, res: any, next: any) => void\n\n /**\n * Custom global error handler. Receives `(err, req, res, next)` — the\n * standard Express error-handling signature. When omitted, the built-in\n * handler formats ZodError, HttpException, and unexpected errors.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * onError: (err, req, res, next) => {\n * logger.error(err)\n * res.status(err.status ?? 500).json({ error: err.message })\n * },\n * })\n * ```\n */\n onError?: (err: any, req: any, res: any, next: any) => void\n\n /**\n * Security defaults applied automatically unless opted out.\n *\n * When not specified, secure defaults are applied:\n * - Helmet security headers are auto-injected\n * - JSON body limit defaults to 1MB\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * security: {\n * helmet: false, // disable auto-helmet\n * },\n * })\n * ```\n */\n security?: {\n /** Auto-inject helmet security headers. Default: true */\n helmet?: boolean\n }\n}\n\n/**\n * The main application class. Wires together Express, the DI container,\n * feature modules, adapters, and the middleware pipeline.\n */\nexport class Application {\n private app: Express\n private container: Container\n private httpServer: http.Server | null = null\n private readonly adapters: AppAdapter[]\n\n private readonly plugins: KickPlugin[]\n\n /** Number of HTTP requests currently being processed */\n private _inFlightRequests = 0\n /** Whether the application is draining (shutting down gracefully) */\n private _draining = false\n /** Whether shutdown has already been initiated (prevents double-shutdown) */\n private _shutdownInitiated = false\n /** Resolvers waiting for in-flight requests to reach zero */\n private _drainResolvers: Array<() => void> = []\n\n constructor(private readonly options: ApplicationOptions) {\n this.app = express()\n this.container = Container.getInstance()\n\n // Sort plugins by `dependsOn` declarations BEFORE reading their adapters/etc.\n // Plugins without `dependsOn` keep their declaration order — this is a\n // pure refinement; no behaviour change for apps that don't use the field.\n this.plugins = mountSort(options.plugins ?? [], 'plugin')\n\n // Build adapter list from plugin adapters + user adapters, synthesize\n // a stable name for any anonymous adapter (so duplicate-name detection\n // and `dependsOn` resolution have something to key on), then sort by\n // adapter `dependsOn`. Plugin-shipped adapters keep their plugin's\n // relative order unless their own `dependsOn` says otherwise.\n const allAdapters = [\n ...this.plugins.flatMap((p) => p.adapters?.() ?? []),\n ...(options.adapters ?? []),\n ]\n let anonAdapterCount = 0\n const namedAdapters: Array<AppAdapter & { name: string }> = allAdapters.map((adapter) => {\n if (!adapter.name) {\n // `constructor.name === 'Object'` for plain object-literal adapters —\n // useless as an identity since two anonymous literals would collide.\n // Only borrow the constructor name when it's an actual class.\n const ctorName = adapter.constructor?.name\n const fallback =\n ctorName && ctorName !== 'Object' ? ctorName : `AnonymousAdapter#${anonAdapterCount++}`\n return Object.assign(adapter, { name: fallback })\n }\n return adapter as AppAdapter & { name: string }\n })\n this.adapters = mountSort(namedAdapters, 'adapter')\n // Wire the request store provider so Container can resolve REQUEST-scoped deps\n Container._requestStoreProvider = () => requestStore.getStore() ?? null\n // Wire logger context provider so logs auto-include requestId\n Logger._contextProvider = () => {\n const store = requestStore.getStore()\n if (!store) return null\n const ctx: Record<string, any> = { requestId: store.requestId }\n const traceId = store.values.get('traceId')\n if (traceId) ctx.traceId = traceId\n const spanId = store.values.get('spanId')\n if (spanId) ctx.spanId = spanId\n return ctx\n }\n }\n\n /** Whether the application is currently draining in-flight requests */\n get isDraining(): boolean {\n return this._draining\n }\n\n /** Number of HTTP requests currently being processed */\n get inFlightRequests(): number {\n return this._inFlightRequests\n }\n\n /** Get the DI container instance */\n getContainer(): Container {\n return this.container\n }\n\n /**\n * Express request handler — delegates to the internal Express app.\n *\n * Used by the Vite dev-server plugin to route requests through KickJS:\n * ```ts\n * const mod = await ssrLoadModule('virtual:kickjs/app')\n * mod.app.handle(req, res, next)\n * ```\n *\n * Also works as a standard Node.js request handler for production:\n * ```ts\n * http.createServer(app.handle.bind(app))\n * ```\n */\n handle(req: http.IncomingMessage, res: http.ServerResponse, next?: (err?: any) => void): void {\n if (next) {\n this.app(req as any, res as any, next)\n } else {\n this.app(req as any, res as any)\n }\n }\n\n /**\n * Full setup pipeline:\n * 1. Adapter beforeMount hooks (early routes — docs, health)\n * 2. Adapter middleware (phase: beforeGlobal)\n * 3. Global middleware (user-declared or defaults)\n * 4. Adapter middleware (phase: afterGlobal)\n * 5. Module registration + DI bootstrap\n * 6. Adapter middleware (phase: beforeRoutes)\n * 7. Module route mounting\n * 8. Adapter middleware (phase: afterRoutes)\n * 9. Error handlers (notFound + global)\n * 10. Adapter beforeStart hooks\n */\n /** Build the adapter context object (shared across all hooks) */\n private adapterCtx(server?: any): AdapterContext {\n const env = process.env.NODE_ENV ?? 'development'\n return {\n app: this.app,\n container: this.container,\n server,\n env,\n isProduction: env === 'production',\n }\n }\n\n /** Call an adapter hook, awaiting async hooks and catching errors */\n private async callHook(\n hook: ((ctx: AdapterContext) => void | Promise<void>) | undefined,\n ctx: AdapterContext,\n ): Promise<void> {\n if (!hook) return\n try {\n const result = hook(ctx)\n if (result && typeof (result as Promise<void>).then === 'function') {\n await result\n }\n } catch (err) {\n log.error(err, 'Adapter hook failed')\n }\n }\n\n async setup(): Promise<void> {\n log.debug('Bootstrapping application...')\n\n // Collect adapter middleware by phase\n const adapterMw = this.collectAdapterMiddleware()\n\n // Expose the Application instance on the Express app for adapter discovery\n ;(this.app as any).__kickApp = this\n\n const ctx = this.adapterCtx()\n\n // ── 1. Adapter beforeMount hooks ──────────────────────────────────\n for (const adapter of this.adapters) {\n await this.callHook(adapter.beforeMount?.bind(adapter), ctx)\n }\n\n // ── 2. Hardened defaults ──────────────────────────────────────────\n this.app.disable('x-powered-by')\n this.app.set('trust proxy', this.options.trustProxy ?? false)\n\n // ── 2a. In-flight request tracking ──────────────────────────────\n this.app.use(this.requestTrackingMiddleware())\n\n // ── 2b. Health check endpoints (before middleware, at root) ──────\n this.mountHealthEndpoints()\n\n // ── 2c. Request scope (AsyncLocalStorage) ────────────────────────\n // Auto-mounted unless the user opted out (`contextStore: 'manual'`)\n // or already included one in their middleware list.\n if (this.shouldAutoMountRequestScope()) {\n this.app.use(requestScopeMiddleware())\n }\n\n // ── 3. Adapter middleware: beforeGlobal ───────────────────────────\n this.mountMiddlewareList(adapterMw.beforeGlobal)\n\n // ── 3b. Plugin registration ──────────────────────────────────────\n for (const plugin of this.plugins) {\n plugin.register?.(this.container)\n }\n\n // ── 3c. Plugin middleware ─────────────────────────────────────────\n for (const plugin of this.plugins) {\n try {\n const mw = plugin.middleware?.() ?? []\n for (const handler of mw) {\n this.app.use(handler)\n }\n } catch (err) {\n log.error(err, `Plugin middleware failed: ${(plugin as any).name ?? 'unknown'}`)\n }\n }\n\n // ── 4. Global middleware ─────────────────────────────────────────\n // Auto-inject helmet unless opted out\n const autoHelmet = this.options.security?.helmet !== false\n if (autoHelmet) {\n try {\n const { helmet: helmetFn } = await import('./middleware/helmet')\n this.app.use(helmetFn())\n } catch {\n // helmet middleware not available — skip silently\n }\n }\n\n if (this.options.middleware) {\n // User-declared pipeline — full control\n for (const entry of this.options.middleware) {\n this.mountMiddlewareEntry(entry)\n }\n } else {\n // Sensible defaults when no middleware declared\n this.app.use(requestId())\n this.app.use(express.json({ limit: this.options.jsonLimit ?? '1mb' }))\n }\n\n // ── 5. Adapter middleware: afterGlobal ────────────────────────────\n this.mountMiddlewareList(adapterMw.afterGlobal)\n\n // ── 6. Module registration + DI bootstrap ────────────────────────\n // Module collection — the static `modules: [...]` array AND the\n // imperative `setup(registry)` callback feed into the same\n // ordered list. Plugins go first so plugin modules / setup\n // calls run before user code (existing precedence).\n //\n // Order within the registry:\n // 1. plugin static modules (`plugin.modules?()`)\n // 2. plugin setup() calls (in plugin dependsOn-sorted order)\n // 3. user static modules (`options.modules`)\n // 4. user setup() callback\n const moduleRegistry = new MutableModuleRegistry()\n for (const plugin of this.plugins) {\n for (const m of plugin.modules?.() ?? []) moduleRegistry.mount(m)\n plugin.setup?.(moduleRegistry)\n }\n for (const m of this.options.modules) moduleRegistry.mount(m)\n this.options.setup?.(moduleRegistry)\n const allModuleEntries: AppModuleEntry[] = moduleRegistry.entries\n const modules = allModuleEntries.map((entry) => {\n // Discriminate class vs instance: classes are functions whose\n // `prototype` carries the AppModule shape; defineModule output is\n // a plain object with `routes`, `register`, `contributors`. Calling\n // `new` on a plain object throws, so we branch up-front rather\n // than try/catch.\n const mod: AppModule = typeof entry === 'function' ? new (entry as AppModuleClass)() : entry\n // `register()` is optional — modules whose classes are entirely\n // decorator-managed (@Service, @Controller, @Repository) don't need it.\n mod.register?.(this.container)\n return mod\n })\n this.container.bootstrap()\n\n // ── 7. Adapter middleware: beforeRoutes ───────────────────────────\n this.mountMiddlewareList(adapterMw.beforeRoutes)\n\n // ── 8. Mount module routes with versioning ───────────────────────\n const apiPrefix = this.options.apiPrefix ?? '/api'\n const defaultVersion = this.options.defaultVersion ?? 1\n const shouldLogRoutes = this.options.logRoutesTable ?? process.env.NODE_ENV !== 'production'\n\n // Collect route metadata during mounting (avoids calling mod.routes() twice)\n const mountedRoutes: Array<{ controller: any; mountPath: string }> = []\n\n // Context Contributors (#107) — collect adapter + plugin + global once;\n // per-module sources are computed inside the loop so module isolation is\n // preserved.\n //\n // Plugin contributors merge at the same `'adapter'` precedence as adapter\n // contributors. Plugins are conceptually \"bundles of adapters + extras\",\n // so a plugin that ships a typed contributor without standing up an\n // accompanying adapter behaves identically to one that does.\n const adapterSources: SourcedRegistration[] = []\n for (const adapter of this.adapters) {\n const adapterContribs = adapter.contributors?.() ?? []\n const adapterLabel = adapter.name ?? adapter.constructor.name ?? 'adapter'\n for (const registration of adapterContribs) {\n adapterSources.push({ source: 'adapter', registration, label: adapterLabel })\n }\n }\n for (const plugin of this.plugins) {\n const pluginContribs = plugin.contributors?.() ?? []\n const pluginLabel = plugin.name ?? plugin.constructor?.name ?? 'plugin'\n for (const registration of pluginContribs) {\n adapterSources.push({ source: 'adapter', registration, label: pluginLabel })\n }\n }\n const globalSources: SourcedRegistration[] = (this.options.contributors ?? []).map(\n (registration): SourcedRegistration => ({\n source: 'global',\n registration,\n label: 'bootstrap',\n }),\n )\n\n for (const mod of modules) {\n const moduleSources: SourcedRegistration[] = (mod.contributors?.() ?? []).map(\n (registration): SourcedRegistration => ({\n source: 'module',\n registration,\n label: mod.constructor?.name ?? 'module',\n }),\n )\n\n // Thread per-module + adapter + global sources to buildRoutes via the\n // module-scoped slot. Module setup is sequential, so the slot is\n // race-free; the finally block clears it even if mod.routes() throws.\n _setExternalContributorSources([...moduleSources, ...adapterSources, ...globalSources])\n let result: ReturnType<AppModule['routes']>\n try {\n result = mod.routes()\n } finally {\n _setExternalContributorSources([])\n }\n if (!result) continue // Non-HTTP modules (queues, cron) may return null\n\n const routeSets = Array.isArray(result) ? result : [result]\n\n for (const route of routeSets) {\n const version = route.version ?? defaultVersion\n const mountPath = `${apiPrefix}/v${version}${normalizePath(route.path)}`\n // Derive the router from `controller` when one wasn't passed\n // explicitly. The common-case shape is `{ path, controller }` —\n // the framework owns `buildRoutes(controller)` so adopters don't\n // import + call it themselves. Adopters who hand-build a router\n // (composing multiple controllers, mounting third-party routers)\n // pass `router` explicitly and we use it as-is.\n const router = route.router ?? buildRoutes(route.controller)\n if (!router) {\n throw new Error(\n `Module route at ${mountPath} requires either 'controller' or 'router'. ` +\n 'Pass `controller: SomeController` for the common case (the framework calls ' +\n 'buildRoutes() internally) or `router: yourRouter` to hand-build it.',\n )\n }\n this.app.use(mountPath, router)\n\n // Notify adapters (e.g. SwaggerAdapter for OpenAPI spec generation)\n if (route.controller) {\n for (const adapter of this.adapters) {\n try {\n adapter.onRouteMount?.(route.controller, mountPath)\n } catch (err) {\n log.error(err, `adapter.onRouteMount() failed for ${mountPath}`)\n }\n }\n if (shouldLogRoutes) {\n mountedRoutes.push({ controller: route.controller, mountPath })\n }\n }\n }\n }\n\n // ── 8b. Log route summary ─────────────────────────────────────────\n if (shouldLogRoutes && mountedRoutes.length > 0) {\n const methodOrder = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n const methodRank = (m: string) => {\n const i = methodOrder.indexOf(m)\n return i === -1 ? 99 : i\n }\n\n let totalRoutes = 0\n log.debug('Routes:')\n\n for (const { controller, mountPath } of mountedRoutes) {\n const defs: RouteDefinition[] = getClassMeta<RouteDefinition[]>(\n METADATA.ROUTES,\n controller,\n [],\n )\n if (defs.length === 0) continue\n\n const counts: Record<string, number> = {}\n for (const def of defs) {\n const m = def.method.toUpperCase()\n counts[m] = (counts[m] || 0) + 1\n }\n totalRoutes += defs.length\n\n const methods = Object.entries(counts)\n .toSorted(([a], [b]) => methodRank(a) - methodRank(b))\n .map(([m, n]) => `${n} ${m}`)\n .join(', ')\n const name = controller.name || 'Controller'\n log.debug(` ${name.padEnd(30)} ${mountPath.padEnd(25)} ${defs.length} routes (${methods})`)\n }\n\n log.debug(` Total: ${totalRoutes} routes`)\n }\n\n // ── 9. Adapter middleware: afterRoutes ────────────────────────────\n this.mountMiddlewareList(adapterMw.afterRoutes)\n\n // ── 10. Error handlers ───────────────────────────────────────────\n this.app.use(this.options.onNotFound ?? notFoundHandler())\n this.app.use(this.options.onError ?? errorHandler())\n\n // ── 11. Adapter beforeStart hooks ────────────────────────────────\n for (const adapter of this.adapters) {\n await this.callHook(adapter.beforeStart?.bind(adapter), ctx)\n }\n }\n\n /** Register modules and DI without starting the HTTP server (used by kick tinker) */\n async registerOnly(): Promise<void> {\n await this.setup()\n }\n\n /**\n * Start the HTTP server.\n *\n * In **dev mode** (Vite plugin active): reuses `globalThis.__kickjs_httpServer`\n * created by Vite. Adapters (WsAdapter, Socket.IO, etc.) receive the real\n * `http.Server` through `afterStart({ server })` — zero adapter changes needed.\n *\n * In **production**: creates its own `http.Server` and binds to the port.\n */\n async start(): Promise<void> {\n await this.setup()\n\n const g = globalThis as any\n\n if (g.__kickjs_httpServer) {\n // ── DEV MODE: Vite owns the http.Server ──────────────────────\n // Don't create a new server or listen — Vite is already listening.\n // Just wire up adapters with the Vite-created server.\n this.httpServer = g.__kickjs_httpServer\n log.debug('Attached to Vite dev server')\n\n for (const adapter of this.adapters) {\n const ctx = this.adapterCtx(this.httpServer!)\n await this.callHook(adapter.afterStart?.bind(adapter), ctx)\n }\n\n for (const plugin of this.plugins) {\n await plugin.onReady?.(this.container)\n }\n\n return\n }\n\n // ── PRODUCTION: Create and own the http.Server ─────────────────\n const port = this.options.port ?? parseInt(process.env.PORT || '3000', 10)\n this.httpServer = http.createServer(this.app)\n\n this.httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n log.error(\n `Port ${port} is already in use. Kill the existing process or use a different port:\\n` +\n ` PORT=${port + 1} kick dev\\n` +\n ` lsof -i :${port} # find what's using it\\n` +\n ` kill <PID> # stop it`,\n )\n process.exit(1)\n }\n throw err\n })\n\n // Wrap listen in a Promise so afterStart/onReady errors propagate\n await new Promise<void>((resolve, reject) => {\n this.httpServer!.listen(port, async () => {\n try {\n log.info(`Server running on http://localhost:${port}`)\n\n for (const adapter of this.adapters) {\n const afterCtx = this.adapterCtx(this.httpServer!)\n await this.callHook(adapter.afterStart?.bind(adapter), afterCtx)\n }\n\n // Plugin onReady hooks\n for (const plugin of this.plugins) {\n await plugin.onReady?.(this.container)\n }\n\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n }\n\n /** HMR rebuild: swap Express handler without restarting the server */\n async rebuild(): Promise<void> {\n // Build the new app fully before swapping — if setup() throws,\n // the old app keeps running so the server stays responsive.\n const prevApp = this.app\n const prevContainer = this.container\n\n try {\n Container.reset()\n this.container = Container.getInstance()\n this.app = express()\n await this.setup()\n } catch (err) {\n log.error(err, 'HMR rebuild failed, keeping previous app')\n // Restore previous state so the server stays responsive\n this.app = prevApp\n this.container = prevContainer\n return\n }\n\n if (this.httpServer) {\n this.httpServer.removeAllListeners('request')\n this.httpServer.on('request', this.app)\n log.debug('HMR: Express app rebuilt and swapped')\n }\n }\n\n /**\n * Graceful shutdown with request draining.\n *\n * 1. Stops accepting new connections (server.close())\n * 2. Waits for in-flight requests to complete (up to shutdownTimeout)\n * 3. Calls adapter.shutdown() for all registered adapters\n * 4. Force-closes after timeout\n *\n * Safe to call multiple times — subsequent calls return the same promise.\n */\n async shutdown(): Promise<void> {\n // Prevent double-shutdown — return immediately if already initiated\n if (this._shutdownInitiated) {\n log.debug('Shutdown already in progress, skipping duplicate call')\n return\n }\n this._shutdownInitiated = true\n this._draining = true\n\n log.debug('Shutting down — draining in-flight requests...')\n\n const timeoutMs = this.options.shutdownTimeout ?? 30_000\n let timer: ReturnType<typeof setTimeout> | undefined\n\n // Start a force-exit timer if timeout is configured\n const forceExitPromise =\n timeoutMs > 0\n ? new Promise<'timeout'>((resolve) => {\n timer = setTimeout(() => resolve('timeout'), timeoutMs)\n timer.unref()\n })\n : new Promise<never>(() => {}) // never resolves — no forced exit\n\n try {\n // Step 1: Stop accepting new connections.\n // server.close() prevents new connections. Its callback fires only when\n // ALL existing connections are fully closed, so we do NOT await it here —\n // we track request draining separately via the tracking middleware.\n if (this.httpServer) {\n this.httpServer.close(() => {})\n }\n\n // Step 2: Wait for in-flight requests to drain (or timeout)\n if (this._inFlightRequests > 0) {\n log.debug(`Waiting for ${this._inFlightRequests} in-flight request(s) to complete...`)\n const drainPromise = new Promise<'drained'>((resolve) => {\n this._drainResolvers.push(() => resolve('drained'))\n })\n\n const result = await Promise.race([drainPromise, forceExitPromise])\n if (result === 'timeout') {\n log.warn(\n `Shutdown timeout (${timeoutMs}ms) reached with ${this._inFlightRequests} request(s) still in-flight, forcing shutdown`,\n )\n } else {\n log.debug('All in-flight requests completed')\n }\n }\n\n // Step 3: Run all plugin + adapter shutdowns concurrently\n const results = await Promise.allSettled([\n ...this.plugins.map((plugin) => Promise.resolve(plugin.shutdown?.())),\n ...this.adapters.map((adapter) => Promise.resolve(adapter.shutdown?.())),\n ])\n for (const result of results) {\n if (result.status === 'rejected') {\n log.error({ err: result.reason }, 'Adapter shutdown failed')\n }\n }\n } finally {\n if (timer) clearTimeout(timer)\n }\n }\n\n getExpressApp(): Express {\n return this.app\n }\n\n /** Get registered adapters — used by DevToolsAdapter for peer discovery */\n getAdapters(): AppAdapter[] {\n return this.adapters\n }\n\n /**\n * Get registered plugins — used by DevToolsAdapter for topology\n * introspection (architecture.md §23). Read-only; returns the\n * already-sorted plugin list (post-`dependsOn` topo-sort).\n */\n getPlugins(): readonly KickPlugin[] {\n return this.plugins\n }\n\n /**\n * Snapshot of every Context Contributor reachable from this app —\n * walks the four registration sites (adapters, plugins, global, and\n * the bootstrap `contributors` option). Module-level contributors\n * are NOT included; module instances aren't retained post-bootstrap.\n * The devtools Topology tab consumes this for the Contributors\n * panel; tests + adopters can call it for diagnostics.\n *\n * Result shape stays minimal — `{ key, source, label, dependsOn }`\n * — so we don't leak `resolve` closures or other internal fields.\n */\n getContributors(): ReadonlyArray<{\n key: string\n source: 'adapter' | 'plugin' | 'global'\n label: string\n dependsOn: readonly string[]\n }> {\n const out: Array<{\n key: string\n source: 'adapter' | 'plugin' | 'global'\n label: string\n dependsOn: readonly string[]\n }> = []\n const ingest = (\n list: ReadonlyArray<unknown> | null | undefined,\n source: 'adapter' | 'plugin' | 'global',\n label: string,\n ): void => {\n if (!list) return\n for (const reg of list) {\n const r = reg as { key?: unknown; dependsOn?: unknown }\n if (r.key === undefined || r.key === null) continue\n // `key` is typically a string but createToken<T>() yields a\n // branded token object — normalise via tokenName so both\n // surfaces flow through.\n const key = typeof r.key === 'string' ? r.key : tokenName(r.key)\n out.push({\n key,\n source,\n label,\n dependsOn: Array.isArray(r.dependsOn) ? (r.dependsOn as string[]) : [],\n })\n }\n }\n for (const adapter of this.adapters) {\n ingest(adapter.contributors?.(), 'adapter', adapter.name ?? 'adapter')\n }\n for (const plugin of this.plugins) {\n ingest(plugin.contributors?.(), 'plugin', plugin.name ?? 'plugin')\n }\n ingest(this.options.contributors, 'global', 'bootstrap')\n return out\n }\n\n getHttpServer(): http.Server | null {\n return this.httpServer\n }\n\n // ── Internal helpers ────────────────────────────────────────────────\n\n /**\n * Decide whether {@link Application} should auto-mount the\n * {@link requestScopeMiddleware} ALS wrapper.\n *\n * Returns `false` when:\n * - `contextStore: 'manual'` was set (caller manages ALS frames), or\n * - the user-supplied `middleware` list already includes a\n * `requestScopeMiddleware()` (detected via the symbol marker stamped\n * in `middleware/request-scope.ts`).\n *\n * Otherwise `true` — preserves the historical default of \"always wrap\n * requests in an ALS frame\" so existing apps see no behavior change.\n */\n private shouldAutoMountRequestScope(): boolean {\n if (this.options.contextStore === 'manual') return false\n\n const userEntries = this.options.middleware ?? []\n for (const entry of userEntries) {\n const handler = typeof entry === 'function' ? entry : entry.handler\n if (isRequestScopeMiddleware(handler)) return false\n }\n return true\n }\n\n private collectAdapterMiddleware() {\n const result = {\n beforeGlobal: [] as AdapterMiddleware[],\n afterGlobal: [] as AdapterMiddleware[],\n beforeRoutes: [] as AdapterMiddleware[],\n afterRoutes: [] as AdapterMiddleware[],\n }\n\n for (const adapter of this.adapters) {\n const entries = adapter.middleware?.() ?? []\n for (const entry of entries) {\n const phase = entry.phase ?? 'afterGlobal'\n result[phase].push(entry)\n }\n }\n\n return result\n }\n\n private mountMiddlewareList(entries: AdapterMiddleware[]): void {\n for (const entry of entries) {\n if (entry.path) {\n this.app.use(entry.path, entry.handler)\n } else {\n this.app.use(entry.handler)\n }\n }\n }\n\n private mountMiddlewareEntry(entry: MiddlewareEntry): void {\n if (typeof entry === 'function') {\n this.app.use(entry)\n } else {\n this.app.use(entry.path, entry.handler)\n }\n }\n\n /** Middleware that tracks in-flight requests for graceful draining */\n private requestTrackingMiddleware(): RequestHandler {\n return (_req, res, next) => {\n this._inFlightRequests++\n const onFinish = () => {\n res.removeListener('finish', onFinish)\n res.removeListener('close', onFinish)\n this._inFlightRequests--\n // If draining and no more in-flight requests, resolve all waiters\n if (this._draining && this._inFlightRequests === 0) {\n for (const resolve of this._drainResolvers) {\n resolve()\n }\n this._drainResolvers = []\n }\n }\n res.on('finish', onFinish)\n res.on('close', onFinish)\n next()\n }\n }\n\n /** Mount /health/live and /health/ready endpoints at the root (no API prefix) */\n private mountHealthEndpoints(): void {\n this.app.get('/health/live', (_req, res) => {\n if (this._draining) {\n res.status(503).json({ status: 'draining', uptime: process.uptime() })\n } else {\n res.json({ status: 'ok', uptime: process.uptime() })\n }\n })\n\n this.app.get('/health/ready', async (_req, res) => {\n if (this._draining) {\n res.status(503).json({ status: 'draining', checks: [] })\n return\n }\n const adaptersWithHealth = this.adapters.filter((a) => a.onHealthCheck)\n const checks = await Promise.allSettled(adaptersWithHealth.map((a) => a.onHealthCheck!()))\n const results = checks.map((c, i) => {\n if (c.status === 'fulfilled') return c.value\n return { name: adaptersWithHealth[i].name ?? 'unknown', status: 'down' as const }\n })\n const healthy = results.every((r) => r.status === 'up')\n res.status(healthy ? 200 : 503).json({\n status: healthy ? 'ready' : 'degraded',\n checks: results,\n })\n })\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"application-DmekGKp7.d.mts","names":[],"sources":["../src/http/application.ts"],"mappings":";;;;;;;;;;;;;;KAoCY,eAAA,GAAkB,cAAA;EAAmB,IAAA;EAAc,OAAA,EAAS,cAAA;AAAA;AAAA,UAEvD,kBAAA;;;;;;;;;AAAjB;;EAWE,OAAA,EAAS,cAAA;EAAA;;;;;;;;;;;;;;;;;EAmBT,KAAA,EAAO,QAAA,EAAU,cAAA;EA+BjB;EA7BA,QAAA,GAAW,UAAA;EAgCX;EA9BA,IAAA;EAoDA;EAlDA,SAAA;EAuEA;EArEA,cAAA;EAmGA;;;;;;;;;;;;;;;;;;;;;EA5EA,UAAA,GAAa,eAAA;EA4KS;EAzKtB,OAAA,GAAU,UAAA;EA0L4B;;;;;;;;;;;;;;;;;;;;EApKtC,YAAA,GAAe,wBAAA;EAyJE;;;;;;;;;;;;;;;;;;;EApIjB,YAAA;EA2N4D;;;;;;;;;;;;;;;;;;;;;;;;;EAhM5D,YAAA;EAmvBiB;EAhvBjB,UAAA,iCAA2C,EAAA,UAAY,QAAA;EAmwB/C;EAjwBR,SAAA;EA+xBQ;;;;EA1xBR,cAAA;EAm0B4B;;;;EA9zB5B,eAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA;IAAsB,OAAA;EAAA;;;;;;;;;;;;;;;;EAiBtB,UAAA,IAAc,GAAA,OAAU,GAAA,OAAU,IAAA;;;;;;;;;;;;;;;;;EAkBlC,OAAA,IAAW,GAAA,OAAU,GAAA,OAAU,GAAA,OAAU,IAAA;;;;;;;;;;;;;;;;;;EAmBzC,QAAA;6DAEE,MAAA;EAAA;AAAA;;;;;cAQS,WAAA;EAAA,iBAiBkB,OAAA;EAAA,QAhBrB,GAAA;EAAA,QACA,SAAA;EAAA,QACA,UAAA;EAAA,iBACS,QAAA;EAAA,iBAEA,OAAA;;UAGT,iBAAA;;UAEA,SAAA;;UAEA,kBAAA;;UAEA,eAAA;cAEqB,OAAA,EAAS,kBAAA;;MAgDlC,UAAA,CAAA;;MAKA,gBAAA,CAAA;;EAKJ,YAAA,CAAA,GAAgB,SAAA;;;;;;;;;;;;;;;EAkBhB,MAAA,CAAO,GAAA,EAAK,IAAA,CAAK,eAAA,EAAiB,GAAA,EAAK,IAAA,CAAK,cAAA,EAAgB,IAAA,IAAQ,GAAA;;;;;;;;;;;;;;;UAsB5D,UAAA;;UAYM,QAAA;EAeR,KAAA,CAAA,GAAS,OAAA;;EAuQT,YAAA,CAAA,GAAgB,OAAA;;;;;;;;;;EAahB,KAAA,CAAA,GAAS,OAAA;;EAkET,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;EAoCX,QAAA,CAAA,GAAY,OAAA;EAgElB,aAAA,CAAA,GAAiB,OAAA;;EAKjB,WAAA,CAAA,GAAe,UAAA;;;;;;EASf,UAAA,CAAA,YAAuB,UAAA;;;;;;;;;;;;EAevB,eAAA,CAAA,GAAmB,aAAA;IACjB,GAAA;IACA,MAAA;IACA,KAAA;IACA,SAAA;EAAA;EAuCF,aAAA,CAAA,GAAiB,IAAA,CAAK,MAAA;;;;;;;;;;;;;;UAmBd,2BAAA;EAAA,QAWA,wBAAA;EAAA,QAmBA,mBAAA;EAAA,QAUA,oBAAA;;UASA,yBAAA;;UAsBA,oBAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap-CwP2GGu0.d.mts","names":[],"sources":["../src/http/bootstrap.ts"],"mappings":";;;;;;AAqCA;;;;;;;;;;;;;;;;;;iBAAsB,SAAA,CAAU,OAAA,EAAS,kBAAA,GAAqB,OAAA,CAAQ,WAAA"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @forinda/kickjs v5.7.0
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) Felix Orinda
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* LICENSE file in the root directory of this source tree.
|
|
8
|
-
*
|
|
9
|
-
* @license MIT
|
|
10
|
-
*/
|
|
11
|
-
import{i as parseQuery}from"./query-Di7R8qNX.mjs";import{AsyncLocalStorage}from"node:async_hooks";const requestStore=new AsyncLocalStorage;function getRequestStore(){let store=requestStore.getStore();if(!store)throw Error(`No active request context. REQUEST-scoped services can only be resolved during an HTTP request.`);return store}function getRequestValue(key){return requestStore.getStore()?.values.get(key)}const signalControllerKey=Symbol.for(`kickjs.requestContext.signalController`);var RequestContext=class{constructor(req,res,next){this.req=req,this.res=res,this.next=next}metadataReadOnly(){return requestStore.getStore()?.values??null}metadataForWrite(){let store=requestStore.getStore();if(store)return store.values;throw Error(`RequestContext.set called outside an AsyncLocalStorage frame. Mount requestScopeMiddleware() before constructing RequestContext, or wrap test setup in requestStore.run({ requestId, instances: new Map(), values: new Map() }, () => ...).`)}get body(){return this.req.body}get params(){return this.req.params}get query(){return this.req.query}get headers(){return this.req.headers}get requestId(){return this.req.requestId??this.req.headers[`x-request-id`]}get signal(){let req=this.req,cached=req[signalControllerKey];if(cached)return cached.signal;let ctrl=new AbortController;req[signalControllerKey]=ctrl;let abort=()=>ctrl.abort(`request closed`);return this.req.once(`close`,abort),this.res.once(`close`,abort),ctrl.signal}get session(){return this.req.session}get user(){return this.metadataReadOnly()?.get(`user`)??this.req.user}get tenantId(){return this.user?.tenantId}get roles(){let u=this.user;return u?.tenantRoles??u?.roles??[]}qs(fieldConfig){return parseQuery(this.req.query,fieldConfig)}get file(){return this.req.file}get files(){let f=this.req.files;return Array.isArray(f)?f:void 0}get(key){return this.metadataReadOnly()?.get(key)}set(key,value){this.metadataForWrite().set(key,value)}json(data,status=200){return this.res.status(status).json(data)}created(data){return this.res.status(201).json(data)}noContent(){return this.res.status(204).end()}notFound(message=`Not Found`){return this.res.status(404).json({message})}badRequest(message){return this.res.status(400).json({message})}html(content,status=200){return this.res.status(status).type(`html`).send(content)}download(buffer,filename,contentType=`application/octet-stream`){return this.res.setHeader(`Content-Disposition`,`attachment; filename="${filename}"`),this.res.setHeader(`Content-Type`,contentType),this.res.send(buffer)}render(template,data={}){return this.res.render(template,data)}async paginate(fetcher,fieldConfig){let parsed=this.qs(fieldConfig),{data,total}=await fetcher(parsed),{page,limit}=parsed.pagination,totalPages=Math.ceil(total/limit)||1,response={data,meta:{page,limit,total,totalPages,hasNext:page<totalPages,hasPrev:page>1}};return this.json(response)}sse(){this.res.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`,"X-Accel-Buffering":`no`}),this.res.flushHeaders();let closeCallbacks=[];return this.req.on(`close`,()=>{for(let cb of closeCallbacks)cb()}),{send:(data,event,id)=>{id&&this.res.write(`id: ${id}\n`),event&&this.res.write(`event: ${event}\n`),this.res.write(`data: ${JSON.stringify(data)}\n\n`)},comment:text=>{this.res.write(`: ${text}\n\n`)},onClose:fn=>{closeCallbacks.push(fn)},close:()=>{this.res.end()}}}};export{requestStore as i,getRequestStore as n,getRequestValue as r,RequestContext as t};
|
|
12
|
-
//# sourceMappingURL=context-CvJQzbBF.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-CvJQzbBF.mjs","names":[],"sources":["../src/http/request-store.ts","../src/http/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { MetaValue } from '../core/execution-context'\n\n/** Per-request storage for REQUEST-scoped DI and request context propagation */\nexport interface RequestStore {\n /** Unique request identifier */\n requestId: string\n /**\n * Per-request singleton cache for REQUEST-scoped services. Keys are\n * heterogeneous (class constructors, `InjectionToken` objects, raw\n * strings), so the map type stays `Map<any, any>`.\n */\n instances: Map<any, any>\n /**\n * Per-request key/value bag. Canonical backing store for\n * `RequestContext.set/get` and the Context Contributor pipeline (#107)\n * since Phase 3. Anything written here is visible to every concrete\n * execution context constructed for the same request, regardless of\n * which middleware/contributor wrote it.\n *\n * Keys are constrained to `string` per the locked decision in\n * `architecture.md` §20.12 (#4); the value type is `unknown` because\n * the concrete shape at each key is determined by the consumer's\n * `ContextMeta` augmentation, not by the store itself.\n *\n * Prefer the {@link getRequestValue} helper over reaching into this\n * map directly — it threads the augmented `ContextMeta` shape through\n * `MetaValue<K>` so consumers get typed reads without an `as` cast.\n * Writes should flow through `ctx.set(key, value)` or a Context\n * Contributor's return value — see the `setRequestValue` note below\n * for why this package intentionally does NOT ship a service-level\n * write helper.\n */\n values: Map<string, unknown>\n}\n\n/** AsyncLocalStorage instance shared across the framework */\nexport const requestStore = new AsyncLocalStorage<RequestStore>()\n\n/**\n * Get the current request store. Throws if called outside a request context.\n * Used by the Container to resolve REQUEST-scoped dependencies.\n */\nexport function getRequestStore(): RequestStore {\n const store = requestStore.getStore()\n if (!store) {\n throw new Error(\n 'No active request context. REQUEST-scoped services can only be resolved during an HTTP request.',\n )\n }\n return store\n}\n\n/**\n * Read a per-request `ContextMeta`-keyed value from outside a controller\n * (services, repositories, anywhere with no `ctx` reference). Returns\n * the augmented type for the key — `MetaValue<K>` — so consumers get the\n * same static safety as `ctx.get(key)` without holding the context.\n *\n * Returns `undefined` outside an active request frame (background jobs,\n * startup code, tests without `requestScopeMiddleware()`) — the helper\n * is intentionally null-tolerant so calling it from a service that\n * runs in both request and non-request paths doesn't throw.\n *\n * @example\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface ContextMeta { tenant: { id: string; name: string } }\n * }\n *\n * @Service()\n * class AuditService {\n * log(action: string) {\n * const tenant = getRequestValue('tenant') // typed: { id; name } | undefined\n * db.audit.insert({ action, tenantId: tenant?.id })\n * }\n * }\n * ```\n */\nexport function getRequestValue<K extends string>(key: K): MetaValue<K> | undefined {\n return requestStore.getStore()?.values.get(key) as MetaValue<K> | undefined\n}\n\n// Intentionally NO `setRequestValue` export.\n//\n// Writes are deliberately context-scoped — only the controlled write\n// surfaces should mutate the per-request Map:\n//\n// 1. A Context Contributor's `resolve()` / `onError()` return value\n// (the runner does `ctx.set(reg.key, value)` on the contributor's\n// behalf). This is the canonical way to populate a key.\n// 2. A controller / middleware / contributor that holds a\n// `RequestContext` instance can call `ctx.set(key, value)`\n// directly. The write is auditable inline at the call site.\n//\n// Letting arbitrary services reach in and mutate the store from\n// anywhere produces \"spooky action at a distance\" — keys appear in\n// the request bag without an obvious source, and tracing which\n// service polluted what becomes a grep exercise. Services that need\n// to publish per-request state should return the value to the caller\n// (controller or contributor) and let *that* layer write it via\n// `ctx.set`. If a future need genuinely requires a service-level\n// write surface, expose a narrow function (`recordTrace`,\n// `markStartTime`) on a service that captures the side effect, not a\n// generic `setRequestValue`.\n","/// <reference types=\"multer\" />\nimport type { Request, Response, NextFunction } from 'express'\nimport { type ContextMeta, type ExecutionContext, type MetaValue } from '../core/execution-context'\nimport { requestStore } from './request-store'\nimport {\n parseQuery,\n type QueryFieldConfig,\n type PaginatedResponse,\n type TypedParsedQuery,\n} from './query'\n\n/**\n * Recursively marks every property of `T` as `readonly`. Preserves\n * `Function`, `Date`, and `RegExp` unchanged, and converts `Map` / `Set`\n * to `ReadonlyMap` / `ReadonlySet` so their mutating methods (`set`,\n * `delete`, `add`, `clear`) are removed from the public surface.\n * Tuples keep their positional element types — only their per-element\n * mutability is locked.\n *\n * Used to seal typed request data (`body`, `params`, `query`) so\n * adopters can't mutate Zod-validated payloads in place — the contract\n * is \"compute new values, stash via `ctx.set`\", not \"edit the request\n * bag\".\n *\n * Type-only. No runtime cost. `DeepReadonly<any>` is `any` (TS\n * distributes conditional types over `any`), so adopters who haven't\n * typed their `Ctx` get no protection but no breakage either.\n */\nexport type DeepReadonly<T> = T extends (...args: never[]) => unknown\n ? T\n : T extends Date | RegExp\n ? T\n : T extends Map<infer K, infer V>\n ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>\n : T extends Set<infer V>\n ? ReadonlySet<DeepReadonly<V>>\n : T extends readonly (infer U)[]\n ? // Distinguish a regular array from a tuple. A regular `T[]` widens\n // its `length` to `number`; a tuple's `length` is a literal\n // (e.g. `2`). The literal branch maps over `keyof T` so each\n // positional slot keeps its own type — `DeepReadonly<[1, 2]>` →\n // `readonly [1, 2]`, not `readonly (1 | 2)[]`.\n number extends T['length']\n ? ReadonlyArray<DeepReadonly<U>>\n : { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T\n\n/**\n * Re-export the augmentable {@link ContextMeta} registry from core/.\n * Declared in `core/execution-context.ts` so non-HTTP transports\n * (WS/queue/cron, V2) share one declaration site. Apps still augment\n * via the canonical `declare module '@forinda/kickjs'` form.\n */\nexport type { ContextMeta } from '../core/execution-context'\n\n/**\n * Symbol key under which the per-request `AbortController` backing\n * `RequestContext.signal` is cached on the Express `req` object.\n * Shared across every `RequestContext` instance built for the same\n * request (router-builder constructs separate wrappers for\n * middleware, the contributor pipeline, and the main handler — they\n * all need the same signal).\n */\nconst signalControllerKey = Symbol.for('kickjs.requestContext.signalController')\n\n/**\n * The shape of a single route entry in `KickRoutes`. Generated by\n * `kick typegen` and consumed via the `Ctx<T>` helper. Each field is\n * `unknown` by default and is filled in as the typegen learns more\n * about the route (URL params from the path, body/query from validation\n * schemas, etc.).\n */\nexport interface RouteShape {\n params?: unknown\n body?: unknown\n query?: unknown\n response?: unknown\n}\n\n/**\n * Unified request/response abstraction passed to every controller method.\n * Shields handlers from raw Express objects and provides convenience methods.\n *\n * Implements {@link ExecutionContext} so the transport-agnostic Context\n * Contributor pipeline (#107) can run against this concrete HTTP context\n * the same way it will run against future WS / queue / cron contexts.\n */\nexport class RequestContext<TBody = any, TParams = any, TQuery = any> implements ExecutionContext {\n constructor(\n public readonly req: Request,\n public readonly res: Response,\n public readonly next: NextFunction,\n ) {}\n\n /**\n * Read-side accessor for the per-request metadata map.\n *\n * Returns the AsyncLocalStorage map (`requestStore.getStore().values`)\n * shared with the Context Contributor pipeline when an ALS frame is\n * active. Returns `null` outside any frame — read-side callers\n * (`ctx.get`, `ctx.user`, `ctx.tenantId`, `ctx.roles`) treat that as\n * \"no metadata\" and fall through to their `req.*` defaults. Reads\n * never throw because outside an ALS frame nobody could have written\n * via `ctx.set` anyway, so an empty answer is the truthful one.\n */\n private metadataReadOnly(): Map<string, any> | null {\n return (requestStore.getStore()?.values as Map<string, any> | undefined) ?? null\n }\n\n /**\n * Write-side accessor for the per-request metadata map.\n *\n * Returns the same map as {@link metadataReadOnly} when an ALS frame\n * is active. Throws otherwise — writing to a fallback map outside\n * the ALS frame would silently lose data because other call sites\n * (downstream contributors, request-logger, etc.) read from the\n * canonical store and would never see it. Mount\n * `requestScopeMiddleware()` (Application does this for you in\n * `'auto'` mode) or wrap your test setup in\n * `requestStore.run({...}, () => …)` to fix.\n */\n private metadataForWrite(): Map<string, any> {\n const store = requestStore.getStore()\n if (store) return store.values as Map<string, any>\n throw new Error(\n 'RequestContext.set called outside an AsyncLocalStorage frame. ' +\n 'Mount requestScopeMiddleware() before constructing RequestContext, ' +\n 'or wrap test setup in requestStore.run({ requestId, instances: new Map(), values: new Map() }, () => ...).',\n )\n }\n\n // ── Request Data ────────────────────────────────────────────────────\n\n /**\n * Parsed request body. Returned as `DeepReadonly<TBody>` so adopters\n * can't mutate Zod-validated payloads in place — compute new values\n * and stash them via `ctx.set('key', value)` or a Context Contributor\n * instead. Reach for `ctx.req.body` if you genuinely need the raw,\n * mutable Express handle (rare).\n */\n get body(): DeepReadonly<TBody> {\n return this.req.body as DeepReadonly<TBody>\n }\n\n /** Path parameters parsed from the route — read-only, see {@link body}. */\n get params(): DeepReadonly<TParams> {\n return this.req.params as DeepReadonly<TParams>\n }\n\n /** Parsed query string — read-only, see {@link body}. */\n get query(): DeepReadonly<TQuery> {\n return this.req.query as DeepReadonly<TQuery>\n }\n\n /** Inbound HTTP headers — read-only, see {@link body}. */\n get headers(): Readonly<Request['headers']> {\n return this.req.headers\n }\n\n get requestId(): string | undefined {\n return (this.req as any).requestId ?? (this.req.headers['x-request-id'] as string | undefined)\n }\n\n /**\n * `AbortSignal` that fires when the underlying HTTP request is\n * closed — either because the client disconnected before the\n * response was sent, or because Node's socket-close event ran\n * after a normal response. Multiple `RequestContext` wrappers for\n * the same request (router-builder constructs one per\n * middleware/contributor pipeline / main handler) share a single\n * `AbortController` cached on the underlying `req` object, so the\n * signal is stable across the request lifecycle.\n *\n * Thread it through anything that takes an `AbortSignal` so the\n * work cancels as soon as the client gives up — e.g. a kickjs-db\n * relational query:\n *\n * ```ts\n * @Get('/:id/full')\n * async show(ctx: RequestContext) {\n * const row = await this.repo.findFullById(ctx.params.id, ctx.signal)\n * if (!row) return ctx.notFound()\n * ctx.json(row)\n * }\n * ```\n *\n * The repo passes `signal` to `db.query.<table>.findUnique({ signal })`;\n * if the client disconnects mid-flight, the in-flight query rejects\n * with `RelationalQueryCancelledError` instead of consuming the\n * connection until completion.\n *\n * Implementation note — both `req.on('close')` and `res.on('close')`\n * are wired so the signal aborts regardless of whether the close\n * event lands on the request stream or the response stream first.\n * Subsequent fires are no-ops (`AbortController.abort()` is\n * idempotent).\n */\n get signal(): AbortSignal {\n const req = this.req as Request & { [signalControllerKey]?: AbortController }\n const cached = req[signalControllerKey]\n if (cached) return cached.signal\n\n const ctrl = new AbortController()\n req[signalControllerKey] = ctrl\n const abort = () => ctrl.abort('request closed')\n this.req.once('close', abort)\n this.res.once('close', abort)\n return ctrl.signal\n }\n\n /** Session data (requires session middleware) */\n get session(): any {\n return (this.req as any).session\n }\n\n /**\n * The authenticated user set by AuthAdapter.\n * Reads from the per-request metadata store first (`ctx.set('user', ...)`),\n * then falls back to `req.user` (set directly by AuthAdapter middleware).\n *\n * Extend the `AuthUser` interface via module augmentation in `@forinda/kickjs-auth`\n * to get full typing.\n *\n * @example\n * ```ts\n * @Get('/me')\n * @Authenticated()\n * getProfile(ctx: RequestContext) {\n * const user = ctx.user\n * if (!user) return ctx.notFound()\n * return ctx.json({ user })\n * }\n * ```\n */\n get user(): MetaValue<'user', Record<string, any>> | undefined {\n return this.metadataReadOnly()?.get('user') ?? (this.req as any).user\n }\n\n /**\n * Tenant identifier for multi-tenant apps. Reads `user.tenantId` —\n * populated either by `AuthAdapter.testMode({ tenantId })`, a tenant\n * resolver middleware, or manually on the strategy's `mapPayload`.\n *\n * Augment `AuthUser` (or `ContextMeta['user']`) to widen the return\n * type if your user shape guarantees a non-`string | undefined` id.\n */\n get tenantId(): string | undefined {\n const u = this.user as Record<string, any> | undefined\n return u?.tenantId\n }\n\n /**\n * Effective role list for the current user. Prefers `user.tenantRoles`\n * (populated by `AuthAdapterOptions.roleResolver` under a tenant) and\n * falls back to `user.roles`. Returns an empty array when the user is\n * absent or the shape has neither field.\n */\n get roles(): string[] {\n const u = this.user as Record<string, any> | undefined\n return u?.tenantRoles ?? u?.roles ?? []\n }\n\n // ── Query String Parsing ───────────────────────────────────────────\n\n /**\n * Parse the request query string into structured filters, sort, pagination, and search.\n * Pass the result to an ORM query builder adapter (Drizzle, Prisma, Sequelize, etc.).\n *\n * Generic over the field config: when passed an inline literal (or\n * `as const`), `filters[].field` and `sort[].field` are narrowed to\n * the exact whitelisted field names. Pass nothing or a non-literal\n * config and you get the loose `string` fallback.\n *\n * @param fieldConfig - Optional whitelist for filterable, sortable, and searchable fields\n *\n * @example\n * ```ts\n * @Get('/')\n * async list(ctx: RequestContext) {\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * } as const)\n * parsed.filters[0].field // typed as 'status' | 'priority'\n * parsed.sort[0].field // typed as 'createdAt' | 'title'\n * }\n * ```\n */\n qs<TConfig extends QueryFieldConfig | undefined = undefined>(\n fieldConfig?: TConfig,\n ): TypedParsedQuery<TConfig> {\n return parseQuery<TConfig>(this.req.query as Record<string, any>, fieldConfig)\n }\n\n // ── File Uploads ────────────────────────────────────────────────────\n\n /**\n * Single uploaded file (requires `@FileUpload({ mode: 'single' })`).\n * Returned as `DeepReadonly<Express.Multer.File>` — file metadata is\n * set by the upload middleware and shouldn't be mutated downstream.\n */\n get file(): DeepReadonly<Express.Multer.File> | undefined {\n return this.req.file\n }\n\n /**\n * Array of uploaded files (requires `@FileUpload({ mode: 'array' })`).\n * Returns a `ReadonlyArray<DeepReadonly<Express.Multer.File>>` — both\n * the list (`push`/`pop`) and each file's fields (`size`, `originalname`,\n * etc.) are locked. Use `ctx.req.files` if you genuinely need the\n * mutable Express handle.\n */\n get files(): ReadonlyArray<DeepReadonly<Express.Multer.File>> | undefined {\n // multer's `Request['files']` is a union — array (mode: 'array' /\n // 'any') or `{ [fieldname]: File[] }` (mode: 'fields'). The\n // `@FileUpload({ mode: 'array' })` decorator only sets the array\n // shape, but `Request['files']` is the broad union. Narrow back to\n // `File[]` here for the common-case getter; adopters using\n // `mode: 'fields'` can read `ctx.req.files` directly for the\n // dictionary shape.\n const f = this.req.files\n return Array.isArray(f) ? f : undefined\n }\n\n // ── Metadata Store ──────────────────────────────────────────────────\n\n /**\n * Read a value from the per-request metadata store.\n *\n * When `ContextMeta` has been augmented with a matching key, the return\n * type is inferred automatically. For ad-hoc keys, pass a generic:\n * `ctx.get<MyType>('custom')`.\n */\n get<K extends string>(key: K): MetaValue<K> | undefined {\n return this.metadataReadOnly()?.get(key) as MetaValue<K> | undefined\n }\n\n /**\n * Write a value to the per-request metadata store. Throws when no\n * AsyncLocalStorage frame is active — see {@link metadataForWrite}\n * for why a silent fallback is the wrong default.\n */\n set<K extends string>(key: K, value: MetaValue<K>): void {\n this.metadataForWrite().set(key, value)\n }\n\n // ── Response Helpers ────────────────────────────────────────────────\n\n json(data: any, status = 200) {\n return this.res.status(status).json(data)\n }\n\n created(data: any) {\n return this.res.status(201).json(data)\n }\n\n noContent() {\n return this.res.status(204).end()\n }\n\n notFound(message = 'Not Found') {\n return this.res.status(404).json({ message })\n }\n\n badRequest(message: string) {\n return this.res.status(400).json({ message })\n }\n\n html(content: string, status = 200) {\n return this.res.status(status).type('html').send(content)\n }\n\n download(buffer: Buffer, filename: string, contentType = 'application/octet-stream') {\n this.res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`)\n this.res.setHeader('Content-Type', contentType)\n return this.res.send(buffer)\n }\n\n /**\n * Render a template using the registered view engine (EJS, Pug, Handlebars, etc.).\n * Requires a ViewAdapter to be configured in bootstrap().\n *\n * @param template - Template name (without extension, relative to viewsDir)\n * @param data - Data to pass to the template\n *\n * @example\n * ```ts\n * ctx.render('dashboard', { user, title: 'Dashboard' })\n * ctx.render('emails/welcome', { name: 'Alice' })\n * ```\n */\n render(template: string, data: Record<string, any> = {}) {\n return this.res.render(template, data)\n }\n\n /**\n * Parse query params and return a standardized paginated response.\n * Calls `ctx.qs()` internally, then wraps your data with pagination meta.\n *\n * @param fetcher - Async function that receives ParsedQuery and returns `{ data, total }`\n * @param fieldConfig - Optional whitelist for filterable, sortable, searchable fields\n *\n * @example\n * ```ts\n * @Get('/')\n * async list(ctx: RequestContext) {\n * return ctx.paginate(\n * async (parsed) => {\n * const data = await db.select().from(users)\n * .where(query.where).limit(parsed.pagination.limit)\n * .offset(parsed.pagination.offset).all()\n * const total = await db.select({ count: count() }).from(users).get()\n * return { data, total: total?.count ?? 0 }\n * },\n * { filterable: ['name', 'role'], sortable: ['createdAt'] },\n * )\n * }\n * ```\n */\n async paginate<T, TConfig extends QueryFieldConfig | undefined = undefined>(\n fetcher: (parsed: TypedParsedQuery<TConfig>) => Promise<{ data: T[]; total: number }>,\n fieldConfig?: TConfig,\n ) {\n const parsed = this.qs<TConfig>(fieldConfig)\n const { data, total } = await fetcher(parsed)\n const { page, limit } = parsed.pagination\n const totalPages = Math.ceil(total / limit) || 1\n\n const response: PaginatedResponse<T> = {\n data,\n meta: {\n page,\n limit,\n total,\n totalPages,\n hasNext: page < totalPages,\n hasPrev: page > 1,\n },\n }\n\n return this.json(response)\n }\n\n // ── Server-Sent Events ──────────────────────────────────────────────\n\n /**\n * Start an SSE (Server-Sent Events) stream.\n * Sets the correct headers and returns helpers to send events.\n *\n * @example\n * ```ts\n * @Get('/events')\n * async stream(ctx: RequestContext) {\n * const sse = ctx.sse()\n *\n * const interval = setInterval(() => {\n * sse.send({ time: new Date().toISOString() }, 'tick')\n * }, 1000)\n *\n * sse.onClose(() => clearInterval(interval))\n * }\n * ```\n */\n sse() {\n this.res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n this.res.flushHeaders()\n\n const closeCallbacks: Array<() => void> = []\n\n this.req.on('close', () => {\n for (const cb of closeCallbacks) cb()\n })\n\n return {\n /** Send an SSE event with optional event name and id */\n send: (data: any, event?: string, id?: string) => {\n if (id) this.res.write(`id: ${id}\\n`)\n if (event) this.res.write(`event: ${event}\\n`)\n this.res.write(`data: ${JSON.stringify(data)}\\n\\n`)\n },\n /** Send a comment (keeps connection alive) */\n comment: (text: string) => {\n this.res.write(`: ${text}\\n\\n`)\n },\n /** Register a callback when the client disconnects */\n onClose: (fn: () => void) => {\n closeCallbacks.push(fn)\n },\n /** End the SSE stream */\n close: () => {\n this.res.end()\n },\n }\n }\n}\n\n/**\n * Short alias for `RequestContext` parameterised by a route shape.\n *\n * Use with `kick typegen`'s generated `KickRoutes` namespace for\n * fully-typed `ctx.params`, `ctx.body`, and `ctx.query`. Metadata\n * typing (`ctx.get()`, `ctx.set()`, `ctx.user`) comes from\n * `ContextMeta` augmentation and works on all `RequestContext` instances.\n *\n * @example\n * ```ts\n * @Get('/:id')\n * getUser(ctx: Ctx<KickRoutes.UserController['getUser']>) {\n * ctx.params.id // typed from RouteShape\n * ctx.body // typed from RouteShape\n * ctx.user // typed from ContextMeta\n * ctx.get('tenant') // typed from ContextMeta\n * }\n * ```\n */\nexport type Ctx<TRoute extends RouteShape = RouteShape> = RequestContext<\n TRoute extends { body: infer B } ? B : any,\n TRoute extends { params: infer P } ? P : any,\n TRoute extends { query: infer Q } ? Q : any\n>\n\n/**\n * Global ambient registry of typed routes, populated by `kick typegen`.\n *\n * Each interface inside `KickRoutes` corresponds to a controller class;\n * each property is one route method on that controller. The values\n * conform to `RouteShape`.\n *\n * Empty by default — declarations come from `.kickjs/types/routes.d.ts`\n * generated alongside the rest of the typegen output.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace KickRoutes {}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decorators-ZcjsV1O0.d.mts","names":[],"sources":["../src/core/assets.ts","../src/core/decorators.ts"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-CkKLXZHg.d.mts","names":[],"sources":["../src/core/define-plugin.ts","../src/core/define-module.ts","../src/core/define-modules.ts","../src/core/metadata.ts","../src/core/circuit-breaker.ts","../src/core/context-errors.ts","../src/core/contributor-pipeline.ts","../src/core/contributor-runner.ts","../src/core/mount-sort.ts","../src/core/define-adapter.ts","../src/core/asset-keys.ts"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-oB1CDq5i.d.mts","names":[],"sources":["../src/config/env.ts","../src/config/config-service.ts"],"mappings":";;;;;;;AAsCA;;cAAa,aAAA,EAAa,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC1B;;;iBAAgB,SAAA,WAAoB,CAAA,CAAE,WAAA,CAAA,CACpC,MAAA,GAAS,IAAA,SAAa,aAAA,KAAkB,CAAA,CAAE,SAAA,CAAU,CAAA,IACnD,CAAA,CAAE,SAAA,QAAiB,aAAA,CAAc,KAAA,GAAQ,CAAA;;;;;;;;;;;;;;;;;;;;;;;;iBA+B5B,OAAA,WAAkB,CAAA,CAAE,SAAA,MAAA,CAAgB,MAAA,EAAQ,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,CAAA;AAAA,iBACxD,OAAA,CAAA,IAAY,MAAA,oBAA0B,GAAA,GAAM,OAAA;;;;;;;;;;;;;;;;;;;AAA5D;;;iBAqCgB,MAAA,WAAiB,MAAA,CAAA,CAAQ,GAAA,EAAK,CAAA,GAAI,OAAA,CAAQ,CAAA;AAAA,iBAC1C,MAAA,WAAiB,CAAA,CAAE,SAAA,gCAAyC,CAAA,CAAE,KAAA,CAAM,CAAA,EAAA,CAClF,GAAA,EAAK,CAAA,EACL,MAAA,EAAQ,CAAA,GACP,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAA;;;;;;;;;AAJd;;;;;;;iBAyBgB,SAAA,CAAA;;;;;;;iBA2BA,aAAA,CAAA;AAAA,KAKJ,GAAA,GAAM,CAAA,CAAE,KAAA,QAAa,aAAA;;;;;AAzKjC;;;;;KC1BK,SAAA,sBAA+B,MAAA,oBAA0B,CAAA,GAAI,CAAA,GAAI,MAAA;;;;;;;KAQjE,WAAA,wBAAmC,CAAA,SAAU,MAAA,GAAS,OAAA,CAAQ,CAAA,IAAK,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;cA2B3D,aAAA;EDgCG;;;;;;;;;;;;;EAAA,YClBF,GAAA,CAAA;EDkBwB;;;;;;;;;ECLpC,GAAA,2BAAA,CAA+B,GAAA,EAAK,SAAA,CAAU,CAAA,IAAK,WAAA,CAAY,CAAA,EAAG,CAAA;EDOhC;;;;AA+BpC;EC7BE,MAAA,CAAA,IAAW,MAAA,oBAA0B,QAAA,CAAS,MAAA,iBAAuB,QAAA,CAAS,OAAA;ED6BzD;ECxBrB,MAAA,CAAA;EAMA,YAAA,CAAA;EAIA,aAAA,CAAA;EAIA,MAAA,CAAA;AAAA;;;;;UASe,kBAAA,cAAgC,MAAA;EAC/C,GAAA,0BAA6B,IAAA,EAAM,GAAA,EAAK,CAAA,GAAI,IAAA,CAAK,CAAA;EACjD,MAAA,IAAU,QAAA,CAAS,IAAA;EACnB,MAAA;EACA,YAAA;EACA,aAAA;EACA,MAAA;AAAA;;;;;;;;;;;;;ADiCF;;;;;;;;;;;;;;;;iBCFgB,mBAAA,WAA8B,CAAA,CAAE,SAAA,MAAA,CAAgB,MAAA,EAAQ,CAAA;EAAA,QAqC5D,kBAAA,CAAkB,CAAA,CAAA,IAAA,CAAA,MAAA,CAAA,CAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-NMHVmDRF.d.mts","names":[],"sources":["../src/core/module-registry.ts","../src/core/plugin.ts"],"mappings":";;;;;;;;;;;;AAiCA;;;;;;;;;AAuBA;;;;;;;;;;;;;;;;;UAvBiB,cAAA;;AC0BjB;;;;;;;;;EDfE,KAAA,CAAM,MAAA,EAAQ,cAAA;AAAA;;;;;;;;;;cAYH,qBAAA,YAAiC,cAAA;EAAA,SACnC,OAAA,EAAS,cAAA;EAElB,KAAA,CAAM,MAAA,EAAQ,cAAA;AAAA;;;AA1BhB;;;;;;;;;AAuBA;;;;;;;;;;;;;;;;;;;ACGA;;;;;;;;;;;;;;;;;;;;;;;;AD1BA,UC0BiB,UAAA;EAsER;EApEP,IAAA;EA0Ea;;;;;;;;;;;;;;;;;;EAtDb,SAAA,YAAqB,gBAAA;;;;;EAMrB,QAAA,EAAU,SAAA,EAAW,SAAA;;;;;;;;;;;;;;EAerB,OAAA,KAAY,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BZ,KAAA,EAAO,QAAA,EAAU,cAAA;;;;;EAMjB,QAAA,KAAa,UAAA;;;;;EAMb,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,YAAA,KAAiB,wBAAA;;;;;EAMjB,OAAA,EAAS,SAAA,EAAW,SAAA,UAAmB,OAAA;;;;;EAMvC,QAAA,YAAoB,OAAA;;;;;;;;;;;;;EAcpB,UAAA,eAAyB,OAAA;;;;;;EAOzB,YAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router-builder-BfCCeQB-.d.mts","names":[],"sources":["../src/http/router-builder.ts"],"mappings":";;;;;;iBAoCgB,8BAAA,CAA+B,OAAA,WAAkB,mBAAA;AAAA,UAIhD,kBAAA;EAJ6B;;;;AAI9C;;EAOE,eAAA,YAA2B,mBAAA;AAAA;;AAc7B;;;;;;;;;;iBAAgB,WAAA,CAAY,eAAA,OAAsB,OAAA,GAAS,kBAAA,GAA0B,MAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spa-DIXtRBj-.d.mts","names":[],"sources":["../src/http/middleware/spa.ts"],"mappings":";;;UAMiB,iBAAA;;;AAAjB;;;;;;;;;;;EAcE,SAAA;EAqEsB;;;;;;;;;;EAzDtB,SAAA;EAkEoB;;;;;;;;;EAvDpB,OAAA;EAuDoB;;;;EAjDpB,YAAA;EAoGA;;;;EA9FA,iBAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkCW,UAAA,YAAsB,UAAA;EAAA,QASb,OAAA;EARpB,IAAA;EAAA,QACQ,SAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,QACA,YAAA;EAAA,QACA,iBAAA;EAAA,QACA,SAAA;cAEY,OAAA,GAAS,iBAAA;EAU7B,WAAA,CAAA;IAAc;EAAA,GAAO,cAAA;EAyCrB,WAAA,CAAA;IAAc;EAAA,GAAO,cAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-DqRwWhX0.d.mts","names":[],"sources":["../src/http/middleware/upload.ts"],"mappings":";;;;;;;;;AAsEA;;;;;AAYA;;iBAZgB,gBAAA,CAAiB,KAAA;;;;;;UAYhB,aAAA,SAAsB,iBAAA;EAIjC;EAFJ,OAAA,GAAU,OAAA;EAmEG;EAjEb,IAAA;AAAA;;;;;;;AAiE6E;;;;;;iBAAtE,MAAA,CAAO,SAAA,UAAmB,OAAA,GAAS,aAAA,GAAqB,cAAA;;;;iBAQxD,KAAA,CAAM,SAAA,UAAmB,QAAA,WAAe,OAAA,GAAS,aAAA,GAAqB,cAAA;AAAc;;;AAAA,iBAQpF,IAAA,CAAK,OAAA,GAAS,aAAA,GAAqB,cAAA;;;;;;AAmB5C;;;;;;;;;iBAAgB,YAAA,CAAA,IACN,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;;;;iBA+B7B,qBAAA,CAAsB,MAAA,EAAQ,gBAAA,GAAmB,cAAA;AAAjE;AAAA,cAmBa,MAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"views-DDT9vghc.d.mts","names":[],"sources":["../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts"],"mappings":";;;;;;;;;UAOiB,cAAA;;EAEf,OAAA;;EAEA,uBAAA;;EAEA,YAAA;;EAEA,WAAA;;EAEA,aAAA;AAAA;;iBAIc,gBAAA,CAAA;;;iBCoCA,0BAAA,6BAEJ,MAAA,SAAe,QAAA,IAAY,MAAA,2BAC3B,MAAA,oBAA0B,MAAA,gBAAA,CACpC,IAAA,EAAM,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA,IAAkB,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA;;;;UCzDjE,YAAA;;EAEf,SAAA;;;;;;EAMA,SAAA,EAAW,GAAA;;;;;;;;;;;;AFLb;;;;;;;;;EE0BE,MAAA,EAAQ,GAAA;AAAA;AFZV;AAAA,cEgBa,YAAA,EAAY,iBAAA,CAAA,YAAA;;;;;iBAMT,eAAA,CAAA,GAAmB,YAAA;;ADcnC;;;;;;;;;;;;;;;;;;;;;;;;;iBCsBgB,eAAA,kBAAA,CAAkC,GAAA,EAAK,CAAA,GAAI,SAAA,CAAU,CAAA;;;;;;;;;;;iBCrDrD,sBAAA,CAAA,IAAsB,GAAA,EACnB,OAAA,EAAO,IAAA,EAAQ,QAAA,EAAQ,IAAA,EAAQ,YAAA;;;;;;;cCnBrC,kBAAA;AAAA,UAKI,mBAAA;;;;;EAKf,iBAAA;AAAA;;;;UAMe,YAAA;;EAEf,OAAA;;EAEA,OAAA;;EAEA,YAAA;EJvBe;EIyBf,KAAA;AAAA;;;;;iBAqBc,gBAAA,CAAiB,MAAA,WAAiB,YAAA;;;;AJhClD;;;;;;;;ACoCA;;;;;;;;;;iBGsCgB,YAAA,CAAa,OAAA,GAAS,mBAAA,IAC5B,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;UC3F5B,kBAAA;;;;;;;;;;;;;;;EAef,MAAA;;EAGA,GAAA;;EAGA,QAAA;;EAGA,MAAA;AAAA;ALtBF;;;;;;;;;;;AAcA;;;;;;;;ACoCA;;;;;;;;;;ADlDA,cKsDa,WAAA,EAAW,cAAA,CAAA,kBAAA"}
|