@forinda/kickjs 8.6.0 → 9.0.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/README.md +27 -164
- package/dist/{adapter-2bnoYEPX.d.mts → adapter-DhjoIYVI.d.mts} +122 -106
- package/dist/adapter-DhjoIYVI.d.mts.map +1 -0
- package/dist/application-BnFA6J97.mjs +12 -0
- package/dist/application-BnFA6J97.mjs.map +1 -0
- package/dist/{application-_QOiAVx2.d.mts → application-DqD58rhF.d.mts} +104 -15
- package/dist/application-DqD58rhF.d.mts.map +1 -0
- package/dist/bootstrap-K1PcxSTg.mjs +12 -0
- package/dist/bootstrap-K1PcxSTg.mjs.map +1 -0
- package/dist/cache-C0qL0NSD.d.mts.map +1 -1
- package/dist/{client-ip-DF_smNX7.mjs → client-ip-DAnYuS54.mjs} +2 -2
- package/dist/{client-ip-DF_smNX7.mjs.map → client-ip-DAnYuS54.mjs.map} +1 -1
- package/dist/config/index.d.mts +2 -2
- package/dist/config/index.mjs +2 -2
- package/dist/config-16yw-Mxk.mjs +12 -0
- package/dist/config-16yw-Mxk.mjs.map +1 -0
- package/dist/container-Dw45sa0Y.mjs +132 -0
- package/dist/container-Dw45sa0Y.mjs.map +1 -0
- package/dist/context-DlIdXA6I.mjs +12 -0
- package/dist/context-DlIdXA6I.mjs.map +1 -0
- package/dist/{context-errors-Ch0DuUQo.mjs → context-errors-BqOFD7UE.mjs} +2 -2
- package/dist/{context-errors-Ch0DuUQo.mjs.map → context-errors-BqOFD7UE.mjs.map} +1 -1
- package/dist/{contributor-runner-DujKZy4N.mjs → contributor-runner-DLvBA96K.mjs} +3 -3
- package/dist/{contributor-runner-DujKZy4N.mjs.map → contributor-runner-DLvBA96K.mjs.map} +1 -1
- package/dist/core/adapter.d.mts +2 -2
- package/dist/core/adapter.mjs +1 -1
- package/dist/core/app-module.mjs +1 -1
- package/dist/core/cache.mjs +2 -2
- package/dist/core/cache.mjs.map +1 -1
- package/dist/core/container.mjs +2 -2
- package/dist/core/cron.d.mts +2 -2
- package/dist/core/cron.mjs +2 -2
- package/dist/core/cron.mjs.map +1 -1
- package/dist/core/decorators.d.mts +1 -1
- package/dist/core/decorators.mjs +2 -2
- package/dist/core/decorators.mjs.map +1 -1
- 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-CCwlN0Gn.mjs +12 -0
- package/dist/{core-y8QbzHM5.mjs.map → core-CCwlN0Gn.mjs.map} +1 -1
- package/dist/cron-BTGytNg-.d.mts +122 -0
- package/dist/cron-BTGytNg-.d.mts.map +1 -0
- package/dist/{csrf-RP5hgc0c.mjs → csrf-B8CGeOlP.mjs} +3 -3
- package/dist/csrf-B8CGeOlP.mjs.map +1 -0
- package/dist/{csrf-C4MwaMp4.d.mts → csrf-COFjRrUz.d.mts} +2 -2
- package/dist/{csrf-C4MwaMp4.d.mts.map → csrf-COFjRrUz.d.mts.map} +1 -1
- package/dist/{decorate-D85rEnU-.mjs → decorate-CahyjuWW.mjs} +1 -1
- package/dist/{decorators-mg0qJ6Qc.d.mts → decorators-DOcWsXci.d.mts} +2 -2
- package/dist/{decorators-mg0qJ6Qc.d.mts.map → decorators-DOcWsXci.d.mts.map} +1 -1
- package/dist/{define-adapter-C1Db6xfO.mjs → define-adapter-Ckc2P-wH.mjs} +2 -2
- package/dist/{define-adapter-C1Db6xfO.mjs.map → define-adapter-Ckc2P-wH.mjs.map} +1 -1
- package/dist/{disposables-CEAxve3A.mjs → disposables-DnvT8DXr.mjs} +2 -2
- package/dist/{disposables-CEAxve3A.mjs.map → disposables-DnvT8DXr.mjs.map} +1 -1
- package/dist/{env-BelmDj19.mjs → env-DMAdjj8Z.mjs} +4 -4
- package/dist/env-DMAdjj8Z.mjs.map +1 -0
- package/dist/{error-handler-C1uWVGVY.d.mts → error-handler-DiueQHes.d.mts} +2 -2
- package/dist/{error-handler-C1uWVGVY.d.mts.map → error-handler-DiueQHes.d.mts.map} +1 -1
- package/dist/{handler-DHlakr0v.mjs → handler-2CbWK63p.mjs} +3 -3
- package/dist/handler-2CbWK63p.mjs.map +1 -0
- package/dist/http/application.d.mts +2 -2
- 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/error-handler.mjs.map +1 -1
- package/dist/http/middleware/helmet.mjs +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.d.mts +1 -1
- package/dist/http/middleware/session.mjs +2 -2
- package/dist/http/middleware/session.mjs.map +1 -1
- 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 +3 -3
- package/dist/http/middleware/upload.mjs.map +1 -1
- 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.d.mts.map +1 -1
- package/dist/http/runtimes/fastify.mjs +3 -3
- package/dist/http/runtimes/fastify.mjs.map +1 -1
- package/dist/http/runtimes/h3-web.d.mts +1 -1
- package/dist/http/runtimes/h3-web.d.mts.map +1 -1
- package/dist/http/runtimes/h3-web.mjs +2 -2
- package/dist/http/runtimes/h3-web.mjs.map +1 -1
- package/dist/http/runtimes/h3.d.mts +1 -6
- package/dist/http/runtimes/h3.d.mts.map +1 -1
- package/dist/http/runtimes/h3.mjs +2 -2
- package/dist/http/runtimes/h3.mjs.map +1 -1
- package/dist/{index-Btz1W0X5.d.mts → index-CDn-p3tq.d.mts} +27 -18
- package/dist/index-CDn-p3tq.d.mts.map +1 -0
- package/dist/index.d.mts +12 -12
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/jobs-C8_ESbx0.mjs +12 -0
- package/dist/jobs-C8_ESbx0.mjs.map +1 -0
- package/dist/{kv-stores-DUFcps01.mjs → kv-stores-BhUmirED.mjs} +3 -3
- package/dist/{kv-stores-DUFcps01.mjs.map → kv-stores-BhUmirED.mjs.map} +1 -1
- package/dist/{kv-stores-Dx5RvIaP.d.mts → kv-stores-CetFF9N_.d.mts} +135 -5
- package/dist/kv-stores-CetFF9N_.d.mts.map +1 -0
- package/dist/{metadata-B8_KXybT.mjs → metadata-BQG1MmqL.mjs} +2 -2
- package/dist/{metadata-B8_KXybT.mjs.map → metadata-BQG1MmqL.mjs.map} +1 -1
- package/dist/{module-registry-BFrJ1PQ4.mjs → module-registry-BXnvRtH9.mjs} +2 -2
- package/dist/{module-registry-BFrJ1PQ4.mjs.map → module-registry-BXnvRtH9.mjs.map} +1 -1
- package/dist/observers-BI8H_g45.mjs +12 -0
- package/dist/observers-BI8H_g45.mjs.map +1 -0
- package/dist/{query-CC7o6I5K.mjs → query-CRTeiKUv.mjs} +2 -2
- package/dist/{query-CC7o6I5K.mjs.map → query-CRTeiKUv.mjs.map} +1 -1
- package/dist/{respond-B-EDymxF.mjs → respond-BAxhPjRj.mjs} +2 -2
- package/dist/{respond-B-EDymxF.mjs.map → respond-BAxhPjRj.mjs.map} +1 -1
- package/dist/route-flag-B1lQht6v.d.mts.map +1 -1
- package/dist/{route-flag-B4sNP7zH.mjs → route-flag-GipNvQ4O.mjs} +3 -3
- package/dist/route-flag-GipNvQ4O.mjs.map +1 -0
- package/dist/{route-policy-CDlMbl1b.mjs → route-policy-DtGR4sar.mjs} +2 -2
- package/dist/route-policy-DtGR4sar.mjs.map +1 -0
- package/dist/{router-builder-C6gmK0nZ.d.mts → router-builder-BSBSYgYW.d.mts} +3 -3
- package/dist/{router-builder-C6gmK0nZ.d.mts.map → router-builder-BSBSYgYW.d.mts.map} +1 -1
- package/dist/{router-builder-cTsA2XtO.mjs → router-builder-C2nnS0VJ.mjs} +3 -3
- package/dist/{router-builder-cTsA2XtO.mjs.map → router-builder-C2nnS0VJ.mjs.map} +1 -1
- package/dist/{runtime-C-7Yhs7a.mjs → runtime-DGzigyO2.mjs} +3 -3
- package/dist/runtime-DGzigyO2.mjs.map +1 -0
- package/dist/{session-BHgRyfqZ.d.mts → session-4Tb6q0Ny.d.mts} +11 -1
- package/dist/session-4Tb6q0Ny.d.mts.map +1 -0
- package/dist/{upload-TW2ZuECF.d.mts → upload-DjhnyHHm.d.mts} +3 -15
- package/dist/{upload-TW2ZuECF.d.mts.map → upload-DjhnyHHm.d.mts.map} +1 -1
- package/dist/{upload-config-CoE6BzM4.mjs → upload-config-CcoZp7xA.mjs} +2 -2
- package/dist/{upload-config-CoE6BzM4.mjs.map → upload-config-CcoZp7xA.mjs.map} +1 -1
- package/dist/web.d.mts +39 -7
- package/dist/web.d.mts.map +1 -1
- package/dist/web.mjs +2 -2
- package/dist/web.mjs.map +1 -1
- package/package.json +15 -10
- package/dist/adapter-2bnoYEPX.d.mts.map +0 -1
- package/dist/application-DZiG1QCw.mjs +0 -12
- package/dist/application-DZiG1QCw.mjs.map +0 -1
- package/dist/application-_QOiAVx2.d.mts.map +0 -1
- package/dist/bootstrap-BEbka3UV.mjs +0 -12
- package/dist/bootstrap-BEbka3UV.mjs.map +0 -1
- package/dist/config-CIOXZmLS.mjs +0 -12
- package/dist/config-CIOXZmLS.mjs.map +0 -1
- package/dist/container-DF8FRoY8.mjs +0 -117
- package/dist/container-DF8FRoY8.mjs.map +0 -1
- package/dist/context-BTqeZH1A.mjs +0 -12
- package/dist/context-BTqeZH1A.mjs.map +0 -1
- package/dist/core-y8QbzHM5.mjs +0 -12
- package/dist/cron-DOOtTY4q.d.mts +0 -30
- package/dist/cron-DOOtTY4q.d.mts.map +0 -1
- package/dist/csrf-RP5hgc0c.mjs.map +0 -1
- package/dist/env-BelmDj19.mjs.map +0 -1
- package/dist/handler-DHlakr0v.mjs.map +0 -1
- package/dist/index-Btz1W0X5.d.mts.map +0 -1
- package/dist/kv-stores-Dx5RvIaP.d.mts.map +0 -1
- package/dist/route-flag-B4sNP7zH.mjs.map +0 -1
- package/dist/route-policy-CDlMbl1b.mjs.map +0 -1
- package/dist/runtime-C-7Yhs7a.mjs.map +0 -1
- package/dist/session-BHgRyfqZ.d.mts.map +0 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
|
|
2
2
|
import { s as InjectionToken, t as Container } from "./container-Dpvd66Yt.mjs";
|
|
3
|
-
import "./index-
|
|
3
|
+
import "./index-CDn-p3tq.mjs";
|
|
4
|
+
import { D as RequestContext, M as AdapterFactory, S as RuntimeResponse, a as KickServer, c as ActiveRuntime, dt as KickPlugin, ft as ModuleRegistry, g as RouteEntry, it as ModuleFactory, p as HttpRuntime, r as AppAdapter, x as RuntimeCapabilities } from "./adapter-DhjoIYVI.mjs";
|
|
4
5
|
import { p as RouteFlags, s as RouteFlagDeclarations } from "./route-flag-B1lQht6v.mjs";
|
|
5
6
|
import { C as MetaValue, c as ContextDecorator, h as DepValue, m as ContributorRegistrations, r as AppModuleEntry, u as ContextDecoratorSpec, x as ContextMetaKey } from "./app-module-DCA65GZZ.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import { t as BuildRoutesOptions } from "./router-builder-
|
|
7
|
+
import { l as Reply, p as PlatformContext } from "./kv-stores-CetFF9N_.mjs";
|
|
8
|
+
import "./session-4Tb6q0Ny.mjs";
|
|
9
|
+
import { t as BuildRoutesOptions } from "./router-builder-BSBSYgYW.mjs";
|
|
9
10
|
import "./request-id-CVzJ1uHD.mjs";
|
|
10
11
|
import "./validate-DMYpUnrf.mjs";
|
|
11
|
-
import "./error-handler-
|
|
12
|
-
import "./csrf-
|
|
12
|
+
import "./error-handler-DiueQHes.mjs";
|
|
13
|
+
import "./csrf-COFjRrUz.mjs";
|
|
13
14
|
import "./helmet-C2QEU6S-.mjs";
|
|
14
15
|
import "./cors-CsibNTQ6.mjs";
|
|
15
16
|
import "./rate-limit-Bwed3jOV.mjs";
|
|
16
|
-
import "./
|
|
17
|
-
import "./upload-TW2ZuECF.mjs";
|
|
17
|
+
import "./upload-DjhnyHHm.mjs";
|
|
18
18
|
import http, { IncomingMessage, ServerResponse } from "node:http";
|
|
19
|
+
import tls from "node:tls";
|
|
19
20
|
import { Express, NextFunction, Request as Request$1, RequestHandler, Response as Response$1, Router } from "express";
|
|
20
21
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
21
22
|
//#region src/core/disposables.d.ts
|
|
@@ -413,6 +414,30 @@ declare function bindRoutePolicy<T extends object>(handler: T, receive: (table:
|
|
|
413
414
|
/** Hand the table to a middleware if it declared the slot. */
|
|
414
415
|
declare function offerRoutePolicy(handler: unknown, table: RoutePolicyTable): void;
|
|
415
416
|
//#endregion
|
|
417
|
+
//#region src/http/cron-adapter.d.ts
|
|
418
|
+
/** Set by the cluster primary on the one worker that runs `KickCronAdapter` jobs. */
|
|
419
|
+
declare const CRON_WORKER_ENV = "KICK_CRON_WORKER";
|
|
420
|
+
interface CronAdapterOptions {
|
|
421
|
+
/** Off for processes that should run no jobs (replicas, one-off scripts). Default `true`. */
|
|
422
|
+
enabled?: boolean;
|
|
423
|
+
}
|
|
424
|
+
declare const KickCronAdapter: AdapterFactory<CronAdapterOptions, unknown>;
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/http/tracing.d.ts
|
|
427
|
+
declare const HANDLER_CHANNEL = "kickjs:handler";
|
|
428
|
+
declare const ERROR_CHANNEL = "kickjs:error";
|
|
429
|
+
declare const RESPONSE_CHANNEL = "kickjs:response";
|
|
430
|
+
/** What subscribers to `kickjs:handler` receive. */
|
|
431
|
+
interface HandlerTraceContext {
|
|
432
|
+
method: string;
|
|
433
|
+
/** The route pattern, e.g. `/api/v1/users/:id` — the span name APMs want. */
|
|
434
|
+
route: string;
|
|
435
|
+
controller?: string;
|
|
436
|
+
handler?: string;
|
|
437
|
+
requestId?: string;
|
|
438
|
+
ctx: RequestContext;
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
416
441
|
//#region src/http/bootstrap.d.ts
|
|
417
442
|
/**
|
|
418
443
|
* Bootstrap a KickJS application.
|
|
@@ -442,10 +467,14 @@ interface KickHandler {
|
|
|
442
467
|
/**
|
|
443
468
|
* Web-standard handler: `fetch(Request) → Response`. Netlify Functions,
|
|
444
469
|
* Vercel's fetch export, and anything else that speaks `Request`.
|
|
470
|
+
*
|
|
471
|
+
* Pass the platform context (Netlify's `context`, `{ waitUntil }` from
|
|
472
|
+
* `@vercel/functions`) so work handed to `ctx.waitUntil()` finishes after
|
|
473
|
+
* the response instead of being frozen with the instance.
|
|
445
474
|
*/
|
|
446
|
-
fetch(request: Request): Promise<Response>;
|
|
475
|
+
fetch(request: Request, platform?: PlatformContext): Promise<Response>;
|
|
447
476
|
/** Node handler: `(req, res)`. Vercel Node functions, `http.createServer`. */
|
|
448
|
-
node(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
477
|
+
node(req: IncomingMessage, res: ServerResponse, platform?: PlatformContext): Promise<void>;
|
|
449
478
|
/** Set the app up now instead of on the first request (warm-up). */
|
|
450
479
|
ready(): Promise<Application>;
|
|
451
480
|
/** Shut adapters down and stop the internal forwarding server, if one started. */
|
|
@@ -480,7 +509,7 @@ interface KickHandler {
|
|
|
480
509
|
* export const handler = createHandler({ modules })
|
|
481
510
|
*
|
|
482
511
|
* // netlify/functions/api.mjs
|
|
483
|
-
* export default (request) => handler.fetch(request)
|
|
512
|
+
* export default (request, context) => handler.fetch(request, context)
|
|
484
513
|
* export const config = { path: '/api/*' }
|
|
485
514
|
* ```
|
|
486
515
|
*/
|
|
@@ -970,6 +999,13 @@ interface ShutdownOptions {
|
|
|
970
999
|
*/
|
|
971
1000
|
closeServer?: boolean;
|
|
972
1001
|
}
|
|
1002
|
+
/** See {@link ApplicationOptions.server}. */
|
|
1003
|
+
interface ServerOptions {
|
|
1004
|
+
/** TLS credentials and options (`key`, `cert`, `ca`, `pfx`, `passphrase`, …). */
|
|
1005
|
+
tls?: tls.SecureContextOptions & tls.TlsOptions;
|
|
1006
|
+
/** Serve HTTP/2 with HTTP/1.1 fallback. Requires `tls` and an HTTP/2-capable runtime. */
|
|
1007
|
+
http2?: boolean;
|
|
1008
|
+
}
|
|
973
1009
|
interface ApplicationOptions {
|
|
974
1010
|
/**
|
|
975
1011
|
* Feature modules to load. Accepts both class form
|
|
@@ -1004,6 +1040,31 @@ interface ApplicationOptions {
|
|
|
1004
1040
|
adapters?: AppAdapter[];
|
|
1005
1041
|
/** Server port (falls back to PORT env var, then 3000) */
|
|
1006
1042
|
port?: number;
|
|
1043
|
+
/**
|
|
1044
|
+
* The Node server `start()` creates in production. Omit for plain HTTP.
|
|
1045
|
+
*
|
|
1046
|
+
* - `{ tls }` — HTTPS on every runtime.
|
|
1047
|
+
* - `{ tls, http2: true }` — HTTP/2 (ALPN `h2`) with HTTP/1.1 fallback, so
|
|
1048
|
+
* HTTP/1.1 clients and WebSocket upgrades keep working. Fastify and h3
|
|
1049
|
+
* only; Express fails at boot with KICK007, and `http2` without `tls`
|
|
1050
|
+
* with KICK008.
|
|
1051
|
+
*
|
|
1052
|
+
* Ignored in dev mode, where Vite owns the server. Most deploys terminate
|
|
1053
|
+
* TLS / HTTP/2 at a proxy and need none of this.
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```ts
|
|
1057
|
+
* bootstrap({
|
|
1058
|
+
* modules,
|
|
1059
|
+
* runtime: fastifyRuntime(),
|
|
1060
|
+
* server: {
|
|
1061
|
+
* tls: { key: readFileSync('key.pem'), cert: readFileSync('cert.pem') },
|
|
1062
|
+
* http2: true,
|
|
1063
|
+
* },
|
|
1064
|
+
* })
|
|
1065
|
+
* ```
|
|
1066
|
+
*/
|
|
1067
|
+
server?: ServerOptions;
|
|
1007
1068
|
/** Global API prefix (default: '/api'). Pass `''` to mount at the root. */
|
|
1008
1069
|
apiPrefix?: string;
|
|
1009
1070
|
/**
|
|
@@ -1269,6 +1330,16 @@ declare class Application {
|
|
|
1269
1330
|
private readonly runtime;
|
|
1270
1331
|
private container;
|
|
1271
1332
|
private httpServer;
|
|
1333
|
+
/** Set by startWithoutServer() — the serverless path, where cron runs over HTTP. */
|
|
1334
|
+
private handlerMode;
|
|
1335
|
+
/** The observer set this app installed — released on shutdown only if still current. */
|
|
1336
|
+
private observers;
|
|
1337
|
+
/**
|
|
1338
|
+
* Open HTTP/2 sessions. `server.close()` stops new sessions, but on older
|
|
1339
|
+
* Node (22 and earlier) existing ones keep accepting new streams — so
|
|
1340
|
+
* shutdown closes them itself.
|
|
1341
|
+
*/
|
|
1342
|
+
private readonly http2Sessions;
|
|
1272
1343
|
/** Forwarding server behind {@link fetch} on Node-based runtimes. */
|
|
1273
1344
|
private loopback?;
|
|
1274
1345
|
private readonly adapters;
|
|
@@ -1358,7 +1429,15 @@ declare class Application {
|
|
|
1358
1429
|
* adapter written against this works under any runtime.
|
|
1359
1430
|
*/
|
|
1360
1431
|
private adapterHttp;
|
|
1361
|
-
/**
|
|
1432
|
+
/**
|
|
1433
|
+
* Call an adapter hook, awaiting async hooks.
|
|
1434
|
+
*
|
|
1435
|
+
* A setup hook (`beforeMount`, `beforeStart`) that throws aborts boot: the
|
|
1436
|
+
* adapter didn't finish wiring itself — a database adapter refusing to
|
|
1437
|
+
* start with pending migrations, say — and serving anyway runs on a
|
|
1438
|
+
* half-built app. `afterStart` runs once the server is already listening,
|
|
1439
|
+
* so its failure is logged and the server keeps going.
|
|
1440
|
+
*/
|
|
1362
1441
|
private callHook;
|
|
1363
1442
|
setup(): Promise<void>;
|
|
1364
1443
|
/** Register modules and DI without starting the HTTP server (used by kick tinker) */
|
|
@@ -1382,6 +1461,8 @@ declare class Application {
|
|
|
1382
1461
|
* In **production**: creates its own `http.Server` and binds to the port.
|
|
1383
1462
|
*/
|
|
1384
1463
|
start(): Promise<void>;
|
|
1464
|
+
/** The production server for {@link ApplicationOptions.server}. */
|
|
1465
|
+
private createNodeServer;
|
|
1385
1466
|
/** HMR rebuild: swap Express handler without restarting the server */
|
|
1386
1467
|
rebuild(): Promise<void>;
|
|
1387
1468
|
/**
|
|
@@ -1447,7 +1528,7 @@ declare class Application {
|
|
|
1447
1528
|
label: string;
|
|
1448
1529
|
dependsOn: readonly string[];
|
|
1449
1530
|
}>;
|
|
1450
|
-
getHttpServer():
|
|
1531
|
+
getHttpServer(): KickServer | null;
|
|
1451
1532
|
/**
|
|
1452
1533
|
* Decide whether {@link Application} should auto-mount the
|
|
1453
1534
|
* {@link requestScopeMiddleware} ALS wrapper.
|
|
@@ -1465,9 +1546,17 @@ declare class Application {
|
|
|
1465
1546
|
private collectAdapterMiddleware;
|
|
1466
1547
|
private mountMiddlewareList;
|
|
1467
1548
|
private mountMiddlewareEntry;
|
|
1549
|
+
/**
|
|
1550
|
+
* `GET /_kick/cron/:scheduleId` — how a serverless platform
|
|
1551
|
+
* (Vercel crons) runs `@Cron` jobs: `kick build:vercel` emits one cron entry
|
|
1552
|
+
* per schedule pointing here. Mounted only in handler mode and only when
|
|
1553
|
+
* `CRON_SECRET` is set; the request must carry `Authorization: Bearer
|
|
1554
|
+
* $CRON_SECRET` (Vercel sends it). Runs every job on that schedule.
|
|
1555
|
+
*/
|
|
1556
|
+
private mountCronTrigger;
|
|
1468
1557
|
/** Middleware that tracks in-flight requests for graceful draining */
|
|
1469
1558
|
private requestTrackingMiddleware;
|
|
1470
1559
|
}
|
|
1471
1560
|
//#endregion
|
|
1472
|
-
export {
|
|
1473
|
-
//# sourceMappingURL=application-
|
|
1561
|
+
export { MissingMountDepError as $, defineHttpContextDecorator as A, CRON_WORKER_ENV as B, RequestStore as C, buildRoutes as D, requestStore as E, bootstrap as F, offerRoutePolicy as G, KickCronAdapter as H, ERROR_CHANNEL as I, GroupAssetKeysOptions as J, AssetKeyPair as K, HANDLER_CHANNEL as L, isClusterPrimary as M, KickHandler as N, expressRuntime as O, createHandler as P, DuplicateMountNameError as Q, HandlerTraceContext as R, requestScopeMiddleware as S, getRequestValue as T, RoutePolicyTable as U, CronAdapterOptions as V, bindRoutePolicy as W, groupAssetKeys as X, GroupAssetKeysResult as Y, stripExt as Z, TRACEPARENT_HEADER as _, Disposable as _t, ShutdownOptions as a, DuplicateContributorError as at, parseTraceparent as b, HealthController as c, CircuitBreaker as ct, healthModule as d, CircuitBreakerStats as dt, MountCycleError as et, SpaAdapter as f, CircuitOpenError as ft, ViewAdapterOptions as g, formatKickError as gt, ViewAdapter as h, KickErrorInit as ht, ServerOptions as i, ContributorCycleError as it, ClusterOptions as j, materializeRouter as k, HealthModuleConfig as l, CircuitBreakerOptions as lt, resolveClientDir as m, KickError as mt, ApplicationOptions as n, MountSortItem as nt, HEALTH_PROBE as o, MissingContextValueError as ot, SpaAdapterOptions as p, FormatKickErrorOptions as pt, AssetKeyStrategy as q, MiddlewareEntry as r, mountSort as rt, HealthCheckResult as s, MissingContributorError as st, Application as t, MountKind as tt, HealthProbe as u, CircuitBreakerState as ut, TraceContext as v, disposeAll as vt, getRequestStore as w, traceContext as x, TraceContextOptions as y, registerDisposable as yt, RESPONSE_CHANNEL as z };
|
|
1562
|
+
//# sourceMappingURL=application-DqD58rhF.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-DqD58rhF.d.mts","names":[],"sources":["../src/core/disposables.ts","../src/core/kick-error.ts","../src/core/circuit-breaker.ts","../src/core/context-errors.ts","../src/core/mount-sort.ts","../src/core/asset-keys.ts","../src/core/route-policy.ts","../src/http/cron-adapter.ts","../src/http/tracing.ts","../src/http/bootstrap.ts","../src/http/handler.ts","../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/runtimes/express.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts","../src/http/middleware/spa.ts","../src/http/health-module.ts","../src/http/application.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forinda/kickjs v9.0.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Felix Orinda
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import{o as reloadEnv}from"./env-DMAdjj8Z.mjs";import{t as Container}from"./container-Dw45sa0Y.mjs";import{createLogger}from"./core/logger.mjs";import{t as Application}from"./application-BnFA6J97.mjs";import{r as reportError}from"./observers-BI8H_g45.mjs";import{listCronJobs,runCronJob}from"./core/cron.mjs";import{reactive,ref}from"./core/reactivity.mjs";import"./core-CCwlN0Gn.mjs";import{t as defineAdapter}from"./define-adapter-Ckc2P-wH.mjs";import{createRequire}from"node:module";import cluster from"node:cluster";import os from"node:os";const log$2=createLogger(`KickCronAdapter`),peerRequire=createRequire(import.meta.url),CRON_WORKER_ENV=`KICK_CRON_WORKER`;function loadCroner(){try{return peerRequire(`croner`).Cron}catch{return}}function isCronProcess(){return!cluster.isWorker||process.env.KICK_CRON_WORKER===`1`}const KickCronAdapter=defineAdapter({name:`KickCronAdapter`,defaults:{enabled:!0},build(options){let schedules=[],inFlight=new Set,jobs=[];return{afterStart({container}){if(options.enabled===!1||!isCronProcess()||(jobs=listCronJobs(container),jobs.length===0))return;let Croner=loadCroner();if(!Croner){log$2.warn(`${jobs.length} @Cron job(s) found but "croner" is not installed, so none will run. Install it: pnpm add croner`);return}let run=(job,trigger)=>{let p=runCronJob(job,container,trigger).catch(()=>{});inFlight.add(p),p.finally(()=>inFlight.delete(p))};for(let job of jobs)schedules.push(new Croner(job.expression,job.timezone?{timezone:job.timezone}:{},()=>run(job,`schedule`))),job.runOnInit&&run(job,`init`);log$2.info(`Scheduled ${jobs.length} @Cron job(s): ${jobs.map(j=>j.name).join(`, `)}`)},async shutdown(){for(let schedule of schedules.splice(0))schedule.stop();await Promise.all(inFlight)},introspect(){return{protocolVersion:1,name:`KickCronAdapter`,kind:`adapter`,state:{jobs:jobs.map(j=>({name:j.name,expression:j.expression,timezone:j.timezone,description:j.description}))}}}}}}),log$1=createLogger(`Cluster`);function isClusterPrimary(){return cluster.isPrimary}function setupClusterPrimary(opts){let numWorkers=opts.workers??os.cpus().length,gracefulTimeout=opts.gracefulShutdownTimeout??5e3,restartDelay=opts.restartDelay??1e3,maxFailures=opts.maxFailures??5,restartWindow=opts.restartWindow??3e4,startupTimeouts=new Map,state=reactive({failureTimestamps:[],shuttingDown:!1}),activeWorkers=ref(0);log$1.info(`Primary ${process.pid} starting ${numWorkers} worker(s)`);let cronWorkerId,forkWorker=()=>{let takesCron=cronWorkerId===void 0,worker=cluster.fork(takesCron?{[CRON_WORKER_ENV]:`1`}:void 0);takesCron&&(cronWorkerId=worker.id)};for(let i=0;i<numWorkers;i++)forkWorker();cluster.on(`fork`,worker=>{let timeout=setTimeout(()=>{log$1.error(`Worker ${worker.process.pid} did not respond within 10s, killing...`),worker.kill()},1e4);startupTimeouts.set(worker.id,timeout),activeWorkers.value++,log$1.debug(`Worker ${worker.process.pid} forked (id=${worker.id})`)}),cluster.on(`listening`,(worker,address)=>{let timeout=startupTimeouts.get(worker.id);timeout&&(clearTimeout(timeout),startupTimeouts.delete(worker.id)),log$1.info(`Worker ${worker.process.pid} listening on ${address.address}:${address.port}`)}),cluster.on(`online`,worker=>{log$1.debug(`Worker ${worker.process.pid} online (id=${worker.id})`)}),cluster.on(`exit`,(worker,code,signal)=>{let timeout=startupTimeouts.get(worker.id);if(timeout&&(clearTimeout(timeout),startupTimeouts.delete(worker.id)),activeWorkers.value--,worker.id===cronWorkerId&&(cronWorkerId=void 0),worker.exitedAfterDisconnect){log$1.info(`Worker ${worker.process.pid} exited gracefully (id=${worker.id})`);return}let now=Date.now();for(;state.failureTimestamps.length>0&&now-state.failureTimestamps[0]>=restartWindow;)state.failureTimestamps.shift();if(state.failureTimestamps.push(now),state.failureTimestamps.length>maxFailures){log$1.error(`${state.failureTimestamps.length} worker failures in ${restartWindow}ms, giving up`),activeWorkers.value===0&&(log$1.error(`All workers have crashed, exiting primary`),process.exit(1));return}log$1.warn(`Worker ${worker.process.pid} died (code=${code}, signal=${signal}), restarting in ${restartDelay}ms... (failures: ${state.failureTimestamps.length}/${maxFailures})`),setTimeout(()=>{activeWorkers.value<numWorkers&&forkWorker()},restartDelay)}),cluster.on(`disconnect`,worker=>{log$1.debug(`Worker ${worker.process.pid} disconnected (id=${worker.id})`)});for(let signal of[`SIGTERM`,`SIGINT`])process.on(signal,()=>{state.shuttingDown||(state.shuttingDown=!0,log$1.info(`Primary received ${signal}, disconnecting workers...`),cluster.disconnect(()=>{log$1.info(`All workers disconnected, exiting primary`),process.exit(0)}),setTimeout(()=>{log$1.warn(`Graceful shutdown timed out, forcing exit`),process.exit(1)},gracefulTimeout).unref())})}function tryReloadEnv(){try{reloadEnv()}catch{}}const log=createLogger(`Process`);async function bootstrap(options){let isViteDev=!!globalThis.__kickjs_httpServer;if(options.cluster&&isViteDev&&log.warn(`Cluster mode is ignored in dev — Vite owns the server. Cluster applies in production (kick start).`),options.cluster&&cluster.isPrimary&&!isViteDev)return setupClusterPrimary(typeof options.cluster==`object`?options.cluster:{}),new Application(options);let g=globalThis,processHooks=options.processHooks??`auto`;if(!g.__kickBootstrapped){if(processHooks!==`manual`&&(process.on(`uncaughtException`,err=>{log.error(err,`Uncaught exception`),reportError(err,{source:`uncaught`}),process.exitCode=1}),process.on(`unhandledRejection`,reason=>{log.error(reason,`Unhandled rejection`),reportError(reason,{source:`unhandled-rejection`}),process.exitCode=1})),processHooks===`auto`&&!g.__kickjs_httpServer)for(let signal of[`SIGINT`,`SIGTERM`])process.on(signal,async()=>{log.info(`Received ${signal}, shutting down...`),g.__app&&await g.__app.shutdown(),process.exit(0)});g.__kickBootstrapped=!0}if(g.__app){log.debug(`HMR rebuild triggered`);try{await g.__app.shutdown({closeServer:!1})}catch(err){log.error(err,`HMR teardown of the previous app failed`)}tryReloadEnv(),Container.reset();let freshApp=new Application(options);return g.__app=freshApp,g.__kickjs_container=freshApp.getContainer(),await freshApp.start(),log.debug(`HMR rebuild complete`),freshApp}let app=new Application(options);return g.__app=app,g.__kickjs_container=app.getContainer(),process.env.KICK_TINKER?(await app.registerOnly(),app):(await app.start(),app)}export{KickCronAdapter as i,isClusterPrimary as n,CRON_WORKER_ENV as r,bootstrap as t};
|
|
12
|
+
//# sourceMappingURL=bootstrap-K1PcxSTg.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-K1PcxSTg.mjs","names":["log","log"],"sources":["../src/http/cron-adapter.ts","../src/http/cluster.ts","../src/http/bootstrap.ts"],"sourcesContent":["/**\n * `KickCronAdapter()` — runs `@Cron` jobs on the Node server.\n *\n * Named with the `Kick` prefix (adapter name and logger too) so it never\n * collides with an app's own `CronAdapter` — which is also why it is opt-in.\n *\n * Opt-in: `@Cron` only records metadata, and apps that schedule jobs with\n * their own adapter keep doing so — adding this one to such an app would run\n * every job twice. Register it once:\n *\n * ```ts\n * bootstrap({ modules, adapters: [KickCronAdapter()] })\n * ```\n *\n * Timing comes from the optional peer `croner` (`pnpm add croner`), loaded\n * only when there is a job to schedule. Each tick resolves the job's class\n * from the container (the same singletons a request gets) and calls\n * `runCronJob`, which applies `enabled` / `overlap` and reports failures to\n * the error observers.\n *\n * Several processes: in `bootstrap({ cluster })`, only the worker the primary\n * marks with `KICK_CRON_WORKER=1` schedules, so a job runs once per cluster.\n * Several separate instances (replicas behind a load balancer) each schedule\n * every job — turn it off on all but one (`KickCronAdapter({ enabled: false })`),\n * or have the job take a lock.\n *\n * @module @forinda/kickjs/http/cron-adapter\n */\nimport cluster from 'node:cluster'\nimport { createRequire } from 'node:module'\nimport { defineAdapter } from '../core/define-adapter'\nimport { createLogger } from '../core/logger'\nimport { listCronJobs, runCronJob, type CronJob } from '../core/cron'\nimport type { Container } from '../core/container'\n\nconst log = createLogger('KickCronAdapter')\nconst peerRequire = createRequire(import.meta.url)\n\n/** Set by the cluster primary on the one worker that runs `KickCronAdapter` jobs. */\nexport const CRON_WORKER_ENV = 'KICK_CRON_WORKER'\n\nexport interface CronAdapterOptions {\n /** Off for processes that should run no jobs (replicas, one-off scripts). Default `true`. */\n enabled?: boolean\n}\n\ninterface Schedule {\n stop(): void\n}\n\ntype CronerCtor = new (pattern: string, options: { timezone?: string }, fn: () => void) => Schedule\n\nfunction loadCroner(): CronerCtor | undefined {\n try {\n return (peerRequire('croner') as { Cron: CronerCtor }).Cron\n } catch {\n return undefined\n }\n}\n\n/** Whether this process should schedule jobs — one worker per cluster. */\nfunction isCronProcess(): boolean {\n return !cluster.isWorker || process.env[CRON_WORKER_ENV] === '1'\n}\n\nexport const KickCronAdapter = defineAdapter<CronAdapterOptions>({\n name: 'KickCronAdapter',\n defaults: { enabled: true },\n build(options) {\n const schedules: Schedule[] = []\n const inFlight = new Set<Promise<unknown>>()\n let jobs: CronJob[] = []\n\n return {\n afterStart({ container }) {\n if (options.enabled === false || !isCronProcess()) return\n jobs = listCronJobs(container)\n if (jobs.length === 0) return\n\n const Croner = loadCroner()\n if (!Croner) {\n log.warn(\n `${jobs.length} @Cron job(s) found but \"croner\" is not installed, so none will run. ` +\n 'Install it: pnpm add croner',\n )\n return\n }\n\n const run = (job: CronJob, trigger: 'schedule' | 'init') => {\n // Failures are already logged and reported by runCronJob.\n const p = runCronJob(job, container as Container, trigger).catch(() => {})\n inFlight.add(p)\n void p.finally(() => inFlight.delete(p))\n }\n\n for (const job of jobs) {\n schedules.push(\n new Croner(\n job.expression,\n // No croner `name`: croner keeps names in a process-wide registry\n // and throws on a duplicate.\n job.timezone ? { timezone: job.timezone } : {},\n () => run(job, 'schedule'),\n ),\n )\n if (job.runOnInit) run(job, 'init')\n }\n log.info(`Scheduled ${jobs.length} @Cron job(s): ${jobs.map((j) => j.name).join(', ')}`)\n },\n\n /**\n * Stops the schedules, then waits for running jobs so they don't lose\n * their dependencies mid-run. Also runs on every HMR reload, so a reload\n * never leaves a second copy ticking.\n */\n async shutdown() {\n for (const schedule of schedules.splice(0)) schedule.stop()\n await Promise.all(inFlight)\n },\n\n introspect() {\n return {\n protocolVersion: 1,\n name: 'KickCronAdapter',\n kind: 'adapter',\n state: {\n jobs: jobs.map((j) => ({\n name: j.name,\n expression: j.expression,\n timezone: j.timezone,\n description: j.description,\n })),\n },\n } as never\n },\n }\n },\n})\n","import cluster from 'node:cluster'\nimport { CRON_WORKER_ENV } from './cron-adapter'\nimport os from 'node:os'\nimport { createLogger } from '../core'\nimport { reactive, ref } from '../core/reactivity'\n\nconst log = createLogger('Cluster')\n\nexport interface ClusterOptions {\n /** Number of worker processes (default: os.cpus().length) */\n workers?: number\n /** Delay before forcefully killing a worker during shutdown (ms), default: 5000 */\n gracefulShutdownTimeout?: number\n /** Delay between worker restarts (ms), default: 1000 */\n restartDelay?: number\n /** Max failures in restartWindow before refusing to restart, default: 5 */\n maxFailures?: number\n /** Time window for counting failures (ms), default: 30000 */\n restartWindow?: number\n}\n\n/** Returns true if the current process is the cluster primary */\nexport function isClusterPrimary(): boolean {\n return cluster.isPrimary\n}\n\n/**\n * Fork worker processes and manage the cluster lifecycle.\n *\n * The primary process forks `workers` child processes. Each worker\n * shares the same port via Node's built-in `cluster` module (OS\n * round-robin load balancing).\n *\n * Features:\n * - Graceful shutdown with timeout fallback to forceful kill\n * - Auto-restart dead workers (respects voluntary disconnects)\n * - Startup timeout detection (stuck workers)\n * - Crash loop prevention (backoff on rapid failures)\n * - Comprehensive lifecycle logging\n */\nexport function setupClusterPrimary(opts: ClusterOptions): void {\n const numWorkers = opts.workers ?? os.cpus().length\n const gracefulTimeout = opts.gracefulShutdownTimeout ?? 5000\n const restartDelay = opts.restartDelay ?? 1000\n const maxFailures = opts.maxFailures ?? 5\n const restartWindow = opts.restartWindow ?? 30000\n\n const startupTimeouts = new Map<number, NodeJS.Timeout>()\n const state = reactive({\n failureTimestamps: [] as number[],\n shuttingDown: false,\n })\n const activeWorkers = ref(0)\n\n log.info(`Primary ${process.pid} starting ${numWorkers} worker(s)`)\n\n // Exactly one worker runs `KickCronAdapter` jobs, so a job fires once per\n // cluster rather than once per worker. The primary marks it with\n // KICK_CRON_WORKER=1; when it dies, the next fork inherits the role.\n let cronWorkerId: number | undefined\n const forkWorker = (): void => {\n const takesCron = cronWorkerId === undefined\n const worker = cluster.fork(takesCron ? { [CRON_WORKER_ENV]: '1' } : undefined)\n if (takesCron) cronWorkerId = worker.id\n }\n\n // Initial fork\n for (let i = 0; i < numWorkers; i++) {\n forkWorker()\n }\n\n // Detect stuck workers (no 'listening' event within timeout)\n cluster.on('fork', (worker) => {\n const timeout = setTimeout(() => {\n log.error(`Worker ${worker.process.pid} did not respond within 10s, killing...`)\n worker.kill()\n }, 10000)\n\n startupTimeouts.set(worker.id, timeout)\n activeWorkers.value++\n log.debug(`Worker ${worker.process.pid} forked (id=${worker.id})`)\n })\n\n // Clear startup timeout when worker is ready\n cluster.on('listening', (worker, address) => {\n const timeout = startupTimeouts.get(worker.id)\n if (timeout) {\n clearTimeout(timeout)\n startupTimeouts.delete(worker.id)\n }\n log.info(`Worker ${worker.process.pid} listening on ${address.address}:${address.port}`)\n })\n\n // Log when worker comes online\n cluster.on('online', (worker) => {\n log.debug(`Worker ${worker.process.pid} online (id=${worker.id})`)\n })\n\n // Auto-restart dead workers (unless intentionally disconnected)\n cluster.on('exit', (worker, code, signal) => {\n // Clear startup timeout if still pending\n const timeout = startupTimeouts.get(worker.id)\n if (timeout) {\n clearTimeout(timeout)\n startupTimeouts.delete(worker.id)\n }\n\n // Don't restart if worker was intentionally disconnected\n activeWorkers.value--\n if (worker.id === cronWorkerId) cronWorkerId = undefined\n\n if (worker.exitedAfterDisconnect) {\n log.info(`Worker ${worker.process.pid} exited gracefully (id=${worker.id})`)\n return\n }\n\n // Track crash history globally (worker IDs change on restart)\n const now = Date.now()\n // Remove timestamps outside the window, then add the current failure\n while (\n state.failureTimestamps.length > 0 &&\n now - state.failureTimestamps[0] >= restartWindow\n ) {\n state.failureTimestamps.shift()\n }\n state.failureTimestamps.push(now)\n\n // Prevent restart loops\n if (state.failureTimestamps.length > maxFailures) {\n log.error(\n `${state.failureTimestamps.length} worker failures in ${restartWindow}ms, giving up`,\n )\n // Optionally exit the primary if too many workers are crashing\n if (activeWorkers.value === 0) {\n log.error('All workers have crashed, exiting primary')\n process.exit(1)\n }\n return\n }\n\n log.warn(\n `Worker ${worker.process.pid} died (code=${code}, signal=${signal}), restarting in ${restartDelay}ms... (failures: ${state.failureTimestamps.length}/${maxFailures})`,\n )\n\n setTimeout(() => {\n // Only restart if we still need more workers\n if (activeWorkers.value < numWorkers) {\n forkWorker()\n }\n }, restartDelay)\n })\n\n // Log when worker disconnects (can happen before exit)\n cluster.on('disconnect', (worker) => {\n log.debug(`Worker ${worker.process.pid} disconnected (id=${worker.id})`)\n })\n\n // Forward shutdown signals to workers (disconnect so exitedAfterDisconnect is set)\n for (const signal of ['SIGTERM', 'SIGINT'] as const) {\n process.on(signal, () => {\n if (state.shuttingDown) return\n state.shuttingDown = true\n log.info(`Primary received ${signal}, disconnecting workers...`)\n cluster.disconnect(() => {\n log.info('All workers disconnected, exiting primary')\n process.exit(0)\n })\n // Force kill after graceful timeout\n setTimeout(() => {\n log.warn('Graceful shutdown timed out, forcing exit')\n process.exit(1)\n }, gracefulTimeout).unref()\n })\n }\n}\n","import cluster from 'node:cluster'\nimport { reportError } from '../core/observers'\nimport { createLogger, Container } from '../core'\nimport { reloadEnv } from '../config/env'\nimport { Application, type ApplicationOptions } from './application'\nimport { setupClusterPrimary, type ClusterOptions } from './cluster'\n\n/** Reload env from .env file (HMR rebuild) — config now lives in-tree. */\nfunction tryReloadEnv(): void {\n try {\n reloadEnv()\n } catch {\n // Best-effort: never crash bootstrap because env reload failed.\n }\n}\n\nconst log = createLogger('Process')\n\n/**\n * Bootstrap a KickJS application.\n *\n * Creates the Express app, registers modules and adapters, and starts\n * the HTTP server. In dev mode (`kick dev`), the Vite plugin manages\n * the server and HMR — source changes are picked up instantly without\n * losing database connections or WebSocket state.\n *\n * @returns The {@link Application} instance.\n *\n * @example\n * ```ts\n * // src/index.ts\n * import 'reflect-metadata'\n * import { bootstrap } from '@forinda/kickjs'\n * import { modules } from './modules'\n *\n * export const app = await bootstrap({ modules })\n * ```\n */\nexport async function bootstrap(options: ApplicationOptions): Promise<Application> {\n // ── Cluster mode ─────────────────────────────────────────────────────\n // In cluster mode the primary process forks workers and never runs the\n // Express application itself. Only workers proceed past this block.\n // Cluster is disabled in Vite dev mode — Vite owns the HTTP server and\n // forked workers would each create their own Vite instance on separate ports.\n const isViteDev = !!(globalThis as any).__kickjs_httpServer\n if (options.cluster && isViteDev) {\n log.warn(\n 'Cluster mode is ignored in dev — Vite owns the server. Cluster applies in production (kick start).',\n )\n }\n if (options.cluster && cluster.isPrimary && !isViteDev) {\n const clusterOpts: ClusterOptions = typeof options.cluster === 'object' ? options.cluster : {}\n setupClusterPrimary(clusterOpts)\n\n // Return a placeholder — the primary never serves HTTP traffic.\n // The Application is created only so callers get a valid reference.\n return new Application(options)\n }\n\n // Internal: globalThis stores the app instance and Vite's HTTP server\n // reference across HMR rebuilds. When __kickjs_httpServer is set, the\n // Vite plugin owns the port and bootstrap() skips server creation.\n // On subsequent calls (HMR), the Express handler is swapped without\n // restarting the server — preserving DB connections and port bindings.\n const g = globalThis as any\n\n // ── Global error handlers + signal hooks ─────────────────────────────\n // `processHooks` controls whether KickJS registers its own\n // process-level handlers. Defaults to `'auto'` — register everything.\n // Adopters that bring their own observability stack (OpenTelemetry SDK,\n // Sentry, custom shutdown choreographer) often register their own\n // SIGINT/SIGTERM handlers and need to opt out of ours so the two don't\n // race to call `process.exit(0)`. Three modes:\n // - 'auto' (default): register uncaughtException/unhandledRejection\n // loggers + SIGINT/SIGTERM → `app.shutdown()` → `process.exit(0)`.\n // - 'errors-only': register the error loggers; skip signal handlers.\n // Use this when you have your own shutdown choreographer that calls\n // `app.shutdown()` itself.\n // - 'manual': skip everything. Adopter is responsible for both error\n // logging and signal-driven shutdown.\n const processHooks = options.processHooks ?? 'auto'\n\n if (!g.__kickBootstrapped) {\n if (processHooks !== 'manual') {\n // A listener here replaces Node's own handling, which exits with code\n // 1. Keep the failure in the exit code: a boot that throws — a top-level\n // `await bootstrap()` rejecting — must not end the process with 0, or a\n // deploy reads the crash as success.\n process.on('uncaughtException', (err) => {\n log.error(err, 'Uncaught exception')\n reportError(err, { source: 'uncaught' })\n process.exitCode = 1\n })\n\n process.on('unhandledRejection', (reason) => {\n log.error(reason as any, 'Unhandled rejection')\n reportError(reason, { source: 'unhandled-rejection' })\n process.exitCode = 1\n })\n }\n\n if (processHooks === 'auto' && !g.__kickjs_httpServer) {\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.on(signal, async () => {\n log.info(`Received ${signal}, shutting down...`)\n if (g.__app) await g.__app.shutdown()\n process.exit(0)\n })\n }\n }\n\n g.__kickBootstrapped = true\n }\n\n // ── HMR rebuild ──────────────────────────────────────────────────────\n if (g.__app) {\n log.debug('HMR rebuild triggered')\n\n // Tear the PREVIOUS app down before replacing it. Without this the old\n // Application was dropped on the floor still running: its adapters kept\n // their sockets and consumers open, so every save added another set.\n //\n // Observed in the wild as one process holding several consumer-group\n // members — on a single-partition topic only one can hold the assignment,\n // and it was a leaked consumer no longer wired to anything, so jobs\n // stopped being processed. A second socket.io server on the same HTTP\n // server crashed `handleUpgrade()` the same way.\n //\n // `closeServer: false` is load-bearing: the dev HTTP server is shared\n // across rebuilds, so a full shutdown would close the listening socket and\n // kill HMR on the first save.\n //\n // Failures are logged, not thrown — one broken adapter must not leave the\n // dev server wedged with no app at all.\n try {\n await g.__app.shutdown({ closeServer: false })\n } catch (err) {\n log.error(err as Error, 'HMR teardown of the previous app failed')\n }\n\n tryReloadEnv()\n Container.reset()\n\n const freshApp = new Application(options)\n g.__app = freshApp\n g.__kickjs_container = freshApp.getContainer()\n\n await freshApp.start()\n log.debug('HMR rebuild complete')\n return freshApp\n }\n\n // ── First boot ───────────────────────────────────────────────────────\n const app = new Application(options)\n g.__app = app\n g.__kickjs_container = app.getContainer()\n\n if (process.env.KICK_TINKER) {\n await app.registerOnly()\n return app\n }\n\n await app.start()\n\n return app\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-C0qL0NSD.d.mts","names":[],"sources":["../src/core/cache.ts"],"mappings":";;;cAIM;;;;;;;;;;;;;;;;;;;;;;;;;UA4BW;;EAEf,IAAI,cAAc;;EAElB,IAAI,aAAa,YAAY,gBAAgB;;EAE7C,IAAI,cAAc;;EAElB,aAAa,iBAAiB;;EAE9B,aAAa;;;;;;;;;;;;cAeF,+BAA+B;mBAGb;UAFrB;EAER,YAA6B;EAEvB,IAAI,cAAc;EAclB,IAAI,aAAa,YAAY,gBAAgB;EAU7C,IAAI,cAAc;EAIlB,YAAY,iBAAiB;EAQ7B,YAAY;;;;;;;;;;;;;;iBAqBJ,iBAAiB,UAAU;;iBAK3B,oBAAoB;UAMnB;;EAEf;;EAEA;;
|
|
1
|
+
{"version":3,"file":"cache-C0qL0NSD.d.mts","names":[],"sources":["../src/core/cache.ts"],"mappings":";;;cAIM;;;;;;;;;;;;;;;;;;;;;;;;;UA4BW;;EAEf,IAAI,cAAc;;EAElB,IAAI,aAAa,YAAY,gBAAgB;;EAE7C,IAAI,cAAc;;EAElB,aAAa,iBAAiB;;EAE9B,aAAa;;;;;;;;;;;;cAeF,+BAA+B;mBAGb;UAFrB;EAER,YAA6B;EAEvB,IAAI,cAAc;EAclB,IAAI,aAAa,YAAY,gBAAgB;EAU7C,IAAI,cAAc;EAIlB,YAAY,iBAAiB;EAQ7B,YAAY;;;;;;;;;;;;;;iBAqBJ,iBAAiB,UAAU;;iBAK3B,oBAAoB;UAMnB;;EAEf;;EAEA;;iBAyCc,UAAU,cAAc;EAAY;IAAiB;;;;;;;iBAsDrD,WAAW,cAAc"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs
|
|
2
|
+
* @forinda/kickjs v9.0.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
function firstHop(value){return typeof value==`string`&&value.length>0?value.split(`,`)[0].trim():void 0}function headerValue(headers,name){if(headers)return typeof headers.get==`function`?headers.get(name):headers[name]}function resolveClientIp(req){if(typeof req.ip==`string`&&req.ip.length>0)return req.ip;let socketAddress=req.socket?.remoteAddress;if(socketAddress)return socketAddress;let h=req.headers;return firstHop(headerValue(h,`cf-connecting-ip`))??firstHop(headerValue(h,`x-forwarded-for`))??firstHop(headerValue(h,`x-real-ip`))}function resolvePathname(req){if(typeof req.path==`string`&&req.path.length>0)return req.path;let url=req.url;if(!url)return`/`;if(/^[a-z][a-z0-9+.-]*:\/\//i.test(url))try{return new URL(url).pathname||`/`}catch{return`/`}let q=url.indexOf(`?`);return(q===-1?url:url.slice(0,q))||`/`}export{resolvePathname as n,resolveClientIp as t};
|
|
12
|
-
//# sourceMappingURL=client-ip-
|
|
12
|
+
//# sourceMappingURL=client-ip-DAnYuS54.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-ip-
|
|
1
|
+
{"version":3,"file":"client-ip-DAnYuS54.mjs","names":[],"sources":["../src/http/client-ip.ts"],"sourcesContent":["/**\n * Client address + pathname resolution shared by everything that runs as\n * connect-style middleware.\n *\n * Fastify and h3 hand middleware the RAW node request, so the Express\n * conveniences (`req.ip`, `req.path`, `req.originalUrl`) are `undefined`\n * there. Reading them directly produced two live bugs:\n *\n * rate-limit → every caller keyed as the `'127.0.0.1'` fallback, i.e. ONE\n * shared bucket, so a single client could exhaust the limit\n * for everyone\n * csrf → `ignorePaths` never matched, so configured exemptions were\n * silently dead\n *\n * Edge-safe: no node imports.\n */\n\n/** The request members these helpers read, from node's shape rather than Express's. */\nexport interface ClientRequestLike {\n url?: string\n /** Express-only convenience. */\n path?: string\n /** Express-only; the full path before router mounting rewrote `url`. */\n originalUrl?: string\n /** Express and Fastify compute this from their trust-proxy settings. */\n ip?: unknown\n /**\n * A plain object on every path the framework currently takes — `WebRequestShim`\n * normalizes a WHATWG `Headers` before the context is built. A `Headers`\n * instance is accepted defensively: it is not index-accessible, so if one ever\n * did arrive, every lookup would return `undefined` SILENTLY and drop each\n * caller into one shared rate-limit bucket.\n */\n headers?: Record<string, string | string[] | undefined> | Headers\n socket?: { remoteAddress?: string }\n}\n\n/** First entry of a possibly comma-separated header value. */\nfunction firstHop(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value.split(',')[0]!.trim() : undefined\n}\n\n/** Read one header from either a plain object or a WHATWG `Headers`. */\nfunction headerValue(\n headers: Record<string, string | string[] | undefined> | Headers | undefined,\n name: string,\n): unknown {\n if (!headers) return undefined\n // `Headers` has no index access, so `headers[name]` silently yields\n // `undefined` rather than the value.\n if (typeof (headers as Headers).get === 'function') return (headers as Headers).get(name)\n return (headers as Record<string, string | string[] | undefined>)[name]\n}\n\n/**\n * The client's IP address, or `undefined` when it cannot be determined.\n *\n * Prefers the address the runtime computed — Express derives `req.ip` from\n * `trust proxy`, Fastify from `trustProxy` — because raw forwarded headers are\n * client-SPOOFABLE wherever nothing normalizes them. h3 takes the same stance:\n * its `getRequestIP` only consults `x-forwarded-for` when explicitly opted in.\n *\n * Order is `req.ip` → `socket.remoteAddress` → forwarded headers. The socket\n * comes BEFORE the headers deliberately: it cannot be spoofed, whereas a direct\n * client can vary `x-forwarded-for` per request to get a fresh rate-limit\n * bucket each time. Headers are consulted only where there is no socket — the\n * web/edge entry, where the platform terminates the connection.\n */\nexport function resolveClientIp(req: ClientRequestLike): string | undefined {\n // 1. What the runtime computed. Express derives `req.ip` from `trust proxy`,\n // Fastify from `trustProxy` — a value vetted against a CONFIGURED proxy,\n // which is the only reason to believe a forwarded header at all.\n if (typeof req.ip === 'string' && req.ip.length > 0) return req.ip\n\n // 2. The socket. Unspoofable, and present on every node runtime.\n //\n // This MUST precede the headers. A raw Fastify / h3 request has no\n // `req.ip`, so consulting `x-forwarded-for` first let a DIRECT client send\n // a different value per request and land in a fresh rate-limit bucket each\n // time — evading the limit entirely. Behind a real proxy the socket is the\n // proxy's address, which buckets clients together; the fix for that is to\n // configure the runtime's trust-proxy setting so step 1 answers, not to\n // trust an unverified header here.\n const socketAddress = req.socket?.remoteAddress\n if (socketAddress) return socketAddress\n\n // 3. Headers, only where there is no socket at all — the web/edge entry,\n // where the platform terminates the connection and these are the only\n // signal available.\n const h = req.headers\n return (\n firstHop(headerValue(h, 'cf-connecting-ip')) ??\n firstHop(headerValue(h, 'x-forwarded-for')) ??\n firstHop(headerValue(h, 'x-real-ip'))\n )\n}\n\n/** Pathname only — `req.url` carries the query string, and `req.path` is Express-only. */\nexport function resolvePathname(req: ClientRequestLike): string {\n if (typeof req.path === 'string' && req.path.length > 0) return req.path\n const url = req.url\n if (!url) return '/'\n // `WebRequestShim` already reduces the WHATWG `Request.url` to path+search,\n // but an ABSOLUTE url would otherwise be returned whole — so a skip prefix\n // like `/health` would never match `https://host/health`.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(url)) {\n try {\n return new URL(url).pathname || '/'\n } catch {\n return '/'\n }\n }\n const q = url.indexOf('?')\n return (q === -1 ? url : url.slice(0, q)) || '/'\n}\n"],"mappings":";;;;;;;;;;AAsCA,SAAS,SAAS,MAAoC,CACpD,OAAO,OAAO,OAAU,UAAY,MAAM,OAAS,EAAI,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAE,KAAK,EAAI,IAAA,EACvF,CAGA,SAAS,YACP,QACA,KACS,CACJ,WAIL,OADI,OAAQ,QAAoB,KAAQ,WAAoB,QAAoB,IAAI,IAAI,EAChF,QAA0D,KACpE,CAgBA,SAAgB,gBAAgB,IAA4C,CAI1E,GAAI,OAAO,IAAI,IAAO,UAAY,IAAI,GAAG,OAAS,EAAG,OAAO,IAAI,GAWhE,IAAM,cAAgB,IAAI,QAAQ,cAClC,GAAI,cAAe,OAAO,cAK1B,IAAM,EAAI,IAAI,QACd,OACE,SAAS,YAAY,EAAG,kBAAkB,CAAC,GAC3C,SAAS,YAAY,EAAG,iBAAiB,CAAC,GAC1C,SAAS,YAAY,EAAG,WAAW,CAAC,CAExC,CAGA,SAAgB,gBAAgB,IAAgC,CAC9D,GAAI,OAAO,IAAI,MAAS,UAAY,IAAI,KAAK,OAAS,EAAG,OAAO,IAAI,KACpE,IAAM,IAAM,IAAI,IAChB,GAAI,CAAC,IAAK,MAAO,IAIjB,GAAI,2BAA2B,KAAK,GAAG,EACrC,GAAI,CACF,OAAO,IAAI,IAAI,GAAG,CAAC,CAAC,UAAY,GAClC,MAAQ,CACN,MAAO,GACT,CAEF,IAAM,EAAI,IAAI,QAAQ,GAAG,EACzB,OAAQ,IAAM,GAAK,IAAM,IAAI,MAAM,EAAG,CAAC,IAAM,GAC/C"}
|
package/dist/config/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
|
|
2
|
-
import { a as baseEnvSchema, c as loadEnv, d as resetEnvCache, i as Env, l as loadEnvFromSchema, n as TypedConfigService, o as defineEnv, r as createConfigService, s as getEnv, t as ConfigService, u as reloadEnv } from "../index-
|
|
3
|
-
export { ConfigService, type Env, type TypedConfigService, baseEnvSchema, createConfigService, defineEnv, getEnv, loadEnv, loadEnvFromSchema, reloadEnv, resetEnvCache };
|
|
2
|
+
import { a as baseEnvSchema, c as loadEnv, d as resetEnvCache, f as withEnv, i as Env, l as loadEnvFromSchema, n as TypedConfigService, o as defineEnv, r as createConfigService, s as getEnv, t as ConfigService, u as reloadEnv } from "../index-CDn-p3tq.mjs";
|
|
3
|
+
export { ConfigService, type Env, type TypedConfigService, baseEnvSchema, createConfigService, defineEnv, getEnv, loadEnv, loadEnvFromSchema, reloadEnv, resetEnvCache, withEnv };
|
package/dist/config/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs
|
|
2
|
+
* @forinda/kickjs v9.0.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{a as loadEnvFromSchema,i as loadEnv,n as defineEnv,o as reloadEnv,r as getEnv,s as resetEnvCache,t as baseEnvSchema}from"../env-
|
|
11
|
+
import{a as loadEnvFromSchema,c as withEnv,i as loadEnv,n as defineEnv,o as reloadEnv,r as getEnv,s as resetEnvCache,t as baseEnvSchema}from"../env-DMAdjj8Z.mjs";import{n as createConfigService,t as ConfigService}from"../config-16yw-Mxk.mjs";export{ConfigService,baseEnvSchema,createConfigService,defineEnv,getEnv,loadEnv,loadEnvFromSchema,reloadEnv,resetEnvCache,withEnv};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forinda/kickjs v9.0.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Felix Orinda
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import{i as loadEnv,o as reloadEnv}from"./env-DMAdjj8Z.mjs";import{Service}from"./core/decorators.mjs";import{t as __decorate}from"./decorate-CahyjuWW.mjs";let ConfigService=class{get env(){return loadEnv()}get(key,fallback){return this.env[key]??fallback}getAll(){return Object.freeze({...this.env})}reload(){reloadEnv(),loadEnv()}isProduction(){return this.env.NODE_ENV===`production`}isDevelopment(){return this.env.NODE_ENV===`development`}isTest(){return this.env.NODE_ENV===`test`}};ConfigService=__decorate([Service()],ConfigService);function createConfigService(schema){let SchemaConfigService=class{get env(){return loadEnv(schema)}get(key,fallback){return this.env[key]??fallback}getAll(){return Object.freeze({...this.env})}reload(){reloadEnv(),loadEnv(schema)}isProduction(){return this.env.NODE_ENV===`production`}isDevelopment(){return this.env.NODE_ENV===`development`}isTest(){return this.env.NODE_ENV===`test`}};return SchemaConfigService=__decorate([Service()],SchemaConfigService),SchemaConfigService}export{createConfigService as n,ConfigService as t};
|
|
12
|
+
//# sourceMappingURL=config-16yw-Mxk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-16yw-Mxk.mjs","names":[],"sources":["../src/config/config-service.ts"],"sourcesContent":["import type { z } from 'zod'\nimport { Service } from '../core/decorators'\nimport type { EnvKey } from '../core/decorators'\nimport { loadEnv, reloadEnv } from './env'\n\n/**\n * Constraint that locks the `key` argument to known `EnvKey` literals\n * once `kick typegen` has populated `KickEnv`. Mirrors the same trick\n * used by the `@Value` decorator: when `EnvKey` resolves to `never`\n * (no schema yet), accept any string for back-compat; once typegen\n * has run, require a known key.\n */\ntype ConfigKey<K extends string> = [EnvKey] extends [never] ? K : K & EnvKey\n\n/**\n * Resolve the return type for a `ConfigService.get(key)` call. When\n * `KickEnv` is populated and the key is a known one, returns the\n * schema-inferred type. Otherwise falls back to the caller-provided\n * generic `T` (default `any`) so existing call sites keep working.\n */\ntype ConfigValue<K extends string, T> = K extends EnvKey ? KickEnv[K] : T\n\n/**\n * Injectable service for accessing typed environment configuration.\n *\n * Once `kick typegen` has run against your `src/env.ts`, both `get()`\n * and `getAll()` are fully typed against the schema with **no extra\n * setup** — you just inject `ConfigService` and call it:\n *\n * ```ts\n * @Service()\n * class DatabaseService {\n * @Autowired() private readonly config!: ConfigService\n *\n * connect() {\n * const url = this.config.get('DATABASE_URL') // typed string\n * const port = this.config.get('PORT') // typed number\n * // const bad = this.config.get('NOPE') // tsc error\n * }\n * }\n * ```\n *\n * Without typegen the previous `<T = any>` generic still works, so\n * legacy code calling `config.get<string>('DATABASE_URL')` keeps\n * compiling.\n */\n@Service()\nexport class ConfigService {\n /**\n * Read the current parsed env on every access. We deliberately do **not**\n * snapshot in the constructor: if `ConfigService` is instantiated before\n * the user's `defineEnv()` schema has been registered (e.g. DI wires it\n * eagerly, or HMR's `reloadEnv()` cleared the cache between rebuilds),\n * an eager snapshot would freeze the base shape and never see the\n * extended values — even though `@Value()` (which reads lazily through\n * `Container._envResolver`) would resolve them correctly.\n *\n * `loadEnv()` is sticky: once an extended schema has been parsed it\n * keeps returning that schema's values, so this getter is cheap (one\n * cached lookup) and always consistent with `@Value`.\n */\n private get env(): Record<string, any> {\n return loadEnv() as Record<string, any>\n }\n\n /**\n * Get an env variable by key.\n *\n * - When `kick typegen` has run, `key` is constrained to known\n * `KickEnv` keys and the return type is inferred from the schema.\n * - When `KickEnv` is empty (no typegen run yet), any string is\n * accepted and `T` falls back to the explicit generic for\n * back-compat.\n */\n get<K extends string, T = any>(key: ConfigKey<K>): ConfigValue<K, T>\n /** With a fallback, returned when the value is unset (`undefined` or `null`). */\n get<K extends string, T = any>(\n key: ConfigKey<K>,\n fallback: NonNullable<ConfigValue<K, T>>,\n ): NonNullable<ConfigValue<K, T>>\n get(key: string, fallback?: unknown): unknown {\n return this.env[key] ?? fallback\n }\n\n /**\n * Get all env config as a readonly object. When `KickEnv` is\n * populated by typegen, returns `Readonly<KickEnv>` so the caller\n * gets autocomplete on every key.\n */\n getAll(): [EnvKey] extends [never] ? Readonly<Record<string, any>> : Readonly<KickEnv> {\n return Object.freeze({ ...this.env }) as any\n }\n\n /** Reload env from .env file (for HMR / file-watch scenarios) */\n reload(): void {\n reloadEnv()\n // Touch loadEnv() so the resolver + cache are repopulated immediately.\n loadEnv()\n }\n\n isProduction(): boolean {\n return this.env.NODE_ENV === 'production'\n }\n\n isDevelopment(): boolean {\n return this.env.NODE_ENV === 'development'\n }\n\n isTest(): boolean {\n return this.env.NODE_ENV === 'test'\n }\n}\n\n/**\n * Typed config service interface — provides autocomplete for env keys.\n * Use with `createConfigService()` for full type safety.\n */\nexport interface TypedConfigService<TEnv extends Record<string, any>> {\n get<K extends string & keyof TEnv>(key: K): TEnv[K]\n get<K extends string & keyof TEnv>(key: K, fallback: NonNullable<TEnv[K]>): NonNullable<TEnv[K]>\n getAll(): Readonly<TEnv>\n reload(): void\n isProduction(): boolean\n isDevelopment(): boolean\n isTest(): boolean\n}\n\n/**\n * Create a typed ConfigService class bound to a specific Zod env schema.\n * The returned class is DI-injectable via `@Service()` and provides\n * fully typed `get()` with key autocomplete.\n *\n * @example\n * ```ts\n * const envSchema = defineEnv((base) =>\n * base.extend({\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * })\n * )\n *\n * // Create a typed, injectable config service\n * export const AppConfigService = createConfigService(envSchema)\n * export type AppConfigService = InstanceType<typeof AppConfigService>\n *\n * // In a controller or service — inject it:\n * @Controller()\n * class UserController {\n * constructor(@Inject(AppConfigService) private config: AppConfigService) {\n * const dbUrl = config.get('DATABASE_URL') // string — autocompletes!\n * const bad = config.get('NOPE') // TS error\n * }\n * }\n * ```\n */\nexport function createConfigService<T extends z.ZodObject<any>>(schema: T) {\n type TEnv = z.infer<T>\n\n @Service()\n class SchemaConfigService implements TypedConfigService<TEnv> {\n /** See note on `ConfigService.env` — same eager-snapshot hazard. */\n private get env(): TEnv {\n return loadEnv(schema)\n }\n\n get<K extends string & keyof TEnv>(key: K): TEnv[K]\n get<K extends string & keyof TEnv>(key: K, fallback: NonNullable<TEnv[K]>): NonNullable<TEnv[K]>\n get(key: string, fallback?: unknown): unknown {\n return (this.env as Record<string, unknown>)[key] ?? fallback\n }\n\n getAll(): Readonly<TEnv> {\n return Object.freeze({ ...this.env })\n }\n\n reload(): void {\n reloadEnv()\n loadEnv(schema)\n }\n\n isProduction(): boolean {\n return this.env.NODE_ENV === 'production'\n }\n\n isDevelopment(): boolean {\n return this.env.NODE_ENV === 'development'\n }\n\n isTest(): boolean {\n return this.env.NODE_ENV === 'test'\n }\n }\n\n return SchemaConfigService as {\n new (): TypedConfigService<TEnv>\n }\n}\n"],"mappings":";;;;;;;;;;4JA+CO,IAAM,cAAN,KAAoB,CAczB,IAAY,KAA2B,CACrC,OAAO,QAAQ,CACjB,CAiBA,IAAI,IAAa,SAA6B,CAC5C,OAAO,KAAK,IAAI,MAAQ,QAC1B,CAOA,QAAuF,CACrF,OAAO,OAAO,OAAO,CAAE,GAAG,KAAK,GAAI,CAAC,CACtC,CAGA,QAAe,CACb,UAAU,EAEV,QAAQ,CACV,CAEA,cAAwB,CACtB,OAAO,KAAK,IAAI,WAAa,YAC/B,CAEA,eAAyB,CACvB,OAAO,KAAK,IAAI,WAAa,aAC/B,CAEA,QAAkB,CAChB,OAAO,KAAK,IAAI,WAAa,MAC/B,CACF,EAjEC,cAAA,WAAA,CAAA,QAAQ,CAAA,EAAA,aAAA,EA6GT,SAAgB,oBAAgD,OAAW,CAGzE,IACM,oBADN,KAC8D,CAE5D,IAAY,KAAY,CACtB,OAAO,QAAQ,MAAM,CACvB,CAIA,IAAI,IAAa,SAA6B,CAC5C,OAAQ,KAAK,IAAgC,MAAQ,QACvD,CAEA,QAAyB,CACvB,OAAO,OAAO,OAAO,CAAE,GAAG,KAAK,GAAI,CAAC,CACtC,CAEA,QAAe,CACb,UAAU,EACV,QAAQ,MAAM,CAChB,CAEA,cAAwB,CACtB,OAAO,KAAK,IAAI,WAAa,YAC/B,CAEA,eAAyB,CACvB,OAAO,KAAK,IAAI,WAAa,aAC/B,CAEA,QAAkB,CAChB,OAAO,KAAK,IAAI,WAAa,MAC/B,CACF,EAEA,MAnCC,qBAAA,WAAA,CAAA,QAAQ,CAAA,EAAA,mBAAA,EAmCF,mBAGT"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forinda/kickjs v9.0.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Felix Orinda
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import{METADATA}from"./core/interfaces.mjs";import{createLogger}from"./core/logger.mjs";import{i as getMetaRecord,n as getClassMetaOrUndefined,o as getMethodMetaOrUndefined,r as getMetaMap,t as getClassMeta}from"./metadata-BQG1MmqL.mjs";import"reflect-metadata";let impl=null;function _setAssetResolver(fn){impl=fn}function resolveAssetViaSlot(namespace,key){if(!impl)throw Error(`@Asset('${namespace}/${key}'): the asset manager is not loaded. Import it (any import from '@forinda/kickjs' loads it) — note @Asset reads from the filesystem and is not supported on edge runtimes.`);return impl(namespace,key)}var KickError=class extends Error{code;summary;cause;fix;docsUrl;context;constructor(init){super(formatKickError(init,{color:!1})),this.name=`KickError`,this.code=init.code,this.summary=init.summary,init.cause!==void 0&&(this.cause=init.cause),init.fix!==void 0&&(this.fix=init.fix),init.docsUrl!==void 0&&(this.docsUrl=init.docsUrl),init.context!==void 0&&(this.context=init.context)}};function ansiEnabled(){return process.env.NO_COLOR?!1:process.env.FORCE_COLOR?!0:process.stderr?.isTTY===!0}function paint(text,code,enabled){return enabled?`[${code}m${text}[0m`:text}const c={red:(s,enabled)=>paint(s,`31`,enabled),bold:(s,enabled)=>paint(s,`1`,enabled),dim:(s,enabled)=>paint(s,`2`,enabled),cyan:(s,enabled)=>paint(s,`36`,enabled),blue:(s,enabled)=>paint(s,`34`,enabled),underline:(s,enabled)=>paint(s,`4`,enabled)};function formatKickError(err,options={}){let color=options.color??ansiEnabled(),lines=[],code=c.bold(c.red(err.code,color),color);if(lines.push(`${code}: ${err.summary}`),err.cause){lines.push(``,` ${c.bold(c.dim(`Cause:`,color),color)}`);for(let line of err.cause.split(`
|
|
12
|
+
`))lines.push(` ${highlightInline(line,color)}`)}if(err.fix){lines.push(``,` ${c.bold(c.dim(`Fix:`,color),color)}`);for(let line of err.fix.split(`
|
|
13
|
+
`))lines.push(` ${highlightInline(line,color)}`)}return err.docsUrl&&(lines.push(``,` ${c.bold(c.dim(`Docs:`,color),color)}`),lines.push(` ${c.underline(c.blue(err.docsUrl,color),color)}`)),lines.join(`
|
|
14
|
+
`)}function highlightInline(line,color){return color?line.replace(/`([^`]+)`/g,(_,inner)=>`\`${c.cyan(inner,!0)}\``):line}const DOCS_BASE=`https://kickjs.app`;function noProviderError(tokenName){return new KickError({code:`KICK001`,summary:`No provider for ${tokenName}`,cause:`\`${tokenName}\` was requested from the DI container but no binding is registered.
|
|
15
|
+
This usually means one of:
|
|
16
|
+
• The class is decorated with @Service() / @Repository() / @Controller(),
|
|
17
|
+
but its enclosing module isn't passed to bootstrap({ modules: [...] }).
|
|
18
|
+
• The class isn't decorated at all (decorators register the binding).
|
|
19
|
+
• You're injecting a token (created with createToken()) that nothing
|
|
20
|
+
provides — add a Container.register(TOKEN, ...) call or a module that
|
|
21
|
+
binds it.`,fix:`If \`${tokenName}\` lives in a module, add the module to bootstrap:
|
|
22
|
+
|
|
23
|
+
bootstrap({
|
|
24
|
+
modules: [
|
|
25
|
+
UsersModule, // add this
|
|
26
|
+
OtherModule,
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
If it's a custom token, register it explicitly:
|
|
31
|
+
|
|
32
|
+
const TENANT_REPO = createToken<TenantRepo>('TENANT_REPO')
|
|
33
|
+
Container.getInstance().register(TENANT_REPO, { useClass: PrismaTenantRepo })`,docsUrl:`${DOCS_BASE}/guide/dependency-injection#registering-services`,context:{token:tokenName}})}function requestScopeMiddlewareMissingError(tokenName){return new KickError({code:`KICK002`,summary:`Cannot resolve REQUEST-scoped "${tokenName}" — request scope middleware not mounted`,cause:`\`${tokenName}\` has \`scope: Scope.REQUEST\` but no AsyncLocalStorage frame is wired.
|
|
34
|
+
The framework's request-scope middleware sets up that frame; without it,
|
|
35
|
+
REQUEST-scoped resolutions have nowhere to cache the per-request instance.`,fix:`Add \`requestScopeMiddleware()\` to your global middleware pipeline,
|
|
36
|
+
or let the framework auto-mount it by using \`contextStore: 'auto'\`
|
|
37
|
+
(the default — only check this if you set it to 'manual' explicitly):
|
|
38
|
+
|
|
39
|
+
bootstrap({
|
|
40
|
+
modules,
|
|
41
|
+
// contextStore: 'auto', // default; no need to set
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
If you set \`contextStore: 'manual'\` deliberately (rare), you own the
|
|
45
|
+
ALS wrapping — call \`requestStore.run({...}, () => ...)\` around the
|
|
46
|
+
code that resolves the binding.`,docsUrl:`${DOCS_BASE}/guide/dependency-injection#scopes`,context:{token:tokenName}})}function requestScopeOutsideRequestError(tokenName){return new KickError({code:`KICK003`,summary:`Cannot resolve REQUEST-scoped "${tokenName}" outside an HTTP request`,cause:`\`${tokenName}\` has \`scope: Scope.REQUEST\` and was resolved outside
|
|
47
|
+
any HTTP request context. The request-scope middleware is wired, but
|
|
48
|
+
the call happened before a request started (e.g. in a top-level
|
|
49
|
+
import, a constructor, a cron job, a test without setup).`,fix:`If the caller is request-scoped (controller, request-scoped service),
|
|
50
|
+
make sure the call happens inside the request lifecycle, not at module
|
|
51
|
+
load time.
|
|
52
|
+
|
|
53
|
+
In tests, wrap setup in an ALS frame manually:
|
|
54
|
+
|
|
55
|
+
import { requestStore } from '@forinda/kickjs'
|
|
56
|
+
|
|
57
|
+
requestStore.run(
|
|
58
|
+
{ requestId: 'test', instances: new Map(), values: new Map() },
|
|
59
|
+
() => {
|
|
60
|
+
const svc = container.resolve(MyRequestScopedService)
|
|
61
|
+
// ...
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
If the caller is a SINGLETON, change \`${tokenName}\` to TRANSIENT or
|
|
66
|
+
SINGLETON scope, or inject a factory instead of the instance directly.`,docsUrl:`${DOCS_BASE}/guide/dependency-injection#scopes`,context:{token:tokenName}})}function envValueMissingError(envKey){return new KickError({code:`KICK004`,summary:`@Value('${envKey}'): environment variable not set and no default provided`,cause:`\`@Value('${envKey}')\` resolved but the env var isn't present and the
|
|
67
|
+
decorator didn't declare a default. Possible reasons:
|
|
68
|
+
• The variable is genuinely missing from your environment / .env file.
|
|
69
|
+
• Your \`src/env.ts\` calls \`loadEnv(envSchema)\` but \`src/index.ts\`
|
|
70
|
+
doesn't import \`./env\` before \`bootstrap()\`. Without that import
|
|
71
|
+
the schema never runs and ConfigService stays empty — \`@Value\`
|
|
72
|
+
only finds the var via the \`process.env\` fallback.`,fix:`Either set the variable in your shell / .env file:
|
|
73
|
+
|
|
74
|
+
${envKey}=...
|
|
75
|
+
|
|
76
|
+
Or add a default at the decorator:
|
|
77
|
+
|
|
78
|
+
@Value('${envKey}', { default: 'fallback' })
|
|
79
|
+
private value!: string
|
|
80
|
+
|
|
81
|
+
If you're using a schema, make sure \`src/env.ts\` is imported from
|
|
82
|
+
\`src/index.ts\` BEFORE bootstrap runs:
|
|
83
|
+
|
|
84
|
+
// src/index.ts
|
|
85
|
+
import 'reflect-metadata'
|
|
86
|
+
import './env' // ← this line is required
|
|
87
|
+
import { bootstrap } from '@forinda/kickjs'
|
|
88
|
+
// ...`,docsUrl:`${DOCS_BASE}/guide/configuration#wiring-the-schema-at-startup`,context:{envKey}})}function moduleRouteMissingControllerError(mountPath){return new KickError({code:`KICK005`,summary:`Module route at ${mountPath} requires either 'controller' or 'router'`,cause:`A module's \`routes()\` returned an entry for \`${mountPath}\` but didn't
|
|
89
|
+
specify how to build the router. The framework needs either a decorated
|
|
90
|
+
controller class (it calls \`buildRoutes()\` internally) or a hand-built
|
|
91
|
+
Express Router.`,fix:`Pass \`controller:\` for the common case:
|
|
92
|
+
|
|
93
|
+
routes() {
|
|
94
|
+
return {
|
|
95
|
+
path: '${mountPath}',
|
|
96
|
+
controller: UsersController,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Or pass \`router:\` for full control:
|
|
101
|
+
|
|
102
|
+
routes() {
|
|
103
|
+
return {
|
|
104
|
+
path: '${mountPath}',
|
|
105
|
+
router: myExpressRouter,
|
|
106
|
+
}
|
|
107
|
+
}`,docsUrl:`${DOCS_BASE}/guide/modules`,context:{mountPath}})}function duplicateRouteError(method,path,owner,prior){return new KickError({code:`KICK006`,summary:`Duplicate route: ${method} ${path} is registered twice`,cause:`\`${owner}\` registers \`${method} ${path}\`, but that verb + path is
|
|
108
|
+
already claimed by \`${prior}\`. Only one handler can win at dispatch
|
|
109
|
+
time, and which one depends on the engine — while \`kick typegen\` and
|
|
110
|
+
the typed client may describe the other. Param names are ignored when
|
|
111
|
+
comparing (\`/:id\` and \`/:taskId\` are the same route).`,fix:`Change one of the two paths, remove the duplicate handler, or mount
|
|
112
|
+
the second controller under a different module path / version:
|
|
113
|
+
|
|
114
|
+
routes() {
|
|
115
|
+
return { path: '/tasks', controller: TasksController, version: 2 }
|
|
116
|
+
}`,docsUrl:`${DOCS_BASE}/guide/modules`,context:{method,path,owner,prior}})}function http2UnsupportedRuntimeError(runtime){return new KickError({code:`KICK007`,summary:`The ${runtime} runtime cannot serve HTTP/2`,cause:`\`bootstrap({ server: { http2: true } })\` asks for an HTTP/2 server, but the
|
|
117
|
+
\`${runtime}\` runtime does not support Node's HTTP/2 compatibility layer —
|
|
118
|
+
requests would hang instead of being answered.`,fix:`Pick one:
|
|
119
|
+
• Switch to a runtime that serves HTTP/2:
|
|
120
|
+
bootstrap({ runtime: fastifyRuntime(), server: { tls, http2: true } })
|
|
121
|
+
(or h3Runtime()).
|
|
122
|
+
• Drop \`http2\` and keep HTTPS over HTTP/1.1: server: { tls }.
|
|
123
|
+
• Terminate HTTP/2 at your proxy / load balancer and serve HTTP/1.1 to it.`,docsUrl:`${DOCS_BASE}/guide/http-runtimes#https-and-http-2`,context:{runtime}})}function http2RequiresTlsError(){return new KickError({code:`KICK008`,summary:`server.http2 requires server.tls`,cause:"`bootstrap({ server: { http2: true } })` was set without `tls`. Browsers only\nnegotiate HTTP/2 over TLS, and KickJS does not serve cleartext HTTP/2 (h2c).",fix:`Pass the certificate and key alongside http2:
|
|
124
|
+
|
|
125
|
+
server: {
|
|
126
|
+
tls: { key: readFileSync('key.pem'), cert: readFileSync('cert.pem') },
|
|
127
|
+
http2: true,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Or remove \`http2\` if TLS is terminated in front of the app.`,docsUrl:`${DOCS_BASE}/guide/http-runtimes#https-and-http-2`})}function tlsCredentialsError(problem){return new KickError({code:`KICK009`,summary:`server.tls is not usable: ${problem}`,cause:`\`bootstrap({ server: { tls } })\` was given TLS options the server cannot use:
|
|
131
|
+
${problem}. Node would start anyway and fail every TLS handshake.`,fix:"Give the server an identity:\n • `key` and `cert` together (PEM, matching each other), or\n • `pfx` (with `passphrase` if it is encrypted), or\n • `SNICallback` / `pskCallback` for per-host or PSK setups.\n\n server: { tls: { key: readFileSync('key.pem'), cert: readFileSync('cert.pem') } }",docsUrl:`${DOCS_BASE}/guide/http-runtimes#https-and-http-2`,context:{problem}})}const INJECTION_TOKEN=Symbol.for(`@kickjs/InjectionToken`);function createToken(name){return Object.freeze({[INJECTION_TOKEN]:!0,name})}function isInjectionToken(value){return typeof value==`object`&&!!value&&value[INJECTION_TOKEN]===!0}const log=createLogger(`Container`);function createReg(fields){return{kind:`unknown`,resolveCount:0,postConstructStatus:`skipped`,dependencies:[],...fields}}function tokenName(token){return typeof token==`symbol`?token.toString():isInjectionToken(token)?token.name:token?.name||String(token)}function getPersistentStore(){return globalThis.__kickjs_persistent||(globalThis.__kickjs_persistent={factories:[],instances:[],resolvedInstances:new Map}),globalThis.__kickjs_persistent}var Container=class Container{static instance;registrations=new Map;resolving=new Set;changeListeners=new Set;pendingChanges=[];notifyTimer=null;static DEBOUNCE_MS=50;static _onReady=null;static _onReset=null;static _onCreate=null;static _envResolver=null;isGlobal=!1;static _requestStoreProvider=null;static getInstance(){return Container.instance||(Container.instance=new Container,Container.instance.isGlobal=!0),Container._onReady&&=(Container._onReady(Container.instance),null),Container.instance}static reset(){Container.instance=new Container,Container.instance.isGlobal=!0,Container._onReset?.(Container.instance);let store=getPersistentStore();for(let{token,factory,scope}of store.factories)if(!Container.instance.has(token)){let name=tokenName(token),existing=store.resolvedInstances.get(name);Container.instance.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existing,resolveCount:+!!existing,postConstructStatus:existing?`completed`:`skipped`}))}for(let{token,instance}of store.instances)Container.instance.has(token)||Container.instance.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1}))}static create(){let container=new Container;return Container._onCreate?.(container),container}register(token,target,scope=`singleton`){let kind=getClassMeta(METADATA.CLASS_KIND,target,`unknown`),reg=createReg({target,scope,kind,dependencies:this.extractDependencies(target)});this.registrations.set(token,reg),typeof token==`function`&&token.name&&this.registrations.set(`__hmr__${token.name}`,reg),this.emit(token,`registered`,kind)}registerFactory(token,factory,scope=`singleton`){let store=getPersistentStore(),name=tokenName(token),factoryUnchanged=(this.isGlobal?store.factories.find(r=>tokenName(r.token)===name):void 0)?.factory===factory,existingInstance=this.isGlobal&&factoryUnchanged?store.resolvedInstances.get(name):void 0;if(this.isGlobal&&!factoryUnchanged&&store.resolvedInstances.delete(name),this.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existingInstance,resolveCount:+!!existingInstance,postConstructStatus:existingInstance?`completed`:`skipped`})),this.isGlobal){let idx=store.factories.findIndex(r=>tokenName(r.token)===name);idx>=0?store.factories[idx]={token,factory,scope}:store.factories.push({token,factory,scope})}this.emit(name,`registered`,`factory`)}registerInstance(token,instance){let store=getPersistentStore(),name=tokenName(token);if(this.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1,firstResolvedAt:Date.now(),lastResolvedAt:Date.now()})),this.isGlobal){store.resolvedInstances.set(name,instance);let idx=store.instances.findIndex(r=>tokenName(r.token)===name);idx>=0?store.instances[idx]={token,instance}:store.instances.push({token,instance})}this.emit(name,`registered`,`instance`)}has(token){return this.registrations.has(token)}getRegistrations(){let entries=[];for(let[token,reg]of this.registrations)entries.push({token:tokenName(token),scope:reg.scope,kind:reg.kind,instantiated:reg.scope===`singleton`?reg.instance!==void 0:reg.resolveCount>0,resolveCount:reg.resolveCount,lastResolvedAt:reg.lastResolvedAt,firstResolvedAt:reg.firstResolvedAt,resolveDurationMs:reg.resolveDurationMs,postConstructStatus:reg.postConstructStatus,dependencies:reg.dependencies});return entries}resolve(token){let reg=this.registrations.get(token);if(!reg&&typeof token==`function`&&token.name&&(reg=this.registrations.get(`__hmr__${token.name}`)),!reg)throw noProviderError(tokenName(token));if(reg.scope===`singleton`&®.instance!==void 0)return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),reg.instance;if(reg.scope===`request`){if(!Container._requestStoreProvider)throw requestScopeMiddlewareMissingError(tokenName(token));let store=Container._requestStoreProvider();if(!store)throw requestScopeOutsideRequestError(tokenName(token));if(store.values.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.values.get(token);if(store.instances.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.instances.get(token);if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance,this.isGlobal&&getPersistentStore().resolvedInstances.set(tokenName(token),instance)),this.emit(token,`resolved`,reg.kind),instance}if(this.resolving.has(token)){let chain=[...this.resolving].map(tokenName);throw chain.push(tokenName(token)),Error(`Circular dependency detected: ${chain.join(` -> `)}`)}this.resolving.add(token);try{let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance),this.emit(token,`resolved`,reg.kind),instance}finally{this.resolving.delete(token)}}bootstrap(){}onChange(callback){return this.changeListeners.add(callback),()=>this.changeListeners.delete(callback)}invalidate(token){let name=tokenName(token),reg=this.registrations.get(token)??this.registrations.get(`__hmr__${name}`);if(reg&&!getPersistentStore().resolvedInstances.has(name)){reg.instance=void 0,reg.resolveCount=0,reg.postConstructStatus=`skipped`,this.emit(name,`invalidated`,reg.kind);for(let[depToken,depReg]of this.registrations)depReg.dependencies.includes(name)&&this.invalidate(depToken)}}emit(rawToken,event,kind){if(this.changeListeners.size===0)return;let token=tokenName(rawToken),existing=this.pendingChanges.find(p=>p.token===token&&p.event===event);existing?existing.timestamp=Date.now():this.pendingChanges.push({token,event,kind,timestamp:Date.now()}),this.notifyTimer&&clearTimeout(this.notifyTimer),this.notifyTimer=setTimeout(()=>{let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}},Container.DEBOUNCE_MS),this.notifyTimer.unref?.()}flushChanges(){this.notifyTimer&&=(clearTimeout(this.notifyTimer),null);let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}}buildPlan(reg){let paramTypes=getClassMeta(METADATA.PARAM_TYPES,reg.target,[]),injectTokens=getMetaRecord(METADATA.INJECT,reg.target),maxInjectIndex=Object.keys(injectTokens).length>0?Math.max(...Object.keys(injectTokens).map(Number))+1:0,paramCount=Math.max(paramTypes.length,maxInjectIndex),ctorTokens=[];for(let index=0;index<paramCount;index++){let token=injectTokens[index]||paramTypes[index];if(!token)throw Error(`Cannot resolve constructor parameter at index ${index} of ${tokenName(reg.target)}. No @Inject token and no design:paramtypes metadata (tsx/ts-node don't emit it). Add @Inject(Token) to every constructor parameter.`);if(reg.scope===`singleton`){let depReg=this.registrations.get(token);if(depReg&&depReg.scope===`request`){let parent=tokenName(reg.target),dep=tokenName(token),fix=getClassMetaOrUndefined(METADATA.CLASS_KIND,reg.target)===`controller`?`@Controller() is always SINGLETON, so inject "${dep}" with @Autowired() instead of the constructor.`:`Give "${parent}" TRANSIENT or REQUEST scope (e.g. @Service({ scope: Scope.REQUEST })), or inject "${dep}" with @Autowired() instead of the constructor.`;throw Error(`Cannot inject REQUEST-scoped "${dep}" into SINGLETON "${parent}". Singletons outlive requests. ${fix}`)}}ctorTokens.push(token)}let autowiredProps=getMetaMap(METADATA.AUTOWIRED,reg.target.prototype),autowired=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE,reg.target.prototype,prop);resolvedToken&&autowired.push([prop,resolvedToken])}let values=[...getMetaMap(METADATA.VALUE,reg.target.prototype).entries()],assetProps=getMetaMap(METADATA.ASSET,reg.target.prototype),assets=[];for(let[prop,config]of assetProps){let slashIdx=config.assetKey.indexOf(`/`);if(slashIdx===-1)throw Error(`@Asset('${config.assetKey}'): asset key must include a '/' separator (e.g. 'mails/welcome').`);assets.push([prop,{namespace:config.assetKey.slice(0,slashIdx),key:config.assetKey.slice(slashIdx+1)}])}return{ctorTokens,autowired,values,assets,postConstruct:getClassMetaOrUndefined(METADATA.POST_CONSTRUCT,reg.target.prototype)}}createInstance(reg){let plan=reg.plan??=this.buildPlan(reg),args=[];for(let token of plan.ctorTokens)args.push(this.resolve(token));let instance=new reg.target(...args);this.injectProperties(instance,plan);let postConstruct=plan.postConstruct;if(postConstruct&&typeof instance[postConstruct]==`function`)try{let result=instance[postConstruct]();result&&typeof result.then==`function`?(reg.postConstructStatus=`pending`,result.then(()=>{reg.postConstructStatus=`completed`}).catch(()=>{reg.postConstructStatus=`failed`}),log.warn(`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() returned a Promise but the container is synchronous. The async operation will not be awaited.`)):reg.postConstructStatus=`completed`}catch(err){reg.postConstructStatus=`failed`,log.error(err,`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() threw an error`)}return instance}extractDependencies(target){let paramTypes=getClassMeta(METADATA.PARAM_TYPES,target,[]),injectTokens=getMetaRecord(METADATA.INJECT,target),ctorDeps=paramTypes.map((type,index)=>tokenName(injectTokens[index]||type)),autowiredProps=getMetaMap(METADATA.AUTOWIRED,target.prototype),propDeps=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE,target.prototype,prop);resolvedToken&&propDeps.push(tokenName(resolvedToken))}return[...new Set([...ctorDeps,...propDeps])]}injectProperties(instance,plan){for(let[prop,resolvedToken]of plan.autowired)Object.defineProperty(instance,prop,{get:()=>{let value=this.resolve(resolvedToken);return this.registrations.get(resolvedToken)?.scope===`singleton`&&Object.defineProperty(instance,prop,{value,enumerable:!0,configurable:!0,writable:!1}),value},enumerable:!0,configurable:!0});for(let[prop,config]of plan.values)Object.defineProperty(instance,prop,{get(){if(Container._envResolver){let val=Container._envResolver(config.envKey);if(val!==void 0)return val}let val=process.env[config.envKey];if(val!==void 0)return val;if(config.defaultValue!==void 0)return config.defaultValue;throw envValueMissingError(config.envKey)},enumerable:!0,configurable:!0});for(let[prop,{namespace,key}]of plan.assets)Object.defineProperty(instance,prop,{get(){return resolveAssetViaSlot(namespace,key)},enumerable:!0,configurable:!0})}};export{isInjectionToken as a,http2UnsupportedRuntimeError as c,KickError as d,formatKickError as f,createToken as i,moduleRouteMissingControllerError as l,tokenName as n,duplicateRouteError as o,_setAssetResolver as p,INJECTION_TOKEN as r,http2RequiresTlsError as s,Container as t,tlsCredentialsError as u};
|
|
132
|
+
//# sourceMappingURL=container-Dw45sa0Y.mjs.map
|