@forinda/kickjs 6.1.1 → 6.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 (166) hide show
  1. package/dist/{adapter-D_uzU-QA.d.mts → adapter-DCfEFZMY.d.mts} +33 -10
  2. package/dist/adapter-DCfEFZMY.d.mts.map +1 -0
  3. package/dist/{app-module-CuAS6hJC.d.mts → app-module-BwYtACD6.d.mts} +3 -3
  4. package/dist/{app-module-CuAS6hJC.d.mts.map → app-module-BwYtACD6.d.mts.map} +1 -1
  5. package/dist/application-DS7t40r-.mjs +12 -0
  6. package/dist/application-DS7t40r-.mjs.map +1 -0
  7. package/dist/{application-CFenLjbY.d.mts → application-p8RRz7p6.d.mts} +4 -4
  8. package/dist/{application-CFenLjbY.d.mts.map → application-p8RRz7p6.d.mts.map} +1 -1
  9. package/dist/{bootstrap-Dj96_O6j.mjs → bootstrap-DESuFm-t.mjs} +3 -3
  10. package/dist/{bootstrap-Dj96_O6j.mjs.map → bootstrap-DESuFm-t.mjs.map} +1 -1
  11. package/dist/{bootstrap-DodgsiDG.d.mts → bootstrap-JXdD0wVC.d.mts} +2 -2
  12. package/dist/{bootstrap-DodgsiDG.d.mts.map → bootstrap-JXdD0wVC.d.mts.map} +1 -1
  13. package/dist/{cache-BSFDtsEG.d.mts → cache-CwZzIH0D.d.mts} +9 -11
  14. package/dist/cache-CwZzIH0D.d.mts.map +1 -0
  15. package/dist/config/index.d.mts +1 -1
  16. package/dist/config/index.mjs +2 -2
  17. package/dist/{config-sXLOhrp4.mjs → config-CqN6u5CP.mjs} +3 -3
  18. package/dist/{config-sXLOhrp4.mjs.map → config-CqN6u5CP.mjs.map} +1 -1
  19. package/dist/container-CkcVS_gA.mjs +108 -0
  20. package/dist/container-CkcVS_gA.mjs.map +1 -0
  21. package/dist/{container-TWOUyR_0.d.mts → container-iED1swkd.d.mts} +19 -3
  22. package/dist/container-iED1swkd.d.mts.map +1 -0
  23. package/dist/context-Cd-p7DfC.mjs +12 -0
  24. package/dist/context-Cd-p7DfC.mjs.map +1 -0
  25. package/dist/contributor-runner-BP9rOU_2.mjs +13 -0
  26. package/dist/contributor-runner-BP9rOU_2.mjs.map +1 -0
  27. package/dist/core/adapter.d.mts +1 -1
  28. package/dist/core/adapter.mjs +1 -1
  29. package/dist/core/app-module.d.mts +1 -1
  30. package/dist/core/app-module.mjs +1 -1
  31. package/dist/core/cache.d.mts +1 -1
  32. package/dist/core/cache.mjs +2 -2
  33. package/dist/core/cache.mjs.map +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 +2 -2
  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 +2 -2
  45. package/dist/core/logger.mjs.map +1 -1
  46. package/dist/core/path.mjs +1 -1
  47. package/dist/core/plugin.d.mts +1 -1
  48. package/dist/core/plugin.mjs +1 -1
  49. package/dist/core/reactivity.d.mts +1 -1
  50. package/dist/core/reactivity.mjs +2 -2
  51. package/dist/core/reactivity.mjs.map +1 -1
  52. package/dist/core-BBnRIW_2.mjs +12 -0
  53. package/dist/core-BBnRIW_2.mjs.map +1 -0
  54. package/dist/csrf-7nUXGTuz.d.mts.map +1 -1
  55. package/dist/csrf-CrChcN0K.mjs +12 -0
  56. package/dist/csrf-CrChcN0K.mjs.map +1 -0
  57. package/dist/{decorators-ejABLo9H.d.mts → decorators-DKjpujFp.d.mts} +13 -4
  58. package/dist/decorators-DKjpujFp.d.mts.map +1 -0
  59. package/dist/disposables-msQG0AHt.mjs +12 -0
  60. package/dist/disposables-msQG0AHt.mjs.map +1 -0
  61. package/dist/env-CrvPEW3K.mjs +13 -0
  62. package/dist/{env-RP8lbK-Z.mjs.map → env-CrvPEW3K.mjs.map} +1 -1
  63. package/dist/handler-BMUwLMu_.mjs +12 -0
  64. package/dist/handler-BMUwLMu_.mjs.map +1 -0
  65. package/dist/http/application.d.mts +1 -1
  66. package/dist/http/application.mjs +2 -2
  67. package/dist/http/bootstrap.d.mts +1 -1
  68. package/dist/http/bootstrap.mjs +2 -2
  69. package/dist/http/context.d.mts +2 -2
  70. package/dist/http/context.mjs +2 -2
  71. package/dist/http/middleware/cors.mjs +1 -1
  72. package/dist/http/middleware/csrf.mjs +2 -3
  73. package/dist/http/middleware/error-handler.mjs +2 -2
  74. package/dist/http/middleware/helmet.mjs +1 -1
  75. package/dist/http/middleware/rate-limit.mjs +2 -2
  76. package/dist/http/middleware/rate-limit.mjs.map +1 -1
  77. package/dist/http/middleware/request-id.mjs +2 -2
  78. package/dist/http/middleware/request-id.mjs.map +1 -1
  79. package/dist/http/middleware/request-logger.mjs +2 -2
  80. package/dist/http/middleware/session.mjs +2 -2
  81. package/dist/http/middleware/session.mjs.map +1 -1
  82. package/dist/http/middleware/spa.d.mts +1 -1
  83. package/dist/http/middleware/spa.mjs +2 -2
  84. package/dist/http/middleware/spa.mjs.map +1 -1
  85. package/dist/http/middleware/upload.d.mts +2 -2
  86. package/dist/http/middleware/upload.mjs +3 -3
  87. package/dist/http/middleware/upload.mjs.map +1 -1
  88. package/dist/http/middleware/validate.mjs +2 -2
  89. package/dist/http/middleware/validate.mjs.map +1 -1
  90. package/dist/http/middleware/views.d.mts +1 -1
  91. package/dist/http/middleware/views.mjs +2 -2
  92. package/dist/http/query/index.mjs +2 -2
  93. package/dist/http/router-builder.d.mts +1 -1
  94. package/dist/http/router-builder.mjs +2 -2
  95. package/dist/http/router-builder.mjs.map +1 -1
  96. package/dist/http/runtimes/fastify.d.mts +1 -1
  97. package/dist/http/runtimes/fastify.d.mts.map +1 -1
  98. package/dist/http/runtimes/fastify.mjs +3 -3
  99. package/dist/http/runtimes/fastify.mjs.map +1 -1
  100. package/dist/http/runtimes/h3-web.d.mts +52 -0
  101. package/dist/http/runtimes/h3-web.d.mts.map +1 -0
  102. package/dist/http/runtimes/h3-web.mjs +12 -0
  103. package/dist/http/runtimes/h3-web.mjs.map +1 -0
  104. package/dist/http/runtimes/h3.d.mts +10 -4
  105. package/dist/http/runtimes/h3.d.mts.map +1 -1
  106. package/dist/http/runtimes/h3.mjs +2 -2
  107. package/dist/http/runtimes/h3.mjs.map +1 -1
  108. package/dist/{index-zIcb4wjg.d.mts → index-ChK5rOgq.d.mts} +2 -2
  109. package/dist/{index-zIcb4wjg.d.mts.map → index-ChK5rOgq.d.mts.map} +1 -1
  110. package/dist/index.d.mts +16 -15
  111. package/dist/index.mjs +2 -2
  112. package/dist/index.mjs.map +1 -1
  113. package/dist/{interfaces-DbTyKGdS.d.mts → interfaces-CayXPOAf.d.mts} +2 -1
  114. package/dist/{interfaces-DbTyKGdS.d.mts.map → interfaces-CayXPOAf.d.mts.map} +1 -1
  115. package/dist/{metadata-C-yduM8v.mjs → metadata-WlPWz6fi.mjs} +2 -2
  116. package/dist/{metadata-C-yduM8v.mjs.map → metadata-WlPWz6fi.mjs.map} +1 -1
  117. package/dist/module-registry-DmTQjSdD.mjs +12 -0
  118. package/dist/module-registry-DmTQjSdD.mjs.map +1 -0
  119. package/dist/{query-C0vJlGKm.mjs → query-B2w_8bwe.mjs} +2 -2
  120. package/dist/{query-C0vJlGKm.mjs.map → query-B2w_8bwe.mjs.map} +1 -1
  121. package/dist/rate-limit-oLL5EPUf.d.mts.map +1 -1
  122. package/dist/{reactivity-DWmLcN0A.d.mts → reactivity-XL0fFDvG.d.mts} +10 -16
  123. package/dist/reactivity-XL0fFDvG.d.mts.map +1 -0
  124. package/dist/reply-Dh15A_0R.mjs +12 -0
  125. package/dist/reply-Dh15A_0R.mjs.map +1 -0
  126. package/dist/reply-Elm9mc8Z.d.mts +63 -0
  127. package/dist/reply-Elm9mc8Z.d.mts.map +1 -0
  128. package/dist/request-id-IDobbxU-.d.mts.map +1 -1
  129. package/dist/rolldown-runtime-C4pdyXoH.mjs +11 -0
  130. package/dist/{router-builder-Bh3qSiDz.d.mts → router-builder-BHTp3zL3.d.mts} +2 -2
  131. package/dist/router-builder-BHTp3zL3.d.mts.map +1 -0
  132. package/dist/session-DSRA-PUX.d.mts.map +1 -1
  133. package/dist/{spa-BdvZFXJz.d.mts → spa-D8I4YnpL.d.mts} +2 -2
  134. package/dist/{spa-BdvZFXJz.d.mts.map → spa-D8I4YnpL.d.mts.map} +1 -1
  135. package/dist/{upload-Bc_rBRtE.d.mts → upload-CUyZ0zNt.d.mts} +9 -7
  136. package/dist/upload-CUyZ0zNt.d.mts.map +1 -0
  137. package/dist/upload-config-B6vGWI0_.mjs +12 -0
  138. package/dist/upload-config-B6vGWI0_.mjs.map +1 -0
  139. package/dist/validate-DoZDOhCi.d.mts.map +1 -1
  140. package/dist/{views-DcTVDiOh.d.mts → views-PFf-CBvN.d.mts} +7 -5
  141. package/dist/views-PFf-CBvN.d.mts.map +1 -0
  142. package/dist/web.d.mts +150 -0
  143. package/dist/web.d.mts.map +1 -0
  144. package/dist/web.mjs +12 -0
  145. package/dist/web.mjs.map +1 -0
  146. package/package.json +22 -13
  147. package/dist/adapter-D_uzU-QA.d.mts.map +0 -1
  148. package/dist/application-KaKi6jgi.mjs +0 -12
  149. package/dist/application-KaKi6jgi.mjs.map +0 -1
  150. package/dist/cache-BSFDtsEG.d.mts.map +0 -1
  151. package/dist/container-Oy24XRnS.mjs +0 -108
  152. package/dist/container-Oy24XRnS.mjs.map +0 -1
  153. package/dist/container-TWOUyR_0.d.mts.map +0 -1
  154. package/dist/context-Co72dNA_.mjs +0 -12
  155. package/dist/context-Co72dNA_.mjs.map +0 -1
  156. package/dist/core-kmJ4y4fJ.mjs +0 -13
  157. package/dist/core-kmJ4y4fJ.mjs.map +0 -1
  158. package/dist/decorators-ejABLo9H.d.mts.map +0 -1
  159. package/dist/env-RP8lbK-Z.mjs +0 -13
  160. package/dist/http/middleware/csrf.mjs.map +0 -1
  161. package/dist/reactivity-DWmLcN0A.d.mts.map +0 -1
  162. package/dist/request-scope-C_taFScZ.mjs +0 -12
  163. package/dist/request-scope-C_taFScZ.mjs.map +0 -1
  164. package/dist/router-builder-Bh3qSiDz.d.mts.map +0 -1
  165. package/dist/upload-Bc_rBRtE.d.mts.map +0 -1
  166. package/dist/views-DcTVDiOh.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contributor-runner-BP9rOU_2.mjs","names":[],"sources":["../src/core/context-errors.ts","../src/core/contributor-pipeline.ts","../src/core/contributor-runner.ts"],"sourcesContent":["/**\n * Errors thrown by the Context Contributor pipeline.\n *\n * All three are startup-time errors raised during pipeline build (topo-sort\n * + validation), not per-request — a misconfigured app fails to boot rather\n * than fails a request. Per the locked design in `architecture.md` §20.4,\n * the pipeline is validated once when each route is mounted and the\n * resolved order is cached on the route.\n */\n\n/**\n * A contributor declared `dependsOn: ['<key>']` but no other contributor\n * (at any precedence level) produces that key for the same route.\n *\n * @example\n * ```text\n * MissingContributorError: Missing context contributor 'tenant' required by 'project' on route GET /projects/:id\n * declared at src/contributors/load-project.ts:42:18\n * ```\n */\nexport class MissingContributorError extends Error {\n readonly key: string\n readonly dependent: string\n readonly route?: string\n readonly dependentDefinedAt?: string\n\n constructor(key: string, dependent: string, route?: string, dependentDefinedAt?: string) {\n const where = route ? ` on route ${route}` : ''\n const declaredAt = formatDeclaredAt(dependentDefinedAt)\n super(`Missing context contributor '${key}' required by '${dependent}'${where}${declaredAt}`)\n this.name = 'MissingContributorError'\n this.key = key\n this.dependent = dependent\n this.route = route\n this.dependentDefinedAt = dependentDefinedAt\n }\n}\n\n/**\n * Slice the first non-framework frame off a captured `Error.stack`\n * and format it as `\\n declared at <frame>` for inclusion in\n * boot-time error messages. Returns `''` when no useful frame is\n * available (hand-rolled registrations, missing stacks).\n */\nfunction formatDeclaredAt(stack: string | undefined): string {\n if (!stack) return ''\n const lines = stack.split('\\n')\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed.startsWith('at ')) continue\n // Skip frames inside this file or the decorator factory — the\n // adopter's call site is the next frame after those.\n if (trimmed.includes('context-decorator.')) continue\n if (trimmed.includes('context-errors.')) continue\n return `\\n declared at ${trimmed.replace(/^at /, '')}`\n }\n return ''\n}\n\n/**\n * Topo-sort detected a cycle in `dependsOn` declarations. The `cycle` field\n * lists the keys in the order they form the loop, with the first key\n * repeated at the end so the path reads naturally.\n *\n * @example\n * ```text\n * ContributorCycleError: Cycle in context contributors on route GET /projects/:id: tenant → project → tenant\n * ```\n */\nexport class ContributorCycleError extends Error {\n readonly cycle: readonly string[]\n readonly route?: string\n\n constructor(cycle: readonly string[], route?: string) {\n const where = route ? ` on route ${route}` : ''\n super(`Cycle in context contributors${where}: ${cycle.join(' → ')}`)\n this.name = 'ContributorCycleError'\n this.cycle = Object.freeze([...cycle])\n this.route = route\n }\n}\n\n/**\n * Two contributors at the same precedence level both produce the same key\n * for the same route. Precedence resolution (method > class > module >\n * adapter > global, per architecture.md §20.4) silently drops\n * lower-precedence duplicates; this error fires only when ties occur\n * within a single level — e.g., two method-level decorators declaring\n * `key: 'tenant'`.\n *\n * `sources` is best-effort identifying labels for each conflicting\n * registration (typically the decorator's call site or registering\n * module/adapter name).\n *\n * @example\n * ```text\n * DuplicateContributorError: Duplicate context contributor for key 'tenant' at the same precedence level. Sources: LoadTenantFromHeader, LoadTenantFromSubdomain\n * ```\n */\nexport class DuplicateContributorError extends Error {\n readonly key: string\n readonly sources: readonly string[]\n\n constructor(key: string, sources: readonly string[]) {\n super(\n `Duplicate context contributor for key '${key}' at the same precedence level. ` +\n `Sources: ${sources.join(', ')}`,\n )\n this.name = 'DuplicateContributorError'\n this.key = key\n this.sources = Object.freeze([...sources])\n }\n}\n","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"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as MiddlewarePath, i as AppAdapterClass, n as AdapterMiddleware, o as MiddlewarePhase, r as AppAdapter, t as AdapterContext } from "../adapter-D_uzU-QA.mjs";
2
+ import { a as MiddlewarePath, i as AppAdapterClass, n as AdapterMiddleware, o as MiddlewarePhase, r as AppAdapter, t as AdapterContext } from "../adapter-DCfEFZMY.mjs";
3
3
  export { AdapterContext, AdapterMiddleware, AppAdapter, AppAdapterClass, MiddlewarePath, MiddlewarePhase };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { i as ModuleRoutes, n as AppModuleClass, r as AppModuleEntry, t as AppModule } from "../app-module-CuAS6hJC.mjs";
2
+ import { i as ModuleRoutes, n as AppModuleClass, r as AppModuleEntry, t as AppModule } from "../app-module-BwYtACD6.mjs";
3
3
  export { AppModule, AppModuleClass, AppModuleEntry, ModuleRoutes };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as Cacheable, c as setCacheProvider, i as CacheProvider, n as CacheEvict, o as MemoryCacheProvider, r as CacheOptions, s as getCacheProvider, t as CACHEABLE_META } from "../cache-BSFDtsEG.mjs";
2
+ import { a as Cacheable, c as setCacheProvider, i as CacheProvider, n as CacheEvict, o as MemoryCacheProvider, r as CacheOptions, s as getCacheProvider, t as CACHEABLE_META } from "../cache-CwZzIH0D.mjs";
3
3
  export { CACHEABLE_META, CacheEvict, CacheOptions, CacheProvider, Cacheable, MemoryCacheProvider, getCacheProvider, setCacheProvider };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import"reflect-metadata";const CACHEABLE_META=`kick/cacheable`;var MemoryCacheProvider=class{store=new Map;async get(key){let entry=this.store.get(key);return entry?entry.expiresAt<=Date.now()?(this.store.delete(key),null):entry.data:null}async set(key,value,ttlMs){this.store.set(key,{data:value,expiresAt:Date.now()+ttlMs})}async del(key){this.store.delete(key)}async delByPrefix(prefix){for(let key of this.store.keys())key.startsWith(prefix)&&this.store.delete(key)}async shutdown(){this.store.clear()}};let _cacheProvider=new MemoryCacheProvider;function setCacheProvider(provider){_cacheProvider=provider}function getCacheProvider(){return _cacheProvider}function Cacheable(ttl,options){return(_target,propertyKey,descriptor)=>{let original=descriptor.value,cacheTtl=(ttl??60)*1e3,keyPrefix=options?.key??String(propertyKey);return descriptor.value=async function(...args){let cacheKey=`${keyPrefix}:${JSON.stringify(args)}`,provider=getCacheProvider(),cached=await provider.get(cacheKey);if(cached!==null)return cached;let result=await original.apply(this,args);return await provider.set(cacheKey,result,cacheTtl),result},descriptor}}function CacheEvict(key){return(_target,_propertyKey,descriptor)=>{let original=descriptor.value;return descriptor.value=async function(...args){let result=await original.apply(this,args),provider=getCacheProvider();return provider.delByPrefix&&await provider.delByPrefix(key),result},descriptor}}export{CACHEABLE_META,CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider};
11
+ import"reflect-metadata";const CACHEABLE_META=`kick/cacheable`;var MemoryCacheProvider=class{maxEntries;store=new Map;constructor(maxEntries=1e4){this.maxEntries=maxEntries}async get(key){let entry=this.store.get(key);return entry?entry.expiresAt<=Date.now()?(this.store.delete(key),null):(this.store.delete(key),this.store.set(key,entry),entry.data):null}async set(key,value,ttlMs){if(!this.store.has(key)&&this.store.size>=this.maxEntries){let oldest=this.store.keys().next().value;oldest!==void 0&&this.store.delete(oldest)}this.store.delete(key),this.store.set(key,{data:value,expiresAt:Date.now()+ttlMs})}async del(key){this.store.delete(key)}async delByPrefix(prefix){for(let key of this.store.keys())key.startsWith(prefix)&&this.store.delete(key)}async shutdown(){this.store.clear()}};let _cacheProvider=new MemoryCacheProvider;function setCacheProvider(provider){_cacheProvider=provider}function getCacheProvider(){return _cacheProvider}const CACHED_NULL={__kickjs_cached_null__:!0};function isCachedNull(value){return typeof value==`object`&&!!value&&value.__kickjs_cached_null__===!0&&Object.keys(value).length===1}function Cacheable(ttl,options){return(_target,propertyKey,descriptor)=>{let original=descriptor.value,cacheTtl=(ttl??60)*1e3,keyPrefix=options?.key??String(propertyKey);return descriptor.value=async function(...args){let cacheKey=`${keyPrefix}:${JSON.stringify(args)}`,provider=getCacheProvider(),cached=await provider.get(cacheKey);if(cached!==null)return isCachedNull(cached)?null:cached;let result=await original.apply(this,args);return result===null?await provider.set(cacheKey,CACHED_NULL,cacheTtl):result!==void 0&&await provider.set(cacheKey,result,cacheTtl),result},descriptor}}function CacheEvict(key){return(_target,_propertyKey,descriptor)=>{let original=descriptor.value;return descriptor.value=async function(...args){let result=await original.apply(this,args),provider=getCacheProvider();return provider.delByPrefix&&await provider.delByPrefix(key),result},descriptor}}export{CACHEABLE_META,CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider};
12
12
  //# sourceMappingURL=cache.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.mjs","names":[],"sources":["../../src/core/cache.ts"],"sourcesContent":["import 'reflect-metadata'\n\n// String metadata key (post-Symbol migration). Slash-delimited\n// `kick/cacheable` so it can't collide with adopter metadata keys.\nconst CACHEABLE_META = 'kick/cacheable'\n\n// ── CacheProvider Interface ─────────────────────────────────────────────\n\n/**\n * Abstract cache backend. Implement this interface to use Redis, Memcached,\n * or any other cache store with @Cacheable and @CacheEvict.\n *\n * @example\n * ```ts\n * class RedisCacheProvider implements CacheProvider {\n * private client: RedisClient\n * constructor(client: RedisClient) { this.client = client }\n * async get(key: string) {\n * const val = await this.client.get(key)\n * return val ? JSON.parse(val) : null\n * }\n * async set(key: string, value: any, ttlMs: number) {\n * await this.client.set(key, JSON.stringify(value), 'PX', ttlMs)\n * }\n * async del(key: string) { await this.client.del(key) }\n * async delByPrefix(prefix: string) {\n * const keys = await this.client.keys(prefix + '*')\n * if (keys.length) await this.client.del(...keys)\n * }\n * }\n * ```\n */\nexport interface CacheProvider {\n /** Retrieve a cached value. Return null if not found or expired. */\n get(key: string): Promise<any | null>\n /** Store a value with a TTL in milliseconds. */\n set(key: string, value: any, ttlMs: number): Promise<void>\n /** Delete a specific cache key. */\n del(key: string): Promise<void>\n /** Delete all keys matching a prefix. Used by @CacheEvict. */\n delByPrefix?(prefix: string): Promise<void>\n /** Optional cleanup on shutdown. */\n shutdown?(): Promise<void>\n}\n\n// ── Built-in Memory Provider ────────────────────────────────────────────\n\n/**\n * Default in-memory cache provider using a Map.\n * Suitable for development and single-instance deployments.\n * For multi-instance or production, use a Redis-backed provider.\n */\nexport class MemoryCacheProvider implements CacheProvider {\n private store = new Map<string, { data: any; expiresAt: number }>()\n\n async get(key: string): Promise<any | null> {\n const entry = this.store.get(key)\n if (!entry) return null\n if (entry.expiresAt <= Date.now()) {\n this.store.delete(key)\n return null\n }\n return entry.data\n }\n\n async set(key: string, value: any, ttlMs: number): Promise<void> {\n this.store.set(key, { data: value, expiresAt: Date.now() + ttlMs })\n }\n\n async del(key: string): Promise<void> {\n this.store.delete(key)\n }\n\n async delByPrefix(prefix: string): Promise<void> {\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key)\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.store.clear()\n }\n}\n\n// ── Global Cache Registry ───────────────────────────────────────────────\n\nlet _cacheProvider: CacheProvider = new MemoryCacheProvider()\n\n/**\n * Set the global cache provider used by @Cacheable and @CacheEvict.\n * Call this before bootstrapping your app.\n *\n * @example\n * ```ts\n * import { setCacheProvider } from '@forinda/kickjs'\n * import { RedisCacheProvider } from './redis-cache'\n *\n * setCacheProvider(new RedisCacheProvider(redisClient))\n * ```\n */\nexport function setCacheProvider(provider: CacheProvider): void {\n _cacheProvider = provider\n}\n\n/** Get the current cache provider */\nexport function getCacheProvider(): CacheProvider {\n return _cacheProvider\n}\n\n// ── Cache Options ───────────────────────────────────────────────────────\n\nexport interface CacheOptions {\n /** Time-to-live in seconds (default: 60) */\n ttl?: number\n /** Cache key prefix (default: ClassName.methodName) */\n key?: string\n}\n\n// ── @Cacheable Decorator ────────────────────────────────────────────────\n\n/**\n * Cache the return value of a method for the specified TTL.\n * Uses the globally registered CacheProvider (default: MemoryCacheProvider).\n *\n * Call `setCacheProvider()` to use Redis or any custom backend.\n *\n * @param ttl - Time-to-live in seconds (default: 60)\n * @param options.key - Custom cache key prefix\n */\nexport function Cacheable(ttl?: number, options?: { key?: string }): MethodDecorator {\n return (_target, propertyKey, descriptor: PropertyDescriptor) => {\n const original = descriptor.value\n const cacheTtl = (ttl ?? 60) * 1000\n const keyPrefix = options?.key ?? String(propertyKey)\n\n descriptor.value = async function (...args: any[]) {\n const cacheKey = `${keyPrefix}:${JSON.stringify(args)}`\n const provider = getCacheProvider()\n\n const cached = await provider.get(cacheKey)\n if (cached !== null) {\n return cached\n }\n\n const result = await original.apply(this, args)\n await provider.set(cacheKey, result, cacheTtl)\n return result\n }\n\n return descriptor\n }\n}\n\n// ── @CacheEvict Decorator ───────────────────────────────────────────────\n\n/**\n * Evict cached values matching a key prefix after the method executes.\n * Works with any CacheProvider that implements `delByPrefix`.\n *\n * @param key - Cache key prefix to evict (matches the @Cacheable key)\n */\nexport function CacheEvict(key: string): MethodDecorator {\n return (_target, _propertyKey, descriptor: PropertyDescriptor) => {\n const original = descriptor.value\n\n descriptor.value = async function (...args: any[]) {\n const result = await original.apply(this, args)\n const provider = getCacheProvider()\n if (provider.delByPrefix) {\n await provider.delByPrefix(key)\n }\n return result\n }\n\n return descriptor\n }\n}\n\nexport { CACHEABLE_META }\n"],"mappings":";;;;;;;;;;yBAIA,MAAM,eAAiB,iBAgDvB,IAAa,oBAAb,KAA0D,CACxD,MAAgB,IAAI,IAEpB,MAAM,IAAI,IAAkC,CAC1C,IAAM,MAAQ,KAAK,MAAM,IAAI,GAAG,EAMhC,OALK,MACD,MAAM,WAAa,KAAK,IAAI,GAC9B,KAAK,MAAM,OAAO,GAAG,EACd,MAEF,MAAM,KALM,IAMrB,CAEA,MAAM,IAAI,IAAa,MAAY,MAA8B,CAC/D,KAAK,MAAM,IAAI,IAAK,CAAE,KAAM,MAAO,UAAW,KAAK,IAAI,EAAI,KAAM,CAAC,CACpE,CAEA,MAAM,IAAI,IAA4B,CACpC,KAAK,MAAM,OAAO,GAAG,CACvB,CAEA,MAAM,YAAY,OAA+B,CAC/C,IAAK,IAAM,OAAO,KAAK,MAAM,KAAK,EAC5B,IAAI,WAAW,MAAM,GACvB,KAAK,MAAM,OAAO,GAAG,CAG3B,CAEA,MAAM,UAA0B,CAC9B,KAAK,MAAM,MAAM,CACnB,CACF,EAIA,IAAI,eAAgC,IAAI,oBAcxC,SAAgB,iBAAiB,SAA+B,CAC9D,eAAiB,QACnB,CAGA,SAAgB,kBAAkC,CAChD,OAAO,cACT,CAsBA,SAAgB,UAAU,IAAc,QAA6C,CACnF,OAAQ,QAAS,YAAa,aAAmC,CAC/D,IAAM,SAAW,WAAW,MACtB,UAAY,KAAO,IAAM,IACzB,UAAY,SAAS,KAAO,OAAO,WAAW,EAgBpD,MAdA,YAAW,MAAQ,eAAgB,GAAG,KAAa,CACjD,IAAM,SAAW,GAAG,UAAU,GAAG,KAAK,UAAU,IAAI,IAC9C,SAAW,iBAAiB,EAE5B,OAAS,MAAM,SAAS,IAAI,QAAQ,EAC1C,GAAI,SAAW,KACb,OAAO,OAGT,IAAM,OAAS,MAAM,SAAS,MAAM,KAAM,IAAI,EAE9C,OADA,MAAM,SAAS,IAAI,SAAU,OAAQ,QAAQ,EACtC,MACT,EAEO,UACT,CACF,CAUA,SAAgB,WAAW,IAA8B,CACvD,OAAQ,QAAS,aAAc,aAAmC,CAChE,IAAM,SAAW,WAAW,MAW5B,MATA,YAAW,MAAQ,eAAgB,GAAG,KAAa,CACjD,IAAM,OAAS,MAAM,SAAS,MAAM,KAAM,IAAI,EACxC,SAAW,iBAAiB,EAIlC,OAHI,SAAS,aACX,MAAM,SAAS,YAAY,GAAG,EAEzB,MACT,EAEO,UACT,CACF"}
1
+ {"version":3,"file":"cache.mjs","names":[],"sources":["../../src/core/cache.ts"],"sourcesContent":["import 'reflect-metadata'\n\n// String metadata key (post-Symbol migration). Slash-delimited\n// `kick/cacheable` so it can't collide with adopter metadata keys.\nconst CACHEABLE_META = 'kick/cacheable'\n\n// ── CacheProvider Interface ─────────────────────────────────────────────\n\n/**\n * Abstract cache backend. Implement this interface to use Redis, Memcached,\n * or any other cache store with @Cacheable and @CacheEvict.\n *\n * @example\n * ```ts\n * class RedisCacheProvider implements CacheProvider {\n * private client: RedisClient\n * constructor(client: RedisClient) { this.client = client }\n * async get(key: string) {\n * const val = await this.client.get(key)\n * return val ? JSON.parse(val) : null\n * }\n * async set(key: string, value: any, ttlMs: number) {\n * await this.client.set(key, JSON.stringify(value), 'PX', ttlMs)\n * }\n * async del(key: string) { await this.client.del(key) }\n * async delByPrefix(prefix: string) {\n * const keys = await this.client.keys(prefix + '*')\n * if (keys.length) await this.client.del(...keys)\n * }\n * }\n * ```\n */\nexport interface CacheProvider {\n /** Retrieve a cached value. Return null if not found or expired. */\n get(key: string): Promise<any | null>\n /** Store a value with a TTL in milliseconds. */\n set(key: string, value: any, ttlMs: number): Promise<void>\n /** Delete a specific cache key. */\n del(key: string): Promise<void>\n /** Delete all keys matching a prefix. Used by @CacheEvict. */\n delByPrefix?(prefix: string): Promise<void>\n /** Optional cleanup on shutdown. */\n shutdown?(): Promise<void>\n}\n\n// ── Built-in Memory Provider ────────────────────────────────────────────\n\n/**\n * Default in-memory cache provider using a Map with LRU eviction.\n * Suitable for development and single-instance deployments.\n * For multi-instance or production, use a Redis-backed provider.\n *\n * Bounded: at most `maxEntries` (default 10 000) live entries. Expiry is\n * lazy (checked on read); the size cap is what bounds memory — without it,\n * a `@Cacheable` method over high-cardinality args grew the Map without\n * limit, since write-once-never-read keys were never revisited.\n */\nexport class MemoryCacheProvider implements CacheProvider {\n private store = new Map<string, { data: any; expiresAt: number }>()\n\n constructor(private readonly maxEntries = 10_000) {}\n\n async get(key: string): Promise<any | null> {\n const entry = this.store.get(key)\n if (!entry) return null\n if (entry.expiresAt <= Date.now()) {\n this.store.delete(key)\n return null\n }\n // LRU touch: re-insert so Map iteration order tracks recency and\n // eviction removes the least-recently-used key, not the oldest write.\n this.store.delete(key)\n this.store.set(key, entry)\n return entry.data\n }\n\n async set(key: string, value: any, ttlMs: number): Promise<void> {\n if (!this.store.has(key) && this.store.size >= this.maxEntries) {\n // Evict the least-recently-used entry (first in iteration order).\n const oldest = this.store.keys().next().value\n if (oldest !== undefined) this.store.delete(oldest)\n }\n this.store.delete(key)\n this.store.set(key, { data: value, expiresAt: Date.now() + ttlMs })\n }\n\n async del(key: string): Promise<void> {\n this.store.delete(key)\n }\n\n async delByPrefix(prefix: string): Promise<void> {\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key)\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.store.clear()\n }\n}\n\n// ── Global Cache Registry ───────────────────────────────────────────────\n\nlet _cacheProvider: CacheProvider = new MemoryCacheProvider()\n\n/**\n * Set the global cache provider used by @Cacheable and @CacheEvict.\n * Call this before bootstrapping your app.\n *\n * @example\n * ```ts\n * import { setCacheProvider } from '@forinda/kickjs'\n * import { RedisCacheProvider } from './redis-cache'\n *\n * setCacheProvider(new RedisCacheProvider(redisClient))\n * ```\n */\nexport function setCacheProvider(provider: CacheProvider): void {\n _cacheProvider = provider\n}\n\n/** Get the current cache provider */\nexport function getCacheProvider(): CacheProvider {\n return _cacheProvider\n}\n\n// ── Cache Options ───────────────────────────────────────────────────────\n\nexport interface CacheOptions {\n /** Time-to-live in seconds (default: 60) */\n ttl?: number\n /** Cache key prefix (default: ClassName.methodName) */\n key?: string\n}\n\n// ── @Cacheable Decorator ────────────────────────────────────────────────\n\n/**\n * Cache the return value of a method for the specified TTL.\n * Uses the globally registered CacheProvider (default: MemoryCacheProvider).\n *\n * Call `setCacheProvider()` to use Redis or any custom backend.\n *\n * @param ttl - Time-to-live in seconds (default: 60)\n * @param options.key - Custom cache key prefix\n */\n// Sentinel stored in place of a legitimate `null` result. Providers signal\n// \"miss\" with null, so caching null directly was indistinguishable from a\n// miss — the method re-executed on every call (cache stampede on the exact\n// path caching should protect). An object envelope (structurally checked,\n// so it survives any JSON round-trip through Redis etc.) rather than a bare\n// string: a method legitimately RETURNING that string (echoing user input,\n// etc.) must not be replayed as null. A user value could still forge this\n// exact single-key shape, but that requires deliberately constructing it —\n// not a value class that occurs by accident.\nconst CACHED_NULL = { __kickjs_cached_null__: true }\n\nfunction isCachedNull(value: unknown): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>).__kickjs_cached_null__ === true &&\n Object.keys(value).length === 1\n )\n}\n\nexport function Cacheable(ttl?: number, options?: { key?: string }): MethodDecorator {\n return (_target, propertyKey, descriptor: PropertyDescriptor) => {\n const original = descriptor.value\n const cacheTtl = (ttl ?? 60) * 1000\n const keyPrefix = options?.key ?? String(propertyKey)\n\n descriptor.value = async function (...args: any[]) {\n const cacheKey = `${keyPrefix}:${JSON.stringify(args)}`\n const provider = getCacheProvider()\n\n const cached = await provider.get(cacheKey)\n if (cached !== null) {\n return isCachedNull(cached) ? null : cached\n }\n\n const result = await original.apply(this, args)\n // Cache legit-null results via the sentinel; `undefined` stays\n // uncached (callers returning undefined get pre-existing behavior).\n if (result === null) await provider.set(cacheKey, CACHED_NULL, cacheTtl)\n else if (result !== undefined) await provider.set(cacheKey, result, cacheTtl)\n return result\n }\n\n return descriptor\n }\n}\n\n// ── @CacheEvict Decorator ───────────────────────────────────────────────\n\n/**\n * Evict cached values matching a key prefix after the method executes.\n * Works with any CacheProvider that implements `delByPrefix`.\n *\n * @param key - Cache key prefix to evict (matches the @Cacheable key)\n */\nexport function CacheEvict(key: string): MethodDecorator {\n return (_target, _propertyKey, descriptor: PropertyDescriptor) => {\n const original = descriptor.value\n\n descriptor.value = async function (...args: any[]) {\n const result = await original.apply(this, args)\n const provider = getCacheProvider()\n if (provider.delByPrefix) {\n await provider.delByPrefix(key)\n }\n return result\n }\n\n return descriptor\n }\n}\n\nexport { CACHEABLE_META }\n"],"mappings":";;;;;;;;;;yBAIA,MAAM,eAAiB,iBAqDvB,IAAa,oBAAb,KAA0D,CAG3B,WAF7B,MAAgB,IAAI,IAEpB,YAAY,WAA8B,IAAQ,CAArB,KAAA,WAAA,UAAsB,CAEnD,MAAM,IAAI,IAAkC,CAC1C,IAAM,MAAQ,KAAK,MAAM,IAAI,GAAG,EAUhC,OATK,MACD,MAAM,WAAa,KAAK,IAAI,GAC9B,KAAK,MAAM,OAAO,GAAG,EACd,OAIT,KAAK,MAAM,OAAO,GAAG,EACrB,KAAK,MAAM,IAAI,IAAK,KAAK,EAClB,MAAM,MATM,IAUrB,CAEA,MAAM,IAAI,IAAa,MAAY,MAA8B,CAC/D,GAAI,CAAC,KAAK,MAAM,IAAI,GAAG,GAAK,KAAK,MAAM,MAAQ,KAAK,WAAY,CAE9D,IAAM,OAAS,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MACpC,SAAW,IAAA,IAAW,KAAK,MAAM,OAAO,MAAM,CACpD,CACA,KAAK,MAAM,OAAO,GAAG,EACrB,KAAK,MAAM,IAAI,IAAK,CAAE,KAAM,MAAO,UAAW,KAAK,IAAI,EAAI,KAAM,CAAC,CACpE,CAEA,MAAM,IAAI,IAA4B,CACpC,KAAK,MAAM,OAAO,GAAG,CACvB,CAEA,MAAM,YAAY,OAA+B,CAC/C,IAAK,IAAM,OAAO,KAAK,MAAM,KAAK,EAC5B,IAAI,WAAW,MAAM,GACvB,KAAK,MAAM,OAAO,GAAG,CAG3B,CAEA,MAAM,UAA0B,CAC9B,KAAK,MAAM,MAAM,CACnB,CACF,EAIA,IAAI,eAAgC,IAAI,oBAcxC,SAAgB,iBAAiB,SAA+B,CAC9D,eAAiB,QACnB,CAGA,SAAgB,kBAAkC,CAChD,OAAO,cACT,CA+BA,MAAM,YAAc,CAAE,uBAAwB,EAAK,EAEnD,SAAS,aAAa,MAAyB,CAC7C,OACE,OAAO,OAAU,YACjB,OACC,MAAkC,yBAA2B,IAC9D,OAAO,KAAK,KAAK,CAAC,CAAC,SAAW,CAElC,CAEA,SAAgB,UAAU,IAAc,QAA6C,CACnF,OAAQ,QAAS,YAAa,aAAmC,CAC/D,IAAM,SAAW,WAAW,MACtB,UAAY,KAAO,IAAM,IACzB,UAAY,SAAS,KAAO,OAAO,WAAW,EAmBpD,MAjBA,YAAW,MAAQ,eAAgB,GAAG,KAAa,CACjD,IAAM,SAAW,GAAG,UAAU,GAAG,KAAK,UAAU,IAAI,IAC9C,SAAW,iBAAiB,EAE5B,OAAS,MAAM,SAAS,IAAI,QAAQ,EAC1C,GAAI,SAAW,KACb,OAAO,aAAa,MAAM,EAAI,KAAO,OAGvC,IAAM,OAAS,MAAM,SAAS,MAAM,KAAM,IAAI,EAK9C,OAFI,SAAW,KAAM,MAAM,SAAS,IAAI,SAAU,YAAa,QAAQ,EAC9D,SAAW,IAAA,IAAW,MAAM,SAAS,IAAI,SAAU,OAAQ,QAAQ,EACrE,MACT,EAEO,UACT,CACF,CAUA,SAAgB,WAAW,IAA8B,CACvD,OAAQ,QAAS,aAAc,aAAmC,CAChE,IAAM,SAAW,WAAW,MAW5B,MATA,YAAW,MAAQ,eAAgB,GAAG,KAAa,CACjD,IAAM,OAAS,MAAM,SAAS,MAAM,KAAM,IAAI,EACxC,SAAW,iBAAiB,EAIlC,OAHI,SAAS,aACX,MAAM,SAAS,YAAY,GAAG,EAEzB,MACT,EAEO,UACT,CACF"}
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as tokenName, i as KickJsRegistry, n as ContainerChangeEvent, r as ContainerChangeListener, t as Container } from "../container-TWOUyR_0.mjs";
2
+ import { a as tokenName, i as KickJsRegistry, n as ContainerChangeEvent, r as ContainerChangeListener, t as Container } from "../container-iED1swkd.mjs";
3
3
  export { Container, ContainerChangeEvent, ContainerChangeListener, KickJsRegistry, tokenName };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{n as tokenName,t as Container}from"../container-Oy24XRnS.mjs";export{Container,tokenName};
11
+ import{n as tokenName,t as Container}from"../container-CkcVS_gA.mjs";export{Container,tokenName};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{c as pushClassMeta,t as getClassMeta}from"../metadata-C-yduM8v.mjs";const CRON_META=`kick/cron`;function Cron(expression,options){return(target,propertyKey)=>{pushClassMeta(CRON_META,target.constructor,{expression,handlerName:propertyKey,description:options?.description,timezone:options?.timezone,runOnInit:options?.runOnInit})}}function getCronJobs(target){return getClassMeta(CRON_META,target,[])}export{CRON_META,Cron,getCronJobs};
11
+ import{c as pushClassMeta,t as getClassMeta}from"../metadata-WlPWz6fi.mjs";const CRON_META=`kick/cron`;function Cron(expression,options){return(target,propertyKey)=>{pushClassMeta(CRON_META,target.constructor,{expression,handlerName:propertyKey,description:options?.description,timezone:options?.timezone,runOnInit:options?.runOnInit})}}function getCronJobs(target){return getClassMeta(CRON_META,target,[])}export{CRON_META,Cron,getCronJobs};
12
12
  //# sourceMappingURL=cron.mjs.map
@@ -1,3 +1,3 @@
1
1
 
2
- import { A as Value, C as Post, D as Repository, E as Put, M as withBuilder, O as RouteDefinition, S as Patch, T as PropertyOrParameterDecorator, _ as Get, a as Autowired, b as Middleware, c as ColumnApiQueryParamsConfig, d as Delete, f as Env, g as FileUploadConfig, h as FileUpload, i as AssetKey, j as normalizeApiQueryParamsConfig, k as Service, l as Component, m as FileTypeFilter, n as ApiQueryParamsConfig, o as BaseUploadOptions, p as EnvKey, r as Asset, s as Builder, t as ApiQueryParams, u as Controller, v as Inject, w as PostConstruct, x as MiddlewareHandler, y as Injectable } from "../decorators-ejABLo9H.mjs";
3
- export { ApiQueryParams, ApiQueryParamsConfig, Asset, AssetKey, Autowired, BaseUploadOptions, Builder, ColumnApiQueryParamsConfig, Component, Controller, Delete, Env, EnvKey, FileTypeFilter, FileUpload, FileUploadConfig, Get, Inject, Injectable, Middleware, MiddlewareHandler, Patch, Post, PostConstruct, PropertyOrParameterDecorator, Put, Repository, RouteDefinition, Service, Value, normalizeApiQueryParamsConfig, withBuilder };
2
+ import { A as Service, C as Post, D as Put, E as PropertyOrParameterDecorator, M as normalizeApiQueryParamsConfig, N as withBuilder, O as Repository, S as Patch, T as PreDestroy, _ as Get, a as Autowired, b as Middleware, c as ColumnApiQueryParamsConfig, d as Delete, f as Env, g as FileUploadConfig, h as FileUpload, i as AssetKey, j as Value, k as RouteDefinition, l as Component, m as FileTypeFilter, n as ApiQueryParamsConfig, o as BaseUploadOptions, p as EnvKey, r as Asset, s as Builder, t as ApiQueryParams, u as Controller, v as Inject, w as PostConstruct, x as MiddlewareHandler, y as Injectable } from "../decorators-DKjpujFp.mjs";
3
+ export { ApiQueryParams, ApiQueryParamsConfig, Asset, AssetKey, Autowired, BaseUploadOptions, Builder, ColumnApiQueryParamsConfig, Component, Controller, Delete, Env, EnvKey, FileTypeFilter, FileUpload, FileUploadConfig, Get, Inject, Injectable, Middleware, MiddlewareHandler, Patch, Post, PostConstruct, PreDestroy, PropertyOrParameterDecorator, Put, Repository, RouteDefinition, Service, Value, normalizeApiQueryParamsConfig, withBuilder };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as Container}from"../container-Oy24XRnS.mjs";import{METADATA}from"./interfaces.mjs";import{c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,l as pushMethodMeta,p as setMethodMeta,u as setClassMeta}from"../metadata-C-yduM8v.mjs";import"reflect-metadata";const pendingRegistrations=[],allRegistrations=new Map;let containerRef=null;function flushPending(container){containerRef=container;for(let{target,scope}of pendingRegistrations)container.has(target)||container.register(target,target,scope);pendingRegistrations.length=0}Container._onReady=flushPending,Container._onReset=container=>{containerRef=container;for(let[,{target,scope}]of allRegistrations)container.has(target)||container.register(target,target,scope)};function registerInContainer(target,scope){setClassMeta(METADATA.INJECTABLE,!0,target),setClassMeta(METADATA.SCOPE,scope,target);let name=target.name||String(target);allRegistrations.set(name,{target,scope}),containerRef?containerRef.has(target)||containerRef.register(target,target,scope):pendingRegistrations.push({target,scope})}function Injectable(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`injectable`,target),registerInContainer(target,options?.scope??`singleton`)}}function Service(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`service`,target),registerInContainer(target,options?.scope??`singleton`)}}function Component(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`component`,target),registerInContainer(target,options?.scope??`singleton`)}}function Repository(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`repository`,target),registerInContainer(target,options?.scope??`singleton`)}}function Controller(){return target=>{setClassMeta(METADATA.CLASS_KIND,`controller`,target),registerInContainer(target,`singleton`)}}function PostConstruct(){return(target,propertyKey)=>{setClassMeta(METADATA.POST_CONSTRUCT,propertyKey,target)}}function applyInjection(token,target,propertyKey,parameterIndex){if(typeof parameterIndex==`number`){setInMetaRecord(METADATA.INJECT,target,parameterIndex,token);return}propertyKey!=null&&setInMetaMap(METADATA.AUTOWIRED,target,propertyKey,token)}function Autowired(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Inject(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Value(envKey,defaultValue){return(target,propertyKey)=>{setInMetaMap(METADATA.VALUE,target,propertyKey,{envKey,defaultValue})}}function Asset(assetKey){return(target,propertyKey)=>{setInMetaMap(METADATA.ASSET,target,propertyKey,{assetKey})}}function createRouteDecorator(method){return(path,validation)=>(target,propertyKey)=>{pushClassMeta(METADATA.ROUTES,target.constructor,{method,path:path||`/`,handlerName:propertyKey,validation})}}const Get=createRouteDecorator(`GET`),Post=createRouteDecorator(`POST`),Put=createRouteDecorator(`PUT`),Delete=createRouteDecorator(`DELETE`),Patch=createRouteDecorator(`PATCH`);function normalizeApiQueryParamsConfig(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:config.searchColumns?config.searchColumns.map(col=>col?.name??``).filter(Boolean):void 0}:config}function ApiQueryParams(config){return(target,propertyKey)=>{let normalized=normalizeApiQueryParamsConfig(config);setMethodMeta(METADATA.QUERY_PARAMS,normalized,target.constructor,propertyKey)}}function Middleware(...handlers){return(target,propertyKey)=>{propertyKey?pushMethodMeta(METADATA.METHOD_MIDDLEWARES,target.constructor,propertyKey,...handlers):pushClassMeta(METADATA.CLASS_MIDDLEWARES,target,...handlers)}}function FileUpload(config){return(target,propertyKey)=>{setMethodMeta(METADATA.FILE_UPLOAD,config,target.constructor,propertyKey)}}function attachBuilder(target){setClassMeta(METADATA.BUILDER,!0,target),target.builder=function(){let props={},proxy=new Proxy({},{get(_,key){return key===`build`?()=>Object.assign(new target,props):value=>(props[key]=value,proxy)}});return proxy}}function Builder(target){attachBuilder(target)}function withBuilder(ctor){return attachBuilder(ctor),ctor}export{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder};
11
+ import{METADATA}from"./interfaces.mjs";import{t as Container}from"../container-CkcVS_gA.mjs";import{c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,l as pushMethodMeta,p as setMethodMeta,u as setClassMeta}from"../metadata-WlPWz6fi.mjs";import"reflect-metadata";const pendingRegistrations=[],allRegistrations=new Map;let containerRef=null;function flushPending(container){containerRef=container;for(let{target,scope}of pendingRegistrations)container.has(target)||container.register(target,target,scope);pendingRegistrations.length=0}Container._onReady=flushPending,Container._onReset=container=>{containerRef=container;for(let[,{target,scope}]of allRegistrations)container.has(target)||container.register(target,target,scope)};function registerInContainer(target,scope){setClassMeta(METADATA.INJECTABLE,!0,target),setClassMeta(METADATA.SCOPE,scope,target);let name=target.name||String(target);allRegistrations.set(name,{target,scope}),containerRef?containerRef.has(target)||containerRef.register(target,target,scope):pendingRegistrations.push({target,scope})}function Injectable(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`injectable`,target),registerInContainer(target,options?.scope??`singleton`)}}function Service(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`service`,target),registerInContainer(target,options?.scope??`singleton`)}}function Component(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`component`,target),registerInContainer(target,options?.scope??`singleton`)}}function Repository(options){return target=>{setClassMeta(METADATA.CLASS_KIND,`repository`,target),registerInContainer(target,options?.scope??`singleton`)}}function Controller(){return target=>{setClassMeta(METADATA.CLASS_KIND,`controller`,target),registerInContainer(target,`singleton`)}}function PostConstruct(){return(target,propertyKey)=>{setClassMeta(METADATA.POST_CONSTRUCT,propertyKey,target)}}function PreDestroy(){return(target,propertyKey)=>{setClassMeta(METADATA.PRE_DESTROY,propertyKey,target)}}function applyInjection(token,target,propertyKey,parameterIndex){if(typeof parameterIndex==`number`){setInMetaRecord(METADATA.INJECT,target,parameterIndex,token);return}propertyKey!=null&&setInMetaMap(METADATA.AUTOWIRED,target,propertyKey,token)}function Autowired(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Inject(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Value(envKey,defaultValue){return(target,propertyKey)=>{setInMetaMap(METADATA.VALUE,target,propertyKey,{envKey,defaultValue})}}function Asset(assetKey){return(target,propertyKey)=>{setInMetaMap(METADATA.ASSET,target,propertyKey,{assetKey})}}function createRouteDecorator(method){return(path,validation)=>(target,propertyKey)=>{pushClassMeta(METADATA.ROUTES,target.constructor,{method,path:path||`/`,handlerName:propertyKey,validation})}}const Get=createRouteDecorator(`GET`),Post=createRouteDecorator(`POST`),Put=createRouteDecorator(`PUT`),Delete=createRouteDecorator(`DELETE`),Patch=createRouteDecorator(`PATCH`);function normalizeApiQueryParamsConfig(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:config.searchColumns?config.searchColumns.map(col=>col?.name??``).filter(Boolean):void 0}:config}function ApiQueryParams(config){return(target,propertyKey)=>{let normalized=normalizeApiQueryParamsConfig(config);setMethodMeta(METADATA.QUERY_PARAMS,normalized,target.constructor,propertyKey)}}function Middleware(...handlers){return(target,propertyKey)=>{propertyKey?pushMethodMeta(METADATA.METHOD_MIDDLEWARES,target.constructor,propertyKey,...handlers):pushClassMeta(METADATA.CLASS_MIDDLEWARES,target,...handlers)}}function FileUpload(config){return(target,propertyKey)=>{setMethodMeta(METADATA.FILE_UPLOAD,config,target.constructor,propertyKey)}}function attachBuilder(target){setClassMeta(METADATA.BUILDER,!0,target),target.builder=function(){let props={},proxy=new Proxy({},{get(_,key){return key===`build`?()=>Object.assign(new target,props):value=>(props[key]=value,proxy)}});return proxy}}function Builder(target){attachBuilder(target)}function withBuilder(ctor){return attachBuilder(ctor),ctor}export{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,PreDestroy,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder};
12
12
  //# sourceMappingURL=decorators.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.mjs","names":[],"sources":["../../src/core/decorators.ts"],"sourcesContent":["import 'reflect-metadata'\nimport { METADATA, Scope, type BuilderOf, type ServiceOptions } from './interfaces'\nimport { Container, type KickJsRegistry } from './container'\nimport {\n setClassMeta,\n setMethodMeta,\n pushClassMeta,\n pushMethodMeta,\n setInMetaMap,\n setInMetaRecord,\n} from './metadata'\n\n// ── Decorator Registration System ───────────────────────────────────────\n// Decorators execute at class-definition time (module load). The Container\n// may not exist yet when the first decorator fires. We queue registrations\n// and flush them on first Container.getInstance() call.\n//\n// `allRegistrations` is a persistent registry that survives Container.reset().\n// On reset (HMR), all previously decorated classes are re-registered on the\n// fresh container. This ensures @Service, @Controller, @Repository, etc.\n// all survive HMR without manual re-registration.\n\ntype PendingRegistration = { target: any; scope: Scope }\nconst pendingRegistrations: PendingRegistration[] = []\n// Keyed by class name to prevent memory leaks during HMR — new class with\n// the same name replaces the old entry instead of accumulating references.\nconst allRegistrations = new Map<string, { target: any; scope: Scope }>()\nlet containerRef: any = null\n\nfunction flushPending(container: any): void {\n containerRef = container\n for (const { target, scope } of pendingRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n pendingRegistrations.length = 0\n}\n\n// Wire up synchronously — Container._onReady is called on first getInstance()\nContainer._onReady = flushPending\n\n// On Container.reset(), update containerRef and replay ALL decorator\n// registrations on the fresh container. This handles HMR where the container\n// is wiped but not all decorated modules are re-evaluated.\nContainer._onReset = (container: any) => {\n containerRef = container\n for (const [, { target, scope }] of allRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n}\n\n// ── Class Decorators ────────────────────────────────────────────────────\n\nfunction registerInContainer(target: any, scope: Scope): void {\n setClassMeta(METADATA.INJECTABLE, true, target)\n setClassMeta(METADATA.SCOPE, scope, target)\n\n // Track in persistent registry — survives Container.reset() for HMR replay.\n // Keyed by name so HMR class re-creation replaces the old entry.\n const name = target.name || String(target)\n allRegistrations.set(name, { target, scope })\n\n if (containerRef) {\n // Container already initialized — register immediately\n if (!containerRef.has(target)) {\n containerRef.register(target, target, scope)\n }\n } else {\n // Container not ready yet — queue for later\n pendingRegistrations.push({ target, scope })\n }\n}\n\n/** Mark a class as injectable with lifecycle scope */\nexport function Injectable(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'injectable', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a service (semantic alias for Injectable) */\nexport function Service(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'service', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a generic managed component */\nexport function Component(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'component', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a repository */\nexport function Repository(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'repository', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/**\n * Mark a class as an HTTP controller and register it in the DI container.\n *\n * Route prefixes are defined by the module's `routes().path` — the single\n * source of truth for where routes mount. The legacy\n * `@Controller('/path')` parameter was removed in v4; if your code\n * still passes a string, see `docs/guide/migration-v3-to-v4.md` for\n * the rename map.\n */\nexport function Controller(): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'controller', target)\n registerInContainer(target, Scope.SINGLETON)\n }\n}\n\n// ── Method Decorators ───────────────────────────────────────────────────\n\n/** Mark a method as a lifecycle hook called after instantiation */\nexport function PostConstruct(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.POST_CONSTRUCT, propertyKey, target)\n }\n}\n\n// ── Injection Decorators (property + constructor parameter) ─────────────\n\n/**\n * A decorator that's valid as either a property decorator OR a\n * constructor-parameter decorator. forinda/kick-js#235 §2 — `@Autowired`\n * and `@Inject` previously diverged at the type level (property-only\n * vs parameter-only) and adopters who picked the wrong name for the\n * position got a cryptic TS1240 error. Both names now accept either\n * position and route to the correct metadata bucket at runtime.\n */\nexport interface PropertyOrParameterDecorator {\n /** Property decorator form — `@Autowired(TOKEN) private foo!: Foo`. */\n (target: object, propertyKey: string | symbol): void\n /** Constructor-parameter form — `constructor(@Inject(TOKEN) foo: Foo) {}`. */\n (target: object, propertyKey: string | symbol | undefined, parameterIndex: number): void\n}\n\nfunction applyInjection(\n token: unknown,\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number | undefined,\n): void {\n // Legacy parameter decorators receive 3 args (target, propertyKey,\n // parameterIndex); property decorators receive 2 (target, propertyKey).\n // The 3rd-arg-number-vs-undefined check is the canonical position\n // detection in TypeScript's legacy decorator runtime.\n if (typeof parameterIndex === 'number') {\n setInMetaRecord(METADATA.INJECT, target, parameterIndex, token)\n return\n }\n if (propertyKey != null) {\n // `target` for a property decorator on a non-static field is the\n // class prototype; the AUTOWIRED bucket is keyed off the prototype\n // identity at resolution time, so pass it through unchanged.\n setInMetaMap(METADATA.AUTOWIRED, target, propertyKey as string, token)\n }\n}\n\n/**\n * Inject a dependency. Works in two positions:\n *\n * - **Property decorator** — `@Autowired(TOKEN) private repo!: UserRepo`.\n * Resolved lazily from the container the first time the property is\n * read.\n * - **Constructor parameter decorator** — `constructor(@Autowired(TOKEN) repo: UserRepo) {}`.\n * Resolved at instantiation; injected into the ctor call.\n *\n * `@Inject` is the same function under another name — they share\n * runtime + types so the two names are interchangeable. Pick whichever\n * reads better at the call site.\n *\n * Like `@Inject`, the typed string-literal overload narrows the token\n * to a key of the augmented `KickJsRegistry`. After `kick typegen`\n * populates the registry, `@Autowired('kick/prisma/Client')` auto-\n * completes the key and typo'd literals become TS2345 errors.\n */\nexport function Autowired<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Inject a dependency by token. Same shape as {@link Autowired} —\n * works as either a property decorator or a constructor-parameter\n * decorator. Pick whichever name reads better at the call site.\n *\n * ## Typed string-literal overload\n *\n * When called with a string literal that matches a key of the\n * augmented `KickJsRegistry`, TypeScript narrows the parameter type\n * to the registered type. Typo'd literals become compile errors\n * (the string isn't `keyof KickJsRegistry`). Class identities and\n * `InjectionToken<T>` keep working unchanged via the second overload.\n *\n * @example\n * ```ts\n * import { Service, Inject } from '@forinda/kickjs'\n * import type { PrismaClient } from '@prisma/client'\n *\n * `@Service()`\n * class UserRepo {\n * constructor(@Inject('kick/prisma/Client') private db: PrismaClient) {}\n * // OR property form, same decorator:\n * @Inject('kick/prisma/Client') private db2!: PrismaClient\n * }\n * ```\n *\n * After `kick typegen` runs, the literal autocompletes from the\n * registry and a typo (`@Inject('kick/prisma/Cleint')`) becomes a\n * TS2345 error.\n */\nexport function Inject<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Global ambient registry of typed environment variables, populated by\n * `kick typegen` from the project's env schema (typically\n * `src/env.ts`'s `export default defineEnv(...)`).\n *\n * Empty by default — declarations come from `.kickjs/types/env.ts`\n * generated alongside the rest of the typegen output. Once populated,\n * `@Value('PORT')` autocompletes the key, type-checks the default\n * value, and `process.env.PORT` is also typed via the parallel\n * `NodeJS.ProcessEnv` augmentation in the same generated file.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface KickEnv {}\n}\n\n/** A string key known to the typed env registry (`never` until typegen runs) */\nexport type EnvKey = keyof KickEnv & string\n\n/**\n * Look up the type of a typed env entry.\n *\n * Decorators can't propagate the property type from the key, so users\n * still need to write the property's type — but `Env<K>` lets them\n * derive it from `KickEnv` instead of duplicating the schema:\n *\n * ```ts\n * @Service()\n * class MyService {\n * @Value('PORT')\n * private port!: Env<'PORT'> // → number, narrowed from KickEnv['PORT']\n * }\n * ```\n */\nexport type Env<K extends EnvKey = EnvKey> = KickEnv[K]\n\n/**\n * Constraint that allows any string when the typed env registry is\n * empty (no `kick typegen` has run yet, so existing code with raw\n * string keys keeps working) but locks the argument down to known\n * `EnvKey` literals once the registry is populated.\n *\n * Implementation: when `EnvKey` is `never`, the conditional resolves\n * to the user's `K`, accepting anything. Once typegen has populated\n * `KickEnv`, `EnvKey` becomes a real union and the conditional forces\n * `K` to be assignable to it.\n */\ntype ValueKey<K extends string> = [EnvKey] extends [never] ? K : K & EnvKey\n\n/**\n * Inject an environment variable value. Evaluated lazily so the env\n * is available at access time, not at decoration time.\n *\n * If no default is provided and the env var is missing, throws at access time\n * to catch misconfiguration early instead of returning undefined.\n *\n * Uses metadata + instance getter to work correctly with `useDefineForClassFields`.\n *\n * Type-safety:\n * - Without `kick typegen` run, any string key is accepted (back-compat).\n * - After typegen, `KickEnv` is populated and unknown keys become a tsc\n * error. The optional `defaultValue` is type-checked against the\n * schema's inferred type for that key.\n */\nexport function Value<K extends string>(\n envKey: ValueKey<K>,\n defaultValue?: K extends EnvKey ? KickEnv[K] : unknown,\n): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.VALUE, target, propertyKey as string, { envKey, defaultValue })\n }\n}\n\n// ── @Asset — typed asset path injection (assets-plan.md PR 3+) ─────────\n\nimport type { KickAssets } from './assets'\n\n/**\n * Flatten the nested `KickAssets` augmentation (typegen-emitted in\n * PR 4) into a string-literal union of dot/slash-joined paths.\n *\n * `interface KickAssets { mails: { welcome: () => string; orders: {\n * confirmation: () => string } } }` becomes\n * `'mails/welcome' | 'mails/orders/confirmation'`.\n *\n * Returns `never` when `KickAssets` is empty (no typegen has run yet)\n * so `AssetKeyArg` falls through to accept any string.\n */\ntype FlattenAssets<T, Prefix extends string = ''> = {\n [K in keyof T & string]: T[K] extends () => string\n ? `${Prefix}${K}`\n : T[K] extends Record<string, unknown>\n ? FlattenAssets<T[K], `${Prefix}${K}/`>\n : never\n}[keyof T & string]\n\n/** Flat union of every known `<namespace>/<key>` path. */\nexport type AssetKey = FlattenAssets<KickAssets>\n\n/**\n * Constraint mirroring `ValueKey<K>` — accepts any string when\n * `AssetKey` is `never` (back-compat for adopters who haven't run\n * `kick typegen` yet) but locks the argument to known literals once\n * the typegen-augmented `KickAssets` interface is populated.\n */\ntype AssetKeyArg<K extends string> = [AssetKey] extends [never] ? K : K & AssetKey\n\n/**\n * Inject the resolved file-system path for a typed asset. Mirrors\n * `@Value`'s lazy-getter pattern — the asset is resolved on every\n * property access, NOT at class instantiation, so:\n *\n * - Tests can swap fixtures + clear the cache without re-instantiating\n * the consuming class.\n * - The first access cost is paid once per process (the manifest cache\n * in `core/assets.ts` makes subsequent calls cheap).\n *\n * Type-safety:\n *\n * - Without typegen, any string key is accepted (back-compat — same\n * trick as `@Value`).\n * - After typegen, `KickAssets` is populated and `AssetKey` is a\n * union of every flattened `<namespace>/<key>` path. Unknown keys\n * become a TS error.\n *\n * @example\n * ```ts\n * import { Service, Asset } from '@forinda/kickjs'\n *\n * `@Service()`\n * class MailService {\n * `@Asset('mails/welcome')`\n * private welcomeTemplate!: string\n *\n * send(user: User) {\n * return ejs.renderFile(this.welcomeTemplate, { user })\n * }\n * }\n * ```\n */\nexport function Asset<K extends string>(assetKey: AssetKeyArg<K>): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.ASSET, target, propertyKey as string, { assetKey })\n }\n}\n\n// ── HTTP Route Decorators ───────────────────────────────────────────────\n\nexport interface RouteDefinition {\n method: string\n path: string\n handlerName: string\n validation?: {\n /** JSON Schema object for validating the request body */\n body?: any\n /** JSON Schema object for validating query parameters */\n query?: any\n /** JSON Schema object for validating URL params */\n params?: any\n /** Schema name in OpenAPI components/schemas for the request body. Auto-generated from handler name if omitted. */\n name?: string\n }\n}\n\nfunction createRouteDecorator(method: string) {\n return (path?: string, validation?: RouteDefinition['validation']): MethodDecorator => {\n return (target, propertyKey) => {\n pushClassMeta<RouteDefinition>(METADATA.ROUTES, target.constructor, {\n method,\n path: path || '/',\n handlerName: propertyKey as string,\n validation,\n })\n }\n }\n}\n\nexport const Get = createRouteDecorator('GET')\nexport const Post = createRouteDecorator('POST')\nexport const Put = createRouteDecorator('PUT')\nexport const Delete = createRouteDecorator('DELETE')\nexport const Patch = createRouteDecorator('PATCH')\n\n// ── Query Params Decorator ─────────────────────────────────────────────\n\nexport interface ApiQueryParamsConfig {\n /** Fields that can be used in filter queries (e.g., `?filter=status:eq:active`) */\n filterable?: string[]\n /** Fields that can be used in sort queries (e.g., `?sort=createdAt:desc`) */\n sortable?: string[]\n /** Fields that are searched with the `?q=` parameter */\n searchable?: string[]\n}\n\n/**\n * Column-object-based query params config (e.g., from DrizzleQueryParamsConfig).\n * `Object.keys()` is used to derive field names for OpenAPI docs.\n */\nexport interface ColumnApiQueryParamsConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/**\n * Normalize a query params config to the string-based ApiQueryParamsConfig.\n * Handles both string-based and column-object-based configs.\n */\nexport function normalizeApiQueryParamsConfig(\n config: ApiQueryParamsConfig | ColumnApiQueryParamsConfig,\n): ApiQueryParamsConfig {\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: config.searchColumns\n ? config.searchColumns.map((col: any) => col?.name ?? '').filter(Boolean)\n : undefined,\n }\n }\n return config as ApiQueryParamsConfig\n}\n\n/**\n * Document the query parameters accepted by a GET endpoint.\n * Used by SwaggerAdapter to generate `filter`, `sort`, `page`, `limit`, and `q` params\n * in the OpenAPI spec, with descriptions listing the allowed fields.\n *\n * Accepts both string-based configs and column-object configs (e.g., DrizzleQueryParamsConfig).\n *\n * @example\n * ```ts\n * // String-based\n * @ApiQueryParams({\n * filterable: ['status', 'category', 'price'],\n * sortable: ['name', 'createdAt', 'price'],\n * searchable: ['name', 'description'],\n * })\n *\n * // Column-object-based (Drizzle)\n * @ApiQueryParams(TASK_QUERY_CONFIG)\n * ```\n */\nexport function ApiQueryParams(\n config: ApiQueryParamsConfig | ColumnApiQueryParamsConfig,\n): MethodDecorator {\n return (target, propertyKey) => {\n const normalized = normalizeApiQueryParamsConfig(config)\n setMethodMeta(METADATA.QUERY_PARAMS, normalized, target.constructor, propertyKey as string)\n }\n}\n\n// ── Middleware Decorators ───────────────────────────────────────────────\n\n/**\n * Middleware handler function.\n * Generic `TCtx` defaults to `any` — import `RequestContext` from\n * `@forinda/kickjs` for full type safety:\n *\n * ```ts\n * import type { MiddlewareHandler } from '@forinda/kickjs'\n * import type { RequestContext } from '@forinda/kickjs'\n *\n * const auth: MiddlewareHandler<RequestContext> = (ctx, next) => {\n * ctx.body // fully typed\n * next()\n * }\n * ```\n */\nexport type MiddlewareHandler<TCtx = any> = (ctx: TCtx, next: () => void) => void | Promise<void>\n\n/** Attach middleware handlers to a class or method */\nexport function Middleware(...handlers: MiddlewareHandler[]): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n pushMethodMeta(\n METADATA.METHOD_MIDDLEWARES,\n target.constructor,\n propertyKey as string,\n ...handlers,\n )\n } else {\n pushClassMeta(METADATA.CLASS_MIDDLEWARES, target, ...handlers)\n }\n }\n}\n\n// ── File Upload Decorator ───────────────────────────────────────────────\n\n/**\n * File filter — accepts short extensions ('jpg'), full MIME types ('image/jpeg'),\n * wildcards ('image/*'), or a function `(mimetype, filename) => boolean` for full control.\n */\nexport type FileTypeFilter = string[] | ((mimetype: string, filename: string) => boolean)\n\n/**\n * Shared upload options used by both the `@FileUpload` decorator and the\n * `upload.single()` / `upload.array()` / `upload.none()` middleware.\n */\nexport interface BaseUploadOptions {\n /** Max file size in bytes (default: 5MB) */\n maxSize?: number\n /**\n * Allowed file types:\n * - **string[]** — short extensions ('jpg'), full MIME types ('image/jpeg'), or wildcards ('image/*')\n * - **function** — `(mimetype, filename) => boolean` for full control\n */\n allowedTypes?: FileTypeFilter\n /** Extend the built-in extension-to-MIME map */\n customMimeMap?: Record<string, string>\n}\n\nexport interface FileUploadConfig extends BaseUploadOptions {\n mode: 'single' | 'array' | 'none'\n /** Form field name (default: 'file') */\n fieldName?: string\n /** Max files for array mode (default: 10) */\n maxCount?: number\n}\n\n/** Configure file upload handling for a controller method */\nexport function FileUpload(config: FileUploadConfig): MethodDecorator {\n return (target, propertyKey) => {\n setMethodMeta(METADATA.FILE_UPLOAD, config, target.constructor, propertyKey as string)\n }\n}\n\n// ── Builder Decorator ───────────────────────────────────────────────────\n\nfunction attachBuilder(target: any): void {\n setClassMeta(METADATA.BUILDER, true, target)\n target.builder = function () {\n const props: Record<string, any> = {}\n const proxy: any = new Proxy(\n {},\n {\n get(_, key) {\n if (key === 'build') {\n return () => Object.assign(new target(), props)\n }\n return (value: any) => {\n props[key as string] = value\n return proxy\n }\n },\n },\n )\n return proxy\n }\n}\n\n/**\n * Add a static `builder()` method for fluent construction.\n *\n * Legacy decorators cannot widen the class type, so by default the\n * caller sees the unmodified shape. Opt into typing by adding a single\n * `declare static` line:\n *\n * ```ts\n * @Builder\n * class UserDto {\n * name!: string\n * email!: string\n * declare static readonly builder: () => BuilderOf<UserDto>\n * }\n * ```\n *\n * `readonly` keeps SonarQube's `typescript:S1444` quiet — the runtime\n * assigns `target.builder` once at decoration time and never again.\n *\n * If you'd rather not write the `declare`, use `withBuilder()` instead —\n * same runtime, types inferred automatically.\n */\nexport function Builder(target: any): void {\n attachBuilder(target)\n}\n\n/**\n * Factory variant of `@Builder`. Wraps the class with the same runtime\n * and returns it intersected with `{ builder(): BuilderOf<T> }` so the\n * static is fully typed without any `declare` boilerplate.\n *\n * ```ts\n * class UserDtoBase {\n * name!: string\n * email!: string\n * }\n * export const UserDto = withBuilder(UserDtoBase)\n * export type UserDto = InstanceType<typeof UserDto>\n *\n * UserDto.builder().name('Alice').email('a@b.com').build() // fully typed\n * ```\n */\nexport function withBuilder<C extends new (...args: any[]) => any>(\n ctor: C,\n): C & { builder(): BuilderOf<InstanceType<C>> } {\n attachBuilder(ctor)\n return ctor as C & { builder(): BuilderOf<InstanceType<C>> }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"decorators.mjs","names":[],"sources":["../../src/core/decorators.ts"],"sourcesContent":["import 'reflect-metadata'\nimport { METADATA, Scope, type BuilderOf, type ServiceOptions } from './interfaces'\nimport { Container, type KickJsRegistry } from './container'\nimport {\n setClassMeta,\n setMethodMeta,\n pushClassMeta,\n pushMethodMeta,\n setInMetaMap,\n setInMetaRecord,\n} from './metadata'\n\n// ── Decorator Registration System ───────────────────────────────────────\n// Decorators execute at class-definition time (module load). The Container\n// may not exist yet when the first decorator fires. We queue registrations\n// and flush them on first Container.getInstance() call.\n//\n// `allRegistrations` is a persistent registry that survives Container.reset().\n// On reset (HMR), all previously decorated classes are re-registered on the\n// fresh container. This ensures @Service, @Controller, @Repository, etc.\n// all survive HMR without manual re-registration.\n\ntype PendingRegistration = { target: any; scope: Scope }\nconst pendingRegistrations: PendingRegistration[] = []\n// Keyed by class name to prevent memory leaks during HMR — new class with\n// the same name replaces the old entry instead of accumulating references.\nconst allRegistrations = new Map<string, { target: any; scope: Scope }>()\nlet containerRef: any = null\n\nfunction flushPending(container: any): void {\n containerRef = container\n for (const { target, scope } of pendingRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n pendingRegistrations.length = 0\n}\n\n// Wire up synchronously — Container._onReady is called on first getInstance()\nContainer._onReady = flushPending\n\n// On Container.reset(), update containerRef and replay ALL decorator\n// registrations on the fresh container. This handles HMR where the container\n// is wiped but not all decorated modules are re-evaluated.\nContainer._onReset = (container: any) => {\n containerRef = container\n for (const [, { target, scope }] of allRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n}\n\n// ── Class Decorators ────────────────────────────────────────────────────\n\nfunction registerInContainer(target: any, scope: Scope): void {\n setClassMeta(METADATA.INJECTABLE, true, target)\n setClassMeta(METADATA.SCOPE, scope, target)\n\n // Track in persistent registry — survives Container.reset() for HMR replay.\n // Keyed by name so HMR class re-creation replaces the old entry.\n const name = target.name || String(target)\n allRegistrations.set(name, { target, scope })\n\n if (containerRef) {\n // Container already initialized — register immediately\n if (!containerRef.has(target)) {\n containerRef.register(target, target, scope)\n }\n } else {\n // Container not ready yet — queue for later\n pendingRegistrations.push({ target, scope })\n }\n}\n\n/** Mark a class as injectable with lifecycle scope */\nexport function Injectable(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'injectable', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a service (semantic alias for Injectable) */\nexport function Service(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'service', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a generic managed component */\nexport function Component(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'component', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a repository */\nexport function Repository(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'repository', target)\n registerInContainer(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/**\n * Mark a class as an HTTP controller and register it in the DI container.\n *\n * Route prefixes are defined by the module's `routes().path` — the single\n * source of truth for where routes mount. The legacy\n * `@Controller('/path')` parameter was removed in v4; if your code\n * still passes a string, see `docs/guide/migration-v3-to-v4.md` for\n * the rename map.\n */\nexport function Controller(): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'controller', target)\n registerInContainer(target, Scope.SINGLETON)\n }\n}\n\n// ── Method Decorators ───────────────────────────────────────────────────\n\n/** Mark a method as a lifecycle hook called after instantiation */\nexport function PostConstruct(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.POST_CONSTRUCT, propertyKey, target)\n }\n}\n\n/**\n * Mark a method as a teardown hook — the counterpart to {@link PostConstruct}.\n *\n * For REQUEST-scoped services it runs when the request's scope closes\n * (response finished or aborted): release per-request resources there\n * (transactions, handles, subscriptions). May be async; errors are logged\n * and swallowed so one failing hook can't break request completion.\n */\nexport function PreDestroy(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.PRE_DESTROY, propertyKey, target)\n }\n}\n\n// ── Injection Decorators (property + constructor parameter) ─────────────\n\n/**\n * A decorator that's valid as either a property decorator OR a\n * constructor-parameter decorator. forinda/kick-js#235 §2 — `@Autowired`\n * and `@Inject` previously diverged at the type level (property-only\n * vs parameter-only) and adopters who picked the wrong name for the\n * position got a cryptic TS1240 error. Both names now accept either\n * position and route to the correct metadata bucket at runtime.\n */\nexport interface PropertyOrParameterDecorator {\n /** Property decorator form — `@Autowired(TOKEN) private foo!: Foo`. */\n (target: object, propertyKey: string | symbol): void\n /** Constructor-parameter form — `constructor(@Inject(TOKEN) foo: Foo) {}`. */\n (target: object, propertyKey: string | symbol | undefined, parameterIndex: number): void\n}\n\nfunction applyInjection(\n token: unknown,\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number | undefined,\n): void {\n // Legacy parameter decorators receive 3 args (target, propertyKey,\n // parameterIndex); property decorators receive 2 (target, propertyKey).\n // The 3rd-arg-number-vs-undefined check is the canonical position\n // detection in TypeScript's legacy decorator runtime.\n if (typeof parameterIndex === 'number') {\n setInMetaRecord(METADATA.INJECT, target, parameterIndex, token)\n return\n }\n if (propertyKey != null) {\n // `target` for a property decorator on a non-static field is the\n // class prototype; the AUTOWIRED bucket is keyed off the prototype\n // identity at resolution time, so pass it through unchanged.\n setInMetaMap(METADATA.AUTOWIRED, target, propertyKey as string, token)\n }\n}\n\n/**\n * Inject a dependency. Works in two positions:\n *\n * - **Property decorator** — `@Autowired(TOKEN) private repo!: UserRepo`.\n * Resolved lazily from the container the first time the property is\n * read.\n * - **Constructor parameter decorator** — `constructor(@Autowired(TOKEN) repo: UserRepo) {}`.\n * Resolved at instantiation; injected into the ctor call.\n *\n * `@Inject` is the same function under another name — they share\n * runtime + types so the two names are interchangeable. Pick whichever\n * reads better at the call site.\n *\n * Like `@Inject`, the typed string-literal overload narrows the token\n * to a key of the augmented `KickJsRegistry`. After `kick typegen`\n * populates the registry, `@Autowired('kick/prisma/Client')` auto-\n * completes the key and typo'd literals become TS2345 errors.\n */\nexport function Autowired<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Inject a dependency by token. Same shape as {@link Autowired} —\n * works as either a property decorator or a constructor-parameter\n * decorator. Pick whichever name reads better at the call site.\n *\n * ## Typed string-literal overload\n *\n * When called with a string literal that matches a key of the\n * augmented `KickJsRegistry`, TypeScript narrows the parameter type\n * to the registered type. Typo'd literals become compile errors\n * (the string isn't `keyof KickJsRegistry`). Class identities and\n * `InjectionToken<T>` keep working unchanged via the second overload.\n *\n * @example\n * ```ts\n * import { Service, Inject } from '@forinda/kickjs'\n * import type { PrismaClient } from '@prisma/client'\n *\n * `@Service()`\n * class UserRepo {\n * constructor(@Inject('kick/prisma/Client') private db: PrismaClient) {}\n * // OR property form, same decorator:\n * @Inject('kick/prisma/Client') private db2!: PrismaClient\n * }\n * ```\n *\n * After `kick typegen` runs, the literal autocompletes from the\n * registry and a typo (`@Inject('kick/prisma/Cleint')`) becomes a\n * TS2345 error.\n */\nexport function Inject<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Global ambient registry of typed environment variables, populated by\n * `kick typegen` from the project's env schema (typically\n * `src/env.ts`'s `export default defineEnv(...)`).\n *\n * Empty by default — declarations come from `.kickjs/types/env.ts`\n * generated alongside the rest of the typegen output. Once populated,\n * `@Value('PORT')` autocompletes the key, type-checks the default\n * value, and `process.env.PORT` is also typed via the parallel\n * `NodeJS.ProcessEnv` augmentation in the same generated file.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface KickEnv {}\n}\n\n/** A string key known to the typed env registry (`never` until typegen runs) */\nexport type EnvKey = keyof KickEnv & string\n\n/**\n * Look up the type of a typed env entry.\n *\n * Decorators can't propagate the property type from the key, so users\n * still need to write the property's type — but `Env<K>` lets them\n * derive it from `KickEnv` instead of duplicating the schema:\n *\n * ```ts\n * @Service()\n * class MyService {\n * @Value('PORT')\n * private port!: Env<'PORT'> // → number, narrowed from KickEnv['PORT']\n * }\n * ```\n */\nexport type Env<K extends EnvKey = EnvKey> = KickEnv[K]\n\n/**\n * Constraint that allows any string when the typed env registry is\n * empty (no `kick typegen` has run yet, so existing code with raw\n * string keys keeps working) but locks the argument down to known\n * `EnvKey` literals once the registry is populated.\n *\n * Implementation: when `EnvKey` is `never`, the conditional resolves\n * to the user's `K`, accepting anything. Once typegen has populated\n * `KickEnv`, `EnvKey` becomes a real union and the conditional forces\n * `K` to be assignable to it.\n */\ntype ValueKey<K extends string> = [EnvKey] extends [never] ? K : K & EnvKey\n\n/**\n * Inject an environment variable value. Evaluated lazily so the env\n * is available at access time, not at decoration time.\n *\n * If no default is provided and the env var is missing, throws at access time\n * to catch misconfiguration early instead of returning undefined.\n *\n * Uses metadata + instance getter to work correctly with `useDefineForClassFields`.\n *\n * Type-safety:\n * - Without `kick typegen` run, any string key is accepted (back-compat).\n * - After typegen, `KickEnv` is populated and unknown keys become a tsc\n * error. The optional `defaultValue` is type-checked against the\n * schema's inferred type for that key.\n */\nexport function Value<K extends string>(\n envKey: ValueKey<K>,\n defaultValue?: K extends EnvKey ? KickEnv[K] : unknown,\n): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.VALUE, target, propertyKey as string, { envKey, defaultValue })\n }\n}\n\n// ── @Asset — typed asset path injection (assets-plan.md PR 3+) ─────────\n\nimport type { KickAssets } from './assets'\n\n/**\n * Flatten the nested `KickAssets` augmentation (typegen-emitted in\n * PR 4) into a string-literal union of dot/slash-joined paths.\n *\n * `interface KickAssets { mails: { welcome: () => string; orders: {\n * confirmation: () => string } } }` becomes\n * `'mails/welcome' | 'mails/orders/confirmation'`.\n *\n * Returns `never` when `KickAssets` is empty (no typegen has run yet)\n * so `AssetKeyArg` falls through to accept any string.\n */\ntype FlattenAssets<T, Prefix extends string = ''> = {\n [K in keyof T & string]: T[K] extends () => string\n ? `${Prefix}${K}`\n : T[K] extends Record<string, unknown>\n ? FlattenAssets<T[K], `${Prefix}${K}/`>\n : never\n}[keyof T & string]\n\n/** Flat union of every known `<namespace>/<key>` path. */\nexport type AssetKey = FlattenAssets<KickAssets>\n\n/**\n * Constraint mirroring `ValueKey<K>` — accepts any string when\n * `AssetKey` is `never` (back-compat for adopters who haven't run\n * `kick typegen` yet) but locks the argument to known literals once\n * the typegen-augmented `KickAssets` interface is populated.\n */\ntype AssetKeyArg<K extends string> = [AssetKey] extends [never] ? K : K & AssetKey\n\n/**\n * Inject the resolved file-system path for a typed asset. Mirrors\n * `@Value`'s lazy-getter pattern — the asset is resolved on every\n * property access, NOT at class instantiation, so:\n *\n * - Tests can swap fixtures + clear the cache without re-instantiating\n * the consuming class.\n * - The first access cost is paid once per process (the manifest cache\n * in `core/assets.ts` makes subsequent calls cheap).\n *\n * Type-safety:\n *\n * - Without typegen, any string key is accepted (back-compat — same\n * trick as `@Value`).\n * - After typegen, `KickAssets` is populated and `AssetKey` is a\n * union of every flattened `<namespace>/<key>` path. Unknown keys\n * become a TS error.\n *\n * @example\n * ```ts\n * import { Service, Asset } from '@forinda/kickjs'\n *\n * `@Service()`\n * class MailService {\n * `@Asset('mails/welcome')`\n * private welcomeTemplate!: string\n *\n * send(user: User) {\n * return ejs.renderFile(this.welcomeTemplate, { user })\n * }\n * }\n * ```\n */\nexport function Asset<K extends string>(assetKey: AssetKeyArg<K>): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.ASSET, target, propertyKey as string, { assetKey })\n }\n}\n\n// ── HTTP Route Decorators ───────────────────────────────────────────────\n\nexport interface RouteDefinition {\n method: string\n path: string\n handlerName: string\n validation?: {\n /** JSON Schema object for validating the request body */\n body?: any\n /** JSON Schema object for validating query parameters */\n query?: any\n /** JSON Schema object for validating URL params */\n params?: any\n /** Schema name in OpenAPI components/schemas for the request body. Auto-generated from handler name if omitted. */\n name?: string\n }\n}\n\nfunction createRouteDecorator(method: string) {\n return (path?: string, validation?: RouteDefinition['validation']): MethodDecorator => {\n return (target, propertyKey) => {\n pushClassMeta<RouteDefinition>(METADATA.ROUTES, target.constructor, {\n method,\n path: path || '/',\n handlerName: propertyKey as string,\n validation,\n })\n }\n }\n}\n\nexport const Get = createRouteDecorator('GET')\nexport const Post = createRouteDecorator('POST')\nexport const Put = createRouteDecorator('PUT')\nexport const Delete = createRouteDecorator('DELETE')\nexport const Patch = createRouteDecorator('PATCH')\n\n// ── Query Params Decorator ─────────────────────────────────────────────\n\nexport interface ApiQueryParamsConfig {\n /** Fields that can be used in filter queries (e.g., `?filter=status:eq:active`) */\n filterable?: string[]\n /** Fields that can be used in sort queries (e.g., `?sort=createdAt:desc`) */\n sortable?: string[]\n /** Fields that are searched with the `?q=` parameter */\n searchable?: string[]\n}\n\n/**\n * Column-object-based query params config (e.g., from DrizzleQueryParamsConfig).\n * `Object.keys()` is used to derive field names for OpenAPI docs.\n */\nexport interface ColumnApiQueryParamsConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/**\n * Normalize a query params config to the string-based ApiQueryParamsConfig.\n * Handles both string-based and column-object-based configs.\n */\nexport function normalizeApiQueryParamsConfig(\n config: ApiQueryParamsConfig | ColumnApiQueryParamsConfig,\n): ApiQueryParamsConfig {\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: config.searchColumns\n ? config.searchColumns.map((col: any) => col?.name ?? '').filter(Boolean)\n : undefined,\n }\n }\n return config as ApiQueryParamsConfig\n}\n\n/**\n * Document the query parameters accepted by a GET endpoint.\n * Used by SwaggerAdapter to generate `filter`, `sort`, `page`, `limit`, and `q` params\n * in the OpenAPI spec, with descriptions listing the allowed fields.\n *\n * Accepts both string-based configs and column-object configs (e.g., DrizzleQueryParamsConfig).\n *\n * @example\n * ```ts\n * // String-based\n * @ApiQueryParams({\n * filterable: ['status', 'category', 'price'],\n * sortable: ['name', 'createdAt', 'price'],\n * searchable: ['name', 'description'],\n * })\n *\n * // Column-object-based (Drizzle)\n * @ApiQueryParams(TASK_QUERY_CONFIG)\n * ```\n */\nexport function ApiQueryParams(\n config: ApiQueryParamsConfig | ColumnApiQueryParamsConfig,\n): MethodDecorator {\n return (target, propertyKey) => {\n const normalized = normalizeApiQueryParamsConfig(config)\n setMethodMeta(METADATA.QUERY_PARAMS, normalized, target.constructor, propertyKey as string)\n }\n}\n\n// ── Middleware Decorators ───────────────────────────────────────────────\n\n/**\n * Middleware handler function.\n * Generic `TCtx` defaults to `any` — import `RequestContext` from\n * `@forinda/kickjs` for full type safety:\n *\n * ```ts\n * import type { MiddlewareHandler } from '@forinda/kickjs'\n * import type { RequestContext } from '@forinda/kickjs'\n *\n * const auth: MiddlewareHandler<RequestContext> = (ctx, next) => {\n * ctx.body // fully typed\n * next()\n * }\n * ```\n */\nexport type MiddlewareHandler<TCtx = any> = (ctx: TCtx, next: () => void) => void | Promise<void>\n\n/** Attach middleware handlers to a class or method */\nexport function Middleware(...handlers: MiddlewareHandler[]): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n pushMethodMeta(\n METADATA.METHOD_MIDDLEWARES,\n target.constructor,\n propertyKey as string,\n ...handlers,\n )\n } else {\n pushClassMeta(METADATA.CLASS_MIDDLEWARES, target, ...handlers)\n }\n }\n}\n\n// ── File Upload Decorator ───────────────────────────────────────────────\n\n/**\n * File filter — accepts short extensions ('jpg'), full MIME types ('image/jpeg'),\n * wildcards ('image/*'), or a function `(mimetype, filename) => boolean` for full control.\n */\nexport type FileTypeFilter = string[] | ((mimetype: string, filename: string) => boolean)\n\n/**\n * Shared upload options used by both the `@FileUpload` decorator and the\n * `upload.single()` / `upload.array()` / `upload.none()` middleware.\n */\nexport interface BaseUploadOptions {\n /** Max file size in bytes (default: 5MB) */\n maxSize?: number\n /**\n * Allowed file types:\n * - **string[]** — short extensions ('jpg'), full MIME types ('image/jpeg'), or wildcards ('image/*')\n * - **function** — `(mimetype, filename) => boolean` for full control\n */\n allowedTypes?: FileTypeFilter\n /** Extend the built-in extension-to-MIME map */\n customMimeMap?: Record<string, string>\n}\n\nexport interface FileUploadConfig extends BaseUploadOptions {\n mode: 'single' | 'array' | 'none'\n /** Form field name (default: 'file') */\n fieldName?: string\n /** Max files for array mode (default: 10) */\n maxCount?: number\n}\n\n/** Configure file upload handling for a controller method */\nexport function FileUpload(config: FileUploadConfig): MethodDecorator {\n return (target, propertyKey) => {\n setMethodMeta(METADATA.FILE_UPLOAD, config, target.constructor, propertyKey as string)\n }\n}\n\n// ── Builder Decorator ───────────────────────────────────────────────────\n\nfunction attachBuilder(target: any): void {\n setClassMeta(METADATA.BUILDER, true, target)\n target.builder = function () {\n const props: Record<string, any> = {}\n const proxy: any = new Proxy(\n {},\n {\n get(_, key) {\n if (key === 'build') {\n return () => Object.assign(new target(), props)\n }\n return (value: any) => {\n props[key as string] = value\n return proxy\n }\n },\n },\n )\n return proxy\n }\n}\n\n/**\n * Add a static `builder()` method for fluent construction.\n *\n * Legacy decorators cannot widen the class type, so by default the\n * caller sees the unmodified shape. Opt into typing by adding a single\n * `declare static` line:\n *\n * ```ts\n * @Builder\n * class UserDto {\n * name!: string\n * email!: string\n * declare static readonly builder: () => BuilderOf<UserDto>\n * }\n * ```\n *\n * `readonly` keeps SonarQube's `typescript:S1444` quiet — the runtime\n * assigns `target.builder` once at decoration time and never again.\n *\n * If you'd rather not write the `declare`, use `withBuilder()` instead —\n * same runtime, types inferred automatically.\n */\nexport function Builder(target: any): void {\n attachBuilder(target)\n}\n\n/**\n * Factory variant of `@Builder`. Wraps the class with the same runtime\n * and returns it intersected with `{ builder(): BuilderOf<T> }` so the\n * static is fully typed without any `declare` boilerplate.\n *\n * ```ts\n * class UserDtoBase {\n * name!: string\n * email!: string\n * }\n * export const UserDto = withBuilder(UserDtoBase)\n * export type UserDto = InstanceType<typeof UserDto>\n *\n * UserDto.builder().name('Alice').email('a@b.com').build() // fully typed\n * ```\n */\nexport function withBuilder<C extends new (...args: any[]) => any>(\n ctor: C,\n): C & { builder(): BuilderOf<InstanceType<C>> } {\n attachBuilder(ctor)\n return ctor as C & { builder(): BuilderOf<InstanceType<C>> }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as METADATA, c as Scope, i as Constructor, l as ServiceOptions, n as BuilderOf, o as MaybePromise, r as ClassKind, s as PostConstructStatus, t as Buildable } from "../interfaces-DbTyKGdS.mjs";
2
+ import { a as METADATA, c as Scope, i as Constructor, l as ServiceOptions, n as BuilderOf, o as MaybePromise, r as ClassKind, s as PostConstructStatus, t as Buildable } from "../interfaces-CayXPOAf.mjs";
3
3
  export { Buildable, BuilderOf, ClassKind, Constructor, METADATA, MaybePromise, PostConstructStatus, Scope, ServiceOptions };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import"reflect-metadata";let Scope=function(Scope){return Scope.SINGLETON=`singleton`,Scope.TRANSIENT=`transient`,Scope.REQUEST=`request`,Scope}({});const METADATA={INJECTABLE:`kick:injectable`,SCOPE:`kick:scope`,CLASS_KIND:`kick:class-kind`,AUTOWIRED:`kick:autowired`,INJECT:`kick:inject`,POST_CONSTRUCT:`kick:post_construct`,BUILDER:`kick:builder`,QUERY_PARAMS:`kick:query:params`,ROUTES:`kick:routes`,CLASS_MIDDLEWARES:`kick:class:middlewares`,METHOD_MIDDLEWARES:`kick:method:middlewares`,CLASS_CONTRIBUTORS:`kick:class:contributors`,METHOD_CONTRIBUTORS:`kick:method:contributors`,FILE_UPLOAD:`kick:file:upload`,VALUE:`kick:value`,ASSET:`kick:asset`,PARAM_TYPES:`design:paramtypes`,PROPERTY_TYPE:`design:type`,RETURN_TYPE:`design:returntype`};export{METADATA,Scope};
11
+ import"reflect-metadata";let Scope=function(Scope){return Scope.SINGLETON=`singleton`,Scope.TRANSIENT=`transient`,Scope.REQUEST=`request`,Scope}({});const METADATA={INJECTABLE:`kick:injectable`,SCOPE:`kick:scope`,CLASS_KIND:`kick:class-kind`,AUTOWIRED:`kick:autowired`,INJECT:`kick:inject`,POST_CONSTRUCT:`kick:post_construct`,PRE_DESTROY:`kick:pre_destroy`,BUILDER:`kick:builder`,QUERY_PARAMS:`kick:query:params`,ROUTES:`kick:routes`,CLASS_MIDDLEWARES:`kick:class:middlewares`,METHOD_MIDDLEWARES:`kick:method:middlewares`,CLASS_CONTRIBUTORS:`kick:class:contributors`,METHOD_CONTRIBUTORS:`kick:method:contributors`,FILE_UPLOAD:`kick:file:upload`,VALUE:`kick:value`,ASSET:`kick:asset`,PARAM_TYPES:`design:paramtypes`,PROPERTY_TYPE:`design:type`,RETURN_TYPE:`design:returntype`};export{METADATA,Scope};
12
12
  //# sourceMappingURL=interfaces.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.mjs","names":[],"sources":["../../src/core/interfaces.ts"],"sourcesContent":["import 'reflect-metadata'\n\n/** Lifecycle scope for DI registrations */\nexport enum Scope {\n SINGLETON = 'singleton',\n TRANSIENT = 'transient',\n /** One instance per HTTP request, cached in AsyncLocalStorage. Throws outside request context. */\n REQUEST = 'request',\n}\n\n/** Generic constructor type */\nexport type Constructor<T = any> = new (...args: any[]) => T\n\n/** A value that may or may not be wrapped in a Promise */\nexport type MaybePromise<T = void> = T | Promise<T>\n\n/** Options for @Service, @Repository, @Component decorators */\nexport interface ServiceOptions {\n scope?: Scope\n}\n\n/** Fluent builder type */\nexport type BuilderOf<T> = {\n [K in keyof T as T[K] extends Function ? never : K]-?: (value: T[K]) => BuilderOf<T>\n} & { build(): T }\n\n/** Class with static builder() method */\nexport interface Buildable<T> {\n builder(): BuilderOf<T>\n}\n\n/** Decorator class kind for devtools introspection */\nexport type ClassKind =\n | 'service'\n | 'controller'\n | 'repository'\n | 'component'\n | 'injectable'\n | 'factory'\n | 'instance'\n | 'unknown'\n\n/** PostConstruct lifecycle status */\nexport type PostConstructStatus = 'pending' | 'completed' | 'failed' | 'skipped'\n\n/**\n * String metadata keys for the DI system + decorator subsystem. Every\n * key starts with the `kick:` prefix; the suffix is a short identifier\n * that may itself contain `:`, `-`, or `_` separators (historical —\n * keys grew alongside the framework). The §22 v4 convention is \"no\n * Symbols\", not \"use a strict naming scheme\" — Symbols don't survive\n * JSON serialisation, can't be addressed by string from worker\n * threads, and don't show up in DevTools snapshots.\n *\n * `reflect-metadata` accepts both Symbol and string keys identically,\n * so the consumer-side change is internal — references go through the\n * `METADATA` enum's named members (e.g. `METADATA.INJECT`), not the\n * underlying string.\n *\n * The `design:*` entries match what TypeScript's\n * `emitDecoratorMetadata` writes; those names are fixed by the\n * compiler + must stay as-is.\n */\nexport const METADATA = {\n INJECTABLE: 'kick:injectable',\n SCOPE: 'kick:scope',\n CLASS_KIND: 'kick:class-kind',\n AUTOWIRED: 'kick:autowired',\n INJECT: 'kick:inject',\n POST_CONSTRUCT: 'kick:post_construct',\n BUILDER: 'kick:builder',\n QUERY_PARAMS: 'kick:query:params',\n ROUTES: 'kick:routes',\n CLASS_MIDDLEWARES: 'kick:class:middlewares',\n METHOD_MIDDLEWARES: 'kick:method:middlewares',\n CLASS_CONTRIBUTORS: 'kick:class:contributors',\n METHOD_CONTRIBUTORS: 'kick:method:contributors',\n FILE_UPLOAD: 'kick:file:upload',\n VALUE: 'kick:value',\n ASSET: 'kick:asset',\n // TypeScript emit metadata keys — fixed by the compiler.\n PARAM_TYPES: 'design:paramtypes',\n PROPERTY_TYPE: 'design:type',\n RETURN_TYPE: 'design:returntype',\n} as const\n"],"mappings":";;;;;;;;;;yBAGA,IAAY,MAAL,SAAA,MAAA,OACL,OAAA,UAAA,YACA,MAAA,UAAA,YAEA,MAAA,QAAA,eACF,EAAA,CAAA,CAAA,EAuDA,MAAa,SAAW,CACtB,WAAY,kBACZ,MAAO,aACP,WAAY,kBACZ,UAAW,iBACX,OAAQ,cACR,eAAgB,sBAChB,QAAS,eACT,aAAc,oBACd,OAAQ,cACR,kBAAmB,yBACnB,mBAAoB,0BACpB,mBAAoB,0BACpB,oBAAqB,2BACrB,YAAa,mBACb,MAAO,aACP,MAAO,aAEP,YAAa,oBACb,cAAe,cACf,YAAa,mBACf"}
1
+ {"version":3,"file":"interfaces.mjs","names":[],"sources":["../../src/core/interfaces.ts"],"sourcesContent":["import 'reflect-metadata'\n\n/** Lifecycle scope for DI registrations */\nexport enum Scope {\n SINGLETON = 'singleton',\n TRANSIENT = 'transient',\n /** One instance per HTTP request, cached in AsyncLocalStorage. Throws outside request context. */\n REQUEST = 'request',\n}\n\n/** Generic constructor type */\nexport type Constructor<T = any> = new (...args: any[]) => T\n\n/** A value that may or may not be wrapped in a Promise */\nexport type MaybePromise<T = void> = T | Promise<T>\n\n/** Options for @Service, @Repository, @Component decorators */\nexport interface ServiceOptions {\n scope?: Scope\n}\n\n/** Fluent builder type */\nexport type BuilderOf<T> = {\n [K in keyof T as T[K] extends Function ? never : K]-?: (value: T[K]) => BuilderOf<T>\n} & { build(): T }\n\n/** Class with static builder() method */\nexport interface Buildable<T> {\n builder(): BuilderOf<T>\n}\n\n/** Decorator class kind for devtools introspection */\nexport type ClassKind =\n | 'service'\n | 'controller'\n | 'repository'\n | 'component'\n | 'injectable'\n | 'factory'\n | 'instance'\n | 'unknown'\n\n/** PostConstruct lifecycle status */\nexport type PostConstructStatus = 'pending' | 'completed' | 'failed' | 'skipped'\n\n/**\n * String metadata keys for the DI system + decorator subsystem. Every\n * key starts with the `kick:` prefix; the suffix is a short identifier\n * that may itself contain `:`, `-`, or `_` separators (historical —\n * keys grew alongside the framework). The §22 v4 convention is \"no\n * Symbols\", not \"use a strict naming scheme\" — Symbols don't survive\n * JSON serialisation, can't be addressed by string from worker\n * threads, and don't show up in DevTools snapshots.\n *\n * `reflect-metadata` accepts both Symbol and string keys identically,\n * so the consumer-side change is internal — references go through the\n * `METADATA` enum's named members (e.g. `METADATA.INJECT`), not the\n * underlying string.\n *\n * The `design:*` entries match what TypeScript's\n * `emitDecoratorMetadata` writes; those names are fixed by the\n * compiler + must stay as-is.\n */\nexport const METADATA = {\n INJECTABLE: 'kick:injectable',\n SCOPE: 'kick:scope',\n CLASS_KIND: 'kick:class-kind',\n AUTOWIRED: 'kick:autowired',\n INJECT: 'kick:inject',\n POST_CONSTRUCT: 'kick:post_construct',\n PRE_DESTROY: 'kick:pre_destroy',\n BUILDER: 'kick:builder',\n QUERY_PARAMS: 'kick:query:params',\n ROUTES: 'kick:routes',\n CLASS_MIDDLEWARES: 'kick:class:middlewares',\n METHOD_MIDDLEWARES: 'kick:method:middlewares',\n CLASS_CONTRIBUTORS: 'kick:class:contributors',\n METHOD_CONTRIBUTORS: 'kick:method:contributors',\n FILE_UPLOAD: 'kick:file:upload',\n VALUE: 'kick:value',\n ASSET: 'kick:asset',\n // TypeScript emit metadata keys — fixed by the compiler.\n PARAM_TYPES: 'design:paramtypes',\n PROPERTY_TYPE: 'design:type',\n RETURN_TYPE: 'design:returntype',\n} as const\n"],"mappings":";;;;;;;;;;yBAGA,IAAY,MAAL,SAAA,MAAA,OACL,OAAA,UAAA,YACA,MAAA,UAAA,YAEA,MAAA,QAAA,eACF,EAAA,CAAA,CAAA,EAuDA,MAAa,SAAW,CACtB,WAAY,kBACZ,MAAO,aACP,WAAY,kBACZ,UAAW,iBACX,OAAQ,cACR,eAAgB,sBAChB,YAAa,mBACb,QAAS,eACT,aAAc,oBACd,OAAQ,cACR,kBAAmB,yBACnB,mBAAoB,0BACpB,mBAAoB,0BACpB,oBAAqB,2BACrB,YAAa,mBACb,MAAO,aACP,MAAO,aAEP,YAAa,oBACb,cAAe,cACf,YAAa,mBACf"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- const LEVEL_RANK={trace:10,debug:20,info:30,warn:40,error:50,fatal:60,silent:100};function thresholdRank(){return LEVEL_RANK[(process.env.LOG_LEVEL??`info`).toLowerCase()]??LEVEL_RANK.info}var ConsoleLoggerProvider=class ConsoleLoggerProvider{prefix;useColor;constructor(prefix){this.prefix=prefix??``,this.useColor=typeof process<`u`&&process.stdout?.isTTY===!0&&!process.env.NO_COLOR}fmt(level,color,msg){let tag=this.prefix?`[${this.prefix}]`:``;return this.useColor?`${color}${level}\x1b[0m ${tag?`\x1b[36m${tag}\x1b[0m `:``}${msg}`:`${level} ${tag?`${tag} `:``}${msg}`}enabled(rank){return rank>=thresholdRank()}info(msg,...args){this.enabled(LEVEL_RANK.info)&&console.log(this.fmt(`INFO`,`\x1B[32m`,msg),...args)}warn(msg,...args){this.enabled(LEVEL_RANK.warn)&&console.warn(this.fmt(`WARN`,`\x1B[33m`,msg),...args)}error(msg,...args){this.enabled(LEVEL_RANK.error)&&console.error(this.fmt(`ERROR`,`\x1B[31m`,msg),...args)}debug(msg,...args){this.enabled(LEVEL_RANK.debug)&&console.debug(this.fmt(`DEBUG`,`\x1B[90m`,msg),...args)}trace(msg,...args){this.enabled(LEVEL_RANK.trace)&&console.trace(this.fmt(`TRACE`,`\x1B[90m`,msg),...args)}fatal(msg,...args){this.enabled(LEVEL_RANK.fatal)&&console.error(this.fmt(`FATAL`,`\x1B[1m\x1B[31m`,msg),...args)}child(bindings){return new ConsoleLoggerProvider(bindings.component)}};let activeProvider=new ConsoleLoggerProvider;const loggerCache=new Map;var Logger=class Logger{_provider;_providerVersion;_name;static _providerVersion=0;constructor(name){this._name=name,this._providerVersion=Logger._providerVersion,this._provider=name?activeProvider.child({component:name}):activeProvider}get provider(){return this._providerVersion!==Logger._providerVersion&&(this._providerVersion=Logger._providerVersion,this._provider=this._name?activeProvider.child({component:this._name}):activeProvider),this._provider}static setProvider(provider){activeProvider=provider,Logger._providerVersion++,loggerCache.clear()}static getProvider(){return activeProvider}static resetProvider(){activeProvider=new ConsoleLoggerProvider,Logger._providerVersion++,loggerCache.clear()}static for(name){let cached=loggerCache.get(name);return cached||(cached=new Logger(name),loggerCache.set(name,cached)),cached}child(name){return new Logger(name)}info(msg,...args){this.provider.info(msg,...args)}warn(msg,...args){this.provider.warn(msg,...args)}error(msgOrObj,msg,...args){typeof msgOrObj==`string`?this.provider.error(msgOrObj):msg?this.provider.error(msg,...args):this.provider.error(String(msgOrObj))}debug(msg,...args){this.provider.debug(msg,...args)}trace(msg,...args){this.provider.trace?this.provider.trace(msg,...args):this.provider.debug(msg,...args)}fatal(msg,...args){this.provider.fatal?this.provider.fatal(msg,...args):this.provider.error(msg,...args)}};function createLogger(name){return Logger.for(name)}export{ConsoleLoggerProvider,Logger,createLogger};
11
+ const LEVEL_RANK={trace:10,debug:20,info:30,warn:40,error:50,fatal:60,silent:100};function thresholdRank(){let level=(process.env.LOG_LEVEL??`info`).toLowerCase();return LEVEL_RANK[level]??LEVEL_RANK.info}var ConsoleLoggerProvider=class ConsoleLoggerProvider{prefix;useColor;constructor(prefix){this.prefix=prefix??``,this.useColor=typeof process<`u`&&process.stdout?.isTTY===!0&&!process.env.NO_COLOR}fmt(level,color,msg){let tag=this.prefix?`[${this.prefix}]`:``;return this.useColor?`${color}${level}\x1b[0m ${tag?`\x1b[36m${tag}\x1b[0m `:``}${msg}`:`${level} ${tag?`${tag} `:``}${msg}`}enabled(rank){return rank>=thresholdRank()}info(msg,...args){this.enabled(LEVEL_RANK.info)&&console.log(this.fmt(`INFO`,`\x1B[32m`,msg),...args)}warn(msg,...args){this.enabled(LEVEL_RANK.warn)&&console.warn(this.fmt(`WARN`,`\x1B[33m`,msg),...args)}error(msg,...args){this.enabled(LEVEL_RANK.error)&&console.error(this.fmt(`ERROR`,`\x1B[31m`,msg),...args)}debug(msg,...args){this.enabled(LEVEL_RANK.debug)&&console.debug(this.fmt(`DEBUG`,`\x1B[90m`,msg),...args)}trace(msg,...args){this.enabled(LEVEL_RANK.trace)&&console.trace(this.fmt(`TRACE`,`\x1B[90m`,msg),...args)}fatal(msg,...args){this.enabled(LEVEL_RANK.fatal)&&console.error(this.fmt(`FATAL`,`\x1B[1m\x1B[31m`,msg),...args)}child(bindings){return new ConsoleLoggerProvider(bindings.component)}};let activeProvider=new ConsoleLoggerProvider;const loggerCache=new Map;var Logger=class Logger{_provider;_providerVersion;_name;static _providerVersion=0;constructor(name){this._name=name,this._providerVersion=Logger._providerVersion,this._provider=name?activeProvider.child({component:name}):activeProvider}get provider(){return this._providerVersion!==Logger._providerVersion&&(this._providerVersion=Logger._providerVersion,this._provider=this._name?activeProvider.child({component:this._name}):activeProvider),this._provider}static setProvider(provider){activeProvider=provider,Logger._providerVersion++,loggerCache.clear()}static getProvider(){return activeProvider}static resetProvider(){activeProvider=new ConsoleLoggerProvider,Logger._providerVersion++,loggerCache.clear()}static for(name){let cached=loggerCache.get(name);return cached||(cached=new Logger(name),loggerCache.set(name,cached)),cached}child(name){return new Logger(name)}info(msg,...args){this.provider.info(msg,...args)}warn(msg,...args){this.provider.warn(msg,...args)}error(msgOrObj,msg,...args){typeof msgOrObj==`string`?this.provider.error(msgOrObj):msg?this.provider.error(msg,...args):this.provider.error(String(msgOrObj))}debug(msg,...args){this.provider.debug(msg,...args)}trace(msg,...args){this.provider.trace?this.provider.trace(msg,...args):this.provider.debug(msg,...args)}fatal(msg,...args){this.provider.fatal?this.provider.fatal(msg,...args):this.provider.error(msg,...args)}};function createLogger(name){return Logger.for(name)}export{ConsoleLoggerProvider,Logger,createLogger};
12
12
  //# sourceMappingURL=logger.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.mjs","names":[],"sources":["../../src/core/logger.ts"],"sourcesContent":["// ── LoggerProvider interface ───────────────────────────────────────────\n\n/**\n * Pluggable logger backend.\n *\n * Implement this interface to plug in any logging library (pino,\n * winston, bunyan, console, etc.). The framework only ever calls the\n * methods declared here — it doesn't know or care which logger is\n * underneath.\n *\n * Default: `ConsoleLoggerProvider` (zero runtime deps).\n *\n * @example\n * ```ts\n * import pino from 'pino'\n * import { Logger, type LoggerProvider } from '@forinda/kickjs'\n *\n * class PinoProvider implements LoggerProvider {\n * constructor(private p = pino()) {}\n * info(msg, ...args) { this.p.info(msg, ...args) }\n * warn(msg, ...args) { this.p.warn(msg, ...args) }\n * error(msg, ...args) { this.p.error(msg, ...args) }\n * debug(msg, ...args) { this.p.debug(msg, ...args) }\n * child({ component }) { return new PinoProvider(this.p.child({ component })) }\n * }\n *\n * Logger.setProvider(new PinoProvider())\n * ```\n *\n * See `docs/guide/logging.md` for Pino, Winston, and silent-logger recipes.\n */\nexport interface LoggerProvider {\n info(msg: string, ...args: any[]): void\n warn(msg: string, ...args: any[]): void\n error(msg: string, ...args: any[]): void\n debug(msg: string, ...args: any[]): void\n trace?(msg: string, ...args: any[]): void\n fatal?(msg: string, ...args: any[]): void\n /** Return a child provider scoped to the given component name */\n child(bindings: { component: string }): LoggerProvider\n}\n\n// ── Log levels ─────────────────────────────────────────────────────────\n\n/** Severity ordering — a message prints only when its rank ≥ the threshold. */\nconst LEVEL_RANK: Record<string, number> = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n silent: 100,\n}\n\n/**\n * Resolve the active threshold rank from `LOG_LEVEL` (default `info`).\n * Read per-call so a late `process.env.LOG_LEVEL` assignment still\n * takes effect. An unrecognised value falls back to `info`.\n */\nfunction thresholdRank(): number {\n const level = (process.env.LOG_LEVEL ?? 'info').toLowerCase()\n return LEVEL_RANK[level] ?? LEVEL_RANK.info\n}\n\n// ── ConsoleLoggerProvider (default) ────────────────────────────────────\n\n/**\n * Default provider — emits through `console.*` methods. Zero deps.\n *\n * Respects the `LOG_LEVEL` env var (default `info`): messages below the\n * threshold are dropped, so the verbose startup detail (the route table,\n * DI wiring, HMR ticks — all `debug`) stays quiet by default and only\n * appears with `LOG_LEVEL=debug`. `error`/`fatal` always print unless\n * `LOG_LEVEL=silent`.\n *\n * Swap it for pino, winston, bunyan, or anything else by implementing\n * the `LoggerProvider` interface and calling `Logger.setProvider()`\n * before `bootstrap()`.\n */\nexport class ConsoleLoggerProvider implements LoggerProvider {\n private prefix: string\n private useColor: boolean\n\n constructor(prefix?: string) {\n this.prefix = prefix ?? ''\n this.useColor =\n typeof process !== 'undefined' && process.stdout?.isTTY === true && !process.env.NO_COLOR\n }\n\n private fmt(level: string, color: string, msg: string): string {\n const tag = this.prefix ? `[${this.prefix}]` : ''\n if (this.useColor) {\n return `${color}${level}\\x1b[0m ${tag ? `\\x1b[36m${tag}\\x1b[0m ` : ''}${msg}`\n }\n return `${level} ${tag ? `${tag} ` : ''}${msg}`\n }\n\n /** Should a message at `rank` print under the current `LOG_LEVEL`? */\n private enabled(rank: number): boolean {\n return rank >= thresholdRank()\n }\n\n info(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.info)) console.log(this.fmt('INFO', '\\x1b[32m', msg), ...args)\n }\n warn(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.warn)) console.warn(this.fmt('WARN', '\\x1b[33m', msg), ...args)\n }\n error(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.error)) console.error(this.fmt('ERROR', '\\x1b[31m', msg), ...args)\n }\n debug(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.debug)) console.debug(this.fmt('DEBUG', '\\x1b[90m', msg), ...args)\n }\n trace(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.trace)) console.trace(this.fmt('TRACE', '\\x1b[90m', msg), ...args)\n }\n fatal(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.fatal))\n console.error(this.fmt('FATAL', '\\x1b[1m\\x1b[31m', msg), ...args)\n }\n child(bindings: { component: string }): LoggerProvider {\n return new ConsoleLoggerProvider(bindings.component)\n }\n}\n\n// ── Active provider ────────────────────────────────────────────────────\n\nlet activeProvider: LoggerProvider = new ConsoleLoggerProvider()\n\n/** Cache of named loggers to avoid creating duplicates */\nconst loggerCache = new Map<string, Logger>()\n\n/**\n * Named logger with component context.\n *\n * @example\n * ```ts\n * // Static factory — preferred\n * const log = Logger.for('UserService')\n * log.info('User created')\n * // Output: [UserService] User created\n *\n * // Shorthand function\n * const log = createLogger('OrderService')\n *\n * // Pluggable backend — see docs/guide/logging.md\n * Logger.setProvider(new MyCustomProvider())\n *\n * // Injectable — auto-named from class\n * class MyService {\n * @Autowired() private logger!: Logger\n * // or inject a named one:\n * private log = Logger.for('MyService')\n * }\n * ```\n */\nexport class Logger {\n private _provider: LoggerProvider\n private _providerVersion: number\n private _name?: string\n\n /** Incremented on every setProvider/resetProvider so instances know to refresh */\n private static _providerVersion = 0\n\n constructor(name?: string) {\n this._name = name\n this._providerVersion = Logger._providerVersion\n this._provider = name ? activeProvider.child({ component: name }) : activeProvider\n }\n\n /** Re-derive the provider if setProvider() was called since construction/last access */\n private get provider(): LoggerProvider {\n if (this._providerVersion !== Logger._providerVersion) {\n this._providerVersion = Logger._providerVersion\n this._provider = this._name ? activeProvider.child({ component: this._name }) : activeProvider\n }\n return this._provider\n }\n\n /**\n * Replace the logging backend for **all** Logger instances created after\n * this call. Clears the logger cache so subsequent `Logger.for()` calls\n * pick up the new provider.\n *\n * @example\n * ```ts\n * Logger.setProvider(new MyWinstonProvider())\n * ```\n */\n static setProvider(provider: LoggerProvider): void {\n activeProvider = provider\n Logger._providerVersion++\n loggerCache.clear()\n }\n\n /** Return the currently active provider (useful for testing) */\n static getProvider(): LoggerProvider {\n return activeProvider\n }\n\n /**\n * Reset the provider back to the default `ConsoleLoggerProvider`.\n * Primarily intended for test teardown.\n */\n static resetProvider(): void {\n activeProvider = new ConsoleLoggerProvider()\n Logger._providerVersion++\n loggerCache.clear()\n }\n\n /** Create or retrieve a cached named logger */\n static for(name: string): Logger {\n let cached = loggerCache.get(name)\n if (!cached) {\n cached = new Logger(name)\n loggerCache.set(name, cached)\n }\n return cached\n }\n\n /** Create a child logger with a sub-component name */\n child(name: string): Logger {\n return new Logger(name)\n }\n\n info(msg: string, ...args: any[]) {\n this.provider.info(msg, ...args)\n }\n\n warn(msg: string, ...args: any[]) {\n this.provider.warn(msg, ...args)\n }\n\n error(msgOrObj: any, msg?: string, ...args: any[]) {\n if (typeof msgOrObj === 'string') {\n this.provider.error(msgOrObj)\n } else if (msg) {\n this.provider.error(msg, ...args)\n } else {\n this.provider.error(String(msgOrObj))\n }\n }\n\n debug(msg: string, ...args: any[]) {\n this.provider.debug(msg, ...args)\n }\n\n trace(msg: string, ...args: any[]) {\n if (this.provider.trace) this.provider.trace(msg, ...args)\n else this.provider.debug(msg, ...args)\n }\n\n fatal(msg: string, ...args: any[]) {\n if (this.provider.fatal) this.provider.fatal(msg, ...args)\n else this.provider.error(msg, ...args)\n }\n}\n\n/** Shorthand for Logger.for(name) */\nexport function createLogger(name: string): Logger {\n return Logger.for(name)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"logger.mjs","names":[],"sources":["../../src/core/logger.ts"],"sourcesContent":["// ── LoggerProvider interface ───────────────────────────────────────────\n\n/**\n * Pluggable logger backend.\n *\n * Implement this interface to plug in any logging library (pino,\n * winston, bunyan, console, etc.). The framework only ever calls the\n * methods declared here — it doesn't know or care which logger is\n * underneath.\n *\n * Default: `ConsoleLoggerProvider` (zero runtime deps).\n *\n * @example\n * ```ts\n * import pino from 'pino'\n * import { Logger, type LoggerProvider } from '@forinda/kickjs'\n *\n * class PinoProvider implements LoggerProvider {\n * constructor(private p = pino()) {}\n * info(msg, ...args) { this.p.info(msg, ...args) }\n * warn(msg, ...args) { this.p.warn(msg, ...args) }\n * error(msg, ...args) { this.p.error(msg, ...args) }\n * debug(msg, ...args) { this.p.debug(msg, ...args) }\n * child({ component }) { return new PinoProvider(this.p.child({ component })) }\n * }\n *\n * Logger.setProvider(new PinoProvider())\n * ```\n *\n * See `docs/guide/logging.md` for Pino, Winston, and silent-logger recipes.\n */\nexport interface LoggerProvider {\n info(msg: string, ...args: any[]): void\n warn(msg: string, ...args: any[]): void\n error(msg: string, ...args: any[]): void\n debug(msg: string, ...args: any[]): void\n trace?(msg: string, ...args: any[]): void\n fatal?(msg: string, ...args: any[]): void\n /** Return a child provider scoped to the given component name */\n child(bindings: { component: string }): LoggerProvider\n}\n\n// ── Log levels ─────────────────────────────────────────────────────────\n\n/** Severity ordering — a message prints only when its rank ≥ the threshold. */\nconst LEVEL_RANK: Record<string, number> = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n silent: 100,\n}\n\n/**\n * Resolve the active threshold rank from `LOG_LEVEL` (default `info`).\n * Read per-call so a late `process.env.LOG_LEVEL` assignment still\n * takes effect. An unrecognised value falls back to `info`.\n */\nfunction thresholdRank(): number {\n const level = (process.env.LOG_LEVEL ?? 'info').toLowerCase()\n return LEVEL_RANK[level] ?? LEVEL_RANK.info\n}\n\n// ── ConsoleLoggerProvider (default) ────────────────────────────────────\n\n/**\n * Default provider — emits through `console.*` methods. Zero deps.\n *\n * Respects the `LOG_LEVEL` env var (default `info`): messages below the\n * threshold are dropped, so the verbose startup detail (the route table,\n * DI wiring, HMR ticks — all `debug`) stays quiet by default and only\n * appears with `LOG_LEVEL=debug`. `error`/`fatal` always print unless\n * `LOG_LEVEL=silent`.\n *\n * Swap it for pino, winston, bunyan, or anything else by implementing\n * the `LoggerProvider` interface and calling `Logger.setProvider()`\n * before `bootstrap()`.\n */\nexport class ConsoleLoggerProvider implements LoggerProvider {\n private prefix: string\n private useColor: boolean\n\n constructor(prefix?: string) {\n this.prefix = prefix ?? ''\n this.useColor =\n typeof process !== 'undefined' && process.stdout?.isTTY === true && !process.env.NO_COLOR\n }\n\n private fmt(level: string, color: string, msg: string): string {\n const tag = this.prefix ? `[${this.prefix}]` : ''\n if (this.useColor) {\n return `${color}${level}\\x1b[0m ${tag ? `\\x1b[36m${tag}\\x1b[0m ` : ''}${msg}`\n }\n return `${level} ${tag ? `${tag} ` : ''}${msg}`\n }\n\n /** Should a message at `rank` print under the current `LOG_LEVEL`? */\n private enabled(rank: number): boolean {\n return rank >= thresholdRank()\n }\n\n info(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.info)) console.log(this.fmt('INFO', '\\x1b[32m', msg), ...args)\n }\n warn(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.warn)) console.warn(this.fmt('WARN', '\\x1b[33m', msg), ...args)\n }\n error(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.error)) console.error(this.fmt('ERROR', '\\x1b[31m', msg), ...args)\n }\n debug(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.debug)) console.debug(this.fmt('DEBUG', '\\x1b[90m', msg), ...args)\n }\n trace(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.trace)) console.trace(this.fmt('TRACE', '\\x1b[90m', msg), ...args)\n }\n fatal(msg: string, ...args: any[]) {\n if (this.enabled(LEVEL_RANK.fatal))\n console.error(this.fmt('FATAL', '\\x1b[1m\\x1b[31m', msg), ...args)\n }\n child(bindings: { component: string }): LoggerProvider {\n return new ConsoleLoggerProvider(bindings.component)\n }\n}\n\n// ── Active provider ────────────────────────────────────────────────────\n\nlet activeProvider: LoggerProvider = new ConsoleLoggerProvider()\n\n/** Cache of named loggers to avoid creating duplicates */\nconst loggerCache = new Map<string, Logger>()\n\n/**\n * Named logger with component context.\n *\n * @example\n * ```ts\n * // Static factory — preferred\n * const log = Logger.for('UserService')\n * log.info('User created')\n * // Output: [UserService] User created\n *\n * // Shorthand function\n * const log = createLogger('OrderService')\n *\n * // Pluggable backend — see docs/guide/logging.md\n * Logger.setProvider(new MyCustomProvider())\n *\n * // Injectable — auto-named from class\n * class MyService {\n * @Autowired() private logger!: Logger\n * // or inject a named one:\n * private log = Logger.for('MyService')\n * }\n * ```\n */\nexport class Logger {\n private _provider: LoggerProvider\n private _providerVersion: number\n private _name?: string\n\n /** Incremented on every setProvider/resetProvider so instances know to refresh */\n private static _providerVersion = 0\n\n constructor(name?: string) {\n this._name = name\n this._providerVersion = Logger._providerVersion\n this._provider = name ? activeProvider.child({ component: name }) : activeProvider\n }\n\n /** Re-derive the provider if setProvider() was called since construction/last access */\n private get provider(): LoggerProvider {\n if (this._providerVersion !== Logger._providerVersion) {\n this._providerVersion = Logger._providerVersion\n this._provider = this._name ? activeProvider.child({ component: this._name }) : activeProvider\n }\n return this._provider\n }\n\n /**\n * Replace the logging backend for **all** Logger instances created after\n * this call. Clears the logger cache so subsequent `Logger.for()` calls\n * pick up the new provider.\n *\n * @example\n * ```ts\n * Logger.setProvider(new MyWinstonProvider())\n * ```\n */\n static setProvider(provider: LoggerProvider): void {\n activeProvider = provider\n Logger._providerVersion++\n loggerCache.clear()\n }\n\n /** Return the currently active provider (useful for testing) */\n static getProvider(): LoggerProvider {\n return activeProvider\n }\n\n /**\n * Reset the provider back to the default `ConsoleLoggerProvider`.\n * Primarily intended for test teardown.\n */\n static resetProvider(): void {\n activeProvider = new ConsoleLoggerProvider()\n Logger._providerVersion++\n loggerCache.clear()\n }\n\n /** Create or retrieve a cached named logger */\n static for(name: string): Logger {\n let cached = loggerCache.get(name)\n if (!cached) {\n cached = new Logger(name)\n loggerCache.set(name, cached)\n }\n return cached\n }\n\n /** Create a child logger with a sub-component name */\n child(name: string): Logger {\n return new Logger(name)\n }\n\n info(msg: string, ...args: any[]) {\n this.provider.info(msg, ...args)\n }\n\n warn(msg: string, ...args: any[]) {\n this.provider.warn(msg, ...args)\n }\n\n error(msgOrObj: any, msg?: string, ...args: any[]) {\n if (typeof msgOrObj === 'string') {\n this.provider.error(msgOrObj)\n } else if (msg) {\n this.provider.error(msg, ...args)\n } else {\n this.provider.error(String(msgOrObj))\n }\n }\n\n debug(msg: string, ...args: any[]) {\n this.provider.debug(msg, ...args)\n }\n\n trace(msg: string, ...args: any[]) {\n if (this.provider.trace) this.provider.trace(msg, ...args)\n else this.provider.debug(msg, ...args)\n }\n\n fatal(msg: string, ...args: any[]) {\n if (this.provider.fatal) this.provider.fatal(msg, ...args)\n else this.provider.error(msg, ...args)\n }\n}\n\n/** Shorthand for Logger.for(name) */\nexport function createLogger(name: string): Logger {\n return Logger.for(name)\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { jt as KickPlugin } from "../adapter-D_uzU-QA.mjs";
2
+ import { Pt as KickPlugin } from "../adapter-DCfEFZMY.mjs";
3
3
  export { KickPlugin };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as isRef, c as toRefs, i as computed, l as unref, n as Ref, o as reactive, r as WatchOptions, s as ref, t as ComputedRef, u as watch } from "../reactivity-DWmLcN0A.mjs";
2
+ import { a as isRef, c as toRefs, i as computed, l as unref, n as Ref, o as reactive, r as WatchOptions, s as ref, t as ComputedRef, u as watch } from "../reactivity-XL0fFDvG.mjs";
3
3
  export { ComputedRef, Ref, WatchOptions, computed, isRef, reactive, ref, toRefs, unref, watch };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.1.1
2
+ * @forinda/kickjs v6.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- let activeEffect=null;const targetMap=new WeakMap;function track(target,key){if(!activeEffect)return;let depsMap=targetMap.get(target);depsMap||(depsMap=new Map,targetMap.set(target,depsMap));let deps=depsMap.get(key);deps||(deps=new Set,depsMap.set(key,deps)),deps.add(activeEffect)}function trigger(target,key){let depsMap=targetMap.get(target);if(!depsMap)return;let deps=depsMap.get(key);if(deps)for(let effect of deps)effect()}function ref(initialValue){let subscribers=new Set,wrapper={_value:initialValue};return{get value(){return track(wrapper,`_value`),wrapper._value},set value(newValue){let oldValue=wrapper._value;if(!Object.is(oldValue,newValue)){wrapper._value=newValue,trigger(wrapper,`_value`);for(let fn of subscribers)fn(newValue,oldValue)}},subscribe(fn){return subscribers.add(fn),()=>subscribers.delete(fn)},toJSON(){return wrapper._value}}}function computed(getter){let cached,dirty=!0,wrapper={_value:void 0},effect=()=>{dirty=!0,trigger(wrapper,`_value`)},recompute=()=>{if(dirty){let prev=activeEffect;activeEffect=effect;try{cached=getter()}finally{activeEffect=prev}wrapper._value=cached,dirty=!1}return cached};return{get value(){return track(wrapper,`_value`),recompute()},toJSON(){return recompute()}}}function watch(source,callback,options){let oldValue,stopped=!1,getter=typeof source==`function`?source:()=>source.value,job=()=>{if(stopped)return;let newValue=getter();Object.is(newValue,oldValue)||(callback(newValue,oldValue),oldValue=newValue)},prev=activeEffect;activeEffect=job;try{oldValue=getter()}finally{activeEffect=prev}return options?.immediate&&callback(oldValue,void 0),()=>{stopped=!0}}function reactive(target){return new Proxy(target,{get(obj,key,receiver){let value=Reflect.get(obj,key,receiver);return typeof key==`symbol`?value:(track(obj,key),typeof value==`object`&&value&&!Array.isArray(value)?reactive(value):value)},set(obj,key,value,receiver){let oldValue=Reflect.get(obj,key,receiver),result=Reflect.set(obj,key,value,receiver);return!Object.is(oldValue,value)&&typeof key!=`symbol`&&trigger(obj,key),result}})}function isRef(value){return typeof value==`object`&&!!value&&`subscribe`in value&&`value`in value}function unref(value){return isRef(value)?value.value:value}function toRefs(obj){let result={};for(let key in obj)result[key]=ref(obj[key]);return result}export{computed,isRef,reactive,ref,toRefs,unref,watch};
11
+ let activeEffect=null;const targetMap=new WeakMap;function createEffect(fn){let effect=fn;return effect.deps=new Set,effect}function cleanupEffect(effect){for(let depSet of effect.deps)depSet.delete(effect);effect.deps.clear()}function runTracked(effect,fn){cleanupEffect(effect);let prev=activeEffect;activeEffect=effect;try{return fn()}finally{activeEffect=prev}}function track(target,key){if(!activeEffect)return;let depsMap=targetMap.get(target);depsMap||(depsMap=new Map,targetMap.set(target,depsMap));let deps=depsMap.get(key);deps||(deps=new Set,depsMap.set(key,deps)),deps.add(activeEffect),activeEffect.deps.add(deps)}function trigger(target,key){let depsMap=targetMap.get(target);if(!depsMap)return;let deps=depsMap.get(key);if(!(!deps||deps.size===0))for(let effect of Array.from(deps))effect()}function ref(initialValue){let subscribers=new Set,wrapper={_value:initialValue};return{get value(){return track(wrapper,`_value`),wrapper._value},set value(newValue){let oldValue=wrapper._value;if(!Object.is(oldValue,newValue)){wrapper._value=newValue,trigger(wrapper,`_value`);for(let fn of subscribers)fn(newValue,oldValue)}},subscribe(fn){return subscribers.add(fn),()=>subscribers.delete(fn)},toJSON(){return wrapper._value}}}function computed(getter){let cached,dirty=!0,disposed=!1,wrapper={_value:void 0},effect=createEffect(()=>{dirty=!0,trigger(wrapper,`_value`)}),recompute=()=>{if(disposed){let prev=activeEffect;activeEffect=null;try{return getter()}finally{activeEffect=prev}}return dirty&&=(cached=runTracked(effect,getter),wrapper._value=cached,!1),cached};return{get value(){return track(wrapper,`_value`),recompute()},dispose(){disposed=!0,cleanupEffect(effect)},toJSON(){return recompute()}}}function watch(source,callback,options){let oldValue,stopped=!1,getter=typeof source==`function`?source:()=>source.value,job=createEffect(()=>{if(stopped)return;let newValue=runTracked(job,getter);Object.is(newValue,oldValue)||(callback(newValue,oldValue),oldValue=newValue)});return oldValue=runTracked(job,getter),options?.immediate&&callback(oldValue,void 0),()=>{stopped=!0,cleanupEffect(job)}}const proxyCache=new WeakMap;function reactive(target){let existing=proxyCache.get(target);if(existing)return existing;let proxy=new Proxy(target,{get(obj,key,receiver){let value=Reflect.get(obj,key,receiver);return typeof key==`symbol`?value:(track(obj,key),typeof value==`object`&&value&&!Array.isArray(value)?reactive(value):value)},set(obj,key,value,receiver){let oldValue=Reflect.get(obj,key,receiver),result=Reflect.set(obj,key,value,receiver);return!Object.is(oldValue,value)&&typeof key!=`symbol`&&trigger(obj,key),result}});return proxyCache.set(target,proxy),proxy}function isRef(value){return typeof value==`object`&&!!value&&`subscribe`in value&&`value`in value}function unref(value){return isRef(value)?value.value:value}function toRefs(obj){let result={};for(let key in obj)result[key]=ref(obj[key]);return result}export{computed,isRef,reactive,ref,toRefs,unref,watch};
12
12
  //# sourceMappingURL=reactivity.mjs.map