@warlock.js/core 5.17.0 → 5.18.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 +14 -0
- package/esm/container/development-model-modules.d.mts +22 -0
- package/esm/container/development-model-modules.d.mts.map +1 -0
- package/esm/container/index.d.mts +2 -0
- package/esm/container/index.d.mts.map +1 -1
- package/esm/container/index.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs +5 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/workers/ts-health.worker.mjs +37 -8
- package/esm/dev-server/health-checker/workers/ts-health.worker.mjs.map +1 -1
- package/esm/dev-server/model-module-registry.mjs +90 -0
- package/esm/dev-server/model-module-registry.mjs.map +1 -0
- package/esm/dev-server/module-loader.mjs +4 -0
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/http/response.d.mts +3 -3
- package/esm/http/response.d.mts.map +1 -1
- package/esm/index.d.mts +2 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
> ⚠ **Versioning: `@warlock.js/*` does not follow SemVer strictly — breaking changes may ship in a minor.** This is a deliberate decision, not an oversight: the framework is pre-adoption and the cost of a major per behaviour fix currently outweighs the benefit. **Pin an exact version or a tilde range (`~4.13.0`) if you need to opt into changes rather than receive them.** Every breaking change is marked **BREAKING** in its entry and summarised in an _Upgrading_ section at the top of the release. **This policy will change once the framework has consumers beyond its author.**
|
|
8
8
|
|
|
9
|
+
## 5.18.0 - 2026-09-22
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Development model loading now shares model ownership and reload notifications with Web's SSR adapter, allowing pages to reuse the same model constructors as Core instead of registering a second copy.
|
|
14
|
+
|
|
15
|
+
## 5.17.1 - 2026-09-22
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `@warlock.js/web` fixes page projection to resolve import references by lexical binding after server configuration is removed. A component-local `t = useTrans()` no longer retains an unrelated metadata-only Core import in the client view.
|
|
20
|
+
- Development startup now completes initial typings generation before it starts health checking, so the checker receives the generated declaration roots. Health checking remains background work and does not delay connector readiness.
|
|
21
|
+
- The health checker preserves declaration roots already included by the project's tsconfig.
|
|
22
|
+
|
|
9
23
|
## 5.17.0 - 2026-09-21
|
|
10
24
|
|
|
11
25
|
### Security
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region ../core/src/container/development-model-modules.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A read-only view of development model modules for adapters that need to
|
|
4
|
+
* bridge an already-loaded model into their own development module graph.
|
|
5
|
+
*
|
|
6
|
+
* This is intentionally a container-only internal contract: it is not a
|
|
7
|
+
* package-root dev-server export and it exposes no module namespace.
|
|
8
|
+
*/
|
|
9
|
+
type DevelopmentModelModuleState = "ready" | "removed";
|
|
10
|
+
type DevelopmentModelModuleEntry = Readonly<{
|
|
11
|
+
/** Exact file URL used by ModuleLoader to import the module. */url: string; /** Monotonic for this canonical path, including removal and re-addition. */
|
|
12
|
+
generation: number;
|
|
13
|
+
hasDefault: boolean;
|
|
14
|
+
state: DevelopmentModelModuleState;
|
|
15
|
+
}>;
|
|
16
|
+
type DevelopmentModelModules = {
|
|
17
|
+
get(absolutePath: string): DevelopmentModelModuleEntry | undefined;
|
|
18
|
+
subscribe(listener: (entry: DevelopmentModelModuleEntry) => void): () => void;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { DevelopmentModelModuleEntry, DevelopmentModelModuleState, DevelopmentModelModules };
|
|
22
|
+
//# sourceMappingURL=development-model-modules.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"development-model-modules.d.mts","names":[],"sources":["../../../../../../../core/src/container/development-model-modules.ts"],"mappings":";;AAOA;;;;AAAuC;AAEvC;KAFY,2BAAA;AAAA,KAEA,2BAAA,GAA8B,QAAQ;EAAA,gEAEhD,GAAA;EAEA,UAAA;EACA,UAAA;EACA,KAAA,EAAO,2BAAA;AAAA;AAAA,KAGG,uBAAA;EACV,GAAA,CAAI,YAAA,WAAuB,2BAAA;EAC3B,SAAA,CAAU,QAAA,GAAW,KAAA,EAAO,2BAA2B;AAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Router } from "../router/router.mjs";
|
|
2
|
+
import { DevelopmentModelModuleEntry, DevelopmentModelModuleState, DevelopmentModelModules } from "./development-model-modules.mjs";
|
|
2
3
|
import { DataSource } from "@warlock.js/cascade";
|
|
3
4
|
import { FastifyInstance } from "fastify";
|
|
4
5
|
import { Server } from "socket.io";
|
|
@@ -13,6 +14,7 @@ type ContainerTypes = {
|
|
|
13
14
|
"http.baseUrl": string;
|
|
14
15
|
socket: Server;
|
|
15
16
|
"database.source": DataSource;
|
|
17
|
+
"development.modelModules": DevelopmentModelModules;
|
|
16
18
|
};
|
|
17
19
|
type ContainerKeys = keyof ContainerTypes | (string & {});
|
|
18
20
|
declare class Container {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"mappings":";;;;;;;;;AAwBA;KAAY,cAAA;EACV,MAAA,EAAQ,MAAA;EACR,aAAA,EAAe,eAAA;EACf,cAAA;EACA,MAAA,EAAQ,MAAA;EACR,iBAAA,EAAmB,UAAA;EACnB,0BAAA,EAA4B,uBAAA;AAAA;AAAA,KAGzB,aAAA,SAAsB,cAAc;AAAA,cAEnC,SAAA;EAVI;;;EAcD,GAAA,iBAAoB,cAAA,EAAgB,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;EAClE,GAAA,UAAa,GAAA,UAAa,KAAA,EAAO,CAAA;EAZhC;;;;;;AAE2C;AACnD;;;;EAyBO,GAAA,iBAAoB,cAAA,EAAgB,GAAA,EAAK,CAAA,GAAI,cAAA,CAAe,CAAA;EAC5D,GAAA,UAAa,GAAA,WAAc,CAAA;EAtBrB;;;EAmCN,GAAA,CAAI,GAAA,EAAK,aAAA;EA/B0C;;;;;;;;;EA4CnD,MAAA,iBAAuB,cAAA,EAAgB,GAAA,EAAK,CAAA,GAAI,cAAA,CAAe,CAAA;EAC/D,MAAA,UAAgB,GAAA,WAAc,CAAA;EADkB;;;EAShD,SAAA,iBAA0B,cAAA,EAAgB,GAAA,EAAK,CAAA,GAAI,cAAA,CAAe,CAAA;EAClE,SAAA,UAAmB,GAAA,WAAc,CAAA;EADkB;;;EASnD,MAAA,CAAO,GAAA;AAAA;AAAA,cAKH,SAAA,EAAS,SAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"sourcesContent":["import { type DataSource } from \"@warlock.js/cascade\";\nimport { type FastifyInstance } from \"fastify\";\nimport { type Server } from \"socket.io\";\nimport { ContainerKeyMissingError } from \"../errors/container-key-missing-error\";\nimport { type Router } from \"../router\";\nimport {\n getRegisteredContainerInstances,\n registerContainerInstance,\n} from \"./container-instance-registry\";\n\nconst containerMap: Map<string, any> = new Map();\n\nregisterContainerInstance();\n\n/**\n * Known container types for better IDE support\n */\nexport type ContainerTypes = {\n router: Router;\n \"http.server\": FastifyInstance;\n \"http.baseUrl\": string;\n socket: Server;\n \"database.source\": DataSource;\n};\n\ntype ContainerKeys = keyof ContainerTypes | (string & {});\n\nclass Container {\n /**\n * Set a value in the container\n */\n public set<K extends keyof ContainerTypes>(key: K, value: ContainerTypes[K]): void;\n public set<T = any>(key: string, value: T): void;\n public set(key: any, value: any) {\n containerMap.set(key, value);\n }\n\n /**\n * Get a value from the container, or throw if `key` is not registered.\n *\n * Behaves exactly like an ordinary \"not registered\" error when only one\n * copy of this module is loaded. When {@link getRegisteredContainerInstances}\n * shows more than one, the thrown error additionally names that count, so\n * a duplicate-instance dev-path miss is never indistinguishable from a\n * genuine missing registration.\n *\n * Use {@link tryGet} instead when the value is genuinely optional.\n */\n public get<K extends keyof ContainerTypes>(key: K): ContainerTypes[K];\n public get<T = any>(key: string): T;\n public get(key: any): any {\n if (!containerMap.has(key)) {\n const instanceCount = getRegisteredContainerInstances().length;\n throw new ContainerKeyMissingError(key, instanceCount, [...containerMap.keys()]);\n }\n\n return containerMap.get(key);\n }\n\n /**\n * Check if a key exists in the container\n */\n public has(key: ContainerKeys) {\n return containerMap.has(key);\n }\n\n /**\n * Get a value from the container, or `undefined` if `key` is not\n * registered.\n *\n * Use this only for genuinely optional dependencies — where the caller\n * has its own fallback for an absent value. For every other read, prefer\n * {@link get}: a hole in the container should fail loudly, not hand back\n * `undefined` typed as the real value.\n */\n public tryGet<K extends keyof ContainerTypes>(key: K): ContainerTypes[K] | undefined;\n public tryGet<T = any>(key: string): T | undefined;\n public tryGet(key: any): any {\n return containerMap.get(key);\n }\n\n /**\n * @deprecated `get` now throws for a missing key; use {@link get}.\n */\n public getOrFail<K extends keyof ContainerTypes>(key: K): ContainerTypes[K];\n public getOrFail<T = any>(key: string): T;\n public getOrFail(key: any): any {\n return this.get(key);\n }\n\n /**\n * Delete a key from the container\n */\n public delete(key: string) {\n containerMap.delete(key);\n }\n}\n\nexport const container = new Container();\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"sourcesContent":["import { type DataSource } from \"@warlock.js/cascade\";\nimport { type FastifyInstance } from \"fastify\";\nimport { type Server } from \"socket.io\";\nimport { ContainerKeyMissingError } from \"../errors/container-key-missing-error\";\nimport { type Router } from \"../router\";\nimport {\n getRegisteredContainerInstances,\n registerContainerInstance,\n} from \"./container-instance-registry\";\nimport type { DevelopmentModelModules } from \"./development-model-modules\";\n\nexport type {\n DevelopmentModelModuleEntry,\n DevelopmentModelModules,\n DevelopmentModelModuleState,\n} from \"./development-model-modules\";\n\nconst containerMap: Map<string, any> = new Map();\n\nregisterContainerInstance();\n\n/**\n * Known container types for better IDE support\n */\nexport type ContainerTypes = {\n router: Router;\n \"http.server\": FastifyInstance;\n \"http.baseUrl\": string;\n socket: Server;\n \"database.source\": DataSource;\n \"development.modelModules\": DevelopmentModelModules;\n};\n\ntype ContainerKeys = keyof ContainerTypes | (string & {});\n\nclass Container {\n /**\n * Set a value in the container\n */\n public set<K extends keyof ContainerTypes>(key: K, value: ContainerTypes[K]): void;\n public set<T = any>(key: string, value: T): void;\n public set(key: any, value: any) {\n containerMap.set(key, value);\n }\n\n /**\n * Get a value from the container, or throw if `key` is not registered.\n *\n * Behaves exactly like an ordinary \"not registered\" error when only one\n * copy of this module is loaded. When {@link getRegisteredContainerInstances}\n * shows more than one, the thrown error additionally names that count, so\n * a duplicate-instance dev-path miss is never indistinguishable from a\n * genuine missing registration.\n *\n * Use {@link tryGet} instead when the value is genuinely optional.\n */\n public get<K extends keyof ContainerTypes>(key: K): ContainerTypes[K];\n public get<T = any>(key: string): T;\n public get(key: any): any {\n if (!containerMap.has(key)) {\n const instanceCount = getRegisteredContainerInstances().length;\n throw new ContainerKeyMissingError(key, instanceCount, [...containerMap.keys()]);\n }\n\n return containerMap.get(key);\n }\n\n /**\n * Check if a key exists in the container\n */\n public has(key: ContainerKeys) {\n return containerMap.has(key);\n }\n\n /**\n * Get a value from the container, or `undefined` if `key` is not\n * registered.\n *\n * Use this only for genuinely optional dependencies — where the caller\n * has its own fallback for an absent value. For every other read, prefer\n * {@link get}: a hole in the container should fail loudly, not hand back\n * `undefined` typed as the real value.\n */\n public tryGet<K extends keyof ContainerTypes>(key: K): ContainerTypes[K] | undefined;\n public tryGet<T = any>(key: string): T | undefined;\n public tryGet(key: any): any {\n return containerMap.get(key);\n }\n\n /**\n * @deprecated `get` now throws for a missing key; use {@link get}.\n */\n public getOrFail<K extends keyof ContainerTypes>(key: K): ContainerTypes[K];\n public getOrFail<T = any>(key: string): T;\n public getOrFail(key: any): any {\n return this.get(key);\n }\n\n /**\n * Delete a key from the container\n */\n public delete(key: string) {\n containerMap.delete(key);\n }\n}\n\nexport const container = new Container();\n"],"mappings":";;;;AAiBA,MAAM,+BAAiC,IAAI,IAAI;AAE/C,0BAA0B;AAgB1B,IAAM,YAAN,MAAgB;CAMd,AAAO,IAAI,KAAU,OAAY;EAC/B,aAAa,IAAI,KAAK,KAAK;CAC7B;CAeA,AAAO,IAAI,KAAe;EACxB,IAAI,CAAC,aAAa,IAAI,GAAG,GAAG;GAC1B,MAAM,gBAAgB,gCAAgC,CAAC,CAAC;GACxD,MAAM,IAAI,yBAAyB,KAAK,eAAe,CAAC,GAAG,aAAa,KAAK,CAAC,CAAC;EACjF;EAEA,OAAO,aAAa,IAAI,GAAG;CAC7B;;;;CAKA,AAAO,IAAI,KAAoB;EAC7B,OAAO,aAAa,IAAI,GAAG;CAC7B;CAaA,AAAO,OAAO,KAAe;EAC3B,OAAO,aAAa,IAAI,GAAG;CAC7B;CAOA,AAAO,UAAU,KAAe;EAC9B,OAAO,KAAK,IAAI,GAAG;CACrB;;;;CAKA,AAAO,OAAO,KAAa;EACzB,aAAa,OAAO,GAAG;CACzB;AACF;AAEA,MAAa,YAAY,IAAI,UAAU"}
|
|
@@ -61,7 +61,7 @@ var DevelopmentServer = class {
|
|
|
61
61
|
const devServerConfig = await warlockConfigManager.get("devServer");
|
|
62
62
|
const generateTypings = this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;
|
|
63
63
|
const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;
|
|
64
|
-
if (generateTypings) typeGenerator.executeGenerateAllCommand();
|
|
64
|
+
if (generateTypings) await typeGenerator.executeGenerateAllCommand();
|
|
65
65
|
if (healthCheckers) filesOrchestrator.startCheckingHealth(healthCheckers === true ? void 0 : healthCheckers);
|
|
66
66
|
} catch (error) {
|
|
67
67
|
devServeLog(colors.redBright(`Failed to start Development Server: ${error}`));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"development-server.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/development-server.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport events from \"@mongez/events\";\r\nimport { fileExistsAsync, unlinkAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { warlockConfigManager } from \"../warlock-config\";\r\nimport { BootPreconditionError } from \"./boot-precondition-error\";\r\nimport { devLogInfo, devLogSection, devLogWarn, devServeLog } from \"./dev-logger\";\r\nimport { filesOrchestrator } from \"./files-orchestrator\";\r\nimport { MANIFEST_PATH } from \"./flags\";\r\nimport type { IncomingReloadTimings } from \"./layer-executor\";\r\nimport { LayerExecutor } from \"./layer-executor\";\r\nimport { printReadyBlock } from \"./ready-block\";\r\nimport { restartDevServer } from \"./restart-dev-server\";\r\nimport { devServerShortcuts } from \"./shortcuts\";\r\nimport type { StartDevServerOptions } from \"./start-development-server\";\r\nimport { typeGenerator } from \"./type-generator\";\r\n\r\ntype Batch = {\r\n added: string[];\r\n changed: string[];\r\n deleted: string[];\r\n timings?: IncomingReloadTimings;\r\n};\r\n\r\n/**\r\n * Top-level coordinator for `warlock dev`. Wires the file orchestrator, the\r\n * connectors, and the layer executor together, and listens for the watcher's\r\n * batched events to drive HMR.\r\n */\r\nexport class DevelopmentServer {\r\n private layerExecutor?: LayerExecutor;\r\n private running = false;\r\n private readonly options: StartDevServerOptions;\r\n\r\n public constructor(options: StartDevServerOptions = {}) {\r\n this.options = options;\r\n devLogSection(\"Starting Development Server...\");\r\n }\r\n\r\n public async start(): Promise<void> {\r\n try {\r\n const startedAt = performance.now();\r\n\r\n // --fresh deletes the manifest so reconciliation re-parses every file\r\n // from disk. Transpile caching is owned by the loader hook (in-memory).\r\n if (this.options.fresh && (await fileExistsAsync(MANIFEST_PATH))) {\r\n await unlinkAsync(MANIFEST_PATH);\r\n devServeLog(colors.cyanBright(\"Cleared manifest (--fresh)\"));\r\n }\r\n\r\n await filesOrchestrator.init();\r\n await filesOrchestrator.initializeAll();\r\n await filesOrchestrator.watchFiles();\r\n\r\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.getFiles());\r\n\r\n this.setupEventListeners();\r\n\r\n // Decorator-driven registries (models, etc.) must be populated before\r\n // routes/services can resolve symbols by name.\r\n await this.autoDiscoverFiles();\r\n\r\n await filesOrchestrator.moduleLoader.loadAll();\r\n\r\n // A rejecting validator (Application.onValidateBoot) must abort boot\r\n // before late-phase connectors bind a port — this is what makes the\r\n // hook actually prevent serving instead of merely being defined.\r\n //\r\n // Wrapped and re-thrown as a BootPreconditionError: a rejected\r\n // validator is, by definition, a precondition whose cause cannot\r\n // change because the worker tried again — the supervisor must not\r\n // restart into the same rejection. See boot-precondition-error.ts.\r\n try {\r\n await Application.runStartupValidators();\r\n } catch (error) {\r\n throw new BootPreconditionError((error as Error).message, { cause: error });\r\n }\r\n\r\n // Late-phase connectors (http, socket) bind after app code has\r\n // registered routes/listeners.\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\r\n\r\n this.layerExecutor = new LayerExecutor(\r\n filesOrchestrator.getDependencyGraph(),\r\n filesOrchestrator.specialFilesCollector,\r\n filesOrchestrator.moduleLoader,\r\n (absolutePath) => filesOrchestrator.bumpVersion(absolutePath),\r\n () => filesOrchestrator.flushVersionBumps(),\r\n );\r\n\r\n this.running = true;\r\n\r\n const duration = performance.now() - startedAt;\r\n\r\n // App modules are loaded and both connector phases are active — signal a\r\n // complete boot so `Application.onceBooted(...)` listeners fire.\r\n Application.markBooted({\r\n environment: Application.environment,\r\n runtimeStrategy: Application.runtimeStrategy,\r\n bootDurationMs: duration,\r\n });\r\n\r\n // ONE block, AFTER the boot it summarises, replacing the scattered\r\n // \"N route(s) registered\" / \"Server ready at …\" / \"ready in …\" lines that\r\n // used to arrive in completion order. Warnings logged during boot sit\r\n // above it and stay there — see `ready-block.ts`.\r\n printReadyBlock(duration);\r\n\r\n // Precedence: explicit CLI option > devServer.* config > default.\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n const generateTypings =\r\n this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;\r\n const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;\r\n\r\n if (generateTypings) typeGenerator.executeGenerateAllCommand();\r\n\r\n if (healthCheckers) {\r\n filesOrchestrator.startCheckingHealth(healthCheckers === true ? undefined : healthCheckers);\r\n }\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to start Development Server: ${error}`));\r\n await this.shutdown();\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Eagerly import files whose decorators populate global registries so any\r\n * symbol-by-name resolution later in boot finds them.\r\n */\r\n private async autoDiscoverFiles(): Promise<void> {\r\n const discoveryTypes = [\"model\"] as const;\r\n for (const file of filesOrchestrator.files.values()) {\r\n if (file.type && (discoveryTypes as readonly string[]).includes(file.type)) {\r\n await filesOrchestrator.moduleLoader.loadModule(file, file.type);\r\n }\r\n }\r\n }\r\n\r\n private setupEventListeners(): void {\r\n events.on(\"dev-server:batch-complete\", (batch: Batch) => this.handleBatchComplete(batch));\r\n }\r\n\r\n private async handleBatchComplete(batch: Batch): Promise<void> {\r\n if (!this.running || !this.layerExecutor) return;\r\n\r\n // warlock.config.ts holds settings read at boot (CLI commands, build\r\n // options, watch patterns, scheduled jobs) and .env feeds every config\r\n // that reads it. Neither can be hot-reloaded without leaving running\r\n // services configured with stale values, so the only honest response is\r\n // a full restart.\r\n const restartTrigger = this.findRestartTrigger(batch);\r\n\r\n if (restartTrigger) {\r\n // On success this never comes back — the process exits and the\r\n // supervisor replaces it. Reaching the next line means the restart was\r\n // declined by config or wasn't possible, so we fall through and still\r\n // hot-reload whatever ordinary code shared the batch.\r\n await this.restartForConfigChange(restartTrigger);\r\n }\r\n\r\n // No-op saves (an editor that fsyncs without a content write) are already\r\n // filtered upstream by the file hash in FileEventHandler, so batch.changed\r\n // only contains genuinely-changed paths by the time it reaches here.\r\n\r\n const total = batch.added.length + batch.changed.length + batch.deleted.length;\r\n if (total === 0) return;\r\n\r\n // Boot-time files still ride along in `changed` when a restart was\r\n // declined (`restartOnConfigChange: false`) or wasn't possible — they are\r\n // never hot-reloaded, so keep them out of the reload set either way.\r\n const codeFiles = [...batch.added, ...batch.changed].filter(\r\n (p) => !isEnvPath(p) && p !== \"warlock.config.ts\",\r\n );\r\n\r\n try {\r\n await this.layerExecutor.executeBatchReload(\r\n codeFiles,\r\n filesOrchestrator.getFiles(),\r\n batch.deleted,\r\n batch.changed,\r\n batch.timings,\r\n );\r\n\r\n typeGenerator.executeTypingsGenerator([...batch.added, ...batch.changed, ...batch.deleted]);\r\n\r\n filesOrchestrator.checkHealth(batch);\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to execute batch reload: ${error}`));\r\n }\r\n }\r\n\r\n /**\r\n * The boot-time file in this batch that can only be applied by restarting,\r\n * or `undefined` when the batch is ordinary application code.\r\n */\r\n private findRestartTrigger(batch: Batch): string | undefined {\r\n if (batch.changed.includes(\"warlock.config.ts\")) {\r\n return \"warlock.config.ts\";\r\n }\r\n\r\n return [...batch.added, ...batch.changed].find(isEnvPath);\r\n }\r\n\r\n /**\r\n * Restart so a changed boot-time file actually takes effect.\r\n *\r\n * Opt out with `devServer.restartOnConfigChange: false` and the old\r\n * behaviour returns: a warning telling you to restart yourself. If the\r\n * restart can't be performed at all (no supervisor, a shutdown that threw),\r\n * `restartDevServer` reports it and we fall back to the same warning rather\r\n * than leaving the server running against config it isn't using.\r\n */\r\n private async restartForConfigChange(file: string): Promise<void> {\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n\r\n if (devServerConfig?.restartOnConfigChange === false) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n return;\r\n }\r\n\r\n devLogInfo(`${file} changed — restarting to apply.`);\r\n\r\n const restarted = await restartDevServer(this);\r\n\r\n if (!restarted) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n }\r\n }\r\n\r\n public async shutdown(): Promise<void> {\r\n // Always hand the terminal back, even on a repeat/no-op shutdown — a\r\n // process that exits while stdin is still in raw mode leaves the user's\r\n // shell without line editing or Ctrl+C.\r\n devServerShortcuts.release();\r\n\r\n if (!this.running) return;\r\n devServeLog(colors.redBright(\"Shutting down Development Server...\"));\r\n this.running = false;\r\n await connectorsManager.shutdown();\r\n devServeLog(colors.greenBright(\"Development Server stopped\"));\r\n }\r\n\r\n public isRunning(): boolean {\r\n return this.running;\r\n }\r\n}\r\n\r\nfunction isEnvPath(path: string): boolean {\r\n const basename = path.split(\"/\").pop() ?? path;\r\n return basename === \".env\" || basename.startsWith(\".env.\");\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"development-server.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/development-server.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport events from \"@mongez/events\";\r\nimport { fileExistsAsync, unlinkAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { warlockConfigManager } from \"../warlock-config\";\r\nimport { BootPreconditionError } from \"./boot-precondition-error\";\r\nimport { devLogInfo, devLogSection, devLogWarn, devServeLog } from \"./dev-logger\";\r\nimport { filesOrchestrator } from \"./files-orchestrator\";\r\nimport { MANIFEST_PATH } from \"./flags\";\r\nimport type { IncomingReloadTimings } from \"./layer-executor\";\r\nimport { LayerExecutor } from \"./layer-executor\";\r\nimport { printReadyBlock } from \"./ready-block\";\r\nimport { restartDevServer } from \"./restart-dev-server\";\r\nimport { devServerShortcuts } from \"./shortcuts\";\r\nimport type { StartDevServerOptions } from \"./start-development-server\";\r\nimport { typeGenerator } from \"./type-generator\";\r\n\r\ntype Batch = {\r\n added: string[];\r\n changed: string[];\r\n deleted: string[];\r\n timings?: IncomingReloadTimings;\r\n};\r\n\r\n/**\r\n * Top-level coordinator for `warlock dev`. Wires the file orchestrator, the\r\n * connectors, and the layer executor together, and listens for the watcher's\r\n * batched events to drive HMR.\r\n */\r\nexport class DevelopmentServer {\r\n private layerExecutor?: LayerExecutor;\r\n private running = false;\r\n private readonly options: StartDevServerOptions;\r\n\r\n public constructor(options: StartDevServerOptions = {}) {\r\n this.options = options;\r\n devLogSection(\"Starting Development Server...\");\r\n }\r\n\r\n public async start(): Promise<void> {\r\n try {\r\n const startedAt = performance.now();\r\n\r\n // --fresh deletes the manifest so reconciliation re-parses every file\r\n // from disk. Transpile caching is owned by the loader hook (in-memory).\r\n if (this.options.fresh && (await fileExistsAsync(MANIFEST_PATH))) {\r\n await unlinkAsync(MANIFEST_PATH);\r\n devServeLog(colors.cyanBright(\"Cleared manifest (--fresh)\"));\r\n }\r\n\r\n await filesOrchestrator.init();\r\n await filesOrchestrator.initializeAll();\r\n await filesOrchestrator.watchFiles();\r\n\r\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.getFiles());\r\n\r\n this.setupEventListeners();\r\n\r\n // Decorator-driven registries (models, etc.) must be populated before\r\n // routes/services can resolve symbols by name.\r\n await this.autoDiscoverFiles();\r\n\r\n await filesOrchestrator.moduleLoader.loadAll();\r\n\r\n // A rejecting validator (Application.onValidateBoot) must abort boot\r\n // before late-phase connectors bind a port — this is what makes the\r\n // hook actually prevent serving instead of merely being defined.\r\n //\r\n // Wrapped and re-thrown as a BootPreconditionError: a rejected\r\n // validator is, by definition, a precondition whose cause cannot\r\n // change because the worker tried again — the supervisor must not\r\n // restart into the same rejection. See boot-precondition-error.ts.\r\n try {\r\n await Application.runStartupValidators();\r\n } catch (error) {\r\n throw new BootPreconditionError((error as Error).message, { cause: error });\r\n }\r\n\r\n // Late-phase connectors (http, socket) bind after app code has\r\n // registered routes/listeners.\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\r\n\r\n this.layerExecutor = new LayerExecutor(\r\n filesOrchestrator.getDependencyGraph(),\r\n filesOrchestrator.specialFilesCollector,\r\n filesOrchestrator.moduleLoader,\r\n (absolutePath) => filesOrchestrator.bumpVersion(absolutePath),\r\n () => filesOrchestrator.flushVersionBumps(),\r\n );\r\n\r\n this.running = true;\r\n\r\n const duration = performance.now() - startedAt;\r\n\r\n // App modules are loaded and both connector phases are active — signal a\r\n // complete boot so `Application.onceBooted(...)` listeners fire.\r\n Application.markBooted({\r\n environment: Application.environment,\r\n runtimeStrategy: Application.runtimeStrategy,\r\n bootDurationMs: duration,\r\n });\r\n\r\n // ONE block, AFTER the boot it summarises, replacing the scattered\r\n // \"N route(s) registered\" / \"Server ready at …\" / \"ready in …\" lines that\r\n // used to arrive in completion order. Warnings logged during boot sit\r\n // above it and stay there — see `ready-block.ts`.\r\n printReadyBlock(duration);\r\n\r\n // Precedence: explicit CLI option > devServer.* config > default.\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n const generateTypings =\r\n this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;\r\n const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;\r\n\r\n if (generateTypings) await typeGenerator.executeGenerateAllCommand();\r\n\r\n if (healthCheckers) {\r\n filesOrchestrator.startCheckingHealth(healthCheckers === true ? undefined : healthCheckers);\r\n }\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to start Development Server: ${error}`));\r\n await this.shutdown();\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Eagerly import files whose decorators populate global registries so any\r\n * symbol-by-name resolution later in boot finds them.\r\n */\r\n private async autoDiscoverFiles(): Promise<void> {\r\n const discoveryTypes = [\"model\"] as const;\r\n for (const file of filesOrchestrator.files.values()) {\r\n if (file.type && (discoveryTypes as readonly string[]).includes(file.type)) {\r\n await filesOrchestrator.moduleLoader.loadModule(file, file.type);\r\n }\r\n }\r\n }\r\n\r\n private setupEventListeners(): void {\r\n events.on(\"dev-server:batch-complete\", (batch: Batch) => this.handleBatchComplete(batch));\r\n }\r\n\r\n private async handleBatchComplete(batch: Batch): Promise<void> {\r\n if (!this.running || !this.layerExecutor) return;\r\n\r\n // warlock.config.ts holds settings read at boot (CLI commands, build\r\n // options, watch patterns, scheduled jobs) and .env feeds every config\r\n // that reads it. Neither can be hot-reloaded without leaving running\r\n // services configured with stale values, so the only honest response is\r\n // a full restart.\r\n const restartTrigger = this.findRestartTrigger(batch);\r\n\r\n if (restartTrigger) {\r\n // On success this never comes back — the process exits and the\r\n // supervisor replaces it. Reaching the next line means the restart was\r\n // declined by config or wasn't possible, so we fall through and still\r\n // hot-reload whatever ordinary code shared the batch.\r\n await this.restartForConfigChange(restartTrigger);\r\n }\r\n\r\n // No-op saves (an editor that fsyncs without a content write) are already\r\n // filtered upstream by the file hash in FileEventHandler, so batch.changed\r\n // only contains genuinely-changed paths by the time it reaches here.\r\n\r\n const total = batch.added.length + batch.changed.length + batch.deleted.length;\r\n if (total === 0) return;\r\n\r\n // Boot-time files still ride along in `changed` when a restart was\r\n // declined (`restartOnConfigChange: false`) or wasn't possible — they are\r\n // never hot-reloaded, so keep them out of the reload set either way.\r\n const codeFiles = [...batch.added, ...batch.changed].filter(\r\n (p) => !isEnvPath(p) && p !== \"warlock.config.ts\",\r\n );\r\n\r\n try {\r\n await this.layerExecutor.executeBatchReload(\r\n codeFiles,\r\n filesOrchestrator.getFiles(),\r\n batch.deleted,\r\n batch.changed,\r\n batch.timings,\r\n );\r\n\r\n typeGenerator.executeTypingsGenerator([...batch.added, ...batch.changed, ...batch.deleted]);\r\n\r\n filesOrchestrator.checkHealth(batch);\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to execute batch reload: ${error}`));\r\n }\r\n }\r\n\r\n /**\r\n * The boot-time file in this batch that can only be applied by restarting,\r\n * or `undefined` when the batch is ordinary application code.\r\n */\r\n private findRestartTrigger(batch: Batch): string | undefined {\r\n if (batch.changed.includes(\"warlock.config.ts\")) {\r\n return \"warlock.config.ts\";\r\n }\r\n\r\n return [...batch.added, ...batch.changed].find(isEnvPath);\r\n }\r\n\r\n /**\r\n * Restart so a changed boot-time file actually takes effect.\r\n *\r\n * Opt out with `devServer.restartOnConfigChange: false` and the old\r\n * behaviour returns: a warning telling you to restart yourself. If the\r\n * restart can't be performed at all (no supervisor, a shutdown that threw),\r\n * `restartDevServer` reports it and we fall back to the same warning rather\r\n * than leaving the server running against config it isn't using.\r\n */\r\n private async restartForConfigChange(file: string): Promise<void> {\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n\r\n if (devServerConfig?.restartOnConfigChange === false) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n return;\r\n }\r\n\r\n devLogInfo(`${file} changed — restarting to apply.`);\r\n\r\n const restarted = await restartDevServer(this);\r\n\r\n if (!restarted) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n }\r\n }\r\n\r\n public async shutdown(): Promise<void> {\r\n // Always hand the terminal back, even on a repeat/no-op shutdown — a\r\n // process that exits while stdin is still in raw mode leaves the user's\r\n // shell without line editing or Ctrl+C.\r\n devServerShortcuts.release();\r\n\r\n if (!this.running) return;\r\n devServeLog(colors.redBright(\"Shutting down Development Server...\"));\r\n this.running = false;\r\n await connectorsManager.shutdown();\r\n devServeLog(colors.greenBright(\"Development Server stopped\"));\r\n }\r\n\r\n public isRunning(): boolean {\r\n return this.running;\r\n }\r\n}\r\n\r\nfunction isEnvPath(path: string): boolean {\r\n const basename = path.split(\"/\").pop() ?? path;\r\n return basename === \".env\" || basename.startsWith(\".env.\");\r\n}\r\n"],"mappings":"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"}
|
|
@@ -20,6 +20,10 @@ var TypescriptHealthChecker = class extends BaseHealthChecker {
|
|
|
20
20
|
const ext = filePath.toLowerCase();
|
|
21
21
|
return ext.endsWith(".ts") || ext.endsWith(".tsx");
|
|
22
22
|
}
|
|
23
|
+
/** Preserve files selected by tsconfig when source updates rebuild the program. */
|
|
24
|
+
getProgramRootNames(files) {
|
|
25
|
+
return [...new Set([...this.parsedConfig?.fileNames || [], ...files.map((file) => file.absolutePath)])];
|
|
26
|
+
}
|
|
23
27
|
/**
|
|
24
28
|
* Extract line and column from diagnostic location
|
|
25
29
|
*/
|
|
@@ -108,7 +112,7 @@ var TypescriptHealthChecker = class extends BaseHealthChecker {
|
|
|
108
112
|
*/
|
|
109
113
|
async onFileChanges(files) {
|
|
110
114
|
if (!this.parsedConfig) return;
|
|
111
|
-
this.program = ts.createProgram(
|
|
115
|
+
this.program = ts.createProgram(this.getProgramRootNames(files), {
|
|
112
116
|
...this.parsedConfig.options,
|
|
113
117
|
incremental: true
|
|
114
118
|
}, void 0, this.program);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript-health-checker.mjs","names":[],"sources":["../../../../../../../../../core/src/dev-server/health-checker/checkers/typescript-health-checker.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport ts from \"typescript\";\nimport type { FileManager } from \"../../file-manager\";\nimport { tsconfigManager } from \"../../tsconfig-manager\";\nimport type { FileHealthCheckerContract } from \"../file-health-checker.contract\";\nimport { FileHealthResult } from \"../file-health-result\";\nimport { BaseHealthChecker } from \"./base-health-checker\";\n\nexport class TypescriptHealthChecker\n extends BaseHealthChecker\n implements FileHealthCheckerContract\n{\n /**\n * Cached TypeScript program instance\n */\n private program: ts.Program | null = null;\n\n /**\n * Cached parsed TypeScript configuration\n */\n private parsedConfig: ts.ParsedCommandLine | null = null;\n\n /**\n * Health checker name\n */\n public name: string = \"TypeScript\";\n\n /**\n * Path to dedicated worker file for TypeScript checking\n * Runs in a separate thread to avoid blocking the main dev server\n */\n public workerPath: string = \"./workers/ts-health.worker\";\n\n /**\n * Whether checker is initialized\n */\n private initialized: boolean = false;\n\n /**\n * Check if file is a TypeScript file\n */\n private isTypeScriptFile(filePath: string): boolean {\n const ext = filePath.toLowerCase();\n return ext.endsWith(\".ts\") || ext.endsWith(\".tsx\");\n }\n\n /**\n * Extract line and column from diagnostic location\n */\n private getDiagnosticLocation(diagnostic: ts.Diagnostic): {\n lineNumber: number;\n columnNumber: number;\n } {\n if (diagnostic.file && diagnostic.start !== undefined) {\n const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);\n return {\n lineNumber: line + 1, // TypeScript uses 0-based, we use 1-based\n columnNumber: character + 1,\n };\n }\n return {\n lineNumber: 1,\n columnNumber: 1,\n };\n }\n\n /**\n * Format diagnostic message\n */\n private formatDiagnosticMessage(diagnostic: ts.Diagnostic): string {\n if (diagnostic.file && diagnostic.start !== undefined) {\n return ts.formatDiagnostic(diagnostic, {\n getCurrentDirectory: () => process.cwd(),\n getCanonicalFileName: (fileName) => fileName,\n getNewLine: () => \"\\n\",\n });\n }\n return diagnostic.messageText.toString();\n }\n\n /**\n * Display health check results in a pretty format\n */\n private displayResults(file: FileManager, result: FileHealthResult): void {\n const stats = result.getStats();\n\n // Only display if there are errors or warnings\n if (stats.errors === 0 && stats.warnings === 0) {\n return;\n }\n\n const fileName = file.relativePath.replace(/\\\\/g, \"/\");\n const errorCount = stats.errors;\n const warningCount = stats.warnings;\n const sourceLines = file.source ? file.source.split(\"\\n\") : [];\n\n // Display header\n // console.log(\n // `\\n${colors.dim(\"╭─\")} ${colors.bold(colors.cyanBright(\"TypeScript Health\"))} ${colors.dim(\"→\")} ${colors.cyan(fileName)}`,\n // );\n\n // Display errors\n if (errorCount > 0) {\n const errorMessages = result.messages.filter((m) => m.type === \"error\");\n for (const error of errorMessages) {\n const icon = colors.redBright(\"✖\");\n const level = colors.redBright(colors.bold(\"ERROR\"));\n console.log(\n `\\n${icon} ${level} ${colors.dim(\"in\")} ${colors.cyanBright(fileName)}${colors.dim(`(${error.lineNumber},${error.columnNumber})`)}`,\n );\n // Extract just the message text (remove file path prefix if present)\n const messageLines = error.message.split(\"\\n\");\n const cleanMessage = messageLines\n .map((line) => line.replace(/^[^:]+:\\d+:\\d+ - /, \"\").trim())\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n console.log(` ${colors.dim(\"→\")} ${colors.red(cleanMessage)}`);\n\n // Display code line with underline indicator\n if (\n sourceLines.length > 0 &&\n error.lineNumber > 0 &&\n error.lineNumber <= sourceLines.length\n ) {\n const lineIndex = error.lineNumber - 1; // Convert to 0-based index\n const lineContent = sourceLines[lineIndex];\n const lineNum = error.lineNumber.toString().padStart(4, \" \");\n const errorLength = error.length || 1;\n const columnIndex = error.columnNumber - 1; // Convert to 0-based index\n\n // Display the line with normal color (not all red)\n console.log(` ${colors.dim(lineNum)} ${colors.dim(\"│\")} ${lineContent || \"\"}`);\n\n // Display underline indicator\n // Padding accounts for: lineNum.length + 1 space + 1 (│) + 1 space = lineNum.length + 3\n // (The initial 2 spaces indent is already in the console.log)\n const prefixPadding = lineNum.length + 3;\n const columnPadding = \" \".repeat(columnIndex);\n const underline = colors.redBright(\"~\".repeat(Math.max(1, errorLength)));\n console.log(` ${colors.dim(\" \".repeat(prefixPadding))}${columnPadding}${underline}`);\n }\n }\n }\n\n // Display warnings\n if (warningCount > 0) {\n const warningMessages = result.messages.filter((m) => m.type === \"warning\");\n for (const warning of warningMessages) {\n const icon = colors.yellowBright(\"⚠\");\n const level = colors.yellowBright(colors.bold(\"WARNING\"));\n console.log(\n `\\n${icon} ${level} ${colors.dim(\"in\")} ${colors.cyanBright(fileName)}${colors.dim(`(${warning.lineNumber},${warning.columnNumber})`)}`,\n );\n // Extract just the message text (remove file path prefix if present)\n const messageLines = warning.message.split(\"\\n\");\n const cleanMessage = messageLines\n .map((line) => line.replace(/^[^:]+:\\d+:\\d+ - /, \"\").trim())\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n console.log(` ${colors.dim(\"→\")} ${colors.yellow(cleanMessage)}`);\n\n // Display code line with underline indicator\n if (\n sourceLines.length > 0 &&\n warning.lineNumber > 0 &&\n warning.lineNumber <= sourceLines.length\n ) {\n const lineIndex = warning.lineNumber - 1; // Convert to 0-based index\n const lineContent = sourceLines[lineIndex];\n const lineNum = warning.lineNumber.toString().padStart(4, \" \");\n const warningLength = warning.length || 1;\n const columnIndex = warning.columnNumber - 1; // Convert to 0-based index\n\n // Display the line with normal color (not all yellow)\n console.log(` ${colors.dim(lineNum)} ${colors.dim(\"│\")} ${lineContent || \"\"}`);\n\n // Display underline indicator\n // Padding accounts for: lineNum.length + 1 space + 1 (│) + 1 space = lineNum.length + 3\n // (The initial 2 spaces indent is already in the console.log)\n const prefixPadding = lineNum.length + 3;\n const columnPadding = \" \".repeat(columnIndex);\n const underline = colors.yellowBright(\"~\".repeat(Math.max(1, warningLength)));\n console.log(` ${colors.dim(\" \".repeat(prefixPadding))}${columnPadding}${underline}`);\n }\n }\n }\n\n // Display summary\n const summary = [];\n if (errorCount > 0) {\n summary.push(colors.red(`${errorCount} error${errorCount > 1 ? \"s\" : \"\"}`));\n }\n if (warningCount > 0) {\n summary.push(colors.yellow(`${warningCount} warning${warningCount > 1 ? \"s\" : \"\"}`));\n }\n\n // console.log(\n // `\\n${colors.dim(\"╰─\")} ${colors.bold(\"TypeScript\")} ${colors.dim(\"→\")} ${summary.join(colors.dim(\" and \"))}`,\n // );\n }\n\n /**\n * Detect when files are changed\n */\n public async onFileChanges(files: FileManager[]): Promise<void> {\n // recreate an incremental program\n if (!this.parsedConfig) {\n return;\n }\n\n this.program = ts.createProgram(\n files.map((file) => file.absolutePath),\n {\n ...this.parsedConfig.options,\n incremental: true,\n },\n undefined,\n this.program!,\n );\n }\n\n /**\n * Initialize the health checker\n */\n public initialize(): TypescriptHealthChecker {\n try {\n // Verify tsconfigManager is initialized and has config\n if (!tsconfigManager.tsconfig || Object.keys(tsconfigManager.tsconfig).length === 0) {\n this.initialized = true;\n return this;\n }\n\n // Parse the config using tsconfigManager's cached config\n this.parsedConfig = ts.parseJsonConfigFileContent(\n tsconfigManager.tsconfig,\n ts.sys,\n process.cwd(),\n );\n\n // Check for config errors\n if (this.parsedConfig.errors.length > 0) {\n // Log config errors but continue (will skip validation)\n console.warn(\n \"TypeScript Health Checker: tsconfig.json has errors:\",\n this.parsedConfig.errors.map((e) => ts.formatDiagnostic(e, ts.createCompilerHost({}))),\n );\n }\n\n this.program = ts.createProgram(this.parsedConfig.fileNames, this.parsedConfig.options);\n\n this.initialized = true;\n } catch (error) {\n // Handle any errors during initialization gracefully\n console.warn(\"TypeScript Health Checker: Failed to initialize:\", error);\n this.initialized = true; // Mark as initialized to prevent retries\n }\n\n return this;\n }\n\n /**\n * Validate the health of the file\n */\n public async validate(file: FileManager, result: FileHealthResult): Promise<FileHealthResult> {\n // Early exit: skip non-TypeScript files\n if (!this.isTypeScriptFile(file.absolutePath)) {\n result.markAsHealthy();\n return result;\n }\n\n // Early exit: check if parsed config is available\n if (!this.parsedConfig) {\n result.markAsHealthy();\n return result;\n }\n\n try {\n // Lazy-load program on first validation if not already created\n if (!this.program) {\n // Create program with all project files from parsed config (for proper type checking)\n this.program = ts.createProgram(this.parsedConfig.fileNames, this.parsedConfig.options);\n }\n\n // Get source file from program\n const sourceFile = this.program.getSourceFile(file.absolutePath);\n\n // If file is not in the program, return healthy (might be excluded)\n if (!sourceFile) {\n result.markAsHealthy();\n return result;\n }\n\n // Get diagnostics for the specific file only\n const syntacticDiagnostics = this.program.getSyntacticDiagnostics(sourceFile);\n const semanticDiagnostics = this.program.getSemanticDiagnostics(sourceFile);\n\n // Combine all diagnostics\n const allDiagnostics = [...syntacticDiagnostics, ...semanticDiagnostics];\n\n // Convert TypeScript diagnostics to FileHealthResult format\n const errors: Array<{\n message: string;\n type: \"error\";\n lineNumber: number;\n columnNumber: number;\n length: number;\n }> = [];\n const warnings: Array<{\n message: string;\n type: \"warning\";\n lineNumber: number;\n columnNumber: number;\n length: number;\n }> = [];\n\n for (const diagnostic of allDiagnostics) {\n const location = this.getDiagnosticLocation(diagnostic);\n const message = this.formatDiagnosticMessage(diagnostic);\n const errorLength = diagnostic.length || 1;\n\n if (diagnostic.category === ts.DiagnosticCategory.Error) {\n errors.push({\n message,\n type: \"error\",\n lineNumber: location.lineNumber,\n columnNumber: location.columnNumber,\n length: errorLength,\n });\n } else if (diagnostic.category === ts.DiagnosticCategory.Warning) {\n warnings.push({\n message,\n type: \"warning\",\n lineNumber: location.lineNumber,\n columnNumber: location.columnNumber,\n length: errorLength,\n });\n }\n }\n\n // Add errors and warnings to result\n if (errors.length > 0) {\n result.addErrors(errors);\n }\n if (warnings.length > 0) {\n result.addWarnings(warnings);\n }\n\n // If no errors or warnings, mark as healthy\n if (errors.length === 0 && warnings.length === 0) {\n result.markAsHealthy();\n } else {\n // Display results if there are errors or warnings\n this.displayResults(file, result);\n }\n } catch (error) {\n // Handle any errors during validation gracefully\n console.warn(`TypeScript Health Checker: Error validating file ${file.relativePath}:`, error);\n result.markAsHealthy(); // Return healthy on error to avoid blocking\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typescript-health-checker.mjs","names":[],"sources":["../../../../../../../../../core/src/dev-server/health-checker/checkers/typescript-health-checker.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport ts from \"typescript\";\nimport type { FileManager } from \"../../file-manager\";\nimport { tsconfigManager } from \"../../tsconfig-manager\";\nimport type { FileHealthCheckerContract } from \"../file-health-checker.contract\";\nimport { FileHealthResult } from \"../file-health-result\";\nimport { BaseHealthChecker } from \"./base-health-checker\";\n\nexport class TypescriptHealthChecker\n extends BaseHealthChecker\n implements FileHealthCheckerContract\n{\n /**\n * Cached TypeScript program instance\n */\n private program: ts.Program | null = null;\n\n /**\n * Cached parsed TypeScript configuration\n */\n private parsedConfig: ts.ParsedCommandLine | null = null;\n\n /**\n * Health checker name\n */\n public name: string = \"TypeScript\";\n\n /**\n * Path to dedicated worker file for TypeScript checking\n * Runs in a separate thread to avoid blocking the main dev server\n */\n public workerPath: string = \"./workers/ts-health.worker\";\n\n /**\n * Whether checker is initialized\n */\n private initialized: boolean = false;\n\n /**\n * Check if file is a TypeScript file\n */\n private isTypeScriptFile(filePath: string): boolean {\n const ext = filePath.toLowerCase();\n return ext.endsWith(\".ts\") || ext.endsWith(\".tsx\");\n }\n\n /** Preserve files selected by tsconfig when source updates rebuild the program. */\n private getProgramRootNames(files: FileManager[]): string[] {\n return [\n ...new Set([\n ...(this.parsedConfig?.fileNames || []),\n ...files.map((file) => file.absolutePath),\n ]),\n ];\n }\n\n /**\n * Extract line and column from diagnostic location\n */\n private getDiagnosticLocation(diagnostic: ts.Diagnostic): {\n lineNumber: number;\n columnNumber: number;\n } {\n if (diagnostic.file && diagnostic.start !== undefined) {\n const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);\n return {\n lineNumber: line + 1, // TypeScript uses 0-based, we use 1-based\n columnNumber: character + 1,\n };\n }\n return {\n lineNumber: 1,\n columnNumber: 1,\n };\n }\n\n /**\n * Format diagnostic message\n */\n private formatDiagnosticMessage(diagnostic: ts.Diagnostic): string {\n if (diagnostic.file && diagnostic.start !== undefined) {\n return ts.formatDiagnostic(diagnostic, {\n getCurrentDirectory: () => process.cwd(),\n getCanonicalFileName: (fileName) => fileName,\n getNewLine: () => \"\\n\",\n });\n }\n return diagnostic.messageText.toString();\n }\n\n /**\n * Display health check results in a pretty format\n */\n private displayResults(file: FileManager, result: FileHealthResult): void {\n const stats = result.getStats();\n\n // Only display if there are errors or warnings\n if (stats.errors === 0 && stats.warnings === 0) {\n return;\n }\n\n const fileName = file.relativePath.replace(/\\\\/g, \"/\");\n const errorCount = stats.errors;\n const warningCount = stats.warnings;\n const sourceLines = file.source ? file.source.split(\"\\n\") : [];\n\n // Display header\n // console.log(\n // `\\n${colors.dim(\"╭─\")} ${colors.bold(colors.cyanBright(\"TypeScript Health\"))} ${colors.dim(\"→\")} ${colors.cyan(fileName)}`,\n // );\n\n // Display errors\n if (errorCount > 0) {\n const errorMessages = result.messages.filter((m) => m.type === \"error\");\n for (const error of errorMessages) {\n const icon = colors.redBright(\"✖\");\n const level = colors.redBright(colors.bold(\"ERROR\"));\n console.log(\n `\\n${icon} ${level} ${colors.dim(\"in\")} ${colors.cyanBright(fileName)}${colors.dim(`(${error.lineNumber},${error.columnNumber})`)}`,\n );\n // Extract just the message text (remove file path prefix if present)\n const messageLines = error.message.split(\"\\n\");\n const cleanMessage = messageLines\n .map((line) => line.replace(/^[^:]+:\\d+:\\d+ - /, \"\").trim())\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n console.log(` ${colors.dim(\"→\")} ${colors.red(cleanMessage)}`);\n\n // Display code line with underline indicator\n if (\n sourceLines.length > 0 &&\n error.lineNumber > 0 &&\n error.lineNumber <= sourceLines.length\n ) {\n const lineIndex = error.lineNumber - 1; // Convert to 0-based index\n const lineContent = sourceLines[lineIndex];\n const lineNum = error.lineNumber.toString().padStart(4, \" \");\n const errorLength = error.length || 1;\n const columnIndex = error.columnNumber - 1; // Convert to 0-based index\n\n // Display the line with normal color (not all red)\n console.log(` ${colors.dim(lineNum)} ${colors.dim(\"│\")} ${lineContent || \"\"}`);\n\n // Display underline indicator\n // Padding accounts for: lineNum.length + 1 space + 1 (│) + 1 space = lineNum.length + 3\n // (The initial 2 spaces indent is already in the console.log)\n const prefixPadding = lineNum.length + 3;\n const columnPadding = \" \".repeat(columnIndex);\n const underline = colors.redBright(\"~\".repeat(Math.max(1, errorLength)));\n console.log(` ${colors.dim(\" \".repeat(prefixPadding))}${columnPadding}${underline}`);\n }\n }\n }\n\n // Display warnings\n if (warningCount > 0) {\n const warningMessages = result.messages.filter((m) => m.type === \"warning\");\n for (const warning of warningMessages) {\n const icon = colors.yellowBright(\"⚠\");\n const level = colors.yellowBright(colors.bold(\"WARNING\"));\n console.log(\n `\\n${icon} ${level} ${colors.dim(\"in\")} ${colors.cyanBright(fileName)}${colors.dim(`(${warning.lineNumber},${warning.columnNumber})`)}`,\n );\n // Extract just the message text (remove file path prefix if present)\n const messageLines = warning.message.split(\"\\n\");\n const cleanMessage = messageLines\n .map((line) => line.replace(/^[^:]+:\\d+:\\d+ - /, \"\").trim())\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n console.log(` ${colors.dim(\"→\")} ${colors.yellow(cleanMessage)}`);\n\n // Display code line with underline indicator\n if (\n sourceLines.length > 0 &&\n warning.lineNumber > 0 &&\n warning.lineNumber <= sourceLines.length\n ) {\n const lineIndex = warning.lineNumber - 1; // Convert to 0-based index\n const lineContent = sourceLines[lineIndex];\n const lineNum = warning.lineNumber.toString().padStart(4, \" \");\n const warningLength = warning.length || 1;\n const columnIndex = warning.columnNumber - 1; // Convert to 0-based index\n\n // Display the line with normal color (not all yellow)\n console.log(` ${colors.dim(lineNum)} ${colors.dim(\"│\")} ${lineContent || \"\"}`);\n\n // Display underline indicator\n // Padding accounts for: lineNum.length + 1 space + 1 (│) + 1 space = lineNum.length + 3\n // (The initial 2 spaces indent is already in the console.log)\n const prefixPadding = lineNum.length + 3;\n const columnPadding = \" \".repeat(columnIndex);\n const underline = colors.yellowBright(\"~\".repeat(Math.max(1, warningLength)));\n console.log(` ${colors.dim(\" \".repeat(prefixPadding))}${columnPadding}${underline}`);\n }\n }\n }\n\n // Display summary\n const summary = [];\n if (errorCount > 0) {\n summary.push(colors.red(`${errorCount} error${errorCount > 1 ? \"s\" : \"\"}`));\n }\n if (warningCount > 0) {\n summary.push(colors.yellow(`${warningCount} warning${warningCount > 1 ? \"s\" : \"\"}`));\n }\n\n // console.log(\n // `\\n${colors.dim(\"╰─\")} ${colors.bold(\"TypeScript\")} ${colors.dim(\"→\")} ${summary.join(colors.dim(\" and \"))}`,\n // );\n }\n\n /**\n * Detect when files are changed\n */\n public async onFileChanges(files: FileManager[]): Promise<void> {\n // recreate an incremental program\n if (!this.parsedConfig) {\n return;\n }\n\n this.program = ts.createProgram(\n this.getProgramRootNames(files),\n {\n ...this.parsedConfig.options,\n incremental: true,\n },\n undefined,\n this.program!,\n );\n }\n\n /**\n * Initialize the health checker\n */\n public initialize(): TypescriptHealthChecker {\n try {\n // Verify tsconfigManager is initialized and has config\n if (!tsconfigManager.tsconfig || Object.keys(tsconfigManager.tsconfig).length === 0) {\n this.initialized = true;\n return this;\n }\n\n // Parse the config using tsconfigManager's cached config\n this.parsedConfig = ts.parseJsonConfigFileContent(\n tsconfigManager.tsconfig,\n ts.sys,\n process.cwd(),\n );\n\n // Check for config errors\n if (this.parsedConfig.errors.length > 0) {\n // Log config errors but continue (will skip validation)\n console.warn(\n \"TypeScript Health Checker: tsconfig.json has errors:\",\n this.parsedConfig.errors.map((e) => ts.formatDiagnostic(e, ts.createCompilerHost({}))),\n );\n }\n\n this.program = ts.createProgram(this.parsedConfig.fileNames, this.parsedConfig.options);\n\n this.initialized = true;\n } catch (error) {\n // Handle any errors during initialization gracefully\n console.warn(\"TypeScript Health Checker: Failed to initialize:\", error);\n this.initialized = true; // Mark as initialized to prevent retries\n }\n\n return this;\n }\n\n /**\n * Validate the health of the file\n */\n public async validate(file: FileManager, result: FileHealthResult): Promise<FileHealthResult> {\n // Early exit: skip non-TypeScript files\n if (!this.isTypeScriptFile(file.absolutePath)) {\n result.markAsHealthy();\n return result;\n }\n\n // Early exit: check if parsed config is available\n if (!this.parsedConfig) {\n result.markAsHealthy();\n return result;\n }\n\n try {\n // Lazy-load program on first validation if not already created\n if (!this.program) {\n // Create program with all project files from parsed config (for proper type checking)\n this.program = ts.createProgram(this.parsedConfig.fileNames, this.parsedConfig.options);\n }\n\n // Get source file from program\n const sourceFile = this.program.getSourceFile(file.absolutePath);\n\n // If file is not in the program, return healthy (might be excluded)\n if (!sourceFile) {\n result.markAsHealthy();\n return result;\n }\n\n // Get diagnostics for the specific file only\n const syntacticDiagnostics = this.program.getSyntacticDiagnostics(sourceFile);\n const semanticDiagnostics = this.program.getSemanticDiagnostics(sourceFile);\n\n // Combine all diagnostics\n const allDiagnostics = [...syntacticDiagnostics, ...semanticDiagnostics];\n\n // Convert TypeScript diagnostics to FileHealthResult format\n const errors: Array<{\n message: string;\n type: \"error\";\n lineNumber: number;\n columnNumber: number;\n length: number;\n }> = [];\n const warnings: Array<{\n message: string;\n type: \"warning\";\n lineNumber: number;\n columnNumber: number;\n length: number;\n }> = [];\n\n for (const diagnostic of allDiagnostics) {\n const location = this.getDiagnosticLocation(diagnostic);\n const message = this.formatDiagnosticMessage(diagnostic);\n const errorLength = diagnostic.length || 1;\n\n if (diagnostic.category === ts.DiagnosticCategory.Error) {\n errors.push({\n message,\n type: \"error\",\n lineNumber: location.lineNumber,\n columnNumber: location.columnNumber,\n length: errorLength,\n });\n } else if (diagnostic.category === ts.DiagnosticCategory.Warning) {\n warnings.push({\n message,\n type: \"warning\",\n lineNumber: location.lineNumber,\n columnNumber: location.columnNumber,\n length: errorLength,\n });\n }\n }\n\n // Add errors and warnings to result\n if (errors.length > 0) {\n result.addErrors(errors);\n }\n if (warnings.length > 0) {\n result.addWarnings(warnings);\n }\n\n // If no errors or warnings, mark as healthy\n if (errors.length === 0 && warnings.length === 0) {\n result.markAsHealthy();\n } else {\n // Display results if there are errors or warnings\n this.displayResults(file, result);\n }\n } catch (error) {\n // Handle any errors during validation gracefully\n console.warn(`TypeScript Health Checker: Error validating file ${file.relativePath}:`, error);\n result.markAsHealthy(); // Return healthy on error to avoid blocking\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import ts from "typescript";
|
|
2
3
|
import { parentPort, workerData } from "worker_threads";
|
|
3
4
|
|
|
@@ -22,6 +23,7 @@ var TypeScriptHealthWorker = class {
|
|
|
22
23
|
this.program = null;
|
|
23
24
|
this.parsedConfig = null;
|
|
24
25
|
this.fileContents = /* @__PURE__ */ new Map();
|
|
26
|
+
this.deletedRootPaths = /* @__PURE__ */ new Set();
|
|
25
27
|
this.initialized = false;
|
|
26
28
|
this.cwd = process.cwd();
|
|
27
29
|
}
|
|
@@ -63,8 +65,11 @@ var TypeScriptHealthWorker = class {
|
|
|
63
65
|
errors: [],
|
|
64
66
|
warnings: []
|
|
65
67
|
}));
|
|
66
|
-
for (const file of files)
|
|
67
|
-
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
this.fileContents.set(file.path, file.content);
|
|
70
|
+
this.deletedRootPaths.delete(this.getPathIdentity(file.path));
|
|
71
|
+
}
|
|
72
|
+
this.program = ts.createProgram(this.getProgramRootNames(), this.parsedConfig.options, this.createCompilerHost(), this.program || void 0);
|
|
68
73
|
const results = [];
|
|
69
74
|
for (const file of files) {
|
|
70
75
|
const result = this.checkSingleFile(file);
|
|
@@ -76,19 +81,28 @@ var TypeScriptHealthWorker = class {
|
|
|
76
81
|
* Handle file changes (update program incrementally)
|
|
77
82
|
*/
|
|
78
83
|
handleFileChanges(files) {
|
|
79
|
-
for (const file of files)
|
|
80
|
-
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
this.fileContents.set(file.path, file.content);
|
|
86
|
+
this.deletedRootPaths.delete(this.getPathIdentity(file.path));
|
|
87
|
+
}
|
|
88
|
+
if (this.parsedConfig && this.program) this.program = ts.createProgram(this.getProgramRootNames(), this.parsedConfig.options, this.createCompilerHost(), this.program || void 0);
|
|
81
89
|
}
|
|
82
90
|
/**
|
|
83
91
|
* Handle deleted files (remove from cache)
|
|
84
92
|
*/
|
|
85
93
|
handleFilesDeleted(files) {
|
|
86
94
|
let hasChanges = false;
|
|
87
|
-
for (const file of files)
|
|
88
|
-
this.
|
|
89
|
-
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
const identity = this.getPathIdentity(file.path);
|
|
97
|
+
const cachedPath = Array.from(this.fileContents.keys()).find((candidate) => this.getPathIdentity(candidate) === identity);
|
|
98
|
+
if (cachedPath) {
|
|
99
|
+
this.fileContents.delete(cachedPath);
|
|
100
|
+
hasChanges = true;
|
|
101
|
+
}
|
|
102
|
+
if (!this.deletedRootPaths.has(identity)) hasChanges = true;
|
|
103
|
+
this.deletedRootPaths.add(identity);
|
|
90
104
|
}
|
|
91
|
-
if (hasChanges && this.parsedConfig) this.program = ts.createProgram(
|
|
105
|
+
if (hasChanges && this.parsedConfig) this.program = ts.createProgram(this.getProgramRootNames(), this.parsedConfig.options, this.createCompilerHost(), this.program || void 0);
|
|
92
106
|
}
|
|
93
107
|
/**
|
|
94
108
|
* Check a single file for diagnostics
|
|
@@ -168,6 +182,21 @@ var TypeScriptHealthWorker = class {
|
|
|
168
182
|
}
|
|
169
183
|
};
|
|
170
184
|
}
|
|
185
|
+
/** Keep tsconfig-selected declarations in every incremental program. */
|
|
186
|
+
getProgramRootNames() {
|
|
187
|
+
const configuredRoots = this.parsedConfig?.fileNames || [];
|
|
188
|
+
const rootsByIdentity = /* @__PURE__ */ new Map();
|
|
189
|
+
for (const filePath of [...configuredRoots, ...this.fileContents.keys()]) {
|
|
190
|
+
const identity = this.getPathIdentity(filePath);
|
|
191
|
+
if (!this.deletedRootPaths.has(identity)) rootsByIdentity.set(identity, filePath);
|
|
192
|
+
}
|
|
193
|
+
return [...rootsByIdentity.values()];
|
|
194
|
+
}
|
|
195
|
+
/** Compare equivalent Windows path spellings as one TypeScript root. */
|
|
196
|
+
getPathIdentity(filePath) {
|
|
197
|
+
const normalized = path.normalize(path.resolve(filePath));
|
|
198
|
+
return ts.sys.useCaseSensitiveFileNames ? normalized : normalized.toLowerCase();
|
|
199
|
+
}
|
|
171
200
|
};
|
|
172
201
|
const worker = new TypeScriptHealthWorker();
|
|
173
202
|
parentPort?.on("message", (message) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts-health.worker.mjs","names":[],"sources":["../../../../../../../../../core/src/dev-server/health-checker/workers/ts-health.worker.ts"],"sourcesContent":["/**\n * TypeScript Health Check Worker\n *\n * This worker runs in a dedicated thread to perform TypeScript type checking\n * without blocking the main dev server thread. It maintains a persistent\n * ts.Program instance for fast incremental type checking.\n *\n * Communication:\n * - Receives: { type: 'init' | 'check' | 'fileChanges' | 'shutdown', ... }\n * - Sends: { type: 'results' | 'initialized' | 'error', ... }\n */\nimport ts from \"typescript\";\nimport { parentPort, workerData } from \"worker_threads\";\n\n/**\n * Serialized file data received from main thread\n */\ntype SerializedFile = {\n /** Absolute file path */\n path: string;\n /** File content (source code) */\n content: string;\n /** Relative path for display */\n relativePath: string;\n};\n\n/**\n * Diagnostic message sent back to main thread\n */\ntype DiagnosticMessage = {\n type: \"error\" | \"warning\";\n message: string;\n lineNumber: number;\n columnNumber: number;\n length: number;\n filePath: string;\n relativePath: string;\n};\n\n/**\n * Health check result for a single file\n */\ntype FileCheckResult = {\n path: string;\n relativePath: string;\n healthy: boolean;\n errors: DiagnosticMessage[];\n warnings: DiagnosticMessage[];\n};\n\n/**\n * Messages received from main thread\n */\n/**\n * Deleted file info\n */\ntype DeletedFile = {\n path: string;\n relativePath: string;\n};\n\n/**\n * Messages received from main thread\n */\ntype WorkerMessage =\n | { type: \"init\"; config: { cwd: string; tsconfigPath?: string } }\n | { type: \"check\"; files: SerializedFile[] }\n | { type: \"fileChanges\"; files: SerializedFile[] }\n | { type: \"filesDeleted\"; files: DeletedFile[] }\n | { type: \"shutdown\" };\n\n/**\n * Messages sent to main thread\n */\ntype WorkerResponse =\n | { type: \"initialized\"; success: boolean; error?: string }\n | { type: \"results\"; results: FileCheckResult[] }\n | { type: \"error\"; message: string };\n\n/**\n * TypeScript Health Worker class\n * Maintains persistent ts.Program for incremental type checking\n */\nclass TypeScriptHealthWorker {\n /**\n * Cached TypeScript program instance\n */\n private program: ts.Program | null = null;\n\n /**\n * Parsed TypeScript configuration\n */\n private parsedConfig: ts.ParsedCommandLine | null = null;\n\n /**\n * In-memory file contents cache\n */\n private fileContents = new Map<string, string>();\n\n /**\n * Whether the worker is initialized\n */\n private initialized = false;\n\n /**\n * Current working directory\n */\n private cwd: string = process.cwd();\n\n /**\n * Initialize the worker with TypeScript configuration\n */\n public initialize(config: { cwd: string; tsconfigPath?: string }): boolean {\n try {\n this.cwd = config.cwd || process.cwd();\n\n // Try to load tsconfig.json\n const tsconfigPath = config.tsconfigPath || ts.findConfigFile(this.cwd, ts.sys.fileExists);\n\n if (!tsconfigPath) {\n this.initialized = true;\n return true; // No tsconfig, will skip type checking\n }\n\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\n if (configFile.error) {\n console.warn(\"TypeScript Worker: Error reading tsconfig:\", configFile.error);\n this.initialized = true;\n return true;\n }\n\n this.parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, this.cwd);\n\n if (this.parsedConfig.errors.length > 0) {\n console.warn(\"TypeScript Worker: tsconfig has errors:\", this.parsedConfig.errors);\n }\n\n this.initialized = true;\n return true;\n } catch (error) {\n console.error(\"TypeScript Worker: Failed to initialize:\", error);\n this.initialized = true;\n return false;\n }\n }\n\n /**\n * Check files for TypeScript errors\n */\n public checkFiles(files: SerializedFile[]): FileCheckResult[] {\n if (!this.parsedConfig) {\n // No config, return all files as healthy\n return files.map((file) => ({\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n }));\n }\n\n // Update in-memory file cache\n for (const file of files) {\n this.fileContents.set(file.path, file.content);\n }\n\n // Create or update the program (incremental)\n this.program = ts.createProgram(\n Array.from(this.fileContents.keys()),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined, // Pass old program for incremental compilation\n );\n\n // Check each file\n const results: FileCheckResult[] = [];\n\n for (const file of files) {\n const result = this.checkSingleFile(file);\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Handle file changes (update program incrementally)\n */\n public handleFileChanges(files: SerializedFile[]): void {\n for (const file of files) {\n this.fileContents.set(file.path, file.content);\n }\n\n // Recreate program with new file contents\n if (this.parsedConfig && this.program) {\n this.program = ts.createProgram(\n Array.from(this.fileContents.keys()),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined,\n );\n }\n }\n\n /**\n * Handle deleted files (remove from cache)\n */\n public handleFilesDeleted(files: DeletedFile[]): void {\n let hasChanges = false;\n\n for (const file of files) {\n if (this.fileContents.has(file.path)) {\n this.fileContents.delete(file.path);\n hasChanges = true;\n }\n }\n\n // Recreate program without deleted files\n if (hasChanges && this.parsedConfig) {\n this.program = ts.createProgram(\n Array.from(this.fileContents.keys()),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined,\n );\n }\n }\n\n /**\n * Check a single file for diagnostics\n */\n private checkSingleFile(file: SerializedFile): FileCheckResult {\n if (!this.program) {\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n };\n }\n\n const sourceFile = this.program.getSourceFile(file.path);\n\n if (!sourceFile) {\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n };\n }\n\n const syntacticDiagnostics = this.program.getSyntacticDiagnostics(sourceFile);\n const semanticDiagnostics = this.program.getSemanticDiagnostics(sourceFile);\n const allDiagnostics = [...syntacticDiagnostics, ...semanticDiagnostics];\n\n const errors: DiagnosticMessage[] = [];\n const warnings: DiagnosticMessage[] = [];\n\n for (const diagnostic of allDiagnostics) {\n const message = this.formatDiagnostic(diagnostic, file);\n\n if (diagnostic.category === ts.DiagnosticCategory.Error) {\n errors.push(message);\n } else if (diagnostic.category === ts.DiagnosticCategory.Warning) {\n warnings.push(message);\n }\n }\n\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: errors.length === 0 && warnings.length === 0,\n errors,\n warnings,\n };\n }\n\n /**\n * Format a TypeScript diagnostic into our message format\n */\n private formatDiagnostic(diagnostic: ts.Diagnostic, file: SerializedFile): DiagnosticMessage {\n let lineNumber = 1;\n let columnNumber = 1;\n let length = 1;\n\n if (diagnostic.file && diagnostic.start !== undefined) {\n const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);\n lineNumber = pos.line + 1;\n columnNumber = pos.character + 1;\n length = diagnostic.length || 1;\n }\n\n const messageText =\n typeof diagnostic.messageText === \"string\"\n ? diagnostic.messageText\n : diagnostic.messageText.messageText;\n\n return {\n type: diagnostic.category === ts.DiagnosticCategory.Error ? \"error\" : \"warning\",\n message: messageText,\n lineNumber,\n columnNumber,\n length,\n filePath: file.path,\n relativePath: file.relativePath,\n };\n }\n\n /**\n * Create a custom compiler host that reads from in-memory cache\n */\n private createCompilerHost(): ts.CompilerHost {\n const defaultHost = ts.createCompilerHost(this.parsedConfig?.options || {});\n\n return {\n ...defaultHost,\n readFile: (fileName: string) => {\n // Check in-memory cache first\n if (this.fileContents.has(fileName)) {\n return this.fileContents.get(fileName);\n }\n // Fall back to disk\n return defaultHost.readFile(fileName);\n },\n fileExists: (fileName: string) => {\n if (this.fileContents.has(fileName)) {\n return true;\n }\n return defaultHost.fileExists(fileName);\n },\n };\n }\n}\n\n// Create worker instance\nconst worker = new TypeScriptHealthWorker();\n\n// Handle messages from main thread\nparentPort?.on(\"message\", (message: WorkerMessage) => {\n try {\n switch (message.type) {\n case \"init\": {\n const success = worker.initialize(message.config);\n const response: WorkerResponse = { type: \"initialized\", success };\n parentPort?.postMessage(response);\n break;\n }\n\n case \"check\": {\n const results = worker.checkFiles(message.files);\n const response: WorkerResponse = { type: \"results\", results };\n parentPort?.postMessage(response);\n break;\n }\n\n case \"fileChanges\": {\n worker.handleFileChanges(message.files);\n break;\n }\n\n case \"filesDeleted\": {\n worker.handleFilesDeleted(message.files);\n break;\n }\n\n case \"shutdown\": {\n process.exit(0);\n }\n }\n } catch (error) {\n const response: WorkerResponse = {\n type: \"error\",\n message: error instanceof Error ? error.message : String(error),\n };\n parentPort?.postMessage(response);\n }\n});\n\n// Handle initialization from workerData if provided\nif (workerData?.autoInit) {\n worker.initialize({ cwd: workerData.cwd || process.cwd() });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmFA,IAAM,yBAAN,MAA6B;;iBAIU;sBAKe;sCAK7B,IAAI,IAAoB;qBAKzB;aAKA,QAAQ,IAAI;;;;;CAKlC,AAAO,WAAW,QAAyD;EACzE,IAAI;GACF,KAAK,MAAM,OAAO,OAAO,QAAQ,IAAI;GAGrC,MAAM,eAAe,OAAO,gBAAgB,GAAG,eAAe,KAAK,KAAK,GAAG,IAAI,UAAU;GAEzF,IAAI,CAAC,cAAc;IACjB,KAAK,cAAc;IACnB,OAAO;GACT;GAEA,MAAM,aAAa,GAAG,eAAe,cAAc,GAAG,IAAI,QAAQ;GAElE,IAAI,WAAW,OAAO;IACpB,QAAQ,KAAK,8CAA8C,WAAW,KAAK;IAC3E,KAAK,cAAc;IACnB,OAAO;GACT;GAEA,KAAK,eAAe,GAAG,2BAA2B,WAAW,QAAQ,GAAG,KAAK,KAAK,GAAG;GAErF,IAAI,KAAK,aAAa,OAAO,SAAS,GACpC,QAAQ,KAAK,2CAA2C,KAAK,aAAa,MAAM;GAGlF,KAAK,cAAc;GACnB,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,MAAM,4CAA4C,KAAK;GAC/D,KAAK,cAAc;GACnB,OAAO;EACT;CACF;;;;CAKA,AAAO,WAAW,OAA4C;EAC5D,IAAI,CAAC,KAAK,cAER,OAAO,MAAM,KAAK,UAAU;GAC1B,MAAM,KAAK;GACX,cAAc,KAAK;GACnB,SAAS;GACT,QAAQ,CAAC;GACT,UAAU,CAAC;EACb,EAAE;EAIJ,KAAK,MAAM,QAAQ,OACjB,KAAK,aAAa,IAAI,KAAK,MAAM,KAAK,OAAO;EAI/C,KAAK,UAAU,GAAG,cAChB,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC,GACnC,KAAK,aAAa,SAClB,KAAK,mBAAmB,GACxB,KAAK,WAAW,MAClB;EAGA,MAAM,UAA6B,CAAC;EAEpC,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,KAAK,gBAAgB,IAAI;GACxC,QAAQ,KAAK,MAAM;EACrB;EAEA,OAAO;CACT;;;;CAKA,AAAO,kBAAkB,OAA+B;EACtD,KAAK,MAAM,QAAQ,OACjB,KAAK,aAAa,IAAI,KAAK,MAAM,KAAK,OAAO;EAI/C,IAAI,KAAK,gBAAgB,KAAK,SAC5B,KAAK,UAAU,GAAG,cAChB,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC,GACnC,KAAK,aAAa,SAClB,KAAK,mBAAmB,GACxB,KAAK,WAAW,MAClB;CAEJ;;;;CAKA,AAAO,mBAAmB,OAA4B;EACpD,IAAI,aAAa;EAEjB,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,aAAa,IAAI,KAAK,IAAI,GAAG;GACpC,KAAK,aAAa,OAAO,KAAK,IAAI;GAClC,aAAa;EACf;EAIF,IAAI,cAAc,KAAK,cACrB,KAAK,UAAU,GAAG,cAChB,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC,GACnC,KAAK,aAAa,SAClB,KAAK,mBAAmB,GACxB,KAAK,WAAW,MAClB;CAEJ;;;;CAKA,AAAQ,gBAAgB,MAAuC;EAC7D,IAAI,CAAC,KAAK,SACR,OAAO;GACL,MAAM,KAAK;GACX,cAAc,KAAK;GACnB,SAAS;GACT,QAAQ,CAAC;GACT,UAAU,CAAC;EACb;EAGF,MAAM,aAAa,KAAK,QAAQ,cAAc,KAAK,IAAI;EAEvD,IAAI,CAAC,YACH,OAAO;GACL,MAAM,KAAK;GACX,cAAc,KAAK;GACnB,SAAS;GACT,QAAQ,CAAC;GACT,UAAU,CAAC;EACb;EAGF,MAAM,uBAAuB,KAAK,QAAQ,wBAAwB,UAAU;EAC5E,MAAM,sBAAsB,KAAK,QAAQ,uBAAuB,UAAU;EAC1E,MAAM,iBAAiB,CAAC,GAAG,sBAAsB,GAAG,mBAAmB;EAEvE,MAAM,SAA8B,CAAC;EACrC,MAAM,WAAgC,CAAC;EAEvC,KAAK,MAAM,cAAc,gBAAgB;GACvC,MAAM,UAAU,KAAK,iBAAiB,YAAY,IAAI;GAEtD,IAAI,WAAW,aAAa,GAAG,mBAAmB,OAChD,OAAO,KAAK,OAAO;QACd,IAAI,WAAW,aAAa,GAAG,mBAAmB,SACvD,SAAS,KAAK,OAAO;EAEzB;EAEA,OAAO;GACL,MAAM,KAAK;GACX,cAAc,KAAK;GACnB,SAAS,OAAO,WAAW,KAAK,SAAS,WAAW;GACpD;GACA;EACF;CACF;;;;CAKA,AAAQ,iBAAiB,YAA2B,MAAyC;EAC3F,IAAI,aAAa;EACjB,IAAI,eAAe;EACnB,IAAI,SAAS;EAEb,IAAI,WAAW,QAAQ,WAAW,UAAU,QAAW;GACrD,MAAM,MAAM,WAAW,KAAK,8BAA8B,WAAW,KAAK;GAC1E,aAAa,IAAI,OAAO;GACxB,eAAe,IAAI,YAAY;GAC/B,SAAS,WAAW,UAAU;EAChC;EAEA,MAAM,cACJ,OAAO,WAAW,gBAAgB,WAC9B,WAAW,cACX,WAAW,YAAY;EAE7B,OAAO;GACL,MAAM,WAAW,aAAa,GAAG,mBAAmB,QAAQ,UAAU;GACtE,SAAS;GACT;GACA;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;EACrB;CACF;;;;CAKA,AAAQ,qBAAsC;EAC5C,MAAM,cAAc,GAAG,mBAAmB,KAAK,cAAc,WAAW,CAAC,CAAC;EAE1E,OAAO;GACL,GAAG;GACH,WAAW,aAAqB;IAE9B,IAAI,KAAK,aAAa,IAAI,QAAQ,GAChC,OAAO,KAAK,aAAa,IAAI,QAAQ;IAGvC,OAAO,YAAY,SAAS,QAAQ;GACtC;GACA,aAAa,aAAqB;IAChC,IAAI,KAAK,aAAa,IAAI,QAAQ,GAChC,OAAO;IAET,OAAO,YAAY,WAAW,QAAQ;GACxC;EACF;CACF;AACF;AAGA,MAAM,SAAS,IAAI,uBAAuB;AAG1C,YAAY,GAAG,YAAY,YAA2B;CACpD,IAAI;EACF,QAAQ,QAAQ,MAAhB;GACE,KAAK,QAAQ;IAEX,MAAM,WAA2B;KAAE,MAAM;KAAe,SADxC,OAAO,WAAW,QAAQ,MACoB;IAAE;IAChE,YAAY,YAAY,QAAQ;IAChC;GACF;GAEA,KAAK,SAAS;IAEZ,MAAM,WAA2B;KAAE,MAAM;KAAW,SADpC,OAAO,WAAW,QAAQ,KACgB;IAAE;IAC5D,YAAY,YAAY,QAAQ;IAChC;GACF;GAEA,KAAK;IACH,OAAO,kBAAkB,QAAQ,KAAK;IACtC;GAGF,KAAK;IACH,OAAO,mBAAmB,QAAQ,KAAK;IACvC;GAGF,KAAK,YACH,QAAQ,KAAK,CAAC;EAElB;CACF,SAAS,OAAO;EACd,MAAM,WAA2B;GAC/B,MAAM;GACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAChE;EACA,YAAY,YAAY,QAAQ;CAClC;AACF,CAAC;AAGD,IAAI,YAAY,UACd,OAAO,WAAW,EAAE,KAAK,WAAW,OAAO,QAAQ,IAAI,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"ts-health.worker.mjs","names":[],"sources":["../../../../../../../../../core/src/dev-server/health-checker/workers/ts-health.worker.ts"],"sourcesContent":["/**\n * TypeScript Health Check Worker\n *\n * This worker runs in a dedicated thread to perform TypeScript type checking\n * without blocking the main dev server thread. It maintains a persistent\n * ts.Program instance for fast incremental type checking.\n *\n * Communication:\n * - Receives: { type: 'init' | 'check' | 'fileChanges' | 'shutdown', ... }\n * - Sends: { type: 'results' | 'initialized' | 'error', ... }\n */\nimport ts from \"typescript\";\nimport path from \"node:path\";\nimport { parentPort, workerData } from \"worker_threads\";\n\n/**\n * Serialized file data received from main thread\n */\ntype SerializedFile = {\n /** Absolute file path */\n path: string;\n /** File content (source code) */\n content: string;\n /** Relative path for display */\n relativePath: string;\n};\n\n/**\n * Diagnostic message sent back to main thread\n */\ntype DiagnosticMessage = {\n type: \"error\" | \"warning\";\n message: string;\n lineNumber: number;\n columnNumber: number;\n length: number;\n filePath: string;\n relativePath: string;\n};\n\n/**\n * Health check result for a single file\n */\ntype FileCheckResult = {\n path: string;\n relativePath: string;\n healthy: boolean;\n errors: DiagnosticMessage[];\n warnings: DiagnosticMessage[];\n};\n\n/**\n * Messages received from main thread\n */\n/**\n * Deleted file info\n */\ntype DeletedFile = {\n path: string;\n relativePath: string;\n};\n\n/**\n * Messages received from main thread\n */\ntype WorkerMessage =\n | { type: \"init\"; config: { cwd: string; tsconfigPath?: string } }\n | { type: \"check\"; files: SerializedFile[] }\n | { type: \"fileChanges\"; files: SerializedFile[] }\n | { type: \"filesDeleted\"; files: DeletedFile[] }\n | { type: \"shutdown\" };\n\n/**\n * Messages sent to main thread\n */\ntype WorkerResponse =\n | { type: \"initialized\"; success: boolean; error?: string }\n | { type: \"results\"; results: FileCheckResult[] }\n | { type: \"error\"; message: string };\n\n/**\n * TypeScript Health Worker class\n * Maintains persistent ts.Program for incremental type checking\n */\nclass TypeScriptHealthWorker {\n /**\n * Cached TypeScript program instance\n */\n private program: ts.Program | null = null;\n\n /**\n * Parsed TypeScript configuration\n */\n private parsedConfig: ts.ParsedCommandLine | null = null;\n\n /**\n * In-memory file contents cache\n */\n private fileContents = new Map<string, string>();\n\n /** Source paths removed since the tsconfig was parsed. */\n private deletedRootPaths = new Set<string>();\n\n /**\n * Whether the worker is initialized\n */\n private initialized = false;\n\n /**\n * Current working directory\n */\n private cwd: string = process.cwd();\n\n /**\n * Initialize the worker with TypeScript configuration\n */\n public initialize(config: { cwd: string; tsconfigPath?: string }): boolean {\n try {\n this.cwd = config.cwd || process.cwd();\n\n // Try to load tsconfig.json\n const tsconfigPath = config.tsconfigPath || ts.findConfigFile(this.cwd, ts.sys.fileExists);\n\n if (!tsconfigPath) {\n this.initialized = true;\n return true; // No tsconfig, will skip type checking\n }\n\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\n if (configFile.error) {\n console.warn(\"TypeScript Worker: Error reading tsconfig:\", configFile.error);\n this.initialized = true;\n return true;\n }\n\n this.parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, this.cwd);\n\n if (this.parsedConfig.errors.length > 0) {\n console.warn(\"TypeScript Worker: tsconfig has errors:\", this.parsedConfig.errors);\n }\n\n this.initialized = true;\n return true;\n } catch (error) {\n console.error(\"TypeScript Worker: Failed to initialize:\", error);\n this.initialized = true;\n return false;\n }\n }\n\n /**\n * Check files for TypeScript errors\n */\n public checkFiles(files: SerializedFile[]): FileCheckResult[] {\n if (!this.parsedConfig) {\n // No config, return all files as healthy\n return files.map((file) => ({\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n }));\n }\n\n // Update in-memory file cache\n for (const file of files) {\n this.fileContents.set(file.path, file.content);\n this.deletedRootPaths.delete(this.getPathIdentity(file.path));\n }\n\n // Create or update the program (incremental)\n this.program = ts.createProgram(\n this.getProgramRootNames(),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined, // Pass old program for incremental compilation\n );\n\n // Check each file\n const results: FileCheckResult[] = [];\n\n for (const file of files) {\n const result = this.checkSingleFile(file);\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Handle file changes (update program incrementally)\n */\n public handleFileChanges(files: SerializedFile[]): void {\n for (const file of files) {\n this.fileContents.set(file.path, file.content);\n this.deletedRootPaths.delete(this.getPathIdentity(file.path));\n }\n\n // Recreate program with new file contents\n if (this.parsedConfig && this.program) {\n this.program = ts.createProgram(\n this.getProgramRootNames(),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined,\n );\n }\n }\n\n /**\n * Handle deleted files (remove from cache)\n */\n public handleFilesDeleted(files: DeletedFile[]): void {\n let hasChanges = false;\n\n for (const file of files) {\n const identity = this.getPathIdentity(file.path);\n const cachedPath = Array.from(this.fileContents.keys()).find(\n (candidate) => this.getPathIdentity(candidate) === identity,\n );\n\n if (cachedPath) {\n this.fileContents.delete(cachedPath);\n hasChanges = true;\n }\n if (!this.deletedRootPaths.has(identity)) hasChanges = true;\n this.deletedRootPaths.add(identity);\n }\n\n // Recreate program without deleted files\n if (hasChanges && this.parsedConfig) {\n this.program = ts.createProgram(\n this.getProgramRootNames(),\n this.parsedConfig.options,\n this.createCompilerHost(),\n this.program || undefined,\n );\n }\n }\n\n /**\n * Check a single file for diagnostics\n */\n private checkSingleFile(file: SerializedFile): FileCheckResult {\n if (!this.program) {\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n };\n }\n\n const sourceFile = this.program.getSourceFile(file.path);\n\n if (!sourceFile) {\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: true,\n errors: [],\n warnings: [],\n };\n }\n\n const syntacticDiagnostics = this.program.getSyntacticDiagnostics(sourceFile);\n const semanticDiagnostics = this.program.getSemanticDiagnostics(sourceFile);\n const allDiagnostics = [...syntacticDiagnostics, ...semanticDiagnostics];\n\n const errors: DiagnosticMessage[] = [];\n const warnings: DiagnosticMessage[] = [];\n\n for (const diagnostic of allDiagnostics) {\n const message = this.formatDiagnostic(diagnostic, file);\n\n if (diagnostic.category === ts.DiagnosticCategory.Error) {\n errors.push(message);\n } else if (diagnostic.category === ts.DiagnosticCategory.Warning) {\n warnings.push(message);\n }\n }\n\n return {\n path: file.path,\n relativePath: file.relativePath,\n healthy: errors.length === 0 && warnings.length === 0,\n errors,\n warnings,\n };\n }\n\n /**\n * Format a TypeScript diagnostic into our message format\n */\n private formatDiagnostic(diagnostic: ts.Diagnostic, file: SerializedFile): DiagnosticMessage {\n let lineNumber = 1;\n let columnNumber = 1;\n let length = 1;\n\n if (diagnostic.file && diagnostic.start !== undefined) {\n const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);\n lineNumber = pos.line + 1;\n columnNumber = pos.character + 1;\n length = diagnostic.length || 1;\n }\n\n const messageText =\n typeof diagnostic.messageText === \"string\"\n ? diagnostic.messageText\n : diagnostic.messageText.messageText;\n\n return {\n type: diagnostic.category === ts.DiagnosticCategory.Error ? \"error\" : \"warning\",\n message: messageText,\n lineNumber,\n columnNumber,\n length,\n filePath: file.path,\n relativePath: file.relativePath,\n };\n }\n\n /**\n * Create a custom compiler host that reads from in-memory cache\n */\n private createCompilerHost(): ts.CompilerHost {\n const defaultHost = ts.createCompilerHost(this.parsedConfig?.options || {});\n\n return {\n ...defaultHost,\n readFile: (fileName: string) => {\n // Check in-memory cache first\n if (this.fileContents.has(fileName)) {\n return this.fileContents.get(fileName);\n }\n // Fall back to disk\n return defaultHost.readFile(fileName);\n },\n fileExists: (fileName: string) => {\n if (this.fileContents.has(fileName)) {\n return true;\n }\n return defaultHost.fileExists(fileName);\n },\n };\n }\n\n /** Keep tsconfig-selected declarations in every incremental program. */\n private getProgramRootNames(): string[] {\n const configuredRoots = this.parsedConfig?.fileNames || [];\n\n const rootsByIdentity = new Map<string, string>();\n\n for (const filePath of [...configuredRoots, ...this.fileContents.keys()]) {\n const identity = this.getPathIdentity(filePath);\n if (!this.deletedRootPaths.has(identity)) rootsByIdentity.set(identity, filePath);\n }\n\n return [...rootsByIdentity.values()];\n }\n\n /** Compare equivalent Windows path spellings as one TypeScript root. */\n private getPathIdentity(filePath: string): string {\n const normalized = path.normalize(path.resolve(filePath));\n\n return ts.sys.useCaseSensitiveFileNames ? normalized : normalized.toLowerCase();\n }\n}\n\n// Create worker instance\nconst worker = new TypeScriptHealthWorker();\n\n// Handle messages from main thread\nparentPort?.on(\"message\", (message: WorkerMessage) => {\n try {\n switch (message.type) {\n case \"init\": {\n const success = worker.initialize(message.config);\n const response: WorkerResponse = { type: \"initialized\", success };\n parentPort?.postMessage(response);\n break;\n }\n\n case \"check\": {\n const results = worker.checkFiles(message.files);\n const response: WorkerResponse = { type: \"results\", results };\n parentPort?.postMessage(response);\n break;\n }\n\n case \"fileChanges\": {\n worker.handleFileChanges(message.files);\n break;\n }\n\n case \"filesDeleted\": {\n worker.handleFilesDeleted(message.files);\n break;\n }\n\n case \"shutdown\": {\n process.exit(0);\n }\n }\n } catch (error) {\n const response: WorkerResponse = {\n type: \"error\",\n message: error instanceof Error ? error.message : String(error),\n };\n parentPort?.postMessage(response);\n }\n});\n\n// Handle initialization from workerData if provided\nif (workerData?.autoInit) {\n worker.initialize({ cwd: workerData.cwd || process.cwd() });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { container } from "../container/index.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
//#region ../core/src/dev-server/model-module-registry.ts
|
|
7
|
+
function normalizeInputPath(absolutePath) {
|
|
8
|
+
const slashNormalized = path.resolve(absolutePath).replaceAll("\\", "/");
|
|
9
|
+
return process.platform === "win32" ? slashNormalized.toLowerCase() : slashNormalized;
|
|
10
|
+
}
|
|
11
|
+
function canonicalPath(absolutePath) {
|
|
12
|
+
let resolvedPath = path.resolve(absolutePath);
|
|
13
|
+
try {
|
|
14
|
+
resolvedPath = fs.realpathSync.native(resolvedPath);
|
|
15
|
+
} catch {}
|
|
16
|
+
return normalizeInputPath(resolvedPath);
|
|
17
|
+
}
|
|
18
|
+
/** Internal writer for the container's read-only development model carrier. */
|
|
19
|
+
var DevelopmentModelModuleRegistry = class {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
22
|
+
this.aliases = /* @__PURE__ */ new Map();
|
|
23
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
24
|
+
}
|
|
25
|
+
get(absolutePath) {
|
|
26
|
+
const inputPath = normalizeInputPath(absolutePath);
|
|
27
|
+
const key = this.aliases.get(inputPath) ?? canonicalPath(absolutePath);
|
|
28
|
+
const entry = this.entries.get(key);
|
|
29
|
+
if (entry !== void 0) this.aliases.set(inputPath, key);
|
|
30
|
+
return entry === void 0 ? void 0 : this.toPublicEntry(entry);
|
|
31
|
+
}
|
|
32
|
+
subscribe(listener) {
|
|
33
|
+
this.listeners.add(listener);
|
|
34
|
+
return () => this.listeners.delete(listener);
|
|
35
|
+
}
|
|
36
|
+
publish(absolutePath, url, namespace) {
|
|
37
|
+
const inputPath = normalizeInputPath(absolutePath);
|
|
38
|
+
const key = canonicalPath(absolutePath);
|
|
39
|
+
this.aliases.set(inputPath, key);
|
|
40
|
+
const current = this.entries.get(key);
|
|
41
|
+
if (current?.state === "ready" && current.namespace === namespace) return;
|
|
42
|
+
const entry = {
|
|
43
|
+
url,
|
|
44
|
+
generation: (current?.generation ?? 0) + 1,
|
|
45
|
+
hasDefault: Object.hasOwn(namespace, "default"),
|
|
46
|
+
state: "ready",
|
|
47
|
+
namespace
|
|
48
|
+
};
|
|
49
|
+
this.entries.set(key, entry);
|
|
50
|
+
this.notify(entry);
|
|
51
|
+
}
|
|
52
|
+
remove(absolutePath) {
|
|
53
|
+
const inputPath = normalizeInputPath(absolutePath);
|
|
54
|
+
const key = this.aliases.get(inputPath) ?? canonicalPath(absolutePath);
|
|
55
|
+
this.aliases.set(inputPath, key);
|
|
56
|
+
const current = this.entries.get(key);
|
|
57
|
+
if (current?.state === "removed") return;
|
|
58
|
+
const entry = {
|
|
59
|
+
url: current?.url ?? pathToFileURL(absolutePath).href,
|
|
60
|
+
generation: (current?.generation ?? 0) + 1,
|
|
61
|
+
hasDefault: false,
|
|
62
|
+
state: "removed"
|
|
63
|
+
};
|
|
64
|
+
this.entries.set(key, entry);
|
|
65
|
+
this.notify(entry);
|
|
66
|
+
}
|
|
67
|
+
notify(entry) {
|
|
68
|
+
const publicEntry = this.toPublicEntry(entry);
|
|
69
|
+
for (const listener of this.listeners) listener(publicEntry);
|
|
70
|
+
}
|
|
71
|
+
toPublicEntry(entry) {
|
|
72
|
+
return {
|
|
73
|
+
url: entry.url,
|
|
74
|
+
generation: entry.generation,
|
|
75
|
+
hasDefault: entry.hasDefault,
|
|
76
|
+
state: entry.state
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
function getDevelopmentModelModuleRegistry() {
|
|
81
|
+
const existing = container.tryGet("development.modelModules");
|
|
82
|
+
if (existing instanceof DevelopmentModelModuleRegistry) return existing;
|
|
83
|
+
const registry = new DevelopmentModelModuleRegistry();
|
|
84
|
+
container.set("development.modelModules", registry);
|
|
85
|
+
return registry;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
export { getDevelopmentModelModuleRegistry };
|
|
90
|
+
//# sourceMappingURL=model-module-registry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-module-registry.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/model-module-registry.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { container } from \"../container\";\nimport type {\n DevelopmentModelModuleEntry,\n DevelopmentModelModules,\n} from \"../container/development-model-modules\";\n\ntype StoredEntry = DevelopmentModelModuleEntry & { readonly namespace?: object };\n\nfunction normalizeInputPath(absolutePath: string): string {\n const slashNormalized = path.resolve(absolutePath).replaceAll(\"\\\\\", \"/\");\n return process.platform === \"win32\" ? slashNormalized.toLowerCase() : slashNormalized;\n}\n\nfunction canonicalPath(absolutePath: string): string {\n let resolvedPath = path.resolve(absolutePath);\n\n try {\n resolvedPath = fs.realpathSync.native(resolvedPath);\n } catch {\n // A deleted model must still resolve to the key it had before deletion.\n }\n\n return normalizeInputPath(resolvedPath);\n}\n\n/** Internal writer for the container's read-only development model carrier. */\nexport class DevelopmentModelModuleRegistry implements DevelopmentModelModules {\n private readonly entries = new Map<string, StoredEntry>();\n private readonly aliases = new Map<string, string>();\n private readonly listeners = new Set<(entry: DevelopmentModelModuleEntry) => void>();\n\n public get(absolutePath: string): DevelopmentModelModuleEntry | undefined {\n const inputPath = normalizeInputPath(absolutePath);\n const key = this.aliases.get(inputPath) ?? canonicalPath(absolutePath);\n const entry = this.entries.get(key);\n if (entry !== undefined) this.aliases.set(inputPath, key);\n return entry === undefined ? undefined : this.toPublicEntry(entry);\n }\n\n public subscribe(listener: (entry: DevelopmentModelModuleEntry) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n public publish(absolutePath: string, url: string, namespace: object): void {\n const inputPath = normalizeInputPath(absolutePath);\n const key = canonicalPath(absolutePath);\n this.aliases.set(inputPath, key);\n const current = this.entries.get(key);\n\n if (current?.state === \"ready\" && current.namespace === namespace) return;\n\n const entry: StoredEntry = {\n url,\n generation: (current?.generation ?? 0) + 1,\n hasDefault: Object.hasOwn(namespace, \"default\"),\n state: \"ready\",\n namespace,\n };\n this.entries.set(key, entry);\n this.notify(entry);\n }\n\n public remove(absolutePath: string): void {\n const inputPath = normalizeInputPath(absolutePath);\n const key = this.aliases.get(inputPath) ?? canonicalPath(absolutePath);\n this.aliases.set(inputPath, key);\n const current = this.entries.get(key);\n\n if (current?.state === \"removed\") return;\n\n const entry: DevelopmentModelModuleEntry = {\n url: current?.url ?? pathToFileURL(absolutePath).href,\n generation: (current?.generation ?? 0) + 1,\n hasDefault: false,\n state: \"removed\",\n };\n this.entries.set(key, entry);\n this.notify(entry);\n }\n\n private notify(entry: DevelopmentModelModuleEntry): void {\n const publicEntry = this.toPublicEntry(entry);\n for (const listener of this.listeners) listener(publicEntry);\n }\n\n private toPublicEntry(entry: DevelopmentModelModuleEntry): DevelopmentModelModuleEntry {\n return {\n url: entry.url,\n generation: entry.generation,\n hasDefault: entry.hasDefault,\n state: entry.state,\n };\n }\n}\n\nexport function getDevelopmentModelModuleRegistry(): DevelopmentModelModuleRegistry {\n const existing = container.tryGet(\"development.modelModules\");\n\n if (existing instanceof DevelopmentModelModuleRegistry) return existing;\n\n const registry = new DevelopmentModelModuleRegistry();\n container.set(\"development.modelModules\", registry);\n return registry;\n}\n"],"mappings":";;;;;;AAWA,SAAS,mBAAmB,cAA8B;CACxD,MAAM,kBAAkB,KAAK,QAAQ,YAAY,CAAC,CAAC,WAAW,MAAM,GAAG;CACvE,OAAO,QAAQ,aAAa,UAAU,gBAAgB,YAAY,IAAI;AACxE;AAEA,SAAS,cAAc,cAA8B;CACnD,IAAI,eAAe,KAAK,QAAQ,YAAY;CAE5C,IAAI;EACF,eAAe,GAAG,aAAa,OAAO,YAAY;CACpD,QAAQ,CAER;CAEA,OAAO,mBAAmB,YAAY;AACxC;;AAGA,IAAa,iCAAb,MAA+E;;iCAClD,IAAI,IAAyB;iCAC7B,IAAI,IAAoB;mCACtB,IAAI,IAAkD;;CAEnF,AAAO,IAAI,cAA+D;EACxE,MAAM,YAAY,mBAAmB,YAAY;EACjD,MAAM,MAAM,KAAK,QAAQ,IAAI,SAAS,KAAK,cAAc,YAAY;EACrE,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,UAAU,QAAW,KAAK,QAAQ,IAAI,WAAW,GAAG;EACxD,OAAO,UAAU,SAAY,SAAY,KAAK,cAAc,KAAK;CACnE;CAEA,AAAO,UAAU,UAAoE;EACnF,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;CAC7C;CAEA,AAAO,QAAQ,cAAsB,KAAa,WAAyB;EACzE,MAAM,YAAY,mBAAmB,YAAY;EACjD,MAAM,MAAM,cAAc,YAAY;EACtC,KAAK,QAAQ,IAAI,WAAW,GAAG;EAC/B,MAAM,UAAU,KAAK,QAAQ,IAAI,GAAG;EAEpC,IAAI,SAAS,UAAU,WAAW,QAAQ,cAAc,WAAW;EAEnE,MAAM,QAAqB;GACzB;GACA,aAAa,SAAS,cAAc,KAAK;GACzC,YAAY,OAAO,OAAO,WAAW,SAAS;GAC9C,OAAO;GACP;EACF;EACA,KAAK,QAAQ,IAAI,KAAK,KAAK;EAC3B,KAAK,OAAO,KAAK;CACnB;CAEA,AAAO,OAAO,cAA4B;EACxC,MAAM,YAAY,mBAAmB,YAAY;EACjD,MAAM,MAAM,KAAK,QAAQ,IAAI,SAAS,KAAK,cAAc,YAAY;EACrE,KAAK,QAAQ,IAAI,WAAW,GAAG;EAC/B,MAAM,UAAU,KAAK,QAAQ,IAAI,GAAG;EAEpC,IAAI,SAAS,UAAU,WAAW;EAElC,MAAM,QAAqC;GACzC,KAAK,SAAS,OAAO,cAAc,YAAY,CAAC,CAAC;GACjD,aAAa,SAAS,cAAc,KAAK;GACzC,YAAY;GACZ,OAAO;EACT;EACA,KAAK,QAAQ,IAAI,KAAK,KAAK;EAC3B,KAAK,OAAO,KAAK;CACnB;CAEA,AAAQ,OAAO,OAA0C;EACvD,MAAM,cAAc,KAAK,cAAc,KAAK;EAC5C,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS,WAAW;CAC7D;CAEA,AAAQ,cAAc,OAAiE;EACrF,OAAO;GACL,KAAK,MAAM;GACX,YAAY,MAAM;GAClB,YAAY,MAAM;GAClB,OAAO,MAAM;EACf;CACF;AACF;AAEA,SAAgB,oCAAoE;CAClF,MAAM,WAAW,UAAU,OAAO,0BAA0B;CAE5D,IAAI,oBAAoB,gCAAgC,OAAO;CAE/D,MAAM,WAAW,IAAI,+BAA+B;CACpD,UAAU,IAAI,4BAA4B,QAAQ;CAClD,OAAO;AACT"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { router } from "../router/router.mjs";
|
|
2
2
|
import { devLogError, formatModuleNotFoundError } from "./dev-logger.mjs";
|
|
3
|
+
import { getDevelopmentModelModuleRegistry } from "./model-module-registry.mjs";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
4
5
|
|
|
5
6
|
//#region ../core/src/dev-server/module-loader.ts
|
|
@@ -38,6 +39,7 @@ var ModuleLoader = class {
|
|
|
38
39
|
constructor(specialFilesCollector) {
|
|
39
40
|
this.specialFilesCollector = specialFilesCollector;
|
|
40
41
|
this.loadedModules = /* @__PURE__ */ new Map();
|
|
42
|
+
getDevelopmentModelModuleRegistry();
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* Eagerly load every special file at boot, in the canonical order so
|
|
@@ -72,6 +74,7 @@ var ModuleLoader = class {
|
|
|
72
74
|
const module = await import(fileUrl);
|
|
73
75
|
this.loadedModules.set(file.absolutePath, module);
|
|
74
76
|
this.registerCleanup(file, module);
|
|
77
|
+
if (type === "model") getDevelopmentModelModuleRegistry().publish(file.absolutePath, fileUrl, module);
|
|
75
78
|
return module;
|
|
76
79
|
};
|
|
77
80
|
return type === "route" ? await router.withSourceFile(file.relativePath, load) : await load();
|
|
@@ -112,6 +115,7 @@ var ModuleLoader = class {
|
|
|
112
115
|
*/
|
|
113
116
|
cleanupDeletedModule(file) {
|
|
114
117
|
this.loadedModules.delete(file.absolutePath);
|
|
118
|
+
if (file.type === "model") getDevelopmentModelModuleRegistry().remove(file.absolutePath);
|
|
115
119
|
if (file.type === "route") router.removeRoutesBySourceFile(file.relativePath);
|
|
116
120
|
this.runCleanup(file);
|
|
117
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-loader.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/module-loader.ts"],"sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { router } from \"../router/router\";\nimport { devLogError, formatModuleNotFoundError } from \"./dev-logger\";\nimport type { CleanupFunction, FileManager } from \"./file-manager\";\nimport type { SpecialFileType, SpecialFilesCollector } from \"./special-files-collector\";\n\ndeclare global {\n var __currentModuleFile: FileManager | undefined;\n}\n\n/**\n * Thrown when a special file (route/event/main/locale) fails to import or\n * register. Carries the file that failed and the original cause so a broken\n * module surfaces with its origin instead of vanishing into a 404.\n */\nexport class ModuleLoadError extends Error {\n public constructor(\n public readonly type: string,\n public readonly file: FileManager,\n public readonly cause: unknown,\n ) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n super(`Failed to load ${type}: ${file.relativePath} - ${reason}`);\n this.name = \"ModuleLoadError\";\n }\n}\n\n/**\n * The four \"special\" file kinds that the dev server eagerly imports at boot.\n * Order matters: locales first (used by everything), events before main so\n * listeners are registered when main runs, routes last so handlers are bound.\n */\nconst SPECIAL_TYPES: readonly SpecialFileType[] = [\"locale\", \"event\", \"main\", \"route\"] as const;\n\n/**\n * Loads application modules through the ESM loader hook. The hook stamps\n * `?v=N` on every import, so each reload is a fresh module without any\n * userland cache-busting plumbing.\n */\nexport class ModuleLoader {\n private readonly loadedModules = new Map<string, unknown>();\n\n constructor(private readonly specialFilesCollector: SpecialFilesCollector) {}\n\n /**\n * Eagerly load every special file at boot, in the canonical order so\n * later phases (e.g. routes) see state earlier phases registered.\n *\n * A failing module no longer vanishes silently: each failure is collected\n * (so one broken file doesn't hide the others) and, once every file has been\n * attempted, an aggregate error is thrown. The boot callers\n * (`DevelopmentServer.start`, the HTTP test bootstrap) already wrap this in a\n * try/catch that logs and aborts the boot — so a broken route module aborts\n * boot loudly rather than booting into a surface that 404s.\n */\n public async loadAll(): Promise<void> {\n const failures: ModuleLoadError[] = [];\n\n for (const type of SPECIAL_TYPES) {\n for (const file of this.specialFilesCollector.getFilesByType(type)) {\n try {\n await this.loadModule(file, type);\n } catch (error) {\n failures.push(\n error instanceof ModuleLoadError ? error : new ModuleLoadError(type, file, error),\n );\n }\n }\n }\n\n if (failures.length > 0) {\n throw new AggregateError(\n failures,\n `${failures.length} module(s) failed to load during boot:\\n` +\n failures.map((failure) => ` - ${failure.message}`).join(\"\\n\"),\n );\n }\n }\n\n /**\n * Import a file through the loader hook. For routes, scopes the import in\n * `router.withSourceFile()` so the added routes carry their origin.\n */\n public async loadModule<T = unknown>(file: FileManager, type: string): Promise<T | undefined> {\n if (file.relativePath.endsWith(\".env\")) return undefined;\n\n globalThis.__currentModuleFile = file;\n\n try {\n const fileUrl = pathToFileURL(file.absolutePath).href;\n\n const load = async () => {\n const module = (await import(fileUrl)) as Record<string, unknown>;\n this.loadedModules.set(file.absolutePath, module);\n this.registerCleanup(file, module);\n return module as T;\n };\n\n return type === \"route\" ? await router.withSourceFile(file.relativePath, load) : await load();\n } catch (error: any) {\n if (error.code === \"ERR_MODULE_NOT_FOUND\") {\n devLogError(formatModuleNotFoundError(error));\n } else {\n // Pass the error through so devLogError prints the stack. With dev\n // source maps enabled, frames remap to the original `.ts` line/col\n // (e.g. the actual call site of an undefined symbol) instead of\n // vanishing — a one-line \"x is not defined\" is undebuggable.\n devLogError(\n `Failed to load ${type}: ${file.relativePath} - ${error?.message || error}`,\n error,\n );\n }\n\n // RETHROW after logging. Previously this returned undefined, so a route\n // file that threw on import or registration disappeared with no boot\n // error — the whole surface 404'd silently. Boot callers abort on the\n // throw; the HMR batch-reload handler catches it, keeps the server\n // alive, and shows it loudly.\n throw error instanceof ModuleLoadError ? error : new ModuleLoadError(type, file, error);\n } finally {\n globalThis.__currentModuleFile = undefined;\n }\n }\n\n /**\n * Reload a previously-loaded special file. The version-bump that makes the\n * import fresh is done by the caller (layer-executor) before invoking this.\n * Routes are removed from the registry first so re-registration is clean.\n */\n public async reloadModule(file: FileManager): Promise<void> {\n const moduleType = this.specialFilesCollector.getFileType(file.relativePath);\n if (!moduleType) return;\n\n this.runCleanup(file);\n\n if (moduleType === \"route\") {\n router.removeRoutesBySourceFile(file.relativePath);\n }\n\n this.loadedModules.delete(file.absolutePath);\n await this.loadModule(file, moduleType);\n }\n\n /**\n * Run every cleanup hook the file registered (or `$cleanup` on its exports)\n * and reset the list. Called once per HMR reload.\n */\n public runCleanup(file: FileManager): void {\n for (const hook of file.cleanup) {\n try {\n if (typeof hook === \"function\") {\n hook();\n } else if (\n hook &&\n typeof (hook as { unsubscribe?: () => void }).unsubscribe === \"function\"\n ) {\n (hook as { unsubscribe: () => void }).unsubscribe();\n }\n } catch {\n // Cleanup errors shouldn't break HMR.\n }\n }\n file.resetCleanup();\n }\n\n /**\n * Clean up after a file whose source was deleted from disk.\n */\n public cleanupDeletedModule(file: FileManager): void {\n this.loadedModules.delete(file.absolutePath);\n\n if (file.type === \"route\") {\n router.removeRoutesBySourceFile(file.relativePath);\n }\n\n this.runCleanup(file);\n }\n\n /**\n * Scan a freshly-loaded module for cleanup handlers and register them on\n * the FileManager so they run before the next reload. Priority: explicit\n * `export function cleanup()` → `.$cleanup` on any exported value.\n */\n private registerCleanup(file: FileManager, module: Record<string, unknown>): void {\n if (typeof module.cleanup === \"function\") {\n file.addCleanup(module.cleanup as CleanupFunction);\n return;\n }\n\n const cleanups: CleanupFunction[] = [];\n\n for (const exportedValue of Object.values(module)) {\n const cleanup = (exportedValue as { $cleanup?: unknown })?.$cleanup;\n if (typeof cleanup === \"function\") {\n cleanups.push(cleanup.bind(exportedValue) as CleanupFunction);\n }\n }\n\n if (cleanups.length > 0) {\n file.addCleanup(cleanups);\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAa,kBAAb,cAAqC,MAAM;CACzC,AAAO,YACL,AAAgB,MAChB,AAAgB,MAChB,AAAgB,OAChB;EACA,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACpE,MAAM,kBAAkB,KAAK,IAAI,KAAK,aAAa,KAAK,QAAQ;EALhD;EACA;EACA;EAIhB,KAAK,OAAO;CACd;AACF;;;;;;AAOA,MAAM,gBAA4C;CAAC;CAAU;CAAS;CAAQ;AAAO;;;;;;AAOrF,IAAa,eAAb,MAA0B;CAGxB,YAAY,AAAiB,uBAA8C;EAA9C;uCAFI,IAAI,IAAqB;CAEkB;;;;;;;;;;;;CAa5E,MAAa,UAAyB;EACpC,MAAM,WAA8B,CAAC;EAErC,KAAK,MAAM,QAAQ,eACjB,KAAK,MAAM,QAAQ,KAAK,sBAAsB,eAAe,IAAI,GAC/D,IAAI;GACF,MAAM,KAAK,WAAW,MAAM,IAAI;EAClC,SAAS,OAAO;GACd,SAAS,KACP,iBAAiB,kBAAkB,QAAQ,IAAI,gBAAgB,MAAM,MAAM,KAAK,CAClF;EACF;EAIJ,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,eACR,UACA,GAAG,SAAS,OAAO,4CACjB,SAAS,KAAK,YAAY,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,IAAI,CACjE;CAEJ;;;;;CAMA,MAAa,WAAwB,MAAmB,MAAsC;EAC5F,IAAI,KAAK,aAAa,SAAS,MAAM,GAAG,OAAO;EAE/C,WAAW,sBAAsB;EAEjC,IAAI;GACF,MAAM,UAAU,cAAc,KAAK,YAAY,CAAC,CAAC;GAEjD,MAAM,OAAO,YAAY;IACvB,MAAM,SAAU,MAAM,OAAO;IAC7B,KAAK,cAAc,IAAI,KAAK,cAAc,MAAM;IAChD,KAAK,gBAAgB,MAAM,MAAM;IACjC,OAAO;GACT;GAEA,OAAO,SAAS,UAAU,MAAM,OAAO,eAAe,KAAK,cAAc,IAAI,IAAI,MAAM,KAAK;EAC9F,SAAS,OAAY;GACnB,IAAI,MAAM,SAAS,wBACjB,YAAY,0BAA0B,KAAK,CAAC;QAM5C,YACE,kBAAkB,KAAK,IAAI,KAAK,aAAa,KAAK,OAAO,WAAW,SACpE,KACF;GAQF,MAAM,iBAAiB,kBAAkB,QAAQ,IAAI,gBAAgB,MAAM,MAAM,KAAK;EACxF,UAAU;GACR,WAAW,sBAAsB;EACnC;CACF;;;;;;CAOA,MAAa,aAAa,MAAkC;EAC1D,MAAM,aAAa,KAAK,sBAAsB,YAAY,KAAK,YAAY;EAC3E,IAAI,CAAC,YAAY;EAEjB,KAAK,WAAW,IAAI;EAEpB,IAAI,eAAe,SACjB,OAAO,yBAAyB,KAAK,YAAY;EAGnD,KAAK,cAAc,OAAO,KAAK,YAAY;EAC3C,MAAM,KAAK,WAAW,MAAM,UAAU;CACxC;;;;;CAMA,AAAO,WAAW,MAAyB;EACzC,KAAK,MAAM,QAAQ,KAAK,SACtB,IAAI;GACF,IAAI,OAAO,SAAS,YAClB,KAAK;QACA,IACL,QACA,OAAQ,KAAsC,gBAAgB,YAE9D,AAAC,KAAqC,YAAY;EAEtD,QAAQ,CAER;EAEF,KAAK,aAAa;CACpB;;;;CAKA,AAAO,qBAAqB,MAAyB;EACnD,KAAK,cAAc,OAAO,KAAK,YAAY;EAE3C,IAAI,KAAK,SAAS,SAChB,OAAO,yBAAyB,KAAK,YAAY;EAGnD,KAAK,WAAW,IAAI;CACtB;;;;;;CAOA,AAAQ,gBAAgB,MAAmB,QAAuC;EAChF,IAAI,OAAO,OAAO,YAAY,YAAY;GACxC,KAAK,WAAW,OAAO,OAA0B;GACjD;EACF;EAEA,MAAM,WAA8B,CAAC;EAErC,KAAK,MAAM,iBAAiB,OAAO,OAAO,MAAM,GAAG;GACjD,MAAM,UAAW,eAA0C;GAC3D,IAAI,OAAO,YAAY,YACrB,SAAS,KAAK,QAAQ,KAAK,aAAa,CAAoB;EAEhE;EAEA,IAAI,SAAS,SAAS,GACpB,KAAK,WAAW,QAAQ;CAE5B;AACF"}
|
|
1
|
+
{"version":3,"file":"module-loader.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/module-loader.ts"],"sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { router } from \"../router/router\";\nimport { devLogError, formatModuleNotFoundError } from \"./dev-logger\";\nimport type { CleanupFunction, FileManager } from \"./file-manager\";\nimport { getDevelopmentModelModuleRegistry } from \"./model-module-registry\";\nimport type { SpecialFileType, SpecialFilesCollector } from \"./special-files-collector\";\n\ndeclare global {\n var __currentModuleFile: FileManager | undefined;\n}\n\n/**\n * Thrown when a special file (route/event/main/locale) fails to import or\n * register. Carries the file that failed and the original cause so a broken\n * module surfaces with its origin instead of vanishing into a 404.\n */\nexport class ModuleLoadError extends Error {\n public constructor(\n public readonly type: string,\n public readonly file: FileManager,\n public readonly cause: unknown,\n ) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n super(`Failed to load ${type}: ${file.relativePath} - ${reason}`);\n this.name = \"ModuleLoadError\";\n }\n}\n\n/**\n * The four \"special\" file kinds that the dev server eagerly imports at boot.\n * Order matters: locales first (used by everything), events before main so\n * listeners are registered when main runs, routes last so handlers are bound.\n */\nconst SPECIAL_TYPES: readonly SpecialFileType[] = [\"locale\", \"event\", \"main\", \"route\"] as const;\n\n/**\n * Loads application modules through the ESM loader hook. The hook stamps\n * `?v=N` on every import, so each reload is a fresh module without any\n * userland cache-busting plumbing.\n */\nexport class ModuleLoader {\n private readonly loadedModules = new Map<string, unknown>();\n\n public constructor(private readonly specialFilesCollector: SpecialFilesCollector) {\n // Web creates its Vite bridge after Core's development orchestration has\n // constructed ModuleLoader. Create the carrier now so a zero-model app can\n // subscribe before a model is added later through HMR.\n getDevelopmentModelModuleRegistry();\n }\n\n /**\n * Eagerly load every special file at boot, in the canonical order so\n * later phases (e.g. routes) see state earlier phases registered.\n *\n * A failing module no longer vanishes silently: each failure is collected\n * (so one broken file doesn't hide the others) and, once every file has been\n * attempted, an aggregate error is thrown. The boot callers\n * (`DevelopmentServer.start`, the HTTP test bootstrap) already wrap this in a\n * try/catch that logs and aborts the boot — so a broken route module aborts\n * boot loudly rather than booting into a surface that 404s.\n */\n public async loadAll(): Promise<void> {\n const failures: ModuleLoadError[] = [];\n\n for (const type of SPECIAL_TYPES) {\n for (const file of this.specialFilesCollector.getFilesByType(type)) {\n try {\n await this.loadModule(file, type);\n } catch (error) {\n failures.push(\n error instanceof ModuleLoadError ? error : new ModuleLoadError(type, file, error),\n );\n }\n }\n }\n\n if (failures.length > 0) {\n throw new AggregateError(\n failures,\n `${failures.length} module(s) failed to load during boot:\\n` +\n failures.map((failure) => ` - ${failure.message}`).join(\"\\n\"),\n );\n }\n }\n\n /**\n * Import a file through the loader hook. For routes, scopes the import in\n * `router.withSourceFile()` so the added routes carry their origin.\n */\n public async loadModule<T = unknown>(file: FileManager, type: string): Promise<T | undefined> {\n if (file.relativePath.endsWith(\".env\")) return undefined;\n\n globalThis.__currentModuleFile = file;\n\n try {\n const fileUrl = pathToFileURL(file.absolutePath).href;\n\n const load = async () => {\n const module = (await import(fileUrl)) as Record<string, unknown>;\n this.loadedModules.set(file.absolutePath, module);\n this.registerCleanup(file, module);\n if (type === \"model\") {\n getDevelopmentModelModuleRegistry().publish(file.absolutePath, fileUrl, module);\n }\n return module as T;\n };\n\n return type === \"route\" ? await router.withSourceFile(file.relativePath, load) : await load();\n } catch (error: any) {\n if (error.code === \"ERR_MODULE_NOT_FOUND\") {\n devLogError(formatModuleNotFoundError(error));\n } else {\n // Pass the error through so devLogError prints the stack. With dev\n // source maps enabled, frames remap to the original `.ts` line/col\n // (e.g. the actual call site of an undefined symbol) instead of\n // vanishing — a one-line \"x is not defined\" is undebuggable.\n devLogError(\n `Failed to load ${type}: ${file.relativePath} - ${error?.message || error}`,\n error,\n );\n }\n\n // RETHROW after logging. Previously this returned undefined, so a route\n // file that threw on import or registration disappeared with no boot\n // error — the whole surface 404'd silently. Boot callers abort on the\n // throw; the HMR batch-reload handler catches it, keeps the server\n // alive, and shows it loudly.\n throw error instanceof ModuleLoadError ? error : new ModuleLoadError(type, file, error);\n } finally {\n globalThis.__currentModuleFile = undefined;\n }\n }\n\n /**\n * Reload a previously-loaded special file. The version-bump that makes the\n * import fresh is done by the caller (layer-executor) before invoking this.\n * Routes are removed from the registry first so re-registration is clean.\n */\n public async reloadModule(file: FileManager): Promise<void> {\n const moduleType = this.specialFilesCollector.getFileType(file.relativePath);\n if (!moduleType) return;\n\n this.runCleanup(file);\n\n if (moduleType === \"route\") {\n router.removeRoutesBySourceFile(file.relativePath);\n }\n\n this.loadedModules.delete(file.absolutePath);\n await this.loadModule(file, moduleType);\n }\n\n /**\n * Run every cleanup hook the file registered (or `$cleanup` on its exports)\n * and reset the list. Called once per HMR reload.\n */\n public runCleanup(file: FileManager): void {\n for (const hook of file.cleanup) {\n try {\n if (typeof hook === \"function\") {\n hook();\n } else if (\n hook &&\n typeof (hook as { unsubscribe?: () => void }).unsubscribe === \"function\"\n ) {\n (hook as { unsubscribe: () => void }).unsubscribe();\n }\n } catch {\n // Cleanup errors shouldn't break HMR.\n }\n }\n file.resetCleanup();\n }\n\n /**\n * Clean up after a file whose source was deleted from disk.\n */\n public cleanupDeletedModule(file: FileManager): void {\n this.loadedModules.delete(file.absolutePath);\n\n if (file.type === \"model\") {\n getDevelopmentModelModuleRegistry().remove(file.absolutePath);\n }\n\n if (file.type === \"route\") {\n router.removeRoutesBySourceFile(file.relativePath);\n }\n\n this.runCleanup(file);\n }\n\n /**\n * Scan a freshly-loaded module for cleanup handlers and register them on\n * the FileManager so they run before the next reload. Priority: explicit\n * `export function cleanup()` → `.$cleanup` on any exported value.\n */\n private registerCleanup(file: FileManager, module: Record<string, unknown>): void {\n if (typeof module.cleanup === \"function\") {\n file.addCleanup(module.cleanup as CleanupFunction);\n return;\n }\n\n const cleanups: CleanupFunction[] = [];\n\n for (const exportedValue of Object.values(module)) {\n const cleanup = (exportedValue as { $cleanup?: unknown })?.$cleanup;\n if (typeof cleanup === \"function\") {\n cleanups.push(cleanup.bind(exportedValue) as CleanupFunction);\n }\n }\n\n if (cleanups.length > 0) {\n file.addCleanup(cleanups);\n }\n }\n}\n"],"mappings":"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"}
|
package/esm/http/response.d.mts
CHANGED
|
@@ -117,7 +117,7 @@ declare class Response {
|
|
|
117
117
|
/**
|
|
118
118
|
* Get raw response
|
|
119
119
|
*/
|
|
120
|
-
get raw(): import("
|
|
120
|
+
get raw(): import("http").ServerResponse<import("http").IncomingMessage>;
|
|
121
121
|
/**
|
|
122
122
|
* Get Current response body
|
|
123
123
|
*/
|
|
@@ -461,7 +461,7 @@ declare class Response {
|
|
|
461
461
|
/**
|
|
462
462
|
* Send a no content response with status code 204
|
|
463
463
|
*/
|
|
464
|
-
noContent(): FastifyReply<import("fastify").RouteGenericInterface, import("fastify").RawServerDefault, import("
|
|
464
|
+
noContent(): FastifyReply<import("fastify").RouteGenericInterface, import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>;
|
|
465
465
|
/**
|
|
466
466
|
* Send an accepted response with status code 202
|
|
467
467
|
* Used for async operations that have been accepted but not yet processed
|
|
@@ -505,7 +505,7 @@ declare class Response {
|
|
|
505
505
|
* Send buffer as a response
|
|
506
506
|
* Useful for dynamically generated content (e.g., resized images, generated PDFs)
|
|
507
507
|
*/
|
|
508
|
-
sendBuffer(buffer: Buffer, options?: number | SendBufferOptions): FastifyReply<import("fastify").RouteGenericInterface, import("fastify").RawServerDefault, import("
|
|
508
|
+
sendBuffer(buffer: Buffer, options?: number | SendBufferOptions): FastifyReply<import("fastify").RouteGenericInterface, import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>;
|
|
509
509
|
/**
|
|
510
510
|
* Send an Image instance as a response
|
|
511
511
|
* Automatically detects image format and sets content type
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.mts","names":[],"sources":["../../../../../../../core/src/http/response.ts"],"mappings":";;;;;;;;;;;;;;KA+BK,WAAA,+BAA0C,MAAA,gBAAsB,KAAK;;;;AAFjC;;;;AAEiC;AAc1E;;;;KAAY,aAAA,GAAgB,sBAAsB;EAStC;;;;;EAHV,GAAG;AAAA;AAAA,aAGO,cAAA;EACV,EAAA;EACA,OAAA;EACA,QAAA;EACA,iBAAA;EACA,KAAA;EACA,SAAA;EACA,YAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;EACA,SAAA;EACA,kBAAA;EACA,QAAA;EACA,iBAAA;EACA,qBAAA;EACA,mBAAA;AAAA;;;;KAMU,eAAA;EACV,SAAA;EACA,SAAA;EACA,MAAA;EACA,QAAA;AAAA;;;;KAMU,iBAAA,GAAoB,eAAe;EAC7C,WAAA;EACA,IAAA;AAAA;AAAA,cAqBW,QAAA;EAAQ;;;EAAA,UAIT,KAAA,EAAQ,KAAA;EAiCD;;;;;;;;;;;;;;;;EAfV,YAAA,EAAe,YAAA;EAsauB;;;EAAA,UAjanC,iBAAA;EAwamE;;;EAAA,UAnanE,WAAA;EAgb4C;;;EA3a/C,OAAA,EAAU,OAAA;EA0kBkB;;;EAAA,UArkBzB,MAAA,EAAM,GAAA;EAmzBQ;;;;;EA5yBjB,UAAA;EAi8BqB;;;EAAA,IA57BjB,GAAA,
|
|
1
|
+
{"version":3,"file":"response.d.mts","names":[],"sources":["../../../../../../../core/src/http/response.ts"],"mappings":"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"}
|
package/esm/index.d.mts
CHANGED
|
@@ -110,6 +110,7 @@ import { SocketConnector } from "./connectors/socket-connector.mjs";
|
|
|
110
110
|
import { StorageConnector } from "./connectors/storage.connector.mjs";
|
|
111
111
|
import { CLICommandAction, CLICommandOption, CLICommandOptions, CLICommandPreload, CLICommandSource, CommandActionData, ResolvedCLICommandOption } from "./commands/types.mjs";
|
|
112
112
|
import { CLICommand, command } from "./commands/cli-command.mjs";
|
|
113
|
+
import { DevelopmentModelModuleEntry, DevelopmentModelModuleState, DevelopmentModelModules } from "./container/development-model-modules.mjs";
|
|
113
114
|
import { ContainerTypes, container } from "./container/index.mjs";
|
|
114
115
|
import { DatabaseLogModel } from "./database/models/database-log/database-log.mjs";
|
|
115
116
|
import { SeedClock, SeedContext, SeedRecordRef, SeedResult, SeederMetadata, Track, TrackableModel } from "./database/seeds/types.mjs";
|
|
@@ -175,7 +176,7 @@ import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager }
|
|
|
175
176
|
import { env } from "@mongez/dotenv";
|
|
176
177
|
import { colors } from "@mongez/copper";
|
|
177
178
|
export * from "@mongez/localization";
|
|
178
|
-
export { $registerUseCase, $unregisterUseCase, AccessConnector, AggregateExpressionInput, AggregateExpressions, AiConnector, AllRepositoryOptions, AppConfigurations, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, type BenchmarkChannel, type BenchmarkConfigurations, type BenchmarkErrorResult, type BenchmarkOptions, BenchmarkProfiler, type BenchmarkProfilerOptions, type BenchmarkResult, BenchmarkSnapshots, type BenchmarkSnapshotsOptions, type BenchmarkStats, type BenchmarkSuccessResult, BootContext, BootListener, BootSignal, BootSignalType, BootValidator, CLICommand, type CLICommandAction, type CLICommandOption, type CLICommandOptions, type CLICommandPreload, type CLICommandSource, CacheConnector, type CacheMiddlewareOptions, CachedRepositoryOptions, type CapturedMail, CascadeAdapter, CascadeQueryBuilder, ChunkCallback, ClosableServer, CloudDriver, CloudStorageDriverContract, CloudStorageDriverOptions, CloudStorageFileData, type CommandActionData, type ConcurrencyLimitOptions, ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry, ConfigSpecialHandlers, ConflictError, Connector, ConnectorBuildContext, ConnectorBuildContribution, ConnectorBuildGenerateResult, ConnectorEsbuildPatch, ConnectorLifecyclePhase, ConnectorName, ConnectorPriority, ConnectorsManager, ConsoleChannel, ContainerTypes, CookieJarUnavailableError, CookieOptions, CspConfig, CsrfOriginCheckReason, CsrfOriginVerdict, CursorPaginationOptions, CursorPaginationResult, DEFAULT_CSP_DIRECTIVES, DOSpacesDriver, DatabaseCacheDriver, type DatabaseCacheOptions, DatabaseConnector, DatabaseLog, DatabaseLogModel, DatabaseLogOptions, DecodedAccessToken, DefineResourceOptions, DeleteManyResult, DetectedImageFormat, EncryptionConfigurations, EncryptionPasswordConfigurations, Environment, EventSubscription, ExistsExceptCurrentIdRuleOptions, ExistsExceptCurrentUserRuleOptions, FastifyInstance, FileNamingStrategy, FileValidationOptions, FileValidator, FileVisibility, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, ForbiddenError, GenerateImageVariantsOptions, GeneratedImageDescriptor, GeneratedImageVariant, GroupByFields, GroupedRoutesOptions, HealthCheck, HealthStatus, HeraldConnector, HttpConfigurations, HttpConnector, HttpContext, HttpError, HttpErrorCodes, HttpReadyReport, HttpTracingConfig, type IdempotencyOptions, Image, ImageFormat, ImageInput, ImageTransformCallback, ImageTransformConfig, ImageTransformOptions, ImageVariantDefinition, ImageVariantFit, ImageVariantOutputFormat, ImageVariantsConfigError, InvalidCspDirectiveError, type IpFilterOptions, ListOptions, LocalDriver, LocalStorageDriverOptions, LocalizedObject, LogConfigurations, LoggerConnector, MAIL_EVENTS, Mail, type MailAddress, type MailAttachment, type MailConfigurations, MailError, type MailErrorCode, type MailEvents, type MailMode, type MailOptions, type MailPriority, type MailResult, MailerConnector, type MailersConfig, type MaintenanceOptions, Middleware, MiddlewareResponse, MimeTypes, NoopChannel, type NormalizedMail, NotAcceptableError, NotAllowedError, NotificationsConnector, PaginationMode, PaginationResult, PartialMiddleware, PartialPick, Path, PipeableReactStream, PipelineOptions, PortInUseError, PositionalHandlerSuspect, PrefixConfig, PrefixOptions, PresignedOptions, PresignedUploadOptions, PutDirectoryOptions, PutDirectoryResult, PutFromUrlOptions, PutOptions, QueryBuilderContract, Queue, R2Driver, R2StorageDriverOptions, type RateLimitOptions, RegisterConfiguredConnectorsOptions, RegisterResource, RegisteredUseCase, RepositoryAdapterContract, RepositoryConfigurations, RepositoryEvent, RepositoryManager, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, Request, RequestContextStore, RequestController, RequestControllerContract, RequestEvent, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestLocals, RequestLog, RequestMethod, RequestUserMovedError, type ResolvedCLICommandOption, ResolvedImageVariantsConfig, Resource, ResourceArraySchema, ResourceCastType, ResourceConstructor, ResourceContract, ResourceFieldBuilder, ResourceFieldBuilderDateOutputOptions, ResourceFieldConfig, ResourceMethod, ResourceNotFoundError, ResourceOutputValueCastType, ResourceSchema, ResourceSelfReference, Response, ResponseBodyValue, ResponseEvent, ResponseSSEController, ResponseSchema, ResponseStatus, ResponseStreamController, Restful, RestfulMiddleware, ReturnedResponse, Route, RouteOptions, RouteRegistry, RouteResource, Router, RouterGroupCallback, RouterStacks, RuntimeStrategy, S3Driver, type SESConfigurations, type SMTPConfigurations, SafeFetchOptions, SafeFetchResult, SaveAsOptions, SaveMode, SaveOptions, ScopedStorage, ScopedStorageContract, SeedClock, SeedContext, SeedRecordRef, SeedResult, Seeder, SeederDependencyCycleError, SeederMetadata, SendBufferOptions, SendFileOptions, ServerError, ShutdownListener, SocketConnector, SocketOptions, Storage, StorageConfigurations, StorageConnector, StorageCopyEventPayload, StorageDriverConfig, StorageDriverContextStore, StorageDriverContract, StorageDriverName, StorageDriverRegistry, StorageDriverType, StorageError, StorageErrorOptions, StorageEventHandler, StorageEventPayload, StorageEventType, StorageFile, StorageFileData, StorageFileInfo, StorageManagerContract, StorageNotInitializedError, StoragePutEventPayload, StreamReactResponseOptions, TemporaryTokenError, TemporaryTokenPayload, TemporaryTokenValidation, TracingContext, TracingContextSource, TracingHooks, TracingPhaseInfo, TracingRequestEndInfo, Track, TrackableModel, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, UPLOADS_DEFAULTS, UnAuthorizedError, UniqueExceptCurrentIdRuleOptions, UniqueExceptCurrentUserRuleOptions, UnknownSeederDependencyError, UploadedFile, UploadedFileImageOptions, UploadedFileJson, UploadedFileQuery, UploadsConfigurations, UploadsImagesConfigurations, UseCase, UseCaseAfterMiddleware, UseCaseBeforeMiddleware, UseCaseBroadcastChannel, UseCaseBroadcastEvent, UseCaseBroadcastOption, UseCaseConfigurations, UseCaseContext, UseCaseErrorResult, UseCaseEventsCallbacksMap, UseCaseGuard, UseCaseHandler, UseCaseOnExecutingContext, UseCaseResult, UseCaseRuntimeOptions, UseCaseWithSchema, ValidationConfiguration, VariantOutputFormat, VariantSourceFormat, WarlockConfig, WarlockConfigManager, WatermarkConfig, WhereOperator, XMLable, addUseCaseHistory, anyMatch, app, appLog, appPath, applyCspHeader, assertConfiguredHttpPortIsFree, assertMailCount, assertMailSent, assertNoReservedConnectorNames, assertPortIsAvailable, assertUniqueConnectorNames, assetsUrl, bootstrap, broadcastUseCaseResult, buildCspHeaderValue, buildIdempotencyCacheKey, buildTracingContext, cachePath, captureMail, clearPositionalHandlerSuspects, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, deriveTraceId, describePositionalHandlerSuspect, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, displayEnvironmentMode, encrypt, ensureFatalIsVisible, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, forgetPositionalHandlerSuspects, generateImageVariants, generateMailId, getDefaultMailConfig, getHttpReadyReport, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getPublicUrl, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, inspectHandlerSignature, ipMatches, isAllowedCsrfOrigin, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isTracingEnabled, isUnknownTsExtensionError, isValidIdempotencyKey, listPositionalHandlerSuspects, loadS3, logResponse, logsPath, looksLikePositionalHandler, mailEvents, matchesDerivedRouteName, measure, mergeCspDirectives, middleware, normalizeOrigin, normalizeRequestPath, normalizeRoutePath, originOf, ownOrigin, parseSize, parseTraceparentTraceId, paths, preflightConfiguredHttpPort, promiseAllObject, publicPath, publicUrl, registerAppConfig, registerConfiguredConnectors, registerHttpPlugins, remedyLines, renderReact, renderReactMail, reportPositionalHandlerSuspects, requestContext, requestMemo, requiresDefaultCsrfGuard, resetHttpReadyReport, resetMailConfig, resetTracingConfigForTests, resolveCspConfig, resolveCsrfOriginVerdict, resolveMailConfig, resolveTracingConfig, resolveWithinRoot, rootPath, routeNameMethodSuffix, router, runDefaultCsrfGuard, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, serializeCspDirectives, setBaseUrl, setConfig, setEnvironment, setHttpReadyReport, setLogConfigurations, setMailConfigurations, setMailMode, shouldPreflightHttpPort, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, streamReactResponse, t, tempPath, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileController, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, validateCspConfigAtBoot, validateCspDirectives, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, wrapResponseInDataKey };
|
|
179
|
+
export { $registerUseCase, $unregisterUseCase, AccessConnector, AggregateExpressionInput, AggregateExpressions, AiConnector, AllRepositoryOptions, AppConfigurations, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, type BenchmarkChannel, type BenchmarkConfigurations, type BenchmarkErrorResult, type BenchmarkOptions, BenchmarkProfiler, type BenchmarkProfilerOptions, type BenchmarkResult, BenchmarkSnapshots, type BenchmarkSnapshotsOptions, type BenchmarkStats, type BenchmarkSuccessResult, BootContext, BootListener, BootSignal, BootSignalType, BootValidator, CLICommand, type CLICommandAction, type CLICommandOption, type CLICommandOptions, type CLICommandPreload, type CLICommandSource, CacheConnector, type CacheMiddlewareOptions, CachedRepositoryOptions, type CapturedMail, CascadeAdapter, CascadeQueryBuilder, ChunkCallback, ClosableServer, CloudDriver, CloudStorageDriverContract, CloudStorageDriverOptions, CloudStorageFileData, type CommandActionData, type ConcurrencyLimitOptions, ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry, ConfigSpecialHandlers, ConflictError, Connector, ConnectorBuildContext, ConnectorBuildContribution, ConnectorBuildGenerateResult, ConnectorEsbuildPatch, ConnectorLifecyclePhase, ConnectorName, ConnectorPriority, ConnectorsManager, ConsoleChannel, ContainerTypes, CookieJarUnavailableError, CookieOptions, CspConfig, CsrfOriginCheckReason, CsrfOriginVerdict, CursorPaginationOptions, CursorPaginationResult, DEFAULT_CSP_DIRECTIVES, DOSpacesDriver, DatabaseCacheDriver, type DatabaseCacheOptions, DatabaseConnector, DatabaseLog, DatabaseLogModel, DatabaseLogOptions, DecodedAccessToken, DefineResourceOptions, DeleteManyResult, DetectedImageFormat, type DevelopmentModelModuleEntry, type DevelopmentModelModuleState, type DevelopmentModelModules, EncryptionConfigurations, EncryptionPasswordConfigurations, Environment, EventSubscription, ExistsExceptCurrentIdRuleOptions, ExistsExceptCurrentUserRuleOptions, FastifyInstance, FileNamingStrategy, FileValidationOptions, FileValidator, FileVisibility, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, ForbiddenError, GenerateImageVariantsOptions, GeneratedImageDescriptor, GeneratedImageVariant, GroupByFields, GroupedRoutesOptions, HealthCheck, HealthStatus, HeraldConnector, HttpConfigurations, HttpConnector, HttpContext, HttpError, HttpErrorCodes, HttpReadyReport, HttpTracingConfig, type IdempotencyOptions, Image, ImageFormat, ImageInput, ImageTransformCallback, ImageTransformConfig, ImageTransformOptions, ImageVariantDefinition, ImageVariantFit, ImageVariantOutputFormat, ImageVariantsConfigError, InvalidCspDirectiveError, type IpFilterOptions, ListOptions, LocalDriver, LocalStorageDriverOptions, LocalizedObject, LogConfigurations, LoggerConnector, MAIL_EVENTS, Mail, type MailAddress, type MailAttachment, type MailConfigurations, MailError, type MailErrorCode, type MailEvents, type MailMode, type MailOptions, type MailPriority, type MailResult, MailerConnector, type MailersConfig, type MaintenanceOptions, Middleware, MiddlewareResponse, MimeTypes, NoopChannel, type NormalizedMail, NotAcceptableError, NotAllowedError, NotificationsConnector, PaginationMode, PaginationResult, PartialMiddleware, PartialPick, Path, PipeableReactStream, PipelineOptions, PortInUseError, PositionalHandlerSuspect, PrefixConfig, PrefixOptions, PresignedOptions, PresignedUploadOptions, PutDirectoryOptions, PutDirectoryResult, PutFromUrlOptions, PutOptions, QueryBuilderContract, Queue, R2Driver, R2StorageDriverOptions, type RateLimitOptions, RegisterConfiguredConnectorsOptions, RegisterResource, RegisteredUseCase, RepositoryAdapterContract, RepositoryConfigurations, RepositoryEvent, RepositoryManager, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, Request, RequestContextStore, RequestController, RequestControllerContract, RequestEvent, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestLocals, RequestLog, RequestMethod, RequestUserMovedError, type ResolvedCLICommandOption, ResolvedImageVariantsConfig, Resource, ResourceArraySchema, ResourceCastType, ResourceConstructor, ResourceContract, ResourceFieldBuilder, ResourceFieldBuilderDateOutputOptions, ResourceFieldConfig, ResourceMethod, ResourceNotFoundError, ResourceOutputValueCastType, ResourceSchema, ResourceSelfReference, Response, ResponseBodyValue, ResponseEvent, ResponseSSEController, ResponseSchema, ResponseStatus, ResponseStreamController, Restful, RestfulMiddleware, ReturnedResponse, Route, RouteOptions, RouteRegistry, RouteResource, Router, RouterGroupCallback, RouterStacks, RuntimeStrategy, S3Driver, type SESConfigurations, type SMTPConfigurations, SafeFetchOptions, SafeFetchResult, SaveAsOptions, SaveMode, SaveOptions, ScopedStorage, ScopedStorageContract, SeedClock, SeedContext, SeedRecordRef, SeedResult, Seeder, SeederDependencyCycleError, SeederMetadata, SendBufferOptions, SendFileOptions, ServerError, ShutdownListener, SocketConnector, SocketOptions, Storage, StorageConfigurations, StorageConnector, StorageCopyEventPayload, StorageDriverConfig, StorageDriverContextStore, StorageDriverContract, StorageDriverName, StorageDriverRegistry, StorageDriverType, StorageError, StorageErrorOptions, StorageEventHandler, StorageEventPayload, StorageEventType, StorageFile, StorageFileData, StorageFileInfo, StorageManagerContract, StorageNotInitializedError, StoragePutEventPayload, StreamReactResponseOptions, TemporaryTokenError, TemporaryTokenPayload, TemporaryTokenValidation, TracingContext, TracingContextSource, TracingHooks, TracingPhaseInfo, TracingRequestEndInfo, Track, TrackableModel, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, UPLOADS_DEFAULTS, UnAuthorizedError, UniqueExceptCurrentIdRuleOptions, UniqueExceptCurrentUserRuleOptions, UnknownSeederDependencyError, UploadedFile, UploadedFileImageOptions, UploadedFileJson, UploadedFileQuery, UploadsConfigurations, UploadsImagesConfigurations, UseCase, UseCaseAfterMiddleware, UseCaseBeforeMiddleware, UseCaseBroadcastChannel, UseCaseBroadcastEvent, UseCaseBroadcastOption, UseCaseConfigurations, UseCaseContext, UseCaseErrorResult, UseCaseEventsCallbacksMap, UseCaseGuard, UseCaseHandler, UseCaseOnExecutingContext, UseCaseResult, UseCaseRuntimeOptions, UseCaseWithSchema, ValidationConfiguration, VariantOutputFormat, VariantSourceFormat, WarlockConfig, WarlockConfigManager, WatermarkConfig, WhereOperator, XMLable, addUseCaseHistory, anyMatch, app, appLog, appPath, applyCspHeader, assertConfiguredHttpPortIsFree, assertMailCount, assertMailSent, assertNoReservedConnectorNames, assertPortIsAvailable, assertUniqueConnectorNames, assetsUrl, bootstrap, broadcastUseCaseResult, buildCspHeaderValue, buildIdempotencyCacheKey, buildTracingContext, cachePath, captureMail, clearPositionalHandlerSuspects, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, deriveTraceId, describePositionalHandlerSuspect, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, displayEnvironmentMode, encrypt, ensureFatalIsVisible, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, forgetPositionalHandlerSuspects, generateImageVariants, generateMailId, getDefaultMailConfig, getHttpReadyReport, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getPublicUrl, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, inspectHandlerSignature, ipMatches, isAllowedCsrfOrigin, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isTracingEnabled, isUnknownTsExtensionError, isValidIdempotencyKey, listPositionalHandlerSuspects, loadS3, logResponse, logsPath, looksLikePositionalHandler, mailEvents, matchesDerivedRouteName, measure, mergeCspDirectives, middleware, normalizeOrigin, normalizeRequestPath, normalizeRoutePath, originOf, ownOrigin, parseSize, parseTraceparentTraceId, paths, preflightConfiguredHttpPort, promiseAllObject, publicPath, publicUrl, registerAppConfig, registerConfiguredConnectors, registerHttpPlugins, remedyLines, renderReact, renderReactMail, reportPositionalHandlerSuspects, requestContext, requestMemo, requiresDefaultCsrfGuard, resetHttpReadyReport, resetMailConfig, resetTracingConfigForTests, resolveCspConfig, resolveCsrfOriginVerdict, resolveMailConfig, resolveTracingConfig, resolveWithinRoot, rootPath, routeNameMethodSuffix, router, runDefaultCsrfGuard, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, serializeCspDirectives, setBaseUrl, setConfig, setEnvironment, setHttpReadyReport, setLogConfigurations, setMailConfigurations, setMailMode, shouldPreflightHttpPort, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, streamReactResponse, t, tempPath, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileController, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, validateCspConfigAtBoot, validateCspDirectives, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, wrapResponseInDataKey };
|
|
179
180
|
import "./config/types.mjs";
|
|
180
181
|
import "./http/request.mjs";
|
|
181
182
|
import "./http/types.mjs";
|
package/package.json
CHANGED
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"@mongez/slug": "^1.0.7",
|
|
26
26
|
"@mongez/supportive-is": "^2.1.4",
|
|
27
27
|
"@mongez/time-wizard": "^1.0.6",
|
|
28
|
-
"@warlock.js/cache": "5.
|
|
29
|
-
"@warlock.js/cascade": "5.
|
|
30
|
-
"@warlock.js/context": "5.
|
|
31
|
-
"@warlock.js/fs": "5.
|
|
32
|
-
"@warlock.js/logger": "5.
|
|
33
|
-
"@warlock.js/seal": "5.
|
|
28
|
+
"@warlock.js/cache": "5.18.0",
|
|
29
|
+
"@warlock.js/cascade": "5.18.0",
|
|
30
|
+
"@warlock.js/context": "5.18.0",
|
|
31
|
+
"@warlock.js/fs": "5.18.0",
|
|
32
|
+
"@warlock.js/logger": "5.18.0",
|
|
33
|
+
"@warlock.js/seal": "5.18.0",
|
|
34
34
|
"bcryptjs": "^3.0.3",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"dayjs": "^1.11.19",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"@aws-sdk/lib-storage": "^3.955.0",
|
|
53
53
|
"@aws-sdk/s3-request-presigner": "^3.955.0",
|
|
54
54
|
"@react-email/render": "^2.0.5",
|
|
55
|
-
"@warlock.js/access": "5.
|
|
56
|
-
"@warlock.js/ai": "5.
|
|
57
|
-
"@warlock.js/herald": "5.
|
|
58
|
-
"@warlock.js/notifications": "5.
|
|
55
|
+
"@warlock.js/access": "5.18.0",
|
|
56
|
+
"@warlock.js/ai": "5.18.0",
|
|
57
|
+
"@warlock.js/herald": "5.18.0",
|
|
58
|
+
"@warlock.js/notifications": "5.18.0",
|
|
59
59
|
"nodemailer": "^8.0.5",
|
|
60
60
|
"react": "^19.2.3",
|
|
61
61
|
"react-dom": "^19.2.3",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
],
|
|
124
124
|
"author": "hassanzohdy",
|
|
125
125
|
"license": "MIT",
|
|
126
|
-
"version": "5.
|
|
126
|
+
"version": "5.18.0",
|
|
127
127
|
"type": "module",
|
|
128
128
|
"main": "./esm/index.mjs",
|
|
129
129
|
"module": "./esm/index.mjs",
|