@forinda/kickjs 8.2.1 → 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.
- package/dist/{adapter-CvHtIu8A.d.mts → adapter-ByxWutTw.d.mts} +3 -3
- package/dist/{adapter-CvHtIu8A.d.mts.map → adapter-ByxWutTw.d.mts.map} +1 -1
- package/dist/{app-module-DyqiS9rQ.d.mts → app-module--bC2V1yw.d.mts} +18 -2
- package/dist/{app-module-DyqiS9rQ.d.mts.map → app-module--bC2V1yw.d.mts.map} +1 -1
- package/dist/{spa-DXTOhkVK.d.mts → application-NBph8Ahc.d.mts} +598 -69
- package/dist/application-NBph8Ahc.d.mts.map +1 -0
- package/dist/application-OZSYZy2Z.mjs +12 -0
- package/dist/application-OZSYZy2Z.mjs.map +1 -0
- package/dist/{bootstrap-DjJFTF0G.mjs → bootstrap-BQrXS6L1.mjs} +3 -3
- package/dist/{bootstrap-DjJFTF0G.mjs.map → bootstrap-BQrXS6L1.mjs.map} +1 -1
- package/dist/{client-ip-Dq-vEUNq.mjs → client-ip-D5ZBfP_C.mjs} +2 -2
- package/dist/{client-ip-Dq-vEUNq.mjs.map → client-ip-D5ZBfP_C.mjs.map} +1 -1
- package/dist/config/index.mjs +2 -2
- package/dist/{config-C3V72KSV.mjs → config-DlAZU1Dm.mjs} +3 -3
- package/dist/{config-C3V72KSV.mjs.map → config-DlAZU1Dm.mjs.map} +1 -1
- package/dist/{container-CZPSM8p-.mjs → container-DzwyumEo.mjs} +3 -3
- package/dist/{container-CZPSM8p-.mjs.map → container-DzwyumEo.mjs.map} +1 -1
- package/dist/{context-Dlkv3CNe.mjs → context-3SqXUyOr.mjs} +3 -3
- package/dist/{context-Dlkv3CNe.mjs.map → context-3SqXUyOr.mjs.map} +1 -1
- package/dist/{context-errors-CkXOg4qD.mjs → context-errors-CpTVAtA_.mjs} +2 -2
- package/dist/{context-errors-CkXOg4qD.mjs.map → context-errors-CpTVAtA_.mjs.map} +1 -1
- package/dist/{contributor-runner-r-_idhMs.mjs → contributor-runner-CX6InEzw.mjs} +3 -3
- package/dist/{contributor-runner-r-_idhMs.mjs.map → contributor-runner-CX6InEzw.mjs.map} +1 -1
- package/dist/core/adapter.d.mts +1 -1
- package/dist/core/adapter.mjs +1 -1
- package/dist/core/app-module.d.mts +1 -1
- package/dist/core/app-module.mjs +1 -1
- package/dist/core/cache.mjs +1 -1
- package/dist/core/container.mjs +2 -2
- package/dist/core/cron.mjs +2 -2
- package/dist/core/decorators.mjs +2 -2
- package/dist/core/errors.mjs +1 -1
- package/dist/core/interfaces.mjs +1 -1
- package/dist/core/logger.mjs +1 -1
- package/dist/core/path.mjs +1 -1
- package/dist/core/plugin.d.mts +1 -1
- package/dist/core/plugin.mjs +1 -1
- package/dist/core/reactivity.mjs +1 -1
- package/dist/{core-yW2dJ6lZ.mjs → core-CsxP6Jec.mjs} +3 -3
- package/dist/{core-yW2dJ6lZ.mjs.map → core-CsxP6Jec.mjs.map} +1 -1
- package/dist/{csrf-CFqmihA5.mjs → csrf-BdKFFh14.mjs} +3 -3
- package/dist/{csrf-CFqmihA5.mjs.map → csrf-BdKFFh14.mjs.map} +1 -1
- package/dist/{csrf-HVVOHlf8.d.mts → csrf-DDwUOF45.d.mts} +2 -2
- package/dist/{csrf-HVVOHlf8.d.mts.map → csrf-DDwUOF45.d.mts.map} +1 -1
- package/dist/{decorate-BzpBGF3k.mjs → decorate-BGm37ho8.mjs} +1 -1
- package/dist/{define-adapter-DexwynMQ.mjs → define-adapter-CpUxOgQ-.mjs} +2 -2
- package/dist/{define-adapter-DexwynMQ.mjs.map → define-adapter-CpUxOgQ-.mjs.map} +1 -1
- package/dist/{disposables-aOd-n5K3.mjs → disposables-Pouc1-v2.mjs} +2 -2
- package/dist/{disposables-aOd-n5K3.mjs.map → disposables-Pouc1-v2.mjs.map} +1 -1
- package/dist/{env-CXMlXrJd.mjs → env-BYseGI8D.mjs} +3 -3
- package/dist/{env-CXMlXrJd.mjs.map → env-BYseGI8D.mjs.map} +1 -1
- package/dist/{error-handler-DVAcTt8p.d.mts → error-handler-BtTYsKLV.d.mts} +2 -2
- package/dist/{error-handler-DVAcTt8p.d.mts.map → error-handler-BtTYsKLV.d.mts.map} +1 -1
- package/dist/{handler-Bhn7_J7d.mjs → handler-s-wYH8Kk.mjs} +3 -3
- package/dist/{handler-Bhn7_J7d.mjs.map → handler-s-wYH8Kk.mjs.map} +1 -1
- package/dist/http/application.d.mts +1 -1
- package/dist/http/application.mjs +2 -2
- package/dist/http/bootstrap.d.mts +1 -1
- package/dist/http/bootstrap.mjs +2 -2
- package/dist/http/context.d.mts +2 -2
- package/dist/http/context.mjs +2 -2
- package/dist/http/middleware/cors.mjs +1 -1
- package/dist/http/middleware/csrf.d.mts +1 -1
- package/dist/http/middleware/csrf.mjs +2 -2
- package/dist/http/middleware/error-handler.d.mts +1 -1
- package/dist/http/middleware/error-handler.mjs +2 -2
- package/dist/http/middleware/helmet.mjs +1 -1
- package/dist/http/middleware/rate-limit.d.mts +1 -1
- package/dist/http/middleware/rate-limit.mjs +2 -2
- package/dist/http/middleware/request-id.mjs +1 -1
- package/dist/http/middleware/request-logger.mjs +2 -2
- package/dist/http/middleware/session.mjs +2 -2
- package/dist/http/middleware/spa.d.mts +1 -1
- package/dist/http/middleware/spa.mjs +2 -2
- package/dist/http/middleware/upload.d.mts +1 -1
- package/dist/http/middleware/upload.mjs +2 -2
- package/dist/http/middleware/validate.mjs +1 -1
- package/dist/http/middleware/views.d.mts +1 -1
- package/dist/http/middleware/views.mjs +2 -2
- package/dist/http/query/index.mjs +2 -2
- package/dist/http/router-builder.d.mts +1 -1
- package/dist/http/router-builder.mjs +2 -2
- package/dist/http/runtimes/fastify.d.mts +1 -1
- package/dist/http/runtimes/fastify.mjs +2 -2
- package/dist/http/runtimes/h3-web.d.mts +1 -1
- package/dist/http/runtimes/h3-web.mjs +2 -2
- package/dist/http/runtimes/h3.d.mts +1 -1
- package/dist/http/runtimes/h3.mjs +2 -2
- package/dist/index.d.mts +14 -16
- package/dist/index.mjs +2 -2
- package/dist/{kv-stores-Cy9GuUcp.mjs → kv-stores-DClxfUzF.mjs} +3 -3
- package/dist/{kv-stores-Cy9GuUcp.mjs.map → kv-stores-DClxfUzF.mjs.map} +1 -1
- package/dist/{kv-stores-D_anc94e.d.mts → kv-stores-DR4ZHDht.d.mts} +4 -4
- package/dist/{kv-stores-D_anc94e.d.mts.map → kv-stores-DR4ZHDht.d.mts.map} +1 -1
- package/dist/{metadata-C-9GVv_0.mjs → metadata-DvU1KnZD.mjs} +2 -2
- package/dist/{metadata-C-9GVv_0.mjs.map → metadata-DvU1KnZD.mjs.map} +1 -1
- package/dist/{module-registry-Wk9JPlyW.mjs → module-registry-sxthbE3E.mjs} +2 -2
- package/dist/{module-registry-Wk9JPlyW.mjs.map → module-registry-sxthbE3E.mjs.map} +1 -1
- package/dist/{query-CN3EnbSs.mjs → query-BsOSNlUY.mjs} +2 -2
- package/dist/{query-CN3EnbSs.mjs.map → query-BsOSNlUY.mjs.map} +1 -1
- package/dist/{rate-limit-BJc724GS.d.mts → rate-limit-D5FHbYKG.d.mts} +2 -2
- package/dist/{rate-limit-BJc724GS.d.mts.map → rate-limit-D5FHbYKG.d.mts.map} +1 -1
- package/dist/{respond-Dh4eK1qn.mjs → respond-Bqj4ktsY.mjs} +2 -2
- package/dist/{respond-Dh4eK1qn.mjs.map → respond-Bqj4ktsY.mjs.map} +1 -1
- package/dist/{route-flag-C6VJQ2el.d.mts → route-flag-BMlwj3Nk.d.mts} +22 -6
- package/dist/route-flag-BMlwj3Nk.d.mts.map +1 -0
- package/dist/route-flag-BsEtXxzP.mjs +12 -0
- package/dist/route-flag-BsEtXxzP.mjs.map +1 -0
- package/dist/{route-policy-dA3BgX2k.mjs → route-policy-TDd9n2Q-.mjs} +2 -2
- package/dist/{route-policy-dA3BgX2k.mjs.map → route-policy-TDd9n2Q-.mjs.map} +1 -1
- package/dist/router-builder-CfSWFOxm.mjs +12 -0
- package/dist/router-builder-CfSWFOxm.mjs.map +1 -0
- package/dist/{router-builder-CxRFEM9j.d.mts → router-builder-DZxYbs5I.d.mts} +9 -2
- package/dist/router-builder-DZxYbs5I.d.mts.map +1 -0
- package/dist/{runtime-DhoVD9YL.mjs → runtime-Cn74iFYv.mjs} +3 -3
- package/dist/{runtime-DhoVD9YL.mjs.map → runtime-Cn74iFYv.mjs.map} +1 -1
- package/dist/{upload-ysRl6aKu.d.mts → upload-BjA0m0ov.d.mts} +2 -2
- package/dist/{upload-ysRl6aKu.d.mts.map → upload-BjA0m0ov.d.mts.map} +1 -1
- package/dist/{upload-config-DIXi-mCL.mjs → upload-config-dNLJaVlD.mjs} +2 -2
- package/dist/{upload-config-DIXi-mCL.mjs.map → upload-config-dNLJaVlD.mjs.map} +1 -1
- package/dist/web.d.mts +3 -3
- package/dist/web.mjs +2 -2
- package/dist/web.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/application-8Pw_oQno.mjs +0 -12
- package/dist/application-8Pw_oQno.mjs.map +0 -1
- package/dist/application-DIxWiYOw.d.mts +0 -496
- package/dist/application-DIxWiYOw.d.mts.map +0 -1
- package/dist/bootstrap-DCUWJRmN.d.mts +0 -27
- package/dist/bootstrap-DCUWJRmN.d.mts.map +0 -1
- package/dist/route-flag-C6VJQ2el.d.mts.map +0 -1
- package/dist/route-flag-DTfTiScc.mjs +0 -12
- package/dist/route-flag-DTfTiScc.mjs.map +0 -1
- package/dist/router-builder-CxRFEM9j.d.mts.map +0 -1
- package/dist/router-builder-DP6CbI7S.mjs +0 -12
- package/dist/router-builder-DP6CbI7S.mjs.map +0 -1
- package/dist/spa-DXTOhkVK.d.mts.map +0 -1
|
@@ -1,496 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { t as Container } from "./container-Dpvd66Yt.mjs";
|
|
3
|
-
import { m as ContributorRegistrations, r as AppModuleEntry } from "./app-module-DyqiS9rQ.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-CvHtIu8A.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 — e.g. docs UI)
|
|
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 (the built-in health module mounts
|
|
378
|
-
* here with everything else — it is NOT an early route)
|
|
379
|
-
* 6. Adapter middleware (phase: beforeRoutes)
|
|
380
|
-
* 7. Module route mounting
|
|
381
|
-
* 8. Adapter middleware (phase: afterRoutes)
|
|
382
|
-
* 9. Adapter beforeStart hooks
|
|
383
|
-
* 10. Error handlers (notFound + global)
|
|
384
|
-
*/
|
|
385
|
-
/** Build the adapter context object (shared across all hooks) */
|
|
386
|
-
private adapterCtx;
|
|
387
|
-
/**
|
|
388
|
-
* The engine-agnostic HTTP surface handed to adapters as `ctx.http`. Each
|
|
389
|
-
* call routes through the active runtime over the current `this.app`, so an
|
|
390
|
-
* adapter written against this works under any runtime.
|
|
391
|
-
*/
|
|
392
|
-
private adapterHttp;
|
|
393
|
-
/** Call an adapter hook, awaiting async hooks and catching errors */
|
|
394
|
-
private callHook;
|
|
395
|
-
setup(): Promise<void>;
|
|
396
|
-
/** Register modules and DI without starting the HTTP server (used by kick tinker) */
|
|
397
|
-
registerOnly(): Promise<void>;
|
|
398
|
-
/**
|
|
399
|
-
* Start the HTTP server.
|
|
400
|
-
*
|
|
401
|
-
* In **dev mode** (Vite plugin active): reuses `globalThis.__kickjs_httpServer`
|
|
402
|
-
* created by Vite. Adapters (WsAdapter, Socket.IO, etc.) receive the real
|
|
403
|
-
* `http.Server` through `afterStart({ server })` — zero adapter changes needed.
|
|
404
|
-
*
|
|
405
|
-
* In **production**: creates its own `http.Server` and binds to the port.
|
|
406
|
-
*/
|
|
407
|
-
start(): Promise<void>;
|
|
408
|
-
/** HMR rebuild: swap Express handler without restarting the server */
|
|
409
|
-
rebuild(): Promise<void>;
|
|
410
|
-
/**
|
|
411
|
-
* Graceful shutdown with request draining.
|
|
412
|
-
*
|
|
413
|
-
* 1. Stops accepting new connections (server.close())
|
|
414
|
-
* 2. Waits for in-flight requests to complete (up to shutdownTimeout)
|
|
415
|
-
* 3. Calls adapter.shutdown() for all registered adapters
|
|
416
|
-
* 4. Force-closes after timeout
|
|
417
|
-
*
|
|
418
|
-
* Safe to call multiple times — subsequent calls return the same promise.
|
|
419
|
-
*/
|
|
420
|
-
shutdown(options?: ShutdownOptions): Promise<void>;
|
|
421
|
-
/**
|
|
422
|
-
* The engine-native app instance, typed from the active runtime
|
|
423
|
-
* ({@link ActiveRuntime} — Express by default). Prefer this over
|
|
424
|
-
* {@link getExpressApp}; under a non-Express runtime the type follows the
|
|
425
|
-
* augmented registry.
|
|
426
|
-
*/
|
|
427
|
-
getRuntimeApp(): ActiveRuntime['app'];
|
|
428
|
-
/**
|
|
429
|
-
* The active HTTP runtime's identity + capabilities — `name` is `'express'`
|
|
430
|
-
* (default), `'fastify'`, or `'h3'`; `capabilities` reports what the engine
|
|
431
|
-
* supports (render / uploads / connectMiddleware / nativeBodyParsing).
|
|
432
|
-
* Surfaced by DevTools and the VS Code extension so tooling can show which
|
|
433
|
-
* engine the app is running on. Cheap, side-effect free.
|
|
434
|
-
*/
|
|
435
|
-
getActiveRuntime(): {
|
|
436
|
-
name: string;
|
|
437
|
-
capabilities: RuntimeCapabilities;
|
|
438
|
-
};
|
|
439
|
-
/** @deprecated Use {@link getRuntimeApp}. Returns the engine-native app. */
|
|
440
|
-
getExpressApp(): Express;
|
|
441
|
-
/** Get registered adapters — used by DevToolsAdapter for peer discovery */
|
|
442
|
-
getAdapters(): AppAdapter[];
|
|
443
|
-
/**
|
|
444
|
-
* Get registered plugins — used by DevToolsAdapter for topology
|
|
445
|
-
* introspection (architecture.md §23). Read-only; returns the
|
|
446
|
-
* already-sorted plugin list (post-`dependsOn` topo-sort).
|
|
447
|
-
*/
|
|
448
|
-
getPlugins(): readonly KickPlugin[];
|
|
449
|
-
/**
|
|
450
|
-
* Snapshot of every Context Contributor reachable from this app —
|
|
451
|
-
* walks **four** registration sites: adapters, plugins, modules,
|
|
452
|
-
* and the bootstrap `contributors` option. The devtools Topology
|
|
453
|
-
* tab consumes this for the Contributors panel; tests + adopters
|
|
454
|
-
* can call it for diagnostics.
|
|
455
|
-
*
|
|
456
|
-
* Per-route (method/class-decorator) registrations are NOT included
|
|
457
|
-
* here — they live on the route registry rather than on any of
|
|
458
|
-
* the four collection sites this method walks. A future RPC will
|
|
459
|
-
* surface them separately.
|
|
460
|
-
*
|
|
461
|
-
* Result shape stays minimal — `{ key, source, label, dependsOn }`
|
|
462
|
-
* — so we don't leak `resolve` closures or other internal fields.
|
|
463
|
-
* `source` matches the five-level precedence union from
|
|
464
|
-
* `ContributorSource` (minus `'method'` / `'class'` which require
|
|
465
|
-
* the per-route walk noted above).
|
|
466
|
-
*/
|
|
467
|
-
getContributors(): ReadonlyArray<{
|
|
468
|
-
key: string;
|
|
469
|
-
source: 'module' | 'adapter' | 'plugin' | 'global';
|
|
470
|
-
label: string;
|
|
471
|
-
dependsOn: readonly string[];
|
|
472
|
-
}>;
|
|
473
|
-
getHttpServer(): http.Server | null;
|
|
474
|
-
/**
|
|
475
|
-
* Decide whether {@link Application} should auto-mount the
|
|
476
|
-
* {@link requestScopeMiddleware} ALS wrapper.
|
|
477
|
-
*
|
|
478
|
-
* Returns `false` when:
|
|
479
|
-
* - `contextStore: 'manual'` was set (caller manages ALS frames), or
|
|
480
|
-
* - the user-supplied `middleware` list already includes a
|
|
481
|
-
* `requestScopeMiddleware()` (detected via the symbol marker stamped
|
|
482
|
-
* in `middleware/request-scope.ts`).
|
|
483
|
-
*
|
|
484
|
-
* Otherwise `true` — preserves the historical default of "always wrap
|
|
485
|
-
* requests in an ALS frame" so existing apps see no behavior change.
|
|
486
|
-
*/
|
|
487
|
-
private shouldAutoMountRequestScope;
|
|
488
|
-
private collectAdapterMiddleware;
|
|
489
|
-
private mountMiddlewareList;
|
|
490
|
-
private mountMiddlewareEntry;
|
|
491
|
-
/** Middleware that tracks in-flight requests for graceful draining */
|
|
492
|
-
private requestTrackingMiddleware;
|
|
493
|
-
}
|
|
494
|
-
//#endregion
|
|
495
|
-
export { ShutdownOptions as i, ApplicationOptions as n, MiddlewareEntry as r, Application as t };
|
|
496
|
-
//# sourceMappingURL=application-DIxWiYOw.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"application-DIxWiYOw.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;;;;;;;;;;;;;;;;UAwB5D;;;;;;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,27 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { n as ApplicationOptions, t as Application } from "./application-DIxWiYOw.mjs";
|
|
3
|
-
//#region src/http/bootstrap.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Bootstrap a KickJS application.
|
|
6
|
-
*
|
|
7
|
-
* Creates the Express app, registers modules and adapters, and starts
|
|
8
|
-
* the HTTP server. In dev mode (`kick dev`), the Vite plugin manages
|
|
9
|
-
* the server and HMR — source changes are picked up instantly without
|
|
10
|
-
* losing database connections or WebSocket state.
|
|
11
|
-
*
|
|
12
|
-
* @returns The {@link Application} instance.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* // src/index.ts
|
|
17
|
-
* import 'reflect-metadata'
|
|
18
|
-
* import { bootstrap } from '@forinda/kickjs'
|
|
19
|
-
* import { modules } from './modules'
|
|
20
|
-
*
|
|
21
|
-
* export const app = await bootstrap({ modules })
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
declare function bootstrap(options: ApplicationOptions): Promise<Application>;
|
|
25
|
-
//#endregion
|
|
26
|
-
export { bootstrap as t };
|
|
27
|
-
//# sourceMappingURL=bootstrap-DCUWJRmN.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap-DCUWJRmN.d.mts","names":[],"sources":["../src/http/bootstrap.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;iBAqCsB,UAAU,SAAS,qBAAqB,QAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-flag-C6VJQ2el.d.mts","names":[],"sources":["../src/core/route-flag.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkDiB;;;;;;;KAQL,uBAAuB,iDAEzB;;;;;KAME,eAAe,kBAAkB,sBAAsB,gBAAgB,iBAC/E,eAAe,KACf;;UAmBa;WACN;;WAEA;;;;;;;;;;;;;;UAeM;;;;;;;GAOd,QAAQ;;;;;GAKR,gBAAgB,8BAA8B,aAAa;;;UAI7C,gBAAgB;GAC9B,OAAO,KAAK,gBAAgB;;;;;;;;;WASpB,KAAK;WACL;;;;;;;;;;;KAYC,UAAU,yBAAyB,IAC3C,gBAAgB,gBAAgB,KAChC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;iBA2CJ,sBAAsB,UAAU,eAC9C,MAAM,IACL,UAAU,eAAe;iBACZ,gBAAgB,GAAG,MAAM,gBAAgB,UAAU;;;;;;;;;;;iBAkEnD,kBACd,4BAA4B,wBAC5B,6BAA6B,yBAC5B;;;;;;;;;UA0Bc,mBAAmB;EAClC,IAAI,MAAM;EACV,IAAI,UAAU,eAAe,MAAM,IAAI,eAAe;;;;;;;;UASvC;WACN,OAAO;WACP;aACE;aACA;aACA;aACA;;;;;;;;;;;;;;;;KAiBD,sBAAsB,KAAK;;;;;;;;;;;;;;;;;;;;;KAsB3B,2BAA2B;;;;;;;;;;;;;;;KAgB3B,gBACR,gBACA,gCACS,2BACA,yBACT;;;;;;;;iBASY,gBACd,MAAM,eACN,OAAO,wBACP,QAAQ;;;;;;;;;;;;;;;;;;;iBA4FM,cAAc,yBAAyB,sBAAsB"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @forinda/kickjs v8.2.1
|
|
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{a as getMethodMeta,c as pushClassMeta,l as pushMethodMeta,t as getClassMeta}from"./metadata-C-9GVv_0.mjs";const FLAG_UNSET=Symbol(`kick.flagUnset`);function isDecoratorApplication(args){let[target,propertyKey]=args;return typeof propertyKey==`string`||typeof propertyKey==`symbol`||args.length===1&&typeof target==`function`}function defineRouteFlag(name){if(name.startsWith(`!`))throw Error(`defineRouteFlag('${name}'): a flag name cannot start with '!' — that prefix is reserved for negated tests ('!${name.slice(1)}' means "does not carry ${name.slice(1)}"). Rename the flag.`);function apply(value,target,propertyKey){let declaration={name,value};if(propertyKey===void 0){pushClassMeta(METADATA.CLASS_FLAGS,target,declaration);return}pushMethodMeta(METADATA.METHOD_FLAGS,target.constructor,String(propertyKey),declaration)}let flag=(...args)=>{if(isDecoratorApplication(args)){apply(!0,args[0],args[1]);return}let value=args[0];return(target,propertyKey)=>{apply(value,target,propertyKey)}};return Object.defineProperty(flag,"flagName",{value:name,enumerable:!0}),Object.defineProperty(flag,"off",{value:(target,propertyKey)=>{apply(FLAG_UNSET,target,propertyKey)},enumerable:!0}),flag}function resolveRouteFlags(classDeclarations,methodDeclarations){let resolved=new Map;for(let{name,value}of[...classDeclarations,...methodDeclarations]){if(value===FLAG_UNSET){resolved.delete(name);continue}resolved.set(name,value)}return resolved}function matchesFlagTest(test,flags,route){let resolved=flags??EMPTY_FLAGS;if(typeof test==`function`)return test({flags:resolved,route});if(typeof test==`string`)return isNegated(test)?!resolved.has(stripNegation(test)):resolved.has(test);if(test.length===0)return!1;let negated=isNegated(test[0]);if(test.some(name=>isNegated(name)!==negated))throw Error(`Route flag test mixes polarities: [${test.map(n=>`'${n}'`).join(`, `)}]. A list is either all names ("carries any of these") or all negated ("carries none of these") — for anything else, pass a predicate.`);return negated?test.every(name=>!resolved.has(stripNegation(name))):test.some(name=>resolved.has(name))}function assertFlagTest(test,site){if(typeof test==`function`||typeof test==`string`||test.length===0)return;let negated=isNegated(test[0]);if(test.some(name=>isNegated(name)!==negated))throw Error(`${site}: route flag test mixes polarities: [${test.map(n=>`'${n}'`).join(`, `)}]. A list is either all names ("carries any of these") or all negated ("carries none of these") — for anything else, pass a predicate.`)}const isNegated=name=>name.startsWith(`!`),stripNegation=name=>name.slice(1),EMPTY_FLAGS=new Map,ROUTE_SLOT=Symbol.for(`kick.route`);function getRouteFlags(controllerClass,handlerName){return resolveRouteFlags(getClassFlagDeclarations(controllerClass),getMethodFlagDeclarations(controllerClass,handlerName))}function getClassFlagDeclarations(controllerClass){return getClassMeta(METADATA.CLASS_FLAGS,controllerClass,[])}function getMethodFlagDeclarations(controllerClass,handlerName){return getMethodMeta(METADATA.METHOD_FLAGS,controllerClass,handlerName,[])}export{getMethodFlagDeclarations as a,resolveRouteFlags as c,getClassFlagDeclarations as i,assertFlagTest as n,getRouteFlags as o,defineRouteFlag as r,matchesFlagTest as s,ROUTE_SLOT as t};
|
|
12
|
-
//# sourceMappingURL=route-flag-DTfTiScc.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-flag-DTfTiScc.mjs","names":[],"sources":["../src/core/route-flag.ts"],"sourcesContent":["/**\n * Route flags — a named, inheritable fact about a route (spec:\n * `route-flags-design.md`).\n *\n * A flag carries no behaviour. It records something about the route that any\n * consumer may read: `auth.public`, `csrf.exempt`, `rate.limit`. Auth reads it\n * to skip a contributor, a guard reads it to return early, Swagger reads it to\n * mark a security scheme — none of them needs to know about the others.\n *\n * The rule that makes presence checks safe: a flag resolves to **absent**, or\n * **present with a value**. `@Public.off` on a method removes what the class\n * set, rather than storing something falsy — so `flags.has(name)` is always the\n * right question for a boolean flag. Without that, `has('auth.public')` could\n * answer `true` for the route that just opted back IN, which is an\n * authorization bypass rather than a papercut.\n */\nimport { METADATA } from './interfaces'\nimport { getClassMeta, getMethodMeta, pushClassMeta, pushMethodMeta } from './metadata'\n\n/**\n * Augmentable route-flag registry — the flag counterpart of `ContextMeta`.\n *\n * Declare a project's flags here and every use of them narrows: a misspelt\n * name is a compile error rather than a flag that silently never matches, and\n * `flags.get('rate.limit')` comes back typed instead of `unknown`.\n *\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface KickRouteFlags {\n * 'auth.public': true\n * 'rate.limit': { rpm: number }\n * }\n * }\n * ```\n *\n * The interface is empty by default, and everything below falls back to plain\n * `string` while it stays that way — a project that has not augmented anything\n * keeps compiling exactly as before. The narrowing switches on with the first\n * declaration.\n *\n * The framework itself declares nothing here: it ships the mechanism and names\n * no flags, so `auth.public` above is a name you chose.\n *\n * Two constraints on what you can declare:\n * - A name cannot start with `!` — that prefix means \"does not carry this flag\"\n * in a {@link RouteFlagTest}, and `defineRouteFlag` rejects it.\n * - Removal is `@Flag.off`, not `@Flag(false)` — so `false` stays usable as a\n * real value for a flag declared `boolean`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface KickRouteFlags {}\n\n/**\n * A flag name. Narrows to the declared keys once {@link KickRouteFlags} is\n * augmented, and stays `string` until then — the same\n * `[Known] extends [never]` fallback `ContextMetaKey` uses, for the same\n * reason: first-day code must keep compiling.\n */\nexport type RouteFlagName = [keyof KickRouteFlags] extends [never]\n ? string\n : keyof KickRouteFlags & string\n\n/**\n * The value type declared for a flag, defaulting to `true` for a bare flag and\n * `unknown` for a name the registry does not know.\n */\nexport type RouteFlagValue<K extends string, Fallback = unknown> = K extends keyof KickRouteFlags\n ? KickRouteFlags[K]\n : Fallback\n\n/**\n * Marks a declaration as \"remove this flag\" during resolution.\n *\n * A dedicated sentinel rather than `false`: `false` is a perfectly good flag\n * VALUE (a registry entry typed `boolean`), and overloading it meant\n * `@Enabled(false)` deleted the flag while `flags.get('enabled')` could never\n * return `false`. With the sentinel, `false` stores like any other value and\n * removal is spelled `@Enabled.off`.\n *\n * Module-private and NOT `Symbol.for`: a registry-global symbol is reachable\n * by application code, so a flag declared with a `symbol` value could be handed\n * this exact sentinel and be deleted instead of stored. It is created and\n * consumed in this module, so it never needs to cross a realm.\n */\nconst FLAG_UNSET: unique symbol = Symbol('kick.flagUnset') as never\n\n/** One `@Flag` application, before precedence resolution. */\nexport interface RouteFlagDeclaration {\n readonly name: string\n /** `false` means \"remove whatever a lower-precedence site declared\". */\n readonly value: unknown\n}\n\n/**\n * A flag decorator. Usable bare (`@Public`) or called with a value\n * (`@Public(false)`, `@RateLimit({ rpm: 10 })`), on a class or a method.\n */\n/**\n * The bare application forms — `@Public` with no call.\n *\n * Split out so they can be withheld from a flag whose declared value is not\n * assignable from `true`: applying `@Limit` bare would store `true` while\n * `flags.get('rate.limit')` promises `{ rpm: number }`, and nothing would catch\n * the mismatch until a consumer read the wrong shape at runtime.\n */\nexport interface BareRouteFlag {\n /**\n * Bare on a class. Typed against `Function` rather than `object` so a flag\n * VALUE that happens to be an object (`@Limit({ rpm: 10 })`) cannot match\n * this signature: overload resolution would otherwise pick it, return\n * `void`, and fail with \"Type 'void' has no call signatures\".\n */\n (target: Function): void\n /**\n * Bare on a method. `propertyKey` is required for the same reason — it makes\n * a one-argument call unambiguous.\n */\n (target: object, propertyKey: string | symbol, descriptor?: PropertyDescriptor): void\n}\n\n/** The called form — `@Limit({ rpm: 10 })` — plus removal via `.off`. */\nexport interface ValuedRouteFlag<V> {\n (value: V): (target: object, propertyKey?: string | symbol) => void\n /**\n * Remove the flag on this route — `@Public.off` on a method drops what the\n * controller set.\n *\n * Applied bare, like the flag itself. Spelled as its own member rather than\n * `@Public(false)` so that `false` stays available as a real value: a flag\n * declared `boolean` can store it, and `flags.get()` can return it.\n */\n readonly off: BareRouteFlag\n readonly flagName: string\n}\n\n/**\n * A flag decorator.\n *\n * Bare application is available only when `true` is a valid value for the flag\n * — which is every flag until {@link KickRouteFlags} declares a value type for\n * it. Declare `'rate.limit': { rpm: number }` and `@RateLimit` on its own stops\n * compiling, because the value it would store is not the value the registry\n * promises readers.\n */\nexport type RouteFlag<V = true> = true extends V\n ? BareRouteFlag & ValuedRouteFlag<V>\n : ValuedRouteFlag<V>\n\n/**\n * True when the arguments look like a decorator application rather than a\n * value. The two forms are `@Public` (TS calls it for us) and `@Public(v)`\n * (we return the decorator).\n *\n * Discrimination has to be exact, because a value can be an object:\n * `@RateLimit({ rpm: 10 })` is a single object argument, and treating it as a\n * class-decorator target silently drops the flag.\n *\n * - method decorator → `(prototype, 'name', descriptor?)`, so arg 1 is a\n * string or symbol\n * - class decorator → `(constructor)`, a single *function* argument\n * - anything else → a value\n */\nfunction isDecoratorApplication(args: unknown[]): boolean {\n const [target, propertyKey] = args\n if (typeof propertyKey === 'string' || typeof propertyKey === 'symbol') return true\n return args.length === 1 && typeof target === 'function'\n}\n\n/**\n * Define a route flag.\n *\n * ```ts\n * export const Public = defineRouteFlag('auth.public')\n * export const RateLimit = defineRouteFlag<{ rpm: number }>('rate.limit')\n * ```\n *\n * Once {@link KickRouteFlags} declares the name, the value type comes from the\n * registry and the explicit generic is redundant:\n *\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface KickRouteFlags {\n * 'rate.limit': { rpm: number }\n * }\n * }\n *\n * const RateLimit = defineRouteFlag('rate.limit') // RouteFlag<{ rpm: number }>\n * ```\n */\nexport function defineRouteFlag<const N extends RouteFlagName>(\n name: N,\n): RouteFlag<RouteFlagValue<N, true>>\nexport function defineRouteFlag<V>(name: RouteFlagName): RouteFlag<V>\nexport function defineRouteFlag(name: string): RouteFlag<never> {\n // `!` is reserved: a test string starting with it means \"does NOT carry this\n // flag\", so a flag literally named '!x' would be indistinguishable from the\n // negation of 'x' — `exemptWhen: '!x'` would silently stop meaning what the\n // author wrote. Rejecting at definition is the only point where the two can\n // still be told apart.\n if (name.startsWith('!')) {\n throw new Error(\n `defineRouteFlag('${name}'): a flag name cannot start with '!' — that prefix is reserved ` +\n `for negated tests ('!${name.slice(1)}' means \"does not carry ${name.slice(1)}\"). ` +\n `Rename the flag.`,\n )\n }\n\n function apply(value: unknown, target: object, propertyKey?: string | symbol): void {\n const declaration: RouteFlagDeclaration = { name, value }\n if (propertyKey === undefined) {\n pushClassMeta<RouteFlagDeclaration>(METADATA.CLASS_FLAGS, target, declaration)\n return\n }\n // Method decorators receive the prototype; metadata helpers key off the\n // constructor so class + method lookups land in the same place.\n pushMethodMeta<RouteFlagDeclaration>(\n METADATA.METHOD_FLAGS,\n (target as { constructor: object }).constructor,\n String(propertyKey),\n declaration,\n )\n }\n\n const flag = (...args: unknown[]): unknown => {\n if (isDecoratorApplication(args)) {\n // Bare form: @Public\n apply(true, args[0] as object, args[1] as string | symbol | undefined)\n return undefined\n }\n // Called form: @Public(false) / @RateLimit({ rpm: 10 })\n const value = args[0]\n return (target: object, propertyKey?: string | symbol): void => {\n apply(value, target, propertyKey)\n }\n }\n\n Object.defineProperty(flag, 'flagName', { value: name, enumerable: true })\n // `.off` is the removal decorator: bare-applicable, and the only way to drop\n // an inherited flag now that `false` is an ordinary value.\n Object.defineProperty(flag, 'off', {\n value: (target: object, propertyKey?: string | symbol): void => {\n apply(FLAG_UNSET, target, propertyKey)\n },\n enumerable: true,\n })\n return flag as unknown as RouteFlag<never>\n}\n\n/**\n * Resolve the flags in force for one route.\n *\n * Precedence is method > class, matching the contributor pipeline's top two\n * levels. Within a level the last declaration wins (decorators apply bottom-up,\n * so the one nearest the handler is applied last).\n *\n * A resolved `false` deletes the key rather than storing it — see the module\n * docblock.\n */\nexport function resolveRouteFlags(\n classDeclarations: readonly RouteFlagDeclaration[],\n methodDeclarations: readonly RouteFlagDeclaration[],\n): RouteFlags {\n const resolved = new Map<string, unknown>()\n\n // Lowest precedence first, so higher levels overwrite.\n for (const { name, value } of [...classDeclarations, ...methodDeclarations]) {\n if (value === FLAG_UNSET) {\n resolved.delete(name)\n continue\n }\n resolved.set(name, value)\n }\n\n // The map is built from declarations, so its value types are whatever the\n // registry says they are — this cast is the one place that knowledge crosses\n // from runtime data into the type system.\n return resolved as RouteFlags\n}\n\n/**\n * The resolved flags for a route.\n *\n * A `ReadonlyMap` whose `has` is narrowed to known names and whose `get`\n * returns the declared value type — so `flags.get('rate.limit')` is\n * `{ rpm: number } | undefined` rather than `unknown`, once the registry\n * declares it. Until then it behaves like `ReadonlyMap<string, unknown>`.\n */\nexport interface RouteFlags extends ReadonlyMap<string, unknown> {\n has(name: RouteFlagName): boolean\n get<K extends RouteFlagName>(name: K): RouteFlagValue<K> | undefined\n}\n\n/**\n * Argument handed to a flag predicate.\n *\n * `route` is undefined only where flags are consulted outside a matched route,\n * which today means a non-HTTP transport running the same contributor pipeline.\n */\nexport interface RouteFlagContext {\n readonly flags: RouteFlags\n readonly route?: {\n readonly method: string\n readonly path: string\n readonly controller?: unknown\n readonly handlerName?: string\n }\n}\n\n/**\n * A predicate form of a flag test, for conditions a single name cannot express:\n * two flags together, a flag's *value*, a path or controller check, an env\n * switch.\n *\n * ```ts\n * skipWhen: ({ flags }) => flags.has('auth.public') || flags.has('internal'),\n * skipWhen: ({ flags }) => (flags.get('rate.limit') as { rpm: number })?.rpm === 0,\n * skipWhen: ({ route }) => route?.path.startsWith('/webhooks') ?? false,\n * ```\n *\n * Keep predicates pure and cheap — they run per request, per contributor.\n */\nexport type RouteFlagPredicate = (ctx: RouteFlagContext) => boolean\n\n/**\n * How a consumer names the routes it cares about.\n *\n * A bare name for the common case, a list when several flags mean the same\n * thing (matched **any-of**), a predicate for everything else — all-of, a\n * flag's value, the route's path.\n *\n * ```ts\n * skipWhen: 'auth.public'\n * skipWhen: ['auth.public', 'health.probe'] // either one\n * skipWhen: ({ flags }) => flags.has('a') && flags.has('b') // both\n * ```\n */\n/**\n * A flag name with `!` in front — matches routes that do **not** carry it.\n *\n * Narrows with the registry like the positive form: once `KickRouteFlags`\n * declares `'auth.public'`, this is `'!auth.public' | …`, so a misspelt\n * negation is a compile error too.\n */\nexport type NegatedRouteFlagName = `!${RouteFlagName}`\n\n/**\n * How a consumer names the routes it cares about.\n *\n * - `'auth.public'` — carries the flag\n * - `'!auth.public'` — does **not** carry it\n * - `['a', 'b']` — carries **any** of them\n * - `['!a', '!b']` — carries **none** of them\n * - a predicate — anything else: all-of, a flag's value, the route's path\n *\n * Lists are single-polarity by construction: `['a', '!b']` matches neither\n * array member of this union, so it is a compile error. Mixed polarity has no\n * reading everyone agrees on — under any-of it means \"a present OR b absent\",\n * which most readers parse as \"and\" — and a predicate says it unambiguously.\n */\nexport type RouteFlagTest =\n | RouteFlagName\n | NegatedRouteFlagName\n | readonly RouteFlagName[]\n | readonly NegatedRouteFlagName[]\n | RouteFlagPredicate\n\n/**\n * Evaluate a {@link RouteFlagTest} against a route's resolved flags.\n *\n * Shared by every consumer that can be exempted per route — the contributor\n * runner, `csrfGuard`, `rateLimitGuard` — so \"which routes does this apply to\"\n * means the same thing everywhere, and a list is any-of in all of them.\n */\nexport function matchesFlagTest(\n test: RouteFlagTest,\n flags: RouteFlags | undefined,\n route?: RouteFlagContext['route'],\n): boolean {\n const resolved = flags ?? EMPTY_FLAGS\n if (typeof test === 'function') return test({ flags: resolved, route })\n\n if (typeof test === 'string') {\n return isNegated(test)\n ? !resolved.has(stripNegation(test))\n : resolved.has(test as RouteFlagName)\n }\n\n if (test.length === 0) return false\n\n // Single polarity per list — the type forbids mixing, and this guards the\n // untyped callers (plain JS, a value read from config) that the type cannot.\n const negated = isNegated(test[0])\n if (test.some((name) => isNegated(name) !== negated)) {\n throw new Error(\n `Route flag test mixes polarities: [${test.map((n) => `'${n}'`).join(', ')}]. ` +\n `A list is either all names (\"carries any of these\") or all negated ` +\n `(\"carries none of these\") — for anything else, pass a predicate.`,\n )\n }\n\n // Positive list is any-of; negated list is its complement, none-of. The two\n // read as opposites, which is what makes flipping every entry do what a\n // reader expects.\n return negated\n ? test.every((name) => !resolved.has(stripNegation(name)))\n : test.some((name) => resolved.has(name as RouteFlagName))\n}\n\n/**\n * Reject a mixed-polarity list at construction time.\n *\n * A list like `['auth.public', '!metered']` is a static mistake, so it fails\n * where it is written — when the contributor or guard is built — rather than\n * on the first request that reaches it. Same reasoning as the contributor\n * pipeline failing a dependency cycle at boot.\n *\n * Call from every factory that accepts a {@link RouteFlagTest}; the check in\n * {@link matchesFlagTest} stays as a backstop for direct callers.\n */\nexport function assertFlagTest(test: RouteFlagTest, site: string): void {\n if (typeof test === 'function' || typeof test === 'string' || test.length === 0) return\n const negated = isNegated(test[0])\n if (test.some((name) => isNegated(name) !== negated)) {\n throw new Error(\n `${site}: route flag test mixes polarities: [${test.map((n) => `'${n}'`).join(', ')}]. ` +\n `A list is either all names (\"carries any of these\") or all negated ` +\n `(\"carries none of these\") — for anything else, pass a predicate.`,\n )\n }\n}\n\nconst isNegated = (name: string): boolean => name.startsWith('!')\nconst stripNegation = (name: string): RouteFlagName => name.slice(1) as RouteFlagName\n\nconst EMPTY_FLAGS: RouteFlags = new Map()\n\n/**\n * Where the matched route is stashed for `ctx.route` to read.\n *\n * It lives on the **request object**, not on the RequestContext: the Express\n * runtime constructs a fresh `RequestContext` per middleware, so anything\n * written to one ctx is invisible to the next and to the handler. Fastify and\n * h3 build a single ctx per request and would not have shown the difference —\n * the cross-runtime test is what surfaced it.\n *\n * `Symbol.for` rather than a private symbol so a duplicated module instance\n * (pnpm hoisting, a bundled copy) still reads the same slot.\n */\nexport const ROUTE_SLOT: unique symbol = Symbol.for('kick.route') as never\n\n/**\n * Resolve the flags in force for one handler, straight from the controller\n * class — the same method-over-class result `buildRouteTable` computes.\n *\n * For consumers that see a controller and a method name rather than a live\n * request: an adapter's `onRouteMount`, an OpenAPI generator, a DevTools route\n * listing. Inside a request, read `ctx.route.flags` instead — it is already\n * resolved.\n *\n * ```ts\n * // Mark flagged routes public in the OpenAPI spec:\n * SwaggerAdapter({\n * bearerAuth: true,\n * securityResolver: ({ controllerClass, handlerName }) =>\n * getRouteFlags(controllerClass, handlerName).has('auth.public') ? null : undefined,\n * })\n * ```\n */\nexport function getRouteFlags(controllerClass: object, handlerName: string): RouteFlags {\n return resolveRouteFlags(\n getClassFlagDeclarations(controllerClass),\n getMethodFlagDeclarations(controllerClass, handlerName),\n )\n}\n\n/** Read the class-level declarations off a controller. */\nexport function getClassFlagDeclarations(controllerClass: object): RouteFlagDeclaration[] {\n return getClassMeta<RouteFlagDeclaration[]>(METADATA.CLASS_FLAGS, controllerClass, [])\n}\n\n/** Read the method-level declarations off a controller method. */\nexport function getMethodFlagDeclarations(\n controllerClass: object,\n handlerName: string,\n): RouteFlagDeclaration[] {\n return getMethodMeta<RouteFlagDeclaration[]>(\n METADATA.METHOD_FLAGS,\n controllerClass,\n handlerName,\n [],\n )\n}\n"],"mappings":";;;;;;;;;;6JAoFA,MAAM,WAA4B,OAAO,gBAAgB,EA8EzD,SAAS,uBAAuB,KAA0B,CACxD,GAAM,CAAC,OAAQ,aAAe,KAE9B,OADI,OAAO,aAAgB,UAAY,OAAO,aAAgB,UACvD,KAAK,SAAW,GAAK,OAAO,QAAW,UAChD,CA2BA,SAAgB,gBAAgB,KAAgC,CAM9D,GAAI,KAAK,WAAW,GAAG,EACrB,MAAU,MACR,oBAAoB,KAAK,uFACC,KAAK,MAAM,CAAC,EAAE,0BAA0B,KAAK,MAAM,CAAC,EAAE,qBAElF,EAGF,SAAS,MAAM,MAAgB,OAAgB,YAAqC,CAClF,IAAM,YAAoC,CAAE,KAAM,KAAM,EACxD,GAAI,cAAgB,IAAA,GAAW,CAC7B,cAAoC,SAAS,YAAa,OAAQ,WAAW,EAC7E,MACF,CAGA,eACE,SAAS,aACR,OAAmC,YACpC,OAAO,WAAW,EAClB,WACF,CACF,CAEA,IAAM,MAAQ,GAAG,OAA6B,CAC5C,GAAI,uBAAuB,IAAI,EAAG,CAEhC,MAAM,GAAM,KAAK,GAAc,KAAK,EAAiC,EACrE,MACF,CAEA,IAAM,MAAQ,KAAK,GACnB,OAAQ,OAAgB,cAAwC,CAC9D,MAAM,MAAO,OAAQ,WAAW,CAClC,CACF,EAWA,OATA,OAAO,eAAe,KAAM,WAAY,CAAE,MAAO,KAAM,WAAY,EAAK,CAAC,EAGzE,OAAO,eAAe,KAAM,MAAO,CACjC,OAAQ,OAAgB,cAAwC,CAC9D,MAAM,WAAY,OAAQ,WAAW,CACvC,EACA,WAAY,EACd,CAAC,EACM,IACT,CAYA,SAAgB,kBACd,kBACA,mBACY,CACZ,IAAM,SAAW,IAAI,IAGrB,IAAK,GAAM,CAAE,KAAM,QAAW,CAAC,GAAG,kBAAmB,GAAG,kBAAkB,EAAG,CAC3E,GAAI,QAAU,WAAY,CACxB,SAAS,OAAO,IAAI,EACpB,QACF,CACA,SAAS,IAAI,KAAM,KAAK,CAC1B,CAKA,OAAO,QACT,CAgGA,SAAgB,gBACd,KACA,MACA,MACS,CACT,IAAM,SAAW,OAAS,YAC1B,GAAI,OAAO,MAAS,WAAY,OAAO,KAAK,CAAE,MAAO,SAAU,KAAM,CAAC,EAEtE,GAAI,OAAO,MAAS,SAClB,OAAO,UAAU,IAAI,EACjB,CAAC,SAAS,IAAI,cAAc,IAAI,CAAC,EACjC,SAAS,IAAI,IAAqB,EAGxC,GAAI,KAAK,SAAW,EAAG,MAAO,GAI9B,IAAM,QAAU,UAAU,KAAK,EAAE,EACjC,GAAI,KAAK,KAAM,MAAS,UAAU,IAAI,IAAM,OAAO,EACjD,MAAU,MACR,sCAAsC,KAAK,IAAK,GAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,uIAG7E,EAMF,OAAO,QACH,KAAK,MAAO,MAAS,CAAC,SAAS,IAAI,cAAc,IAAI,CAAC,CAAC,EACvD,KAAK,KAAM,MAAS,SAAS,IAAI,IAAqB,CAAC,CAC7D,CAaA,SAAgB,eAAe,KAAqB,KAAoB,CACtE,GAAI,OAAO,MAAS,YAAc,OAAO,MAAS,UAAY,KAAK,SAAW,EAAG,OACjF,IAAM,QAAU,UAAU,KAAK,EAAE,EACjC,GAAI,KAAK,KAAM,MAAS,UAAU,IAAI,IAAM,OAAO,EACjD,MAAU,MACR,GAAG,KAAK,uCAAuC,KAAK,IAAK,GAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,uIAGtF,CAEJ,CAEA,MAAM,UAAa,MAA0B,KAAK,WAAW,GAAG,EAC1D,cAAiB,MAAgC,KAAK,MAAM,CAAC,EAE7D,YAA0B,IAAI,IAcvB,WAA4B,OAAO,IAAI,YAAY,EAoBhE,SAAgB,cAAc,gBAAyB,YAAiC,CACtF,OAAO,kBACL,yBAAyB,eAAe,EACxC,0BAA0B,gBAAiB,WAAW,CACxD,CACF,CAGA,SAAgB,yBAAyB,gBAAiD,CACxF,OAAO,aAAqC,SAAS,YAAa,gBAAiB,CAAC,CAAC,CACvF,CAGA,SAAgB,0BACd,gBACA,YACwB,CACxB,OAAO,cACL,SAAS,aACT,gBACA,YACA,CAAC,CACH,CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router-builder-CxRFEM9j.d.mts","names":[],"sources":["../src/http/router-builder.ts"],"mappings":";;;;iBAoCgB,+BAA+B,kBAAkB;;;;;;;;;;;iBAcjD,kBACd,UAAU,qBACV,gBACA,kBACA;UAUe;;;;;;;EAOf,2BAA2B;;;;;;;;;;;;;;;;;;;;;;iBAuBb,gBACd,sBACA,UAAS,qBACR"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @forinda/kickjs v8.2.1
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) Felix Orinda
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* LICENSE file in the root directory of this source tree.
|
|
8
|
-
*
|
|
9
|
-
* @license MIT
|
|
10
|
-
*/
|
|
11
|
-
import{METADATA}from"./core/interfaces.mjs";import{o as duplicateRouteError,t as Container}from"./container-CZPSM8p-.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"./metadata-C-9GVv_0.mjs";import{a as getMethodFlagDeclarations,c as resolveRouteFlags,i as getClassFlagDeclarations}from"./route-flag-DTfTiScc.mjs";import{i as MissingContributorError,n as DuplicateContributorError,t as ContributorCycleError}from"./context-errors-CkXOg4qD.mjs";import{t as runContributors}from"./contributor-runner-r-_idhMs.mjs";const PRECEDENCE_ORDER=[`method`,`class`,`module`,`adapter`,`global`];function buildPipeline(sources,options={}){let route=options.route,bySource=new Map;for(let src of PRECEDENCE_ORDER)bySource.set(src,[]);for(let entry of sources)bySource.get(entry.source).push(entry);let winners=new Map;for(let src of PRECEDENCE_ORDER){let rows=bySource.get(src),keysAtThisLevel=new Map;for(let row of rows){let key=row.registration.key,existing=keysAtThisLevel.get(key)??[];existing.push(row),keysAtThisLevel.set(key,existing)}for(let[key,rowsForKey]of keysAtThisLevel){if(rowsForKey.length>1)throw new DuplicateContributorError(key,rowsForKey.map((r,i)=>r.label??`${src}#${i}`));winners.has(key)||winners.set(key,rowsForKey[0])}}let survivors=[...winners.values()].map(r=>r.registration),allKeys=new Set(survivors.map(r=>r.key));for(let reg of survivors)for(let dep of reg.dependsOn)if(!allKeys.has(dep))throw new MissingContributorError(dep,reg.key,route,reg.definedAt);let sorted=topoSort(survivors,route);return Object.freeze({contributors:Object.freeze(sorted),keys:allKeys})}function topoSort(registrations,route){let indegree=new Map,dependents=new Map,byKey=new Map;for(let reg of registrations)indegree.set(reg.key,reg.dependsOn.length),byKey.set(reg.key,reg);for(let reg of registrations)for(let dep of reg.dependsOn){let list=dependents.get(dep)??[];list.push(reg),dependents.set(dep,list)}let queue=[];for(let reg of registrations)indegree.get(reg.key)===0&&queue.push(reg);let sorted=[];for(;queue.length>0;){let node=queue.shift();sorted.push(node);for(let dependent of dependents.get(node.key)??[]){let next=(indegree.get(dependent.key)??0)-1;indegree.set(dependent.key,next),next===0&&queue.push(dependent)}}if(sorted.length!==registrations.length)throw new ContributorCycleError(reconstructCycle(registrations.filter(r=>(indegree.get(r.key)??0)>0),byKey),route);return sorted}function reconstructCycle(residual,byKey){let residualKeys=new Set(residual.map(r=>r.key)),start=residual[0].key,path=[start],seen=new Set([start]),cursor=start;for(;;){let nextDep=byKey.get(cursor).dependsOn.find(d=>residualKeys.has(d));if(nextDep===void 0)break;if(seen.has(nextDep)){let cycleStart=path.indexOf(nextDep);return[...path.slice(cycleStart),nextDep]}path.push(nextDep),seen.add(nextDep),cursor=nextDep}return path}let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function assertRouteUnique(registry,method,fullPath,owner){let key=`${method.toUpperCase()} ${fullPath.replace(/:[^/]+/g,`:*`)}`,prior=registry.get(key);if(prior!==void 0)throw duplicateRouteError(method.toUpperCase(),fullPath,owner,prior);registry.set(key,owner)}function buildRouteTable(controllerClass,options={}){let container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]),classFlagDeclarations=getClassFlagDeclarations(controllerClass),entries=[];for(let route of routes){let method=route.method.toUpperCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName),methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]),flags=resolveRouteFlags(classFlagDeclarations,getMethodFlagDeclarations(controllerClass,route.handlerName)),contributorRunner=null;if(classContributors.length>0||methodContributors.length>0||externalSources.length>0){let pipeline=buildPipeline([...methodContributors.map((registration,i)=>({source:`method`,registration,label:`${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`})),...classContributors.map((registration,i)=>({source:`class`,registration,label:`${controllerClass.name}.@class#${i}(${registration.key})`})),...externalSources],{route:`${method} ${fullPath}`});contributorRunner=ctx=>runContributors({pipeline,ctx,container,flags})}let handler=ctx=>container.resolve(controllerClass)[route.handlerName](ctx);entries.push({method,path:fullPath,middlewares:[...classMiddlewares,...methodMiddlewares],contributorRunner,handler,meta:{controller:controllerClass,handlerName:route.handlerName,validation:route.validation,upload:fileUploadConfig,flags}})}return entries}export{buildPipeline as i,assertRouteUnique as n,buildRouteTable as r,_setExternalContributorSources as t};
|
|
12
|
-
//# sourceMappingURL=router-builder-DP6CbI7S.mjs.map
|