@warlock.js/core 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/esm/application/application.d.mts +126 -4
  3. package/esm/application/application.d.mts.map +1 -1
  4. package/esm/application/application.mjs +133 -0
  5. package/esm/application/application.mjs.map +1 -1
  6. package/esm/application/index.d.mts +1 -1
  7. package/esm/cli/types.d.mts +2 -1
  8. package/esm/cli/types.d.mts.map +1 -1
  9. package/esm/config/config-getter.mjs +5 -5
  10. package/esm/config/config-getter.mjs.map +1 -1
  11. package/esm/config/config-loader.mjs +2 -2
  12. package/esm/config/config-loader.mjs.map +1 -1
  13. package/esm/connectors/access-connector.mjs +2 -2
  14. package/esm/connectors/access-connector.mjs.map +1 -1
  15. package/esm/connectors/cache-connector.mjs +2 -2
  16. package/esm/connectors/cache-connector.mjs.map +1 -1
  17. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  18. package/esm/connectors/connectors-manager.mjs +4 -1
  19. package/esm/connectors/connectors-manager.mjs.map +1 -1
  20. package/esm/connectors/database-connector.mjs +2 -2
  21. package/esm/connectors/database-connector.mjs.map +1 -1
  22. package/esm/connectors/herald-connector.mjs +2 -2
  23. package/esm/connectors/herald-connector.mjs.map +1 -1
  24. package/esm/connectors/http-connector.d.mts +8 -0
  25. package/esm/connectors/http-connector.d.mts.map +1 -1
  26. package/esm/connectors/http-connector.mjs +40 -7
  27. package/esm/connectors/http-connector.mjs.map +1 -1
  28. package/esm/connectors/logger-connector.mjs +2 -2
  29. package/esm/connectors/logger-connector.mjs.map +1 -1
  30. package/esm/connectors/mail-connector.mjs +2 -2
  31. package/esm/connectors/mail-connector.mjs.map +1 -1
  32. package/esm/connectors/notifications-connector.mjs +2 -2
  33. package/esm/connectors/notifications-connector.mjs.map +1 -1
  34. package/esm/connectors/socket-connector.mjs +3 -3
  35. package/esm/connectors/socket-connector.mjs.map +1 -1
  36. package/esm/dev-server/development-server.mjs +7 -0
  37. package/esm/dev-server/development-server.mjs.map +1 -1
  38. package/esm/dev-server/loader/transpile-cache.mjs +1 -1
  39. package/esm/http/config.mjs +2 -2
  40. package/esm/http/config.mjs.map +1 -1
  41. package/esm/http/createHttpApplication.mjs +2 -2
  42. package/esm/http/createHttpApplication.mjs.map +1 -1
  43. package/esm/http/health.d.mts +57 -0
  44. package/esm/http/health.d.mts.map +1 -0
  45. package/esm/http/health.mjs +75 -0
  46. package/esm/http/health.mjs.map +1 -0
  47. package/esm/http/index.d.mts +2 -1
  48. package/esm/http/index.mjs +2 -1
  49. package/esm/http/middleware/idempotency.middleware.mjs +5 -5
  50. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  51. package/esm/http/middleware/inject-request-context.mjs +2 -2
  52. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  53. package/esm/http/middleware/maintenance.middleware.mjs +4 -4
  54. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  55. package/esm/http/plugins.mjs +9 -9
  56. package/esm/http/plugins.mjs.map +1 -1
  57. package/esm/http/response.mjs +5 -5
  58. package/esm/http/response.mjs.map +1 -1
  59. package/esm/http/server.d.mts +16 -1
  60. package/esm/http/server.d.mts.map +1 -1
  61. package/esm/http/server.mjs +23 -3
  62. package/esm/http/server.mjs.map +1 -1
  63. package/esm/http/types.d.mts +45 -0
  64. package/esm/http/types.d.mts.map +1 -1
  65. package/esm/index.d.mts +5 -4
  66. package/esm/index.mjs +6 -5
  67. package/esm/production/production-builder.mjs +2 -2
  68. package/esm/production/production-builder.mjs.map +1 -1
  69. package/esm/utils/environment.d.mts +6 -1
  70. package/esm/utils/environment.d.mts.map +1 -1
  71. package/esm/utils/environment.mjs.map +1 -1
  72. package/esm/utils/index.d.mts +1 -1
  73. package/esm/utils/paths.mjs +2 -2
  74. package/esm/utils/paths.mjs.map +1 -1
  75. package/esm/validation/validateAll.mjs +2 -2
  76. package/esm/validation/validateAll.mjs.map +1 -1
  77. package/llms-full.txt +164 -2
  78. package/llms.txt +2 -1
  79. package/package.json +9 -9
  80. package/skills/health-checks/SKILL.md +98 -0
  81. package/skills/use-app-context/SKILL.md +62 -2
package/CHANGELOG.md CHANGED
@@ -4,7 +4,22 @@ All notable changes to `@warlock.js/core` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
- ## [Unreleased]
7
+ ## 4.4.0 - 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - `Application.onceBooted(cb)` — run a callback once the app is fully booted (fires immediately if already booted)
12
+ - `Application.whenBooted()` — promise that resolves with the boot context when the app is fully booted
13
+ - `Application.isBooted` — whether the app has finished booting
14
+ - `Application.onShutdown(cb)` — run teardown once on shutdown, before connectors stop (mirror of `onceBooted`)
15
+ - `Application.isShuttingDown` — whether shutdown has begun
16
+ - built-in `/health` (liveness) and `/ready` (readiness) endpoints with a `health` check registry (`health.addCheck`)
17
+ - graceful HTTP shutdown — drains in-flight requests on shutdown, bounded by `http.gracefulShutdown.timeout`
18
+ - `http.health.*` config to toggle or rename the health endpoints
19
+
20
+ ### Fixed
21
+
22
+ - connector shutdown no longer reverses the connector list in place (could corrupt order on a repeated shutdown)
8
23
 
9
24
  ## 4.3.0 - 2026-06-21
10
25
 
@@ -1,6 +1,33 @@
1
- import { Environment } from "../utils/environment.mjs";
1
+ import { Environment, RuntimeStrategy } from "../utils/environment.mjs";
2
2
 
3
3
  //#region ../@warlock.js/core/src/application/application.d.ts
4
+ /**
5
+ * Snapshot of the global facts known the moment the application finished
6
+ * booting: every connector (early + late) is active and all app files
7
+ * (locales, events, main, routes) are loaded. Passed to every
8
+ * `Application.onceBooted` listener and resolved from `Application.whenBooted`.
9
+ */
10
+ type BootContext = {
11
+ environment: Environment;
12
+ runtimeStrategy: RuntimeStrategy;
13
+ /**
14
+ * Wall-clock milliseconds the boot took. The dev server measures it; the
15
+ * production entry omits it.
16
+ */
17
+ bootDurationMs?: number;
18
+ };
19
+ /**
20
+ * A callback registered through `Application.onceBooted`. May be async — its
21
+ * rejection is caught and logged so a single listener can never break boot or
22
+ * the other listeners.
23
+ */
24
+ type BootListener = (context: BootContext) => void | Promise<void>;
25
+ /**
26
+ * A callback registered through `Application.onShutdown`. Runs once, while the
27
+ * connectors (db, cache, http) are still up, so it can release app-owned
28
+ * resources cleanly. May be async — its rejection is caught and logged.
29
+ */
30
+ type ShutdownListener = () => void | Promise<void>;
4
31
  declare class Application {
5
32
  /**
6
33
  * Project start time regarding the process start time
@@ -9,7 +36,29 @@ declare class Application {
9
36
  /**
10
37
  * Runtime strategy
11
38
  */
12
- static runtimeStrategy: "production" | "development";
39
+ static runtimeStrategy: RuntimeStrategy;
40
+ /**
41
+ * Whether the application has finished booting. Flipped once by `markBooted`.
42
+ */
43
+ private static booted;
44
+ /**
45
+ * The boot context, set once booted so it can be replayed to late subscribers.
46
+ */
47
+ private static bootContext;
48
+ /**
49
+ * Listeners queued before boot completed, drained in registration order by
50
+ * `markBooted`.
51
+ */
52
+ private static bootListeners;
53
+ /**
54
+ * Whether the application has begun (or finished) shutting down. Flipped once
55
+ * by `runShutdownHooks`.
56
+ */
57
+ private static shuttingDown;
58
+ /**
59
+ * Teardown callbacks, run once when shutdown begins.
60
+ */
61
+ private static shutdownListeners;
13
62
  /**
14
63
  * Get framework version
15
64
  */
@@ -17,7 +66,80 @@ declare class Application {
17
66
  /**
18
67
  * Set the runtime strategy
19
68
  */
20
- static setRuntimeStrategy(strategy: "production" | "development"): void;
69
+ static setRuntimeStrategy(strategy: RuntimeStrategy): void;
70
+ /**
71
+ * Whether the application has finished booting — every connector in both
72
+ * the early and late phases is active and all app files (locales, events,
73
+ * main, routes) have been loaded.
74
+ */
75
+ static get isBooted(): boolean;
76
+ /**
77
+ * Run a callback once the application is fully booted.
78
+ *
79
+ * App files (`main` / `events` / `routes` / locales) are imported BEFORE the
80
+ * late-phase connectors (http, socket) start, so a listener registered at
81
+ * import time waits for the whole sequence to finish before it runs. If the
82
+ * application is already booted, the callback runs on the next microtask —
83
+ * so a late subscriber never silently misses the event.
84
+ *
85
+ * @example
86
+ * Application.onceBooted(({ environment }) => {
87
+ * log.info("app", "booted", `ready in ${environment}`);
88
+ * });
89
+ */
90
+ static onceBooted(listener: BootListener): void;
91
+ /**
92
+ * Promise form of {@link onceBooted} — resolves with the boot context once
93
+ * the application is booted, or immediately if it already is.
94
+ */
95
+ static whenBooted(): Promise<BootContext>;
96
+ /**
97
+ * Flip the boot latch and drain every queued listener. The dev server and
98
+ * the production entry call this once, right after the late phase starts. It
99
+ * is idempotent — a second call is a no-op, so a double-invoke can never
100
+ * double-fire listeners.
101
+ *
102
+ * @internal Framework entry points call this; application code must not.
103
+ */
104
+ static markBooted(context: BootContext): void;
105
+ /**
106
+ * Invoke a single boot listener, isolating its failure so neither boot nor
107
+ * the remaining listeners are affected.
108
+ */
109
+ private static runBootListener;
110
+ /**
111
+ * Whether the application has begun shutting down. Once true, readiness
112
+ * checks report not-ready so a load balancer drains this instance before the
113
+ * HTTP server stops accepting requests.
114
+ */
115
+ static get isShuttingDown(): boolean;
116
+ /**
117
+ * Register a teardown callback, run once when the application shuts down —
118
+ * before the connectors (db, cache, http) are torn down, so it can still use
119
+ * them. The natural place to close an app-owned resource opened in
120
+ * `onceBooted`. If shutdown has already begun, the callback runs immediately.
121
+ *
122
+ * @example
123
+ * Application.onShutdown(async () => {
124
+ * await myQueueConsumer.stop();
125
+ * });
126
+ */
127
+ static onShutdown(listener: ShutdownListener): void;
128
+ /**
129
+ * Flip the shutdown latch and run every teardown callback. The connectors
130
+ * manager calls this at the start of shutdown — before any connector is torn
131
+ * down. Idempotent and error-isolated: each callback is awaited, a thrown one
132
+ * is logged and does not stop the rest. Listeners run LIFO (reverse of
133
+ * registration), mirroring resource-unwind order.
134
+ *
135
+ * @internal Framework entry points call this; application code must not.
136
+ */
137
+ static runShutdownHooks(): Promise<void>;
138
+ /**
139
+ * Invoke a single teardown callback, isolating its failure so the rest still
140
+ * run and shutdown is never blocked by one bad hook.
141
+ */
142
+ private static runShutdownListener;
21
143
  /**
22
144
  * Get project uptime in milliseconds
23
145
  */
@@ -68,5 +190,5 @@ declare class Application {
68
190
  static get publicPath(): string;
69
191
  }
70
192
  //#endregion
71
- export { Application };
193
+ export { Application, BootContext, BootListener, ShutdownListener };
72
194
  //# sourceMappingURL=application.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"application.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/application/application.ts"],"mappings":";;;cAIa,WAAA;;AAAb;;kBAIyB,SAAA,EAAS,IAAA;EAAA;;;EAAA,OAKlB,eAAA;EAiC+B;;;EAAA,WA5B3B,OAAA;EAAA;;;EAAA,OAOJ,kBAAA,CAAmB,QAAA;EAcf;;;EAAA,WAPA,MAAA;EAcW;;;EAAA,WAPX,WAAA,IAAe,WAAA;EAmCf;;;EAAA,OA5BJ,cAAA,CAAe,GAAA,EAAK,WAAA;EAwDhB;;;EAAA,WAjDA,YAAA;;;;aAOA,aAAA;;;;aAOA,MAAA;;;;aAOA,QAAA;;;;aAOA,OAAA;;;;aAOA,OAAA;;;;aAOA,WAAA;;;;aAOA,WAAA;;;;aAOA,UAAA;AAAA"}
1
+ {"version":3,"file":"application.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/application/application.ts"],"mappings":";;;;;AAgBA;;;;KAAY,WAAA;EACV,WAAA,EAAa,WAAA;EACb,eAAA,EAAiB,eAAe;EAAf;;;AAKH;EAAd,cAAA;AAAA;;;;;;KAQU,YAAA,IAAgB,OAAA,EAAS,WAAA,YAAuB,OAAO;AAAA;AAOnE;;;;AAPmE,KAOvD,gBAAA,gBAAgC,OAAO;AAAA,cAEtC,WAAA;EAAW;;;EAAA,gBAIC,SAAA,EAAS,IAAA;EA4CW;;;EAAA,OAvC7B,eAAA,EAAiB,eAAA;EAmGG;;;EAAA,eA9FnB,MAAA;EAgNmB;;;EAAA,eA3MnB,WAAA;EAfiB;;;;EAAA,eAqBjB,aAAA;EAAA;;;;EAAA,eAMA,YAAA;EAiB4B;;;EAAA,eAZ5B,iBAAA;EAuCoB;;;EAAA,WAlCjB,OAAA;EAiDkB;;;EAAA,OA1CtB,kBAAA,CAAmB,QAAA,EAAU,eAAA;EAgFtB;;;;;EAAA,WAvEH,QAAA;EAyHsB;;;;;;;;;;;;;;EAAA,OAvG1B,UAAA,CAAW,QAAA,EAAU,YAAA;EA2MjB;;;AAOU;EAPV,OA5LJ,UAAA,IAAc,OAAA,CAAQ,WAAA;;;;;;;;;SAkBtB,UAAA,CAAW,OAAA,EAAS,WAAA;;;;;iBAoBb,eAAA;;;;;;aAgBH,cAAA;;;;;;;;;;;;SAeJ,UAAA,CAAW,QAAA,EAAU,gBAAA;;;;;;;;;;SAmBf,gBAAA,IAAoB,OAAA;;;;;iBAmBnB,mBAAA;;;;aAWH,MAAA;;;;aAOA,WAAA,IAAe,WAAA;;;;SAOnB,cAAA,CAAe,GAAA,EAAK,WAAA;;;;aAOhB,YAAA;;;;aAOA,aAAA;;;;aAOA,MAAA;;;;aAOA,QAAA;;;;aAOA,OAAA;;;;aAOA,OAAA;;;;aAOA,WAAA;;;;aAOA,WAAA;;;;aAOA,UAAA;AAAA"}
@@ -1,12 +1,25 @@
1
1
  import { environment, setEnvironment } from "../utils/environment.mjs";
2
2
  import { appPath, publicPath, rootPath, srcPath, storagePath, uploadsPath } from "../utils/paths.mjs";
3
3
  import { getFrameworkVersion } from "../utils/framework-vesion.mjs";
4
+ import { log } from "@warlock.js/logger";
4
5
 
5
6
  //#region ../@warlock.js/core/src/application/application.ts
6
7
  var Application = class {
7
8
  static {
8
9
  this.startedAt = /* @__PURE__ */ new Date(Date.now() - process.uptime() * 1e3);
9
10
  }
11
+ static {
12
+ this.booted = false;
13
+ }
14
+ static {
15
+ this.bootListeners = [];
16
+ }
17
+ static {
18
+ this.shuttingDown = false;
19
+ }
20
+ static {
21
+ this.shutdownListeners = [];
22
+ }
10
23
  /**
11
24
  * Get framework version
12
25
  */
@@ -20,6 +33,126 @@ var Application = class {
20
33
  this.runtimeStrategy = strategy;
21
34
  }
22
35
  /**
36
+ * Whether the application has finished booting — every connector in both
37
+ * the early and late phases is active and all app files (locales, events,
38
+ * main, routes) have been loaded.
39
+ */
40
+ static get isBooted() {
41
+ return this.booted;
42
+ }
43
+ /**
44
+ * Run a callback once the application is fully booted.
45
+ *
46
+ * App files (`main` / `events` / `routes` / locales) are imported BEFORE the
47
+ * late-phase connectors (http, socket) start, so a listener registered at
48
+ * import time waits for the whole sequence to finish before it runs. If the
49
+ * application is already booted, the callback runs on the next microtask —
50
+ * so a late subscriber never silently misses the event.
51
+ *
52
+ * @example
53
+ * Application.onceBooted(({ environment }) => {
54
+ * log.info("app", "booted", `ready in ${environment}`);
55
+ * });
56
+ */
57
+ static onceBooted(listener) {
58
+ if (this.booted && this.bootContext) {
59
+ const context = this.bootContext;
60
+ this.runBootListener(listener, context);
61
+ return;
62
+ }
63
+ this.bootListeners.push(listener);
64
+ }
65
+ /**
66
+ * Promise form of {@link onceBooted} — resolves with the boot context once
67
+ * the application is booted, or immediately if it already is.
68
+ */
69
+ static whenBooted() {
70
+ if (this.booted && this.bootContext) return Promise.resolve(this.bootContext);
71
+ return new Promise((resolve) => {
72
+ this.onceBooted((context) => resolve(context));
73
+ });
74
+ }
75
+ /**
76
+ * Flip the boot latch and drain every queued listener. The dev server and
77
+ * the production entry call this once, right after the late phase starts. It
78
+ * is idempotent — a second call is a no-op, so a double-invoke can never
79
+ * double-fire listeners.
80
+ *
81
+ * @internal Framework entry points call this; application code must not.
82
+ */
83
+ static markBooted(context) {
84
+ if (this.booted) return;
85
+ this.booted = true;
86
+ this.bootContext = context;
87
+ const listeners = this.bootListeners;
88
+ this.bootListeners = [];
89
+ for (const listener of listeners) this.runBootListener(listener, context);
90
+ }
91
+ /**
92
+ * Invoke a single boot listener, isolating its failure so neither boot nor
93
+ * the remaining listeners are affected.
94
+ */
95
+ static async runBootListener(listener, context) {
96
+ try {
97
+ await listener(context);
98
+ } catch (error) {
99
+ log.error("application", "booted-listener", error);
100
+ }
101
+ }
102
+ /**
103
+ * Whether the application has begun shutting down. Once true, readiness
104
+ * checks report not-ready so a load balancer drains this instance before the
105
+ * HTTP server stops accepting requests.
106
+ */
107
+ static get isShuttingDown() {
108
+ return this.shuttingDown;
109
+ }
110
+ /**
111
+ * Register a teardown callback, run once when the application shuts down —
112
+ * before the connectors (db, cache, http) are torn down, so it can still use
113
+ * them. The natural place to close an app-owned resource opened in
114
+ * `onceBooted`. If shutdown has already begun, the callback runs immediately.
115
+ *
116
+ * @example
117
+ * Application.onShutdown(async () => {
118
+ * await myQueueConsumer.stop();
119
+ * });
120
+ */
121
+ static onShutdown(listener) {
122
+ if (this.shuttingDown) {
123
+ this.runShutdownListener(listener);
124
+ return;
125
+ }
126
+ this.shutdownListeners.push(listener);
127
+ }
128
+ /**
129
+ * Flip the shutdown latch and run every teardown callback. The connectors
130
+ * manager calls this at the start of shutdown — before any connector is torn
131
+ * down. Idempotent and error-isolated: each callback is awaited, a thrown one
132
+ * is logged and does not stop the rest. Listeners run LIFO (reverse of
133
+ * registration), mirroring resource-unwind order.
134
+ *
135
+ * @internal Framework entry points call this; application code must not.
136
+ */
137
+ static async runShutdownHooks() {
138
+ if (this.shuttingDown) return;
139
+ this.shuttingDown = true;
140
+ const listeners = this.shutdownListeners.reverse();
141
+ this.shutdownListeners = [];
142
+ for (const listener of listeners) await this.runShutdownListener(listener);
143
+ }
144
+ /**
145
+ * Invoke a single teardown callback, isolating its failure so the rest still
146
+ * run and shutdown is never blocked by one bad hook.
147
+ */
148
+ static async runShutdownListener(listener) {
149
+ try {
150
+ await listener();
151
+ } catch (error) {
152
+ log.error("application", "shutdown-listener", error);
153
+ }
154
+ }
155
+ /**
23
156
  * Get project uptime in milliseconds
24
157
  */
25
158
  static get uptime() {
@@ -1 +1 @@
1
- {"version":3,"file":"application.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/application/application.ts"],"sourcesContent":["import { environment, setEnvironment, type Environment } from \"../utils/environment\";\nimport { getFrameworkVersion } from \"../utils/framework-vesion\";\nimport { appPath, publicPath, rootPath, srcPath, storagePath, uploadsPath } from \"../utils/paths\";\n\nexport class Application {\n /**\n * Project start time regarding the process start time\n */\n public static readonly startedAt = new Date(Date.now() - process.uptime() * 1000);\n\n /**\n * Runtime strategy\n */\n public static runtimeStrategy: \"production\" | \"development\";\n\n /**\n * Get framework version\n */\n public static get version() {\n return getFrameworkVersion();\n }\n\n /**\n * Set the runtime strategy\n */\n public static setRuntimeStrategy(strategy: \"production\" | \"development\") {\n this.runtimeStrategy = strategy;\n }\n\n /**\n * Get project uptime in milliseconds\n */\n public static get uptime(): number {\n return process.uptime() * 1000;\n }\n\n /**\n * Get the current environment\n */\n public static get environment(): Environment {\n return environment();\n }\n\n /**\n * Set the current environment\n */\n public static setEnvironment(env: Environment) {\n setEnvironment(env);\n }\n\n /**\n * Check if the application is running in production environment\n */\n public static get isProduction(): boolean {\n return this.environment === \"production\";\n }\n\n /**\n * Check if the application is running in development environment\n */\n public static get isDevelopment(): boolean {\n return this.environment === \"development\";\n }\n\n /**\n * Check if the application is running in test environment\n */\n public static get isTest(): boolean {\n return this.environment === \"test\";\n }\n\n /**\n * Get the root path\n */\n public static get rootPath(): string {\n return rootPath();\n }\n\n /**\n * Get the src path\n */\n public static get srcPath(): string {\n return srcPath();\n }\n\n /**\n * Get the app path\n */\n public static get appPath(): string {\n return appPath();\n }\n\n /**\n * Get the storage path\n */\n public static get storagePath(): string {\n return storagePath();\n }\n\n /**\n * Get the uploads path\n */\n public static get uploadsPath(): string {\n return uploadsPath();\n }\n\n /**\n * Get the public path\n */\n public static get publicPath(): string {\n return publicPath();\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,cAAb,MAAyB;;mCAIY,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,OAAO,IAAI,GAAI;;;;;CAUhF,WAAkB,UAAU;EAC1B,OAAO,oBAAoB;CAC7B;;;;CAKA,OAAc,mBAAmB,UAAwC;EACvE,KAAK,kBAAkB;CACzB;;;;CAKA,WAAkB,SAAiB;EACjC,OAAO,QAAQ,OAAO,IAAI;CAC5B;;;;CAKA,WAAkB,cAA2B;EAC3C,OAAO,YAAY;CACrB;;;;CAKA,OAAc,eAAe,KAAkB;EAC7C,eAAe,GAAG;CACpB;;;;CAKA,WAAkB,eAAwB;EACxC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,gBAAyB;EACzC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,SAAkB;EAClC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,WAAmB;EACnC,OAAO,SAAS;CAClB;;;;CAKA,WAAkB,UAAkB;EAClC,OAAO,QAAQ;CACjB;;;;CAKA,WAAkB,UAAkB;EAClC,OAAO,QAAQ;CACjB;;;;CAKA,WAAkB,cAAsB;EACtC,OAAO,YAAY;CACrB;;;;CAKA,WAAkB,cAAsB;EACtC,OAAO,YAAY;CACrB;;;;CAKA,WAAkB,aAAqB;EACrC,OAAO,WAAW;CACpB;AACF"}
1
+ {"version":3,"file":"application.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/application/application.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\nimport {\n environment,\n setEnvironment,\n type Environment,\n type RuntimeStrategy,\n} from \"../utils/environment\";\nimport { getFrameworkVersion } from \"../utils/framework-vesion\";\nimport { appPath, publicPath, rootPath, srcPath, storagePath, uploadsPath } from \"../utils/paths\";\n\n/**\n * Snapshot of the global facts known the moment the application finished\n * booting: every connector (early + late) is active and all app files\n * (locales, events, main, routes) are loaded. Passed to every\n * `Application.onceBooted` listener and resolved from `Application.whenBooted`.\n */\nexport type BootContext = {\n environment: Environment;\n runtimeStrategy: RuntimeStrategy;\n /**\n * Wall-clock milliseconds the boot took. The dev server measures it; the\n * production entry omits it.\n */\n bootDurationMs?: number;\n};\n\n/**\n * A callback registered through `Application.onceBooted`. May be async — its\n * rejection is caught and logged so a single listener can never break boot or\n * the other listeners.\n */\nexport type BootListener = (context: BootContext) => void | Promise<void>;\n\n/**\n * A callback registered through `Application.onShutdown`. Runs once, while the\n * connectors (db, cache, http) are still up, so it can release app-owned\n * resources cleanly. May be async — its rejection is caught and logged.\n */\nexport type ShutdownListener = () => void | Promise<void>;\n\nexport class Application {\n /**\n * Project start time regarding the process start time\n */\n public static readonly startedAt = new Date(Date.now() - process.uptime() * 1000);\n\n /**\n * Runtime strategy\n */\n public static runtimeStrategy: RuntimeStrategy;\n\n /**\n * Whether the application has finished booting. Flipped once by `markBooted`.\n */\n private static booted = false;\n\n /**\n * The boot context, set once booted so it can be replayed to late subscribers.\n */\n private static bootContext: BootContext | undefined;\n\n /**\n * Listeners queued before boot completed, drained in registration order by\n * `markBooted`.\n */\n private static bootListeners: BootListener[] = [];\n\n /**\n * Whether the application has begun (or finished) shutting down. Flipped once\n * by `runShutdownHooks`.\n */\n private static shuttingDown = false;\n\n /**\n * Teardown callbacks, run once when shutdown begins.\n */\n private static shutdownListeners: ShutdownListener[] = [];\n\n /**\n * Get framework version\n */\n public static get version() {\n return getFrameworkVersion();\n }\n\n /**\n * Set the runtime strategy\n */\n public static setRuntimeStrategy(strategy: RuntimeStrategy) {\n this.runtimeStrategy = strategy;\n }\n\n /**\n * Whether the application has finished booting — every connector in both\n * the early and late phases is active and all app files (locales, events,\n * main, routes) have been loaded.\n */\n public static get isBooted(): boolean {\n return this.booted;\n }\n\n /**\n * Run a callback once the application is fully booted.\n *\n * App files (`main` / `events` / `routes` / locales) are imported BEFORE the\n * late-phase connectors (http, socket) start, so a listener registered at\n * import time waits for the whole sequence to finish before it runs. If the\n * application is already booted, the callback runs on the next microtask —\n * so a late subscriber never silently misses the event.\n *\n * @example\n * Application.onceBooted(({ environment }) => {\n * log.info(\"app\", \"booted\", `ready in ${environment}`);\n * });\n */\n public static onceBooted(listener: BootListener): void {\n if (this.booted && this.bootContext) {\n const context = this.bootContext;\n void this.runBootListener(listener, context);\n\n return;\n }\n\n this.bootListeners.push(listener);\n }\n\n /**\n * Promise form of {@link onceBooted} — resolves with the boot context once\n * the application is booted, or immediately if it already is.\n */\n public static whenBooted(): Promise<BootContext> {\n if (this.booted && this.bootContext) {\n return Promise.resolve(this.bootContext);\n }\n\n return new Promise<BootContext>((resolve) => {\n this.onceBooted((context) => resolve(context));\n });\n }\n\n /**\n * Flip the boot latch and drain every queued listener. The dev server and\n * the production entry call this once, right after the late phase starts. It\n * is idempotent — a second call is a no-op, so a double-invoke can never\n * double-fire listeners.\n *\n * @internal Framework entry points call this; application code must not.\n */\n public static markBooted(context: BootContext): void {\n if (this.booted) {\n return;\n }\n\n this.booted = true;\n this.bootContext = context;\n\n const listeners = this.bootListeners;\n this.bootListeners = [];\n\n for (const listener of listeners) {\n void this.runBootListener(listener, context);\n }\n }\n\n /**\n * Invoke a single boot listener, isolating its failure so neither boot nor\n * the remaining listeners are affected.\n */\n private static async runBootListener(\n listener: BootListener,\n context: BootContext,\n ): Promise<void> {\n try {\n await listener(context);\n } catch (error) {\n log.error(\"application\", \"booted-listener\", error as Error);\n }\n }\n\n /**\n * Whether the application has begun shutting down. Once true, readiness\n * checks report not-ready so a load balancer drains this instance before the\n * HTTP server stops accepting requests.\n */\n public static get isShuttingDown(): boolean {\n return this.shuttingDown;\n }\n\n /**\n * Register a teardown callback, run once when the application shuts down —\n * before the connectors (db, cache, http) are torn down, so it can still use\n * them. The natural place to close an app-owned resource opened in\n * `onceBooted`. If shutdown has already begun, the callback runs immediately.\n *\n * @example\n * Application.onShutdown(async () => {\n * await myQueueConsumer.stop();\n * });\n */\n public static onShutdown(listener: ShutdownListener): void {\n if (this.shuttingDown) {\n void this.runShutdownListener(listener);\n\n return;\n }\n\n this.shutdownListeners.push(listener);\n }\n\n /**\n * Flip the shutdown latch and run every teardown callback. The connectors\n * manager calls this at the start of shutdown — before any connector is torn\n * down. Idempotent and error-isolated: each callback is awaited, a thrown one\n * is logged and does not stop the rest. Listeners run LIFO (reverse of\n * registration), mirroring resource-unwind order.\n *\n * @internal Framework entry points call this; application code must not.\n */\n public static async runShutdownHooks(): Promise<void> {\n if (this.shuttingDown) {\n return;\n }\n\n this.shuttingDown = true;\n\n const listeners = this.shutdownListeners.reverse();\n this.shutdownListeners = [];\n\n for (const listener of listeners) {\n await this.runShutdownListener(listener);\n }\n }\n\n /**\n * Invoke a single teardown callback, isolating its failure so the rest still\n * run and shutdown is never blocked by one bad hook.\n */\n private static async runShutdownListener(listener: ShutdownListener): Promise<void> {\n try {\n await listener();\n } catch (error) {\n log.error(\"application\", \"shutdown-listener\", error as Error);\n }\n }\n\n /**\n * Get project uptime in milliseconds\n */\n public static get uptime(): number {\n return process.uptime() * 1000;\n }\n\n /**\n * Get the current environment\n */\n public static get environment(): Environment {\n return environment();\n }\n\n /**\n * Set the current environment\n */\n public static setEnvironment(env: Environment) {\n setEnvironment(env);\n }\n\n /**\n * Check if the application is running in production environment\n */\n public static get isProduction(): boolean {\n return this.environment === \"production\";\n }\n\n /**\n * Check if the application is running in development environment\n */\n public static get isDevelopment(): boolean {\n return this.environment === \"development\";\n }\n\n /**\n * Check if the application is running in test environment\n */\n public static get isTest(): boolean {\n return this.environment === \"test\";\n }\n\n /**\n * Get the root path\n */\n public static get rootPath(): string {\n return rootPath();\n }\n\n /**\n * Get the src path\n */\n public static get srcPath(): string {\n return srcPath();\n }\n\n /**\n * Get the app path\n */\n public static get appPath(): string {\n return appPath();\n }\n\n /**\n * Get the storage path\n */\n public static get storagePath(): string {\n return storagePath();\n }\n\n /**\n * Get the uploads path\n */\n public static get uploadsPath(): string {\n return uploadsPath();\n }\n\n /**\n * Get the public path\n */\n public static get publicPath(): string {\n return publicPath();\n }\n}\n"],"mappings":";;;;;;AAwCA,IAAa,cAAb,MAAyB;;mCAIY,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,OAAO,IAAI,GAAI;;;gBAUxD;;;uBAWuB,CAAC;;;sBAMlB;;;2BAKyB,CAAC;;;;;CAKxD,WAAkB,UAAU;EAC1B,OAAO,oBAAoB;CAC7B;;;;CAKA,OAAc,mBAAmB,UAA2B;EAC1D,KAAK,kBAAkB;CACzB;;;;;;CAOA,WAAkB,WAAoB;EACpC,OAAO,KAAK;CACd;;;;;;;;;;;;;;;CAgBA,OAAc,WAAW,UAA8B;EACrD,IAAI,KAAK,UAAU,KAAK,aAAa;GACnC,MAAM,UAAU,KAAK;GACrB,AAAK,KAAK,gBAAgB,UAAU,OAAO;GAE3C;EACF;EAEA,KAAK,cAAc,KAAK,QAAQ;CAClC;;;;;CAMA,OAAc,aAAmC;EAC/C,IAAI,KAAK,UAAU,KAAK,aACtB,OAAO,QAAQ,QAAQ,KAAK,WAAW;EAGzC,OAAO,IAAI,SAAsB,YAAY;GAC3C,KAAK,YAAY,YAAY,QAAQ,OAAO,CAAC;EAC/C,CAAC;CACH;;;;;;;;;CAUA,OAAc,WAAW,SAA4B;EACnD,IAAI,KAAK,QACP;EAGF,KAAK,SAAS;EACd,KAAK,cAAc;EAEnB,MAAM,YAAY,KAAK;EACvB,KAAK,gBAAgB,CAAC;EAEtB,KAAK,MAAM,YAAY,WACrB,AAAK,KAAK,gBAAgB,UAAU,OAAO;CAE/C;;;;;CAMA,aAAqB,gBACnB,UACA,SACe;EACf,IAAI;GACF,MAAM,SAAS,OAAO;EACxB,SAAS,OAAO;GACd,IAAI,MAAM,eAAe,mBAAmB,KAAc;EAC5D;CACF;;;;;;CAOA,WAAkB,iBAA0B;EAC1C,OAAO,KAAK;CACd;;;;;;;;;;;;CAaA,OAAc,WAAW,UAAkC;EACzD,IAAI,KAAK,cAAc;GACrB,AAAK,KAAK,oBAAoB,QAAQ;GAEtC;EACF;EAEA,KAAK,kBAAkB,KAAK,QAAQ;CACtC;;;;;;;;;;CAWA,aAAoB,mBAAkC;EACpD,IAAI,KAAK,cACP;EAGF,KAAK,eAAe;EAEpB,MAAM,YAAY,KAAK,kBAAkB,QAAQ;EACjD,KAAK,oBAAoB,CAAC;EAE1B,KAAK,MAAM,YAAY,WACrB,MAAM,KAAK,oBAAoB,QAAQ;CAE3C;;;;;CAMA,aAAqB,oBAAoB,UAA2C;EAClF,IAAI;GACF,MAAM,SAAS;EACjB,SAAS,OAAO;GACd,IAAI,MAAM,eAAe,qBAAqB,KAAc;EAC9D;CACF;;;;CAKA,WAAkB,SAAiB;EACjC,OAAO,QAAQ,OAAO,IAAI;CAC5B;;;;CAKA,WAAkB,cAA2B;EAC3C,OAAO,YAAY;CACrB;;;;CAKA,OAAc,eAAe,KAAkB;EAC7C,eAAe,GAAG;CACpB;;;;CAKA,WAAkB,eAAwB;EACxC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,gBAAyB;EACzC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,SAAkB;EAClC,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,WAAkB,WAAmB;EACnC,OAAO,SAAS;CAClB;;;;CAKA,WAAkB,UAAkB;EAClC,OAAO,QAAQ;CACjB;;;;CAKA,WAAkB,UAAkB;EAClC,OAAO,QAAQ;CACjB;;;;CAKA,WAAkB,cAAsB;EACtC,OAAO,YAAY;CACrB;;;;CAKA,WAAkB,cAAsB;EACtC,OAAO,YAAY;CACrB;;;;CAKA,WAAkB,aAAqB;EACrC,OAAO,WAAW;CACpB;AACF"}
@@ -1,3 +1,3 @@
1
1
  import { app } from "./app.mjs";
2
- import { Application } from "./application.mjs";
2
+ import { Application, BootContext, BootListener, ShutdownListener } from "./application.mjs";
3
3
  import { AppConfigurations } from "./application-config-types.mjs";
@@ -1,3 +1,4 @@
1
+ import { RuntimeStrategy } from "../utils/environment.mjs";
1
2
  import { ConnectorName } from "../connectors/types.mjs";
2
3
  //#region ../@warlock.js/core/src/cli/types.d.ts
3
4
  /**
@@ -68,7 +69,7 @@ type CLICommandPreload = {
68
69
  /**
69
70
  * Set runtime strategy
70
71
  */
71
- runtimeStrategy?: "production" | "development";
72
+ runtimeStrategy?: RuntimeStrategy;
72
73
  /**
73
74
  * Override current detect environment
74
75
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/cli/types.ts"],"mappings":";;;;AAe4B;AAY5B;;;;KAZY,gBAAA;;;;AAwBK;AAsBjB;;;;;;KAlCY,iBAAA;EAuDV;;;;EAlDA,IAAA;EA4Ea;;AAAa;AAgB5B;;EArFE,OAAA,EAAS,MAAM;AAAA;;;;;;;;;AA4HP;AAGV;;;;;;;;;AAEO;KA3GK,iBAAA;EAkHgB;;;;;EA5G1B,MAAA;EA4GwE;AAAA;AAsB1E;EA7HE,QAAA;;;;EAKA,aAAA;EAuKU;;;EAlKV,eAAA;EA8HA;;;EAzHA,WAAA;EA2IA;;;;EArIA,GAAA;EAuJA;;;AAA0B;EAjJ1B,SAAA;;;;;;;;EASA,UAAA,GAAa,aAAa;AAAA;;;;;;;;;;;;;;KAgBhB,gBAAA;;;;;EAKV,IAAA;;;;;EAMA,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,IAAA;;;;EAKA,YAAA;;;;;EAMA,QAAA;AAAA;AAAA,KAGU,wBAAA,GAA2B,IAAI,CAAC,gBAAA;EAC1C,IAAA;EACA,KAAA;AAAA;;;;;KAOU,gBAAA,IAAoB,IAAA,EAAM,iBAAA,YAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;KAsB9D,iBAAA;;;;;EAKV,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,OAAA,GAAU,iBAAA;;;;;;EAOV,UAAA;;;;;EAMA,SAAA,GAAY,gBAAA;;;;;EAMZ,MAAA,EAAQ,gBAAA;;;;;EAMR,OAAA,GAAU,gBAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/cli/types.ts"],"mappings":";;;;;AAgB4B;AAY5B;;;;KAZY,gBAAA;;;;AAwBK;AAsBjB;;;;;;KAlCY,iBAAA;EAuDV;;;;EAlDA,IAAA;EA4EA;;;AAA0B;AAgB5B;EArFE,OAAA,EAAS,MAAM;AAAA;;;;;;;;;;AA4HP;AAGV;;;;;;;;;KAzGY,iBAAA;EAkHA;;;;;EA5GV,MAAA;EA4GiE;;AAAO;EAvGxE,QAAA;EA6H2B;;;EAxH3B,aAAA;EAiKQ;;;EA5JR,eAAA,GAAkB,eAAA;EAwHlB;;;EAnHA,WAAA;EAoIU;;;;EA9HV,GAAA;EAiJQ;;;;EA3IR,SAAA;;;;;;;;EASA,UAAA,GAAa,aAAa;AAAA;;;;;;;;;;;;;;KAgBhB,gBAAA;;;;;EAKV,IAAA;;;;;EAMA,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,IAAA;;;;EAKA,YAAA;;;;;EAMA,QAAA;AAAA;AAAA,KAGU,wBAAA,GAA2B,IAAI,CAAC,gBAAA;EAC1C,IAAA;EACA,KAAA;AAAA;;;;;KAOU,gBAAA,IAAoB,IAAA,EAAM,iBAAA,YAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;KAsB9D,iBAAA;;;;;EAKV,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,OAAA,GAAU,iBAAA;;;;;;EAOV,UAAA;;;;;EAMA,SAAA,GAAY,gBAAA;;;;;EAMZ,MAAA,EAAQ,gBAAA;;;;;EAMR,OAAA,GAAU,gBAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- import config from "@mongez/config";
1
+ import baseConfig from "@mongez/config";
2
2
 
3
3
  //#region ../@warlock.js/core/src/config/config-getter.ts
4
4
  /**
@@ -14,15 +14,15 @@ import config from "@mongez/config";
14
14
  * const port = config.key<number>("database.port", 27017);
15
15
  * ```
16
16
  */
17
- const config$1 = {
17
+ const config = {
18
18
  key(key, defaultValue) {
19
- return config.get(key, defaultValue);
19
+ return baseConfig.get(key, defaultValue);
20
20
  },
21
21
  get(name, defaultValue) {
22
- return config.get(name, defaultValue);
22
+ return baseConfig.get(name, defaultValue);
23
23
  }
24
24
  };
25
25
 
26
26
  //#endregion
27
- export { config$1 as config };
27
+ export { config };
28
28
  //# sourceMappingURL=config-getter.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-getter.mjs","names":["config","baseConfig"],"sources":["../../../../../../../@warlock.js/core/src/config/config-getter.ts"],"sourcesContent":["import baseConfig from \"@mongez/config\";\nimport type { ConfigKey, ConfigRegistry } from \"./types\";\n\n/**\n * Config accessor interface with typed overloads\n */\ninterface ConfigAccessor {\n /**\n * Get a config value by dot-notation key path.\n */\n key<T = any>(key: ConfigKey | (string & {}), defaultValue?: T): T;\n\n /**\n * Get an entire config group by name with type inference.\n * Returns the typed config object for known config names.\n */\n get<K extends keyof ConfigRegistry>(name: K, defaultValue?: ConfigRegistry[K]): ConfigRegistry[K];\n\n /**\n * Get an entire config group by name (dynamic string).\n */\n get<T = any>(name: string, defaultValue?: T): T;\n}\n\n/**\n * Config accessor with typed autocomplete and return type inference.\n *\n * @example\n * ```typescript\n * // Get entire config group - returns the actual config type\n * const db = config.get(\"database\"); // → DatabaseConfigurations\n *\n * // Get specific key with dot notation\n * const host = config.key(\"database.host\");\n * const port = config.key<number>(\"database.port\", 27017);\n * ```\n */\nexport const config: ConfigAccessor = {\n key(key: ConfigKey | (string & {}), defaultValue?: any): any {\n return baseConfig.get(key, defaultValue);\n },\n\n get(name: string, defaultValue?: any): any {\n return baseConfig.get(name, defaultValue);\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAqCA,MAAaA,WAAyB;CACpC,IAAI,KAAgC,cAAyB;EAC3D,OAAOC,OAAW,IAAI,KAAK,YAAY;CACzC;CAEA,IAAI,MAAc,cAAyB;EACzC,OAAOA,OAAW,IAAI,MAAM,YAAY;CAC1C;AACF"}
1
+ {"version":3,"file":"config-getter.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/config/config-getter.ts"],"sourcesContent":["import baseConfig from \"@mongez/config\";\nimport type { ConfigKey, ConfigRegistry } from \"./types\";\n\n/**\n * Config accessor interface with typed overloads\n */\ninterface ConfigAccessor {\n /**\n * Get a config value by dot-notation key path.\n */\n key<T = any>(key: ConfigKey | (string & {}), defaultValue?: T): T;\n\n /**\n * Get an entire config group by name with type inference.\n * Returns the typed config object for known config names.\n */\n get<K extends keyof ConfigRegistry>(name: K, defaultValue?: ConfigRegistry[K]): ConfigRegistry[K];\n\n /**\n * Get an entire config group by name (dynamic string).\n */\n get<T = any>(name: string, defaultValue?: T): T;\n}\n\n/**\n * Config accessor with typed autocomplete and return type inference.\n *\n * @example\n * ```typescript\n * // Get entire config group - returns the actual config type\n * const db = config.get(\"database\"); // → DatabaseConfigurations\n *\n * // Get specific key with dot notation\n * const host = config.key(\"database.host\");\n * const port = config.key<number>(\"database.port\", 27017);\n * ```\n */\nexport const config: ConfigAccessor = {\n key(key: ConfigKey | (string & {}), defaultValue?: any): any {\n return baseConfig.get(key, defaultValue);\n },\n\n get(name: string, defaultValue?: any): any {\n return baseConfig.get(name, defaultValue);\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAqCA,MAAa,SAAyB;CACpC,IAAI,KAAgC,cAAyB;EAC3D,OAAO,WAAW,IAAI,KAAK,YAAY;CACzC;CAEA,IAAI,MAAc,cAAyB;EACzC,OAAO,WAAW,IAAI,MAAM,YAAY;CAC1C;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { configSpecialHandlers } from "./config-special-handlers.mjs";
2
- import config from "@mongez/config";
2
+ import baseConfig from "@mongez/config";
3
3
  import { colors } from "@mongez/copper";
4
4
  import { pathToFileURL } from "node:url";
5
5
 
@@ -27,7 +27,7 @@ var ConfigLoader = class {
27
27
  console.log(colors.red(`config error: `), `Config file ${colors.yellow(file.relativePath)} does not have a default export`);
28
28
  return;
29
29
  }
30
- config.set(configName, configValue);
30
+ baseConfig.set(configName, configValue);
31
31
  await configSpecialHandlers.execute(configName, configValue);
32
32
  } catch (error) {
33
33
  throw error;
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/config/config-loader.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { pathToFileURL } from \"node:url\";\r\nimport type { FileManager } from \"../dev-server/file-manager\";\r\nimport { configSpecialHandlers } from \"./config-special-handlers\";\r\n\r\n/**\r\n * Special Config Handler\r\n * A function that handles special configuration loading\r\n */\r\nexport type SpecialConfigHandler = (configValue: any) => void | Promise<void>;\r\n\r\n/**\r\n * Config Loader\r\n * Dynamically loads all configuration files and registers them with @mongez/config\r\n * Supports special handlers for configs that require additional processing\r\n */\r\nexport class ConfigLoader {\r\n public async loadAll(configFiles: FileManager[]): Promise<void> {\r\n for (const file of configFiles) {\r\n await this.loadConfig(file);\r\n }\r\n }\r\n\r\n /**\r\n * Load a single configuration file.\r\n *\r\n * The ESM loader hook stamps a version token onto the URL so that each HMR\r\n * cycle gets a fresh module — no manual cache-busting needed here.\r\n */\r\n public async loadConfig(file: FileManager): Promise<void> {\r\n const configName = this.getConfigName(file.relativePath);\r\n\r\n try {\r\n const fileUrl = pathToFileURL(file.absolutePath).href;\r\n const configModule = await import(fileUrl);\r\n const configValue = configModule.default;\r\n\r\n if (configValue === undefined) {\r\n console.log(\r\n colors.red(`config error: `),\r\n `Config file ${colors.yellow(file.relativePath)} does not have a default export`,\r\n );\r\n return;\r\n }\r\n\r\n config.set(configName, configValue);\r\n await configSpecialHandlers.execute(configName, configValue);\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n public async reloadConfig(file: FileManager): Promise<void> {\r\n await this.loadConfig(file);\r\n }\r\n\r\n private getConfigName(relativePath: string): string {\r\n const match = relativePath.match(/^src\\/config\\/(.+)\\.(ts|tsx)$/);\r\n\r\n if (!match) {\r\n throw new Error(`Invalid config file path: ${relativePath}`);\r\n }\r\n\r\n return match[1];\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AAiBA,IAAa,eAAb,MAA0B;CACxB,MAAa,QAAQ,aAA2C;EAC9D,KAAK,MAAM,QAAQ,aACjB,MAAM,KAAK,WAAW,IAAI;CAE9B;;;;;;;CAQA,MAAa,WAAW,MAAkC;EACxD,MAAM,aAAa,KAAK,cAAc,KAAK,YAAY;EAEvD,IAAI;GAGF,MAAM,eAAc,MADO,OADX,cAAc,KAAK,YAAY,CAAC,CAAC,MAEjB,CAAC;GAEjC,IAAI,gBAAgB,QAAW;IAC7B,QAAQ,IACN,OAAO,IAAI,gBAAgB,GAC3B,eAAe,OAAO,OAAO,KAAK,YAAY,EAAE,gCAClD;IACA;GACF;GAEA,OAAO,IAAI,YAAY,WAAW;GAClC,MAAM,sBAAsB,QAAQ,YAAY,WAAW;EAC7D,SAAS,OAAO;GACd,MAAM;EACR;CACF;CAEA,MAAa,aAAa,MAAkC;EAC1D,MAAM,KAAK,WAAW,IAAI;CAC5B;CAEA,AAAQ,cAAc,cAA8B;EAClD,MAAM,QAAQ,aAAa,MAAM,+BAA+B;EAEhE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,6BAA6B,cAAc;EAG7D,OAAO,MAAM;CACf;AACF"}
1
+ {"version":3,"file":"config-loader.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/config/config-loader.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { pathToFileURL } from \"node:url\";\r\nimport type { FileManager } from \"../dev-server/file-manager\";\r\nimport { configSpecialHandlers } from \"./config-special-handlers\";\r\n\r\n/**\r\n * Special Config Handler\r\n * A function that handles special configuration loading\r\n */\r\nexport type SpecialConfigHandler = (configValue: any) => void | Promise<void>;\r\n\r\n/**\r\n * Config Loader\r\n * Dynamically loads all configuration files and registers them with @mongez/config\r\n * Supports special handlers for configs that require additional processing\r\n */\r\nexport class ConfigLoader {\r\n public async loadAll(configFiles: FileManager[]): Promise<void> {\r\n for (const file of configFiles) {\r\n await this.loadConfig(file);\r\n }\r\n }\r\n\r\n /**\r\n * Load a single configuration file.\r\n *\r\n * The ESM loader hook stamps a version token onto the URL so that each HMR\r\n * cycle gets a fresh module — no manual cache-busting needed here.\r\n */\r\n public async loadConfig(file: FileManager): Promise<void> {\r\n const configName = this.getConfigName(file.relativePath);\r\n\r\n try {\r\n const fileUrl = pathToFileURL(file.absolutePath).href;\r\n const configModule = await import(fileUrl);\r\n const configValue = configModule.default;\r\n\r\n if (configValue === undefined) {\r\n console.log(\r\n colors.red(`config error: `),\r\n `Config file ${colors.yellow(file.relativePath)} does not have a default export`,\r\n );\r\n return;\r\n }\r\n\r\n config.set(configName, configValue);\r\n await configSpecialHandlers.execute(configName, configValue);\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n public async reloadConfig(file: FileManager): Promise<void> {\r\n await this.loadConfig(file);\r\n }\r\n\r\n private getConfigName(relativePath: string): string {\r\n const match = relativePath.match(/^src\\/config\\/(.+)\\.(ts|tsx)$/);\r\n\r\n if (!match) {\r\n throw new Error(`Invalid config file path: ${relativePath}`);\r\n }\r\n\r\n return match[1];\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AAiBA,IAAa,eAAb,MAA0B;CACxB,MAAa,QAAQ,aAA2C;EAC9D,KAAK,MAAM,QAAQ,aACjB,MAAM,KAAK,WAAW,IAAI;CAE9B;;;;;;;CAQA,MAAa,WAAW,MAAkC;EACxD,MAAM,aAAa,KAAK,cAAc,KAAK,YAAY;EAEvD,IAAI;GAGF,MAAM,eAAc,MADO,OADX,cAAc,KAAK,YAAY,CAAC,CAAC,MAEjB,CAAC;GAEjC,IAAI,gBAAgB,QAAW;IAC7B,QAAQ,IACN,OAAO,IAAI,gBAAgB,GAC3B,eAAe,OAAO,OAAO,KAAK,YAAY,EAAE,gCAClD;IACA;GACF;GAEA,WAAO,IAAI,YAAY,WAAW;GAClC,MAAM,sBAAsB,QAAQ,YAAY,WAAW;EAC7D,SAAS,OAAO;GACd,MAAM;EACR;CACF;CAEA,MAAa,aAAa,MAAkC;EAC1D,MAAM,KAAK,WAAW,IAAI;CAC5B;CAEA,AAAQ,cAAc,cAA8B;EAClD,MAAM,QAAQ,aAAa,MAAM,+BAA+B;EAEhE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,6BAA6B,cAAc;EAG7D,OAAO,MAAM;CACf;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./types.mjs";
2
2
  import { BaseConnector } from "./base-connector.mjs";
3
- import config from "@mongez/config";
3
+ import baseConfig from "@mongez/config";
4
4
  import { log } from "@warlock.js/logger";
5
5
 
6
6
  //#region ../@warlock.js/core/src/connectors/access-connector.ts
@@ -29,7 +29,7 @@ var AccessConnector = class extends BaseConnector {
29
29
  * Register the access configuration with the package.
30
30
  */
31
31
  async start() {
32
- const accessConfiguration = config.get("access");
32
+ const accessConfiguration = baseConfig.get("access");
33
33
  if (!accessConfiguration) return;
34
34
  const { setAccessConfig } = await import("@warlock.js/access");
35
35
  setAccessConfig(accessConfiguration);
@@ -1 +1 @@
1
- {"version":3,"file":"access-connector.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/access-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { log } from \"@warlock.js/logger\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\n/**\n * Access Connector\n *\n * Wires `@warlock.js/access` from `src/config/access.ts` at boot: reads the\n * exported config object and hands it to the package's `setAccessConfig`, which\n * validates that a resolver is present — so a misconfigured authorization layer\n * fails at STARTUP, not on the first protected request. Config files stay\n * declarative; the connector performs the side-effect.\n *\n * The package is lazy-imported (only when an access config is present), so core\n * carries no hard dependency on it — the same pattern as the notifications and\n * herald connectors.\n */\nexport class AccessConnector extends BaseConnector {\n public readonly name = \"access\";\n public readonly priority = ConnectorPriority.ACCESS;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger an access restart\n */\n protected readonly watchedFiles = [\"src/config/access.ts\"];\n\n /**\n * Register the access configuration with the package.\n */\n public async start(): Promise<void> {\n const accessConfiguration = config.get(\"access\");\n\n if (!accessConfiguration) {\n return;\n }\n\n const { setAccessConfig } = await import(\"@warlock.js/access\");\n\n setAccessConfig(accessConfiguration);\n this.active = true;\n\n log.info(\"access\", \"configured\", \"Authorization wired from config/access.ts\");\n }\n\n /**\n * Clear the active config so a dev restart re-registers from the (possibly\n * changed, or now-removed) config file. The package holds no external\n * connection of its own.\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n const { resetAccessConfig } = await import(\"@warlock.js/access\");\n\n resetAccessConfig();\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,IAAa,kBAAb,cAAqC,cAAc;;;cAC1B;;;sBAOW,CAAC,sBAAsB;;;;;CAKzD,MAAa,QAAuB;EAClC,MAAM,sBAAsB,OAAO,IAAI,QAAQ;EAE/C,IAAI,CAAC,qBACH;EAGF,MAAM,EAAE,oBAAoB,MAAM,OAAO;EAEzC,gBAAgB,mBAAmB;EACnC,KAAK,SAAS;EAEd,IAAI,KAAK,UAAU,cAAc,2CAA2C;CAC9E;;;;;;CAOA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,MAAM,EAAE,sBAAsB,MAAM,OAAO;EAE3C,kBAAkB;EAClB,KAAK,SAAS;CAChB;AACF"}
1
+ {"version":3,"file":"access-connector.mjs","names":["config"],"sources":["../../../../../../../@warlock.js/core/src/connectors/access-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { log } from \"@warlock.js/logger\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\n/**\n * Access Connector\n *\n * Wires `@warlock.js/access` from `src/config/access.ts` at boot: reads the\n * exported config object and hands it to the package's `setAccessConfig`, which\n * validates that a resolver is present — so a misconfigured authorization layer\n * fails at STARTUP, not on the first protected request. Config files stay\n * declarative; the connector performs the side-effect.\n *\n * The package is lazy-imported (only when an access config is present), so core\n * carries no hard dependency on it — the same pattern as the notifications and\n * herald connectors.\n */\nexport class AccessConnector extends BaseConnector {\n public readonly name = \"access\";\n public readonly priority = ConnectorPriority.ACCESS;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger an access restart\n */\n protected readonly watchedFiles = [\"src/config/access.ts\"];\n\n /**\n * Register the access configuration with the package.\n */\n public async start(): Promise<void> {\n const accessConfiguration = config.get(\"access\");\n\n if (!accessConfiguration) {\n return;\n }\n\n const { setAccessConfig } = await import(\"@warlock.js/access\");\n\n setAccessConfig(accessConfiguration);\n this.active = true;\n\n log.info(\"access\", \"configured\", \"Authorization wired from config/access.ts\");\n }\n\n /**\n * Clear the active config so a dev restart re-registers from the (possibly\n * changed, or now-removed) config file. The package holds no external\n * connection of its own.\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n const { resetAccessConfig } = await import(\"@warlock.js/access\");\n\n resetAccessConfig();\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,IAAa,kBAAb,cAAqC,cAAc;;;cAC1B;;;sBAOW,CAAC,sBAAsB;;;;;CAKzD,MAAa,QAAuB;EAClC,MAAM,sBAAsBA,WAAO,IAAI,QAAQ;EAE/C,IAAI,CAAC,qBACH;EAGF,MAAM,EAAE,oBAAoB,MAAM,OAAO;EAEzC,gBAAgB,mBAAmB;EACnC,KAAK,SAAS;EAEd,IAAI,KAAK,UAAU,cAAc,2CAA2C;CAC9E;;;;;;CAOA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,MAAM,EAAE,sBAAsB,MAAM,OAAO;EAE3C,kBAAkB;EAClB,KAAK,SAAS;CAChB;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./types.mjs";
2
2
  import { BaseConnector } from "./base-connector.mjs";
3
- import config from "@mongez/config";
3
+ import baseConfig from "@mongez/config";
4
4
  import { cache } from "@warlock.js/cache";
5
5
 
6
6
  //#region ../@warlock.js/core/src/connectors/cache-connector.ts
@@ -20,7 +20,7 @@ var CacheConnector = class extends BaseConnector {
20
20
  * Initialize cache connection
21
21
  */
22
22
  async start() {
23
- const cacheConfig = config.get("cache");
23
+ const cacheConfig = baseConfig.get("cache");
24
24
  if (!cacheConfig) return;
25
25
  cache.setCacheConfigurations(cacheConfig);
26
26
  await cache.init();
@@ -1 +1 @@
1
- {"version":3,"file":"cache-connector.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/cache-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { cache } from \"@warlock.js/cache\";\r\nimport { BaseConnector } from \"./base-connector\";\r\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\r\n\r\n/**\r\n * Cache Connector\r\n * Manages cache engine connection lifecycle\r\n */\r\nexport class CacheConnector extends BaseConnector {\r\n public readonly name = \"cache\";\r\n public readonly priority = ConnectorPriority.CACHE;\r\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\r\n\r\n /**\r\n * Files that trigger cache restart\r\n */\r\n protected readonly watchedFiles = [\"src/config/cache.ts\", \"src/config/cache.tsx\"];\r\n\r\n /**\r\n * Initialize cache connection\r\n */\r\n public async start(): Promise<void> {\r\n const cacheConfig = config.get(\"cache\");\r\n\r\n if (!cacheConfig) return;\r\n\r\n cache.setCacheConfigurations(cacheConfig);\r\n\r\n await cache.init();\r\n\r\n this.active = true;\r\n }\r\n\r\n /**\r\n * Shutdown cache connection\r\n */\r\n public async shutdown(): Promise<void> {\r\n if (!this.active) {\r\n return;\r\n }\r\n\r\n // TODO: Implement actual cache disconnection\r\n // - Close all active connections\r\n // - Clean up resources\r\n\r\n this.active = false;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;AASA,IAAa,iBAAb,cAAoC,cAAc;;;cACzB;;;sBAOW,CAAC,uBAAuB,sBAAsB;;;;;CAKhF,MAAa,QAAuB;EAClC,MAAM,cAAc,OAAO,IAAI,OAAO;EAEtC,IAAI,CAAC,aAAa;EAElB,MAAM,uBAAuB,WAAW;EAExC,MAAM,MAAM,KAAK;EAEjB,KAAK,SAAS;CAChB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAOF,KAAK,SAAS;CAChB;AACF"}
1
+ {"version":3,"file":"cache-connector.mjs","names":["config"],"sources":["../../../../../../../@warlock.js/core/src/connectors/cache-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { cache } from \"@warlock.js/cache\";\r\nimport { BaseConnector } from \"./base-connector\";\r\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\r\n\r\n/**\r\n * Cache Connector\r\n * Manages cache engine connection lifecycle\r\n */\r\nexport class CacheConnector extends BaseConnector {\r\n public readonly name = \"cache\";\r\n public readonly priority = ConnectorPriority.CACHE;\r\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\r\n\r\n /**\r\n * Files that trigger cache restart\r\n */\r\n protected readonly watchedFiles = [\"src/config/cache.ts\", \"src/config/cache.tsx\"];\r\n\r\n /**\r\n * Initialize cache connection\r\n */\r\n public async start(): Promise<void> {\r\n const cacheConfig = config.get(\"cache\");\r\n\r\n if (!cacheConfig) return;\r\n\r\n cache.setCacheConfigurations(cacheConfig);\r\n\r\n await cache.init();\r\n\r\n this.active = true;\r\n }\r\n\r\n /**\r\n * Shutdown cache connection\r\n */\r\n public async shutdown(): Promise<void> {\r\n if (!this.active) {\r\n return;\r\n }\r\n\r\n // TODO: Implement actual cache disconnection\r\n // - Close all active connections\r\n // - Clean up resources\r\n\r\n this.active = false;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;AASA,IAAa,iBAAb,cAAoC,cAAc;;;cACzB;;;sBAOW,CAAC,uBAAuB,sBAAsB;;;;;CAKhF,MAAa,QAAuB;EAClC,MAAM,cAAcA,WAAO,IAAI,OAAO;EAEtC,IAAI,CAAC,aAAa;EAElB,MAAM,uBAAuB,WAAW;EAExC,MAAM,MAAM,KAAK;EAEjB,KAAK,SAAS;CAChB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAOF,KAAK,SAAS;CAChB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"connectors-manager.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/connectors-manager.ts"],"mappings":";;;cAea,iBAAA;EAAA;;;EAAA,iBAIM,UAAA;EA8BF;;;;EA8B0C;;;EAvClD,QAAA,IAAY,UAAA,EAAY,SAAA;EAmEC;;;EA1DzB,IAAA,IAAQ,SAAA;EATR;;;EAgBM,KAAA,CAAM,eAAA,GAAkB,aAAA,KAAkB,OAAA;EAPxC;;;;;;;;;EA8BF,UAAA,CAAW,KAAA,EAAO,uBAAA,GAA0B,OAAA;EAiBX;;;EAAjC,YAAA,CAAa,kBAAA,EAAoB,aAAA,KAAkB,OAAA;EAWvC;;;EAAZ,QAAA,IAAY,OAAA;EA4Cd;;;;AAA2C;;;;EAzB/C,qBAAA;AAAA;AAAA,cAyBI,iBAAA,EAAiB,iBAA0B"}
1
+ {"version":3,"file":"connectors-manager.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/connectors-manager.ts"],"mappings":";;;cAgBa,iBAAA;EAAA;;;EAAA,iBAIM,UAAA;EA8BF;;;;EA8B0C;;;EAvClD,QAAA,IAAY,UAAA,EAAY,SAAA;EAmEC;;;EA1DzB,IAAA,IAAQ,SAAA;EATR;;;EAgBM,KAAA,CAAM,eAAA,GAAkB,aAAA,KAAkB,OAAA;EAPxC;;;;;;;;;EA8BF,UAAA,CAAW,KAAA,EAAO,uBAAA,GAA0B,OAAA;EAiBX;;;EAAjC,YAAA,CAAa,kBAAA,EAAoB,aAAA,KAAkB,OAAA;EAWvC;;;EAAZ,QAAA,IAAY,OAAA;EAkDd;;;;AAA2C;;;;EAzB/C,qBAAA;AAAA;AAAA,cAyBI,iBAAA,EAAiB,iBAA0B"}
@@ -1,3 +1,5 @@
1
+ import { Application } from "../application/application.mjs";
2
+ import "../application/index.mjs";
1
3
  import { AccessConnector } from "./access-connector.mjs";
2
4
  import { CacheConnector } from "./cache-connector.mjs";
3
5
  import { HeraldConnector } from "./herald-connector.mjs";
@@ -74,7 +76,8 @@ var ConnectorsManager = class {
74
76
  * Shutdown all connectors
75
77
  */
76
78
  async shutdown() {
77
- for (const connector of this.connectors.reverse()) try {
79
+ await Application.runShutdownHooks();
80
+ for (const connector of [...this.connectors].reverse()) try {
78
81
  await connector.shutdown();
79
82
  } catch (error) {
80
83
  devServeLog(colors.redBright(`❌ Failed to shutdown ${connector.name}: ${error}`));
@@ -1 +1 @@
1
- {"version":3,"file":"connectors-manager.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/connectors-manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { devServeLog } from \"../dev-server/dev-logger\";\r\nimport { AccessConnector } from \"./access-connector\";\r\nimport { CacheConnector } from \"./cache-connector\";\r\nimport { DatabaseConnector } from \"./database-connector\";\r\nimport { HeraldConnector } from \"./herald-connector\";\r\nimport { HttpConnector } from \"./http-connector\";\r\nimport { LoggerConnector } from \"./logger-connector\";\r\nimport { MailerConnector } from \"./mail-connector\";\r\nimport { NotificationsConnector } from \"./notifications-connector\";\r\nimport { SocketConnector } from \"./socket-connector\";\r\nimport { StorageConnector } from \"./storage.connector\";\r\nimport { ConnectorLifecyclePhase } from \"./types\";\r\nimport type { Connector, ConnectorName } from \"./types\";\r\n\r\nexport class ConnectorsManager {\r\n /**\r\n * Connectors list\r\n */\r\n private readonly connectors: Connector[] = [];\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor() {\r\n this.register(new LoggerConnector());\r\n this.register(new MailerConnector());\r\n this.register(new HttpConnector());\r\n this.register(new DatabaseConnector());\r\n this.register(new HeraldConnector());\r\n this.register(new CacheConnector());\r\n this.register(new StorageConnector());\r\n this.register(new SocketConnector());\r\n this.register(new NotificationsConnector());\r\n this.register(new AccessConnector());\r\n }\r\n\r\n /**\r\n * Register a connector\r\n */\r\n public register(...connectors: Connector[]): void {\r\n this.connectors.push(...connectors);\r\n // sort connectors by priority\r\n this.connectors.sort((a, b) => a.priority - b.priority);\r\n }\r\n\r\n /**\r\n * Get all connectors\r\n */\r\n public list(): Connector[] {\r\n return this.connectors;\r\n }\r\n\r\n /**\r\n * start all connectors\r\n */\r\n public async start(connectorsNames?: ConnectorName[]): Promise<void> {\r\n const connectorsList = connectorsNames\r\n ? this.connectors.filter((connector) => connectorsNames.includes(connector.name))\r\n : this.connectors;\r\n\r\n for (const connector of connectorsList) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of connectorsList) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors in the given lifecycle phase.\r\n *\r\n * The production builder and dev preload split startup around app\r\n * code: early phase before app imports, late phase after. Within a\r\n * phase, all connectors `boot()` first, then all `start()`, so\r\n * cross-connector wiring inside the phase still works (e.g. socket\r\n * reads http's instance during its own boot).\r\n */\r\n public async startPhase(phase: ConnectorLifecyclePhase): Promise<void> {\r\n const phaseConnectors = this.connectors.filter(\r\n (connector) => connector.lifecyclePhase === phase,\r\n );\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors except the given ones\r\n */\r\n public async startWithout(excludedConnectors: ConnectorName[]): Promise<void> {\r\n await this.start(\r\n this.connectors\r\n .filter((connector) => !excludedConnectors.includes(connector.name))\r\n .map((connector) => connector.name),\r\n );\r\n }\r\n\r\n /**\r\n * Shutdown all connectors\r\n */\r\n public async shutdown(): Promise<void> {\r\n // shut down connectors in reverse order\r\n for (const connector of this.connectors.reverse()) {\r\n try {\r\n await connector.shutdown();\r\n } catch (error) {\r\n devServeLog(colors.redBright(`❌ Failed to shutdown ${connector.name}: ${error}`));\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown connectors on process kill\r\n *\r\n * Handles graceful shutdown for both Unix and Windows:\r\n * - SIGINT: Ctrl+C on Unix, also caught on Windows but unreliable in child processes\r\n * - SIGTERM: Termination signal (Unix primarily)\r\n * - beforeExit: Fires when Node.js empties its event loop (more reliable on Windows)\r\n */\r\n public shutdownOnProcessKill(): void {\r\n let isShuttingDown = false;\r\n\r\n const gracefulShutdown = async (signal: string) => {\r\n if (isShuttingDown) return;\r\n isShuttingDown = true;\r\n\r\n console.log(`\\nExiting...`);\r\n await this.shutdown();\r\n process.exit(0);\r\n };\r\n\r\n // Unix signals\r\n process.on(\"SIGINT\", () => gracefulShutdown(\"SIGINT\"));\r\n process.on(\"SIGTERM\", () => gracefulShutdown(\"SIGTERM\"));\r\n\r\n // Windows-specific: handle when process is about to exit\r\n // This is more reliable on Windows for spawned child processes\r\n if (process.platform === \"win32\") {\r\n // Handle Ctrl+C on Windows specifically\r\n process.on(\"SIGHUP\", () => gracefulShutdown(\"SIGHUP\"));\r\n }\r\n }\r\n}\r\n\r\nexport const connectorsManager = new ConnectorsManager();\r\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAa,oBAAb,MAA+B;;;;CAS7B,AAAO,cAAc;oBALsB,CAAC;EAM1C,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,cAAc,CAAC;EACjC,KAAK,SAAS,IAAI,kBAAkB,CAAC;EACrC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,eAAe,CAAC;EAClC,KAAK,SAAS,IAAI,iBAAiB,CAAC;EACpC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,uBAAuB,CAAC;EAC1C,KAAK,SAAS,IAAI,gBAAgB,CAAC;CACrC;;;;CAKA,AAAO,SAAS,GAAG,YAA+B;EAChD,KAAK,WAAW,KAAK,GAAG,UAAU;EAElC,KAAK,WAAW,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CACxD;;;;CAKA,AAAO,OAAoB;EACzB,OAAO,KAAK;CACd;;;;CAKA,MAAa,MAAM,iBAAkD;EACnE,MAAM,iBAAiB,kBACnB,KAAK,WAAW,QAAQ,cAAc,gBAAgB,SAAS,UAAU,IAAI,CAAC,IAC9E,KAAK;EAET,KAAK,MAAM,aAAa,gBACtB,MAAM,UAAU,KAAK;EAGvB,KAAK,MAAM,aAAa,gBACtB,MAAM,UAAU,MAAM;CAE1B;;;;;;;;;;CAWA,MAAa,WAAW,OAA+C;EACrE,MAAM,kBAAkB,KAAK,WAAW,QACrC,cAAc,UAAU,mBAAmB,KAC9C;EAEA,KAAK,MAAM,aAAa,iBACtB,MAAM,UAAU,KAAK;EAGvB,KAAK,MAAM,aAAa,iBACtB,MAAM,UAAU,MAAM;CAE1B;;;;CAKA,MAAa,aAAa,oBAAoD;EAC5E,MAAM,KAAK,MACT,KAAK,WACF,QAAQ,cAAc,CAAC,mBAAmB,SAAS,UAAU,IAAI,CAAC,CAAC,CACnE,KAAK,cAAc,UAAU,IAAI,CACtC;CACF;;;;CAKA,MAAa,WAA0B;EAErC,KAAK,MAAM,aAAa,KAAK,WAAW,QAAQ,GAC9C,IAAI;GACF,MAAM,UAAU,SAAS;EAC3B,SAAS,OAAO;GACd,YAAY,OAAO,UAAU,wBAAwB,UAAU,KAAK,IAAI,OAAO,CAAC;EAClF;CAEJ;;;;;;;;;CAUA,AAAO,wBAA8B;EACnC,IAAI,iBAAiB;EAErB,MAAM,mBAAmB,OAAO,WAAmB;GACjD,IAAI,gBAAgB;GACpB,iBAAiB;GAEjB,QAAQ,IAAI,cAAc;GAC1B,MAAM,KAAK,SAAS;GACpB,QAAQ,KAAK,CAAC;EAChB;EAGA,QAAQ,GAAG,gBAAgB,iBAAiB,QAAQ,CAAC;EACrD,QAAQ,GAAG,iBAAiB,iBAAiB,SAAS,CAAC;EAIvD,IAAI,QAAQ,aAAa,SAEvB,QAAQ,GAAG,gBAAgB,iBAAiB,QAAQ,CAAC;CAEzD;AACF;AAEA,MAAa,oBAAoB,IAAI,kBAAkB"}
1
+ {"version":3,"file":"connectors-manager.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/connectors/connectors-manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { Application } from \"../application\";\r\nimport { devServeLog } from \"../dev-server/dev-logger\";\r\nimport { AccessConnector } from \"./access-connector\";\r\nimport { CacheConnector } from \"./cache-connector\";\r\nimport { DatabaseConnector } from \"./database-connector\";\r\nimport { HeraldConnector } from \"./herald-connector\";\r\nimport { HttpConnector } from \"./http-connector\";\r\nimport { LoggerConnector } from \"./logger-connector\";\r\nimport { MailerConnector } from \"./mail-connector\";\r\nimport { NotificationsConnector } from \"./notifications-connector\";\r\nimport { SocketConnector } from \"./socket-connector\";\r\nimport { StorageConnector } from \"./storage.connector\";\r\nimport { ConnectorLifecyclePhase } from \"./types\";\r\nimport type { Connector, ConnectorName } from \"./types\";\r\n\r\nexport class ConnectorsManager {\r\n /**\r\n * Connectors list\r\n */\r\n private readonly connectors: Connector[] = [];\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor() {\r\n this.register(new LoggerConnector());\r\n this.register(new MailerConnector());\r\n this.register(new HttpConnector());\r\n this.register(new DatabaseConnector());\r\n this.register(new HeraldConnector());\r\n this.register(new CacheConnector());\r\n this.register(new StorageConnector());\r\n this.register(new SocketConnector());\r\n this.register(new NotificationsConnector());\r\n this.register(new AccessConnector());\r\n }\r\n\r\n /**\r\n * Register a connector\r\n */\r\n public register(...connectors: Connector[]): void {\r\n this.connectors.push(...connectors);\r\n // sort connectors by priority\r\n this.connectors.sort((a, b) => a.priority - b.priority);\r\n }\r\n\r\n /**\r\n * Get all connectors\r\n */\r\n public list(): Connector[] {\r\n return this.connectors;\r\n }\r\n\r\n /**\r\n * start all connectors\r\n */\r\n public async start(connectorsNames?: ConnectorName[]): Promise<void> {\r\n const connectorsList = connectorsNames\r\n ? this.connectors.filter((connector) => connectorsNames.includes(connector.name))\r\n : this.connectors;\r\n\r\n for (const connector of connectorsList) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of connectorsList) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors in the given lifecycle phase.\r\n *\r\n * The production builder and dev preload split startup around app\r\n * code: early phase before app imports, late phase after. Within a\r\n * phase, all connectors `boot()` first, then all `start()`, so\r\n * cross-connector wiring inside the phase still works (e.g. socket\r\n * reads http's instance during its own boot).\r\n */\r\n public async startPhase(phase: ConnectorLifecyclePhase): Promise<void> {\r\n const phaseConnectors = this.connectors.filter(\r\n (connector) => connector.lifecyclePhase === phase,\r\n );\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors except the given ones\r\n */\r\n public async startWithout(excludedConnectors: ConnectorName[]): Promise<void> {\r\n await this.start(\r\n this.connectors\r\n .filter((connector) => !excludedConnectors.includes(connector.name))\r\n .map((connector) => connector.name),\r\n );\r\n }\r\n\r\n /**\r\n * Shutdown all connectors\r\n */\r\n public async shutdown(): Promise<void> {\r\n // App-owned teardown runs first, while every connector (db, cache, http) is\r\n // still up, so cleanup hooks can use them. Idempotent + error-isolated.\r\n await Application.runShutdownHooks();\r\n\r\n // Shut down connectors in reverse priority order. Copy the list first —\r\n // `reverse()` mutates in place, so reversing the live array would corrupt\r\n // the order on a second shutdown pass.\r\n for (const connector of [...this.connectors].reverse()) {\r\n try {\r\n await connector.shutdown();\r\n } catch (error) {\r\n devServeLog(colors.redBright(`❌ Failed to shutdown ${connector.name}: ${error}`));\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown connectors on process kill\r\n *\r\n * Handles graceful shutdown for both Unix and Windows:\r\n * - SIGINT: Ctrl+C on Unix, also caught on Windows but unreliable in child processes\r\n * - SIGTERM: Termination signal (Unix primarily)\r\n * - beforeExit: Fires when Node.js empties its event loop (more reliable on Windows)\r\n */\r\n public shutdownOnProcessKill(): void {\r\n let isShuttingDown = false;\r\n\r\n const gracefulShutdown = async (signal: string) => {\r\n if (isShuttingDown) return;\r\n isShuttingDown = true;\r\n\r\n console.log(`\\nExiting...`);\r\n await this.shutdown();\r\n process.exit(0);\r\n };\r\n\r\n // Unix signals\r\n process.on(\"SIGINT\", () => gracefulShutdown(\"SIGINT\"));\r\n process.on(\"SIGTERM\", () => gracefulShutdown(\"SIGTERM\"));\r\n\r\n // Windows-specific: handle when process is about to exit\r\n // This is more reliable on Windows for spawned child processes\r\n if (process.platform === \"win32\") {\r\n // Handle Ctrl+C on Windows specifically\r\n process.on(\"SIGHUP\", () => gracefulShutdown(\"SIGHUP\"));\r\n }\r\n }\r\n}\r\n\r\nexport const connectorsManager = new ConnectorsManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,oBAAb,MAA+B;;;;CAS7B,AAAO,cAAc;oBALsB,CAAC;EAM1C,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,cAAc,CAAC;EACjC,KAAK,SAAS,IAAI,kBAAkB,CAAC;EACrC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,eAAe,CAAC;EAClC,KAAK,SAAS,IAAI,iBAAiB,CAAC;EACpC,KAAK,SAAS,IAAI,gBAAgB,CAAC;EACnC,KAAK,SAAS,IAAI,uBAAuB,CAAC;EAC1C,KAAK,SAAS,IAAI,gBAAgB,CAAC;CACrC;;;;CAKA,AAAO,SAAS,GAAG,YAA+B;EAChD,KAAK,WAAW,KAAK,GAAG,UAAU;EAElC,KAAK,WAAW,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CACxD;;;;CAKA,AAAO,OAAoB;EACzB,OAAO,KAAK;CACd;;;;CAKA,MAAa,MAAM,iBAAkD;EACnE,MAAM,iBAAiB,kBACnB,KAAK,WAAW,QAAQ,cAAc,gBAAgB,SAAS,UAAU,IAAI,CAAC,IAC9E,KAAK;EAET,KAAK,MAAM,aAAa,gBACtB,MAAM,UAAU,KAAK;EAGvB,KAAK,MAAM,aAAa,gBACtB,MAAM,UAAU,MAAM;CAE1B;;;;;;;;;;CAWA,MAAa,WAAW,OAA+C;EACrE,MAAM,kBAAkB,KAAK,WAAW,QACrC,cAAc,UAAU,mBAAmB,KAC9C;EAEA,KAAK,MAAM,aAAa,iBACtB,MAAM,UAAU,KAAK;EAGvB,KAAK,MAAM,aAAa,iBACtB,MAAM,UAAU,MAAM;CAE1B;;;;CAKA,MAAa,aAAa,oBAAoD;EAC5E,MAAM,KAAK,MACT,KAAK,WACF,QAAQ,cAAc,CAAC,mBAAmB,SAAS,UAAU,IAAI,CAAC,CAAC,CACnE,KAAK,cAAc,UAAU,IAAI,CACtC;CACF;;;;CAKA,MAAa,WAA0B;EAGrC,MAAM,YAAY,iBAAiB;EAKnC,KAAK,MAAM,aAAa,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,QAAQ,GACnD,IAAI;GACF,MAAM,UAAU,SAAS;EAC3B,SAAS,OAAO;GACd,YAAY,OAAO,UAAU,wBAAwB,UAAU,KAAK,IAAI,OAAO,CAAC;EAClF;CAEJ;;;;;;;;;CAUA,AAAO,wBAA8B;EACnC,IAAI,iBAAiB;EAErB,MAAM,mBAAmB,OAAO,WAAmB;GACjD,IAAI,gBAAgB;GACpB,iBAAiB;GAEjB,QAAQ,IAAI,cAAc;GAC1B,MAAM,KAAK,SAAS;GACpB,QAAQ,KAAK,CAAC;EAChB;EAGA,QAAQ,GAAG,gBAAgB,iBAAiB,QAAQ,CAAC;EACrD,QAAQ,GAAG,iBAAiB,iBAAiB,SAAS,CAAC;EAIvD,IAAI,QAAQ,aAAa,SAEvB,QAAQ,GAAG,gBAAgB,iBAAiB,QAAQ,CAAC;CAEzD;AACF;AAEA,MAAa,oBAAoB,IAAI,kBAAkB"}
@@ -1,7 +1,7 @@
1
1
  import { container } from "../container/index.mjs";
2
2
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./types.mjs";
3
3
  import { BaseConnector } from "./base-connector.mjs";
4
- import config from "@mongez/config";
4
+ import baseConfig from "@mongez/config";
5
5
  import { connectToDatabase, dataSourceRegistry } from "@warlock.js/cascade";
6
6
 
7
7
  //#region ../@warlock.js/core/src/connectors/database-connector.ts
@@ -21,7 +21,7 @@ var DatabaseConnector = class extends BaseConnector {
21
21
  * Initialize database connection
22
22
  */
23
23
  async start() {
24
- const databaseConfig = config.get("database");
24
+ const databaseConfig = baseConfig.get("database");
25
25
  if (!databaseConfig) return;
26
26
  try {
27
27
  const source = await connectToDatabase(databaseConfig);