@forinda/kickjs 5.16.0 → 5.18.0-alpha.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 (123) hide show
  1. package/dist/adapter-D_uzU-QA.d.mts +2121 -0
  2. package/dist/adapter-D_uzU-QA.d.mts.map +1 -0
  3. package/dist/{context-decorator-DDZ30BIW.d.mts → app-module-CuAS6hJC.d.mts} +184 -4
  4. package/dist/app-module-CuAS6hJC.d.mts.map +1 -0
  5. package/dist/application-BU5N6o_M.mjs +12 -0
  6. package/dist/application-BU5N6o_M.mjs.map +1 -0
  7. package/dist/{application-CAnpDQ8d.d.mts → application-pbx53JnX.d.mts} +31 -5
  8. package/dist/application-pbx53JnX.d.mts.map +1 -0
  9. package/dist/{bootstrap-CFK2aAjA.mjs → bootstrap-CFog4v_L.mjs} +3 -3
  10. package/dist/{bootstrap-CFK2aAjA.mjs.map → bootstrap-CFog4v_L.mjs.map} +1 -1
  11. package/dist/{bootstrap-8DDzBjhx.d.mts → bootstrap-DzfBj6Ja.d.mts} +2 -2
  12. package/dist/{bootstrap-8DDzBjhx.d.mts.map → bootstrap-DzfBj6Ja.d.mts.map} +1 -1
  13. package/dist/config/index.mjs +2 -2
  14. package/dist/{config-BW5taOVX.mjs → config-Di3HlRTS.mjs} +3 -3
  15. package/dist/{config-BW5taOVX.mjs.map → config-Di3HlRTS.mjs.map} +1 -1
  16. package/dist/{container-D1gfs6__.mjs → container-BiGimG12.mjs} +3 -3
  17. package/dist/{container-D1gfs6__.mjs.map → container-BiGimG12.mjs.map} +1 -1
  18. package/dist/context-leZajQAj.mjs +12 -0
  19. package/dist/context-leZajQAj.mjs.map +1 -0
  20. package/dist/core/adapter.d.mts +1 -1
  21. package/dist/core/adapter.mjs +1 -1
  22. package/dist/core/app-module.d.mts +1 -1
  23. package/dist/core/app-module.mjs +1 -1
  24. package/dist/core/cache.mjs +1 -1
  25. package/dist/core/container.mjs +2 -2
  26. package/dist/core/cron.mjs +2 -2
  27. package/dist/core/decorators.mjs +2 -2
  28. package/dist/core/errors.d.mts +1 -1
  29. package/dist/core/errors.mjs +2 -2
  30. package/dist/core/errors.mjs.map +1 -1
  31. package/dist/core/interfaces.mjs +1 -1
  32. package/dist/core/logger.mjs +1 -1
  33. package/dist/core/path.mjs +1 -1
  34. package/dist/core/plugin.d.mts +1 -1
  35. package/dist/core/plugin.mjs +1 -1
  36. package/dist/core/reactivity.mjs +1 -1
  37. package/dist/{core-Db2uyqtM.mjs → core-Y_xSqZ7v.mjs} +3 -3
  38. package/dist/{core-Db2uyqtM.mjs.map → core-Y_xSqZ7v.mjs.map} +1 -1
  39. package/dist/{env-nwsfCpdy.mjs → env-BoMBrGWs.mjs} +3 -3
  40. package/dist/{env-nwsfCpdy.mjs.map → env-BoMBrGWs.mjs.map} +1 -1
  41. package/dist/{errors-CRCTjEUK.d.mts → errors-n0H96eEz.d.mts} +3 -1
  42. package/dist/errors-n0H96eEz.d.mts.map +1 -0
  43. package/dist/http/application.d.mts +1 -1
  44. package/dist/http/application.mjs +2 -2
  45. package/dist/http/bootstrap.d.mts +1 -1
  46. package/dist/http/bootstrap.mjs +2 -2
  47. package/dist/http/context.d.mts +2 -2
  48. package/dist/http/context.mjs +2 -2
  49. package/dist/http/middleware/cors.mjs +1 -1
  50. package/dist/http/middleware/csrf.mjs +1 -1
  51. package/dist/http/middleware/error-handler.mjs +2 -2
  52. package/dist/http/middleware/helmet.mjs +1 -1
  53. package/dist/http/middleware/rate-limit.mjs +1 -1
  54. package/dist/http/middleware/request-id.mjs +1 -1
  55. package/dist/http/middleware/request-logger.mjs +2 -2
  56. package/dist/http/middleware/session.mjs +1 -1
  57. package/dist/http/middleware/spa.d.mts +1 -1
  58. package/dist/http/middleware/spa.mjs +2 -2
  59. package/dist/http/middleware/upload.d.mts +2 -2
  60. package/dist/http/middleware/upload.mjs +3 -3
  61. package/dist/http/middleware/upload.mjs.map +1 -1
  62. package/dist/http/middleware/validate.mjs +2 -2
  63. package/dist/http/middleware/views.d.mts +1 -1
  64. package/dist/http/middleware/views.mjs +2 -2
  65. package/dist/http/query/index.d.mts +2 -2
  66. package/dist/http/query/index.mjs +2 -2
  67. package/dist/http/router-builder.d.mts +2 -2
  68. package/dist/http/router-builder.mjs +2 -2
  69. package/dist/http/router-builder.mjs.map +1 -1
  70. package/dist/http/runtimes/fastify.d.mts +66 -0
  71. package/dist/http/runtimes/fastify.d.mts.map +1 -0
  72. package/dist/http/runtimes/fastify.mjs +13 -0
  73. package/dist/http/runtimes/fastify.mjs.map +1 -0
  74. package/dist/http/runtimes/h3.d.mts +29 -0
  75. package/dist/http/runtimes/h3.d.mts.map +1 -0
  76. package/dist/http/runtimes/h3.mjs +12 -0
  77. package/dist/http/runtimes/h3.mjs.map +1 -0
  78. package/dist/{index-kEmG8nWU.d.mts → index-BtWJ9SLP.d.mts} +53 -7
  79. package/dist/{index-kEmG8nWU.d.mts.map → index-BtWJ9SLP.d.mts.map} +1 -1
  80. package/dist/index.d.mts +12 -17
  81. package/dist/index.mjs +2 -2
  82. package/dist/index.mjs.map +1 -1
  83. package/dist/{metadata-Cll3P0N0.mjs → metadata-Pvgc-K8a.mjs} +2 -2
  84. package/dist/{metadata-Cll3P0N0.mjs.map → metadata-Pvgc-K8a.mjs.map} +1 -1
  85. package/dist/query-B3PhfU6M.mjs +12 -0
  86. package/dist/query-B3PhfU6M.mjs.map +1 -0
  87. package/dist/request-scope-BaM8_e_8.mjs +12 -0
  88. package/dist/request-scope-BaM8_e_8.mjs.map +1 -0
  89. package/dist/router-builder-Bh3qSiDz.d.mts +39 -0
  90. package/dist/router-builder-Bh3qSiDz.d.mts.map +1 -0
  91. package/dist/{spa-IRFC2eMN.d.mts → spa-BdvZFXJz.d.mts} +3 -2
  92. package/dist/spa-BdvZFXJz.d.mts.map +1 -0
  93. package/dist/{upload-D6edAELH.d.mts → upload-Bc_rBRtE.d.mts} +37 -2
  94. package/dist/upload-Bc_rBRtE.d.mts.map +1 -0
  95. package/dist/{views-BCl-mETX.d.mts → views-DcTVDiOh.d.mts} +31 -7
  96. package/dist/views-DcTVDiOh.d.mts.map +1 -0
  97. package/package.json +29 -1
  98. package/dist/adapter-CkpcZwzZ.d.mts +0 -407
  99. package/dist/adapter-CkpcZwzZ.d.mts.map +0 -1
  100. package/dist/app-module-BTzb-Vd6.d.mts +0 -92
  101. package/dist/app-module-BTzb-Vd6.d.mts.map +0 -1
  102. package/dist/application-CAnpDQ8d.d.mts.map +0 -1
  103. package/dist/application-DnnqoDWx.mjs +0 -12
  104. package/dist/application-DnnqoDWx.mjs.map +0 -1
  105. package/dist/context-Dnt2Di7p.d.mts +0 -434
  106. package/dist/context-Dnt2Di7p.d.mts.map +0 -1
  107. package/dist/context-MD7Mc_o_.mjs +0 -12
  108. package/dist/context-MD7Mc_o_.mjs.map +0 -1
  109. package/dist/context-decorator-DDZ30BIW.d.mts.map +0 -1
  110. package/dist/errors-CRCTjEUK.d.mts.map +0 -1
  111. package/dist/execution-context-B4cfhivz.d.mts +0 -98
  112. package/dist/execution-context-B4cfhivz.d.mts.map +0 -1
  113. package/dist/index-frSEHiL_.d.mts +0 -796
  114. package/dist/index-frSEHiL_.d.mts.map +0 -1
  115. package/dist/plugin-BQCn6zyj.d.mts +0 -260
  116. package/dist/plugin-BQCn6zyj.d.mts.map +0 -1
  117. package/dist/query-D-FiLfmd.mjs +0 -12
  118. package/dist/query-D-FiLfmd.mjs.map +0 -1
  119. package/dist/router-builder-oQpcQMWl.d.mts +0 -31
  120. package/dist/router-builder-oQpcQMWl.d.mts.map +0 -1
  121. package/dist/spa-IRFC2eMN.d.mts.map +0 -1
  122. package/dist/upload-D6edAELH.d.mts.map +0 -1
  123. package/dist/views-BCl-mETX.d.mts.map +0 -1
@@ -1,407 +0,0 @@
1
-
2
- import { i as Constructor, o as MaybePromise } from "./interfaces-DbTyKGdS.mjs";
3
- import { t as Container } from "./container-TWOUyR_0.mjs";
4
- import { s as ContributorRegistrations } from "./context-decorator-DDZ30BIW.mjs";
5
- import http from "node:http";
6
- import { ErrorRequestHandler, Express, RequestHandler } from "express";
7
-
8
- //#region src/core/introspect.d.ts
9
- /**
10
- * Per-primitive introspection snapshot — the JSON shape an adapter or
11
- * plugin's optional `introspect()` hook returns for DevTools.
12
- *
13
- * Lives in `@forinda/kickjs` (not `@forinda/kickjs-devtools-kit`) so
14
- * adopters can type their `introspect()` return without taking on a
15
- * dep on the kit just to satisfy the contract. The kit's own
16
- * `IntrospectionSnapshot` type is structurally identical and continues
17
- * to be the canonical doc location for DevTools-side consumers; the
18
- * two stay in lockstep — if a field is added on one side it MUST be
19
- * added on the other (covered by the type-equivalence test in the
20
- * devtools-kit package).
21
- *
22
- * Cheap-by-default principle: `state` and `metrics` should be O(1) to
23
- * compute (counters, flags, recent samples). Anything that requires a
24
- * DB round trip or a full DI graph walk belongs behind a separate
25
- * explicit RPC — DevTools polls topology aggressively.
26
- */
27
- interface IntrospectionSnapshot {
28
- /**
29
- * Wire-protocol version this snapshot conforms to. The kit ships a
30
- * `PROTOCOL_VERSION` constant typed as the literal `1`; we mirror
31
- * that literal here so structural parity is enforced both ways —
32
- * an adopter who writes `protocolVersion: 1` satisfies both this
33
- * type and the kit's `IntrospectionSnapshot`. Bumping is a
34
- * coordinated kit + framework release; widen this literal in
35
- * lockstep.
36
- */
37
- protocolVersion: 1;
38
- /** Stable identity matching the `name` from `definePlugin` / `defineAdapter`. */
39
- name: string;
40
- /** Discriminator for what kind of primitive this is. */
41
- kind: IntrospectionKind;
42
- /** Optional version string from the plugin / adapter metadata. */
43
- version?: string;
44
- /**
45
- * Reactive state surface — JSON-serialisable snapshot. Keep small
46
- * (under a few KB) so polling stays cheap.
47
- */
48
- state?: Record<string, unknown>;
49
- /** DI tokens this primitive registers (`provides`) or consumes (`requires`). */
50
- tokens?: {
51
- provides: readonly string[];
52
- requires: readonly string[];
53
- };
54
- /**
55
- * Per-instance counters — active connections, in-flight jobs, cached
56
- * items, etc. Numbers only so the DevTools panel can chart trends.
57
- */
58
- metrics?: Record<string, number>;
59
- /** Self-reported memory footprint estimate, in bytes. Optional. */
60
- memoryBytes?: number;
61
- }
62
- /**
63
- * What kind of primitive an introspection snapshot describes. Matches
64
- * the kit's `IntrospectionKind` and the routing the topology aggregator
65
- * does when bucketing adapters vs plugins vs middleware vs contributor
66
- * primitives.
67
- */
68
- type IntrospectionKind = 'plugin' | 'adapter' | 'middleware' | 'contributor';
69
- /**
70
- * Convenience function signature for adopter-side helpers that return
71
- * a complete snapshot. Used by the optional `introspect?()` hook on
72
- * `AppAdapter` and `KickPlugin`.
73
- */
74
- type IntrospectFn = () => IntrospectionSnapshot | Promise<IntrospectionSnapshot>;
75
- //#endregion
76
- //#region src/core/augmentation.d.ts
77
- /**
78
- * Augmentation registry — runtime no-op + the canonical
79
- * `KickJsPluginRegistry` interface that `kick typegen` augments.
80
- *
81
- * See `architecture.md` §21.2.1 (typegen for `dependsOn`) and §21.3.3
82
- * (standardized augmentation registry) for the design rationale.
83
- *
84
- * @module @forinda/kickjs/core/augmentation
85
- */
86
- /**
87
- * Marker interface that lists every plugin/adapter in the project. The
88
- * keys are the literal `name` field passed to `defineAdapter` /
89
- * `definePlugin` (or declared on a `class implements AppAdapter`); the
90
- * values are the kind tag (`'plugin' | 'adapter'`).
91
- *
92
- * The framework ships this empty. `kick typegen` augments it from
93
- * source so `keyof KickJsPluginRegistry` resolves to a string-literal
94
- * union of every name in scope — and therefore so do `dependsOn`
95
- * declarations on plugins/adapters.
96
- */
97
- interface KickJsPluginRegistry {}
98
- /**
99
- * Resolves to a string-literal union of every registered plugin/adapter
100
- * name when the project's typegen has populated `KickJsPluginRegistry`,
101
- * or to `string` when the registry is empty (no typegen pass yet).
102
- *
103
- * Falling back to `string` keeps fresh projects compiling — the typegen
104
- * narrowing is a progressive enhancement, not a hard prerequisite.
105
- */
106
- type KickJsPluginName = keyof KickJsPluginRegistry extends never ? string : keyof KickJsPluginRegistry & string;
107
- /**
108
- * Metadata attached to a `defineAugmentation` call. The fields are
109
- * documentation-only — `kick typegen` surfaces them in the generated
110
- * `.kickjs/types/augmentations.d.ts` so adopters can browse every
111
- * augmentable interface from one place.
112
- *
113
- * `description` and `example` may both be multi-line — typegen preserves
114
- * line breaks when rendering them as JSDoc. There is no upper limit on
115
- * the example size; entire shape definitions or worked snippets are fine.
116
- */
117
- interface AugmentationMeta {
118
- /**
119
- * Free-form description of what the augmentation customises. May be
120
- * multi-line — newlines are preserved in the generated JSDoc.
121
- */
122
- description?: string;
123
- /**
124
- * TS snippet showing a typical shape. May be multi-line; rendered
125
- * inside a fenced code block in the generated catalogue. Use this
126
- * for anything from `'{ foo: string }'` up to a full interface body
127
- * — typegen no longer flattens newlines.
128
- */
129
- example?: string;
130
- }
131
- /**
132
- * Advertise an augmentable interface so `kick typegen` can list it in
133
- * `.kickjs/types/augmentations.d.ts` for project-wide discovery.
134
- *
135
- * **This function is a runtime AND type-level no-op.** It does NOT
136
- * augment the interface for TypeScript. The actual augmentation is
137
- * the `declare module` block — `defineAugmentation` only adds an
138
- * entry to the typegen catalogue so other contributors / adopters
139
- * can find the interface and know its expected shape.
140
- *
141
- * Both calls are needed:
142
- *
143
- * ```ts
144
- * import { defineAugmentation } from '@forinda/kickjs'
145
- *
146
- * // (1) The actual TS augmentation — what `ctx.get('tenant')` reads.
147
- * declare module '@forinda/kickjs' {
148
- * interface ContextMeta {
149
- * tenant: { id: string; name: string }
150
- * }
151
- * }
152
- *
153
- * // (2) Catalogue entry — what `kick typegen` lists in
154
- * // `.kickjs/types/augmentations.d.ts` for browseability.
155
- * defineAugmentation('ContextMeta', {
156
- * description: 'Tenant resolved from x-tenant-id by TenantAdapter',
157
- * example: `{
158
- * tenant: {
159
- * id: string
160
- * name: string
161
- * plan: 'free' | 'pro' | 'enterprise'
162
- * featureFlags: Record<string, boolean>
163
- * }
164
- * }`,
165
- * })
166
- * ```
167
- *
168
- * Skip `defineAugmentation` if you don't care about the catalogue — the
169
- * `declare module` block alone is enough for runtime + type behaviour.
170
- *
171
- * @deprecated Low value in practice: it does nothing at runtime or at
172
- * the type level — the `declare module` block alone gives you the
173
- * augmentation, and the generated `.kickjs/types/kick__augmentations.d.ts`
174
- * catalogue it feeds is documentation-only. Prefer a plain
175
- * `declare module '@forinda/kickjs' { ... }` block and (optionally) a
176
- * JSDoc comment on your own interface. `defineAugmentation` and the
177
- * `kick/augmentations` typegen plugin will be removed in a future major.
178
- */
179
- declare function defineAugmentation(name: string, meta?: AugmentationMeta): void;
180
- //#endregion
181
- //#region src/core/adapter.d.ts
182
- /**
183
- * Where in the middleware pipeline an adapter's middleware should be inserted.
184
- *
185
- * - `beforeGlobal` — runs **before** any user-defined global middleware.
186
- * Suitable for transport-level concerns (request tagging, low-level
187
- * request rewriting). Fires on every request.
188
- * - `afterGlobal` — runs **after** global middleware, **before** module
189
- * routes are matched. Default for most adapter middleware (auth pre-checks,
190
- * tenant resolution, body shaping). Fires on every request.
191
- * - `beforeRoutes` — runs just before module routes are mounted. Functionally
192
- * equivalent to `afterGlobal` for most cases; the explicit phase exists so
193
- * adapters that *must* sit at the very end of the pre-route stack can
194
- * declare intent. Fires on every request.
195
- * - `afterRoutes` — sits **after** module routes but **before** error
196
- * handlers. **Fires only on fall-through** — when no route matched (404
197
- * path) or a route handler called `next()` without ending the response.
198
- * Controllers that respond with `ctx.json(...)` end the chain and skip
199
- * this phase, so it is *not* a "post-response" hook. For per-response
200
- * work (logging, metrics) use `res.on('finish', ...)` from inside an
201
- * earlier-phase middleware instead.
202
- */
203
- type MiddlewarePhase = 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes';
204
- /**
205
- * Path scope for an {@link AdapterMiddleware} entry. Mirrors what
206
- * Express's `app.use(path, handler)` accepts so adapters get the full
207
- * range without learning a new mini-language:
208
- *
209
- * - A `string` prefix — `'/api'` matches `/api`, `/api/v1`, `/api/v1/x`.
210
- * - A `RegExp` — `/^\/api\/v\d+/` matches `/api/v1`, `/api/v42`.
211
- * - An array of either — `['/api', '/admin']` matches either prefix;
212
- * `['/api', /^\/internal\//]` mixes shapes.
213
- *
214
- * Omit to apply the middleware unconditionally (Express `app.use(handler)`).
215
- */
216
- type MiddlewarePath = string | RegExp | ReadonlyArray<string | RegExp>;
217
- /** A middleware entry contributed by an adapter */
218
- interface AdapterMiddleware {
219
- /**
220
- * Express handler: either a `RequestHandler` (`(req, res, next) => …`)
221
- * or an `ErrorRequestHandler` (`(err, req, res, next) => …`).
222
- * Express dispatches based on arity, so the same field accepts both
223
- * without a discriminant.
224
- */
225
- handler: RequestHandler | ErrorRequestHandler;
226
- /** Which phase to insert into (default: 'afterGlobal') */
227
- phase?: MiddlewarePhase;
228
- /**
229
- * Optional path scope. See {@link MiddlewarePath} for the accepted
230
- * shapes (string prefix, RegExp, or an array mixing both). Omit to
231
- * apply the middleware to every request that reaches this phase.
232
- */
233
- path?: MiddlewarePath;
234
- }
235
- /**
236
- * Context passed to adapter lifecycle hooks.
237
- * Populated by the Application — provides access to the Express app,
238
- * DI container, http.Server, and environment info without requiring
239
- * external type imports. Cast `app` or `server` if you need specific types.
240
- *
241
- * @example
242
- * ```ts
243
- * beforeMount({ app, container }: AdapterContext) {
244
- * app.use(myMiddleware())
245
- * }
246
- * ```
247
- */
248
- interface AdapterContext {
249
- /** Express application instance — fully typed */
250
- app: Express;
251
- /** DI container */
252
- container: Container;
253
- /** Node.js http.Server — only available in afterStart */
254
- server?: http.Server;
255
- /** Current NODE_ENV value (default: 'development') */
256
- env: string;
257
- /** true when NODE_ENV === 'production' */
258
- isProduction: boolean;
259
- }
260
- /**
261
- * Adapters plug into the Application lifecycle.
262
- * Implement this to add WebSocket, database, rate limiting, docs, etc.
263
- *
264
- * All hooks receive an `AdapterContext` populated by the framework.
265
- * No external type imports needed — the context is fully typed from KickJS internals.
266
- *
267
- * @example
268
- * ```ts
269
- * import type { AppAdapter, AdapterContext } from '@forinda/kickjs/adapter'
270
- *
271
- * class SentryAdapter implements AppAdapter {
272
- * name = 'SentryAdapter'
273
- *
274
- * beforeMount({ app, container }: AdapterContext) {
275
- * app.use(Sentry.expressRequestHandler())
276
- * }
277
- *
278
- * afterStart({ server }: AdapterContext) {
279
- * // server is the http.Server — available after listen()
280
- * }
281
- *
282
- * async shutdown() {
283
- * await Sentry.close(2000)
284
- * }
285
- * }
286
- * ```
287
- */
288
- interface AppAdapter {
289
- /**
290
- * Human-readable name for logging and `dependsOn` resolution.
291
- * Required when the adapter (or any other in the same set) declares
292
- * `dependsOn` — sort errors will reference adapters by name.
293
- */
294
- name?: string;
295
- /**
296
- * Other adapter names that must mount before this one. The framework
297
- * topologically sorts adapters at boot — cycles or unknown names throw
298
- * via `MountCycleError` / `MissingMountDepError` so bad configurations
299
- * fail boot rather than corrupt live traffic.
300
- *
301
- * Common pattern: `TenantAdapter` populates `req.tenant` and
302
- * `AuthAdapter` reads it for tenant-scoped RBAC, so AuthAdapter
303
- * declares `dependsOn = ['TenantAdapter']`. Adapters without
304
- * `dependsOn` retain their declaration order.
305
- *
306
- * @example
307
- * ```ts
308
- * class AuthAdapter implements AppAdapter {
309
- * name = 'AuthAdapter'
310
- * dependsOn = ['TenantAdapter']
311
- * }
312
- * ```
313
- */
314
- dependsOn?: readonly KickJsPluginName[];
315
- /**
316
- * Return middleware entries to be inserted into the pipeline.
317
- * The `phase` controls ordering relative to global middleware and routes.
318
- */
319
- middleware?(): AdapterMiddleware[];
320
- /**
321
- * Return Context Contributors (#107) that apply to every route in the
322
- * application. Adapter-level contributors merge into the per-route
323
- * pipeline at the `'adapter'` precedence level — they win over global
324
- * contributors but lose to module, class, and method decorators.
325
- *
326
- * Useful for cross-cutting concerns: an auth adapter contributing
327
- * `user`, a multi-tenant adapter contributing `tenant`, etc. Optional.
328
- */
329
- contributors?(): ContributorRegistrations;
330
- /**
331
- * Called before global middleware — register early routes (docs UI, health).
332
- * May return a Promise — async rejections are caught and logged.
333
- */
334
- beforeMount?(ctx: AdapterContext): void | Promise<void>;
335
- /**
336
- * Called for each module route that gets mounted.
337
- * Use this to collect route metadata (e.g. for OpenAPI spec generation).
338
- */
339
- onRouteMount?(controllerClass: Constructor, mountPath: string): void;
340
- /**
341
- * Called after modules are registered and the DI container is bootstrapped,
342
- * before the HTTP server starts listening. **This is the correct hook for
343
- * adapters that register DI bindings, resolve services, or otherwise prepare
344
- * container-side state** (e.g. `container.registerInstance(MAILER, ...)`).
345
- *
346
- * `beforeStart` runs inside `app.setup()`, which means it also fires under
347
- * `createTestApp` — adapters using this hook work in unit tests without
348
- * needing a live HTTP server. See {@link afterStart} for the contrast.
349
- *
350
- * May return a Promise — async rejections are caught and logged.
351
- */
352
- beforeStart?(ctx: AdapterContext): void | Promise<void>;
353
- /**
354
- * Called after the HTTP server is listening — `ctx.server` is the live
355
- * `http.Server`. **Use this hook only for work that genuinely needs a
356
- * listening server**: attaching Socket.IO / WS upgrades, reading the bound
357
- * port via `server.address()`, dispatching to the local server over HTTP,
358
- * etc.
359
- *
360
- * `afterStart` does NOT fire under `createTestApp`, since tests call
361
- * `app.setup()` and never `app.start()`. If your adapter only registers DI
362
- * bindings or resolves services, use {@link beforeStart} — that hook runs
363
- * in both production (`start()`) and tests (`setup()`).
364
- *
365
- * May return a Promise — async rejections are caught and logged.
366
- */
367
- afterStart?(ctx: AdapterContext): void | Promise<void>;
368
- /**
369
- * Called by the /health/ready endpoint. Return the health status of your adapter's
370
- * backing service (database, Redis, queue, etc.).
371
- */
372
- onHealthCheck?(): Promise<{
373
- name: string;
374
- status: 'up' | 'down';
375
- }>;
376
- /** Called on shutdown — clean up connections */
377
- shutdown?(): MaybePromise;
378
- /**
379
- * Optional DevTools introspection hook (architecture.md §23). Returns
380
- * a snapshot describing this adapter's current state, metrics, and DI
381
- * tokens. DevTools awaits the result, so async work is fine — but keep
382
- * the implementation cheap (counters + flags, no DB round trips) since
383
- * the topology endpoint polls on a short interval.
384
- *
385
- * The return type lives in `@forinda/kickjs` itself ({@link IntrospectionSnapshot})
386
- * so adopters can author `introspect()` without importing the kit just
387
- * to satisfy the contract. The kit's own `IntrospectionSnapshot` type
388
- * stays structurally identical; either import works.
389
- */
390
- introspect?(): IntrospectionSnapshot | Promise<IntrospectionSnapshot>;
391
- /**
392
- * Optional DevTools tabs this adapter contributes (architecture.md §23).
393
- * Returns an array of `DevtoolsTabDescriptor` shapes — typically one
394
- * entry per adapter-owned panel (iframe / launch / html view).
395
- *
396
- * Same untyped-at-this-layer convention as {@link introspect}: adapter
397
- * authors import `defineDevtoolsTab` (or the bare type) from
398
- * `@forinda/kickjs-devtools-kit` so `@forinda/kickjs` doesn't take on
399
- * a runtime dep on the kit.
400
- */
401
- devtoolsTabs?(): readonly unknown[];
402
- }
403
- /** Constructor type for AppAdapter classes */
404
- type AppAdapterClass = new () => AppAdapter;
405
- //#endregion
406
- export { MiddlewarePath as a, KickJsPluginName as c, IntrospectFn as d, IntrospectionKind as f, AppAdapterClass as i, KickJsPluginRegistry as l, AdapterMiddleware as n, MiddlewarePhase as o, IntrospectionSnapshot as p, AppAdapter as r, AugmentationMeta as s, AdapterContext as t, defineAugmentation as u };
407
- //# sourceMappingURL=adapter-CkpcZwzZ.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-CkpcZwzZ.d.mts","names":[],"sources":["../src/core/introspect.ts","../src/core/augmentation.ts","../src/core/adapter.ts"],"mappings":";;;;;;;;;;;;;;;AAkBA;;;;;;;;;;;UAAiB,qBAAA;EAgBf;;;;;;;;;EANA,eAAA;EAoBW;EAlBX,IAAA;EA2B2B;EAzB3B,IAAA,EAAM,iBAAA;EAyBqB;EAvB3B,OAAA;EA8BU;;;;EAzBV,KAAA,GAAQ,MAAA;EAyB+C;EAvBvD,MAAA;IAAW,QAAA;IAA6B,QAAA;EAAA;EAuBuB;;AAAqB;;EAlBpF,OAAA,GAAU,MAAA;;EAEV,WAAA;AAAA;;;AC3BmC;AAUrC;;;KD0BY,iBAAA;ACxBkB;AAY9B;;;;AAZ8B,KD+BlB,YAAA,SAAqB,qBAAA,GAAwB,OAAA,CAAQ,qBAAA;;;;;;;;;;AA9CjE;;;;;;;;;;;;;UCGiB,oBAAA;;;;;;;;AD2BJ;KCjBD,gBAAA,SAAyB,oBAAA,gCAE3B,oBAAoB;;;;ADwBD;AAO7B;;;;;;UCnBiB,gBAAA;EDmB+C;;;;ECd9D,WAAA;EDcoF;;;;AC3CtF;;EAoCE,OAAO;AAAA;AApC4B;AAUrC;;;;AAE8B;AAY9B;;;;AAYS;AAmDT;;;;;;;;AAAwE;;;;AC/ExE;;;;AAA2B;AAc3B;;;;;;;;;;;;AAA4E;AAG5E;;;;;;;ADzBqC,iBAuFrB,kBAAA,CAAmB,IAAA,UAAc,IAAA,GAAO,gBAAgB;;;AD1FxE;;;;;;;;;;;;;;;;;;;;;AAAA,KEWY,eAAA;AFmBC;AASb;;;;AAA6B;AAO7B;;;;;;AAhBa,KELD,cAAA,YAA0B,MAAA,GAAS,aAAA,UAAuB,MAAA;;UAGrD,iBAAA;EFkBwC;;;AAA6B;;;EEXpF,OAAA,EAAS,cAAA,GAAiB,mBAAA;EDhCX;ECkCf,KAAA,GAAQ,eAAA;;;ADlC2B;AAUrC;;EC8BE,IAAA,GAAO,cAAA;AAAA;AD5BqB;AAY9B;;;;AAYS;AAmDT;;;;;;;AA3E8B,UC4Cb,cAAA;ED+BuD;EC7BtE,GAAA,EAAK,OAAA;;EAEL,SAAA,EAAW,SAAA;EApDD;EAsDV,MAAA,GAAS,IAAA,CAAK,MAAA;;EAEd,GAAA;EAxDyB;EA0DzB,YAAA;AAAA;;;;;;;;;;;AA5C0E;AAG5E;;;;;;;;;;;;;;;;;UAwEiB,UAAA;EAzCA;;;;;EA+Cf,IAAA;EAzCoB;;;;;;;;;;;;AAIR;AA+Bd;;;;;;EA2BE,SAAA,YAAqB,gBAAA;EAuBqB;;;;EAjB1C,UAAA,KAAe,iBAAA;EAqD0B;;;;;;;;;EA1CzC,YAAA,KAAiB,wBAAA;EAjBI;;;;EAuBrB,WAAA,EAAa,GAAA,EAAK,cAAA,UAAwB,OAAA;EAA1C;;;;EAMA,YAAA,EAAc,eAAA,EAAiB,WAAA,EAAa,SAAA;EAAb;;;;;;;;;;;;EAc/B,WAAA,EAAa,GAAA,EAAK,cAAA,UAAwB,OAAA;EAsBd;;;;;;;;;;AA6BhB;AAId;;;EAvCE,UAAA,EAAY,GAAA,EAAK,cAAA,UAAwB,OAAA;EAuCO;;;;EAjChD,aAAA,KAAkB,OAAA;IAAU,IAAA;IAAc,MAAA;EAAA;;EAG1C,QAAA,KAAa,YAAA;;;;;;;;;;;;;EAcb,UAAA,KAAe,qBAAA,GAAwB,OAAA,CAAQ,qBAAA;;;;;;;;;;;EAY/C,YAAA;AAAA;;KAIU,eAAA,aAA4B,UAAU"}
@@ -1,92 +0,0 @@
1
-
2
- import { t as Container } from "./container-TWOUyR_0.mjs";
3
- import { s as ContributorRegistrations } from "./context-decorator-DDZ30BIW.mjs";
4
-
5
- //#region src/core/app-module.d.ts
6
- /**
7
- * Route set returned by a module's routes() method.
8
- * Combined with versioning: /{apiPrefix}/v{version}{path}
9
- *
10
- * **Either** `controller` or `router` is required. Pass `controller`
11
- * for the common case — the framework calls `buildRoutes(controller)`
12
- * internally to derive the Express Router. Pass `router` directly
13
- * only when you need to compose multiple controllers under one path
14
- * or hand-build the router yourself.
15
- */
16
- interface ModuleRoutes {
17
- /** URL prefix (e.g. '/users') */
18
- path: string;
19
- /**
20
- * Express Router instance. Optional — when omitted, the framework
21
- * builds one from `controller` via `buildRoutes(controller)`. Pass
22
- * an explicit router only when you need to compose multiple
23
- * controllers or hand-roll the router shape.
24
- */
25
- router?: any;
26
- /** Optional API version override (defaults to Application.defaultVersion) */
27
- version?: number;
28
- /**
29
- * Controller class. Required unless `router` is provided. Used both
30
- * for the auto-derived router (via `buildRoutes(controller)`) and
31
- * for OpenAPI spec generation via `SwaggerAdapter`.
32
- */
33
- controller?: any;
34
- }
35
- /**
36
- * Interface that every feature module must implement.
37
- * Modules register their DI bindings and declare their routes.
38
- *
39
- * `register` is **optional**. Modules that only contain decorated classes
40
- * (`@Service`, `@Controller`, `@Repository`, `@Component`) don't need it
41
- * because the decorators handle DI registration automatically. Implement
42
- * `register(container)` only when you need to bind a token (typically a
43
- * repository interface symbol) to a concrete implementation via
44
- * `container.registerFactory()` / `container.registerInstance()`.
45
- */
46
- interface AppModule {
47
- /** Optional — bind interfaces to implementations in the container */
48
- register?(container: Container): void;
49
- /**
50
- * Return Context Contributors (#107) that apply to every route this
51
- * module mounts. Module-level contributors are merged into the per-route
52
- * pipeline at the `'module'` precedence level — they win over adapter
53
- * and global contributors but lose to method and class decorators.
54
- *
55
- * Optional — modules without per-module contributors omit this hook.
56
- */
57
- contributors?(): ContributorRegistrations;
58
- /** Return route definitions for this module, or null for non-HTTP modules */
59
- routes(): ModuleRoutes | ModuleRoutes[] | null;
60
- }
61
- /**
62
- * Constructor type for the legacy `class FooModule implements AppModule`
63
- * pattern. The framework still accepts these — bootstrap discriminates
64
- * class vs instance at boot — but new code should prefer
65
- * {@link defineModule} for parity with {@link defineAdapter} and
66
- * {@link definePlugin}, plus typed config + `.scoped()` / `.definition`.
67
- *
68
- * @deprecated Use `defineModule({ ... })` and `AppModuleEntry` for the
69
- * `bootstrap({ modules })` array. The class form continues to work
70
- * through v5 and is not slated for removal in any specific release —
71
- * this annotation is a soft "prefer the factory form" hint, not an
72
- * imminent deprecation.
73
- */
74
- type AppModuleClass = new () => AppModule;
75
- /**
76
- * Either form accepted by `bootstrap({ modules })` and
77
- * `KickPlugin.modules?()`:
78
- *
79
- * - **Class** — the legacy form. Bootstrap calls `new ModuleClass()`.
80
- * - **Instance** — produced by {@link defineModule}'s factory call
81
- * (e.g. `TasksModule({ scope: 'admin' })`). Bootstrap uses it
82
- * directly without `new`.
83
- *
84
- * Both shapes route through the same lifecycle (`register`,
85
- * `contributors`, `routes`) — the only difference is who owns
86
- * instantiation. defineModule callers use this union so adopters can
87
- * mix-and-match in the same `modules` array.
88
- */
89
- type AppModuleEntry = AppModuleClass | AppModule;
90
- //#endregion
91
- export { ModuleRoutes as i, AppModuleClass as n, AppModuleEntry as r, AppModule as t };
92
- //# sourceMappingURL=app-module-BTzb-Vd6.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"app-module-BTzb-Vd6.d.mts","names":[],"sources":["../src/core/app-module.ts"],"mappings":";;;;;;;AAaA;;;;;;;;UAAiB,YAAA;EAiBL;EAfV,IAAA;EA6BwB;;;;;;EAtBxB,MAAA;EAmCqC;EAjCrC,OAAA;EAsBqB;;;;;EAhBrB,UAAA;AAAA;;AA2BqC;AAgBvC;;;;AAAgD;AAgBhD;;;;UA7CiB,SAAA;;EAEf,QAAA,EAAU,SAAA,EAAW,SAAA;;;;;;;;;EASrB,YAAA,KAAiB,wBAAA;;EAEjB,MAAA,IAAU,YAAA,GAAe,YAAA;AAAA;;;;;;;;;;;;;;KAgBf,cAAA,aAA2B,SAAS;;;;;;;;;;;;;;;KAgBpC,cAAA,GAAiB,cAAA,GAAiB,SAAS"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"application-CAnpDQ8d.d.mts","names":[],"sources":["../src/http/application.ts"],"mappings":";;;;;;;;;;;;;;KAqCY,eAAA,GAAkB,cAAA;EAAmB,IAAA;EAAc,OAAA,EAAS,cAAc;AAAA;AAAA,UAErE,kBAAA;;;;;;;;AAFqE;AAEtF;;EAWE,OAAA,EAAS,cAAA;EAAA;;;;;;;;;;;;;;;;;EAmBT,KAAA,EAAO,QAAA,EAAU,cAAA;EAQjB;EANA,QAAA,GAAW,UAAA;EA6BG;EA3Bd,IAAA;EAiCa;EA/Bb,SAAA;EAkCU;EAhCV,cAAA;EAsDe;;;;;;;;;;;;;;;;;;;;;EA/Bf,WAAA,GAAc,eAAA;EAiLZ;;AAAM;AAQV;;EAnLE,UAAA,GAAa,eAAA;EAqNyB;EAlNtC,OAAA,GAAU,UAAA;EAmRE;;;;;;;;;;;;;;;;;;;;EA7PZ,YAAA,GAAe,wBAAA;EAoLP;;;;;;;;;;;;;;;;;;EAhKR,YAAA;EAyO4D;;;;;;;;;;;;;;;;;;;;;;;;;EA9M5D,YAAA;EAy0BiB;EAt0BjB,UAAA,iCAA2C,EAAA,UAAY,QAAA;EAy1B/C;EAv1BR,SAAA;EAq3BQ;;;;;AA+CoB;;EA55B5B,aAAA;;;;;EAKA,cAAA;;;;;EAKA,eAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA;IAAsB,OAAA;EAAA;;;;;;;;;;;;;;;;EAiBtB,UAAA,IAAc,GAAA,OAAU,GAAA,OAAU,IAAA;;;;;;;;;;;;;;;;;EAkBlC,OAAA,IAAW,GAAA,OAAU,GAAA,OAAU,GAAA,OAAU,IAAA;;;;;;;;;;;;;;;;;;EAmBzC,QAAA;6DAEE,MAAA;EAAA;AAAA;;;;;cAQS,WAAA;EAAA,iBAkCkB,OAAA;EAAA,QAjCrB,GAAA;EAAA,QACA,SAAA;EAAA,QACA,UAAA;EAAA,iBACS,QAAA;EAAA,iBAEA,OAAA;;;;;;;;;;;;;UAcT,mBAAA;;UAMA,iBAAA;;UAEA,SAAA;;UAEA,kBAAA;;UAEA,eAAA;EAER,WAAA,CAA6B,OAAA,EAAS,kBAAA;;MAqClC,UAAA;;MAKA,gBAAA;;EAKJ,YAAA,IAAgB,SAAA;;;;;;;;;;;;;;;EAkBhB,MAAA,CAAO,GAAA,EAAK,IAAA,CAAK,eAAA,EAAiB,GAAA,EAAK,IAAA,CAAK,cAAA,EAAgB,IAAA,IAAQ,GAAA;;;;;;;;;;;;;;;UAsB5D,UAAA;;UAYM,QAAA;EAeR,KAAA,IAAS,OAAA;;EA0TT,YAAA,IAAgB,OAAA;;;;;;;;;;EAahB,KAAA,IAAS,OAAA;;EA0ET,OAAA,IAAW,OAAA;;;;;;;;;;;EAoCX,QAAA,IAAY,OAAA;EAgElB,aAAA,IAAiB,OAAA;;EAKjB,WAAA,IAAe,UAAA;;;;;;EASf,UAAA,aAAuB,UAAA;;;;;;;;;;;;;;;;;;;EAsBvB,eAAA,IAAmB,aAAA;IACjB,GAAA;IACA,MAAA;IACA,KAAA;IACA,SAAA;EAAA;EA6CF,aAAA,IAAiB,IAAA,CAAK,MAAA;;;;;;;;;;;;;;UAmBd,2BAAA;EAAA,QAWA,wBAAA;EAAA,QAmBA,mBAAA;EAAA,QAgBA,oBAAA;;UASA,yBAAA;;UAsBA,oBAAA;AAAA"}
@@ -1,12 +0,0 @@
1
- /**
2
- * @forinda/kickjs v5.16.0
3
- *
4
- * Copyright (c) Felix Orinda
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- import{n as tokenName,r as moduleRouteMissingControllerError,t as Container}from"./container-D1gfs6__.mjs";import{METADATA}from"./core/interfaces.mjs";import{Logger,createLogger}from"./core/logger.mjs";import{t as getClassMeta}from"./metadata-Cll3P0N0.mjs";import{g as MutableModuleRegistry,s as mountSort}from"./core-Db2uyqtM.mjs";import{normalizePath}from"./core/path.mjs";import{requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore}from"./context-MD7Mc_o_.mjs";import{_setExternalContributorSources,buildRoutes}from"./http/router-builder.mjs";import http from"node:http";import express from"express";import crypto from"node:crypto";const REQUEST_SCOPE_MIDDLEWARE_MARKER=Symbol.for(`@kickjs/requestScopeMiddleware`);function requestScopeMiddleware(){let mw=(req,_res,next)=>{let requestIdHeader=req.headers[`x-request-id`],requestId=(Array.isArray(requestIdHeader)?requestIdHeader[0]:requestIdHeader)||crypto.randomUUID(),store={requestId,instances:new Map,values:new Map};req.requestId=requestId,requestStore.run(store,()=>next())};return mw[REQUEST_SCOPE_MIDDLEWARE_MARKER]=!0,mw}function isRequestScopeMiddleware(value){return typeof value==`function`?value[REQUEST_SCOPE_MIDDLEWARE_MARKER]===!0:!1}const log=createLogger(`Application`);var Application=class{options;app;container;httpServer=null;adapters;plugins;_moduleContributors=[];_inFlightRequests=0;_draining=!1;_shutdownInitiated=!1;_drainResolvers=[];constructor(options){this.options=options,this.app=express(),this.container=Container.getInstance(),this.plugins=mountSort(options.plugins??[],`plugin`);let allAdapters=[...this.plugins.flatMap(p=>p.adapters?.()??[]),...options.adapters??[]],anonAdapterCount=0,namedAdapters=allAdapters.map(adapter=>{if(!adapter.name){let ctorName=adapter.constructor?.name,fallback=ctorName&&ctorName!==`Object`?ctorName:`AnonymousAdapter#${anonAdapterCount++}`;return Object.assign(adapter,{name:fallback})}return adapter});this.adapters=mountSort(namedAdapters,`adapter`),Container._requestStoreProvider=()=>requestStore.getStore()??null}get isDraining(){return this._draining}get inFlightRequests(){return this._inFlightRequests}getContainer(){return this.container}handle(req,res,next){next?this.app(req,res,next):this.app(req,res)}adapterCtx(server){let env=process.env.NODE_ENV??`development`;return{app:this.app,container:this.container,server,env,isProduction:env===`production`}}async callHook(hook,ctx){if(hook)try{let result=hook(ctx);result&&typeof result.then==`function`&&await result}catch(err){log.error(err,`Adapter hook failed`)}}async setup(){log.debug(`Bootstrapping application...`),this._moduleContributors=[];let adapterMw=this.collectAdapterMiddleware();this.app.__kickApp=this;let ctx=this.adapterCtx();for(let adapter of this.adapters)await this.callHook(adapter.beforeMount?.bind(adapter),ctx);this.app.disable(`x-powered-by`),this.app.set(`trust proxy`,this.options.trustProxy??!1),this.app.use(this.requestTrackingMiddleware()),this.mountHealthEndpoints(),this.shouldAutoMountRequestScope()&&this.app.use(requestScopeMiddleware()),this.mountMiddlewareList(adapterMw.beforeGlobal);for(let plugin of this.plugins)plugin.register?.(this.container);for(let plugin of this.plugins)try{let mw=plugin.middleware?.()??[];for(let handler of mw)this.app.use(handler)}catch(err){log.error(err,`Plugin middleware failed: ${plugin.name??`unknown`}`)}if(this.options.security?.helmet!==!1)try{let{helmet:helmetFn}=await import(`./http/middleware/helmet.mjs`);this.app.use(helmetFn())}catch{}let userMiddlewares=this.options.middlewares??this.options.middleware;if(userMiddlewares)for(let entry of userMiddlewares)this.mountMiddlewareEntry(entry);else this.app.use(requestId()),this.app.use(express.json({limit:this.options.jsonLimit??`1mb`}));this.mountMiddlewareList(adapterMw.afterGlobal);let moduleRegistry=new MutableModuleRegistry;for(let plugin of this.plugins){for(let m of plugin.modules?.()??[])moduleRegistry.mount(m);plugin.setup?.(moduleRegistry)}for(let m of this.options.modules)moduleRegistry.mount(m);this.options.setup?.(moduleRegistry);let modules=moduleRegistry.entries.map(entry=>{let mod=typeof entry==`function`?new entry:entry;return mod.register?.(this.container),mod});this.container.bootstrap(),this.container.has(Logger)||this.container.registerInstance(Logger,new Logger),this.mountMiddlewareList(adapterMw.beforeRoutes);let apiPrefix=this.options.apiPrefix??`/api`,defaultVersion=this.options.defaultVersion??1,shouldLogRoutes=this.options.logRouteTable??this.options.logRoutesTable??!1,mountedRoutes=[],adapterSources=[];for(let adapter of this.adapters){let adapterContribs=adapter.contributors?.()??[],adapterLabel=adapter.name??adapter.constructor.name??`adapter`;for(let registration of adapterContribs)adapterSources.push({source:`adapter`,registration,label:adapterLabel})}for(let plugin of this.plugins){let pluginContribs=plugin.contributors?.()??[],pluginLabel=plugin.name??plugin.constructor?.name??`plugin`;for(let registration of pluginContribs)adapterSources.push({source:`adapter`,registration,label:pluginLabel})}let globalSources=(this.options.contributors??[]).map(registration=>({source:`global`,registration,label:`bootstrap`}));this._moduleContributors=[];for(let mod of modules){let declaredName=mod.name,ctorName=mod.constructor?.name,moduleLabel=typeof declaredName==`string`&&declaredName.length>0?declaredName:ctorName&&ctorName!==`Object`?ctorName:`module`,moduleSources=(mod.contributors?.()??[]).map(registration=>({source:`module`,registration,label:moduleLabel}));for(let src of moduleSources)this._moduleContributors.push({registration:src.registration,label:moduleLabel});_setExternalContributorSources([...moduleSources,...adapterSources,...globalSources]);try{let result=mod.routes();if(!result)continue;let routeSets=Array.isArray(result)?result:[result];for(let route of routeSets){let mountPath=`${apiPrefix}/v${route.version??defaultVersion}${normalizePath(route.path)}`,router=route.router??buildRoutes(route.controller);if(!router)throw moduleRouteMissingControllerError(mountPath);if(this.app.use(mountPath,router),route.controller){for(let adapter of this.adapters)try{adapter.onRouteMount?.(route.controller,mountPath)}catch(err){log.error(err,`adapter.onRouteMount() failed for ${mountPath}`)}shouldLogRoutes&&mountedRoutes.push({controller:route.controller,mountPath})}}}finally{_setExternalContributorSources([])}}if(shouldLogRoutes&&mountedRoutes.length>0){let methodOrder=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],methodRank=m=>{let i=methodOrder.indexOf(m);return i===-1?99:i},totalRoutes=0;log.info(`Routes:`);for(let{controller,mountPath}of mountedRoutes){let defs=getClassMeta(METADATA.ROUTES,controller,[]);if(defs.length===0)continue;let counts={};for(let def of defs){let m=def.method.toUpperCase();counts[m]=(counts[m]||0)+1}totalRoutes+=defs.length;let methods=Object.entries(counts).toSorted(([a],[b])=>methodRank(a)-methodRank(b)).map(([m,n])=>`${n} ${m}`).join(`, `),name=controller.name||`Controller`;log.info(` ${name.padEnd(30)} ${mountPath.padEnd(25)} ${defs.length} routes (${methods})`)}log.info(` Total: ${totalRoutes} routes`)}this.mountMiddlewareList(adapterMw.afterRoutes);for(let adapter of this.adapters)await this.callHook(adapter.beforeStart?.bind(adapter),ctx);this.app.use(this.options.onNotFound??notFoundHandler()),this.app.use(this.options.onError??errorHandler())}async registerOnly(){await this.setup()}async start(){await this.setup();let g=globalThis;if(g.__kickjs_httpServer){this.httpServer=g.__kickjs_httpServer,log.debug(`Attached to Vite dev server`),g.__kickjs_app_shutdown=()=>this.shutdown();for(let adapter of this.adapters){let ctx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),ctx)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);return}let port=this.options.port??parseInt(process.env.PORT||`3000`,10);this.httpServer=http.createServer(this.app),this.httpServer.on(`error`,err=>{throw err.code===`EADDRINUSE`&&(log.error(`Port ${port} is already in use. Kill the existing process or use a different port:\n PORT=${port+1} kick dev\n lsof -i :${port} # find what's using it\n kill <PID> # stop it`),process.exit(1)),err}),await new Promise((resolve,reject)=>{this.httpServer.listen(port,async()=>{try{log.info(`Server running on http://localhost:${port}`);for(let adapter of this.adapters){let afterCtx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),afterCtx)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);resolve()}catch(err){reject(err)}})})}async rebuild(){let prevApp=this.app,prevContainer=this.container;try{Container.reset(),this.container=Container.getInstance(),this.app=express(),await this.setup()}catch(err){log.error(err,`HMR rebuild failed, keeping previous app`),this.app=prevApp,this.container=prevContainer;return}this.httpServer&&(this.httpServer.removeAllListeners(`request`),this.httpServer.on(`request`,this.app),log.debug(`HMR: Express app rebuilt and swapped`))}async shutdown(){if(this._shutdownInitiated){log.debug(`Shutdown already in progress, skipping duplicate call`);return}this._shutdownInitiated=!0,this._draining=!0,log.debug(`Shutting down — draining in-flight requests...`);let timeoutMs=this.options.shutdownTimeout??3e4,timer,forceExitPromise=timeoutMs>0?new Promise(resolve=>{timer=setTimeout(()=>resolve(`timeout`),timeoutMs),timer.unref()}):new Promise(()=>{});try{if(this.httpServer&&this.httpServer.close(()=>{}),this._inFlightRequests>0){log.debug(`Waiting for ${this._inFlightRequests} in-flight request(s) to complete...`);let drainPromise=new Promise(resolve=>{this._drainResolvers.push(()=>resolve(`drained`))});await Promise.race([drainPromise,forceExitPromise])===`timeout`?log.warn(`Shutdown timeout (${timeoutMs}ms) reached with ${this._inFlightRequests} request(s) still in-flight, forcing shutdown`):log.debug(`All in-flight requests completed`)}let results=await Promise.allSettled([...this.plugins.map(plugin=>Promise.resolve(plugin.shutdown?.())),...this.adapters.map(adapter=>Promise.resolve(adapter.shutdown?.()))]);for(let result of results)result.status===`rejected`&&log.error({err:result.reason},`Adapter shutdown failed`)}finally{timer&&clearTimeout(timer)}}getExpressApp(){return this.app}getAdapters(){return this.adapters}getPlugins(){return this.plugins}getContributors(){let out=[],ingest=(list,source,label)=>{if(list)for(let reg of list){let r=reg;if(r.key===void 0||r.key===null)continue;let key=typeof r.key==`string`?r.key:tokenName(r.key);out.push({key,source,label,dependsOn:Array.isArray(r.dependsOn)?r.dependsOn:[]})}};for(let adapter of this.adapters)ingest(adapter.contributors?.(),`adapter`,adapter.name??`adapter`);for(let plugin of this.plugins)ingest(plugin.contributors?.(),`plugin`,plugin.name??`plugin`);for(let{registration,label}of this._moduleContributors)ingest([registration],`module`,label);return ingest(this.options.contributors,`global`,`bootstrap`),out}getHttpServer(){return this.httpServer}shouldAutoMountRequestScope(){if(this.options.contextStore===`manual`)return!1;let userEntries=this.options.middlewares??this.options.middleware??[];for(let entry of userEntries)if(isRequestScopeMiddleware(typeof entry==`function`?entry:entry.handler))return!1;return!0}collectAdapterMiddleware(){let result={beforeGlobal:[],afterGlobal:[],beforeRoutes:[],afterRoutes:[]};for(let adapter of this.adapters){let entries=adapter.middleware?.()??[];for(let entry of entries)result[entry.phase??`afterGlobal`].push(entry)}return result}mountMiddlewareList(entries){for(let entry of entries){if(entry.path===void 0){this.app.use(entry.handler);continue}let path=Array.isArray(entry.path)?[...entry.path]:entry.path;this.app.use(path,entry.handler)}}mountMiddlewareEntry(entry){typeof entry==`function`?this.app.use(entry):this.app.use(entry.path,entry.handler)}requestTrackingMiddleware(){return(_req,res,next)=>{this._inFlightRequests++;let onFinish=()=>{if(res.removeListener(`finish`,onFinish),res.removeListener(`close`,onFinish),this._inFlightRequests--,this._draining&&this._inFlightRequests===0){for(let resolve of this._drainResolvers)resolve();this._drainResolvers=[]}};res.on(`finish`,onFinish),res.on(`close`,onFinish),next()}}mountHealthEndpoints(){this.app.get(`/health/live`,(_req,res)=>{this._draining?res.status(503).json({status:`draining`,uptime:process.uptime()}):res.json({status:`ok`,uptime:process.uptime()})}),this.app.get(`/health/ready`,async(_req,res)=>{if(this._draining){res.status(503).json({status:`draining`,checks:[]});return}let adaptersWithHealth=this.adapters.filter(a=>a.onHealthCheck),results=(await Promise.allSettled(adaptersWithHealth.map(a=>a.onHealthCheck()))).map((c,i)=>c.status===`fulfilled`?c.value:{name:adaptersWithHealth[i].name??`unknown`,status:`down`}),healthy=results.every(r=>r.status===`up`);res.status(healthy?200:503).json({status:healthy?`ready`:`degraded`,checks:results})})}};export{requestScopeMiddleware as n,Application as t};
12
- //# sourceMappingURL=application-DnnqoDWx.mjs.map