@warlock.js/core 4.9.2 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/esm/application/application.d.mts +19 -0
  3. package/esm/application/application.d.mts.map +1 -1
  4. package/esm/application/application.mjs +25 -0
  5. package/esm/application/application.mjs.map +1 -1
  6. package/esm/application/boot-signal.d.mts +77 -0
  7. package/esm/application/boot-signal.d.mts.map +1 -0
  8. package/esm/application/boot-signal.mjs +64 -0
  9. package/esm/application/boot-signal.mjs.map +1 -0
  10. package/esm/application/index.d.mts +1 -0
  11. package/esm/application/index.mjs +2 -1
  12. package/esm/bootstrap.mjs +2 -2
  13. package/esm/bootstrap.mjs.map +1 -1
  14. package/esm/cli/cli-commands.manager.mjs +3 -3
  15. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  16. package/esm/cli/cli-commands.utils.mjs +57 -1
  17. package/esm/cli/cli-commands.utils.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
  19. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  20. package/esm/cli/commands/start-production.command.mjs +4 -25
  21. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  22. package/esm/cli/types.d.mts +6 -1
  23. package/esm/cli/types.d.mts.map +1 -1
  24. package/esm/config/config-setter.d.mts +27 -0
  25. package/esm/config/config-setter.d.mts.map +1 -0
  26. package/esm/config/config-setter.mjs +10 -0
  27. package/esm/config/config-setter.mjs.map +1 -0
  28. package/esm/config/index.mjs +1 -0
  29. package/esm/connectors/http-connector.d.mts.map +1 -1
  30. package/esm/connectors/http-connector.mjs +2 -1
  31. package/esm/connectors/http-connector.mjs.map +1 -1
  32. package/esm/connectors/storage.connector.d.mts +11 -5
  33. package/esm/connectors/storage.connector.d.mts.map +1 -1
  34. package/esm/connectors/storage.connector.mjs +11 -5
  35. package/esm/connectors/storage.connector.mjs.map +1 -1
  36. package/esm/dev-server/loader/load-hook.mjs +1 -1
  37. package/esm/generations/stubs.mjs +1 -1
  38. package/esm/generations/stubs.mjs.map +1 -1
  39. package/esm/http/index.d.mts +1 -0
  40. package/esm/http/index.mjs +1 -0
  41. package/esm/http/port-preflight.d.mts +34 -0
  42. package/esm/http/port-preflight.d.mts.map +1 -0
  43. package/esm/http/port-preflight.mjs +66 -0
  44. package/esm/http/port-preflight.mjs.map +1 -0
  45. package/esm/http/response.d.mts +17 -2
  46. package/esm/http/response.d.mts.map +1 -1
  47. package/esm/http/response.mjs +36 -2
  48. package/esm/http/response.mjs.map +1 -1
  49. package/esm/index.d.mts +6 -2
  50. package/esm/index.mjs +6 -2
  51. package/esm/production/assert-generated-imports.mjs +94 -0
  52. package/esm/production/assert-generated-imports.mjs.map +1 -0
  53. package/esm/production/production-builder.mjs +29 -4
  54. package/esm/production/production-builder.mjs.map +1 -1
  55. package/esm/production/production-supervisor.mjs +108 -0
  56. package/esm/production/production-supervisor.mjs.map +1 -0
  57. package/esm/storage/storage.d.mts +16 -0
  58. package/esm/storage/storage.d.mts.map +1 -1
  59. package/esm/storage/storage.mjs +24 -1
  60. package/esm/storage/storage.mjs.map +1 -1
  61. package/esm/tests/index.d.mts +2 -1
  62. package/esm/tests/index.mjs +1 -0
  63. package/esm/tests/start-http-development-server.d.mts +15 -2
  64. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  65. package/esm/tests/start-http-development-server.mjs +43 -16
  66. package/esm/tests/start-http-development-server.mjs.map +1 -1
  67. package/esm/tests/test-helpers.d.mts.map +1 -1
  68. package/esm/tests/test-helpers.mjs +2 -1
  69. package/esm/tests/test-helpers.mjs.map +1 -1
  70. package/esm/tests/test-server-port-channel.d.mts +27 -0
  71. package/esm/tests/test-server-port-channel.d.mts.map +1 -0
  72. package/esm/tests/test-server-port-channel.mjs +32 -0
  73. package/esm/tests/test-server-port-channel.mjs.map +1 -0
  74. package/esm/utils/load-environment.mjs +59 -0
  75. package/esm/utils/load-environment.mjs.map +1 -0
  76. package/llms-full.txt +113 -7
  77. package/llms.txt +1 -1
  78. package/package.json +13 -13
  79. package/skills/run-app/SKILL.md +63 -0
  80. package/skills/send-response/SKILL.md +23 -4
  81. package/skills/test-http/SKILL.md +27 -3
@@ -30,6 +30,62 @@ async function displayStartupBanner({ environment }) {
30
30
  console.log();
31
31
  }
32
32
  /**
33
+ * Announce that the production server is up — printed on **stdout**, and only
34
+ * after the child process reported a completed boot.
35
+ *
36
+ * Stdout is the channel a supervisor or CI gate greps to decide the service is
37
+ * healthy, so nothing optimistic may be written to it: this function is the
38
+ * single place allowed to say "started", and it is called from exactly one
39
+ * place, the `warlock:ready` handler in the `start` command.
40
+ */
41
+ async function displayProductionReadyBanner({ bootDurationMs } = {}) {
42
+ const version = await getWarlockVersion();
43
+ const duration = bootDurationMs ? colors.dim(` in ${bootDurationMs}ms`) : "";
44
+ console.log(` ⚡ ${colors.bold(colors.greenBright("Warlock.js"))} ${colors.dim(`v${version}`)} ${colors.green("✔")} production server started${duration}`);
45
+ console.log();
46
+ }
47
+ /**
48
+ * Report that the production server died before it ever served anything.
49
+ *
50
+ * Written to **both** streams on purpose. Stderr is where a human and a log
51
+ * collector look; stdout is where a supervisor that greps for the success
52
+ * banner looks, and it must find a failure there rather than silence — silence
53
+ * on stdout is what let a boot failure be recorded as a healthy start.
54
+ */
55
+ function displayProductionStartFailure(exitCode) {
56
+ const lines = [
57
+ "",
58
+ ` ${colors.red("✖")} ${colors.bold("warlock start")} failed — the server never finished booting`,
59
+ ` ${colors.dim(`the application process exited with code ${exitCode}`)}`,
60
+ ` ${colors.dim("the cause is printed above, in the application's own output")}`,
61
+ ""
62
+ ];
63
+ for (const line of lines) {
64
+ console.error(line);
65
+ console.log(line);
66
+ }
67
+ }
68
+ /**
69
+ * Note that a running child has not reported readiness yet.
70
+ *
71
+ * Emitted on **stderr** only. The process may be perfectly healthy — an older
72
+ * bundle has no readiness signal at all — so saying anything on stdout would be
73
+ * the very false-green this channel exists to prevent.
74
+ *
75
+ * The wording offers the likely causes rather than asserting one. It cannot
76
+ * distinguish an old bundle from a slow boot from a boot that is about to fail,
77
+ * and an earlier version claimed "the bundle predates readiness reporting" —
78
+ * which was simply wrong when a current bundle was mid-crash, and sent the
79
+ * reader after the wrong thing.
80
+ */
81
+ function displayMissingReadinessNotice(waitedMs) {
82
+ console.error();
83
+ console.error(` ${colors.yellow("!")} still running after ${Math.round(waitedMs / 1e3)}s with no readiness signal`);
84
+ console.error(` ${colors.dim("either the boot is still in progress, or this bundle was built")}`);
85
+ console.error(` ${colors.dim("before readiness reporting — re-run")} ${colors.cyan("warlock build")} ${colors.dim("if the banner never appears")}`);
86
+ console.error();
87
+ }
88
+ /**
33
89
  * Display command execution header
34
90
  */
35
91
  function displayExecutingCommand(commandName) {
@@ -213,5 +269,5 @@ function displayCommandHelp(command) {
213
269
  }
214
270
 
215
271
  //#endregion
216
- export { displayBootError, displayCommandError, displayCommandHelp, displayCommandNotFound, displayCommandSuccess, displayExecutingCommand, displayHelp, displayMissingCommand, displayMissingOptions, displayStartupBanner, displayWarlockVersionInTerminal, isMatchingCommandName };
272
+ export { displayBootError, displayCommandError, displayCommandHelp, displayCommandNotFound, displayCommandSuccess, displayExecutingCommand, displayHelp, displayMissingCommand, displayMissingOptions, displayMissingReadinessNotice, displayProductionReadyBanner, displayProductionStartFailure, displayStartupBanner, displayWarlockVersionInTerminal, isMatchingCommandName };
217
273
  //# sourceMappingURL=cli-commands.utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-commands.utils.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.utils.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { Environment } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\n\nexport function isMatchingCommandName(commandName: string, targetingCommandName: string) {\n return commandName.split(\" \")[0] === targetingCommandName;\n}\n\n/**\n * Display the Warlock.js version banner\n */\nexport async function displayWarlockVersionInTerminal() {\n const version = await getWarlockVersion();\n console.log(`⚡ ${colors.bold(\"Warlock.js\")} ${colors.greenBright(`v${version}`)}`);\n}\n\ntype StartBannerOptions = {\n environment: Environment;\n};\n\nfunction getTextColorMethod(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.yellowBright;\n case \"production\":\n return colors.greenBright;\n case \"test\":\n return colors.blueBright;\n default:\n return colors.whiteBright;\n }\n}\n\n/**\n * Display CLI startup banner\n */\nexport async function displayStartupBanner({ environment }: StartBannerOptions) {\n const version = await getWarlockVersion();\n const textColorMethod = getTextColorMethod(environment);\n console.log(` ⚡ ${colors.bold(textColorMethod(\"Warlock.js\"))} ${colors.dim(`v${version}`)}`);\n console.log();\n}\n\n/**\n * Display command execution header\n */\nexport function displayExecutingCommand(commandName: string) {\n console.log(` ${colors.cyan(\"›\")} Running ${colors.bold(colors.white(commandName))}...`);\n console.log();\n}\n\n/**\n * Display command not found error with optional suggestions\n */\nexport function displayCommandNotFound(commandName: string, suggestions?: string[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Command ${colors.magenta(commandName)} not found`);\n\n if (suggestions && suggestions.length > 0) {\n console.log();\n console.log(` ${colors.yellow(\"Did you mean?\")}`);\n suggestions.forEach((suggestion) => {\n console.log(` ${colors.cyan(\"→\")} ${colors.white(suggestion)}`);\n });\n }\n\n console.log();\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display missing command error\n */\nexport function displayMissingCommand() {\n console.log();\n console.log(` ${colors.red(\"✖\")} No command specified`);\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display command success message\n */\nexport function displayCommandSuccess(commandName: string, durationMs?: number) {\n const duration = durationMs ? colors.dim(` (${durationMs}ms)`) : \"\";\n console.log();\n console.log(\n ` ${colors.green(\"✔\")} ${colors.bold(commandName)} completed successfully${duration}`,\n );\n console.log();\n}\n\n/**\n * Display command error message\n */\nexport function displayCommandError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed`);\n console.log(` ${colors.dim(error.message)}`);\n console.log();\n}\n\n/**\n * Display a FATAL boot/preload failure and exit information.\n *\n * Preload failures — a bad import in a config file, a connector that throws\n * on startup, a missing module export — happen BEFORE the command's run loop\n * exists, so there is nothing to recover into: they are always fatal. Unlike\n * `displayCommandError`, this prints the full stack (not just `error.message`)\n * because the stack names the exact file + line of the offending import, which\n * is the single most useful clue when a config file pulls in a broken module.\n *\n * Surfacing this loudly is the difference between a clear error and a silent\n * hang: an unhandled preload rejection used to escape while the already-started\n * loader worker thread kept the process alive, leaving `warlock dev` frozen\n * just after the banner with no message at all.\n *\n * @example\n * // SyntaxError: The requested module '@warlock.js/cascade' does not\n * // provide an export named 'belongsTo'\n * // at src/app/.../permission.model.ts:2\n */\nexport function displayBootError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed to start`);\n console.log(` ${colors.red(error.message)}`);\n if (error.stack) {\n console.log();\n console.log(colors.dim(error.stack));\n }\n console.log();\n}\n\n/**\n * Display missing required options error\n */\nexport function displayMissingOptions(options: { name: string; text: string }[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Missing required options:`);\n options.forEach((opt) => {\n console.log(` ${colors.yellow(opt.text)} ${colors.dim(`(--${opt.name})`)}`);\n });\n console.log();\n}\n\n/**\n * Command info for help display\n */\nexport type HelpCommandInfo = {\n name: string;\n alias?: string;\n description?: string;\n source: \"framework\" | \"plugin\" | \"project\";\n};\n\n/**\n * Display global help with all commands grouped by source\n */\nexport async function displayHelp(commands: HelpCommandInfo[]) {\n const version = await getWarlockVersion();\n\n console.log();\n console.log(\n ` ⚡ ${colors.bold(colors.yellowBright(\"Warlock.js\"))} CLI ${colors.dim(`v${version}`)}`,\n );\n console.log();\n console.log(\n ` ${colors.bold(\"Usage:\")} ${colors.cyan(\"warlock\")} ${colors.dim(\"<command>\")} ${colors.dim(\"[options]\")}`,\n );\n console.log();\n\n // Group by source\n const grouped: Record<string, HelpCommandInfo[]> = {\n framework: [],\n plugin: [],\n project: [],\n };\n\n commands.forEach((cmd) => {\n grouped[cmd.source]?.push(cmd);\n });\n\n // Display each group\n const groupLabels: Record<string, string> = {\n framework: \"Framework Commands\",\n plugin: \"Plugin Commands\",\n project: \"Project Commands\",\n };\n\n for (const [source, cmds] of Object.entries(grouped)) {\n if (cmds.length === 0) continue;\n\n console.log(` ${colors.bold(colors.white(groupLabels[source]))}`);\n console.log();\n\n // Find max name length for alignment\n const maxLen = Math.max(...cmds.map((c) => c.name.length + (c.alias ? c.alias.length + 4 : 0)));\n\n cmds.forEach((cmd) => {\n const aliasStr = cmd.alias ? colors.dim(` (${cmd.alias})`) : \"\";\n const nameWithAlias = cmd.name + (cmd.alias ? ` (${cmd.alias})` : \"\");\n const padding = \" \".repeat(maxLen - nameWithAlias.length + 2);\n // const desc = cmd.description || colors.dim(\"No description\");\n const desc = cmd.description || \"\";\n console.log(` ${colors.cyan(cmd.name)}${aliasStr}${padding}${desc}`);\n });\n console.log();\n }\n\n // Display global flags\n console.log(` ${colors.bold(colors.white(\"Global Flags\"))}`);\n console.log();\n\n const globalFlags = [\n { flag: \"--help, -h\", description: \"Show help for a command\" },\n { flag: \"--version, -v\", description: \"Show Warlock version\" },\n { flag: \"--no-cache\", description: \"Force reload without cache\" },\n { flag: \"--warm-cache\", description: \"Pre-cache all project commands\" },\n ];\n\n const maxFlagLen = Math.max(...globalFlags.map((f) => f.flag.length));\n\n globalFlags.forEach(({ flag, description }) => {\n const padding = \" \".repeat(maxFlagLen - flag.length + 2);\n console.log(` ${colors.yellow(flag)}${padding}${description}`);\n });\n console.log();\n\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock <command> --help\")} ${colors.dim(\"for command-specific help\")}`,\n );\n console.log();\n}\n\n/**\n * Display help for a specific command\n */\nexport function displayCommandHelp(command: {\n name: string;\n alias?: string;\n description?: string;\n options?: { name: string; text: string; description?: string; required?: boolean }[];\n}) {\n console.log();\n console.log(\n ` ${colors.bold(colors.cyan(command.name))}${command.alias ? colors.dim(` (${command.alias})`) : \"\"}`,\n );\n\n if (command.description) {\n console.log(` ${command.description}`);\n }\n console.log();\n\n if (command.options && command.options.length > 0) {\n console.log(` ${colors.bold(\"Options:\")}`);\n console.log();\n\n const maxLen = Math.max(...command.options.map((o) => o.text.length));\n\n command.options.forEach((opt) => {\n const padding = \" \".repeat(maxLen - opt.text.length + 2);\n const required = opt.required ? colors.red(\" (required)\") : \"\";\n const desc = opt.description || \"\";\n console.log(` ${colors.green(opt.text)}${padding}${desc}${required}`);\n });\n console.log();\n } else {\n console.log(` ${colors.dim(\"No options available\")}`);\n console.log();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-commands.utils.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.utils.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { Environment } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\n\nexport function isMatchingCommandName(commandName: string, targetingCommandName: string) {\n return commandName.split(\" \")[0] === targetingCommandName;\n}\n\n/**\n * Display the Warlock.js version banner\n */\nexport async function displayWarlockVersionInTerminal() {\n const version = await getWarlockVersion();\n console.log(`⚡ ${colors.bold(\"Warlock.js\")} ${colors.greenBright(`v${version}`)}`);\n}\n\ntype StartBannerOptions = {\n environment: Environment;\n};\n\nfunction getTextColorMethod(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.yellowBright;\n case \"production\":\n return colors.greenBright;\n case \"test\":\n return colors.blueBright;\n default:\n return colors.whiteBright;\n }\n}\n\n/**\n * Display CLI startup banner\n */\nexport async function displayStartupBanner({ environment }: StartBannerOptions) {\n const version = await getWarlockVersion();\n const textColorMethod = getTextColorMethod(environment);\n console.log(` ⚡ ${colors.bold(textColorMethod(\"Warlock.js\"))} ${colors.dim(`v${version}`)}`);\n console.log();\n}\n\ntype ProductionReadyBannerOptions = {\n bootDurationMs?: number;\n};\n\n/**\n * Announce that the production server is up — printed on **stdout**, and only\n * after the child process reported a completed boot.\n *\n * Stdout is the channel a supervisor or CI gate greps to decide the service is\n * healthy, so nothing optimistic may be written to it: this function is the\n * single place allowed to say \"started\", and it is called from exactly one\n * place, the `warlock:ready` handler in the `start` command.\n */\nexport async function displayProductionReadyBanner({\n bootDurationMs,\n}: ProductionReadyBannerOptions = {}) {\n const version = await getWarlockVersion();\n const duration = bootDurationMs ? colors.dim(` in ${bootDurationMs}ms`) : \"\";\n\n console.log(\n ` ⚡ ${colors.bold(colors.greenBright(\"Warlock.js\"))} ${colors.dim(`v${version}`)} ${colors.green(\"✔\")} production server started${duration}`,\n );\n console.log();\n}\n\n/**\n * Report that the production server died before it ever served anything.\n *\n * Written to **both** streams on purpose. Stderr is where a human and a log\n * collector look; stdout is where a supervisor that greps for the success\n * banner looks, and it must find a failure there rather than silence — silence\n * on stdout is what let a boot failure be recorded as a healthy start.\n */\nexport function displayProductionStartFailure(exitCode: number) {\n const lines = [\n \"\",\n ` ${colors.red(\"✖\")} ${colors.bold(\"warlock start\")} failed — the server never finished booting`,\n ` ${colors.dim(`the application process exited with code ${exitCode}`)}`,\n ` ${colors.dim(\"the cause is printed above, in the application's own output\")}`,\n \"\",\n ];\n\n for (const line of lines) {\n console.error(line);\n console.log(line);\n }\n}\n\n/**\n * Note that a running child has not reported readiness yet.\n *\n * Emitted on **stderr** only. The process may be perfectly healthy — an older\n * bundle has no readiness signal at all — so saying anything on stdout would be\n * the very false-green this channel exists to prevent.\n *\n * The wording offers the likely causes rather than asserting one. It cannot\n * distinguish an old bundle from a slow boot from a boot that is about to fail,\n * and an earlier version claimed \"the bundle predates readiness reporting\" —\n * which was simply wrong when a current bundle was mid-crash, and sent the\n * reader after the wrong thing.\n */\nexport function displayMissingReadinessNotice(waitedMs: number) {\n console.error();\n console.error(\n ` ${colors.yellow(\"!\")} still running after ${Math.round(waitedMs / 1000)}s with no readiness signal`,\n );\n console.error(` ${colors.dim(\"either the boot is still in progress, or this bundle was built\")}`);\n console.error(\n ` ${colors.dim(\"before readiness reporting — re-run\")} ${colors.cyan(\"warlock build\")} ${colors.dim(\"if the banner never appears\")}`,\n );\n console.error();\n}\n\n/**\n * Display command execution header\n */\nexport function displayExecutingCommand(commandName: string) {\n console.log(` ${colors.cyan(\"›\")} Running ${colors.bold(colors.white(commandName))}...`);\n console.log();\n}\n\n/**\n * Display command not found error with optional suggestions\n */\nexport function displayCommandNotFound(commandName: string, suggestions?: string[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Command ${colors.magenta(commandName)} not found`);\n\n if (suggestions && suggestions.length > 0) {\n console.log();\n console.log(` ${colors.yellow(\"Did you mean?\")}`);\n suggestions.forEach((suggestion) => {\n console.log(` ${colors.cyan(\"→\")} ${colors.white(suggestion)}`);\n });\n }\n\n console.log();\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display missing command error\n */\nexport function displayMissingCommand() {\n console.log();\n console.log(` ${colors.red(\"✖\")} No command specified`);\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display command success message\n */\nexport function displayCommandSuccess(commandName: string, durationMs?: number) {\n const duration = durationMs ? colors.dim(` (${durationMs}ms)`) : \"\";\n console.log();\n console.log(\n ` ${colors.green(\"✔\")} ${colors.bold(commandName)} completed successfully${duration}`,\n );\n console.log();\n}\n\n/**\n * Display command error message\n */\nexport function displayCommandError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed`);\n console.log(` ${colors.dim(error.message)}`);\n console.log();\n}\n\n/**\n * Display a FATAL boot/preload failure and exit information.\n *\n * Preload failures — a bad import in a config file, a connector that throws\n * on startup, a missing module export — happen BEFORE the command's run loop\n * exists, so there is nothing to recover into: they are always fatal. Unlike\n * `displayCommandError`, this prints the full stack (not just `error.message`)\n * because the stack names the exact file + line of the offending import, which\n * is the single most useful clue when a config file pulls in a broken module.\n *\n * Surfacing this loudly is the difference between a clear error and a silent\n * hang: an unhandled preload rejection used to escape while the already-started\n * loader worker thread kept the process alive, leaving `warlock dev` frozen\n * just after the banner with no message at all.\n *\n * @example\n * // SyntaxError: The requested module '@warlock.js/cascade' does not\n * // provide an export named 'belongsTo'\n * // at src/app/.../permission.model.ts:2\n */\nexport function displayBootError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed to start`);\n console.log(` ${colors.red(error.message)}`);\n if (error.stack) {\n console.log();\n console.log(colors.dim(error.stack));\n }\n console.log();\n}\n\n/**\n * Display missing required options error\n */\nexport function displayMissingOptions(options: { name: string; text: string }[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Missing required options:`);\n options.forEach((opt) => {\n console.log(` ${colors.yellow(opt.text)} ${colors.dim(`(--${opt.name})`)}`);\n });\n console.log();\n}\n\n/**\n * Command info for help display\n */\nexport type HelpCommandInfo = {\n name: string;\n alias?: string;\n description?: string;\n source: \"framework\" | \"plugin\" | \"project\";\n};\n\n/**\n * Display global help with all commands grouped by source\n */\nexport async function displayHelp(commands: HelpCommandInfo[]) {\n const version = await getWarlockVersion();\n\n console.log();\n console.log(\n ` ⚡ ${colors.bold(colors.yellowBright(\"Warlock.js\"))} CLI ${colors.dim(`v${version}`)}`,\n );\n console.log();\n console.log(\n ` ${colors.bold(\"Usage:\")} ${colors.cyan(\"warlock\")} ${colors.dim(\"<command>\")} ${colors.dim(\"[options]\")}`,\n );\n console.log();\n\n // Group by source\n const grouped: Record<string, HelpCommandInfo[]> = {\n framework: [],\n plugin: [],\n project: [],\n };\n\n commands.forEach((cmd) => {\n grouped[cmd.source]?.push(cmd);\n });\n\n // Display each group\n const groupLabels: Record<string, string> = {\n framework: \"Framework Commands\",\n plugin: \"Plugin Commands\",\n project: \"Project Commands\",\n };\n\n for (const [source, cmds] of Object.entries(grouped)) {\n if (cmds.length === 0) continue;\n\n console.log(` ${colors.bold(colors.white(groupLabels[source]))}`);\n console.log();\n\n // Find max name length for alignment\n const maxLen = Math.max(...cmds.map((c) => c.name.length + (c.alias ? c.alias.length + 4 : 0)));\n\n cmds.forEach((cmd) => {\n const aliasStr = cmd.alias ? colors.dim(` (${cmd.alias})`) : \"\";\n const nameWithAlias = cmd.name + (cmd.alias ? ` (${cmd.alias})` : \"\");\n const padding = \" \".repeat(maxLen - nameWithAlias.length + 2);\n // const desc = cmd.description || colors.dim(\"No description\");\n const desc = cmd.description || \"\";\n console.log(` ${colors.cyan(cmd.name)}${aliasStr}${padding}${desc}`);\n });\n console.log();\n }\n\n // Display global flags\n console.log(` ${colors.bold(colors.white(\"Global Flags\"))}`);\n console.log();\n\n const globalFlags = [\n { flag: \"--help, -h\", description: \"Show help for a command\" },\n { flag: \"--version, -v\", description: \"Show Warlock version\" },\n { flag: \"--no-cache\", description: \"Force reload without cache\" },\n { flag: \"--warm-cache\", description: \"Pre-cache all project commands\" },\n ];\n\n const maxFlagLen = Math.max(...globalFlags.map((f) => f.flag.length));\n\n globalFlags.forEach(({ flag, description }) => {\n const padding = \" \".repeat(maxFlagLen - flag.length + 2);\n console.log(` ${colors.yellow(flag)}${padding}${description}`);\n });\n console.log();\n\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock <command> --help\")} ${colors.dim(\"for command-specific help\")}`,\n );\n console.log();\n}\n\n/**\n * Display help for a specific command\n */\nexport function displayCommandHelp(command: {\n name: string;\n alias?: string;\n description?: string;\n options?: { name: string; text: string; description?: string; required?: boolean }[];\n}) {\n console.log();\n console.log(\n ` ${colors.bold(colors.cyan(command.name))}${command.alias ? colors.dim(` (${command.alias})`) : \"\"}`,\n );\n\n if (command.description) {\n console.log(` ${command.description}`);\n }\n console.log();\n\n if (command.options && command.options.length > 0) {\n console.log(` ${colors.bold(\"Options:\")}`);\n console.log();\n\n const maxLen = Math.max(...command.options.map((o) => o.text.length));\n\n command.options.forEach((opt) => {\n const padding = \" \".repeat(maxLen - opt.text.length + 2);\n const required = opt.required ? colors.red(\" (required)\") : \"\";\n const desc = opt.description || \"\";\n console.log(` ${colors.green(opt.text)}${padding}${desc}${required}`);\n });\n console.log();\n } else {\n console.log(` ${colors.dim(\"No options available\")}`);\n console.log();\n }\n}\n"],"mappings":"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"}
@@ -51,7 +51,7 @@ import { guarded } from "app/shared/utils/router";
51
51
  // router.get("/${name.kebab}", listController);
52
52
  `;
53
53
  await putFileAsync(path.join(modulePath, "routes.ts"), routesContent);
54
- const localesContent = `import { groupedTranslations } from "@mongez/localization";
54
+ const localesContent = `import { groupedTranslations } from "@warlock.js/core";
55
55
 
56
56
  groupedTranslations("${name.camel}", {
57
57
  // Add your translations here
@@ -1 +1 @@
1
- {"version":3,"file":"module.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/module.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"../../../types\";\r\nimport {\r\n crudCreateControllerStub,\r\n crudCreateSchemaStub,\r\n crudCreateServiceStub,\r\n crudDeleteControllerStub,\r\n crudDeleteServiceStub,\r\n crudGetServiceStub,\r\n crudListControllerStub,\r\n crudListServiceStub,\r\n crudModelStub,\r\n crudRepositoryStub,\r\n crudResourceStub,\r\n crudRoutesStub,\r\n crudSeedStub,\r\n crudShowControllerStub,\r\n crudUpdateControllerStub,\r\n crudUpdateSchemaStub,\r\n crudUpdateServiceStub,\r\n} from \"../templates/stubs\";\r\nimport { pluralName, singularName } from \"../utils/name-parser\";\r\nimport { moduleExists } from \"../utils/path-resolver\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\nimport { createMigrationFile } from \"./migration.generator\";\r\n\r\nexport async function generateModule(data: CommandActionData): Promise<void> {\r\n const moduleName = data.args[0];\r\n\r\n if (!moduleName) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock generate.module <name> [options]\"));\r\n console.log(colors.yellow(\"Example: warlock generate.module products\"));\r\n console.log(colors.yellow(\" warlock generate.module products --minimal\"));\r\n process.exit(1);\r\n }\r\n\r\n const name = pluralName(moduleName);\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n // Full CRUD is the default; --minimal (-m) opts down to a bare skeleton.\r\n const minimal = data.options.minimal || data.options.m;\r\n const withCrud = !minimal;\r\n\r\n // Check if module already exists\r\n if ((await moduleExists(name.kebab)) && !force) {\r\n console.log(colors.red(`Error: Module \"${name.kebab}\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n const modulePath = appPath(name.kebab);\r\n\r\n // Create module directory structure\r\n const directories = [\r\n \"controllers\",\r\n \"services\",\r\n \"models\",\r\n \"repositories\",\r\n \"schema\",\r\n \"resources\",\r\n \"events\",\r\n \"types\",\r\n \"utils\",\r\n ];\r\n\r\n for (const dir of directories) {\r\n await ensureDirectoryAsync(path.join(modulePath, dir));\r\n }\r\n\r\n // Create main.ts\r\n const mainContent = `// You may use this file as a custom entry point to be executed when the app starts.\r\n// it runs only once, and you don't need to import the routes.ts file, it will be imported automatically.\r\n// Use it to regsiter or boot up any custom logic for this module.\r\n`;\r\n await putFileAsync(path.join(modulePath, \"main.ts\"), mainContent);\r\n\r\n // Create routes.ts\r\n const routesContent = withCrud\r\n ? crudRoutesStub(name)\r\n : `import { router } from \"@warlock.js/core\";\r\nimport { guarded } from \"app/shared/utils/router\";\r\n\r\n// Define your routes here\r\n// Example:\r\n// router.get(\"/${name.kebab}\", listController);\r\n`;\r\n await putFileAsync(path.join(modulePath, \"routes.ts\"), routesContent);\r\n\r\n // Create utils/locales.ts\r\n const localesContent = `import { groupedTranslations } from \"@mongez/localization\";\r\n\r\ngroupedTranslations(\"${name.camel}\", {\r\n // Add your translations here\r\n // Example:\r\n // welcome: {\r\n // en: \"Welcome\",\r\n // ar: \"مرحبا\",\r\n // },\r\n});\r\n`;\r\n await putFileAsync(path.join(modulePath, \"utils\", \"locales.ts\"), localesContent);\r\n\r\n // Full CRUD scaffold (the default) unless --minimal was passed\r\n if (withCrud) {\r\n const entity = singularName(moduleName);\r\n\r\n // Create controllers\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `create-${entity.kebab}.controller.ts`),\r\n crudCreateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `update-${entity.kebab}.controller.ts`),\r\n crudUpdateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `list-${name.kebab}.controller.ts`),\r\n crudListControllerStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `get-${entity.kebab}.controller.ts`),\r\n crudShowControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `delete-${entity.kebab}.controller.ts`),\r\n crudDeleteControllerStub(entity),\r\n );\r\n\r\n // Create schema files — each exports both the schema value and its\r\n // inferred type; controllers import them directly (no requests/ alias).\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `create-${entity.kebab}.schema.ts`),\r\n crudCreateSchemaStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `update-${entity.kebab}.schema.ts`),\r\n crudUpdateSchemaStub(entity),\r\n );\r\n\r\n // Create model\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab));\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, `${entity.kebab}.model.ts`),\r\n crudModelStub(entity),\r\n );\r\n\r\n // Create model index\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, \"index.ts\"),\r\n `export * from \"./${entity.kebab}.model\";\\n`,\r\n );\r\n\r\n // Create migrations folder\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab, \"migrations\"));\r\n\r\n // Create resource\r\n await putFileAsync(\r\n path.join(modulePath, \"resources\", `${entity.kebab}.resource.ts`),\r\n crudResourceStub(name),\r\n );\r\n\r\n // Create repository\r\n await putFileAsync(\r\n path.join(modulePath, \"repositories\", `${name.kebab}.repository.ts`),\r\n crudRepositoryStub(name),\r\n );\r\n\r\n // Create services\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `create-${entity.kebab}.service.ts`),\r\n crudCreateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `update-${entity.kebab}.service.ts`),\r\n crudUpdateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `list-${name.kebab}.service.ts`),\r\n crudListServiceStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `get-${entity.kebab}.service.ts`),\r\n crudGetServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `delete-${entity.kebab}.service.ts`),\r\n crudDeleteServiceStub(entity),\r\n );\r\n\r\n // Create seeds\r\n await ensureDirectoryAsync(path.join(modulePath, \"seeds\"));\r\n await putFileAsync(path.join(modulePath, \"seeds\", `${name.kebab}.seed.ts`), crudSeedStub(name));\r\n\r\n // Create migration\r\n await createMigrationFile(name.kebab, entity.kebab);\r\n }\r\n\r\n console.log(colors.cyan(`\\n✨ Module \"${name.kebab}\" generated successfully!`));\r\n\r\n if (withCrud) {\r\n console.log(colors.gray(`\\n📦 CRUD scaffold created with:`));\r\n console.log(colors.gray(` - List, Get, Create, Update & Delete controllers`));\r\n console.log(colors.gray(` - Repository`));\r\n console.log(colors.gray(` - Validation schemas in schema/`));\r\n console.log(colors.gray(` - Model with resource`));\r\n console.log(colors.gray(` - Routes configured`));\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(\r\n colors.gray(` 1. Update model schema in models/${name.kebab}/${name.kebab}.model.ts`),\r\n );\r\n console.log(colors.gray(` 2. Update schema rules in schema/*.schema.ts`));\r\n console.log(\r\n colors.gray(` 3. Create migration: warlock generate.model ${name.kebab}/${name.kebab}`),\r\n );\r\n } else {\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(colors.gray(` 1. Define routes in ${name.kebab}/routes.ts`));\r\n console.log(\r\n colors.gray(` 2. Create controllers: warlock generate.controller ${name.kebab}/<name>`),\r\n );\r\n console.log(colors.gray(` 3. Create models: warlock generate.model ${name.kebab}/<name>`));\r\n console.log(colors.gray(`\\nTip: omit --minimal to generate a full CRUD scaffold`));\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/module.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"../../../types\";\r\nimport {\r\n crudCreateControllerStub,\r\n crudCreateSchemaStub,\r\n crudCreateServiceStub,\r\n crudDeleteControllerStub,\r\n crudDeleteServiceStub,\r\n crudGetServiceStub,\r\n crudListControllerStub,\r\n crudListServiceStub,\r\n crudModelStub,\r\n crudRepositoryStub,\r\n crudResourceStub,\r\n crudRoutesStub,\r\n crudSeedStub,\r\n crudShowControllerStub,\r\n crudUpdateControllerStub,\r\n crudUpdateSchemaStub,\r\n crudUpdateServiceStub,\r\n} from \"../templates/stubs\";\r\nimport { pluralName, singularName } from \"../utils/name-parser\";\r\nimport { moduleExists } from \"../utils/path-resolver\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\nimport { createMigrationFile } from \"./migration.generator\";\r\n\r\nexport async function generateModule(data: CommandActionData): Promise<void> {\r\n const moduleName = data.args[0];\r\n\r\n if (!moduleName) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock generate.module <name> [options]\"));\r\n console.log(colors.yellow(\"Example: warlock generate.module products\"));\r\n console.log(colors.yellow(\" warlock generate.module products --minimal\"));\r\n process.exit(1);\r\n }\r\n\r\n const name = pluralName(moduleName);\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n // Full CRUD is the default; --minimal (-m) opts down to a bare skeleton.\r\n const minimal = data.options.minimal || data.options.m;\r\n const withCrud = !minimal;\r\n\r\n // Check if module already exists\r\n if ((await moduleExists(name.kebab)) && !force) {\r\n console.log(colors.red(`Error: Module \"${name.kebab}\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n const modulePath = appPath(name.kebab);\r\n\r\n // Create module directory structure\r\n const directories = [\r\n \"controllers\",\r\n \"services\",\r\n \"models\",\r\n \"repositories\",\r\n \"schema\",\r\n \"resources\",\r\n \"events\",\r\n \"types\",\r\n \"utils\",\r\n ];\r\n\r\n for (const dir of directories) {\r\n await ensureDirectoryAsync(path.join(modulePath, dir));\r\n }\r\n\r\n // Create main.ts\r\n const mainContent = `// You may use this file as a custom entry point to be executed when the app starts.\r\n// it runs only once, and you don't need to import the routes.ts file, it will be imported automatically.\r\n// Use it to regsiter or boot up any custom logic for this module.\r\n`;\r\n await putFileAsync(path.join(modulePath, \"main.ts\"), mainContent);\r\n\r\n // Create routes.ts\r\n const routesContent = withCrud\r\n ? crudRoutesStub(name)\r\n : `import { router } from \"@warlock.js/core\";\r\nimport { guarded } from \"app/shared/utils/router\";\r\n\r\n// Define your routes here\r\n// Example:\r\n// router.get(\"/${name.kebab}\", listController);\r\n`;\r\n await putFileAsync(path.join(modulePath, \"routes.ts\"), routesContent);\r\n\r\n // Create utils/locales.ts\r\n const localesContent = `import { groupedTranslations } from \"@warlock.js/core\";\r\n\r\ngroupedTranslations(\"${name.camel}\", {\r\n // Add your translations here\r\n // Example:\r\n // welcome: {\r\n // en: \"Welcome\",\r\n // ar: \"مرحبا\",\r\n // },\r\n});\r\n`;\r\n await putFileAsync(path.join(modulePath, \"utils\", \"locales.ts\"), localesContent);\r\n\r\n // Full CRUD scaffold (the default) unless --minimal was passed\r\n if (withCrud) {\r\n const entity = singularName(moduleName);\r\n\r\n // Create controllers\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `create-${entity.kebab}.controller.ts`),\r\n crudCreateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `update-${entity.kebab}.controller.ts`),\r\n crudUpdateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `list-${name.kebab}.controller.ts`),\r\n crudListControllerStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `get-${entity.kebab}.controller.ts`),\r\n crudShowControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `delete-${entity.kebab}.controller.ts`),\r\n crudDeleteControllerStub(entity),\r\n );\r\n\r\n // Create schema files — each exports both the schema value and its\r\n // inferred type; controllers import them directly (no requests/ alias).\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `create-${entity.kebab}.schema.ts`),\r\n crudCreateSchemaStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `update-${entity.kebab}.schema.ts`),\r\n crudUpdateSchemaStub(entity),\r\n );\r\n\r\n // Create model\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab));\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, `${entity.kebab}.model.ts`),\r\n crudModelStub(entity),\r\n );\r\n\r\n // Create model index\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, \"index.ts\"),\r\n `export * from \"./${entity.kebab}.model\";\\n`,\r\n );\r\n\r\n // Create migrations folder\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab, \"migrations\"));\r\n\r\n // Create resource\r\n await putFileAsync(\r\n path.join(modulePath, \"resources\", `${entity.kebab}.resource.ts`),\r\n crudResourceStub(name),\r\n );\r\n\r\n // Create repository\r\n await putFileAsync(\r\n path.join(modulePath, \"repositories\", `${name.kebab}.repository.ts`),\r\n crudRepositoryStub(name),\r\n );\r\n\r\n // Create services\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `create-${entity.kebab}.service.ts`),\r\n crudCreateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `update-${entity.kebab}.service.ts`),\r\n crudUpdateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `list-${name.kebab}.service.ts`),\r\n crudListServiceStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `get-${entity.kebab}.service.ts`),\r\n crudGetServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `delete-${entity.kebab}.service.ts`),\r\n crudDeleteServiceStub(entity),\r\n );\r\n\r\n // Create seeds\r\n await ensureDirectoryAsync(path.join(modulePath, \"seeds\"));\r\n await putFileAsync(path.join(modulePath, \"seeds\", `${name.kebab}.seed.ts`), crudSeedStub(name));\r\n\r\n // Create migration\r\n await createMigrationFile(name.kebab, entity.kebab);\r\n }\r\n\r\n console.log(colors.cyan(`\\n✨ Module \"${name.kebab}\" generated successfully!`));\r\n\r\n if (withCrud) {\r\n console.log(colors.gray(`\\n📦 CRUD scaffold created with:`));\r\n console.log(colors.gray(` - List, Get, Create, Update & Delete controllers`));\r\n console.log(colors.gray(` - Repository`));\r\n console.log(colors.gray(` - Validation schemas in schema/`));\r\n console.log(colors.gray(` - Model with resource`));\r\n console.log(colors.gray(` - Routes configured`));\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(\r\n colors.gray(` 1. Update model schema in models/${name.kebab}/${name.kebab}.model.ts`),\r\n );\r\n console.log(colors.gray(` 2. Update schema rules in schema/*.schema.ts`));\r\n console.log(\r\n colors.gray(` 3. Create migration: warlock generate.model ${name.kebab}/${name.kebab}`),\r\n );\r\n } else {\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(colors.gray(` 1. Define routes in ${name.kebab}/routes.ts`));\r\n console.log(\r\n colors.gray(` 2. Create controllers: warlock generate.controller ${name.kebab}/<name>`),\r\n );\r\n console.log(colors.gray(` 3. Create models: warlock generate.model ${name.kebab}/<name>`));\r\n console.log(colors.gray(`\\nTip: omit --minimal to generate a full CRUD scaffold`));\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { command } from "../cli-command.mjs";
2
- import { displayStartupBanner } from "../cli-commands.utils.mjs";
3
2
  import { resolveBuildConfig } from "../../production/resolve-build-config.mjs";
4
- import { spawn } from "child_process";
3
+ import { superviseProductionProcess } from "../../production/production-supervisor.mjs";
5
4
 
6
5
  //#region ../core/src/cli/commands/start-production.command.ts
7
6
  const startProductionCommand = command({
@@ -9,9 +8,6 @@ const startProductionCommand = command({
9
8
  description: "Start production server",
10
9
  persistent: true,
11
10
  preload: { warlockConfig: true },
12
- preAction: async () => {
13
- displayStartupBanner({ environment: "production" });
14
- },
15
11
  action: async () => {
16
12
  const { entryPath, sourcemap } = resolveBuildConfig();
17
13
  const nodeArgs = [];
@@ -22,26 +18,9 @@ const startProductionCommand = command({
22
18
  const extraArgs = process.argv.slice(startIndex + 1);
23
19
  nodeArgs.push(...extraArgs);
24
20
  }
25
- console.log(`🚀 Starting production server...\n`);
26
- const child = spawn(process.execPath, nodeArgs, {
27
- stdio: "inherit",
28
- cwd: process.cwd(),
29
- env: process.env,
30
- detached: false
31
- });
32
- let isShuttingDown = false;
33
- const forwardSignal = (signal) => {
34
- if (isShuttingDown) return;
35
- isShuttingDown = true;
36
- child.kill(signal);
37
- };
38
- process.on("SIGTERM", () => forwardSignal("SIGTERM"));
39
- process.on("SIGINT", () => {
40
- isShuttingDown = true;
41
- });
42
- child.on("exit", (code) => {
43
- process.exit(code ?? 0);
44
- });
21
+ console.error(`🚀 Starting production server...\n`);
22
+ const { exitCode } = await superviseProductionProcess({ nodeArgs });
23
+ process.exit(exitCode);
45
24
  }
46
25
  });
47
26
 
@@ -1 +1 @@
1
- {"version":3,"file":"start-production.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/start-production.command.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport { resolveBuildConfig } from \"../../production/resolve-build-config\";\nimport { command } from \"../cli-command\";\nimport { displayStartupBanner } from \"../cli-commands.utils\";\n\nexport const startProductionCommand = command({\n name: \"start\",\n description: \"Start production server\",\n persistent: true,\n preload: {\n warlockConfig: true,\n },\n preAction: async () => {\n displayStartupBanner({ environment: \"production\" });\n },\n action: async () => {\n const { entryPath, sourcemap } = resolveBuildConfig();\n\n // Build node args\n const nodeArgs: string[] = [];\n\n // Enable source maps if configured\n if (sourcemap !== false) {\n nodeArgs.push(\"--enable-source-maps\");\n }\n\n // Add entry file\n nodeArgs.push(entryPath);\n\n // Pass through any additional flags after \"start\" command\n // process.argv = [node, cli.ts, start, ...extra]\n const startIndex = process.argv.findIndex((arg) => arg === \"start\");\n if (startIndex !== -1 && startIndex < process.argv.length - 1) {\n const extraArgs = process.argv.slice(startIndex + 1);\n nodeArgs.push(...extraArgs);\n }\n\n console.log(`🚀 Starting production server...\\n`);\n\n // Spawn child process\n // On Windows, we need to be careful with signals - the console sends Ctrl+C\n // to all processes in the group, so we just need to not interfere\n // `process.execPath`, not \"node\": the bare name needs `node` on PATH, which\n // a systemd unit, a cron job, or a slim container may not provide and a\n // PATH `node` that *is* present can be a different version than the one\n // running this CLI. execPath is the binary already executing us.\n const child = spawn(process.execPath, nodeArgs, {\n stdio: \"inherit\",\n cwd: process.cwd(),\n env: process.env,\n // Important: keep child in same process group for proper signal handling\n detached: false,\n });\n\n // Track if we're shutting down to prevent double-exit\n let isShuttingDown = false;\n\n // Forward signals to child (needed for SIGTERM, helpful for explicit forwarding)\n const forwardSignal = (signal: NodeJS.Signals) => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n // Send signal to child\n child.kill(signal);\n };\n\n // On SIGTERM, forward it (SIGTERM doesn't auto-propagate like SIGINT on Windows)\n process.on(\"SIGTERM\", () => forwardSignal(\"SIGTERM\"));\n\n // On SIGINT (Ctrl+C), mark as shutting down but let child handle it naturally\n // On Windows, Ctrl+C is sent to both processes, so child already gets it\n process.on(\"SIGINT\", () => {\n isShuttingDown = true;\n // Give child a chance to exit gracefully before we do anything\n });\n\n // Exit with child's exit code\n child.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n },\n});\n"],"mappings":";;;;;;AAKA,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,aAAa;CACb,YAAY;CACZ,SAAS,EACP,eAAe,KACjB;CACA,WAAW,YAAY;EACrB,qBAAqB,EAAE,aAAa,aAAa,CAAC;CACpD;CACA,QAAQ,YAAY;EAClB,MAAM,EAAE,WAAW,cAAc,mBAAmB;EAGpD,MAAM,WAAqB,CAAC;EAG5B,IAAI,cAAc,OAChB,SAAS,KAAK,sBAAsB;EAItC,SAAS,KAAK,SAAS;EAIvB,MAAM,aAAa,QAAQ,KAAK,WAAW,QAAQ,QAAQ,OAAO;EAClE,IAAI,eAAe,MAAM,aAAa,QAAQ,KAAK,SAAS,GAAG;GAC7D,MAAM,YAAY,QAAQ,KAAK,MAAM,aAAa,CAAC;GACnD,SAAS,KAAK,GAAG,SAAS;EAC5B;EAEA,QAAQ,IAAI,oCAAoC;EAShD,MAAM,QAAQ,MAAM,QAAQ,UAAU,UAAU;GAC9C,OAAO;GACP,KAAK,QAAQ,IAAI;GACjB,KAAK,QAAQ;GAEb,UAAU;EACZ,CAAC;EAGD,IAAI,iBAAiB;EAGrB,MAAM,iBAAiB,WAA2B;GAChD,IAAI,gBAAgB;GACpB,iBAAiB;GAGjB,MAAM,KAAK,MAAM;EACnB;EAGA,QAAQ,GAAG,iBAAiB,cAAc,SAAS,CAAC;EAIpD,QAAQ,GAAG,gBAAgB;GACzB,iBAAiB;EAEnB,CAAC;EAGD,MAAM,GAAG,SAAS,SAAS;GACzB,QAAQ,KAAK,QAAQ,CAAC;EACxB,CAAC;CACH;AACF,CAAC"}
1
+ {"version":3,"file":"start-production.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/start-production.command.ts"],"sourcesContent":["import { superviseProductionProcess } from \"../../production/production-supervisor\";\nimport { resolveBuildConfig } from \"../../production/resolve-build-config\";\nimport { command } from \"../cli-command\";\n\nexport const startProductionCommand = command({\n name: \"start\",\n description: \"Start production server\",\n persistent: true,\n preload: {\n warlockConfig: true,\n },\n action: async () => {\n const { entryPath, sourcemap } = resolveBuildConfig();\n\n // Build node args\n const nodeArgs: string[] = [];\n\n // Enable source maps if configured\n if (sourcemap !== false) {\n nodeArgs.push(\"--enable-source-maps\");\n }\n\n // Add entry file\n nodeArgs.push(entryPath);\n\n // Pass through any additional flags after \"start\" command\n // process.argv = [node, cli.ts, start, ...extra]\n const startIndex = process.argv.findIndex((arg) => arg === \"start\");\n if (startIndex !== -1 && startIndex < process.argv.length - 1) {\n const extraArgs = process.argv.slice(startIndex + 1);\n nodeArgs.push(...extraArgs);\n }\n\n // Progress goes to stderr, never stdout. Stdout carries exactly one claim —\n // \"started\" — so that whatever greps it cannot mistake an intention for an\n // outcome. The banner that used to print here, in `preAction`, printed\n // before the child had even been spawned.\n console.error(`🚀 Starting production server...\\n`);\n\n const { exitCode } = await superviseProductionProcess({ nodeArgs });\n\n process.exit(exitCode);\n },\n});\n"],"mappings":";;;;;AAIA,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,aAAa;CACb,YAAY;CACZ,SAAS,EACP,eAAe,KACjB;CACA,QAAQ,YAAY;EAClB,MAAM,EAAE,WAAW,cAAc,mBAAmB;EAGpD,MAAM,WAAqB,CAAC;EAG5B,IAAI,cAAc,OAChB,SAAS,KAAK,sBAAsB;EAItC,SAAS,KAAK,SAAS;EAIvB,MAAM,aAAa,QAAQ,KAAK,WAAW,QAAQ,QAAQ,OAAO;EAClE,IAAI,eAAe,MAAM,aAAa,QAAQ,KAAK,SAAS,GAAG;GAC7D,MAAM,YAAY,QAAQ,KAAK,MAAM,aAAa,CAAC;GACnD,SAAS,KAAK,GAAG,SAAS;EAC5B;EAMA,QAAQ,MAAM,oCAAoC;EAElD,MAAM,EAAE,aAAa,MAAM,2BAA2B,EAAE,SAAS,CAAC;EAElE,QAAQ,KAAK,QAAQ;CACvB;AACF,CAAC"}
@@ -76,7 +76,12 @@ type CLICommandPreload = {
76
76
  environemnt?: "production" | "development" | "test";
77
77
  /**
78
78
  * Whether to load environment variables from .env files.
79
- * Note: If `bootstrap` is true, env is loaded automatically.
79
+ *
80
+ * @deprecated No longer read — env is loaded for every command that declares
81
+ * a preload block, before `warlock.config.ts` is evaluated. It has to be:
82
+ * config files call `env()` in their module body, and loading config first
83
+ * resolved every one of those calls to its default. Setting this flag is
84
+ * harmless and does nothing; remove it. Dropped at 5.0.
80
85
  */
81
86
  env?: boolean;
82
87
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/cli/types.ts"],"mappings":";;;;;AAgB4B;AAY5B;;;;KAZY,gBAAA;;;;AAwBK;AAsBjB;;;;;;KAlCY,iBAAA;EAuDV;;;;EAlDA,IAAA;EA4EA;;;AAA0B;AAgB5B;EArFE,OAAA,EAAS,MAAM;AAAA;;;;;;;;;;AA4HP;AAGV;;;;;;;;;KAzGY,iBAAA;EAkHA;;;;;EA5GV,MAAA;EA4GiE;;AAAO;EAvGxE,QAAA;EA6H2B;;;EAxH3B,aAAA;EAiKQ;;;EA5JR,eAAA,GAAkB,eAAA;EAwHlB;;;EAnHA,WAAA;EAoIU;;;;EA9HV,GAAA;EAiJQ;;;;EA3IR,SAAA;;;;;;;;EASA,UAAA,GAAa,aAAa;AAAA;;;;;;;;;;;;;;KAgBhB,gBAAA;;;;;EAKV,IAAA;;;;;EAMA,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,IAAA;;;;EAKA,YAAA;;;;;EAMA,QAAA;AAAA;AAAA,KAGU,wBAAA,GAA2B,IAAI,CAAC,gBAAA;EAC1C,IAAA;EACA,KAAA;AAAA;;;;;KAOU,gBAAA,IAAoB,IAAA,EAAM,iBAAA,YAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;KAsB9D,iBAAA;;;;;EAKV,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,OAAA,GAAU,iBAAA;;;;;;EAOV,UAAA;;;;;EAMA,SAAA,GAAY,gBAAA;;;;;EAMZ,MAAA,EAAQ,gBAAA;;;;;EAMR,OAAA,GAAU,gBAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/cli/types.ts"],"mappings":";;;;;AAgB4B;AAY5B;;;;KAZY,gBAAA;;;;AAwBK;AAsBjB;;;;;;KAlCY,iBAAA;EAuDV;;;;EAlDA,IAAA;EAiFA;;;AAA0B;AAgB5B;EA1FE,OAAA,EAAS,MAAM;AAAA;;;;;;;;;;AAiIP;AAGV;;;;;;;;;KA9GY,iBAAA;EAuHA;;;;;EAjHV,MAAA;EAiHiE;;AAAO;EA5GxE,QAAA;EAkI2B;;;EA7H3B,aAAA;EAsKQ;;;EAjKR,eAAA,GAAkB,eAAA;EA6HlB;;;EAxHA,WAAA;EAyIU;;;;;;;;;EA9HV,GAAA;;;;;EAMA,SAAA;;;;;;;;EASA,UAAA,GAAa,aAAa;AAAA;;;;;;;;;;;;;;KAgBhB,gBAAA;;;;;EAKV,IAAA;;;;;EAMA,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,IAAA;;;;EAKA,YAAA;;;;;EAMA,QAAA;AAAA;AAAA,KAGU,wBAAA,GAA2B,IAAI,CAAC,gBAAA;EAC1C,IAAA;EACA,KAAA;AAAA;;;;;KAOU,gBAAA,IAAoB,IAAA,EAAM,iBAAA,YAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;KAsB9D,iBAAA;;;;;EAKV,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,OAAA,GAAU,iBAAA;;;;;;EAOV,UAAA;;;;;EAMA,SAAA,GAAY,gBAAA;;;;;EAMZ,MAAA,EAAQ,gBAAA;;;;;EAMR,OAAA,GAAU,gBAAA;AAAA"}
@@ -0,0 +1,27 @@
1
+ import { ConfigRegistry } from "./types.mjs";
2
+
3
+ //#region ../core/src/config/config-setter.d.ts
4
+ /**
5
+ * Register a config group under its name.
6
+ *
7
+ * The write side of the config store, kept separate from the `config` accessor
8
+ * on purpose: `config` is a read-only surface and widening it would invite app
9
+ * code to mutate configuration at runtime. Registration is a boot-time act.
10
+ *
11
+ * Exported chiefly so the production build's generated config loader has a
12
+ * specifier the consuming app actually declares — `@warlock.js/core` — rather
13
+ * than `@mongez/config`, which is core's dependency and not the app's.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { setConfig } from "@warlock.js/core";
18
+ * import databaseConfig from "../../src/config/database";
19
+ *
20
+ * setConfig("database", databaseConfig);
21
+ * ```
22
+ */
23
+ declare function setConfig<K extends keyof ConfigRegistry>(name: K, value: ConfigRegistry[K]): void;
24
+ declare function setConfig(name: string, value: unknown): void;
25
+ //#endregion
26
+ export { setConfig };
27
+ //# sourceMappingURL=config-setter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-setter.d.mts","names":[],"sources":["../../../../../../../core/src/config/config-setter.ts"],"mappings":";;;;;AAsBA;;;;;;;;;;;;;;;;;iBAAgB,SAAA,iBAA0B,cAAA,EAAgB,IAAA,EAAM,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;AAAA,iBACzE,SAAA,CAAU,IAAA,UAAc,KAAc"}
@@ -0,0 +1,10 @@
1
+ import config from "@mongez/config";
2
+
3
+ //#region ../core/src/config/config-setter.ts
4
+ function setConfig(name, value) {
5
+ config.set(name, value);
6
+ }
7
+
8
+ //#endregion
9
+ export { setConfig };
10
+ //# sourceMappingURL=config-setter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-setter.mjs","names":[],"sources":["../../../../../../../core/src/config/config-setter.ts"],"sourcesContent":["import baseConfig from \"@mongez/config\";\nimport type { ConfigRegistry } from \"./types\";\n\n/**\n * Register a config group under its name.\n *\n * The write side of the config store, kept separate from the `config` accessor\n * on purpose: `config` is a read-only surface and widening it would invite app\n * code to mutate configuration at runtime. Registration is a boot-time act.\n *\n * Exported chiefly so the production build's generated config loader has a\n * specifier the consuming app actually declares — `@warlock.js/core` — rather\n * than `@mongez/config`, which is core's dependency and not the app's.\n *\n * @example\n * ```typescript\n * import { setConfig } from \"@warlock.js/core\";\n * import databaseConfig from \"../../src/config/database\";\n *\n * setConfig(\"database\", databaseConfig);\n * ```\n */\nexport function setConfig<K extends keyof ConfigRegistry>(name: K, value: ConfigRegistry[K]): void;\nexport function setConfig(name: string, value: unknown): void;\nexport function setConfig(name: string, value: unknown): void {\n baseConfig.set(name, value);\n}\n"],"mappings":";;;AAwBA,SAAgB,UAAU,MAAc,OAAsB;CAC5D,OAAW,IAAI,MAAM,KAAK;AAC5B"}
@@ -1,5 +1,6 @@
1
1
  import { config } from "./config-getter.mjs";
2
2
  import { ConfigSpecialHandlers, configSpecialHandlers } from "./config-special-handlers.mjs";
3
3
  import { registerAppConfig } from "./config-handlers.mjs";
4
+ import { setConfig } from "./config-setter.mjs";
4
5
 
5
6
  export { };
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"mappings":";;;;;;;AAsCA;;cAAa,aAAA,SAAsB,aAAA;EAAA,SACjB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,IAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EAmBb;;;;;EAAA,mBAZE,YAAA;EAV2B;;;EAAA,UAepC,IAAA,GAAO,eAAA;EAbO;;;;;EAoBX,IAAA,IAAI,OAAA;EAPP;;;;;;;EAAA,QA0CF,oBAAA;EAiFgB;;;;;EAjDX,KAAA,IAAS,OAAA;EAsFqB;;;;;EArC9B,OAAA,IAAW,OAAA;;;;EASX,QAAA,IAAY,OAAA;;;;;;EA4BlB,aAAA,CAAc,YAAA;AAAA"}
1
+ {"version":3,"file":"http-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"mappings":";;;;;;;AAsCA;;cAAa,aAAA,SAAsB,aAAA;EAAA,SACjB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,IAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EAmBb;;;;;EAAA,mBAZE,YAAA;EAV2B;;;EAAA,UAepC,IAAA,GAAO,eAAA;EAbO;;;;;EAoBX,IAAA,IAAI,OAAA;EAPP;;;;;;;EAAA,QA0CF,oBAAA;EAmFgB;;;;;EAnDX,KAAA,IAAS,OAAA;EAwFqB;;;;;EArC9B,OAAA,IAAW,OAAA;;;;EASX,QAAA,IAAY,OAAA;;;;;;EA4BlB,aAAA,CAAc,YAAA;AAAA"}
@@ -1,9 +1,9 @@
1
1
  import { setBaseUrl } from "../utils/urls.mjs";
2
2
  import { container } from "../container/index.mjs";
3
+ import { Application } from "../application/application.mjs";
3
4
  import { router } from "../router/router.mjs";
4
5
  import { registerHttpPlugins } from "../http/plugins.mjs";
5
6
  import { closeServerWithTimeout, getHttpServer, startHttpServer } from "../http/server.mjs";
6
- import { Application } from "../application/application.mjs";
7
7
  import "../application/index.mjs";
8
8
  import { health } from "../http/health.mjs";
9
9
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./types.mjs";
@@ -94,6 +94,7 @@ var HttpConnector = class extends BaseConnector {
94
94
  port: httpConfig.port,
95
95
  host: httpConfig.host || "localhost"
96
96
  });
97
+ Application.setServedPort(httpConfig.port);
97
98
  const baseUrl = config.get("app.baseUrl");
98
99
  log.success(`http`, "connection", `Server ready at ${baseUrl}`);
99
100
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { Application } from \"../application\";\r\nimport { devLogError } from \"../dev-server/dev-logger\";\r\nimport { health } from \"../http/health\";\r\nimport { registerHttpPlugins } from \"../http/plugins\";\r\nimport { closeServerWithTimeout, FastifyInstance, getHttpServer, startHttpServer } from \"../http/server\";\r\nimport { router } from \"../router/router\";\r\nimport { Environment } from \"../utils\";\r\nimport { setBaseUrl } from \"../utils/urls\";\r\nimport { container } from \"./../container\";\r\nimport { BaseConnector } from \"./base-connector\";\r\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\r\n\r\nfunction environmentColor(environment: Environment) {\r\n switch (environment) {\r\n case \"development\":\r\n return colors.magentaBright(environment);\r\n case \"test\":\r\n return colors.yellowBright(environment);\r\n case \"production\":\r\n return colors.greenBright(environment);\r\n default:\r\n return colors.white(environment);\r\n }\r\n}\r\n\r\n/**\r\n * Default time to wait for in-flight requests to drain on shutdown before the\r\n * HTTP server is force-closed. Override via `http.gracefulShutdown.timeout`.\r\n */\r\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\r\n\r\n/**\r\n * HTTP Connector\r\n * Manages HTTP server (Fastify) lifecycle\r\n */\r\nexport class HttpConnector extends BaseConnector {\r\n public readonly name = \"http\";\r\n public readonly priority = ConnectorPriority.HTTP;\r\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\r\n\r\n /**\r\n * Files that trigger HTTP server restart\r\n * Note: routes.ts changes will be handled by HMR with wildcard routing\r\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\r\n */\r\n protected readonly watchedFiles = [\"src/config/http.ts\", \"src/config/http.tsx\"];\r\n\r\n /**\r\n * Fastify Server instance\r\n */\r\n protected http?: FastifyInstance;\r\n\r\n /**\r\n * Boot the connector — construction only (create Fastify, register\r\n * plugins, populate container). Route scanning is deferred to\r\n * `start()` so it reads the router after app code has registered.\r\n */\r\n public async boot() {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig) return;\r\n\r\n const port = httpConfig.port;\r\n log.info(\r\n `http`,\r\n \"connection\",\r\n `Starting http server on port ${port} in ${environmentColor(Application.environment)} mode`,\r\n );\r\n\r\n this.http = startHttpServer(httpConfig.serverOptions);\r\n\r\n container.set(\"http.server\", this.http);\r\n\r\n await registerHttpPlugins(this.http);\r\n\r\n const baseUrl = config.get(\"app.baseUrl\");\r\n\r\n // update base url\r\n setBaseUrl(baseUrl);\r\n\r\n // Liveness/readiness endpoints — registered on Fastify before start() scans\r\n // the app router, so they exist by the time the server listens.\r\n this.registerHealthRoutes();\r\n }\r\n\r\n /**\r\n * Register the built-in liveness (`/health`) and readiness (`/ready`)\r\n * endpoints directly on the Fastify instance — infra routes, kept off the app\r\n * router so they're immune to HMR and never collide with route scanning. Opt\r\n * out via `http.health.enabled = false`; rename via `http.health.path` /\r\n * `http.health.readinessPath`.\r\n */\r\n private registerHealthRoutes(): void {\r\n if (!this.http) {\r\n return;\r\n }\r\n\r\n const healthConfig = config.get(\"http.health\");\r\n\r\n if (healthConfig?.enabled === false) {\r\n return;\r\n }\r\n\r\n const livenessPath = healthConfig?.path ?? \"/health\";\r\n const readinessPath = healthConfig?.readinessPath ?? \"/ready\";\r\n\r\n this.http.get(livenessPath, async (_request: any, reply: any) => {\r\n const result = health.liveness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n\r\n this.http.get(readinessPath, async (_request: any, reply: any) => {\r\n const result = await health.readiness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n }\r\n\r\n /**\r\n * Initialize HTTP server — bind app-registered routes to Fastify\r\n * then listen. Scanning here (not in `boot`) lets HTTP boot before\r\n * app code without losing routes.\r\n */\r\n public async start(): Promise<void> {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig || !this.http) return;\r\n\r\n if (Application.runtimeStrategy === \"development\") {\r\n router.scanDevServer(this.http);\r\n } else {\r\n router.scan(this.http);\r\n }\r\n\r\n // Surface the route table as a readiness signal + a boot log, so an empty\r\n // or partial route surface (e.g. a route module that failed to register) is\r\n // detectable instead of silently 404ing. `addRoutesRegisteredCheck` is\r\n // keyed by name, so re-running on restart is idempotent.\r\n health.addRoutesRegisteredCheck(() => router.routeCount());\r\n log.info(\"http\", \"routes\", `${router.routeCount()} route(s) registered`);\r\n\r\n try {\r\n // We can use the url of the server\r\n await this.http.listen({\r\n port: httpConfig.port,\r\n host: httpConfig.host || \"localhost\",\r\n });\r\n\r\n const baseUrl = config.get(\"app.baseUrl\");\r\n\r\n log.success(`http`, \"connection\", `Server ready at ${baseUrl}`);\r\n } catch (error) {\r\n devLogError(\"Error while starting http server\", error);\r\n\r\n // A failed listen()/port-bind at boot means the app can't serve — fatal.\r\n // devLogError above is the dev-server console UI and bypasses the logger\r\n // pipeline, so also route it through `log.fatal` to reach Sentry/file,\r\n // then `await log.flush()` to drain buffered/async channels before exit.\r\n await log.fatal(\"http\", \"connection\", error);\r\n await log.flush();\r\n\r\n process.exit(1); // stop the process, exit with error\r\n }\r\n\r\n this.active = true;\r\n }\r\n\r\n /**\r\n * Restart — needs a fresh Fastify instance since `start()` now\r\n * re-runs `router.scan()`, and re-scanning the same Fastify would\r\n * register duplicate route handlers.\r\n */\r\n public async restart(): Promise<void> {\r\n await this.shutdown();\r\n await this.boot();\r\n await this.start();\r\n }\r\n\r\n /**\r\n * Shutdown HTTP server\r\n */\r\n public async shutdown(): Promise<void> {\r\n if (!this.active) {\r\n return;\r\n }\r\n\r\n const server = getHttpServer();\r\n\r\n if (server) {\r\n const timeout = config.get(\"http.gracefulShutdown.timeout\", DEFAULT_SHUTDOWN_TIMEOUT);\r\n const drained = await closeServerWithTimeout(server, timeout);\r\n\r\n if (!drained) {\r\n log.warn(\r\n \"http\",\r\n \"shutdown\",\r\n `In-flight requests did not drain within ${timeout}ms; closing anyway`,\r\n );\r\n }\r\n }\r\n\r\n this.active = false;\r\n }\r\n\r\n /**\r\n * Override shouldRestart to handle routes.ts specially\r\n * routes.ts changes should NOT restart the server (use HMR instead)\r\n * Now receives config file paths directly from layer-executor\r\n */\r\n public shouldRestart(changedFiles: string[]): boolean {\r\n // Only restart for config changes, not routes\r\n return changedFiles.some((file) => {\r\n const relativePath = file.replace(/\\\\/g, \"/\");\r\n return this.watchedFiles.includes(relativePath);\r\n });\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"http-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { Application } from \"../application\";\r\nimport { devLogError } from \"../dev-server/dev-logger\";\r\nimport { health } from \"../http/health\";\r\nimport { registerHttpPlugins } from \"../http/plugins\";\r\nimport { closeServerWithTimeout, FastifyInstance, getHttpServer, startHttpServer } from \"../http/server\";\r\nimport { router } from \"../router/router\";\r\nimport { Environment } from \"../utils\";\r\nimport { setBaseUrl } from \"../utils/urls\";\r\nimport { container } from \"./../container\";\r\nimport { BaseConnector } from \"./base-connector\";\r\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\r\n\r\nfunction environmentColor(environment: Environment) {\r\n switch (environment) {\r\n case \"development\":\r\n return colors.magentaBright(environment);\r\n case \"test\":\r\n return colors.yellowBright(environment);\r\n case \"production\":\r\n return colors.greenBright(environment);\r\n default:\r\n return colors.white(environment);\r\n }\r\n}\r\n\r\n/**\r\n * Default time to wait for in-flight requests to drain on shutdown before the\r\n * HTTP server is force-closed. Override via `http.gracefulShutdown.timeout`.\r\n */\r\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\r\n\r\n/**\r\n * HTTP Connector\r\n * Manages HTTP server (Fastify) lifecycle\r\n */\r\nexport class HttpConnector extends BaseConnector {\r\n public readonly name = \"http\";\r\n public readonly priority = ConnectorPriority.HTTP;\r\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\r\n\r\n /**\r\n * Files that trigger HTTP server restart\r\n * Note: routes.ts changes will be handled by HMR with wildcard routing\r\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\r\n */\r\n protected readonly watchedFiles = [\"src/config/http.ts\", \"src/config/http.tsx\"];\r\n\r\n /**\r\n * Fastify Server instance\r\n */\r\n protected http?: FastifyInstance;\r\n\r\n /**\r\n * Boot the connector — construction only (create Fastify, register\r\n * plugins, populate container). Route scanning is deferred to\r\n * `start()` so it reads the router after app code has registered.\r\n */\r\n public async boot() {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig) return;\r\n\r\n const port = httpConfig.port;\r\n log.info(\r\n `http`,\r\n \"connection\",\r\n `Starting http server on port ${port} in ${environmentColor(Application.environment)} mode`,\r\n );\r\n\r\n this.http = startHttpServer(httpConfig.serverOptions);\r\n\r\n container.set(\"http.server\", this.http);\r\n\r\n await registerHttpPlugins(this.http);\r\n\r\n const baseUrl = config.get(\"app.baseUrl\");\r\n\r\n // update base url\r\n setBaseUrl(baseUrl);\r\n\r\n // Liveness/readiness endpoints — registered on Fastify before start() scans\r\n // the app router, so they exist by the time the server listens.\r\n this.registerHealthRoutes();\r\n }\r\n\r\n /**\r\n * Register the built-in liveness (`/health`) and readiness (`/ready`)\r\n * endpoints directly on the Fastify instance — infra routes, kept off the app\r\n * router so they're immune to HMR and never collide with route scanning. Opt\r\n * out via `http.health.enabled = false`; rename via `http.health.path` /\r\n * `http.health.readinessPath`.\r\n */\r\n private registerHealthRoutes(): void {\r\n if (!this.http) {\r\n return;\r\n }\r\n\r\n const healthConfig = config.get(\"http.health\");\r\n\r\n if (healthConfig?.enabled === false) {\r\n return;\r\n }\r\n\r\n const livenessPath = healthConfig?.path ?? \"/health\";\r\n const readinessPath = healthConfig?.readinessPath ?? \"/ready\";\r\n\r\n this.http.get(livenessPath, async (_request: any, reply: any) => {\r\n const result = health.liveness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n\r\n this.http.get(readinessPath, async (_request: any, reply: any) => {\r\n const result = await health.readiness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n }\r\n\r\n /**\r\n * Initialize HTTP server — bind app-registered routes to Fastify\r\n * then listen. Scanning here (not in `boot`) lets HTTP boot before\r\n * app code without losing routes.\r\n */\r\n public async start(): Promise<void> {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig || !this.http) return;\r\n\r\n if (Application.runtimeStrategy === \"development\") {\r\n router.scanDevServer(this.http);\r\n } else {\r\n router.scan(this.http);\r\n }\r\n\r\n // Surface the route table as a readiness signal + a boot log, so an empty\r\n // or partial route surface (e.g. a route module that failed to register) is\r\n // detectable instead of silently 404ing. `addRoutesRegisteredCheck` is\r\n // keyed by name, so re-running on restart is idempotent.\r\n health.addRoutesRegisteredCheck(() => router.routeCount());\r\n log.info(\"http\", \"routes\", `${router.routeCount()} route(s) registered`);\r\n\r\n try {\r\n // We can use the url of the server\r\n await this.http.listen({\r\n port: httpConfig.port,\r\n host: httpConfig.host || \"localhost\",\r\n });\r\n\r\n Application.setServedPort(httpConfig.port);\r\n\r\n const baseUrl = config.get(\"app.baseUrl\");\r\n\r\n log.success(`http`, \"connection\", `Server ready at ${baseUrl}`);\r\n } catch (error) {\r\n devLogError(\"Error while starting http server\", error);\r\n\r\n // A failed listen()/port-bind at boot means the app can't serve — fatal.\r\n // devLogError above is the dev-server console UI and bypasses the logger\r\n // pipeline, so also route it through `log.fatal` to reach Sentry/file,\r\n // then `await log.flush()` to drain buffered/async channels before exit.\r\n await log.fatal(\"http\", \"connection\", error);\r\n await log.flush();\r\n\r\n process.exit(1); // stop the process, exit with error\r\n }\r\n\r\n this.active = true;\r\n }\r\n\r\n /**\r\n * Restart — needs a fresh Fastify instance since `start()` now\r\n * re-runs `router.scan()`, and re-scanning the same Fastify would\r\n * register duplicate route handlers.\r\n */\r\n public async restart(): Promise<void> {\r\n await this.shutdown();\r\n await this.boot();\r\n await this.start();\r\n }\r\n\r\n /**\r\n * Shutdown HTTP server\r\n */\r\n public async shutdown(): Promise<void> {\r\n if (!this.active) {\r\n return;\r\n }\r\n\r\n const server = getHttpServer();\r\n\r\n if (server) {\r\n const timeout = config.get(\"http.gracefulShutdown.timeout\", DEFAULT_SHUTDOWN_TIMEOUT);\r\n const drained = await closeServerWithTimeout(server, timeout);\r\n\r\n if (!drained) {\r\n log.warn(\r\n \"http\",\r\n \"shutdown\",\r\n `In-flight requests did not drain within ${timeout}ms; closing anyway`,\r\n );\r\n }\r\n }\r\n\r\n this.active = false;\r\n }\r\n\r\n /**\r\n * Override shouldRestart to handle routes.ts specially\r\n * routes.ts changes should NOT restart the server (use HMR instead)\r\n * Now receives config file paths directly from layer-executor\r\n */\r\n public shouldRestart(changedFiles: string[]): boolean {\r\n // Only restart for config changes, not routes\r\n return changedFiles.some((file) => {\r\n const relativePath = file.replace(/\\\\/g, \"/\");\r\n return this.watchedFiles.includes(relativePath);\r\n });\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -15,14 +15,20 @@ declare class StorageConnector extends BaseConnector {
15
15
  */
16
16
  protected readonly watchedFiles: string[];
17
17
  /**
18
- * Initialize cache connection
18
+ * Initialize storage.
19
19
  *
20
20
  * Unlike its siblings (database, cache, herald), this connector does NOT
21
21
  * early-return when `config.get("storage")` is absent. Storage is
22
- * intentionally always-on: `storage.init()` falls back to the built-in
23
- * `local` driver (see `storageConfig("default", "local")`), so file storage
24
- * works out of the box even when a project ships no `src/config/storage.ts`.
25
- * A config-presence guard here would break that default local storage.
22
+ * intentionally always-on: `storage.init()` registers a built-in `local`
23
+ * driver rooted at `uploadsPath()` before reading configured drivers, so file
24
+ * storage works out of the box even when a project ships no
25
+ * `src/config/storage.ts`. A config-presence guard here would break that.
26
+ *
27
+ * That fallback is real as of 4.11.0. Until then this comment described it
28
+ * and nothing implemented it, so every app without `src/config/storage.ts`
29
+ * died at boot with `Storage driver "local" is not configured` — a decision
30
+ * to skip the guard, taken on the authority of behaviour nobody had written.
31
+ * See `Storage.registerBuiltInLocalDriver`.
26
32
  */
27
33
  start(): Promise<void>;
28
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"storage.connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"mappings":";;;;;;AASA;;cAAa,gBAAA,SAAyB,aAAA;EAAA,SACpB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,OAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA2BL;;;EAAA,mBAtBN,YAAA;EARiB;;;;;;;;;;EAoBvB,KAAA,IAAS,OAAA;EAUT;;;EAAA,QAAA,IAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"storage.connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"mappings":";;;;;;AASA;;cAAa,gBAAA,SAAyB,aAAA;EAAA,SACpB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,OAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAiCL;;;EAAA,mBA5BN,YAAA;EARiB;;;;;;;;;;;;;;AAoCJ;;EAVnB,KAAA,IAAS,OAAA;;;;EAUT,QAAA,IAAY,OAAA;AAAA"}
@@ -18,14 +18,20 @@ var StorageConnector = class extends BaseConnector {
18
18
  this.watchedFiles = ["src/config/storage.ts", "src/config/storage.tsx"];
19
19
  }
20
20
  /**
21
- * Initialize cache connection
21
+ * Initialize storage.
22
22
  *
23
23
  * Unlike its siblings (database, cache, herald), this connector does NOT
24
24
  * early-return when `config.get("storage")` is absent. Storage is
25
- * intentionally always-on: `storage.init()` falls back to the built-in
26
- * `local` driver (see `storageConfig("default", "local")`), so file storage
27
- * works out of the box even when a project ships no `src/config/storage.ts`.
28
- * A config-presence guard here would break that default local storage.
25
+ * intentionally always-on: `storage.init()` registers a built-in `local`
26
+ * driver rooted at `uploadsPath()` before reading configured drivers, so file
27
+ * storage works out of the box even when a project ships no
28
+ * `src/config/storage.ts`. A config-presence guard here would break that.
29
+ *
30
+ * That fallback is real as of 4.11.0. Until then this comment described it
31
+ * and nothing implemented it, so every app without `src/config/storage.ts`
32
+ * died at boot with `Storage driver "local" is not configured` — a decision
33
+ * to skip the guard, taken on the authority of behaviour nobody had written.
34
+ * See `Storage.registerBuiltInLocalDriver`.
29
35
  */
30
36
  async start() {
31
37
  await loadS3();
@@ -1 +1 @@
1
- {"version":3,"file":"storage.connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"sourcesContent":["import { storage } from \"../storage\";\nimport { loadS3 } from \"../storage/drivers/cloud-driver\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\n/**\n * Cache Connector\n * Manages cache engine connection lifecycle\n */\nexport class StorageConnector extends BaseConnector {\n public readonly name = \"storage\";\n public readonly priority = ConnectorPriority.STORAGE;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger cache restart\n */\n protected readonly watchedFiles = [\"src/config/storage.ts\", \"src/config/storage.tsx\"];\n\n /**\n * Initialize cache connection\n *\n * Unlike its siblings (database, cache, herald), this connector does NOT\n * early-return when `config.get(\"storage\")` is absent. Storage is\n * intentionally always-on: `storage.init()` falls back to the built-in\n * `local` driver (see `storageConfig(\"default\", \"local\")`), so file storage\n * works out of the box even when a project ships no `src/config/storage.ts`.\n * A config-presence guard here would break that default local storage.\n */\n public async start(): Promise<void> {\n await loadS3();\n await storage.init();\n\n this.active = true;\n }\n\n /**\n * Shutdown cache connection\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n storage.reset();\n\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;AASA,IAAa,mBAAb,cAAsC,cAAc;;;cAC3B;;;sBAOW,CAAC,yBAAyB,wBAAwB;;;;;;;;;;;;CAYpF,MAAa,QAAuB;EAClC,MAAM,OAAO;EACb,MAAM,QAAQ,KAAK;EAEnB,KAAK,SAAS;CAChB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,QAAQ,MAAM;EAEd,KAAK,SAAS;CAChB;AACF"}
1
+ {"version":3,"file":"storage.connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"sourcesContent":["import { storage } from \"../storage\";\nimport { loadS3 } from \"../storage/drivers/cloud-driver\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\n/**\n * Cache Connector\n * Manages cache engine connection lifecycle\n */\nexport class StorageConnector extends BaseConnector {\n public readonly name = \"storage\";\n public readonly priority = ConnectorPriority.STORAGE;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger cache restart\n */\n protected readonly watchedFiles = [\"src/config/storage.ts\", \"src/config/storage.tsx\"];\n\n /**\n * Initialize storage.\n *\n * Unlike its siblings (database, cache, herald), this connector does NOT\n * early-return when `config.get(\"storage\")` is absent. Storage is\n * intentionally always-on: `storage.init()` registers a built-in `local`\n * driver rooted at `uploadsPath()` before reading configured drivers, so file\n * storage works out of the box even when a project ships no\n * `src/config/storage.ts`. A config-presence guard here would break that.\n *\n * That fallback is real as of 4.11.0. Until then this comment described it\n * and nothing implemented it, so every app without `src/config/storage.ts`\n * died at boot with `Storage driver \"local\" is not configured` — a decision\n * to skip the guard, taken on the authority of behaviour nobody had written.\n * See `Storage.registerBuiltInLocalDriver`.\n */\n public async start(): Promise<void> {\n await loadS3();\n await storage.init();\n\n this.active = true;\n }\n\n /**\n * Shutdown cache connection\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n storage.reset();\n\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;AASA,IAAa,mBAAb,cAAsC,cAAc;;;cAC3B;;;sBAOW,CAAC,yBAAyB,wBAAwB;;;;;;;;;;;;;;;;;;CAkBpF,MAAa,QAAuB;EAClC,MAAM,OAAO;EACb,MAAM,QAAQ,KAAK;EAEnB,KAAK,SAAS;CAChB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,QAAQ,MAAM;EAEd,KAAK,SAAS;CAChB;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { TranspileCache, cacheKey } from "./transpile-cache.mjs";
2
2
  import { sourceSlug } from "./source-slug.mjs";
3
- import { transformSync } from "esbuild";
4
3
  import { readFileSync } from "node:fs";
4
+ import { transformSync } from "esbuild";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  //#region ../core/src/dev-server/loader/load-hook.ts
@@ -280,7 +280,7 @@ export default {
280
280
  },
281
281
  } as SocketOptions;
282
282
  `;
283
- const communicatorsConfigStub = `import { env } from "@mongez/dotenv";
283
+ const communicatorsConfigStub = `import { env } from "@warlock.js/core";
284
284
  import type { BrokerConfigurations, RabbitMQClientOptions } from "@warlock.js/herald";
285
285
 
286
286
  const heraldConfigurations: BrokerConfigurations<RabbitMQClientOptions> = {