@forinda/kickjs 8.0.0 → 8.2.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.
Files changed (179) hide show
  1. package/dist/{adapter-BuOTeSQy.d.mts → adapter-BxXGuLnW.d.mts} +57 -13
  2. package/dist/adapter-BxXGuLnW.d.mts.map +1 -0
  3. package/dist/{app-module-DZW0uYNA.d.mts → app-module-DoM8RNZ9.d.mts} +37 -4
  4. package/dist/app-module-DoM8RNZ9.d.mts.map +1 -0
  5. package/dist/{application-D1koPFmH.d.mts → application-NjKJPm9r.d.mts} +10 -4
  6. package/dist/application-NjKJPm9r.d.mts.map +1 -0
  7. package/dist/application-blu78Mlf.mjs +12 -0
  8. package/dist/application-blu78Mlf.mjs.map +1 -0
  9. package/dist/{bootstrap-BIjHgG4v.d.mts → bootstrap-Sjn3hPqw.d.mts} +2 -2
  10. package/dist/bootstrap-Sjn3hPqw.d.mts.map +1 -0
  11. package/dist/{bootstrap-BG86IC_L.mjs → bootstrap-YxZ7Xitb.mjs} +3 -3
  12. package/dist/{bootstrap-BG86IC_L.mjs.map → bootstrap-YxZ7Xitb.mjs.map} +1 -1
  13. package/dist/{client-ip-C909xeVL.mjs → client-ip-B6kKJLEJ.mjs} +2 -2
  14. package/dist/{client-ip-C909xeVL.mjs.map → client-ip-B6kKJLEJ.mjs.map} +1 -1
  15. package/dist/config/index.d.mts +1 -1
  16. package/dist/config/index.mjs +2 -2
  17. package/dist/{config-ClZsO9eK.mjs → config-Bwgvk__k.mjs} +3 -3
  18. package/dist/{config-ClZsO9eK.mjs.map → config-Bwgvk__k.mjs.map} +1 -1
  19. package/dist/container-BnkQIDQF.mjs +117 -0
  20. package/dist/container-BnkQIDQF.mjs.map +1 -0
  21. package/dist/{container-BW_MlgLj.d.mts → container-Dpvd66Yt.d.mts} +19 -2
  22. package/dist/container-Dpvd66Yt.d.mts.map +1 -0
  23. package/dist/context-GJc8Pf2d.mjs +12 -0
  24. package/dist/context-GJc8Pf2d.mjs.map +1 -0
  25. package/dist/{context-errors-vB3ZRXSu.mjs → context-errors-Bm1GxsOQ.mjs} +2 -2
  26. package/dist/{context-errors-vB3ZRXSu.mjs.map → context-errors-Bm1GxsOQ.mjs.map} +1 -1
  27. package/dist/contributor-runner-CzL4-aIt.mjs +12 -0
  28. package/dist/contributor-runner-CzL4-aIt.mjs.map +1 -0
  29. package/dist/core/adapter.d.mts +1 -1
  30. package/dist/core/adapter.mjs +1 -1
  31. package/dist/core/app-module.d.mts +1 -1
  32. package/dist/core/app-module.mjs +1 -1
  33. package/dist/core/cache.mjs +1 -1
  34. package/dist/core/container.d.mts +1 -1
  35. package/dist/core/container.mjs +2 -2
  36. package/dist/core/cron.mjs +2 -2
  37. package/dist/core/decorators.d.mts +1 -1
  38. package/dist/core/decorators.mjs +2 -2
  39. package/dist/core/decorators.mjs.map +1 -1
  40. package/dist/core/errors.mjs +1 -1
  41. package/dist/core/interfaces.d.mts +1 -1
  42. package/dist/core/interfaces.mjs +2 -2
  43. package/dist/core/interfaces.mjs.map +1 -1
  44. package/dist/core/logger.mjs +1 -1
  45. package/dist/core/path.mjs +1 -1
  46. package/dist/core/plugin.d.mts +1 -1
  47. package/dist/core/plugin.mjs +1 -1
  48. package/dist/core/reactivity.mjs +1 -1
  49. package/dist/core-w7s4ntck.mjs +12 -0
  50. package/dist/core-w7s4ntck.mjs.map +1 -0
  51. package/dist/csrf-BjmtOJZs.mjs +12 -0
  52. package/dist/csrf-BjmtOJZs.mjs.map +1 -0
  53. package/dist/csrf-COqPWlMd.d.mts +102 -0
  54. package/dist/csrf-COqPWlMd.d.mts.map +1 -0
  55. package/dist/{decorate-DDLimr-C.mjs → decorate-CpBbxrvT.mjs} +1 -1
  56. package/dist/{decorators-CbTBUVOu.d.mts → decorators-mg0qJ6Qc.d.mts} +3 -3
  57. package/dist/{decorators-CbTBUVOu.d.mts.map → decorators-mg0qJ6Qc.d.mts.map} +1 -1
  58. package/dist/{define-adapter-BIwBwOK_.mjs → define-adapter-whaSQuNs.mjs} +2 -2
  59. package/dist/{define-adapter-BIwBwOK_.mjs.map → define-adapter-whaSQuNs.mjs.map} +1 -1
  60. package/dist/{disposables-Ht0M5KKc.mjs → disposables-lWieeJLG.mjs} +2 -2
  61. package/dist/{disposables-Ht0M5KKc.mjs.map → disposables-lWieeJLG.mjs.map} +1 -1
  62. package/dist/{env-CabVU6cy.mjs → env-Bd6akCGH.mjs} +3 -3
  63. package/dist/{env-CabVU6cy.mjs.map → env-Bd6akCGH.mjs.map} +1 -1
  64. package/dist/{error-handler-C-xGxZKP.d.mts → error-handler-DMUz6X18.d.mts} +7 -6
  65. package/dist/{error-handler-C-xGxZKP.d.mts.map → error-handler-DMUz6X18.d.mts.map} +1 -1
  66. package/dist/handler-gK6lNZKW.mjs +13 -0
  67. package/dist/handler-gK6lNZKW.mjs.map +1 -0
  68. package/dist/http/application.d.mts +1 -1
  69. package/dist/http/application.mjs +2 -2
  70. package/dist/http/bootstrap.d.mts +1 -1
  71. package/dist/http/bootstrap.mjs +2 -2
  72. package/dist/http/context.d.mts +2 -2
  73. package/dist/http/context.mjs +2 -2
  74. package/dist/http/middleware/cors.mjs +1 -1
  75. package/dist/http/middleware/csrf.d.mts +2 -2
  76. package/dist/http/middleware/csrf.mjs +2 -2
  77. package/dist/http/middleware/error-handler.d.mts +1 -1
  78. package/dist/http/middleware/error-handler.mjs +2 -2
  79. package/dist/http/middleware/error-handler.mjs.map +1 -1
  80. package/dist/http/middleware/helmet.mjs +1 -1
  81. package/dist/http/middleware/rate-limit.d.mts +1 -1
  82. package/dist/http/middleware/rate-limit.mjs +2 -2
  83. package/dist/http/middleware/rate-limit.mjs.map +1 -1
  84. package/dist/http/middleware/request-id.mjs +1 -1
  85. package/dist/http/middleware/request-logger.mjs +2 -2
  86. package/dist/http/middleware/session.mjs +2 -2
  87. package/dist/http/middleware/spa.d.mts +1 -1
  88. package/dist/http/middleware/spa.mjs +2 -2
  89. package/dist/http/middleware/upload.d.mts +1 -1
  90. package/dist/http/middleware/upload.mjs +2 -2
  91. package/dist/http/middleware/validate.mjs +1 -1
  92. package/dist/http/middleware/views.d.mts +1 -1
  93. package/dist/http/middleware/views.mjs +2 -2
  94. package/dist/http/query/index.mjs +2 -2
  95. package/dist/http/router-builder.d.mts +1 -1
  96. package/dist/http/router-builder.mjs +2 -2
  97. package/dist/http/runtimes/fastify.d.mts +1 -1
  98. package/dist/http/runtimes/fastify.d.mts.map +1 -1
  99. package/dist/http/runtimes/fastify.mjs +3 -3
  100. package/dist/http/runtimes/fastify.mjs.map +1 -1
  101. package/dist/http/runtimes/h3-web.d.mts +1 -1
  102. package/dist/http/runtimes/h3-web.mjs +2 -2
  103. package/dist/http/runtimes/h3.d.mts +1 -1
  104. package/dist/http/runtimes/h3.d.mts.map +1 -1
  105. package/dist/http/runtimes/h3.mjs +2 -2
  106. package/dist/http/runtimes/h3.mjs.map +1 -1
  107. package/dist/{index-Di64XOKG.d.mts → index-Btz1W0X5.d.mts} +2 -2
  108. package/dist/{index-Di64XOKG.d.mts.map → index-Btz1W0X5.d.mts.map} +1 -1
  109. package/dist/index.d.mts +17 -16
  110. package/dist/index.mjs +2 -2
  111. package/dist/index.mjs.map +1 -1
  112. package/dist/{interfaces-BTTBdkvG.d.mts → interfaces-C1hCSoLD.d.mts} +3 -1
  113. package/dist/{interfaces-BTTBdkvG.d.mts.map → interfaces-C1hCSoLD.d.mts.map} +1 -1
  114. package/dist/kv-stores-Bp4_RP-e.mjs +12 -0
  115. package/dist/kv-stores-Bp4_RP-e.mjs.map +1 -0
  116. package/dist/{kv-stores-CbsEHE5J.d.mts → kv-stores-sjVe0MqN.d.mts} +41 -7
  117. package/dist/kv-stores-sjVe0MqN.d.mts.map +1 -0
  118. package/dist/{metadata-CeUKYNRZ.mjs → metadata-DRRQ0p_l.mjs} +2 -2
  119. package/dist/{metadata-CeUKYNRZ.mjs.map → metadata-DRRQ0p_l.mjs.map} +1 -1
  120. package/dist/{module-registry-DIngF0Ne.mjs → module-registry-C7yEmbZ8.mjs} +2 -2
  121. package/dist/{module-registry-DIngF0Ne.mjs.map → module-registry-C7yEmbZ8.mjs.map} +1 -1
  122. package/dist/{query-BzyWJrd6.mjs → query-BSp_RIR0.mjs} +2 -2
  123. package/dist/{query-BzyWJrd6.mjs.map → query-BSp_RIR0.mjs.map} +1 -1
  124. package/dist/{rate-limit-D_cKx6Uw.d.mts → rate-limit-CzFoSCzS.d.mts} +21 -1
  125. package/dist/rate-limit-CzFoSCzS.d.mts.map +1 -0
  126. package/dist/{respond-ERWwiws9.mjs → respond-BygP3p23.mjs} +2 -2
  127. package/dist/{respond-ERWwiws9.mjs.map → respond-BygP3p23.mjs.map} +1 -1
  128. package/dist/route-flag-GzEeUmws.d.mts +242 -0
  129. package/dist/route-flag-GzEeUmws.d.mts.map +1 -0
  130. package/dist/route-flag-vuWGpI1V.mjs +12 -0
  131. package/dist/route-flag-vuWGpI1V.mjs.map +1 -0
  132. package/dist/route-policy-D5lQltyg.mjs +12 -0
  133. package/dist/route-policy-D5lQltyg.mjs.map +1 -0
  134. package/dist/router-builder-HeSJZ2QG.mjs +12 -0
  135. package/dist/router-builder-HeSJZ2QG.mjs.map +1 -0
  136. package/dist/{router-builder-DptndWhd.d.mts → router-builder-eLpJYyrb.d.mts} +2 -2
  137. package/dist/router-builder-eLpJYyrb.d.mts.map +1 -0
  138. package/dist/runtime-B3fCMmZW.mjs +12 -0
  139. package/dist/runtime-B3fCMmZW.mjs.map +1 -0
  140. package/dist/{spa-DhGwM3_A.d.mts → spa-BBwrECcF.d.mts} +40 -14
  141. package/dist/spa-BBwrECcF.d.mts.map +1 -0
  142. package/dist/{upload-config-DtnX4uVR.mjs → upload-config-CP5v2Fh-.mjs} +2 -2
  143. package/dist/{upload-config-DtnX4uVR.mjs.map → upload-config-CP5v2Fh-.mjs.map} +1 -1
  144. package/dist/{upload-CbJOK-j9.d.mts → upload-yKvKdRWG.d.mts} +3 -3
  145. package/dist/{upload-CbJOK-j9.d.mts.map → upload-yKvKdRWG.d.mts.map} +1 -1
  146. package/dist/web.d.mts +4 -4
  147. package/dist/web.d.mts.map +1 -1
  148. package/dist/web.mjs +2 -2
  149. package/dist/web.mjs.map +1 -1
  150. package/package.json +1 -1
  151. package/dist/adapter-BuOTeSQy.d.mts.map +0 -1
  152. package/dist/app-module-DZW0uYNA.d.mts.map +0 -1
  153. package/dist/application-D1koPFmH.d.mts.map +0 -1
  154. package/dist/application-IWSzCdB8.mjs +0 -12
  155. package/dist/application-IWSzCdB8.mjs.map +0 -1
  156. package/dist/bootstrap-BIjHgG4v.d.mts.map +0 -1
  157. package/dist/container-BW_MlgLj.d.mts.map +0 -1
  158. package/dist/container-GbeejFhH.mjs +0 -117
  159. package/dist/container-GbeejFhH.mjs.map +0 -1
  160. package/dist/context-BnIvtMUl.mjs +0 -12
  161. package/dist/context-BnIvtMUl.mjs.map +0 -1
  162. package/dist/core-DVbWxSyr.mjs +0 -12
  163. package/dist/core-DVbWxSyr.mjs.map +0 -1
  164. package/dist/csrf-DGZqpNSq.mjs +0 -12
  165. package/dist/csrf-DGZqpNSq.mjs.map +0 -1
  166. package/dist/csrf-DwOhkOC6.d.mts +0 -51
  167. package/dist/csrf-DwOhkOC6.d.mts.map +0 -1
  168. package/dist/handler-9qFN7nRw.mjs +0 -13
  169. package/dist/handler-9qFN7nRw.mjs.map +0 -1
  170. package/dist/kv-stores-CbsEHE5J.d.mts.map +0 -1
  171. package/dist/kv-stores-pjWD0-ri.mjs +0 -12
  172. package/dist/kv-stores-pjWD0-ri.mjs.map +0 -1
  173. package/dist/rate-limit-D_cKx6Uw.d.mts.map +0 -1
  174. package/dist/reply-CdVb6ipw.mjs +0 -12
  175. package/dist/reply-CdVb6ipw.mjs.map +0 -1
  176. package/dist/router-builder-DptndWhd.d.mts.map +0 -1
  177. package/dist/router-builder-FukfDDLt.mjs +0 -12
  178. package/dist/router-builder-FukfDDLt.mjs.map +0 -1
  179. package/dist/spa-DhGwM3_A.d.mts.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"kv-stores-pjWD0-ri.mjs","names":[],"sources":["../src/http/middleware/rate-limit-guard.ts","../src/http/middleware/kv-stores.ts"],"sourcesContent":["// Ctx-style rate limiter — runs on BOTH pipelines: the node runtimes (via\n// `@Middleware()` / class middleware) and the `@forinda/kickjs/web` fetch\n// entry (via `@Middleware()` or `createWebApp({ middleware })`), unlike the\n// connect-style `rateLimit()` which is node-only. Zero runtime imports —\n// part of the edge purity graph.\nimport type { MiddlewareHandler } from '../../core/decorators'\nimport type { RequestContext } from '../context'\nimport type { RateLimitStore } from './rate-limit'\n\nexport interface RateLimitGuardOptions {\n /** Maximum number of requests per window (default: 100). */\n max?: number\n /** Time window in milliseconds (default: 60_000). */\n windowMs?: number\n /** Response message when the limit is exceeded (default: 'Too Many Requests'). */\n message?: string\n /** HTTP status code when the limit is exceeded (default: 429). */\n statusCode?: number\n /**\n * Rate-limit key per request. Default: `cf-connecting-ip`, then the first\n * hop of `x-forwarded-for`, then `x-real-ip`, then `'global'`. Return a\n * user/tenant id here for authenticated quotas.\n */\n keyGenerator?: (ctx: RequestContext) => string\n /** Send `X-RateLimit-*` / `Retry-After` headers (default: true). */\n headers?: boolean\n /**\n * Counter backend. Defaults to a per-isolate in-memory map — fine for a\n * single node process, useless on edge where isolates recycle: pass a\n * `KvRateLimitStore` (or Redis-backed store) there.\n */\n store?: RateLimitStore\n /** Skip limiting for a request (health checks, allowlists). */\n skip?: (ctx: RequestContext) => boolean\n}\n\n/** Timer-free in-memory store: sweeps expired entries when the map grows. */\nclass LazyMemoryStore implements RateLimitStore {\n private readonly hits = new Map<string, { hits: number; reset: number }>()\n\n constructor(private readonly windowMs: number) {}\n\n async increment(key: string): Promise<{ totalHits: number; resetTime: Date }> {\n const now = Date.now()\n const entry = this.hits.get(key)\n if (entry && entry.reset > now) {\n entry.hits++\n return { totalHits: entry.hits, resetTime: new Date(entry.reset) }\n }\n // ponytail: sweep-on-growth instead of a cleanup interval — no timer to\n // dispose, edge-safe. Bound is 10k live keys before a full sweep.\n if (this.hits.size > 10_000) {\n for (const [k, e] of this.hits) if (e.reset <= now) this.hits.delete(k)\n }\n const fresh = { hits: 1, reset: now + this.windowMs }\n this.hits.set(key, fresh)\n return { totalHits: 1, resetTime: new Date(fresh.reset) }\n }\n\n async decrement(key: string): Promise<void> {\n const entry = this.hits.get(key)\n if (entry && entry.hits > 0) entry.hits--\n }\n\n async reset(key: string): Promise<void> {\n this.hits.delete(key)\n }\n}\n\nfunction defaultKey(ctx: RequestContext): string {\n // `ctx.ip` carries the resolution that used to live here: prefer the\n // runtime-computed address (Express derives it from `trust proxy`), fall\n // back to forwarded headers only for runtimes that compute none.\n return ctx.ip ?? 'global'\n}\n\n/**\n * Rate limiting as a `(ctx, next)` middleware.\n *\n * ```ts\n * // Edge (Cloudflare Workers) — app-wide:\n * const app = createWebApp({\n * h3, modules,\n * middlewares: [rateLimitGuard({ max: 60, windowMs: 60_000, store: new KvRateLimitStore(env.KV, { windowMs: 60_000 }) })],\n * })\n *\n * // Any runtime — per controller/route:\n * @Middleware(rateLimitGuard({ max: 10 }))\n * @Post('/login')\n * ```\n */\nexport function rateLimitGuard(options: RateLimitGuardOptions = {}): MiddlewareHandler {\n const max = options.max ?? 100\n const windowMs = options.windowMs ?? 60_000\n const message = options.message ?? 'Too Many Requests'\n const statusCode = options.statusCode ?? 429\n const keyGenerator = options.keyGenerator ?? defaultKey\n const sendHeaders = options.headers ?? true\n const store = options.store ?? new LazyMemoryStore(windowMs)\n\n return async (ctx: RequestContext, next: () => void): Promise<void> => {\n if (options.skip?.(ctx)) return next()\n\n const { totalHits, resetTime } = await store.increment(keyGenerator(ctx))\n const remaining = Math.max(0, max - totalHits)\n\n if (sendHeaders) {\n ctx.setHeader('X-RateLimit-Limit', String(max))\n ctx.setHeader('X-RateLimit-Remaining', String(remaining))\n ctx.setHeader('X-RateLimit-Reset', String(Math.ceil(resetTime.getTime() / 1000)))\n }\n\n if (totalHits > max) {\n const retryAfter = Math.max(1, Math.ceil((resetTime.getTime() - Date.now()) / 1000))\n if (sendHeaders) ctx.setHeader('Retry-After', String(retryAfter))\n ctx.json({ message }, statusCode)\n return\n }\n\n next()\n }\n}\n","// Edge-safe store adapters over a minimal KV surface. Zero runtime imports —\n// this file must stay importable from the `@forinda/kickjs/web` purity graph.\nimport type { RateLimitStore } from './rate-limit'\nimport type { SessionData, SessionStore } from './session'\n\n/**\n * The minimal async KV surface both stores need. Structurally compatible\n * with a Cloudflare Workers `KVNamespace` binding — pass it straight in:\n *\n * ```ts\n * export default {\n * fetch(req: Request, env: Env) {\n * const store = new KvRateLimitStore(env.MY_KV, { windowMs: 60_000 })\n * // ...\n * },\n * }\n * ```\n *\n * Anything with the same shape works too (Deno KV wrapper, Upstash Redis\n * REST client adapter, a Map-backed fake in tests).\n */\nexport interface KvLike {\n get(key: string): Promise<string | null>\n put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>\n delete(key: string): Promise<void>\n}\n\n/** Cloudflare KV rejects TTLs under 60 seconds — clamp up, never down. */\nfunction kvTtlSeconds(ms: number): number {\n return Math.max(60, Math.ceil(ms / 1000))\n}\n\nexport interface KvRateLimitStoreOptions {\n /** Window length in ms — must match the `windowMs` given to the limiter. */\n windowMs: number\n /** Key prefix in the KV namespace (default `'rl:'`). */\n prefix?: string\n}\n\n/**\n * {@link RateLimitStore} over a {@link KvLike} namespace, for edge deployments\n * where in-memory counters reset on every isolate.\n *\n * KV is eventually consistent and this is a read-modify-write counter, so\n * limiting is **approximate** under concurrency — bursts racing across\n * isolates can each read the same count. That is the accepted trade-off for\n * abuse throttling; use a Durable Object / Redis store when you need an\n * exact ceiling (billing quotas, strict per-key locks).\n */\nexport class KvRateLimitStore implements RateLimitStore {\n private readonly prefix: string\n private readonly windowMs: number\n\n constructor(\n private readonly kv: KvLike,\n options: KvRateLimitStoreOptions,\n ) {\n this.windowMs = options.windowMs\n this.prefix = options.prefix ?? 'rl:'\n }\n\n async increment(key: string): Promise<{ totalHits: number; resetTime: Date }> {\n const now = Date.now()\n const entry = await this.read(key)\n if (entry && entry.reset > now) {\n entry.hits++\n await this.write(key, entry)\n return { totalHits: entry.hits, resetTime: new Date(entry.reset) }\n }\n const fresh = { hits: 1, reset: now + this.windowMs }\n await this.write(key, fresh)\n return { totalHits: 1, resetTime: new Date(fresh.reset) }\n }\n\n async decrement(key: string): Promise<void> {\n const entry = await this.read(key)\n if (entry && entry.hits > 0) {\n entry.hits--\n await this.write(key, entry)\n }\n }\n\n async reset(key: string): Promise<void> {\n await this.kv.delete(this.prefix + key)\n }\n\n private async read(key: string): Promise<{ hits: number; reset: number } | null> {\n const raw = await this.kv.get(this.prefix + key)\n if (raw === null) return null\n try {\n return JSON.parse(raw) as { hits: number; reset: number }\n } catch {\n return null\n }\n }\n\n private async write(key: string, entry: { hits: number; reset: number }): Promise<void> {\n await this.kv.put(this.prefix + key, JSON.stringify(entry), {\n expirationTtl: kvTtlSeconds(Math.max(entry.reset - Date.now(), 1000)),\n })\n }\n}\n\nexport interface KvSessionStoreOptions {\n /** Key prefix in the KV namespace (default `'sess:'`). */\n prefix?: string\n}\n\n/**\n * {@link SessionStore} over a {@link KvLike} namespace. Plug into the node\n * `session()` middleware today (e.g. backed by Upstash/Cloudflare KV via\n * REST) so sessions survive restarts and horizontal scaling; TTL expiry is\n * delegated to the KV layer via `expirationTtl`.\n *\n * Cost note: `touch()` is a get + put round-trip (KvLike has no TTL-only\n * refresh), so `session({ rolling: true })` doubles KV operations per\n * request — per-operation-billed platforms may prefer `rolling: false`.\n */\nexport class KvSessionStore implements SessionStore {\n private readonly prefix: string\n\n constructor(\n private readonly kv: KvLike,\n options: KvSessionStoreOptions = {},\n ) {\n this.prefix = options.prefix ?? 'sess:'\n }\n\n async get(sid: string): Promise<SessionData | null> {\n const raw = await this.kv.get(this.prefix + sid)\n if (raw === null) return null\n try {\n return JSON.parse(raw) as SessionData\n } catch {\n return null\n }\n }\n\n async set(sid: string, data: SessionData, maxAge: number): Promise<void> {\n await this.kv.put(this.prefix + sid, JSON.stringify(data), {\n expirationTtl: kvTtlSeconds(maxAge),\n })\n }\n\n async destroy(sid: string): Promise<void> {\n await this.kv.delete(this.prefix + sid)\n }\n\n async touch(sid: string, maxAge: number): Promise<void> {\n const data = await this.get(sid)\n if (data !== null) await this.set(sid, data, maxAge)\n }\n}\n"],"mappings":";;;;;;;;;;AAqCA,IAAM,gBAAN,KAAgD,CAGjB,SAF7B,KAAwB,IAAI,IAE5B,YAAY,SAAmC,CAAlB,KAAA,SAAA,QAAmB,CAEhD,MAAM,UAAU,IAA8D,CAC5E,IAAM,IAAM,KAAK,IAAI,EACf,MAAQ,KAAK,KAAK,IAAI,GAAG,EAC/B,GAAI,OAAS,MAAM,MAAQ,IAEzB,MADA,OAAM,OACC,CAAE,UAAW,MAAM,KAAM,UAAW,IAAI,KAAK,MAAM,KAAK,CAAE,EAInE,GAAI,KAAK,KAAK,KAAO,IACd,IAAA,GAAM,CAAC,EAAG,KAAM,KAAK,KAAU,EAAE,OAAS,KAAK,KAAK,KAAK,OAAO,CAAC,EAExE,IAAM,MAAQ,CAAE,KAAM,EAAG,MAAO,IAAM,KAAK,QAAS,EAEpD,OADA,KAAK,KAAK,IAAI,IAAK,KAAK,EACjB,CAAE,UAAW,EAAG,UAAW,IAAI,KAAK,MAAM,KAAK,CAAE,CAC1D,CAEA,MAAM,UAAU,IAA4B,CAC1C,IAAM,MAAQ,KAAK,KAAK,IAAI,GAAG,EAC3B,OAAS,MAAM,KAAO,GAAG,MAAM,MACrC,CAEA,MAAM,MAAM,IAA4B,CACtC,KAAK,KAAK,OAAO,GAAG,CACtB,CACF,EAEA,SAAS,WAAW,IAA6B,CAI/C,OAAO,IAAI,IAAM,QACnB,CAiBA,SAAgB,eAAe,QAAiC,CAAC,EAAsB,CACrF,IAAM,IAAM,QAAQ,KAAO,IACrB,SAAW,QAAQ,UAAY,IAC/B,QAAU,QAAQ,SAAW,oBAC7B,WAAa,QAAQ,YAAc,IACnC,aAAe,QAAQ,cAAgB,WACvC,YAAc,QAAQ,SAAW,GACjC,MAAQ,QAAQ,OAAS,IAAI,gBAAgB,QAAQ,EAE3D,OAAO,MAAO,IAAqB,OAAoC,CACrE,GAAI,QAAQ,OAAO,GAAG,EAAG,OAAO,KAAK,EAErC,GAAM,CAAE,UAAW,WAAc,MAAM,MAAM,UAAU,aAAa,GAAG,CAAC,EAClE,UAAY,KAAK,IAAI,EAAG,IAAM,SAAS,EAQ7C,GANI,cACF,IAAI,UAAU,oBAAqB,OAAO,GAAG,CAAC,EAC9C,IAAI,UAAU,wBAAyB,OAAO,SAAS,CAAC,EACxD,IAAI,UAAU,oBAAqB,OAAO,KAAK,KAAK,UAAU,QAAQ,EAAI,GAAI,CAAC,CAAC,GAG9E,UAAY,IAAK,CACnB,IAAM,WAAa,KAAK,IAAI,EAAG,KAAK,MAAM,UAAU,QAAQ,EAAI,KAAK,IAAI,GAAK,GAAI,CAAC,EAC/E,aAAa,IAAI,UAAU,cAAe,OAAO,UAAU,CAAC,EAChE,IAAI,KAAK,CAAE,OAAQ,EAAG,UAAU,EAChC,MACF,CAEA,KAAK,CACP,CACF,CC7FA,SAAS,aAAa,GAAoB,CACxC,OAAO,KAAK,IAAI,GAAI,KAAK,KAAK,GAAK,GAAI,CAAC,CAC1C,CAmBA,IAAa,iBAAb,KAAwD,CAKnC,GAJnB,OACA,SAEA,YACE,GACA,QACA,CAFiB,KAAA,GAAA,GAGjB,KAAK,SAAW,QAAQ,SACxB,KAAK,OAAS,QAAQ,QAAU,KAClC,CAEA,MAAM,UAAU,IAA8D,CAC5E,IAAM,IAAM,KAAK,IAAI,EACf,MAAQ,MAAM,KAAK,KAAK,GAAG,EACjC,GAAI,OAAS,MAAM,MAAQ,IAGzB,MAFA,OAAM,OACN,MAAM,KAAK,MAAM,IAAK,KAAK,EACpB,CAAE,UAAW,MAAM,KAAM,UAAW,IAAI,KAAK,MAAM,KAAK,CAAE,EAEnE,IAAM,MAAQ,CAAE,KAAM,EAAG,MAAO,IAAM,KAAK,QAAS,EAEpD,OADA,MAAM,KAAK,MAAM,IAAK,KAAK,EACpB,CAAE,UAAW,EAAG,UAAW,IAAI,KAAK,MAAM,KAAK,CAAE,CAC1D,CAEA,MAAM,UAAU,IAA4B,CAC1C,IAAM,MAAQ,MAAM,KAAK,KAAK,GAAG,EAC7B,OAAS,MAAM,KAAO,IACxB,MAAM,OACN,MAAM,KAAK,MAAM,IAAK,KAAK,EAE/B,CAEA,MAAM,MAAM,IAA4B,CACtC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAS,GAAG,CACxC,CAEA,MAAc,KAAK,IAA8D,CAC/E,IAAM,IAAM,MAAM,KAAK,GAAG,IAAI,KAAK,OAAS,GAAG,EAC/C,GAAI,MAAQ,KAAM,OAAO,KACzB,GAAI,CACF,OAAO,KAAK,MAAM,GAAG,CACvB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,MAAc,MAAM,IAAa,MAAuD,CACtF,MAAM,KAAK,GAAG,IAAI,KAAK,OAAS,IAAK,KAAK,UAAU,KAAK,EAAG,CAC1D,cAAe,aAAa,KAAK,IAAI,MAAM,MAAQ,KAAK,IAAI,EAAG,GAAI,CAAC,CACtE,CAAC,CACH,CACF,EAiBa,eAAb,KAAoD,CAI/B,GAHnB,OAEA,YACE,GACA,QAAiC,CAAC,EAClC,CAFiB,KAAA,GAAA,GAGjB,KAAK,OAAS,QAAQ,QAAU,OAClC,CAEA,MAAM,IAAI,IAA0C,CAClD,IAAM,IAAM,MAAM,KAAK,GAAG,IAAI,KAAK,OAAS,GAAG,EAC/C,GAAI,MAAQ,KAAM,OAAO,KACzB,GAAI,CACF,OAAO,KAAK,MAAM,GAAG,CACvB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,MAAM,IAAI,IAAa,KAAmB,OAA+B,CACvE,MAAM,KAAK,GAAG,IAAI,KAAK,OAAS,IAAK,KAAK,UAAU,IAAI,EAAG,CACzD,cAAe,aAAa,MAAM,CACpC,CAAC,CACH,CAEA,MAAM,QAAQ,IAA4B,CACxC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAS,GAAG,CACxC,CAEA,MAAM,MAAM,IAAa,OAA+B,CACtD,IAAM,KAAO,MAAM,KAAK,IAAI,GAAG,EAC3B,OAAS,MAAM,MAAM,KAAK,IAAI,IAAK,KAAM,MAAM,CACrD,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limit-D_cKx6Uw.d.mts","names":[],"sources":["../src/http/middleware/rate-limit.ts"],"mappings":";;;UAMiB;EACf,UAAU,cAAc;IAAU;IAAmB,WAAW;;EAChE,UAAU,cAAc;EACxB,MAAM,cAAc;;UAGL;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA,gBAAgB,KAAK;;EAErB;;EAEA,QAAQ;;EAER,QAAQ,KAAK;;EAEb;;;;;;;;;;;;;;;;;;;;;;iBAsFc,UAAU,UAAS,oBAAqB,KAgBnC,SAAO,KAAO,UAAQ,MAAQ,iBAAY"}
@@ -1,12 +0,0 @@
1
- /**
2
- * @forinda/kickjs v8.0.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{METADATA}from"./core/interfaces.mjs";import{createLogger}from"./core/logger.mjs";import{n as getClassMetaOrUndefined}from"./metadata-CeUKYNRZ.mjs";import{HttpException}from"./core/errors.mjs";import{i as requestStore}from"./context-BnIvtMUl.mjs";function normalizeMediaType(contentType){return((Array.isArray(contentType)?contentType[0]:contentType)??``).split(`;`)[0].trim().toLowerCase()}function isJsonMediaType(mediaType){return mediaType===`application/json`||/^application\/[\w.+-]+\+json$/.test(mediaType)}function classifyMediaType(contentType){let mediaType=normalizeMediaType(contentType);return mediaType?isJsonMediaType(mediaType)?`json`:mediaType===`application/x-www-form-urlencoded`?`urlencoded`:mediaType.startsWith(`multipart/`)?`multipart`:mediaType.startsWith(`text/`)?`text`:`unsupported`:`unsupported`}const EXPRESS_JSON_TYPES=[`application/json`,`application/*+json`],SUPPORTED_BODY_TYPES=[`application/json`,`application/*+json`,`application/x-www-form-urlencoded`,`text/*`,`multipart/form-data`];function unsupportedMediaTypeError(contentType){let mediaType=normalizeMediaType(contentType);return new HttpException(415,mediaType?`Unsupported Media Type: ${mediaType}`:`Unsupported Media Type: request has a body but no Content-Type`,void 0,{Accept:SUPPORTED_BODY_TYPES.join(`, `)})}const BODY_METHODS=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);function rejectUnsupportedBody(){return(req,_res,next)=>{if(!BODY_METHODS.has((req.method??`GET`).toUpperCase())||classifyMediaType(req.headers?.[`content-type`])!==`unsupported`)return next();let reject=()=>next(unsupportedMediaTypeError(req.headers?.[`content-type`])),length=req.headers?.[`content-length`];if(length!==void 0)return length===`0`?next():reject();if(req.headers?.[`transfer-encoding`]===void 0)return next();let decided=!1,settle=fn=>()=>{decided||(decided=!0,req.off?.(`data`,onData),req.off?.(`end`,onEnd),fn())},onData=settle(reject),onEnd=settle(()=>next());req.on?.(`data`,onData),req.on?.(`end`,onEnd)}}const log=createLogger(`RequestScope`),preDestroyCache=new Map;function preDestroyMethod(ctor){let method=preDestroyCache.get(ctor);return method===void 0&&(method=getClassMetaOrUndefined(METADATA.PRE_DESTROY,ctor.prototype)??null,preDestroyCache.set(ctor,method)),method}function disposeRequestStore(store){if(!store.disposed&&(store.disposed=!0,store.instances.size!==0)){for(let instance of store.instances.values()){if(typeof instance!=`object`||!instance)continue;let method=preDestroyMethod(instance.constructor);if(!(!method||typeof instance[method]!=`function`))try{let result=instance[method]();result&&typeof result.then==`function`&&result.catch(err=>log.error(err,`@PreDestroy on ${instance.constructor.name} rejected`))}catch(err){log.error(err,`@PreDestroy on ${instance.constructor.name} threw`)}}store.instances.clear()}}const REQUEST_SCOPE_MIDDLEWARE_MARKER=Symbol.for(`@kickjs/requestScopeMiddleware`);function createRequestStore(requestId){return{requestId:requestId||crypto.randomUUID(),instances:new Map,values:new Map}}function requestScopeMiddleware(){let mw=(req,res,next)=>{let requestIdHeader=req.headers[`x-request-id`],requestId=(Array.isArray(requestIdHeader)?requestIdHeader[0]:requestIdHeader)||crypto.randomUUID(),store=createRequestStore(requestId);req.requestId=requestId,res.once(`close`,()=>disposeRequestStore(store)),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}const REPLY_BRAND=Symbol.for(`kickjs.reply`);function reply(status,body){return{[REPLY_BRAND]:!0,status,body}}reply.ok=body=>reply(200,body),reply.created=body=>reply(201,body),reply.accepted=body=>reply(202,body),reply.noContent=()=>reply(204,void 0);function isReply(value){return typeof value==`object`&&!!value&&value[REPLY_BRAND]===!0}function applyHandlerResult(ctx,result){return result===void 0?!1:isReply(result)?result.status===204?(ctx.noContent(),!0):(ctx.json(result.body,result.status),!0):(ctx.json(result),!0)}export{disposeRequestStore as a,EXPRESS_JSON_TYPES as c,unsupportedMediaTypeError as d,createRequestStore as i,classifyMediaType as l,isReply as n,isRequestScopeMiddleware as o,reply as r,requestScopeMiddleware as s,applyHandlerResult as t,rejectUnsupportedBody as u};
12
- //# sourceMappingURL=reply-CdVb6ipw.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reply-CdVb6ipw.mjs","names":[],"sources":["../src/http/body-policy.ts","../src/http/middleware/request-scope.ts","../src/http/reply.ts"],"sourcesContent":["/**\n * How a request body is interpreted, decided in ONE place for every runtime.\n *\n * Before this, each engine brought its own library's opinion: Express parsed\n * only exact `application/json` and silently left everything else `undefined`,\n * Fastify answered 415 for anything it had no parser for, and h3 routed\n * unrecognised types through `destr` — so the same request produced a parsed\n * object, an empty body, or a 415 depending on which engine the app was\n * configured with. The runtime is meant to be swappable; that made it not\n * swappable for any app accepting a body outside `application/json` (#590).\n *\n * @module @forinda/kickjs/http/body-policy\n */\n\nimport { HttpException } from '../core/errors'\n\n/**\n * A header as the runtimes hand it over. Node's `IncomingHttpHeaders` and\n * Fastify's normalised map both allow `string[]`, so take the union here\n * rather than casting at every call site.\n */\nexport type HeaderValue = string | string[] | undefined\n\n/** What the framework does with a body of a given media type. */\nexport type BodyKind =\n /** Parse as JSON, strictly. */\n | 'json'\n /** Parse as `application/x-www-form-urlencoded` into an object. */\n | 'urlencoded'\n /** Hand the handler the raw string. Never JSON — see `classifyMediaType`. */\n | 'text'\n /** Left to the upload path, which consumes the stream itself. */\n | 'multipart'\n /** Not parsed. `ctx.body` stays undefined. */\n | 'unsupported'\n\n/**\n * Strip parameters and normalise: `Application/JSON; charset=utf-8` →\n * `application/json`. h3's own check is `===` against the bare type, which is\n * why `application/json; charset=utf-8` — what most clients actually send —\n * missed it and fell to a non-strict branch.\n */\nexport function normalizeMediaType(contentType: HeaderValue): string {\n const value = Array.isArray(contentType) ? contentType[0] : contentType\n return (value ?? '').split(';')[0].trim().toLowerCase()\n}\n\n/**\n * `application/*+json` is JSON, and this is not a judgement call: RFC 6838\n * §4.2.8 says a media type \"MUST NOT be given names incorporating suffixes for\n * structured syntaxes they do not actually employ\", and RFC 6839 §2 exists so\n * receivers can \"do generic processing of the underlying representation\".\n * Spring, ASP.NET Core and Hono's validator all match the suffix by default.\n *\n * So `application/merge-patch+json` (RFC 7396) and `application/problem+json`\n * (RFC 9457) parse as JSON — which the framework already emits for every\n * problem response, and previously could not read back.\n */\nfunction isJsonMediaType(mediaType: string): boolean {\n return mediaType === 'application/json' || /^application\\/[\\w.+-]+\\+json$/.test(mediaType)\n}\n\n/**\n * Decide how to treat a body, from its declared media type alone.\n *\n * `text/*` deliberately yields the raw string and is NEVER JSON-parsed.\n * `text/plain` is one of three CORS-safelisted content types, so it crosses\n * origins without a preflight; JSON-parsing it would re-open the simple-request\n * CSRF that requiring `application/json` closes. h3's own source carries this\n * warning, and h3 v2 pulled back from `destr` partly because of it.\n */\nexport function classifyMediaType(contentType: HeaderValue): BodyKind {\n const mediaType = normalizeMediaType(contentType)\n if (!mediaType) return 'unsupported'\n if (isJsonMediaType(mediaType)) return 'json'\n if (mediaType === 'application/x-www-form-urlencoded') return 'urlencoded'\n if (mediaType.startsWith('multipart/')) return 'multipart'\n if (mediaType.startsWith('text/')) return 'text'\n return 'unsupported'\n}\n\n/**\n * Media types Express should hand to its JSON body parser. `express.json()`\n * defaults to the exact string `application/json`, and `type-is` will not match\n * `application/json` against a bare wildcard-plus-json pattern (a length guard rejects\n * it), so BOTH entries are required — passing only the wildcard silently stops\n * parsing ordinary JSON.\n */\nexport const EXPRESS_JSON_TYPES = ['application/json', 'application/*+json'] as const\n\n/** Media types the framework claims to parse, for diagnostics and docs. */\nexport const SUPPORTED_BODY_TYPES = [\n 'application/json',\n 'application/*+json',\n 'application/x-www-form-urlencoded',\n 'text/*',\n 'multipart/form-data',\n] as const\n\n/**\n * The 415 for a body whose media type the framework does not parse.\n *\n * Only ever raised when a body was actually SENT. An absent or empty body is\n * treated as absent regardless of its declared type — the same call Spring's\n * `readWithMessageConverters` and ASP.NET's `BodyModelBinder` both make, and\n * the reason neither rejects a bodyless POST. A bare `POST` with no payload is\n * a legitimate request, not an unsupported one.\n *\n * Carries `Accept`, which RFC 9110 §15.5.16 says a 415 \"can\" use to indicate\n * which media types would have been accepted.\n */\nexport function unsupportedMediaTypeError(contentType: HeaderValue): HttpException {\n const mediaType = normalizeMediaType(contentType)\n return new HttpException(\n 415,\n mediaType\n ? `Unsupported Media Type: ${mediaType}`\n : 'Unsupported Media Type: request has a body but no Content-Type',\n undefined,\n { Accept: SUPPORTED_BODY_TYPES.join(', ') },\n )\n}\n\n/** Verbs whose body the framework reads. */\nconst BODY_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\n/**\n * Did this request actually carry a body? Decided from the framing headers\n * rather than from whether a parser produced anything — the distinction that\n * separates \"nothing was sent\" from \"something was sent that we could not\n * read\", which is the whole basis for answering 415 on one and 200 on the other.\n */\nexport function hasRequestBody(headers: Record<string, unknown>): boolean {\n const length = headers['content-length'] as string | undefined\n const encoding = headers['transfer-encoding'] as string | undefined\n return encoding !== undefined || (length !== undefined && length !== '0')\n}\n\n/**\n * Connect middleware that answers 415 for a body Express's parsers left\n * untouched.\n *\n * Express has no concept of an unsupported type: a parser whose `type` does not\n * match simply calls `next()` and leaves `req.body` undefined, indistinguishable\n * from a request that sent nothing. Mounted after the parsers, this restores the\n * distinction so Express agrees with the other runtimes.\n */\nexport function rejectUnsupportedBody() {\n return (req: any, _res: unknown, next: (err?: unknown) => void): void => {\n if (!BODY_METHODS.has((req.method ?? 'GET').toUpperCase())) return next()\n if (classifyMediaType(req.headers?.['content-type']) !== 'unsupported') return next()\n\n const reject = (): void => next(unsupportedMediaTypeError(req.headers?.['content-type']))\n const length = req.headers?.['content-length'] as string | undefined\n if (length !== undefined) return length === '0' ? next() : reject()\n if (req.headers?.['transfer-encoding'] === undefined) return next()\n\n // Chunked: the framing headers say a body MAY follow, but an empty chunked\n // post sends none. Deciding on the headers alone would 415 a request that\n // sent nothing, so wait to see whether a byte actually arrives — the same\n // distinction the other runtimes make by inspecting the read body.\n let decided = false\n const settle = (fn: () => void) => (): void => {\n if (decided) return\n decided = true\n req.off?.('data', onData)\n req.off?.('end', onEnd)\n fn()\n }\n const onData = settle(reject)\n const onEnd = settle(() => next())\n req.on?.('data', onData)\n req.on?.('end', onEnd)\n }\n}\n","// Web Crypto via globalThis.crypto (randomUUID) — portable across\n// node/bun/deno/workers; node:crypto deliberately NOT imported here so this\n// module stays on the edge-safe request path.\nimport type { Request, Response, NextFunction } from 'express'\nimport { requestStore, type RequestStore } from '../request-store'\nimport { METADATA } from '../../core/interfaces'\nimport { getClassMetaOrUndefined } from '../../core/metadata'\nimport { createLogger } from '../../core/logger'\n\nconst log = createLogger('RequestScope')\n\n// Per-class @PreDestroy method-name cache: the metadata is immutable after\n// class definition, so pay the Reflect read once per class, not once per\n// instance per request. `null` = \"checked, none declared\".\nconst preDestroyCache = new Map<Function, string | symbol | null>()\n\nfunction preDestroyMethod(ctor: Function): string | symbol | null {\n let method = preDestroyCache.get(ctor)\n if (method === undefined) {\n method =\n getClassMetaOrUndefined<string | symbol>(METADATA.PRE_DESTROY, (ctor as any).prototype) ??\n null\n preDestroyCache.set(ctor, method)\n }\n return method\n}\n\n/**\n * Run `@PreDestroy` hooks on every REQUEST-scoped instance the request\n * created, then drop them. Called when the response closes (finished or\n * aborted) — the counterpart to `@PostConstruct`, closing the lifecycle gap\n * where per-request services holding transactions/handles/subscriptions were\n * silently dropped with no cleanup callback.\n *\n * Idempotent (guarded by `store.disposed`); hook errors are logged and\n * swallowed so one failing teardown can't break request completion. Async\n * hooks are fired without being awaited — the response is already gone.\n */\nexport function disposeRequestStore(store: RequestStore): void {\n if (store.disposed) return\n store.disposed = true\n if (store.instances.size === 0) return\n for (const instance of store.instances.values()) {\n if (instance === null || typeof instance !== 'object') continue\n const method = preDestroyMethod(instance.constructor)\n if (!method || typeof instance[method] !== 'function') continue\n try {\n const result = instance[method]()\n if (result && typeof result.then === 'function') {\n ;(result as Promise<void>).catch((err) =>\n log.error(err, `@PreDestroy on ${instance.constructor.name} rejected`),\n )\n }\n } catch (err) {\n log.error(err, `@PreDestroy on ${instance.constructor.name} threw`)\n }\n }\n store.instances.clear()\n}\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 * Build a fresh per-request store. Shared by {@link requestScopeMiddleware}\n * (Express) and by runtimes whose handler runs outside the connect-middleware\n * chain (e.g. Fastify), so both establish an identical ALS frame.\n */\nexport function createRequestStore(requestId?: string): RequestStore {\n return {\n requestId: requestId || crypto.randomUUID(),\n instances: new Map(),\n // Explicit generic locks the \"string keys only\" decision.\n values: new Map<string, unknown>(),\n }\n}\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 = createRequestStore(requestId)\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 // 'close' fires once the response finished OR the client aborted — run\n // @PreDestroy teardown for the request's scoped instances either way.\n res.once('close', () => disposeRequestStore(store))\n // Pass `next` directly — `run` invokes it with no args, so the arrow\n // wrapper previously allocated per request added nothing.\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","// Return-value handler support (response-inference-design.md §3.1).\n//\n// Handlers may RETURN their response payload instead of calling `ctx.json`:\n// the runtime auto-sends it when the pipeline finishes with nothing written.\n// `reply(status, body)` wraps a payload with a non-200 status while staying\n// transparent to type inference (`Reply<201, Task>` carries both statically).\n//\n// Edge-safe: no node imports — shared by all four runtimes including the\n// `@forinda/kickjs/web` entry.\n\nimport type { RequestContext } from './context'\nimport type { RuntimeResponse } from './runtime'\n\n// `Symbol.for` so the brand survives duplicate module copies (workspace-linked\n// + published package in one process — same rationale as the request-scope\n// middleware marker).\nconst REPLY_BRAND = Symbol.for('kickjs.reply')\n\n/**\n * A status-carrying response payload for return-value handlers. Create via\n * {@link reply} — the brand is what runtimes detect, and the generics are\n * what the `kick/routes` response typegen unwraps (`Reply<S, T>` → `T`).\n */\nexport interface Reply<S extends number = number, T = unknown> {\n readonly [REPLY_BRAND]: true\n readonly status: S\n readonly body: T\n}\n\n/**\n * Wrap a return-value handler's payload with an explicit status code:\n *\n * ```ts\n * @Post('/')\n * async create(ctx: RequestContext) {\n * return reply(201, await this.tasks.create(ctx.body))\n * }\n * ```\n */\nexport function reply<S extends number, T>(status: S, body: T): Reply<S, T> {\n return { [REPLY_BRAND]: true, status, body }\n}\n\n/** `reply(200, body)` — explicit form of a bare `return body`. */\nreply.ok = <T>(body: T): Reply<200, T> => reply(200, body)\n/** `reply(201, body)` */\nreply.created = <T>(body: T): Reply<201, T> => reply(201, body)\n/** `reply(202, body)` */\nreply.accepted = <T>(body: T): Reply<202, T> => reply(202, body)\n/** Empty 204 — maps to `ctx.noContent()`. */\nreply.noContent = (): Reply<204, undefined> => reply(204, undefined)\n\n/**\n * The statically-inferred response type of a controller handler — what\n * `kick typegen` emits into `KickRoutes[...].response`:\n *\n * - `Awaited<ReturnType>` of the method\n * - `Reply<S, T>` unwraps to `T`\n * - `void`/`undefined` members are dropped (imperative `ctx.json` branches);\n * a handler that never returns a value stays `unknown`, matching the\n * pre-inference emission\n *\n * ```ts\n * type R = InferHandlerResponse<UsersController['get']> // → User\n * ```\n */\nexport type InferHandlerResponse<H> = H extends (...args: never[]) => infer R\n ? [Exclude<Awaited<R>, void | undefined>] extends [never]\n ? unknown\n : UnwrapReply<Exclude<Awaited<R>, void | undefined>>\n : unknown\n\ntype UnwrapReply<A> =\n A extends Reply<number, infer B>\n ? B\n : A extends RuntimeResponse\n ? // The engine's own response object says nothing about the body, so a\n // handler ending `return ctx.json(...)` degrades to `unknown` — which is\n // what the typegen comment always claimed happened. It did not: the bare\n // `RuntimeResponse` leaked into `KickRoutes[...].response`, offering\n // `.status()` / `.setHeader()` to a client expecting a payload.\n // Confidently wrong is worse than unknown.\n //\n // To carry a payload, return it (`return user`) or wrap it\n // (`return reply(201, user)`) — that is what `reply` is for.\n unknown\n : A\n\nexport function isReply(value: unknown): value is Reply {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[REPLY_BRAND] === true\n )\n}\n\n/**\n * Send a handler's RETURN value when the pipeline finished without anything\n * having written a response. Callers guard on \"driver/response unsettled\" —\n * this only decides how to send:\n *\n * - `undefined` → not handled (returns false; runtime keeps today's behavior)\n * - `Reply` wrapper → status + body (204 → `ctx.noContent()`)\n * - anything else → `ctx.json(value)` (200)\n *\n * Returns true when a response was produced.\n */\nexport function applyHandlerResult(ctx: RequestContext, result: unknown): boolean {\n if (result === undefined) return false\n if (isReply(result)) {\n // Only 204 maps to noContent — an undefined body must NOT coerce the\n // declared status (reply(304, undefined) keeps its 304; the json body\n // is simply empty).\n if (result.status === 204) {\n ctx.noContent()\n return true\n }\n ctx.json(result.body, result.status)\n return true\n }\n ctx.json(result)\n return true\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"router-builder-DptndWhd.d.mts","names":[],"sources":["../src/http/router-builder.ts"],"mappings":";;;;iBA+BgB,+BAA+B,kBAAkB;;;;;;;;;;;iBAcjD,kBACd,UAAU,qBACV,gBACA,kBACA;UAUe;;;;;;;EAOf,2BAA2B;;;;;;;;;;;;;;;;;;;;;;iBAuBb,gBACd,sBACA,UAAS,qBACR"}
@@ -1,12 +0,0 @@
1
- /**
2
- * @forinda/kickjs v8.0.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{METADATA}from"./core/interfaces.mjs";import{o as duplicateRouteError,t as Container}from"./container-GbeejFhH.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"./metadata-CeUKYNRZ.mjs";import{i as MissingContributorError,n as DuplicateContributorError,t as ContributorCycleError}from"./context-errors-vB3ZRXSu.mjs";const PRECEDENCE_ORDER=[`method`,`class`,`module`,`adapter`,`global`];function buildPipeline(sources,options={}){let route=options.route,bySource=new Map;for(let src of PRECEDENCE_ORDER)bySource.set(src,[]);for(let entry of sources)bySource.get(entry.source).push(entry);let winners=new Map;for(let src of PRECEDENCE_ORDER){let rows=bySource.get(src),keysAtThisLevel=new Map;for(let row of rows){let key=row.registration.key,existing=keysAtThisLevel.get(key)??[];existing.push(row),keysAtThisLevel.set(key,existing)}for(let[key,rowsForKey]of keysAtThisLevel){if(rowsForKey.length>1)throw new DuplicateContributorError(key,rowsForKey.map((r,i)=>r.label??`${src}#${i}`));winners.has(key)||winners.set(key,rowsForKey[0])}}let survivors=[...winners.values()].map(r=>r.registration),allKeys=new Set(survivors.map(r=>r.key));for(let reg of survivors)for(let dep of reg.dependsOn)if(!allKeys.has(dep))throw new MissingContributorError(dep,reg.key,route,reg.definedAt);let sorted=topoSort(survivors,route);return Object.freeze({contributors:Object.freeze(sorted),keys:allKeys})}function topoSort(registrations,route){let indegree=new Map,dependents=new Map,byKey=new Map;for(let reg of registrations)indegree.set(reg.key,reg.dependsOn.length),byKey.set(reg.key,reg);for(let reg of registrations)for(let dep of reg.dependsOn){let list=dependents.get(dep)??[];list.push(reg),dependents.set(dep,list)}let queue=[];for(let reg of registrations)indegree.get(reg.key)===0&&queue.push(reg);let sorted=[];for(;queue.length>0;){let node=queue.shift();sorted.push(node);for(let dependent of dependents.get(node.key)??[]){let next=(indegree.get(dependent.key)??0)-1;indegree.set(dependent.key,next),next===0&&queue.push(dependent)}}if(sorted.length!==registrations.length)throw new ContributorCycleError(reconstructCycle(registrations.filter(r=>(indegree.get(r.key)??0)>0),byKey),route);return sorted}function reconstructCycle(residual,byKey){let residualKeys=new Set(residual.map(r=>r.key)),start=residual[0].key,path=[start],seen=new Set([start]),cursor=start;for(;;){let nextDep=byKey.get(cursor).dependsOn.find(d=>residualKeys.has(d));if(nextDep===void 0)break;if(seen.has(nextDep)){let cycleStart=path.indexOf(nextDep);return[...path.slice(cycleStart),nextDep]}path.push(nextDep),seen.add(nextDep),cursor=nextDep}return path}async function runContributors(options){let{pipeline,ctx,container}=options;for(let reg of pipeline.contributors)await runOne(reg,ctx,container)}async function runOne(reg,ctx,container){let value;try{let deps=resolveDeps(reg,container);value=await reg.resolve(ctx,deps)}catch(err){if(reg.optional)return;if(reg.onError){let replacement=await reg.onError(err,ctx);if(replacement===void 0)return;ctx.set(reg.key,replacement);return}throw err}ctx.set(reg.key,value)}function resolveDeps(reg,container){let out={};for(let[key,token]of Object.entries(reg.deps))out[key]=container.resolve(token);return out}let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function assertRouteUnique(registry,method,fullPath,owner){let key=`${method.toUpperCase()} ${fullPath.replace(/:[^/]+/g,`:*`)}`,prior=registry.get(key);if(prior!==void 0)throw duplicateRouteError(method.toUpperCase(),fullPath,owner,prior);registry.set(key,owner)}function buildRouteTable(controllerClass,options={}){let container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]),entries=[];for(let route of routes){let method=route.method.toUpperCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName),methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]),contributorRunner=null;if(classContributors.length>0||methodContributors.length>0||externalSources.length>0){let pipeline=buildPipeline([...methodContributors.map((registration,i)=>({source:`method`,registration,label:`${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`})),...classContributors.map((registration,i)=>({source:`class`,registration,label:`${controllerClass.name}.@class#${i}(${registration.key})`})),...externalSources],{route:`${method} ${fullPath}`});contributorRunner=ctx=>runContributors({pipeline,ctx,container})}let handler=ctx=>container.resolve(controllerClass)[route.handlerName](ctx);entries.push({method,path:fullPath,middlewares:[...classMiddlewares,...methodMiddlewares],contributorRunner,handler,meta:{controller:controllerClass,handlerName:route.handlerName,validation:route.validation,upload:fileUploadConfig}})}return entries}export{buildPipeline as a,runContributors as i,assertRouteUnique as n,buildRouteTable as r,_setExternalContributorSources as t};
12
- //# sourceMappingURL=router-builder-FukfDDLt.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"router-builder-FukfDDLt.mjs","names":[],"sources":["../src/core/contributor-pipeline.ts","../src/core/contributor-runner.ts","../src/http/router-builder.ts"],"sourcesContent":["import type { AnyContributorRegistration } from './context-decorator'\nimport {\n ContributorCycleError,\n DuplicateContributorError,\n MissingContributorError,\n} from './context-errors'\n\n/**\n * Where a contributor was registered, ordered by precedence\n * (lower index wins). Mirrors `architecture.md` §20.4.\n *\n * Levels (highest → lowest precedence):\n * - `'method'` — `@LoadX` on a controller method.\n * - `'class'` — `@LoadX` on a controller class.\n * - `'module'` — returned by `AppModule.contributors?()`. Per-module scope.\n * - `'adapter'` — returned by `AppAdapter.contributors?()`. Cross-cutting, applies\n * to every module's routes.\n * - `'global'` — `ApplicationOptions.contributors`. App-wide default.\n */\nexport type ContributorSource = 'method' | 'class' | 'module' | 'adapter' | 'global'\n\n/** Precedence-ranked source list. Index = precedence; method (0) wins ties. */\nconst PRECEDENCE_ORDER: readonly ContributorSource[] = [\n 'method',\n 'class',\n 'module',\n 'adapter',\n 'global',\n]\n\n/**\n * Input row to {@link buildPipeline}. Pairs a registration with its origin\n * so dedup can apply the precedence rule and diagnostic errors can name\n * the offending site.\n *\n * `registration` is the type-erased {@link AnyContributorRegistration}\n * — the runner doesn't care about each registration's narrow `D` /\n * `Ctx` shape, and adopters frequently mix HTTP / transport-agnostic\n * contributors in the same `externalSources` / `contributors()`\n * array. The narrow generics from `defineContextDecorator` survive\n * at the *definition* site (where `resolve` is typed); they're erased\n * here so collections compose without `as any` casts.\n */\nexport interface SourcedRegistration {\n registration: AnyContributorRegistration\n source: ContributorSource\n /** Human label surfaced in DuplicateContributorError messages. */\n label?: string\n}\n\n/**\n * Resolved, validated, topo-sorted pipeline ready for execution.\n *\n * `contributors` is in execution order — every entry's `dependsOn`\n * keys appear earlier. `keys` is the set of all keys this pipeline\n * produces; the runner uses it for fast existence checks and the\n * builder uses it for missing-dep validation.\n */\nexport interface ContributorPipeline {\n readonly contributors: readonly AnyContributorRegistration[]\n readonly keys: ReadonlySet<string>\n}\n\nexport interface BuildPipelineOptions {\n /** Optional route identifier surfaced in diagnostic errors (e.g., 'GET /projects/:id'). */\n route?: string\n}\n\n/**\n * Build, validate, and topo-sort a contributor pipeline.\n *\n * Pipeline:\n *\n * 1. Group sources by precedence (method > class > module > adapter > global).\n * 2. Within each precedence level, two contributors with the same `key`\n * raise {@link DuplicateContributorError}.\n * 3. Across levels, the highest-precedence contributor wins each key\n * (lower-precedence duplicates are silently dropped — that is the\n * documented override mechanism, not an error).\n * 4. Every key referenced by `dependsOn` must be produced by the\n * surviving set, otherwise {@link MissingContributorError}.\n * 5. Kahn's algorithm topo-sorts the survivors. Any nodes left after\n * the queue drains form a cycle → {@link ContributorCycleError}\n * with a reconstructed path.\n *\n * Pure function — no DI, no I/O. Safe to call once at startup per\n * route and cache the result.\n */\nexport function buildPipeline(\n sources: readonly SourcedRegistration[],\n options: BuildPipelineOptions = {},\n): ContributorPipeline {\n const route = options.route\n\n // Step 1: group by source.\n const bySource = new Map<ContributorSource, SourcedRegistration[]>()\n for (const src of PRECEDENCE_ORDER) bySource.set(src, [])\n for (const entry of sources) bySource.get(entry.source)!.push(entry)\n\n // Step 2 + 3: dedup with precedence. We walk highest precedence first,\n // claim its keys, and drop lower-precedence rows that conflict.\n const winners = new Map<string, SourcedRegistration>()\n for (const src of PRECEDENCE_ORDER) {\n const rows = bySource.get(src)!\n const keysAtThisLevel = new Map<string, SourcedRegistration[]>()\n for (const row of rows) {\n const key = row.registration.key\n const existing = keysAtThisLevel.get(key) ?? []\n existing.push(row)\n keysAtThisLevel.set(key, existing)\n }\n for (const [key, rowsForKey] of keysAtThisLevel) {\n if (rowsForKey.length > 1) {\n const labels = rowsForKey.map((r, i) => r.label ?? `${src}#${i}`)\n throw new DuplicateContributorError(key, labels)\n }\n if (!winners.has(key)) winners.set(key, rowsForKey[0])\n }\n }\n\n const survivors = [...winners.values()].map((r) => r.registration)\n const allKeys = new Set(survivors.map((r) => r.key))\n\n // Step 4: dependsOn validation.\n for (const reg of survivors) {\n for (const dep of reg.dependsOn) {\n if (!allKeys.has(dep)) {\n throw new MissingContributorError(dep, reg.key, route, reg.definedAt)\n }\n }\n }\n\n // Step 5: Kahn topo-sort.\n const sorted = topoSort(survivors, route)\n\n return Object.freeze({\n contributors: Object.freeze(sorted),\n keys: allKeys,\n })\n}\n\n/**\n * Kahn's algorithm with deterministic ordering on ties.\n *\n * Nodes with no remaining incoming edges enter the queue in input\n * order. Cycles surface as nodes still present after the queue\n * drains; we reconstruct one cycle path for diagnostics.\n */\nfunction topoSort(\n registrations: readonly AnyContributorRegistration[],\n route: string | undefined,\n): AnyContributorRegistration[] {\n // indegree[key] = number of unresolved dependsOn entries for key\n const indegree = new Map<string, number>()\n // dependents[depKey] = list of registrations that depend on depKey\n const dependents = new Map<string, AnyContributorRegistration[]>()\n const byKey = new Map<string, AnyContributorRegistration>()\n\n for (const reg of registrations) {\n indegree.set(reg.key, reg.dependsOn.length)\n byKey.set(reg.key, reg)\n }\n for (const reg of registrations) {\n for (const dep of reg.dependsOn) {\n const list = dependents.get(dep) ?? []\n list.push(reg)\n dependents.set(dep, list)\n }\n }\n\n // Initial queue: registrations with zero indegree, preserving input order.\n const queue: AnyContributorRegistration[] = []\n for (const reg of registrations) {\n if (indegree.get(reg.key) === 0) queue.push(reg)\n }\n\n const sorted: AnyContributorRegistration[] = []\n while (queue.length > 0) {\n const node = queue.shift()!\n sorted.push(node)\n for (const dependent of dependents.get(node.key) ?? []) {\n const next = (indegree.get(dependent.key) ?? 0) - 1\n indegree.set(dependent.key, next)\n if (next === 0) queue.push(dependent)\n }\n }\n\n if (sorted.length !== registrations.length) {\n const remaining = registrations.filter((r) => (indegree.get(r.key) ?? 0) > 0)\n throw new ContributorCycleError(reconstructCycle(remaining, byKey), route)\n }\n\n return sorted\n}\n\n/**\n * Reconstruct one cycle path from the residual graph.\n *\n * Walks from any remaining node, following its first dependency that's\n * still in the residual set, until we revisit a node. The path returned\n * starts and ends with the same key so the message reads naturally:\n * `tenant → project → tenant`.\n */\nfunction reconstructCycle(\n residual: readonly AnyContributorRegistration[],\n byKey: ReadonlyMap<string, AnyContributorRegistration>,\n): string[] {\n const residualKeys = new Set(residual.map((r) => r.key))\n const start = residual[0].key\n const path: string[] = [start]\n const seen = new Set<string>([start])\n let cursor: string = start\n\n while (true) {\n const reg = byKey.get(cursor)!\n const nextDep = reg.dependsOn.find((d) => residualKeys.has(d))\n if (nextDep === undefined) break\n if (seen.has(nextDep)) {\n // Trim the prefix that isn't part of the cycle.\n const cycleStart = path.indexOf(nextDep)\n return [...path.slice(cycleStart), nextDep]\n }\n path.push(nextDep)\n seen.add(nextDep)\n cursor = nextDep\n }\n\n // Fallback — couldn't reconstruct a strict cycle, return what we walked.\n return path\n}\n","import type { Container } from './container'\nimport type { ContributorRegistration } from './context-decorator'\nimport type { ExecutionContext } from './execution-context'\nimport type { ContributorPipeline } from './contributor-pipeline'\n\nexport interface RunContributorsOptions {\n /** Pre-built, validated, topo-sorted pipeline. */\n pipeline: ContributorPipeline\n /** The execution context contributors read from and write into. */\n ctx: ExecutionContext\n /** DI container used to resolve declared `deps`. */\n container: Container\n}\n\n/**\n * Execute a built {@link ContributorPipeline} against an {@link ExecutionContext}.\n *\n * Sequential — one contributor at a time, in the topo order chosen at\n * pipeline-build time. Per `architecture.md` §20.10, V1 does not run\n * contributors in parallel even within the same topo level; that is a\n * V2 follow-up gated on profiling.\n *\n * ## Error matrix (architecture.md §20.9)\n *\n * | resolve outcome | optional | onError defined | runner behaviour |\n * |-----------------|----------|-----------------|--------------------------------------------------|\n * | throws | true | — | skip; `ctx.get(key)` remains undefined |\n * | throws | false | yes | call hook; returned value (if any) stored |\n * | throws | false | no | propagate the original error |\n * | hook throws | — | yes | propagate the new error |\n * | hook returns | — | yes | undefined/void → skip; value → `ctx.set(key, v)` |\n * | resolves | — | — | `ctx.set(key, value)` |\n *\n * Container.resolve() throwing while building `deps` is treated as a\n * resolve-side throw and flows through the same matrix — adopters who\n * need different behaviour for missing-DI vs. data-lookup failures can\n * wrap the container access inside their own `resolve()`.\n */\nexport async function runContributors(options: RunContributorsOptions): Promise<void> {\n const { pipeline, ctx, container } = options\n\n for (const reg of pipeline.contributors) {\n await runOne(reg, ctx, container)\n }\n}\n\nasync function runOne(\n reg: ContributorRegistration,\n ctx: ExecutionContext,\n container: Container,\n): Promise<void> {\n let value: unknown\n try {\n const deps = resolveDeps(reg, container)\n value = await reg.resolve(ctx, deps)\n } catch (err) {\n if (reg.optional) return\n if (reg.onError) {\n // Hook throws are unconditionally propagated — there is no\n // second-chance hook. Returning undefined/void from the hook\n // means \"skip\"; returning a value means \"use this instead\".\n const replacement = await reg.onError(err, ctx)\n if (replacement === undefined) return\n ctx.set(reg.key, replacement as never)\n return\n }\n throw err\n }\n ctx.set(reg.key, value as never)\n}\n\n/**\n * Resolve a contributor's declared dependencies through the container,\n * preserving the property names from the spec so `resolve(ctx, deps)`\n * receives exactly the shape the user wrote.\n *\n * Empty `deps` produces an empty object — `resolve()` always sees an\n * object, never `undefined`.\n */\nfunction resolveDeps(reg: ContributorRegistration, container: Container): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [key, token] of Object.entries(reg.deps)) {\n out[key] = container.resolve(token as never)\n }\n return out\n}\n","// Concrete-module imports (NOT the `../core` barrel): the barrel re-exports\n// the asset manager, whose eager `node:fs` import would poison the edge-safe\n// `@forinda/kickjs/web` entry graph that flows through this file.\nimport { buildPipeline, type SourcedRegistration } from '../core/contributor-pipeline'\nimport { runContributors } from '../core/contributor-runner'\nimport { Container } from '../core/container'\nimport { METADATA } from '../core/interfaces'\nimport type { ContributorRegistration } from '../core/context-decorator'\nimport type { FileUploadConfig, MiddlewareHandler, RouteDefinition } from '../core/decorators'\nimport { getClassMeta, getMethodMeta, getMethodMetaOrUndefined } from '../core/metadata'\nimport { duplicateRouteError } from '../core/kick-errors'\nimport type { CtxHandler, RouteEntry, RouteMethod } from './runtime'\n\n/**\n * Per-module SourcedRegistration[] threaded through the route-mount loop by\n * Application.setup(). Carries module + adapter + global contributors so\n * buildRouteTable() can merge them with class + method ones into a single pipeline.\n *\n * Module setup is sequential, so this slot is race-free. Cleared in a finally\n * block after each module mounts. Outside of Application setup the slot is\n * empty — direct buildRouteTable()/buildRoutes() callers (mostly tests) see only\n * class + method contributors unless they pass `externalSources` explicitly.\n *\n * Same idiom as `Container._requestStoreProvider`: an internal escape hatch for\n * cross-module wiring without inversion-of-control gymnastics.\n *\n * @internal\n */\nlet _externalContributorSources: readonly SourcedRegistration[] = []\n\n/** @internal — set by Application.setup() before each `mod.routes()` call. */\nexport function _setExternalContributorSources(sources: readonly SourcedRegistration[]): void {\n _externalContributorSources = sources\n}\n\n/**\n * Boot-time duplicate-route guard shared by the node and web mount loops.\n *\n * Registers `METHOD /mounted/path` into `registry` and throws\n * {@link duplicateRouteError} (KICK006) if another handler already claimed\n * it. Param *names* are ignored — `/tasks/:id` and `/tasks/:taskId` are the\n * same route at dispatch time, so they collide here too.\n *\n * @internal\n */\nexport function assertRouteUnique(\n registry: Map<string, string>,\n method: string,\n fullPath: string,\n owner: string,\n): void {\n const key = `${method.toUpperCase()} ${fullPath.replace(/:[^/]+/g, ':*')}`\n const prior = registry.get(key)\n if (prior !== undefined) {\n throw duplicateRouteError(method.toUpperCase(), fullPath, owner, prior)\n }\n registry.set(key, owner)\n}\n\nexport interface BuildRoutesOptions {\n /**\n * Extra contributors to merge into the per-route pipeline at their declared\n * precedence levels. Pass explicitly when calling buildRouteTable/buildRoutes\n * outside the Application route-mount loop (typically in tests). When omitted,\n * falls back to the slot set by Application.setup().\n */\n externalSources?: readonly SourcedRegistration[]\n}\n\n/**\n * Turn a controller class decorated with @Get, @Post, etc. into a plain-data\n * {@link RouteEntry}[] — the engine-neutral route table an {@link HttpRuntime}\n * materializes (see `docs/http/spec-http-runtimes.md`, Avenue B).\n *\n * What used to be per-handler Express `(req, res, next)` closures is now\n * captured as data: `middlewares` keep their `(ctx, next)` shape, the contributor\n * pipeline is pre-built into a `contributorRunner` closure, validation / upload\n * stay as metadata, and the terminal `handler` resolves the controller per-request\n * (to respect DI scoping) and invokes it. The runtime owns how these get wrapped\n * onto its engine.\n *\n * Routes use only the method-level decorator paths (e.g. @Get('/me') → '/me').\n * The @Controller path is NOT baked in — it is metadata for Swagger/introspection.\n * The module's routes().path is the single source of truth for the mount prefix,\n * which avoids path doubling when both the module and controller specify the path.\n *\n * The contributor pipeline is built here, so a cycle or missing `dependsOn`\n * throws at table-build time (boot) rather than on first request.\n */\nexport function buildRouteTable(\n controllerClass: any,\n options: BuildRoutesOptions = {},\n): RouteEntry[] {\n const container = Container.getInstance()\n const externalSources = options.externalSources ?? _externalContributorSources\n const routes: RouteDefinition[] = getClassMeta<RouteDefinition[]>(\n METADATA.ROUTES,\n controllerClass,\n [],\n )\n\n // Class-level middleware\n const classMiddlewares: MiddlewareHandler[] = getClassMeta<MiddlewareHandler[]>(\n METADATA.CLASS_MIDDLEWARES,\n controllerClass,\n [],\n )\n\n // Class-level Context Contributors (#107) — applied to every method on the\n // controller. Method-level contributors are added per-route below.\n const classContributors: ContributorRegistration[] = getClassMeta<ContributorRegistration[]>(\n METADATA.CLASS_CONTRIBUTORS,\n controllerClass,\n [],\n )\n\n const entries: RouteEntry[] = []\n\n for (const route of routes) {\n const method = route.method.toUpperCase() as RouteMethod\n const fullPath = route.path || '/'\n\n // Method-level middleware\n const methodMiddlewares: MiddlewareHandler[] = getMethodMeta<MiddlewareHandler[]>(\n METADATA.METHOD_MIDDLEWARES,\n controllerClass,\n route.handlerName,\n [],\n )\n\n // @FileUpload decorator — runtime supplies the upload backend.\n const fileUploadConfig = getMethodMetaOrUndefined<FileUploadConfig>(\n METADATA.FILE_UPLOAD,\n controllerClass,\n route.handlerName,\n )\n\n // Context Contributor pipeline (#107) — class + method contributors,\n // built once at mount time so cycle/missing-dep failures abort boot.\n const methodContributors: ContributorRegistration[] = getMethodMeta<ContributorRegistration[]>(\n METADATA.METHOD_CONTRIBUTORS,\n controllerClass,\n route.handlerName,\n [],\n )\n\n let contributorRunner: CtxHandler | null = null\n if (\n classContributors.length > 0 ||\n methodContributors.length > 0 ||\n externalSources.length > 0\n ) {\n // Labels are surfaced in DuplicateContributorError messages — include\n // the registration's key + the original index so a same-key collision\n // at one precedence level points at the conflicting decorator slot,\n // not just the host method/class.\n const sources: SourcedRegistration[] = [\n ...methodContributors.map((registration, i): SourcedRegistration => ({\n source: 'method',\n registration,\n label: `${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`,\n })),\n ...classContributors.map((registration, i): SourcedRegistration => ({\n source: 'class',\n registration,\n label: `${controllerClass.name}.@class#${i}(${registration.key})`,\n })),\n ...externalSources,\n ]\n const pipeline = buildPipeline(sources, {\n route: `${method} ${fullPath}`,\n })\n contributorRunner = (ctx) => runContributors({ pipeline, ctx, container })\n }\n\n // Terminal handler — resolve controller per-request to respect DI scoping.\n const handler: CtxHandler = (ctx) => container.resolve(controllerClass)[route.handlerName](ctx)\n\n entries.push({\n method,\n path: fullPath,\n middlewares: [...classMiddlewares, ...methodMiddlewares],\n contributorRunner,\n handler,\n meta: {\n controller: controllerClass,\n handlerName: route.handlerName,\n validation: route.validation,\n upload: fileUploadConfig,\n },\n })\n }\n\n return entries\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"spa-DhGwM3_A.d.mts","names":[],"sources":["../src/core/disposables.ts","../src/core/kick-error.ts","../src/core/circuit-breaker.ts","../src/core/context-errors.ts","../src/core/mount-sort.ts","../src/core/asset-keys.ts","../src/http/health-module.ts","../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/runtimes/express.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts","../src/http/middleware/spa.ts"],"mappings":"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"}