@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.
- package/CHANGELOG.md +16 -1
- package/esm/application/application.d.mts +126 -4
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs +133 -0
- package/esm/application/application.mjs.map +1 -1
- package/esm/application/index.d.mts +1 -1
- package/esm/cli/types.d.mts +2 -1
- package/esm/cli/types.d.mts.map +1 -1
- package/esm/config/config-getter.mjs +5 -5
- package/esm/config/config-getter.mjs.map +1 -1
- package/esm/config/config-loader.mjs +2 -2
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/connectors/access-connector.mjs +2 -2
- package/esm/connectors/access-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.mjs +2 -2
- package/esm/connectors/cache-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +4 -1
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.mjs +2 -2
- package/esm/connectors/database-connector.mjs.map +1 -1
- package/esm/connectors/herald-connector.mjs +2 -2
- package/esm/connectors/herald-connector.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts +8 -0
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +40 -7
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.mjs +2 -2
- package/esm/connectors/logger-connector.mjs.map +1 -1
- package/esm/connectors/mail-connector.mjs +2 -2
- package/esm/connectors/mail-connector.mjs.map +1 -1
- package/esm/connectors/notifications-connector.mjs +2 -2
- package/esm/connectors/notifications-connector.mjs.map +1 -1
- package/esm/connectors/socket-connector.mjs +3 -3
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +7 -0
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs +1 -1
- package/esm/http/config.mjs +2 -2
- package/esm/http/config.mjs.map +1 -1
- package/esm/http/createHttpApplication.mjs +2 -2
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/health.d.mts +57 -0
- package/esm/http/health.d.mts.map +1 -0
- package/esm/http/health.mjs +75 -0
- package/esm/http/health.mjs.map +1 -0
- package/esm/http/index.d.mts +2 -1
- package/esm/http/index.mjs +2 -1
- package/esm/http/middleware/idempotency.middleware.mjs +5 -5
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +2 -2
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +4 -4
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/plugins.mjs +9 -9
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/response.mjs +5 -5
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts +16 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +23 -3
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/types.d.mts +45 -0
- package/esm/http/types.d.mts.map +1 -1
- package/esm/index.d.mts +5 -4
- package/esm/index.mjs +6 -5
- package/esm/production/production-builder.mjs +2 -2
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/utils/environment.d.mts +6 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/environment.mjs.map +1 -1
- package/esm/utils/index.d.mts +1 -1
- package/esm/utils/paths.mjs +2 -2
- package/esm/utils/paths.mjs.map +1 -1
- package/esm/validation/validateAll.mjs +2 -2
- package/esm/validation/validateAll.mjs.map +1 -1
- package/llms-full.txt +164 -2
- package/llms.txt +2 -1
- package/package.json +9 -9
- package/skills/health-checks/SKILL.md +98 -0
- 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
|
-
##
|
|
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:
|
|
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:
|
|
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":";;;
|
|
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"}
|
package/esm/cli/types.d.mts
CHANGED
|
@@ -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?:
|
|
72
|
+
runtimeStrategy?: RuntimeStrategy;
|
|
72
73
|
/**
|
|
73
74
|
* Override current detect environment
|
|
74
75
|
*/
|
package/esm/cli/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../@warlock.js/core/src/cli/types.ts"],"mappings":"
|
|
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
|
|
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
|
|
17
|
+
const config = {
|
|
18
18
|
key(key, defaultValue) {
|
|
19
|
-
return
|
|
19
|
+
return baseConfig.get(key, defaultValue);
|
|
20
20
|
},
|
|
21
21
|
get(name, defaultValue) {
|
|
22
|
-
return
|
|
22
|
+
return baseConfig.get(name, defaultValue);
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
|
-
export { config
|
|
27
|
+
export { config };
|
|
28
28
|
//# sourceMappingURL=config-getter.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-getter.mjs","names":[
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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":";;;
|
|
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
|
-
|
|
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 //
|
|
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
|
|
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 =
|
|
24
|
+
const databaseConfig = baseConfig.get("database");
|
|
25
25
|
if (!databaseConfig) return;
|
|
26
26
|
try {
|
|
27
27
|
const source = await connectToDatabase(databaseConfig);
|