@forinda/kickjs 8.2.0 → 8.3.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 (137) hide show
  1. package/dist/{adapter-BxXGuLnW.d.mts → adapter-ByxWutTw.d.mts} +3 -3
  2. package/dist/{adapter-BxXGuLnW.d.mts.map → adapter-ByxWutTw.d.mts.map} +1 -1
  3. package/dist/{app-module-DoM8RNZ9.d.mts → app-module--bC2V1yw.d.mts} +18 -2
  4. package/dist/{app-module-DoM8RNZ9.d.mts.map → app-module--bC2V1yw.d.mts.map} +1 -1
  5. package/dist/{spa-BBwrECcF.d.mts → application-NBph8Ahc.d.mts} +598 -69
  6. package/dist/application-NBph8Ahc.d.mts.map +1 -0
  7. package/dist/application-OZSYZy2Z.mjs +12 -0
  8. package/dist/application-OZSYZy2Z.mjs.map +1 -0
  9. package/dist/{bootstrap-YxZ7Xitb.mjs → bootstrap-BQrXS6L1.mjs} +3 -3
  10. package/dist/{bootstrap-YxZ7Xitb.mjs.map → bootstrap-BQrXS6L1.mjs.map} +1 -1
  11. package/dist/{client-ip-B6kKJLEJ.mjs → client-ip-D5ZBfP_C.mjs} +2 -2
  12. package/dist/{client-ip-B6kKJLEJ.mjs.map → client-ip-D5ZBfP_C.mjs.map} +1 -1
  13. package/dist/config/index.mjs +2 -2
  14. package/dist/{config-Bwgvk__k.mjs → config-DlAZU1Dm.mjs} +3 -3
  15. package/dist/{config-Bwgvk__k.mjs.map → config-DlAZU1Dm.mjs.map} +1 -1
  16. package/dist/{container-BnkQIDQF.mjs → container-DzwyumEo.mjs} +3 -3
  17. package/dist/{container-BnkQIDQF.mjs.map → container-DzwyumEo.mjs.map} +1 -1
  18. package/dist/{context-GJc8Pf2d.mjs → context-3SqXUyOr.mjs} +3 -3
  19. package/dist/{context-GJc8Pf2d.mjs.map → context-3SqXUyOr.mjs.map} +1 -1
  20. package/dist/{context-errors-Bm1GxsOQ.mjs → context-errors-CpTVAtA_.mjs} +2 -2
  21. package/dist/{context-errors-Bm1GxsOQ.mjs.map → context-errors-CpTVAtA_.mjs.map} +1 -1
  22. package/dist/{contributor-runner-CzL4-aIt.mjs → contributor-runner-CX6InEzw.mjs} +3 -3
  23. package/dist/{contributor-runner-CzL4-aIt.mjs.map → contributor-runner-CX6InEzw.mjs.map} +1 -1
  24. package/dist/core/adapter.d.mts +1 -1
  25. package/dist/core/adapter.mjs +1 -1
  26. package/dist/core/app-module.d.mts +1 -1
  27. package/dist/core/app-module.mjs +1 -1
  28. package/dist/core/cache.mjs +1 -1
  29. package/dist/core/container.mjs +2 -2
  30. package/dist/core/cron.mjs +2 -2
  31. package/dist/core/decorators.mjs +2 -2
  32. package/dist/core/errors.mjs +1 -1
  33. package/dist/core/interfaces.mjs +1 -1
  34. package/dist/core/logger.mjs +1 -1
  35. package/dist/core/path.mjs +1 -1
  36. package/dist/core/plugin.d.mts +1 -1
  37. package/dist/core/plugin.mjs +1 -1
  38. package/dist/core/reactivity.mjs +1 -1
  39. package/dist/{core-w7s4ntck.mjs → core-CsxP6Jec.mjs} +3 -3
  40. package/dist/{core-w7s4ntck.mjs.map → core-CsxP6Jec.mjs.map} +1 -1
  41. package/dist/{csrf-BjmtOJZs.mjs → csrf-BdKFFh14.mjs} +3 -3
  42. package/dist/{csrf-BjmtOJZs.mjs.map → csrf-BdKFFh14.mjs.map} +1 -1
  43. package/dist/{csrf-COqPWlMd.d.mts → csrf-DDwUOF45.d.mts} +2 -2
  44. package/dist/{csrf-COqPWlMd.d.mts.map → csrf-DDwUOF45.d.mts.map} +1 -1
  45. package/dist/{decorate-CpBbxrvT.mjs → decorate-BGm37ho8.mjs} +1 -1
  46. package/dist/{define-adapter-whaSQuNs.mjs → define-adapter-CpUxOgQ-.mjs} +2 -2
  47. package/dist/{define-adapter-whaSQuNs.mjs.map → define-adapter-CpUxOgQ-.mjs.map} +1 -1
  48. package/dist/{disposables-lWieeJLG.mjs → disposables-Pouc1-v2.mjs} +2 -2
  49. package/dist/{disposables-lWieeJLG.mjs.map → disposables-Pouc1-v2.mjs.map} +1 -1
  50. package/dist/{env-Bd6akCGH.mjs → env-BYseGI8D.mjs} +3 -3
  51. package/dist/{env-Bd6akCGH.mjs.map → env-BYseGI8D.mjs.map} +1 -1
  52. package/dist/{error-handler-DMUz6X18.d.mts → error-handler-BtTYsKLV.d.mts} +2 -2
  53. package/dist/{error-handler-DMUz6X18.d.mts.map → error-handler-BtTYsKLV.d.mts.map} +1 -1
  54. package/dist/{handler-gK6lNZKW.mjs → handler-s-wYH8Kk.mjs} +3 -3
  55. package/dist/{handler-gK6lNZKW.mjs.map → handler-s-wYH8Kk.mjs.map} +1 -1
  56. package/dist/http/application.d.mts +1 -1
  57. package/dist/http/application.mjs +2 -2
  58. package/dist/http/bootstrap.d.mts +1 -1
  59. package/dist/http/bootstrap.mjs +2 -2
  60. package/dist/http/context.d.mts +2 -2
  61. package/dist/http/context.mjs +2 -2
  62. package/dist/http/middleware/cors.mjs +1 -1
  63. package/dist/http/middleware/csrf.d.mts +1 -1
  64. package/dist/http/middleware/csrf.mjs +2 -2
  65. package/dist/http/middleware/error-handler.d.mts +1 -1
  66. package/dist/http/middleware/error-handler.mjs +2 -2
  67. package/dist/http/middleware/helmet.mjs +1 -1
  68. package/dist/http/middleware/rate-limit.d.mts +1 -1
  69. package/dist/http/middleware/rate-limit.mjs +2 -2
  70. package/dist/http/middleware/request-id.mjs +1 -1
  71. package/dist/http/middleware/request-logger.mjs +2 -2
  72. package/dist/http/middleware/session.mjs +2 -2
  73. package/dist/http/middleware/spa.d.mts +1 -1
  74. package/dist/http/middleware/spa.mjs +2 -2
  75. package/dist/http/middleware/upload.d.mts +1 -1
  76. package/dist/http/middleware/upload.mjs +2 -2
  77. package/dist/http/middleware/validate.mjs +1 -1
  78. package/dist/http/middleware/views.d.mts +1 -1
  79. package/dist/http/middleware/views.mjs +2 -2
  80. package/dist/http/query/index.mjs +2 -2
  81. package/dist/http/router-builder.d.mts +1 -1
  82. package/dist/http/router-builder.mjs +2 -2
  83. package/dist/http/runtimes/fastify.d.mts +1 -1
  84. package/dist/http/runtimes/fastify.mjs +2 -2
  85. package/dist/http/runtimes/h3-web.d.mts +1 -1
  86. package/dist/http/runtimes/h3-web.mjs +2 -2
  87. package/dist/http/runtimes/h3.d.mts +1 -1
  88. package/dist/http/runtimes/h3.mjs +2 -2
  89. package/dist/index.d.mts +14 -16
  90. package/dist/index.mjs +2 -2
  91. package/dist/{kv-stores-Bp4_RP-e.mjs → kv-stores-DClxfUzF.mjs} +3 -3
  92. package/dist/{kv-stores-Bp4_RP-e.mjs.map → kv-stores-DClxfUzF.mjs.map} +1 -1
  93. package/dist/{kv-stores-sjVe0MqN.d.mts → kv-stores-DR4ZHDht.d.mts} +4 -4
  94. package/dist/{kv-stores-sjVe0MqN.d.mts.map → kv-stores-DR4ZHDht.d.mts.map} +1 -1
  95. package/dist/{metadata-DRRQ0p_l.mjs → metadata-DvU1KnZD.mjs} +2 -2
  96. package/dist/{metadata-DRRQ0p_l.mjs.map → metadata-DvU1KnZD.mjs.map} +1 -1
  97. package/dist/{module-registry-C7yEmbZ8.mjs → module-registry-sxthbE3E.mjs} +2 -2
  98. package/dist/{module-registry-C7yEmbZ8.mjs.map → module-registry-sxthbE3E.mjs.map} +1 -1
  99. package/dist/{query-BSp_RIR0.mjs → query-BsOSNlUY.mjs} +2 -2
  100. package/dist/{query-BSp_RIR0.mjs.map → query-BsOSNlUY.mjs.map} +1 -1
  101. package/dist/{rate-limit-CzFoSCzS.d.mts → rate-limit-D5FHbYKG.d.mts} +2 -2
  102. package/dist/{rate-limit-CzFoSCzS.d.mts.map → rate-limit-D5FHbYKG.d.mts.map} +1 -1
  103. package/dist/{respond-BygP3p23.mjs → respond-Bqj4ktsY.mjs} +2 -2
  104. package/dist/{respond-BygP3p23.mjs.map → respond-Bqj4ktsY.mjs.map} +1 -1
  105. package/dist/{route-flag-GzEeUmws.d.mts → route-flag-BMlwj3Nk.d.mts} +22 -6
  106. package/dist/route-flag-BMlwj3Nk.d.mts.map +1 -0
  107. package/dist/route-flag-BsEtXxzP.mjs +12 -0
  108. package/dist/route-flag-BsEtXxzP.mjs.map +1 -0
  109. package/dist/{route-policy-D5lQltyg.mjs → route-policy-TDd9n2Q-.mjs} +2 -2
  110. package/dist/{route-policy-D5lQltyg.mjs.map → route-policy-TDd9n2Q-.mjs.map} +1 -1
  111. package/dist/router-builder-CfSWFOxm.mjs +12 -0
  112. package/dist/router-builder-CfSWFOxm.mjs.map +1 -0
  113. package/dist/{router-builder-eLpJYyrb.d.mts → router-builder-DZxYbs5I.d.mts} +9 -2
  114. package/dist/router-builder-DZxYbs5I.d.mts.map +1 -0
  115. package/dist/{runtime-B3fCMmZW.mjs → runtime-Cn74iFYv.mjs} +3 -3
  116. package/dist/{runtime-B3fCMmZW.mjs.map → runtime-Cn74iFYv.mjs.map} +1 -1
  117. package/dist/{upload-yKvKdRWG.d.mts → upload-BjA0m0ov.d.mts} +2 -2
  118. package/dist/{upload-yKvKdRWG.d.mts.map → upload-BjA0m0ov.d.mts.map} +1 -1
  119. package/dist/{upload-config-CP5v2Fh-.mjs → upload-config-dNLJaVlD.mjs} +2 -2
  120. package/dist/{upload-config-CP5v2Fh-.mjs.map → upload-config-dNLJaVlD.mjs.map} +1 -1
  121. package/dist/web.d.mts +3 -3
  122. package/dist/web.mjs +2 -2
  123. package/dist/web.mjs.map +1 -1
  124. package/package.json +1 -1
  125. package/dist/application-NjKJPm9r.d.mts +0 -495
  126. package/dist/application-NjKJPm9r.d.mts.map +0 -1
  127. package/dist/application-blu78Mlf.mjs +0 -12
  128. package/dist/application-blu78Mlf.mjs.map +0 -1
  129. package/dist/bootstrap-Sjn3hPqw.d.mts +0 -27
  130. package/dist/bootstrap-Sjn3hPqw.d.mts.map +0 -1
  131. package/dist/route-flag-GzEeUmws.d.mts.map +0 -1
  132. package/dist/route-flag-vuWGpI1V.mjs +0 -12
  133. package/dist/route-flag-vuWGpI1V.mjs.map +0 -1
  134. package/dist/router-builder-HeSJZ2QG.mjs +0 -12
  135. package/dist/router-builder-HeSJZ2QG.mjs.map +0 -1
  136. package/dist/router-builder-eLpJYyrb.d.mts.map +0 -1
  137. package/dist/spa-BBwrECcF.d.mts.map +0 -1
@@ -1,495 +0,0 @@
1
-
2
- import { t as Container } from "./container-Dpvd66Yt.mjs";
3
- import { m as ContributorRegistrations, r as AppModuleEntry } from "./app-module-DoM8RNZ9.mjs";
4
- import { b as RuntimeCapabilities, f as HttpRuntime, lt as KickPlugin, r as AppAdapter, s as ActiveRuntime, ut as ModuleRegistry, x as RuntimeResponse } from "./adapter-BxXGuLnW.mjs";
5
- import http from "node:http";
6
- import { Express, RequestHandler } from "express";
7
- //#region src/http/application.d.ts
8
- /**
9
- * A middleware entry in the declarative pipeline.
10
- * Can be a bare handler or an object with path scoping.
11
- */
12
- type MiddlewareEntry = RequestHandler | {
13
- path: string;
14
- handler: RequestHandler;
15
- };
16
- /** Options for {@link Application.shutdown}. */
17
- interface ShutdownOptions {
18
- /**
19
- * Close the HTTP server and drain in-flight requests. Default `true`.
20
- *
21
- * Set `false` for a dev HMR rebuild, where the server is shared across
22
- * reloads — adapters, plugins, and disposables are still torn down, which is
23
- * the part a reload needs.
24
- */
25
- closeServer?: boolean;
26
- }
27
- interface ApplicationOptions {
28
- /**
29
- * Feature modules to load. Accepts both class form
30
- * (`SomeModule extends AppModule`, instantiated via `new`) and
31
- * instance form (output of {@link defineModule}'s factory call,
32
- * e.g. `TasksModule({ scope: 'admin' })`) — the loader discriminates
33
- * and either `new`-s the class or uses the instance directly.
34
- *
35
- * Static path — for conditional / dynamic registration, use the
36
- * {@link ApplicationOptions.setup} callback instead.
37
- */
38
- modules: AppModuleEntry[];
39
- /**
40
- * Imperative module registration callback. Receives a
41
- * {@link ModuleRegistry} you call `.mount(module)` on to register
42
- * modules conditionally — based on env flags, runtime config, a
43
- * tenant list, etc. Runs after every plugin's `setup()` hook and
44
- * after the static `modules: [...]` array is collected.
45
- *
46
- * @example
47
- * ```ts
48
- * bootstrap({
49
- * modules: [HelloModule()],
50
- * setup(registry) {
51
- * if (env.ENABLE_ADMIN) registry.mount(AdminModule())
52
- * },
53
- * })
54
- * ```
55
- */
56
- setup?(registry: ModuleRegistry): void;
57
- /** Adapters that hook into the lifecycle (DB, Redis, Swagger, etc.) */
58
- adapters?: AppAdapter[];
59
- /** Server port (falls back to PORT env var, then 3000) */
60
- port?: number;
61
- /** Global API prefix (default: '/api'). Pass `''` to mount at the root. */
62
- apiPrefix?: string;
63
- /**
64
- * Default API version (default: 1) — routes become /{prefix}/v{version}/{path}.
65
- *
66
- * `false` opts out of URL versioning: routes mount at /{prefix}/{path}.
67
- * A module can still version itself by setting `version` on its
68
- * `ModuleRoutes` — the per-mount value always wins over this default, in
69
- * either direction.
70
- */
71
- defaultVersion?: number | false;
72
- /**
73
- * Global middleware pipeline. Declared in order.
74
- * Replaces the hardcoded middleware stack — you control exactly what runs.
75
- *
76
- * @example
77
- * ```ts
78
- * bootstrap({
79
- * modules,
80
- * middlewares: [
81
- * helmet(),
82
- * cors(),
83
- * compression(),
84
- * morgan('dev'),
85
- * express.json({ limit: '1mb' }),
86
- * ],
87
- * })
88
- * ```
89
- *
90
- * If omitted, a sensible default is applied:
91
- * requestId(), express.json({ limit: '100kb' })
92
- */
93
- middlewares?: MiddlewareEntry[];
94
- /** Plugins that bundle modules, adapters, middleware, and DI bindings */
95
- plugins?: KickPlugin[];
96
- /**
97
- * Global Context Contributors (#107) that apply to every route in the
98
- * application. Merge into the per-route pipeline at the `'global'`
99
- * precedence level — they lose to module, adapter, class, and method
100
- * contributors with the same key, providing app-wide defaults that any
101
- * narrower scope can override.
102
- *
103
- * @example
104
- * ```ts
105
- * const StartedAt = defineContextDecorator({
106
- * key: 'requestStartedAt',
107
- * resolve: () => Date.now(),
108
- * })
109
- *
110
- * bootstrap({
111
- * modules,
112
- * contributors: [StartedAt.registration],
113
- * })
114
- * ```
115
- */
116
- contributors?: ContributorRegistrations;
117
- /**
118
- * Backing store strategy for {@link RequestContext} `set/get` and the
119
- * Context Contributor pipeline (#107).
120
- *
121
- * - `'auto'` (default) — Application mounts {@link requestScopeMiddleware}
122
- * automatically before any user middleware. If the user-supplied
123
- * `middleware` list already includes `requestScopeMiddleware()`,
124
- * detection skips the auto-mount so adopters can control its position.
125
- * - `'manual'` — Application never mounts the wrapper. The Context
126
- * Contributor pipeline still runs on every route — the runner is
127
- * inserted by `router-builder` regardless of ALS state. What
128
- * degrades without an ALS frame is the *backing store*:
129
- * REQUEST-scoped DI throws (no `requestStore.getStore()` to read
130
- * from) and `RequestContext.set/get` throws. Use `'manual'` only when you
131
- * genuinely intend to wrap requests in your own ALS frame (rare —
132
- * multi-tenant adapters used to do this; post-Phase 3 they share
133
- * the framework's frame instead).
134
- */
135
- contextStore?: 'auto' | 'manual';
136
- /**
137
- * Controls whether KickJS registers process-level handlers at bootstrap.
138
- *
139
- * - `'auto'` (default) — register `uncaughtException` /
140
- * `unhandledRejection` loggers AND `SIGINT` / `SIGTERM` →
141
- * `app.shutdown()` → `process.exit(0)`.
142
- * - `'errors-only'` — register the error loggers; skip the signal
143
- * handlers. Use this when you have your own shutdown choreographer
144
- * (OpenTelemetry SDK, Sentry, custom orchestrator) that calls
145
- * `app.shutdown()` itself and you don't want two listeners racing
146
- * to call `process.exit`.
147
- * - `'manual'` — skip everything. Adopter is responsible for both
148
- * error logging and signal-driven shutdown.
149
- *
150
- * Background: many observability SDKs install their own SIGTERM
151
- * handler to flush spans/metrics before the process exits. Two
152
- * separate handlers calling `process.exit(0)` race each other and
153
- * can truncate the in-flight flush. Setting this to `'errors-only'`
154
- * or `'manual'` lets the SDK own the lifecycle while still letting
155
- * KickJS surface uncaught exceptions / rejections via the logger.
156
- *
157
- * The dev mode auto-detect (`__kickjs_httpServer` set by the Vite
158
- * plugin) suppresses signal registration regardless of this option,
159
- * since Vite owns the lifecycle in development.
160
- */
161
- processHooks?: 'auto' | 'errors-only' | 'manual';
162
- /**
163
- * The HTTP engine driver. Defaults to {@link expressRuntime} — Express stays
164
- * the zero-config default, so existing apps are unaffected.
165
- *
166
- * Any `HttpRuntime` is accepted — pass `fastifyRuntime()` (from
167
- * `@forinda/kickjs/fastify`) or a custom engine driver. Defaults to
168
- * {@link expressRuntime}. The engine-native escape hatches
169
- * (`getRuntimeApp()`, `AdapterContext.app`) follow the active runtime via the
170
- * registry (`ActiveRuntime`, spec §4.3b) — Express by default, or the augmented
171
- * engine once the `kick/runtime` typegen output is present.
172
- */
173
- /**
174
- * Mount the built-in `GET /health/live` and `GET /health/ready`.
175
- *
176
- * On by default. Set `false` to take them over completely — supply your own
177
- * module at `/health` and nothing collides. The built-in is registered last,
178
- * so an app module claiming the same path already wins without this.
179
- *
180
- * They mount as an ordinary module, which means they sit inside the
181
- * middleware chain: whatever guards the rest of the app guards these too.
182
- * Exempt the path if your orchestrator must reach them unauthenticated.
183
- */
184
- health?: boolean;
185
- runtime?: HttpRuntime;
186
- /** Express `trust proxy` setting */
187
- trustProxy?: boolean | number | string | ((ip: string, hopIndex: number) => boolean);
188
- /** Maximum JSON body size (only used when middleware is not provided) */
189
- jsonLimit?: string | number;
190
- /**
191
- * Print the route table on startup. Default: `false`. Set to `true`
192
- * to log a per-controller summary (method counts + mount paths) once
193
- * the app has mounted. Emitted at `info` level, so it is still subject
194
- * to `LOG_LEVEL` filtering (visible at the default `info`; hidden at
195
- * `warn`/`error`/`silent`).
196
- */
197
- logRouteTable?: boolean;
198
- /**
199
- * @deprecated Use {@link ApplicationOptions.logRouteTable}. Kept as an
200
- * alias for back-compat; `logRouteTable` wins when both are set.
201
- */
202
- logRoutesTable?: boolean;
203
- /**
204
- * Maximum time (ms) to wait for graceful shutdown before forcing exit.
205
- * Default: 30000 (30 seconds). Set to 0 to disable forced exit.
206
- */
207
- shutdownTimeout?: number;
208
- /**
209
- * Enable cluster mode for multi-core utilization.
210
- *
211
- * When enabled, the primary process forks worker processes that share the
212
- * same port via Node's built-in `cluster` module (OS load balancing).
213
- *
214
- * - `true` — use all available CPU cores
215
- * - `{ workers: N }` — use exactly N workers
216
- *
217
- * Workers are auto-restarted on crash. SIGTERM/SIGINT on the primary is
218
- * forwarded to all workers.
219
- *
220
- * @example
221
- * ```ts
222
- * bootstrap({ modules, cluster: true })
223
- * bootstrap({ modules, cluster: { workers: 4 } })
224
- * ```
225
- */
226
- cluster?: boolean | {
227
- workers?: number;
228
- };
229
- /**
230
- * Custom 404 handler for unmatched routes. When omitted, the built-in
231
- * handler returns `{ message: 'Not Found' }` with status 404.
232
- *
233
- * Connect-**shaped**, not Express-**semantic** — see {@link onError} for the
234
- * per-runtime table. The same two caveats apply: `next()` is inert on
235
- * Fastify and h3, and Express-only request members (`req.originalUrl`,
236
- * `req.path`, `req.ip`) are `undefined` there, so read `req.url`.
237
- *
238
- * @example
239
- * ```ts
240
- * bootstrap({
241
- * modules,
242
- * onNotFound: (req, res) => {
243
- * // `req.originalUrl` is Express-only — use `req.url` for portability.
244
- * res.status(404).json({ error: 'Route not found', path: req.url })
245
- * },
246
- * })
247
- * ```
248
- */
249
- onNotFound?: (req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void;
250
- /**
251
- * Custom global error handler, receiving `(err, req, res, next)`. When
252
- * omitted, the built-in handler formats ZodError, HttpException, and
253
- * unexpected errors.
254
- *
255
- * The shape is connect-style, but it is NOT "the standard Express error
256
- * handler" on every engine — what you actually receive differs, and the
257
- * differences bite:
258
- *
259
- * | | `req` | `res` | `next` |
260
- * | --- | --- | --- | --- |
261
- * | Express | native `Request` | native `Response` | real `next` |
262
- * | Fastify | `request.raw` | reply driver | **no-op** |
263
- * | h3 | `event.node.req` | response driver | no-op (or dev fall-through) |
264
- *
265
- * Two consequences worth knowing before you write one:
266
- *
267
- * - **`next(err)` does nothing on Fastify and h3.** It is a no-op function
268
- * there, so delegating to the default handler silently drops the error.
269
- * Send a response yourself rather than passing it on.
270
- * - **Express-only request members are `undefined` elsewhere.**
271
- * `req.originalUrl`, `req.path`, and `req.ip` do not exist on the raw node
272
- * request — use `req.url` and the `x-forwarded-*` headers, or reach for
273
- * `ctx.ip` in a contributor / guard where a `RequestContext` is available.
274
- *
275
- * `res` is typed {@link RuntimeResponse} because that is what every engine
276
- * genuinely provides: `status`, `json`, `send`, `setHeader`, `render`,
277
- * `writeHead`, `end`. Express's own `Response` satisfies it.
278
- *
279
- * @example
280
- * ```ts
281
- * bootstrap({
282
- * modules,
283
- * onError: (err, req, res) => {
284
- * logger.error(err, `${req.method} ${req.url}`) // not `originalUrl`
285
- * res.status(err.status ?? 500).json({ error: err.message })
286
- * },
287
- * })
288
- * ```
289
- */
290
- onError?: (err: any, req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void;
291
- /**
292
- * Security defaults applied automatically unless opted out.
293
- *
294
- * When not specified, secure defaults are applied:
295
- * - Helmet security headers are auto-injected
296
- * - JSON body limit defaults to 1MB
297
- *
298
- * @example
299
- * ```ts
300
- * bootstrap({
301
- * modules,
302
- * security: {
303
- * helmet: false, // disable auto-helmet
304
- * },
305
- * })
306
- * ```
307
- */
308
- security?: {
309
- /** Auto-inject helmet security headers. Default: true */
310
- helmet?: boolean;
311
- };
312
- }
313
- declare class Application {
314
- private readonly options;
315
- private app;
316
- /** The HTTP engine driver. Defaults to {@link expressRuntime}. */
317
- private readonly runtime;
318
- private container;
319
- private httpServer;
320
- private readonly adapters;
321
- /**
322
- * Flags of every mounted route, for middleware that runs before routing and
323
- * therefore cannot read `ctx.route` (the connect-style rate limiter). Handed
324
- * to middleware that declares the slot — see `core/route-policy.ts`.
325
- */
326
- private readonly routePolicy;
327
- private readonly plugins;
328
- /**
329
- * Snapshot of every module-level Context Contributor registration
330
- * captured during `setup()`. The registrations themselves are
331
- * frozen by `defineContextDecorator`, so storing references is
332
- * safe — they outlive the module instances they came from.
333
- *
334
- * Used by {@link getContributors} so DevTools and adopter tooling
335
- * can render the module-level contributors alongside the adapter /
336
- * plugin / global ones. Populated once per `setup()` call; cleared
337
- * at the start of each invocation so a re-setup (test harnesses,
338
- * dev-server restarts) doesn't accumulate stale entries.
339
- */
340
- private _moduleContributors;
341
- /** Number of HTTP requests currently being processed */
342
- private _inFlightRequests;
343
- /** Whether the application is draining (shutting down gracefully) */
344
- private _draining;
345
- /** Whether shutdown has already been initiated (prevents double-shutdown) */
346
- private _shutdownInitiated;
347
- /** Resolvers waiting for in-flight requests to reach zero */
348
- private _drainResolvers;
349
- constructor(options: ApplicationOptions);
350
- /** Whether the application is currently draining in-flight requests */
351
- get isDraining(): boolean;
352
- /** Number of HTTP requests currently being processed */
353
- get inFlightRequests(): number;
354
- /** Get the DI container instance */
355
- getContainer(): Container;
356
- /**
357
- * Express request handler — delegates to the internal Express app.
358
- *
359
- * Used by the Vite dev-server plugin to route requests through KickJS:
360
- * ```ts
361
- * const mod = await ssrLoadModule('virtual:kickjs/app')
362
- * mod.app.handle(req, res, next)
363
- * ```
364
- *
365
- * Also works as a standard Node.js request handler for production:
366
- * ```ts
367
- * http.createServer(app.handle.bind(app))
368
- * ```
369
- */
370
- handle(req: http.IncomingMessage, res: http.ServerResponse, next?: (err?: any) => void): void;
371
- /**
372
- * Full setup pipeline:
373
- * 1. Adapter beforeMount hooks (early routes — docs, health)
374
- * 2. Adapter middleware (phase: beforeGlobal)
375
- * 3. Global middleware (user-declared or defaults)
376
- * 4. Adapter middleware (phase: afterGlobal)
377
- * 5. Module registration + DI bootstrap
378
- * 6. Adapter middleware (phase: beforeRoutes)
379
- * 7. Module route mounting
380
- * 8. Adapter middleware (phase: afterRoutes)
381
- * 9. Error handlers (notFound + global)
382
- * 10. Adapter beforeStart hooks
383
- */
384
- /** Build the adapter context object (shared across all hooks) */
385
- private adapterCtx;
386
- /**
387
- * The engine-agnostic HTTP surface handed to adapters as `ctx.http`. Each
388
- * call routes through the active runtime over the current `this.app`, so an
389
- * adapter written against this works under any runtime.
390
- */
391
- private adapterHttp;
392
- /** Call an adapter hook, awaiting async hooks and catching errors */
393
- private callHook;
394
- setup(): Promise<void>;
395
- /** Register modules and DI without starting the HTTP server (used by kick tinker) */
396
- registerOnly(): Promise<void>;
397
- /**
398
- * Start the HTTP server.
399
- *
400
- * In **dev mode** (Vite plugin active): reuses `globalThis.__kickjs_httpServer`
401
- * created by Vite. Adapters (WsAdapter, Socket.IO, etc.) receive the real
402
- * `http.Server` through `afterStart({ server })` — zero adapter changes needed.
403
- *
404
- * In **production**: creates its own `http.Server` and binds to the port.
405
- */
406
- start(): Promise<void>;
407
- /** HMR rebuild: swap Express handler without restarting the server */
408
- rebuild(): Promise<void>;
409
- /**
410
- * Graceful shutdown with request draining.
411
- *
412
- * 1. Stops accepting new connections (server.close())
413
- * 2. Waits for in-flight requests to complete (up to shutdownTimeout)
414
- * 3. Calls adapter.shutdown() for all registered adapters
415
- * 4. Force-closes after timeout
416
- *
417
- * Safe to call multiple times — subsequent calls return the same promise.
418
- */
419
- shutdown(options?: ShutdownOptions): Promise<void>;
420
- /**
421
- * The engine-native app instance, typed from the active runtime
422
- * ({@link ActiveRuntime} — Express by default). Prefer this over
423
- * {@link getExpressApp}; under a non-Express runtime the type follows the
424
- * augmented registry.
425
- */
426
- getRuntimeApp(): ActiveRuntime['app'];
427
- /**
428
- * The active HTTP runtime's identity + capabilities — `name` is `'express'`
429
- * (default), `'fastify'`, or `'h3'`; `capabilities` reports what the engine
430
- * supports (render / uploads / connectMiddleware / nativeBodyParsing).
431
- * Surfaced by DevTools and the VS Code extension so tooling can show which
432
- * engine the app is running on. Cheap, side-effect free.
433
- */
434
- getActiveRuntime(): {
435
- name: string;
436
- capabilities: RuntimeCapabilities;
437
- };
438
- /** @deprecated Use {@link getRuntimeApp}. Returns the engine-native app. */
439
- getExpressApp(): Express;
440
- /** Get registered adapters — used by DevToolsAdapter for peer discovery */
441
- getAdapters(): AppAdapter[];
442
- /**
443
- * Get registered plugins — used by DevToolsAdapter for topology
444
- * introspection (architecture.md §23). Read-only; returns the
445
- * already-sorted plugin list (post-`dependsOn` topo-sort).
446
- */
447
- getPlugins(): readonly KickPlugin[];
448
- /**
449
- * Snapshot of every Context Contributor reachable from this app —
450
- * walks **four** registration sites: adapters, plugins, modules,
451
- * and the bootstrap `contributors` option. The devtools Topology
452
- * tab consumes this for the Contributors panel; tests + adopters
453
- * can call it for diagnostics.
454
- *
455
- * Per-route (method/class-decorator) registrations are NOT included
456
- * here — they live on the route registry rather than on any of
457
- * the four collection sites this method walks. A future RPC will
458
- * surface them separately.
459
- *
460
- * Result shape stays minimal — `{ key, source, label, dependsOn }`
461
- * — so we don't leak `resolve` closures or other internal fields.
462
- * `source` matches the five-level precedence union from
463
- * `ContributorSource` (minus `'method'` / `'class'` which require
464
- * the per-route walk noted above).
465
- */
466
- getContributors(): ReadonlyArray<{
467
- key: string;
468
- source: 'module' | 'adapter' | 'plugin' | 'global';
469
- label: string;
470
- dependsOn: readonly string[];
471
- }>;
472
- getHttpServer(): http.Server | null;
473
- /**
474
- * Decide whether {@link Application} should auto-mount the
475
- * {@link requestScopeMiddleware} ALS wrapper.
476
- *
477
- * Returns `false` when:
478
- * - `contextStore: 'manual'` was set (caller manages ALS frames), or
479
- * - the user-supplied `middleware` list already includes a
480
- * `requestScopeMiddleware()` (detected via the symbol marker stamped
481
- * in `middleware/request-scope.ts`).
482
- *
483
- * Otherwise `true` — preserves the historical default of "always wrap
484
- * requests in an ALS frame" so existing apps see no behavior change.
485
- */
486
- private shouldAutoMountRequestScope;
487
- private collectAdapterMiddleware;
488
- private mountMiddlewareList;
489
- private mountMiddlewareEntry;
490
- /** Middleware that tracks in-flight requests for graceful draining */
491
- private requestTrackingMiddleware;
492
- }
493
- //#endregion
494
- export { ShutdownOptions as i, ApplicationOptions as n, MiddlewareEntry as r, Application as t };
495
- //# sourceMappingURL=application-NjKJPm9r.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"application-NjKJPm9r.d.mts","names":[],"sources":["../src/http/application.ts"],"mappings":";;;;;;;;;;;KA4DY,kBAAkB;EAAmB;EAAc,SAAS;;;UAGvD;;;;;;;;EAQf;;UAGe;;;;;;;;;;;EAWf,SAAS;;;;;;;;;;;;;;;;;;EAmBT,OAAO,UAAU;;EAEjB,WAAW;;EAEX;;EAEA;;;;;;;;;EASA;;;;;;;;;;;;;;;;;;;;;;EAuBA,cAAc;;EAGd,UAAU;;;;;;;;;;;;;;;;;;;;;EAsBV,eAAe;;;;;;;;;;;;;;;;;;;EAoBf;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA;;;;;;;;;;;;;;;;;;;;;;;EAwBA;EAEA,UAAU;;EAGV,2CAA2C,YAAY;;EAEvD;;;;;;;;EAQA;;;;;EAKA;;;;;EAKA;;;;;;;;;;;;;;;;;;;EAoBA;IAAsB;;;;;;;;;;;;;;;;;;;;;;EAsBtB,cAAc,UAAU,KAAK,iBAAiB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0CrD,WAAW,UAAU,UAAU,KAAK,iBAAiB,OAAO;;;;;;;;;;;;;;;;;;EAmB5D;;IAEE;;;cAwHS;mBA0CkB;UAzCrB;;mBAES;UACT;UACA;mBACS;;;;;;mBAMA;mBAEA;;;;;;;;;;;;;UAcT;;UAMA;;UAEA;;UAEA;;UAEA;EAER,YAA6B,SAAS;;MAyClC;;MAKA;;EAKJ,gBAAgB;;;;;;;;;;;;;;;EAkBhB,OAAO,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB,QAAQ;;;;;;;;;;;;;;;UAuB5D;;;;;;UAiBA;;UA0BM;EAeR,SAAS;;EA4YT,gBAAgB;;;;;;;;;;EAahB,SAAS;;EA0ET,WAAW;;;;;;;;;;;EA8CX,SAAS,UAAS,kBAAuB;;;;;;;EAuJ/C,iBAAiB;;;;;;;;EAWjB;IAAsB;IAAc,cAAc;;;EAKlD,iBAAiB;;EAKjB,eAAe;;;;;;EASf,uBAAuB;;;;;;;;;;;;;;;;;;;EAsBvB,mBAAmB;IACjB;IACA;IACA;IACA;;EA6CF,iBAAiB,KAAK;;;;;;;;;;;;;;UAmBd;UAWA;UAmBA;UAUA;;UASA"}
@@ -1,12 +0,0 @@
1
- /**
2
- * @forinda/kickjs v8.2.0
3
- *
4
- * Copyright (c) Felix Orinda
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- import{METADATA}from"./core/interfaces.mjs";import{i as createToken,n as tokenName,s as moduleRouteMissingControllerError,t as Container}from"./container-BnkQIDQF.mjs";import{Logger,createLogger}from"./core/logger.mjs";import{t as getClassMeta}from"./metadata-DRRQ0p_l.mjs";import{Controller,Get}from"./core/decorators.mjs";import{t as MutableModuleRegistry}from"./module-registry-C7yEmbZ8.mjs";import{i as runDisposables,n as drainDisposables,r as registerDisposable,t as disposeAll}from"./disposables-lWieeJLG.mjs";import{buildMountPath,joinPaths}from"./core/path.mjs";import"./core-w7s4ntck.mjs";import{n as assertRouteUnique,r as buildRouteTable,t as _setExternalContributorSources}from"./router-builder-HeSJZ2QG.mjs";import{r as offerRoutePolicy,t as RoutePolicyTable}from"./route-policy-D5lQltyg.mjs";import{c as requestScopeMiddleware,d as rejectUnsupportedBody,i as reply,l as EXPRESS_JSON_TYPES,n as applyHandlerResult,s as isRequestScopeMiddleware,t as publishMatchedRoute}from"./runtime-B3fCMmZW.mjs";import{requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore,t as RequestContext}from"./context-GJc8Pf2d.mjs";import{t as __decorate}from"./decorate-CpBbxrvT.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware}from"./http/middleware/upload.mjs";import http from"node:http";import express,{Router}from"express";const mergeConfig=(defaults={},overrides={})=>({...defaults,...overrides}),composeName=(base,scope)=>`${base}:${scope}`;function defineModule(options){if(typeof options!=`object`||!options)throw TypeError(`defineModule: options must be an object literal. See architecture.md §21 for the spec shape.`);if(typeof options.name!=`string`||options.name.length===0){let got=typeof options.name==`string`?`""`:typeof options.name;throw TypeError(`defineModule: options.name must be a non-empty string (got ${got}). Module names appear in diagnostics and \`.scoped()\` namespacing — anonymous modules make cycle / missing-dep errors hard to localize.`)}if(typeof options.build!=`function`)throw TypeError(`defineModule(${options.name}): options.build is required and must be a function (got ${typeof options.build}). The build function returns the AppModule shape — register() / routes() / contributors() — wired with the resolved config.`);let buildSync=(instanceName,scoped,overrides)=>{let config=mergeConfig(options.defaults,overrides),built=options.build(config,{name:instanceName,scoped});return Object.defineProperty(built,"__moduleName",{value:instanceName,enumerable:!1,writable:!1,configurable:!1}),built},factory=(config=>buildSync(options.name,!1,config));return factory.scoped=(scopeName,config)=>buildSync(composeName(options.name,scopeName),!0,config),Object.defineProperty(factory,"definition",{value:Object.freeze({...options}),writable:!1,enumerable:!0,configurable:!1}),factory}var DuplicateMountNameError=class extends Error{kind;mountName;constructor(kind,mountName){super(`Duplicate ${kind} name '${mountName}' — every ${kind} must declare a unique name`),this.name=`DuplicateMountNameError`,this.kind=kind,this.mountName=mountName}},MissingMountDepError=class extends Error{kind;missing;dependent;constructor(kind,missing,dependent){super(`Missing ${kind} dependency '${missing}' required by '${dependent}' — is the ${kind} installed and named correctly?`),this.name=`MissingMountDepError`,this.kind=kind,this.missing=missing,this.dependent=dependent}},MountCycleError=class extends Error{kind;cycle;constructor(kind,cycle){super(`Cycle in ${kind} dependsOn declarations: ${cycle.join(` → `)}`),this.name=`MountCycleError`,this.kind=kind,this.cycle=Object.freeze([...cycle])}};function mountSort(items,kind){let byName=new Map;for(let item of items){if(byName.has(item.name))throw new DuplicateMountNameError(kind,item.name);byName.set(item.name,item)}let indegree=new Map,dependents=new Map;for(let item of items)indegree.set(item.name,item.dependsOn?.length??0);for(let item of items)for(let dep of item.dependsOn??[]){if(!byName.has(dep))throw new MissingMountDepError(kind,dep,item.name);let list=dependents.get(dep)??[];list.push(item),dependents.set(dep,list)}let queue=[];for(let item of items)indegree.get(item.name)===0&&queue.push(item);let sorted=[];for(;queue.length>0;){let node=queue.shift();sorted.push(node);for(let dependent of dependents.get(node.name)??[]){let next=(indegree.get(dependent.name)??0)-1;indegree.set(dependent.name,next),next===0&&queue.push(dependent)}}if(sorted.length!==items.length)throw new MountCycleError(kind,reconstructCycle(items.filter(it=>(indegree.get(it.name)??0)>0),byName));return sorted}function reconstructCycle(residual,byName){let residualNames=new Set(residual.map(r=>r.name)),start=residual[0].name,path=[start],seen=new Set([start]),cursor=start;for(;;){let nextDep=byName.get(cursor).dependsOn?.find(d=>residualNames.has(d));if(nextDep===void 0)break;if(seen.has(nextDep)){let cycleStart=path.indexOf(nextDep);return[...path.slice(cycleStart),nextDep]}path.push(nextDep),seen.add(nextDep),cursor=nextDep}return path}function __decorateMetadata(k,v){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(k,v)}const HEALTH_PROBE=createToken(`kick/Health/probe`);let HealthController=class{probe;constructor(probe){this.probe=probe}live(){return this.probe.isDraining()?reply(503,{status:`draining`,uptime:process.uptime()}):reply(200,{status:`ok`,uptime:process.uptime()})}async ready(){if(this.probe.isDraining())return reply(503,{status:`draining`,checks:[]});let checks=await this.probe.checks(),healthy=checks.every(c=>c.status===`up`);return reply(healthy?200:503,{status:healthy?`ready`:`degraded`,checks})}};__decorate([Get(`/live`),__decorateMetadata(`design:type`,Function),__decorateMetadata(`design:paramtypes`,[]),__decorateMetadata(`design:returntype`,void 0)],HealthController.prototype,`live`,null),__decorate([Get(`/ready`),__decorateMetadata(`design:type`,Function),__decorateMetadata(`design:paramtypes`,[]),__decorateMetadata(`design:returntype`,Promise)],HealthController.prototype,`ready`,null),HealthController=__decorate([Controller(),__decorateMetadata(`design:paramtypes`,[Object])],HealthController);const healthModule=defineModule({name:`HealthModule`,build:()=>({register(container){container.registerFactory(HealthController,()=>{let probe=container.resolve(HEALTH_PROBE);return new HealthController(probe)})},routes(){return{path:`/health`,controller:HealthController,version:!1,prefix:!1}}})});function materializeRouter(entries){let router=Router();for(let entry of entries){let method=entry.method.toLowerCase(),handlers=[];handlers.push((req,_res,next)=>{publishMatchedRoute(req,entry),next()}),entry.meta.validation&&handlers.push(validate(entry.meta.validation)),entry.meta.upload&&handlers.push(buildUploadMiddleware(entry.meta.upload));for(let mw of entry.middlewares)handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});if(entry.contributorRunner){let run=entry.contributorRunner;handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await run(ctx),next()}catch(err){next(err)}})}handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{let result=await entry.handler(ctx);res.headersSent||applyHandlerResult(ctx,result)}catch(err){next(err)}}),router[method](entry.path,...handlers)}return router}function buildRoutes(controllerClass,options={}){return materializeRouter(buildRouteTable(controllerClass,options))}function expressRuntime(){return{name:`express`,createApp(options={}){let app=express();return app.disable(`x-powered-by`),app.set(`trust proxy`,options.trustProxy??!1),app},nodeHandler(app){return(req,res,next)=>{next?app(req,res,next):app(req,res)}},mountRoutes(app,table){for(let{mountPath,routes}of table)app.use(mountPath,materializeRouter(routes))},useConnect(app,mw,opts={}){opts.path===void 0?app.use(mw):app.use(opts.path,mw)},serveStatic(app,prefix,dir){app.use(prefix,express.static(dir))},setNotFound(app,mw){app.use(mw)},setErrorHandler(app,mw){app.use(mw)},capabilities:{render:!0,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!1}}}const log=createLogger(`Application`);function isModuleFactory(entry){return typeof entry==`function`&&`definition`in entry&&typeof entry.scoped==`function`}function isConfigurable(factory){return factory.definition?.defaults!=null}function toAppModule(entry){if(typeof entry!=`function`)return entry;if(isModuleFactory(entry)){if(isConfigurable(entry)){let name=entry.definition?.name||entry.name||`<anonymous>`;throw TypeError(`bootstrap: module \`${name}\` takes configuration, so it must be invoked.\n Write \`${name}()\` for its defaults, or \`${name}({ … })\` to configure it.\n Passing it bare would have silently selected the defaults.`)}return entry()}let name=entry.name||`<anonymous>`;if(!isConstructible(entry))throw TypeError(`bootstrap: module entry \`${name}\` is a function, but not a module class and not a defineModule() factory (no \`definition\`).\n A defineModule() module is passed as \`${name}\` or \`${name}()\`; a class must implement AppModule.`);let mod=new entry;if(typeof mod?.routes!=`function`)throw TypeError(`bootstrap: module entry \`${name}\` constructed, but the result does not implement AppModule — \`routes()\` is missing.\n A module class needs \`routes()\`; a defineModule() module is passed as \`${name}\` or \`${name}()\`.`);return mod}function isConstructible(fn){try{return Reflect.construct(String,[],fn),!0}catch{return!1}}var Application=class{options;app;runtime;container;httpServer=null;adapters;routePolicy=new RoutePolicyTable;plugins;_moduleContributors=[];_inFlightRequests=0;_draining=!1;_shutdownInitiated=!1;_drainResolvers=[];constructor(options){this.options=options,this.runtime=options.runtime??expressRuntime(),this.app=this.runtime.createApp({trustProxy:options.trustProxy}),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){let handler=this.runtime.nodeHandler(this.app);next?handler(req,res,next):handler(req,res)}adapterCtx(server){let env=process.env.NODE_ENV??`development`;return{http:this.adapterHttp(),app:this.app,container:this.container,server,env,isProduction:env===`production`}}adapterHttp(){return{route:(method,path,handler)=>{let entry={method,path,middlewares:[],contributorRunner:null,handler,meta:{}};this.runtime.mountRoutes(this.app,[{mountPath:`/`,routes:[entry]}])},mount:(prefix,routes)=>{this.runtime.mountRoutes(this.app,[{mountPath:prefix,routes}])},serveStatic:(prefix,dir)=>{this.runtime.serveStatic(this.app,prefix,dir)},use:(mw,opts)=>{this.runtime.useConnect(this.app,mw,opts)}}}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.runtime.useConnect(this.app,this.requestTrackingMiddleware()),this.shouldAutoMountRequestScope()&&this.runtime.useConnect(this.app,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.runtime.useConnect(this.app,handler)}catch(err){log.error(err,`Plugin middleware failed: ${plugin.name??`unknown`}`)}let declaredHelmet=(this.options.middlewares??[]).some(entry=>typeof entry==`function`&&Symbol.for(`kick/http/helmet`)in entry);if(this.options.security?.helmet!==!1&&!declaredHelmet)try{let{helmet:helmetFn}=await import(`./http/middleware/helmet.mjs`);this.runtime.useConnect(this.app,helmetFn())}catch{}let userMiddlewares=this.options.middlewares;if(userMiddlewares)for(let entry of userMiddlewares)this.mountMiddlewareEntry(entry);else if(this.runtime.useConnect(this.app,requestId()),!this.runtime.capabilities.nativeBodyParsing){let limit=this.options.jsonLimit??`1mb`;this.runtime.useConnect(this.app,express.json({limit,type:[...EXPRESS_JSON_TYPES]})),this.runtime.useConnect(this.app,express.urlencoded({extended:!0,limit})),this.runtime.useConnect(this.app,express.text({limit,type:`text/*`})),this.runtime.useConnect(this.app,rejectUnsupportedBody())}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),this.options.health!==!1&&(this.container.registerInstance(HEALTH_PROBE,{isDraining:()=>this._draining,checks:async()=>{let withCheck=this.adapters.filter(a=>a.onHealthCheck);return(await Promise.allSettled(withCheck.map(a=>a.onHealthCheck()))).map((c,i)=>c.status===`fulfilled`?c.value:{name:withCheck[i].name??`unknown`,status:`down`})}}),moduleRegistry.mount(healthModule()));let modules=moduleRegistry.entries.map(entry=>{let mod=toAppModule(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=[];let seenRoutes=new Map,mountedPaths=[];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 version=route.version??defaultVersion,mountPath=buildMountPath(route.prefix===!1?``:apiPrefix,version,route.path);if(route.router)this.runtime.useConnect(this.app,route.router,{path:mountPath});else if(route.controller){let routeTable=buildRouteTable(route.controller),ctrl=route.controller.name??`controller`;for(let entry of routeTable){let owner=`${ctrl}.${String(entry.meta.handlerName??`?`)}`,fullPath=joinPaths(mountPath,entry.path);assertRouteUnique(seenRoutes,entry.method,fullPath,owner),mountedPaths.push({method:entry.method,path:fullPath}),this.routePolicy.add(entry.method,fullPath,entry.meta.flags)}this.runtime.mountRoutes(this.app,[{mountPath,routes:routeTable}])}else throw moduleRouteMissingControllerError(mountPath);if(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);for(let entry of this.options.middlewares??[])offerRoutePolicy(typeof entry==`function`?entry:entry.handler,this.routePolicy);for(let adapter of this.adapters)for(let mw of adapter.middleware?.()??[])offerRoutePolicy(mw.handler,this.routePolicy);this.runtime.setNotFound(this.app,this.options.onNotFound??notFoundHandler(mountedPaths)),this.runtime.setErrorHandler(this.app,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.runtime.nodeHandler(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,staleDisposables=drainDisposables();try{Container.reset(),this.container=Container.getInstance(),this.app=this.runtime.createApp({trustProxy:this.options.trustProxy}),await this.setup()}catch(err){log.error(err,`HMR rebuild failed, keeping previous app`),await runDisposables(drainDisposables());for(let d of staleDisposables)registerDisposable(d);this.app=prevApp,this.container=prevContainer;return}await runDisposables(staleDisposables),this.httpServer&&(this.httpServer.removeAllListeners(`request`),this.httpServer.on(`request`,this.runtime.nodeHandler(this.app)),log.debug(`HMR: app rebuilt and swapped`))}async shutdown(options={}){let closeServer=options.closeServer??!0;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(closeServer&&this.httpServer&&this.httpServer.close(()=>{}),closeServer&&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 hookTimeoutMs=closeServer?timeoutMs:Math.min(timeoutMs,5e3),timeBoxed=(label,run)=>{let hook=(async()=>run())();return hookTimeoutMs<=0?hook:new Promise((resolve,reject)=>{let t=setTimeout(()=>{log.warn(`${label} did not finish shutting down within ${hookTimeoutMs}ms — continuing without it. Its resources may still be held.`),resolve(void 0)},hookTimeoutMs);t.unref(),hook.then(value=>{clearTimeout(t),resolve(value)},err=>{clearTimeout(t),reject(err)})})},wasListening=this.httpServer?.listening===!0,results=await Promise.allSettled([...this.plugins.map(plugin=>timeBoxed(`Plugin '${plugin.name}'`,()=>plugin.shutdown?.())),...this.adapters.map(adapter=>timeBoxed(`Adapter '${adapter.name}'`,()=>adapter.shutdown?.()))]);!closeServer&&wasListening&&this.httpServer?.listening===!1&&log.error("An adapter or plugin closed the shared HTTP server during an HMR reload. The dev server is now unreachable and will not rebind until you restart it. Close only what you own: socket.io's `io.close()` closes the HTTP server passed to its constructor — on a reload, disconnect the sockets instead.");for(let result of results)result.status===`rejected`&&log.error({err:result.reason},`Adapter shutdown failed`);await disposeAll(),this.container.flushChanges()}finally{timer&&clearTimeout(timer)}}getRuntimeApp(){return this.app}getActiveRuntime(){return{name:this.runtime.name,capabilities:this.runtime.capabilities}}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??[];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.runtime.useConnect(this.app,entry.handler);continue}this.runtime.useConnect(this.app,entry.handler,{path:entry.path})}}mountMiddlewareEntry(entry){typeof entry==`function`?this.runtime.useConnect(this.app,entry):this.runtime.useConnect(this.app,entry.handler,{path:entry.path})}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()}}};export{HEALTH_PROBE as a,DuplicateMountNameError as c,mountSort as d,defineModule as f,materializeRouter as i,MissingMountDepError as l,buildRoutes as n,HealthController as o,expressRuntime as r,healthModule as s,Application as t,MountCycleError as u};
12
- //# sourceMappingURL=application-blu78Mlf.mjs.map