@warlock.js/core 4.12.0 → 4.13.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 +116 -0
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -1
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +1 -1
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/create-database.command.mjs +1 -1
- package/esm/cli/commands/create-database.command.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -1
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
- package/esm/cli/commands/doctor.command.mjs +1 -1
- package/esm/cli/commands/doctor.command.mjs.map +1 -1
- package/esm/cli/commands/drop-tables.command.mjs +1 -1
- package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/path-resolver.mjs +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes.command.mjs +1 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/seed.command.mjs +1 -1
- package/esm/cli/commands/seed.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +1 -1
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.command.mjs +1 -1
- package/esm/cli/commands/storage-put.command.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs +2 -2
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -1
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/{cli → commands}/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -0
- package/esm/{cli → commands}/cli-command.mjs +1 -1
- package/esm/commands/cli-command.mjs.map +1 -0
- package/esm/{cli → commands}/types.d.mts +1 -1
- package/esm/commands/types.d.mts.map +1 -0
- package/esm/config/load-config-files.mjs +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/base-connector.mjs +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +5 -3
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +0 -2
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +2 -2
- package/esm/dev-server/file-event-handler.mjs +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.mjs +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.mjs +1 -1
- package/esm/dev-server/file-operations.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +3 -18
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
- package/esm/dev-server/layer-executor.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +2 -2
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +3 -3
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/http/build-cors-options.mjs +32 -0
- package/esm/http/build-cors-options.mjs.map +1 -0
- package/esm/http/middleware/max-body-size.middleware.d.mts +13 -5
- package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +13 -5
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +2 -9
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +3 -8
- package/esm/http/server.mjs.map +1 -1
- package/esm/index.d.mts +9 -17
- package/esm/index.mjs +4 -15
- package/esm/production/production-builder.mjs +51 -3
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/router/router.d.mts +9 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +58 -5
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +24 -1
- package/esm/router/types.d.mts.map +1 -1
- package/esm/tests/index.d.mts +2 -2
- package/esm/tests/index.mjs +1 -2
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +38 -15
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +34 -13
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +3 -3
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/utils/framework-vesion.mjs +1 -1
- package/esm/utils/index.d.mts +1 -0
- package/esm/utils/index.mjs +1 -0
- package/esm/utils/normalized-path.d.mts +55 -0
- package/esm/utils/normalized-path.d.mts.map +1 -0
- package/esm/{dev-server/path.mjs → utils/normalized-path.mjs} +15 -2
- package/esm/utils/normalized-path.mjs.map +1 -0
- package/esm/vite/index.d.mts +2 -0
- package/esm/vite/index.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts +1 -1
- package/esm/vite/lower-stage3-decorators.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/types.d.mts +34 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +1 -2
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +78 -23
- package/llms.txt +3 -3
- package/package.json +24 -12
- package/skills/lower-stage3-decorators/SKILL.md +4 -2
- package/skills/run-app/SKILL.md +6 -0
- package/skills/test-http/SKILL.md +29 -10
- package/skills/test-service/SKILL.md +12 -8
- package/skills/use-middleware/SKILL.md +5 -1
- package/esm/cli/cli-command.d.mts.map +0 -1
- package/esm/cli/cli-command.mjs.map +0 -1
- package/esm/cli/types.d.mts.map +0 -1
- package/esm/dev-server/files-orchestrator.d.mts +0 -89
- package/esm/dev-server/files-orchestrator.d.mts.map +0 -1
- package/esm/dev-server/files-watcher.d.mts +0 -69
- package/esm/dev-server/files-watcher.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts +0 -55
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts +0 -44
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts +0 -60
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts +0 -119
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts.map +0 -1
- package/esm/dev-server/health-checker/index.mjs +0 -7
- package/esm/dev-server/module-loader.d.mts +0 -63
- package/esm/dev-server/module-loader.d.mts.map +0 -1
- package/esm/dev-server/path.mjs.map +0 -1
- package/esm/tests/test-server-port-channel.d.mts +0 -27
- package/esm/tests/test-server-port-channel.d.mts.map +0 -1
- /package/esm/{cli → commands}/index.d.mts +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region ../core/src/utils/normalized-path.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Path helpers that always hand back forward slashes.
|
|
4
|
+
*
|
|
5
|
+
* Node's `path` returns `\` on Windows, and these values are compared against
|
|
6
|
+
* module specifiers, glob results and map keys — all of which use `/`. A raw
|
|
7
|
+
* `path.relative()` therefore produces a string that looks right in a log and
|
|
8
|
+
* fails every lookup it is used for.
|
|
9
|
+
*
|
|
10
|
+
* This lived under `dev-server/` and was imported from there by production
|
|
11
|
+
* modules (`connectors`, `database`, `config`), which put the whole dev-server
|
|
12
|
+
* directory into the production module graph. The code was never dev-only —
|
|
13
|
+
* only its location was.
|
|
14
|
+
*/
|
|
15
|
+
declare class Path {
|
|
16
|
+
/**
|
|
17
|
+
* Convert the given absolute path to a relative path
|
|
18
|
+
*/
|
|
19
|
+
static toRelative(absolutePath: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get relative path of the given path
|
|
22
|
+
*/
|
|
23
|
+
static relative(relativePath: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Get normalized absolute path of the given path
|
|
26
|
+
*/
|
|
27
|
+
static toNormalizedAbsolute(relativePath: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get absolute path of the given path
|
|
30
|
+
*/
|
|
31
|
+
static toAbsolute(relativePath: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Normalize the given path (convert backslashes to forward slashes)
|
|
34
|
+
*/
|
|
35
|
+
static normalize(filePath: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Join paths and normalize
|
|
38
|
+
*/
|
|
39
|
+
static join(...paths: string[]): string;
|
|
40
|
+
/**
|
|
41
|
+
* Get directory name of a path
|
|
42
|
+
*/
|
|
43
|
+
static dirname(filePath: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Get base name of a path
|
|
46
|
+
*/
|
|
47
|
+
static basename(filePath: string, ext?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Get extension of a path
|
|
50
|
+
*/
|
|
51
|
+
static extname(filePath: string): string;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { Path };
|
|
55
|
+
//# sourceMappingURL=normalized-path.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalized-path.d.mts","names":[],"sources":["../../../../../../../core/src/utils/normalized-path.ts"],"mappings":";;AAeA;;;;;;;;;;;;cAAa,IAAA;EAgCa;;;EAAA,OA5BV,UAAA,CAAW,YAAA;EA0CH;;;EAAA,OAnCR,QAAA,CAAS,YAAA;EAiDT;;;EAAA,OA1CA,oBAAA,CAAqB,YAAA;;;;SAOrB,UAAA,CAAW,YAAA;;;;SAOX,SAAA,CAAU,QAAA;;;;SAOV,IAAA,IAAQ,KAAA;;;;SAOR,OAAA,CAAQ,QAAA;;;;SAOR,QAAA,CAAS,QAAA,UAAkB,GAAA;;;;SAO3B,OAAA,CAAQ,QAAA;AAAA"}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
//#region ../core/src/
|
|
3
|
+
//#region ../core/src/utils/normalized-path.ts
|
|
4
|
+
/**
|
|
5
|
+
* Path helpers that always hand back forward slashes.
|
|
6
|
+
*
|
|
7
|
+
* Node's `path` returns `\` on Windows, and these values are compared against
|
|
8
|
+
* module specifiers, glob results and map keys — all of which use `/`. A raw
|
|
9
|
+
* `path.relative()` therefore produces a string that looks right in a log and
|
|
10
|
+
* fails every lookup it is used for.
|
|
11
|
+
*
|
|
12
|
+
* This lived under `dev-server/` and was imported from there by production
|
|
13
|
+
* modules (`connectors`, `database`, `config`), which put the whole dev-server
|
|
14
|
+
* directory into the production module graph. The code was never dev-only —
|
|
15
|
+
* only its location was.
|
|
16
|
+
*/
|
|
4
17
|
var Path = class {
|
|
5
18
|
/**
|
|
6
19
|
* Convert the given absolute path to a relative path
|
|
@@ -60,4 +73,4 @@ var Path = class {
|
|
|
60
73
|
|
|
61
74
|
//#endregion
|
|
62
75
|
export { Path };
|
|
63
|
-
//# sourceMappingURL=path.mjs.map
|
|
76
|
+
//# sourceMappingURL=normalized-path.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalized-path.mjs","names":[],"sources":["../../../../../../../core/src/utils/normalized-path.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * Path helpers that always hand back forward slashes.\n *\n * Node's `path` returns `\\` on Windows, and these values are compared against\n * module specifiers, glob results and map keys — all of which use `/`. A raw\n * `path.relative()` therefore produces a string that looks right in a log and\n * fails every lookup it is used for.\n *\n * This lived under `dev-server/` and was imported from there by production\n * modules (`connectors`, `database`, `config`), which put the whole dev-server\n * directory into the production module graph. The code was never dev-only —\n * only its location was.\n */\nexport class Path {\n /**\n * Convert the given absolute path to a relative path\n */\n public static toRelative(absolutePath: string) {\n return this.normalize(path.relative(process.cwd(), absolutePath));\n }\n\n /**\n * Get relative path of the given path\n */\n public static relative(relativePath: string) {\n return this.normalize(path.relative(process.cwd(), relativePath));\n }\n\n /**\n * Get normalized absolute path of the given path\n */\n public static toNormalizedAbsolute(relativePath: string) {\n return this.normalize(path.resolve(process.cwd(), relativePath));\n }\n\n /**\n * Get absolute path of the given path\n */\n public static toAbsolute(relativePath: string) {\n return this.normalize(path.resolve(process.cwd(), relativePath));\n }\n\n /**\n * Normalize the given path (convert backslashes to forward slashes)\n */\n public static normalize(filePath: string) {\n return filePath.replace(/\\\\/g, \"/\");\n }\n\n /**\n * Join paths and normalize\n */\n public static join(...paths: string[]) {\n return this.normalize(path.join(...paths));\n }\n\n /**\n * Get directory name of a path\n */\n public static dirname(filePath: string) {\n return this.normalize(path.dirname(filePath));\n }\n\n /**\n * Get base name of a path\n */\n public static basename(filePath: string, ext?: string) {\n return path.basename(filePath, ext);\n }\n\n /**\n * Get extension of a path\n */\n public static extname(filePath: string) {\n return path.extname(filePath);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,IAAa,OAAb,MAAkB;;;;CAIhB,OAAc,WAAW,cAAsB;EAC7C,OAAO,KAAK,UAAU,KAAK,SAAS,QAAQ,IAAI,GAAG,YAAY,CAAC;CAClE;;;;CAKA,OAAc,SAAS,cAAsB;EAC3C,OAAO,KAAK,UAAU,KAAK,SAAS,QAAQ,IAAI,GAAG,YAAY,CAAC;CAClE;;;;CAKA,OAAc,qBAAqB,cAAsB;EACvD,OAAO,KAAK,UAAU,KAAK,QAAQ,QAAQ,IAAI,GAAG,YAAY,CAAC;CACjE;;;;CAKA,OAAc,WAAW,cAAsB;EAC7C,OAAO,KAAK,UAAU,KAAK,QAAQ,QAAQ,IAAI,GAAG,YAAY,CAAC;CACjE;;;;CAKA,OAAc,UAAU,UAAkB;EACxC,OAAO,SAAS,QAAQ,OAAO,GAAG;CACpC;;;;CAKA,OAAc,KAAK,GAAG,OAAiB;EACrC,OAAO,KAAK,UAAU,KAAK,KAAK,GAAG,KAAK,CAAC;CAC3C;;;;CAKA,OAAc,QAAQ,UAAkB;EACtC,OAAO,KAAK,UAAU,KAAK,QAAQ,QAAQ,CAAC;CAC9C;;;;CAKA,OAAc,SAAS,UAAkB,KAAc;EACrD,OAAO,KAAK,SAAS,UAAU,GAAG;CACpC;;;;CAKA,OAAc,QAAQ,UAAkB;EACtC,OAAO,KAAK,QAAQ,QAAQ;CAC9B;AACF"}
|
package/esm/vite/index.mjs
CHANGED
|
@@ -39,7 +39,7 @@ interface DecoratorLoweringPlugin {
|
|
|
39
39
|
* @example
|
|
40
40
|
* ```ts
|
|
41
41
|
* // vite.config.ts / vitest.config.ts
|
|
42
|
-
* import { lowerStage3Decorators } from "@warlock.js/core";
|
|
42
|
+
* import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
43
43
|
* import { defineConfig } from "vitest/config";
|
|
44
44
|
*
|
|
45
45
|
* export default defineConfig({
|
|
@@ -26,7 +26,7 @@ import { transform } from "esbuild";
|
|
|
26
26
|
* @example
|
|
27
27
|
* ```ts
|
|
28
28
|
* // vite.config.ts / vitest.config.ts
|
|
29
|
-
* import { lowerStage3Decorators } from "@warlock.js/core";
|
|
29
|
+
* import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
30
30
|
* import { defineConfig } from "vitest/config";
|
|
31
31
|
*
|
|
32
32
|
* export default defineConfig({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lower-stage3-decorators.mjs","names":[],"sources":["../../../../../../../core/src/vite/lower-stage3-decorators.ts"],"sourcesContent":["import { transform } from \"esbuild\";\n\n/**\n * The Vite/Vitest plugin object returned by {@link lowerStage3Decorators}.\n *\n * Declared structurally so `@warlock.js/core` needn't take a `vite` dependency\n * just for one type — the shape is assignable to Vite's `PluginOption` wherever\n * it's placed in a config's `plugins` array.\n */\nexport interface DecoratorLoweringPlugin {\n name: string;\n enforce: \"pre\";\n transform(code: string, id: string): Promise<{ code: string; map: string } | null>;\n}\n\n/**\n * Vite/Vitest plugin that lowers TC39 Stage-3 (native) decorators to plain JS\n * **before** the rest of the transform pipeline runs.\n *\n * Cascade models declare relations with native decorators (`@RegisterModel`,\n * `@BelongsTo`, `@HasMany`, …). Vitest's bundled rolldown-vite transforms `.ts`\n * with **oxc**, and Vite's SSR `moduleRunnerTransform` rewrites the decorator's\n * import reference into `@(0, __vite_ssr_import__.RegisterModel)()` — invalid\n * decorator syntax that throws \"SyntaxError: Invalid or unexpected token\" on\n * every model load. oxc can only lower *legacy* decorators, and legacy lowering\n * would mangle the Stage-3 `(value, context)` signature these decorators rely on\n * (they read `context.kind` / `context.metadata`).\n *\n * esbuild lowers Stage-3 decorators correctly while preserving their runtime\n * semantics — the same transform the production `tsx`/build path uses, so a file\n * lowered here behaves identically in dev, test, and prod. Running `enforce:\n * \"pre\"` hands oxc / the SSR rewrite already-lowered, decorator-free code. Files\n * with no decorator skip esbuild entirely, so the fast path is untouched.\n *\n * Keep it **first** in the `plugins` array so it runs ahead of `@mongez/vite`\n * and Vite's core transform.\n *\n * @example\n * ```ts\n * // vite.config.ts / vitest.config.ts\n * import { lowerStage3Decorators } from \"@warlock.js/core\";\n * import { defineConfig } from \"vitest/config\";\n *\n * export default defineConfig({\n * plugins: [lowerStage3Decorators(), mongezVite()],\n * });\n * ```\n */\nexport function lowerStage3Decorators(): DecoratorLoweringPlugin {\n return {\n name: \"warlock:lower-stage3-decorators\",\n enforce: \"pre\",\n async transform(code, id) {\n const [filepath] = id.split(\"?\");\n\n if (!/\\.tsx?$/.test(filepath) || filepath.includes(\"/node_modules/\")) {\n return null;\n }\n\n // Cheap gate: only files that actually carry a decorator pay esbuild's cost.\n if (!/(^|\\n)\\s*@[A-Za-z_$]/.test(code)) {\n return null;\n }\n\n const result = await transform(code, {\n loader: filepath.endsWith(\".tsx\") ? \"tsx\" : \"ts\",\n format: \"esm\",\n target: \"es2022\",\n sourcemap: true,\n sourcefile: filepath,\n // Force native (Stage-3) semantics regardless of any ambient tsconfig\n // esbuild might otherwise honor.\n tsconfigRaw: { compilerOptions: { experimentalDecorators: false } },\n });\n\n return { code: result.code, map: result.map };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,wBAAiD;CAC/D,OAAO;EACL,MAAM;EACN,SAAS;EACT,MAAM,UAAU,MAAM,IAAI;GACxB,MAAM,CAAC,YAAY,GAAG,MAAM,GAAG;GAE/B,IAAI,CAAC,UAAU,KAAK,QAAQ,KAAK,SAAS,SAAS,gBAAgB,GACjE,OAAO;GAIT,IAAI,CAAC,uBAAuB,KAAK,IAAI,GACnC,OAAO;GAGT,MAAM,SAAS,MAAM,UAAU,MAAM;IACnC,QAAQ,SAAS,SAAS,MAAM,IAAI,QAAQ;IAC5C,QAAQ;IACR,QAAQ;IACR,WAAW;IACX,YAAY;IAGZ,aAAa,EAAE,iBAAiB,EAAE,wBAAwB,MAAM,EAAE;GACpE,CAAC;GAED,OAAO;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;GAAI;EAC9C;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"lower-stage3-decorators.mjs","names":[],"sources":["../../../../../../../core/src/vite/lower-stage3-decorators.ts"],"sourcesContent":["import { transform } from \"esbuild\";\n\n/**\n * The Vite/Vitest plugin object returned by {@link lowerStage3Decorators}.\n *\n * Declared structurally so `@warlock.js/core` needn't take a `vite` dependency\n * just for one type — the shape is assignable to Vite's `PluginOption` wherever\n * it's placed in a config's `plugins` array.\n */\nexport interface DecoratorLoweringPlugin {\n name: string;\n enforce: \"pre\";\n transform(code: string, id: string): Promise<{ code: string; map: string } | null>;\n}\n\n/**\n * Vite/Vitest plugin that lowers TC39 Stage-3 (native) decorators to plain JS\n * **before** the rest of the transform pipeline runs.\n *\n * Cascade models declare relations with native decorators (`@RegisterModel`,\n * `@BelongsTo`, `@HasMany`, …). Vitest's bundled rolldown-vite transforms `.ts`\n * with **oxc**, and Vite's SSR `moduleRunnerTransform` rewrites the decorator's\n * import reference into `@(0, __vite_ssr_import__.RegisterModel)()` — invalid\n * decorator syntax that throws \"SyntaxError: Invalid or unexpected token\" on\n * every model load. oxc can only lower *legacy* decorators, and legacy lowering\n * would mangle the Stage-3 `(value, context)` signature these decorators rely on\n * (they read `context.kind` / `context.metadata`).\n *\n * esbuild lowers Stage-3 decorators correctly while preserving their runtime\n * semantics — the same transform the production `tsx`/build path uses, so a file\n * lowered here behaves identically in dev, test, and prod. Running `enforce:\n * \"pre\"` hands oxc / the SSR rewrite already-lowered, decorator-free code. Files\n * with no decorator skip esbuild entirely, so the fast path is untouched.\n *\n * Keep it **first** in the `plugins` array so it runs ahead of `@mongez/vite`\n * and Vite's core transform.\n *\n * @example\n * ```ts\n * // vite.config.ts / vitest.config.ts\n * import { lowerStage3Decorators } from \"@warlock.js/core/vite\";\n * import { defineConfig } from \"vitest/config\";\n *\n * export default defineConfig({\n * plugins: [lowerStage3Decorators(), mongezVite()],\n * });\n * ```\n */\nexport function lowerStage3Decorators(): DecoratorLoweringPlugin {\n return {\n name: \"warlock:lower-stage3-decorators\",\n enforce: \"pre\",\n async transform(code, id) {\n const [filepath] = id.split(\"?\");\n\n if (!/\\.tsx?$/.test(filepath) || filepath.includes(\"/node_modules/\")) {\n return null;\n }\n\n // Cheap gate: only files that actually carry a decorator pay esbuild's cost.\n if (!/(^|\\n)\\s*@[A-Za-z_$]/.test(code)) {\n return null;\n }\n\n const result = await transform(code, {\n loader: filepath.endsWith(\".tsx\") ? \"tsx\" : \"ts\",\n format: \"esm\",\n target: \"es2022\",\n sourcemap: true,\n sourcefile: filepath,\n // Force native (Stage-3) semantics regardless of any ambient tsconfig\n // esbuild might otherwise honor.\n tsconfigRaw: { compilerOptions: { experimentalDecorators: false } },\n });\n\n return { code: result.code, map: result.map };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,wBAAiD;CAC/D,OAAO;EACL,MAAM;EACN,SAAS;EACT,MAAM,UAAU,MAAM,IAAI;GACxB,MAAM,CAAC,YAAY,GAAG,MAAM,GAAG;GAE/B,IAAI,CAAC,UAAU,KAAK,QAAQ,KAAK,SAAS,SAAS,gBAAgB,GACjE,OAAO;GAIT,IAAI,CAAC,uBAAuB,KAAK,IAAI,GACnC,OAAO;GAGT,MAAM,SAAS,MAAM,UAAU,MAAM;IACnC,QAAQ,SAAS,SAAS,MAAM,IAAI,QAAQ;IAC5C,QAAQ;IACR,QAAQ;IACR,WAAW;IACX,YAAY;IAGZ,aAAa,EAAE,iBAAiB,EAAE,wBAAwB,MAAM,EAAE;GACpE,CAAC;GAED,OAAO;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;GAAI;EAC9C;CACF;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager } from "./warlock-config.manager.mjs";
|
|
2
1
|
import { defineConfig } from "./define-config.mjs";
|
|
2
|
+
import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager } from "./warlock-config.manager.mjs";
|
|
3
3
|
|
|
4
4
|
export { };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CLICommand } from "../
|
|
1
|
+
import { CLICommand } from "../commands/cli-command.mjs";
|
|
2
2
|
import { FileHealthCheckerContract } from "../dev-server/health-checker/file-health-checker.contract.mjs";
|
|
3
3
|
import { MigrationConstructor } from "@warlock.js/cascade";
|
|
4
4
|
import { BuildOptions } from "esbuild";
|
|
@@ -45,6 +45,39 @@ type WarlockConfig = {
|
|
|
45
45
|
* @default app.js
|
|
46
46
|
*/
|
|
47
47
|
outFile?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Produce ONE self-contained file you can run with `node dist/app.js`.
|
|
50
|
+
*
|
|
51
|
+
* Sets `packages: "bundle"` and `splitting: false` as defaults — write
|
|
52
|
+
* either of them yourself and yours wins.
|
|
53
|
+
*
|
|
54
|
+
* ⚠ Not literally one file: native `.node` addons cannot be inlined by
|
|
55
|
+
* any bundler and are still emitted alongside it. "Single bundle" means
|
|
56
|
+
* one JavaScript file plus any native addons your dependencies carry.
|
|
57
|
+
*
|
|
58
|
+
* Without this flag the build stays the default: dependencies remain real
|
|
59
|
+
* `import` specifiers resolved from `node_modules` at runtime, which is
|
|
60
|
+
* what you want when you deploy the folder rather than the file.
|
|
61
|
+
*
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
singleBundle?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Inject the ESM interop shim — `require`, `__filename`, `__dirname`.
|
|
67
|
+
*
|
|
68
|
+
* Bundled CommonJS dependencies call `require(...)` and read `__dirname`
|
|
69
|
+
* to find their own assets. In an ES module none of those exist, so
|
|
70
|
+
* esbuild replaces them with a stub that **throws at runtime** — after a
|
|
71
|
+
* build that reported success. This recreates them via
|
|
72
|
+
* `createRequire(import.meta.url)`.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately NOT owned by {@link singleBundle}: it applies to any ESM
|
|
75
|
+
* build, so setting `packages: "bundle"` by hand works too. Turn it off
|
|
76
|
+
* only if you are certain nothing in your graph is CommonJS.
|
|
77
|
+
*
|
|
78
|
+
* @default true
|
|
79
|
+
*/
|
|
80
|
+
esmShim?: boolean;
|
|
48
81
|
/**
|
|
49
82
|
* Minify output
|
|
50
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/types.ts"],"mappings":";;;;;;;;AAUA;;;KAAY,aAAA;EAaF;;;EATR,MAAA;IACE,IAAA;IACA,IAAA;IACA,cAAA;EAAA;EAFA;;;EAQF,KAAA,GAAQ,IAAA,CAAK,YAAA;IAAL;;;;;IAMN,MAAA;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/types.ts"],"mappings":";;;;;;;;AAUA;;;KAAY,aAAA;EAaF;;;EATR,MAAA;IACE,IAAA;IACA,IAAA;IACA,cAAA;EAAA;EAFA;;;EAQF,KAAA,GAAQ,IAAA,CAAK,YAAA;IAAL;;;;;IAMN,MAAA;IAkDA;;;;;;;;;;IAvCA,YAAA;IAqFiB;;;;;IA/EjB,OAAA;IAwIA;;;;;;;AAuBO;;;;;;;;;IA9IP,YAAA;;;;;;;;;;;;;;;;IAgBA,OAAA;;;;;;IAMA,MAAA;;;;;;IAMA,SAAA;EAAA;;;;EAMF,GAAA;IACE,QAAA,GAAW,UAAA;EAAA;;;;EAMb,SAAA;;;;IAIE,KAAA;;;;;;MAME,OAAA;;;;;;MAMA,OAAA;IAAA;;;;IAKF,cAAA,GAAiB,yBAAA;;;;;IAKjB,eAAA;;;;;;;;;IASA,eAAA;;;;;;;;;IASA,qBAAA;;;;;;;;;;IAUA,mBAAA;EAAA;;;;EAMF,QAAA;;;;;;;;;;;;;;;;;;IAkBE,UAAA,GAAa,KAAA,CAAM,oBAAA;EAAA;;;;;;;EASrB,OAAA;;;;;;;IAOE,OAAA;;;;;;IAOA,OAAA;EAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;AAeA;;;;AAAwD;AAaxD;iBAbgB,yBAAA,CAA0B,KAAc;;;;;;;cAa3C,oBAAA;EAyHR;;;EAAA,QArHK,MAAA;EAyIS;;;EAAA,QApIT,OAAA;EAqIL;;;;;EA9HU,IAAA,IAAQ,OAAA,CAAQ,aAAA;EAPrB;;;;;;EAAA,QAgCM,MAAA;EA6EH;;;;;;;;EAAA,QAtCG,cAAA;EA2DR;;;;;;;;;;;EArBC,GAAA,mBAAsB,aAAA,EAC3B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,aAAA,CAAc,GAAA;EAsCV;;;;;AAWuB;AAoBhC;;;;EAnDQ,OAAA,mBAA0B,aAAA,EAC9B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,OAAA,CAAQ,aAAA,CAAc,GAAA;;;;MAQd,QAAA;;;;;;EASJ,MAAA,IAAU,aAAA;;;;EAWJ,MAAA,IAAU,OAAA;AAAA;;;;;;;;;;;;;;cAoBZ,oBAAA,EAAoB,oBAA6B"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { rootPath } from "../utils/paths.mjs";
|
|
2
2
|
import "../utils/index.mjs";
|
|
3
|
-
import { devLogWarn } from "../dev-server/dev-logger.mjs";
|
|
4
3
|
import { get } from "@mongez/reinforcements";
|
|
5
4
|
import { fileExistsAsync } from "@warlock.js/fs";
|
|
6
5
|
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
@@ -47,7 +46,7 @@ var WarlockConfigManager = class {
|
|
|
47
46
|
async doLoad() {
|
|
48
47
|
const configPath = rootPath("warlock.config.ts");
|
|
49
48
|
if (!await fileExistsAsync(configPath)) {
|
|
50
|
-
|
|
49
|
+
console.warn("warlock.config.ts is missing — run `warlock init` to create it");
|
|
51
50
|
return;
|
|
52
51
|
}
|
|
53
52
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport {
|
|
1
|
+
{"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport { rootPath } from \"../utils\";\r\nimport { WarlockConfig } from \"./types\";\r\n\r\n/**\r\n * True when `error` is Node's \"this runtime can't import a `.ts` file\" error.\r\n *\r\n * Node only executes TypeScript natively from v22.18 / v23.6 onward (earlier\r\n * 22.x needs `--experimental-strip-types`). On older runtimes a bare\r\n * `import(\"warlock.config.ts\")` throws `ERR_UNKNOWN_FILE_EXTENSION` — which is\r\n * the signal to fall back to transpiling the config ourselves.\r\n */\r\nexport function isUnknownTsExtensionError(error: unknown): boolean {\r\n return (\r\n (error as { code?: string })?.code === \"ERR_UNKNOWN_FILE_EXTENSION\" ||\r\n /Unknown file extension \"\\.tsx?\"/.test(String((error as Error)?.message))\r\n );\r\n}\r\n\r\n/**\r\n * Warlock Config Manager\r\n *\r\n * Manages lazy loading of the pre-compiled warlock.config.js file\r\n * from the .warlock/cache directory.\r\n */\r\nexport class WarlockConfigManager {\r\n /**\r\n * Cached config instance\r\n */\r\n private config?: WarlockConfig;\r\n\r\n /**\r\n * Loading promise to prevent duplicate loads\r\n */\r\n private loading?: Promise<WarlockConfig | undefined>;\r\n\r\n /**\r\n * Load warlock.config.js (cached after first load)\r\n *\r\n * @returns The resolved Warlock configuration\r\n */\r\n public async load(): Promise<WarlockConfig | undefined> {\r\n // Already loaded\r\n if (this.config) {\r\n return this.config;\r\n }\r\n\r\n // Currently loading (prevent duplicate loads)\r\n if (this.loading) {\r\n return this.loading;\r\n }\r\n\r\n // Start loading\r\n this.loading = this.doLoad();\r\n this.config = await this.loading;\r\n this.loading = undefined;\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Internal load implementation\r\n *\r\n * The ESM loader hook transpiles `warlock.config.ts` on import — no\r\n * separate compile-to-disk step is needed.\r\n */\r\n private async doLoad(): Promise<WarlockConfig | undefined> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n // Written straight to the console, NOT through `@warlock.js/logger`.\r\n // This runs during CLI bootstrap, before `LoggerConnector.start()` has\r\n // configured a single channel, so a logger call here reaches nobody in\r\n // any application — the warning would be silently dropped exactly when\r\n // the user most needs it.\r\n console.warn(\"warlock.config.ts is missing — run `warlock init` to create it\");\r\n return;\r\n }\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(configPath).href);\r\n return configModule.default;\r\n } catch (error) {\r\n // `dev` registers a TS loader hook, and Node ≥ 22.18 / 23.6 strips types\r\n // natively — in both cases the direct import above works. But `build` /\r\n // `start` register no hook, so on an older Node the import throws\r\n // ERR_UNKNOWN_FILE_EXTENSION. Transpile the config with esbuild ourselves\r\n // so config loading is Node-version-independent rather than relying on an\r\n // experimental runtime feature.\r\n if (isUnknownTsExtensionError(error)) {\r\n return await this.loadViaEsbuild(configPath);\r\n }\r\n\r\n throw new Error(`Failed to load warlock.config.ts: ${error}`);\r\n }\r\n }\r\n\r\n /**\r\n * Fallback config loader for runtimes without native TypeScript support.\r\n *\r\n * Transpiles `warlock.config.ts` with esbuild (already a core dependency),\r\n * writes the result as a sibling `.mjs` — so the config's own imports (bare\r\n * `@warlock.js/*` and any relative paths) resolve exactly as they would from\r\n * the original location — imports it, then removes the temp file.\r\n */\r\n private async loadViaEsbuild(configPath: string): Promise<WarlockConfig | undefined> {\r\n const { transformSync } = await import(\"esbuild\");\r\n const source = await readFile(configPath, \"utf8\");\r\n\r\n const { code } = transformSync(source, {\r\n loader: \"ts\",\r\n format: \"esm\",\r\n target: `node${process.versions.node.split(\".\")[0]}`,\r\n sourcefile: configPath,\r\n });\r\n\r\n // Unique sibling name so concurrent processes never clobber each other.\r\n const compiledPath = configPath.replace(\r\n /warlock\\.config\\.ts$/,\r\n `.warlock.config.${process.pid}.mjs`,\r\n );\r\n\r\n await writeFile(compiledPath, code, \"utf8\");\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(compiledPath).href);\r\n return configModule.default;\r\n } finally {\r\n await unlink(compiledPath).catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * Get config value by key (dot notation supported)\r\n *\r\n * @example\r\n * config.get(\"server.port\") // Returns 3000\r\n * config.get(\"cli.commands\") // Returns array of commands\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @returns The config value\r\n * @throws Error if config is not loaded\r\n */\r\n public get<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): WarlockConfig[Key] {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return get(this.config, key as string, defaultValue);\r\n }\r\n\r\n /**\r\n * Lazy get - loads config if not already loaded\r\n *\r\n * @example\r\n * const port = await config.lazyGet(\"server\");\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @param defaultValue - Default value if config key is undefined\r\n * @returns The config value\r\n */\r\n async lazyGet<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): Promise<WarlockConfig[Key]> {\r\n await this.load();\r\n return this.get(key, defaultValue);\r\n }\r\n\r\n /**\r\n * Check if config is loaded\r\n */\r\n public get isLoaded(): boolean {\r\n return this.config !== undefined;\r\n }\r\n\r\n /**\r\n * Get the entire config object\r\n *\r\n * @throws Error if config is not loaded\r\n */\r\n public getAll(): WarlockConfig {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Reload config (useful for HMR/development)\r\n */\r\n public async reload(): Promise<void> {\r\n this.config = undefined;\r\n this.loading = undefined;\r\n await this.load();\r\n }\r\n}\r\n\r\n/**\r\n * Exported singleton instance\r\n *\r\n * @example\r\n * import { warlockConfig } from \"@warlock.js/core\";\r\n *\r\n * // Lazy load and get value\r\n * const port = await warlockConfig.lazyGet(\"server.port\");\r\n *\r\n * // Or load first, then get\r\n * await warlockConfig.load();\r\n * const commands = warlockConfig.get(\"cli.commands\");\r\n */\r\nexport const warlockConfigManager = new WarlockConfigManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,0BAA0B,OAAyB;CACjE,OACG,OAA6B,SAAS,gCACvC,kCAAkC,KAAK,OAAQ,OAAiB,OAAO,CAAC;AAE5E;;;;;;;AAQA,IAAa,uBAAb,MAAkC;;;;;;CAgBhC,MAAa,OAA2C;EAEtD,IAAI,KAAK,QACP,OAAO,KAAK;EAId,IAAI,KAAK,SACP,OAAO,KAAK;EAId,KAAK,UAAU,KAAK,OAAO;EAC3B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,UAAU;EAEf,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,SAA6C;EACzD,MAAM,aAAa,SAAS,mBAAmB;EAE/C,IAAI,CAAE,MAAM,gBAAgB,UAAU,GAAI;GAMxC,QAAQ,KAAK,gEAAgE;GAC7E;EACF;EAEA,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,UAAU,CAAC,CAAC,MACzC,CAAC;EACtB,SAAS,OAAO;GAOd,IAAI,0BAA0B,KAAK,GACjC,OAAO,MAAM,KAAK,eAAe,UAAU;GAG7C,MAAM,IAAI,MAAM,qCAAqC,OAAO;EAC9D;CACF;;;;;;;;;CAUA,MAAc,eAAe,YAAwD;EACnF,MAAM,EAAE,kBAAkB,MAAM,OAAO;EAGvC,MAAM,EAAE,SAAS,cAAc,MAFV,SAAS,YAAY,MAAM,GAET;GACrC,QAAQ;GACR,QAAQ;GACR,QAAQ,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC;GAChD,YAAY;EACd,CAAC;EAGD,MAAM,eAAe,WAAW,QAC9B,wBACA,mBAAmB,QAAQ,IAAI,KACjC;EAEA,MAAM,UAAU,cAAc,MAAM,MAAM;EAE1C,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,YAAY,CAAC,CAAC,MAC3C,CAAC;EACtB,UAAU;GACR,MAAM,OAAO,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC;EAC3C;CACF;;;;;;;;;;;;CAaA,AAAO,IACL,KACA,cACoB;EACpB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,IAAI,KAAK,QAAQ,KAAe,YAAY;CACrD;;;;;;;;;;;CAYA,MAAM,QACJ,KACA,cAC6B;EAC7B,MAAM,KAAK,KAAK;EAChB,OAAO,KAAK,IAAI,KAAK,YAAY;CACnC;;;;CAKA,IAAW,WAAoB;EAC7B,OAAO,KAAK,WAAW;CACzB;;;;;;CAOA,AAAO,SAAwB;EAC7B,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,KAAK;CACd;;;;CAKA,MAAa,SAAwB;EACnC,KAAK,SAAS;EACd,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;CAClB;AACF;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB,IAAI,qBAAqB"}
|
package/llms-full.txt
CHANGED
|
@@ -2439,7 +2439,7 @@ For an even smoother handoff, give the load balancer time to observe the 503 bef
|
|
|
2439
2439
|
|
|
2440
2440
|
---
|
|
2441
2441
|
name: lower-stage3-decorators
|
|
2442
|
-
description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.'
|
|
2442
|
+
description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core/vite"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.'
|
|
2443
2443
|
---
|
|
2444
2444
|
|
|
2445
2445
|
# Lower Stage-3 decorators for Vite/Vitest
|
|
@@ -2454,12 +2454,14 @@ The cause is two transforms that don't understand each other. Vite 8 transpiles
|
|
|
2454
2454
|
|
|
2455
2455
|
`lowerStage3Decorators()` fixes it by getting **esbuild** to lower the decorators *first*.
|
|
2456
2456
|
|
|
2457
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** The plugin used to be re-exported from the package root; it is not any more, because that put the Vite integration into every application's production module graph. `import { lowerStage3Decorators } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/vite` to the specifier and nothing else changes.**
|
|
2458
|
+
|
|
2457
2459
|
## Use it
|
|
2458
2460
|
|
|
2459
2461
|
Drop it **first** in your `plugins` array:
|
|
2460
2462
|
|
|
2461
2463
|
```ts title="vite.config.ts"
|
|
2462
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
2464
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
2463
2465
|
import mongezVite from "@mongez/vite";
|
|
2464
2466
|
import { defineConfig } from "vitest/config";
|
|
2465
2467
|
|
|
@@ -2519,7 +2521,9 @@ await new Image("./photo.jpg")
|
|
|
2519
2521
|
.save("./output.webp");
|
|
2520
2522
|
```
|
|
2521
2523
|
|
|
2522
|
-
That's the full contract. The chain doesn't
|
|
2524
|
+
That's the full contract. The chain doesn't run sharp until the output method fires; the
|
|
2525
|
+
constructor resolves the sharp module itself, so a missing sharp throws there rather than at the
|
|
2526
|
+
final await.
|
|
2523
2527
|
|
|
2524
2528
|
## Installation
|
|
2525
2529
|
|
|
@@ -3285,6 +3289,8 @@ export default defineConfig({
|
|
|
3285
3289
|
outFile: "app.js", // default — bundle filename
|
|
3286
3290
|
minify: true, // default — esbuild minify
|
|
3287
3291
|
sourcemap: true, // default — true | false | "inline" | "linked"
|
|
3292
|
+
singleBundle: false, // default — one runnable file, deps inlined
|
|
3293
|
+
esmShim: true, // default — require/__filename/__dirname for bundled CJS
|
|
3288
3294
|
},
|
|
3289
3295
|
});
|
|
3290
3296
|
```
|
|
@@ -3296,6 +3302,10 @@ Defaults are sensible for the typical "Node service" deployment. Knobs to actual
|
|
|
3296
3302
|
- **`minify: false`** — flip to debug a production-only bug. Larger bundle, readable stack traces.
|
|
3297
3303
|
- **`sourcemap: "inline"`** — embed the source map in the bundle. Useful when your error reporter only captures the bundle and can't fetch a `.map` sidecar.
|
|
3298
3304
|
- **`sourcemap: false`** — skip source maps entirely. Smaller artifact, but stack traces in production logs lose their file:line precision (and `warlock start` will not enable `--enable-source-maps` since there's nothing to map).
|
|
3305
|
+
- **`singleBundle: true`** — inline dependencies so `node dist/app.js` runs with no `node_modules` and no launcher. Sets `packages: "bundle"` + `splitting: false` as **defaults you can override**. ⚠ Native `.node` addons are still emitted alongside — "single bundle" is one JS file *plus* any native addons. Do NOT reach for it as the default: keeping deps external is right when you deploy the folder.
|
|
3306
|
+
- **`esmShim: false`** — only if you are certain nothing in the graph is CommonJS. Leave it on.
|
|
3307
|
+
|
|
3308
|
+
⚠ **The trap this replaced.** Setting `packages: "bundle"` by hand used to produce a clean build whose process died on start with `Error: Dynamic require of "node:assert" is not supported`. The output is ESM; bundled CJS deps call `require(...)` and read `__dirname`, and neither exists in an ES module, so the bundler substitutes a throwing stub. **A green `warlock build` was not evidence the bundle ran.** `esmShim` now injects `createRequire(import.meta.url)` and friends automatically for any ESM build, so both `singleBundle` and a hand-written `packages: "bundle"` work. An existing hand-written `banner` is preserved — the shim is prepended, not substituted.
|
|
3299
3309
|
|
|
3300
3310
|
### What it preloads
|
|
3301
3311
|
|
|
@@ -4556,20 +4566,22 @@ Client `PUT`s the bytes straight to R2 — your server never sees them.
|
|
|
4556
4566
|
|
|
4557
4567
|
---
|
|
4558
4568
|
name: test-http
|
|
4559
|
-
description: 'Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.'
|
|
4569
|
+
description: 'Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core/tests"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.'
|
|
4560
4570
|
---
|
|
4561
4571
|
|
|
4562
4572
|
# Warlock — HTTP integration tests
|
|
4563
4573
|
|
|
4564
4574
|
Some tests need the full stack: route matching, middleware chain, validation, controller, response serialization. For those, you boot the real HTTP server once per test run and make real `fetch` calls against it.
|
|
4565
4575
|
|
|
4566
|
-
`startHttpTestServer()` is the bootstrap. `testGet` / `testPost` / `expectJson` are the call helpers. Both ship in `@warlock.js/core`.
|
|
4576
|
+
`startHttpTestServer()` is the bootstrap. `testGet` / `testPost` / `expectJson` are the call helpers. Both ship in `@warlock.js/core/tests`.
|
|
4577
|
+
|
|
4578
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** These helpers used to be re-exported from the package root; they are not any more, because that put the test helpers into every application's production module graph. `import { testGet } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/tests` to the specifier and nothing else changes.**
|
|
4567
4579
|
|
|
4568
4580
|
## The shape
|
|
4569
4581
|
|
|
4570
4582
|
```ts title="src/app/users/tests/users.controller.test.ts"
|
|
4571
4583
|
import { describe, expect, it } from "vitest";
|
|
4572
|
-
import { expectJson, testGet, testPost } from "@warlock.js/core";
|
|
4584
|
+
import { expectJson, testGet, testPost } from "@warlock.js/core/tests";
|
|
4573
4585
|
|
|
4574
4586
|
describe("Users API", () => {
|
|
4575
4587
|
it("GET /users returns the list", async () => {
|
|
@@ -4595,7 +4607,7 @@ No `beforeAll`, no manual server start — the project's `src/test-global-setup.
|
|
|
4595
4607
|
## The bootstrap — `startHttpTestServer` / `stopHttpTestServer`
|
|
4596
4608
|
|
|
4597
4609
|
```ts
|
|
4598
|
-
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core";
|
|
4610
|
+
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core/tests";
|
|
4599
4611
|
```
|
|
4600
4612
|
|
|
4601
4613
|
`startHttpTestServer()` boots a **minimal but real** HTTP server:
|
|
@@ -4636,6 +4648,23 @@ startHttpTestServer({ port: 2032 }).
|
|
|
4636
4648
|
|
|
4637
4649
|
This runs whether or not you passed a port, so a collision never reaches you as a bare `EADDRINUSE` from inside Fastify. The failure is a `PortInUseError` carrying `port` and `host`. The same check is available on its own — `assertPortIsAvailable(port, host)` throws it, `isPortAvailable(port, host)` returns a boolean.
|
|
4638
4650
|
|
|
4651
|
+
### Port `0` is refused
|
|
4652
|
+
|
|
4653
|
+
`port: 0` — "let the OS pick one" — is not supported, and says so rather than half-working:
|
|
4654
|
+
|
|
4655
|
+
```
|
|
4656
|
+
startHttpTestServer() cannot run on port 0. Pass an explicit port — e.g.
|
|
4657
|
+
startHttpTestServer({ port: 3999 }) — or set `http.port` in your config. Test workers
|
|
4658
|
+
are separate processes that resolve the server's URL from the port published at
|
|
4659
|
+
startup, and an OS-assigned port is not knowable to them.
|
|
4660
|
+
```
|
|
4661
|
+
|
|
4662
|
+
The reason is the worker handoff described above: the port the OS assigns is never recorded anywhere the workers can read, so `getTestServerUrl()` would resolve `0` and send every request to `http://host:0`.
|
|
4663
|
+
|
|
4664
|
+
### If startup fails, nothing is left behind
|
|
4665
|
+
|
|
4666
|
+
`startHttpTestServer()` owns the connectors it starts. A failure part-way through — a bad config, a connector that won't boot — tears down whatever came up, withdraws the published port, and **rethrows the original error**; a failure during that cleanup is logged but never replaces the cause. `stopHttpTestServer()` withdraws the port and resets its state even when the shutdown itself throws, and still surfaces that failure.
|
|
4667
|
+
|
|
4639
4668
|
## Project wiring — `src/test-global-setup.ts` + `vite.config.ts`
|
|
4640
4669
|
|
|
4641
4670
|
```ts title="src/test-global-setup.ts"
|
|
@@ -4643,7 +4672,7 @@ This runs whether or not you passed a port, so a collision never reaches you as
|
|
|
4643
4672
|
* Global Test Setup
|
|
4644
4673
|
* Runs ONCE in the main process before all test workers start.
|
|
4645
4674
|
*/
|
|
4646
|
-
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core";
|
|
4675
|
+
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core/tests";
|
|
4647
4676
|
|
|
4648
4677
|
export async function setup() {
|
|
4649
4678
|
await startHttpTestServer();
|
|
@@ -4655,7 +4684,7 @@ export async function teardown() {
|
|
|
4655
4684
|
```
|
|
4656
4685
|
|
|
4657
4686
|
```ts title="vite.config.ts"
|
|
4658
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
4687
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
4659
4688
|
import mongezVite from "@mongez/vite";
|
|
4660
4689
|
import { defineConfig } from "vitest/config";
|
|
4661
4690
|
|
|
@@ -4680,7 +4709,7 @@ Everything is built on native `fetch` — no extra dependency, no special wire f
|
|
|
4680
4709
|
### URL resolution
|
|
4681
4710
|
|
|
4682
4711
|
```ts
|
|
4683
|
-
import { getTestServerUrl } from "@warlock.js/core";
|
|
4712
|
+
import { getTestServerUrl } from "@warlock.js/core/tests";
|
|
4684
4713
|
|
|
4685
4714
|
const url = getTestServerUrl(); // → "http://localhost:2031" (defaults)
|
|
4686
4715
|
```
|
|
@@ -4697,7 +4726,7 @@ import {
|
|
|
4697
4726
|
testPut,
|
|
4698
4727
|
testPatch,
|
|
4699
4728
|
testDelete,
|
|
4700
|
-
} from "@warlock.js/core";
|
|
4729
|
+
} from "@warlock.js/core/tests";
|
|
4701
4730
|
|
|
4702
4731
|
await testGet("/products");
|
|
4703
4732
|
await testGet("/products?published=true", { headers: { "X-Tenant": "abc" } });
|
|
@@ -4714,7 +4743,7 @@ All accept a relative path (leading `/` optional) and a standard `RequestInit`.
|
|
|
4714
4743
|
### Parsing + asserting — `expectJson<T>`
|
|
4715
4744
|
|
|
4716
4745
|
```ts
|
|
4717
|
-
import { expectJson, parseJsonResponse } from "@warlock.js/core";
|
|
4746
|
+
import { expectJson, parseJsonResponse } from "@warlock.js/core/tests";
|
|
4718
4747
|
|
|
4719
4748
|
// Parse-only
|
|
4720
4749
|
const body = await parseJsonResponse<MyShape>(response);
|
|
@@ -4733,7 +4762,7 @@ const body = await expectJson<MyShape>(response, 404); // expects 404 (testi
|
|
|
4733
4762
|
|
|
4734
4763
|
```ts
|
|
4735
4764
|
import { describe, expect, it } from "vitest";
|
|
4736
|
-
import { expectJson, testGet, testPost } from "@warlock.js/core";
|
|
4765
|
+
import { expectJson, testGet, testPost } from "@warlock.js/core/tests";
|
|
4737
4766
|
|
|
4738
4767
|
describe("Products API — happy path", () => {
|
|
4739
4768
|
it("creates and reads back a product", async () => {
|
|
@@ -4872,7 +4901,7 @@ This is fine for normal test flow. It bites when you're inside a transaction the
|
|
|
4872
4901
|
|
|
4873
4902
|
---
|
|
4874
4903
|
name: test-service
|
|
4875
|
-
description: 'Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.'
|
|
4904
|
+
description: 'Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core/tests"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.'
|
|
4876
4905
|
---
|
|
4877
4906
|
|
|
4878
4907
|
# Warlock — test a service
|
|
@@ -4881,6 +4910,8 @@ For unit tests, you import the thing under test and call it directly. No HTTP, n
|
|
|
4881
4910
|
|
|
4882
4911
|
`setupTest()` is the one-call bootstrap that gives each worker that environment.
|
|
4883
4912
|
|
|
4913
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** `setupTest` used to be re-exported from the package root; it is not any more, because that put the test helpers into every application's production module graph. `import { setupTest } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/tests` to the specifier and nothing else changes.**
|
|
4914
|
+
|
|
4884
4915
|
## The shape
|
|
4885
4916
|
|
|
4886
4917
|
```ts title="src/app/users/tests/register-user.service.test.ts"
|
|
@@ -4909,7 +4940,7 @@ No `beforeAll(setupTest)` in this file — the project's `src/test-setup.ts` (re
|
|
|
4909
4940
|
## `setupTest({ connectors })` — the worker bootstrap
|
|
4910
4941
|
|
|
4911
4942
|
```ts
|
|
4912
|
-
import { setupTest } from "@warlock.js/core";
|
|
4943
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
4913
4944
|
|
|
4914
4945
|
await setupTest({ connectors: true });
|
|
4915
4946
|
```
|
|
@@ -4957,13 +4988,13 @@ The `warlock add test` feature creates both files. The standard wiring:
|
|
|
4957
4988
|
* Per-Worker Test Setup
|
|
4958
4989
|
* Runs in EACH Vitest worker thread before tests execute.
|
|
4959
4990
|
*/
|
|
4960
|
-
import { setupTest } from "@warlock.js/core";
|
|
4991
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
4961
4992
|
|
|
4962
4993
|
await setupTest({ connectors: true });
|
|
4963
4994
|
```
|
|
4964
4995
|
|
|
4965
4996
|
```ts title="vite.config.ts"
|
|
4966
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
4997
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
4967
4998
|
import mongezVite from "@mongez/vite";
|
|
4968
4999
|
import { defineConfig } from "vitest/config";
|
|
4969
5000
|
|
|
@@ -5082,11 +5113,11 @@ Vitest runs tests in a single worker file sequentially, so an `afterEach` trunca
|
|
|
5082
5113
|
|
|
5083
5114
|
```ts title="src/app/utils/tests/slugify.test.ts"
|
|
5084
5115
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
5085
|
-
import { setupTest } from "@warlock.js/core";
|
|
5116
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
5086
5117
|
import { slugify } from "../utils/slugify";
|
|
5087
5118
|
|
|
5088
5119
|
beforeAll(async () => {
|
|
5089
|
-
await setupTest({ connectors: false }); //
|
|
5120
|
+
await setupTest({ connectors: false }); // starts no connectors at all
|
|
5090
5121
|
});
|
|
5091
5122
|
|
|
5092
5123
|
describe("slugify", () => {
|
|
@@ -5096,7 +5127,9 @@ describe("slugify", () => {
|
|
|
5096
5127
|
});
|
|
5097
5128
|
```
|
|
5098
5129
|
|
|
5099
|
-
`setupTest` is idempotent per worker (`isSetupComplete` flag) — calling it again with different options after `src/test-setup.ts` already ran is a no-op. To genuinely skip connectors, either set `tests.connectors: false` in config (project-wide) or rely on the default in `src/test-setup.ts` being what you want most of the time.
|
|
5130
|
+
`setupTest` is idempotent per worker (`isSetupComplete` flag) — calling it again with different options after `src/test-setup.ts` already ran is a no-op. **That includes a `connectors: false` call: if `src/test-setup.ts` already ran `setupTest()` in this worker, the example above changes nothing.** To genuinely skip connectors, either set `tests.connectors: false` in config (project-wide) or rely on the default in `src/test-setup.ts` being what you want most of the time.
|
|
5131
|
+
|
|
5132
|
+
⚠ **Config beats the parameter.** If `tests.connectors` is set at all, `setupTest({ connectors })` cannot override it — the config value wins. That is the current contract, not an accident; a per-call override is under discussion for a later release.
|
|
5100
5133
|
|
|
5101
5134
|
## Gotchas
|
|
5102
5135
|
|
|
@@ -5104,7 +5137,7 @@ describe("slugify", () => {
|
|
|
5104
5137
|
- **Per-worker connections are separate from the HTTP server's connections.** A row inserted by a service-level test is on the worker's connection; the HTTP test server has its own. They don't see each other unless they're both pointing at the same physical DB and the inserting test has already committed.
|
|
5105
5138
|
- **`NODE_ENV` is set to `"test"`** by `setupTest`. Code that branches on `Application.isProduction` / `Application.isDevelopment` sees `false` for both. If your tests need production-like config (cookies, CORS), set those values in `src/config/*.ts` explicitly under the test branch — don't rely on the env flag.
|
|
5106
5139
|
- **No HTTP from this layer.** `setupTest({ connectors: true })` never starts the HTTP connector by design. Don't try to `request.app.http` your way to a fetch test — use the `test-http` skill instead.
|
|
5107
|
-
- **Don't import `vitest-setup` from `@warlock.js/core/src/...`.** The public surface is `import { setupTest } from "@warlock.js/core"`. Reaching into source paths breaks when the package layout shifts.
|
|
5140
|
+
- **Don't import `vitest-setup` from `@warlock.js/core/src/...`.** The public surface is `import { setupTest } from "@warlock.js/core/tests"`. Reaching into source paths breaks when the package layout shifts.
|
|
5108
5141
|
- **Test files need the `.test.ts` suffix.** `include: ["src/app/**/*.test.ts"]` is what vitest scans. A file named `service.tests.ts` (plural) silently doesn't run.
|
|
5109
5142
|
|
|
5110
5143
|
## See also
|
|
@@ -6241,6 +6274,8 @@ Server errors (5xx) are not cached — clients can retry past a 5xx. 4xx respons
|
|
|
6241
6274
|
|
|
6242
6275
|
`http.bodyLimit` in config is read by Fastify at server-start and applies to every body. `middleware.maxBodySize()` is a per-route middleware on top — it checks `Content-Length` after route match and rejects with 413 before body parsing runs. Use both: global as a safety net, per-route for tight caps on small-payload endpoints.
|
|
6243
6276
|
|
|
6277
|
+
⚠ **Since 4.13.0, configuring nothing gives you Fastify's own 1 MB limit.** Earlier versions defaulted to 200 GB, which **replaced** Fastify's protection rather than adding to it — so an app that set nothing had effectively no cap. **Set `bodyLimit` explicitly if you need more than 1 MB.**
|
|
6278
|
+
|
|
6244
6279
|
```ts
|
|
6245
6280
|
// src/config/http.ts
|
|
6246
6281
|
export default { bodyLimit: 10 * 1024 * 1024 }; // 10MB globally
|
|
@@ -6271,7 +6306,9 @@ export default {
|
|
|
6271
6306
|
|
|
6272
6307
|
## `ipFilter` — fail-closed
|
|
6273
6308
|
|
|
6274
|
-
`deny` wins over `allow`. If the IP can't be read (empty / unparseable), the request is rejected with 403. Reads via `request.detectIp()
|
|
6309
|
+
`deny` wins over `allow`. If the IP can't be read (empty / unparseable), the request is rejected with 403. Reads via `request.detectIp()`.
|
|
6310
|
+
|
|
6311
|
+
⚠ **Since 4.13.0 `http.trustProxy` defaults to `false`**, so `request.detectIp()` returns the socket address and **`X-Real-IP` / `X-Forwarded-For` are ignored unless you opt in.** Set `trustProxy: true` **only when you are genuinely behind a proxy that overwrites those headers** — before 4.13.0 the default was `true`, which meant any client could set its own forwarding header and be believed.
|
|
6275
6312
|
|
|
6276
6313
|
```ts
|
|
6277
6314
|
import { middleware } from "@warlock.js/core";
|
|
@@ -8151,12 +8188,30 @@ The framework ships a fixed set of commands you call but don't author. Knowing t
|
|
|
8151
8188
|
|
|
8152
8189
|
| Command | Flags / args | Preloads |
|
|
8153
8190
|
| -------- | ----------------------------------------------- | ------------------------------ |
|
|
8154
|
-
| `warlock migrate` | `--list` (
|
|
8191
|
+
| `warlock migrate` | `--list` / `-l` (executed **and** pending), `--pending` (pending only, sets an exit code), `--fresh` / `-f` (drop tables first) | database, logger |
|
|
8155
8192
|
| `warlock seed` | `--name <pattern>` (run seeds matching the pattern) | full bootstrap (env, configs, app modules) |
|
|
8156
8193
|
| `warlock create-database <name>` | bare positional `<name>` | database |
|
|
8157
8194
|
| `warlock drop.tables` | `--force, -f` (skip confirmation prompt) | database, logger |
|
|
8158
8195
|
| `warlock db.indexes` | builds DB indexes for every registered model | database |
|
|
8159
8196
|
|
|
8197
|
+
**Asking what will run next.** `warlock migrate --list` prints executed migrations and then the pending ones **in execution order**. Do not derive the pending set by differencing `--all` against `--list`: `--all` globs `src/app` only, while `--list` reads the migrations table, which also holds migrations that packages register through `database.migrations` (`@warlock.js/auth` contributes two). The difference under-counts pending, in the direction that says "safe to proceed".
|
|
8198
|
+
|
|
8199
|
+
`--list` is a report and always exits `0`. `--pending` is a gate, and its exit code is its whole API:
|
|
8200
|
+
|
|
8201
|
+
| Exit | Meaning |
|
|
8202
|
+
| ---- | ------- |
|
|
8203
|
+
| `0` | computed, nothing pending |
|
|
8204
|
+
| `1` | computed, N pending |
|
|
8205
|
+
| `2` | **could not be computed** |
|
|
8206
|
+
|
|
8207
|
+
```bash
|
|
8208
|
+
warlock migrate --pending && ./deploy.sh
|
|
8209
|
+
```
|
|
8210
|
+
|
|
8211
|
+
`2` is separate from `1` on purpose — a script must be able to tell a backlog from an unknown, because one means *run them* and the other means *stop*. When the migration files cannot be loaded, both commands print `Pending: unavailable — <reason>` beneath a complete executed listing rather than reporting `0`.
|
|
8212
|
+
|
|
8213
|
+
**If you are writing a command that reports on pending migrations:** register migrations first. `listPendingMigrations()` filters the runner's registry, so a caller that has not loaded anything gets `[]` — which reads as "nothing pending" and is not the same claim.
|
|
8214
|
+
|
|
8160
8215
|
### Scaffolding
|
|
8161
8216
|
|
|
8162
8217
|
The `generate.*` family covers every module piece:
|