@warlock.js/core 5.6.0 → 5.8.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 (94) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/esm/benchmark/profiler.d.mts.map +1 -1
  3. package/esm/benchmark/profiler.mjs +4 -3
  4. package/esm/benchmark/profiler.mjs.map +1 -1
  5. package/esm/cli/cli-commands.manager.mjs +12 -9
  6. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  7. package/esm/cli/commands/doctor/checks/index.mjs +2 -0
  8. package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
  9. package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs +55 -0
  10. package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs.map +1 -0
  11. package/esm/cli/commands/generate/generators/controller.generator.mjs +4 -4
  12. package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
  13. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  14. package/esm/cli/commands/generate/generators/model.generator.mjs +4 -4
  15. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  17. package/esm/cli/commands/generate/generators/repository.generator.mjs +4 -4
  18. package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
  19. package/esm/cli/commands/generate/generators/resource.generator.mjs +4 -4
  20. package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
  21. package/esm/cli/commands/generate/generators/service.generator.mjs +4 -4
  22. package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
  23. package/esm/cli/commands/generate/templates/stubs.mjs +6 -3
  24. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  25. package/esm/cli/commands/routes/format-routes-table.mjs +3 -3
  26. package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
  27. package/esm/cli/parse-cli-args.mjs +1 -0
  28. package/esm/cli/parse-cli-args.mjs.map +1 -1
  29. package/esm/cli/string-similarity.mjs +13 -4
  30. package/esm/cli/string-similarity.mjs.map +1 -1
  31. package/esm/commands/cli-command.d.mts.map +1 -1
  32. package/esm/commands/cli-command.mjs +5 -2
  33. package/esm/commands/cli-command.mjs.map +1 -1
  34. package/esm/config/config-loader.mjs +3 -1
  35. package/esm/config/config-loader.mjs.map +1 -1
  36. package/esm/dev-server/dependency-graph.d.mts.map +1 -1
  37. package/esm/dev-server/dependency-graph.mjs +2 -1
  38. package/esm/dev-server/dependency-graph.mjs.map +1 -1
  39. package/esm/dev-server/dev-logger.mjs +7 -5
  40. package/esm/dev-server/dev-logger.mjs.map +1 -1
  41. package/esm/dev-server/files-orchestrator.mjs +2 -1
  42. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  43. package/esm/dev-server/parse-imports.mjs +22 -8
  44. package/esm/dev-server/parse-imports.mjs.map +1 -1
  45. package/esm/encryption/encrypt.mjs +1 -0
  46. package/esm/encryption/encrypt.mjs.map +1 -1
  47. package/esm/generations/add-command.action.mjs +8 -2
  48. package/esm/generations/add-command.action.mjs.map +1 -1
  49. package/esm/generations/features/shared/relocate-conflicting-home-route.mjs +6 -1
  50. package/esm/generations/features/shared/relocate-conflicting-home-route.mjs.map +1 -1
  51. package/esm/generations/stubs.mjs +6 -6
  52. package/esm/generations/stubs.mjs.map +1 -1
  53. package/esm/http/boot-port-preflight.d.mts +25 -1
  54. package/esm/http/boot-port-preflight.d.mts.map +1 -1
  55. package/esm/http/boot-port-preflight.mjs +51 -2
  56. package/esm/http/boot-port-preflight.mjs.map +1 -1
  57. package/esm/http/index.d.mts +1 -1
  58. package/esm/http/index.mjs +1 -1
  59. package/esm/http/middleware/maintenance.middleware.mjs +1 -2
  60. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  61. package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
  62. package/esm/http/middleware/utils/cidr-match.mjs +1 -0
  63. package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
  64. package/esm/http/request.d.mts.map +1 -1
  65. package/esm/http/request.mjs +14 -7
  66. package/esm/http/request.mjs.map +1 -1
  67. package/esm/index.d.mts +2 -2
  68. package/esm/index.mjs +2 -2
  69. package/esm/repositories/adapters/cascade/filter-applicator.mjs +4 -2
  70. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  71. package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
  72. package/esm/router/positional-handler-diagnostics.mjs +9 -3
  73. package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
  74. package/esm/router/route-registry.mjs +1 -1
  75. package/esm/router/route-registry.mjs.map +1 -1
  76. package/esm/storage/scoped-storage.d.mts.map +1 -1
  77. package/esm/storage/scoped-storage.mjs +1 -0
  78. package/esm/storage/scoped-storage.mjs.map +1 -1
  79. package/esm/storage/storage.d.mts.map +1 -1
  80. package/esm/storage/storage.mjs +6 -2
  81. package/esm/storage/storage.mjs.map +1 -1
  82. package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
  83. package/esm/storage/utils/safe-fetch.mjs +6 -1
  84. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  85. package/esm/utils/load-environment.mjs +4 -1
  86. package/esm/utils/load-environment.mjs.map +1 -1
  87. package/esm/utils/recorded-environment-overrides.mjs +41 -0
  88. package/esm/utils/recorded-environment-overrides.mjs.map +1 -0
  89. package/esm/utils/version-compare.d.mts.map +1 -1
  90. package/esm/utils/version-compare.mjs +12 -4
  91. package/esm/utils/version-compare.mjs.map +1 -1
  92. package/esm/vite/lower-stage3-decorators.mjs +1 -0
  93. package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
  94. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"load-environment.mjs","names":[],"sources":["../../../../../../../core/src/utils/load-environment.ts"],"sourcesContent":["import { loadEnv, type EnvLoaderOptions } from \"@mongez/dotenv\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { detectEnvironmentOverrides } from \"./detect-environment-overrides\";\nimport { reportEnvironmentOverrides } from \"./report-environment-overrides\";\n\n/**\n * Core's env precedence policy, in one place because two callers load env:\n * this module at boot, and the dev server when an `.env` file changes.\n *\n * `@mongez/dotenv` defaults to `precedence: \"file-wins\"`, which is backwards.\n * The `.env` file is a checked-in DEFAULT; a variable already exported into\n * `process.env` is the deliberate, situational override — a second instance on\n * another port, CI pointed at another database, a container's configuration.\n * Under the default, `PORT=6060 warlock dev` produced a server on the `.env`\n * file's 3000 and said nothing. A default that silently beats an explicit\n * instruction is the wrong way round, so core opts into `process-wins`:\n * the file supplies only the keys the environment does not already carry.\n *\n * EMPTY STRING — `FOO=` in the environment counts as SET, and wins, so the\n * file's value is discarded and `env(\"FOO\")` returns `\"\"`. Chosen over\n * \"empty means absent\" because the loader cannot tell a deliberate blanking\n * from an accident, and only one of the two readings is expressible: an\n * operator who wants the file's value can unset the variable, whereas under\n * \"empty means absent\" an operator who wants a blank value has no way to ask\n * for one. It also matches POSIX (an exported empty variable is set), dotenv,\n * dotenv-flow and Vite. Note the consequence: a blank export beats the second\n * argument too, so `env(\"FOO\", \"fallback\")` yields `\"\"`, not the fallback.\n *\n * Keys this loader itself wrote are tracked by the library and are NOT treated\n * as process-provided, so editing `.env` during a dev session still takes\n * effect on reload rather than being pinned by the previous load's own writes.\n */\nexport const environmentLoaderOptions: EnvLoaderOptions = {\n precedence: \"process-wins\",\n};\n\n/**\n * The files `loadEnv()` will look for, in the order it considers them.\n *\n * Mirrors `@mongez/dotenv`'s own resolution — `.env.shared` first, then\n * `.env.<NODE_ENV>` if it exists, else plain `.env` — because the presence\n * check has to ask the same question the loader is about to ask.\n */\nfunction candidateEnvFiles(directory: string): string[] {\n const files = [path.join(directory, \".env\"), path.join(directory, \".env.shared\")];\n\n if (process.env.NODE_ENV) {\n files.push(path.join(directory, `.env.${process.env.NODE_ENV}`));\n }\n\n return files;\n}\n\n/**\n * The files `loadEnv()` will actually read, in its own read order.\n *\n * Differs from {@link candidateEnvFiles}, which lists every candidate to\n * answer \"is there anything to load at all\". `loadEnv()` itself only ever\n * reads `.env.shared` (if present) plus ONE of `.env.<NODE_ENV>` /\n * `.env` — the NODE_ENV-specific file when it exists, `.env` otherwise —\n * never both. Override detection has to mirror that exactly, or a key\n * declared only in a file `loadEnv()` would not have touched could be\n * reported as having \"lost\" a comparison it was never actually part of.\n */\nfunction resolveLoadedEnvFiles(directory: string): string[] {\n const files: string[] = [];\n const sharedFile = path.join(directory, \".env.shared\");\n\n if (existsSync(sharedFile)) {\n files.push(sharedFile);\n }\n\n const nodeEnvFile = process.env.NODE_ENV\n ? path.join(directory, `.env.${process.env.NODE_ENV}`)\n : undefined;\n const derivedFile =\n nodeEnvFile && existsSync(nodeEnvFile) ? nodeEnvFile : path.join(directory, \".env\");\n\n if (existsSync(derivedFile)) {\n files.push(derivedFile);\n }\n\n return files;\n}\n\n/**\n * Whether this process has already loaded its env files.\n *\n * Two callers reach here on a bootstrapping command — the CLI preload phase and\n * `bootstrap()` — and `loadEnv()` defaults to `override: true`, so a second pass\n * re-parses the same files and re-writes `process.env`. Anything set in between\n * (a `warlock.config.ts` module body, a config file, an orchestrator) would be\n * silently clobbered by the later load. Same shape as the defect where a\n * caller's test-server port was overwritten by a re-read of `.env`.\n *\n * A deliberate reload is still possible and still used: the dev server calls\n * `loadEnv()` directly when an `.env` file changes on disk, which is a reload\n * the user asked for rather than an accidental second pass.\n */\nlet environmentLoaded = false;\n\n/**\n * Load `.env` files when there are any, and do nothing when there are not.\n *\n * Loads at most **once per process** — see {@link environmentLoaded}.\n *\n * `@mongez/dotenv`'s `loadEnvFile` **throws** when the file is absent. That was\n * survivable while only `dev` loaded env; now that every command loads it —\n * because `warlock.config.ts` calls `env()` in its module body and had been\n * evaluated against an empty store under every command — an unguarded call\n * would turn \"this project has no .env\" from a non-event into a hard failure\n * of `warlock build`. A project without a `.env` is legitimate: config files\n * carry defaults, and containers inject their own variables.\n *\n * The guard lives here rather than waiting on a fix in `@mongez/dotenv`: core\n * decides its own env policy and must not depend on another package's release\n * schedule to avoid crashing a working build.\n */\nexport async function loadEnvironmentFiles(directory: string = process.cwd()): Promise<void> {\n if (environmentLoaded) {\n return;\n }\n\n const hasEnvFile = candidateEnvFiles(directory).some((file) => existsSync(file));\n\n if (!hasEnvFile) {\n // Not latched: a project may create its `.env` between a command's preload\n // and its bootstrap, and there is nothing to protect from a second override\n // when the first pass loaded nothing.\n return;\n }\n\n environmentLoaded = true;\n\n // Snapshot BEFORE `loadEnv()` runs: the loader writes into `process.env` for\n // every key its files supply, so a snapshot taken afterwards could no\n // longer distinguish an ambient override from a value the loader itself\n // just wrote — see `detectEnvironmentOverrides`.\n const processEnvironmentSnapshot = { ...process.env };\n\n // The precedence itself is correct (see `environmentLoaderOptions` above);\n // the defect this guards against is the SILENCE — `.env` losing to an\n // ambient value with no diagnostic at all.\n reportEnvironmentOverrides(\n detectEnvironmentOverrides(resolveLoadedEnvFiles(directory), processEnvironmentSnapshot),\n );\n\n // `dir` has to be forwarded: `loadEnv()` defaults it to `process.cwd()`, so\n // without this the existence check above asks about `directory` while the\n // load itself reads somewhere else entirely. Identical in production, where\n // `directory` IS `process.cwd()`.\n await loadEnv(undefined, { ...environmentLoaderOptions, dir: directory });\n}\n\n/**\n * Forget that env has been loaded, so the next call loads again.\n *\n * @internal For tests, which run many boots inside one process.\n */\nexport function resetLoadedEnvironment(): void {\n environmentLoaded = false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,2BAA6C,EACxD,YAAY,eACd;;;;;;;;AASA,SAAS,kBAAkB,WAA6B;CACtD,MAAM,QAAQ,CAAC,KAAK,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK,WAAW,aAAa,CAAC;CAEhF,IAAI,QAAQ,IAAI,UACd,MAAM,KAAK,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,CAAC;CAGjE,OAAO;AACT;;;;;;;;;;;;AAaA,SAAS,sBAAsB,WAA6B;CAC1D,MAAM,QAAkB,CAAC;CACzB,MAAM,aAAa,KAAK,KAAK,WAAW,aAAa;CAErD,IAAI,WAAW,UAAU,GACvB,MAAM,KAAK,UAAU;CAGvB,MAAM,cAAc,QAAQ,IAAI,WAC5B,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,IACnD;CACJ,MAAM,cACJ,eAAe,WAAW,WAAW,IAAI,cAAc,KAAK,KAAK,WAAW,MAAM;CAEpF,IAAI,WAAW,WAAW,GACxB,MAAM,KAAK,WAAW;CAGxB,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,IAAI,oBAAoB;;;;;;;;;;;;;;;;;;AAmBxB,eAAsB,qBAAqB,YAAoB,QAAQ,IAAI,GAAkB;CAC3F,IAAI,mBACF;CAKF,IAAI,CAFe,kBAAkB,SAAS,CAAC,CAAC,MAAM,SAAS,WAAW,IAAI,CAEhE,GAIZ;CAGF,oBAAoB;CAMpB,MAAM,6BAA6B,EAAE,GAAG,QAAQ,IAAI;CAKpD,2BACE,2BAA2B,sBAAsB,SAAS,GAAG,0BAA0B,CACzF;CAMA,MAAM,QAAQ,QAAW;EAAE,GAAG;EAA0B,KAAK;CAAU,CAAC;AAC1E"}
1
+ {"version":3,"file":"load-environment.mjs","names":[],"sources":["../../../../../../../core/src/utils/load-environment.ts"],"sourcesContent":["import { loadEnv, type EnvLoaderOptions } from \"@mongez/dotenv\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { detectEnvironmentOverrides } from \"./detect-environment-overrides\";\nimport { recordEnvironmentOverrides } from \"./recorded-environment-overrides\";\nimport { reportEnvironmentOverrides } from \"./report-environment-overrides\";\n\n/**\n * Core's env precedence policy, in one place because two callers load env:\n * this module at boot, and the dev server when an `.env` file changes.\n *\n * `@mongez/dotenv` defaults to `precedence: \"file-wins\"`, which is backwards.\n * The `.env` file is a checked-in DEFAULT; a variable already exported into\n * `process.env` is the deliberate, situational override — a second instance on\n * another port, CI pointed at another database, a container's configuration.\n * Under the default, `PORT=6060 warlock dev` produced a server on the `.env`\n * file's 3000 and said nothing. A default that silently beats an explicit\n * instruction is the wrong way round, so core opts into `process-wins`:\n * the file supplies only the keys the environment does not already carry.\n *\n * EMPTY STRING — `FOO=` in the environment counts as SET, and wins, so the\n * file's value is discarded and `env(\"FOO\")` returns `\"\"`. Chosen over\n * \"empty means absent\" because the loader cannot tell a deliberate blanking\n * from an accident, and only one of the two readings is expressible: an\n * operator who wants the file's value can unset the variable, whereas under\n * \"empty means absent\" an operator who wants a blank value has no way to ask\n * for one. It also matches POSIX (an exported empty variable is set), dotenv,\n * dotenv-flow and Vite. Note the consequence: a blank export beats the second\n * argument too, so `env(\"FOO\", \"fallback\")` yields `\"\"`, not the fallback.\n *\n * Keys this loader itself wrote are tracked by the library and are NOT treated\n * as process-provided, so editing `.env` during a dev session still takes\n * effect on reload rather than being pinned by the previous load's own writes.\n */\nexport const environmentLoaderOptions: EnvLoaderOptions = {\n precedence: \"process-wins\",\n};\n\n/**\n * The files `loadEnv()` will look for, in the order it considers them.\n *\n * Mirrors `@mongez/dotenv`'s own resolution — `.env.shared` first, then\n * `.env.<NODE_ENV>` if it exists, else plain `.env` — because the presence\n * check has to ask the same question the loader is about to ask.\n */\nfunction candidateEnvFiles(directory: string): string[] {\n const files = [path.join(directory, \".env\"), path.join(directory, \".env.shared\")];\n\n if (process.env.NODE_ENV) {\n files.push(path.join(directory, `.env.${process.env.NODE_ENV}`));\n }\n\n return files;\n}\n\n/**\n * The files `loadEnv()` will actually read, in its own read order.\n *\n * Differs from {@link candidateEnvFiles}, which lists every candidate to\n * answer \"is there anything to load at all\". `loadEnv()` itself only ever\n * reads `.env.shared` (if present) plus ONE of `.env.<NODE_ENV>` /\n * `.env` — the NODE_ENV-specific file when it exists, `.env` otherwise —\n * never both. Override detection has to mirror that exactly, or a key\n * declared only in a file `loadEnv()` would not have touched could be\n * reported as having \"lost\" a comparison it was never actually part of.\n */\nfunction resolveLoadedEnvFiles(directory: string): string[] {\n const files: string[] = [];\n const sharedFile = path.join(directory, \".env.shared\");\n\n if (existsSync(sharedFile)) {\n files.push(sharedFile);\n }\n\n const nodeEnvFile = process.env.NODE_ENV\n ? path.join(directory, `.env.${process.env.NODE_ENV}`)\n : undefined;\n const derivedFile =\n nodeEnvFile && existsSync(nodeEnvFile) ? nodeEnvFile : path.join(directory, \".env\");\n\n if (existsSync(derivedFile)) {\n files.push(derivedFile);\n }\n\n return files;\n}\n\n/**\n * Whether this process has already loaded its env files.\n *\n * Two callers reach here on a bootstrapping command — the CLI preload phase and\n * `bootstrap()` — and `loadEnv()` defaults to `override: true`, so a second pass\n * re-parses the same files and re-writes `process.env`. Anything set in between\n * (a `warlock.config.ts` module body, a config file, an orchestrator) would be\n * silently clobbered by the later load. Same shape as the defect where a\n * caller's test-server port was overwritten by a re-read of `.env`.\n *\n * A deliberate reload is still possible and still used: the dev server calls\n * `loadEnv()` directly when an `.env` file changes on disk, which is a reload\n * the user asked for rather than an accidental second pass.\n */\nlet environmentLoaded = false;\n\n/**\n * Load `.env` files when there are any, and do nothing when there are not.\n *\n * Loads at most **once per process** — see {@link environmentLoaded}.\n *\n * `@mongez/dotenv`'s `loadEnvFile` **throws** when the file is absent. That was\n * survivable while only `dev` loaded env; now that every command loads it —\n * because `warlock.config.ts` calls `env()` in its module body and had been\n * evaluated against an empty store under every command — an unguarded call\n * would turn \"this project has no .env\" from a non-event into a hard failure\n * of `warlock build`. A project without a `.env` is legitimate: config files\n * carry defaults, and containers inject their own variables.\n *\n * The guard lives here rather than waiting on a fix in `@mongez/dotenv`: core\n * decides its own env policy and must not depend on another package's release\n * schedule to avoid crashing a working build.\n */\nexport async function loadEnvironmentFiles(directory: string = process.cwd()): Promise<void> {\n if (environmentLoaded) {\n return;\n }\n\n const hasEnvFile = candidateEnvFiles(directory).some((file) => existsSync(file));\n\n if (!hasEnvFile) {\n // Not latched: a project may create its `.env` between a command's preload\n // and its bootstrap, and there is nothing to protect from a second override\n // when the first pass loaded nothing.\n return;\n }\n\n environmentLoaded = true;\n\n // Snapshot BEFORE `loadEnv()` runs: the loader writes into `process.env` for\n // every key its files supply, so a snapshot taken afterwards could no\n // longer distinguish an ambient override from a value the loader itself\n // just wrote — see `detectEnvironmentOverrides`.\n const processEnvironmentSnapshot = { ...process.env };\n\n // The precedence itself is correct (see `environmentLoaderOptions` above);\n // the defect this guards against is the SILENCE — `.env` losing to an\n // ambient value with no diagnostic at all.\n const overrides = detectEnvironmentOverrides(\n resolveLoadedEnvFiles(directory),\n processEnvironmentSnapshot,\n );\n\n // Print the overrides here AND retain them: a later point in the boot (the\n // port-collision reporter) needs to know an ambient `HTTP_PORT` beat `.env`\n // to avoid advising a rebuild that the env var would just override again —\n // see `recorded-environment-overrides.ts` (finding 8782b840).\n recordEnvironmentOverrides(overrides);\n reportEnvironmentOverrides(overrides);\n\n // `dir` has to be forwarded: `loadEnv()` defaults it to `process.cwd()`, so\n // without this the existence check above asks about `directory` while the\n // load itself reads somewhere else entirely. Identical in production, where\n // `directory` IS `process.cwd()`.\n await loadEnv(undefined, { ...environmentLoaderOptions, dir: directory });\n}\n\n/**\n * Forget that env has been loaded, so the next call loads again.\n *\n * @internal For tests, which run many boots inside one process.\n */\nexport function resetLoadedEnvironment(): void {\n environmentLoaded = false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,2BAA6C,EACxD,YAAY,eACd;;;;;;;;AASA,SAAS,kBAAkB,WAA6B;CACtD,MAAM,QAAQ,CAAC,KAAK,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK,WAAW,aAAa,CAAC;CAEhF,IAAI,QAAQ,IAAI,UACd,MAAM,KAAK,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,CAAC;CAGjE,OAAO;AACT;;;;;;;;;;;;AAaA,SAAS,sBAAsB,WAA6B;CAC1D,MAAM,QAAkB,CAAC;CACzB,MAAM,aAAa,KAAK,KAAK,WAAW,aAAa;CAErD,IAAI,WAAW,UAAU,GACvB,MAAM,KAAK,UAAU;CAGvB,MAAM,cAAc,QAAQ,IAAI,WAC5B,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,IACnD;CACJ,MAAM,cACJ,eAAe,WAAW,WAAW,IAAI,cAAc,KAAK,KAAK,WAAW,MAAM;CAEpF,IAAI,WAAW,WAAW,GACxB,MAAM,KAAK,WAAW;CAGxB,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,IAAI,oBAAoB;;;;;;;;;;;;;;;;;;AAmBxB,eAAsB,qBAAqB,YAAoB,QAAQ,IAAI,GAAkB;CAC3F,IAAI,mBACF;CAKF,IAAI,CAFe,kBAAkB,SAAS,CAAC,CAAC,MAAM,SAAS,WAAW,IAAI,CAEhE,GAIZ;CAGF,oBAAoB;CAMpB,MAAM,6BAA6B,EAAE,GAAG,QAAQ,IAAI;CAKpD,MAAM,YAAY,2BAChB,sBAAsB,SAAS,GAC/B,0BACF;CAMA,2BAA2B,SAAS;CACpC,2BAA2B,SAAS;CAMpC,MAAM,QAAQ,QAAW;EAAE,GAAG;EAA0B,KAAK;CAAU,CAAC;AAC1E"}
@@ -0,0 +1,41 @@
1
+ //#region ../core/src/utils/recorded-environment-overrides.ts
2
+ /**
3
+ * The environment overrides {@link detectEnvironmentOverrides} found on this
4
+ * boot, kept so a LATER point in the boot can ask which `.env` keys the ambient
5
+ * process environment beat — WITHOUT re-reading `.env` or `process.env` a second
6
+ * time, and without re-deriving the comparison.
7
+ *
8
+ * Why this exists: `reportPortInUse` (`src/http/boot-port-preflight.ts`) runs
9
+ * far downstream of `loadEnvironmentFiles()` and needs to know whether the
10
+ * colliding `http.port` came from an ambient `HTTP_PORT` (in which case
11
+ * "edit src/config/http.ts and rebuild" is ACTIVELY WRONG — the env var wins,
12
+ * so the rebuild changes nothing) or from config (where that advice is right).
13
+ * The detector already knows this at env-load time; the provenance has to
14
+ * travel to the point of use rather than be recomputed there (finding
15
+ * 8782b840, requirement 3).
16
+ *
17
+ * Keyed by env var name, last-write-wins — a single boot loads env at most once
18
+ * (`loadEnvironmentFiles` latches), and a deliberate dev-server reload re-records
19
+ * the current truth.
20
+ */
21
+ let recorded = /* @__PURE__ */ new Map();
22
+ /**
23
+ * Record the overrides detected on this boot. Replaces any prior record, so a
24
+ * re-load reflects the current environment rather than accumulating stale keys.
25
+ */
26
+ function recordEnvironmentOverrides(overrides) {
27
+ recorded = new Map(overrides.map((override) => [override.key, override]));
28
+ }
29
+ /**
30
+ * The recorded override for `key`, or `undefined` if the ambient environment
31
+ * did not beat a `.env` value for it on this boot. The returned object is the
32
+ * detector's own finding (effective value + the `.env` value it overrode), so a
33
+ * caller can name both without recomputing either.
34
+ */
35
+ function getRecordedEnvironmentOverride(key) {
36
+ return recorded.get(key);
37
+ }
38
+
39
+ //#endregion
40
+ export { getRecordedEnvironmentOverride, recordEnvironmentOverrides };
41
+ //# sourceMappingURL=recorded-environment-overrides.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorded-environment-overrides.mjs","names":[],"sources":["../../../../../../../core/src/utils/recorded-environment-overrides.ts"],"sourcesContent":["import type { EnvironmentOverride } from \"./detect-environment-overrides\";\n\n/**\n * The environment overrides {@link detectEnvironmentOverrides} found on this\n * boot, kept so a LATER point in the boot can ask which `.env` keys the ambient\n * process environment beat — WITHOUT re-reading `.env` or `process.env` a second\n * time, and without re-deriving the comparison.\n *\n * Why this exists: `reportPortInUse` (`src/http/boot-port-preflight.ts`) runs\n * far downstream of `loadEnvironmentFiles()` and needs to know whether the\n * colliding `http.port` came from an ambient `HTTP_PORT` (in which case\n * \"edit src/config/http.ts and rebuild\" is ACTIVELY WRONG — the env var wins,\n * so the rebuild changes nothing) or from config (where that advice is right).\n * The detector already knows this at env-load time; the provenance has to\n * travel to the point of use rather than be recomputed there (finding\n * 8782b840, requirement 3).\n *\n * Keyed by env var name, last-write-wins — a single boot loads env at most once\n * (`loadEnvironmentFiles` latches), and a deliberate dev-server reload re-records\n * the current truth.\n */\nlet recorded = new Map<string, EnvironmentOverride>();\n\n/**\n * Record the overrides detected on this boot. Replaces any prior record, so a\n * re-load reflects the current environment rather than accumulating stale keys.\n */\nexport function recordEnvironmentOverrides(overrides: EnvironmentOverride[]): void {\n recorded = new Map(overrides.map((override) => [override.key, override]));\n}\n\n/**\n * The recorded override for `key`, or `undefined` if the ambient environment\n * did not beat a `.env` value for it on this boot. The returned object is the\n * detector's own finding (effective value + the `.env` value it overrode), so a\n * caller can name both without recomputing either.\n */\nexport function getRecordedEnvironmentOverride(key: string): EnvironmentOverride | undefined {\n return recorded.get(key);\n}\n\n/**\n * Forget every recorded override.\n *\n * @internal For tests, which run many boots inside one process.\n */\nexport function clearRecordedEnvironmentOverrides(): void {\n recorded = new Map();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,IAAI,2BAAW,IAAI,IAAiC;;;;;AAMpD,SAAgB,2BAA2B,WAAwC;CACjF,WAAW,IAAI,IAAI,UAAU,KAAK,aAAa,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC;AAC1E;;;;;;;AAQA,SAAgB,+BAA+B,KAA8C;CAC3F,OAAO,SAAS,IAAI,GAAG;AACzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"version-compare.d.mts","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"mappings":";;AAkGA;;;;AAA8D;;;;;;;;;;iBAA9C,cAAA,CAAe,MAAA,UAAgB,OAAe"}
1
+ {"version":3,"file":"version-compare.d.mts","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"mappings":";;AAiHA;;;;AAA8D;;;;;;;;;;iBAA9C,cAAA,CAAe,MAAA,UAAgB,OAAe"}
@@ -6,17 +6,21 @@
6
6
  */
7
7
  function parseVersion(version) {
8
8
  const [coreAndPrerelease] = version.trim().replace(/^v/i, "").split("+");
9
+ if (!coreAndPrerelease) return;
9
10
  const [core, ...prereleaseParts] = coreAndPrerelease.split("-");
11
+ if (!core) return;
10
12
  const segments = core.split(".");
11
13
  if (segments.length !== 3) return;
12
14
  const numbers = segments.map((segment) => Number(segment));
13
15
  if (numbers.some((value) => !Number.isInteger(value) || value < 0)) return;
14
16
  const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join("-").split(".") : [];
17
+ const [major, minor, patch] = numbers;
18
+ if (major === void 0 || minor === void 0 || patch === void 0) return;
15
19
  return {
16
20
  core: [
17
- numbers[0],
18
- numbers[1],
19
- numbers[2]
21
+ major,
22
+ minor,
23
+ patch
20
24
  ],
21
25
  prerelease
22
26
  };
@@ -57,7 +61,11 @@ function isNewerVersion(latest, current) {
57
61
  const latestParsed = parseVersion(latest);
58
62
  const currentParsed = parseVersion(current);
59
63
  if (!latestParsed || !currentParsed) return false;
60
- for (let index = 0; index < 3; index++) if (latestParsed.core[index] !== currentParsed.core[index]) return latestParsed.core[index] > currentParsed.core[index];
64
+ for (let index = 0; index < 3; index++) {
65
+ const latestPart = latestParsed.core[index];
66
+ const currentPart = currentParsed.core[index];
67
+ if (latestPart !== currentPart) return (latestPart ?? 0) > (currentPart ?? 0);
68
+ }
61
69
  const latestIsStable = latestParsed.prerelease.length === 0;
62
70
  const currentIsStable = currentParsed.prerelease.length === 0;
63
71
  if (latestIsStable && currentIsStable) return false;
@@ -1 +1 @@
1
- {"version":3,"file":"version-compare.mjs","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"sourcesContent":["/**\n * Zero-dependency semver comparison — just enough for the framework's\n * self-update tooling (the dev-server update notice + the `warlock update`\n * command).\n *\n * We deliberately avoid pulling in a semver library: the only question these\n * callers ask is \"is the published version newer than what's installed?\".\n */\n\ntype ParsedVersion = {\n /** Numeric core as `[major, minor, patch]`. */\n core: [number, number, number];\n /** Dot-separated prerelease identifiers; empty for a stable release. */\n prerelease: string[];\n};\n\n/**\n * Parse a semver string into its numeric core + prerelease identifiers.\n * Tolerates a leading `v` and ignores build metadata (`+…`). Returns\n * `undefined` when the core isn't exactly three non-negative integers.\n */\nfunction parseVersion(version: string): ParsedVersion | undefined {\n const cleaned = version.trim().replace(/^v/i, \"\");\n const [coreAndPrerelease] = cleaned.split(\"+\"); // drop build metadata\n const [core, ...prereleaseParts] = coreAndPrerelease.split(\"-\");\n const segments = core.split(\".\");\n\n if (segments.length !== 3) {\n return undefined;\n }\n\n const numbers = segments.map((segment) => Number(segment));\n\n if (numbers.some((value) => !Number.isInteger(value) || value < 0)) {\n return undefined;\n }\n\n const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join(\"-\").split(\".\") : [];\n\n return {\n core: [numbers[0], numbers[1], numbers[2]],\n prerelease,\n };\n}\n\n/**\n * Compare two prerelease identifier lists per semver rules: numeric\n * identifiers compare numerically, alphanumeric ones lexically, a numeric\n * identifier ranks below an alphanumeric one, and a longer list wins when\n * otherwise equal. Returns a negative / zero / positive number.\n */\nfunction comparePrerelease(left: string[], right: string[]): number {\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index++) {\n const a = left[index];\n const b = right[index];\n\n if (a === undefined) {\n return -1;\n }\n\n if (b === undefined) {\n return 1;\n }\n\n const aIsNumeric = /^\\d+$/.test(a);\n const bIsNumeric = /^\\d+$/.test(b);\n\n if (aIsNumeric && bIsNumeric) {\n const diff = Number(a) - Number(b);\n\n if (diff !== 0) {\n return diff;\n }\n\n continue;\n }\n\n if (aIsNumeric !== bIsNumeric) {\n return aIsNumeric ? -1 : 1;\n }\n\n if (a !== b) {\n return a < b ? -1 : 1;\n }\n }\n\n return 0;\n}\n\n/**\n * Return `true` when `latest` is a strictly newer semantic version than\n * `current`. Compares the numeric `major.minor.patch` first, then treats a\n * stable release as newer than a prerelease of the same core\n * (`1.2.0` > `1.2.0-beta`). Unparseable input yields `false` — we never nag\n * on a version string we can't understand.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const latestParsed = parseVersion(latest);\n const currentParsed = parseVersion(current);\n\n if (!latestParsed || !currentParsed) {\n return false;\n }\n\n for (let index = 0; index < 3; index++) {\n if (latestParsed.core[index] !== currentParsed.core[index]) {\n return latestParsed.core[index] > currentParsed.core[index];\n }\n }\n\n // Equal numeric core — order by prerelease (a stable release beats a prerelease).\n const latestIsStable = latestParsed.prerelease.length === 0;\n const currentIsStable = currentParsed.prerelease.length === 0;\n\n if (latestIsStable && currentIsStable) {\n return false;\n }\n\n if (latestIsStable !== currentIsStable) {\n return latestIsStable;\n }\n\n return comparePrerelease(latestParsed.prerelease, currentParsed.prerelease) > 0;\n}\n"],"mappings":";;;;;;AAqBA,SAAS,aAAa,SAA4C;CAEhE,MAAM,CAAC,qBADS,QAAQ,KAAK,CAAC,CAAC,QAAQ,OAAO,EACZ,CAAC,CAAC,MAAM,GAAG;CAC7C,MAAM,CAAC,MAAM,GAAG,mBAAmB,kBAAkB,MAAM,GAAG;CAC9D,MAAM,WAAW,KAAK,MAAM,GAAG;CAE/B,IAAI,SAAS,WAAW,GACtB;CAGF,MAAM,UAAU,SAAS,KAAK,YAAY,OAAO,OAAO,CAAC;CAEzD,IAAI,QAAQ,MAAM,UAAU,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,CAAC,GAC/D;CAGF,MAAM,aAAa,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;CAExF,OAAO;EACL,MAAM;GAAC,QAAQ;GAAI,QAAQ;GAAI,QAAQ;EAAE;EACzC;CACF;AACF;;;;;;;AAQA,SAAS,kBAAkB,MAAgB,OAAyB;CAClE,MAAM,SAAS,KAAK,IAAI,KAAK,QAAQ,MAAM,MAAM;CAEjD,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS;EAC3C,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,MAAM;EAEhB,IAAI,MAAM,QACR,OAAO;EAGT,IAAI,MAAM,QACR,OAAO;EAGT,MAAM,aAAa,QAAQ,KAAK,CAAC;EACjC,MAAM,aAAa,QAAQ,KAAK,CAAC;EAEjC,IAAI,cAAc,YAAY;GAC5B,MAAM,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC;GAEjC,IAAI,SAAS,GACX,OAAO;GAGT;EACF;EAEA,IAAI,eAAe,YACjB,OAAO,aAAa,KAAK;EAG3B,IAAI,MAAM,GACR,OAAO,IAAI,IAAI,KAAK;CAExB;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,QAAgB,SAA0B;CACvE,MAAM,eAAe,aAAa,MAAM;CACxC,MAAM,gBAAgB,aAAa,OAAO;CAE1C,IAAI,CAAC,gBAAgB,CAAC,eACpB,OAAO;CAGT,KAAK,IAAI,QAAQ,GAAG,QAAQ,GAAG,SAC7B,IAAI,aAAa,KAAK,WAAW,cAAc,KAAK,QAClD,OAAO,aAAa,KAAK,SAAS,cAAc,KAAK;CAKzD,MAAM,iBAAiB,aAAa,WAAW,WAAW;CAC1D,MAAM,kBAAkB,cAAc,WAAW,WAAW;CAE5D,IAAI,kBAAkB,iBACpB,OAAO;CAGT,IAAI,mBAAmB,iBACrB,OAAO;CAGT,OAAO,kBAAkB,aAAa,YAAY,cAAc,UAAU,IAAI;AAChF"}
1
+ {"version":3,"file":"version-compare.mjs","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"sourcesContent":["/**\n * Zero-dependency semver comparison — just enough for the framework's\n * self-update tooling (the dev-server update notice + the `warlock update`\n * command).\n *\n * We deliberately avoid pulling in a semver library: the only question these\n * callers ask is \"is the published version newer than what's installed?\".\n */\n\ntype ParsedVersion = {\n /** Numeric core as `[major, minor, patch]`. */\n core: [number, number, number];\n /** Dot-separated prerelease identifiers; empty for a stable release. */\n prerelease: string[];\n};\n\n/**\n * Parse a semver string into its numeric core + prerelease identifiers.\n * Tolerates a leading `v` and ignores build metadata (`+…`). Returns\n * `undefined` when the core isn't exactly three non-negative integers.\n */\nfunction parseVersion(version: string): ParsedVersion | undefined {\n const cleaned = version.trim().replace(/^v/i, \"\");\n const [coreAndPrerelease] = cleaned.split(\"+\"); // drop build metadata\n\n if (!coreAndPrerelease) {\n return undefined;\n }\n\n const [core, ...prereleaseParts] = coreAndPrerelease.split(\"-\");\n\n if (!core) {\n return undefined;\n }\n const segments = core.split(\".\");\n\n if (segments.length !== 3) {\n return undefined;\n }\n\n const numbers = segments.map((segment) => Number(segment));\n\n if (numbers.some((value) => !Number.isInteger(value) || value < 0)) {\n return undefined;\n }\n\n const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join(\"-\").split(\".\") : [];\n\n const [major, minor, patch] = numbers;\n\n if (major === undefined || minor === undefined || patch === undefined) {\n return undefined;\n }\n\n return {\n core: [major, minor, patch],\n prerelease,\n };\n}\n\n/**\n * Compare two prerelease identifier lists per semver rules: numeric\n * identifiers compare numerically, alphanumeric ones lexically, a numeric\n * identifier ranks below an alphanumeric one, and a longer list wins when\n * otherwise equal. Returns a negative / zero / positive number.\n */\nfunction comparePrerelease(left: string[], right: string[]): number {\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index++) {\n const a = left[index];\n const b = right[index];\n\n if (a === undefined) {\n return -1;\n }\n\n if (b === undefined) {\n return 1;\n }\n\n const aIsNumeric = /^\\d+$/.test(a);\n const bIsNumeric = /^\\d+$/.test(b);\n\n if (aIsNumeric && bIsNumeric) {\n const diff = Number(a) - Number(b);\n\n if (diff !== 0) {\n return diff;\n }\n\n continue;\n }\n\n if (aIsNumeric !== bIsNumeric) {\n return aIsNumeric ? -1 : 1;\n }\n\n if (a !== b) {\n return a < b ? -1 : 1;\n }\n }\n\n return 0;\n}\n\n/**\n * Return `true` when `latest` is a strictly newer semantic version than\n * `current`. Compares the numeric `major.minor.patch` first, then treats a\n * stable release as newer than a prerelease of the same core\n * (`1.2.0` > `1.2.0-beta`). Unparseable input yields `false` — we never nag\n * on a version string we can't understand.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const latestParsed = parseVersion(latest);\n const currentParsed = parseVersion(current);\n\n if (!latestParsed || !currentParsed) {\n return false;\n }\n\n for (let index = 0; index < 3; index++) {\n const latestPart = latestParsed.core[index];\n const currentPart = currentParsed.core[index];\n\n // A parsed semver core is always three numbers, so both reads hold. Treating\n // a missing part as 0 rather than asserting: this decides whether to tell\n // the user a NEWER VERSION EXISTS, and `undefined > undefined` is `false` —\n // so an assertion would silently answer \"not newer\" and suppress the\n // notice, which is the failure nobody would ever notice happening.\n if (latestPart !== currentPart) {\n return (latestPart ?? 0) > (currentPart ?? 0);\n }\n }\n\n // Equal numeric core — order by prerelease (a stable release beats a prerelease).\n const latestIsStable = latestParsed.prerelease.length === 0;\n const currentIsStable = currentParsed.prerelease.length === 0;\n\n if (latestIsStable && currentIsStable) {\n return false;\n }\n\n if (latestIsStable !== currentIsStable) {\n return latestIsStable;\n }\n\n return comparePrerelease(latestParsed.prerelease, currentParsed.prerelease) > 0;\n}\n"],"mappings":";;;;;;AAqBA,SAAS,aAAa,SAA4C;CAEhE,MAAM,CAAC,qBADS,QAAQ,KAAK,CAAC,CAAC,QAAQ,OAAO,EACZ,CAAC,CAAC,MAAM,GAAG;CAE7C,IAAI,CAAC,mBACH;CAGF,MAAM,CAAC,MAAM,GAAG,mBAAmB,kBAAkB,MAAM,GAAG;CAE9D,IAAI,CAAC,MACH;CAEF,MAAM,WAAW,KAAK,MAAM,GAAG;CAE/B,IAAI,SAAS,WAAW,GACtB;CAGF,MAAM,UAAU,SAAS,KAAK,YAAY,OAAO,OAAO,CAAC;CAEzD,IAAI,QAAQ,MAAM,UAAU,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,CAAC,GAC/D;CAGF,MAAM,aAAa,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;CAExF,MAAM,CAAC,OAAO,OAAO,SAAS;CAE9B,IAAI,UAAU,UAAa,UAAU,UAAa,UAAU,QAC1D;CAGF,OAAO;EACL,MAAM;GAAC;GAAO;GAAO;EAAK;EAC1B;CACF;AACF;;;;;;;AAQA,SAAS,kBAAkB,MAAgB,OAAyB;CAClE,MAAM,SAAS,KAAK,IAAI,KAAK,QAAQ,MAAM,MAAM;CAEjD,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS;EAC3C,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,MAAM;EAEhB,IAAI,MAAM,QACR,OAAO;EAGT,IAAI,MAAM,QACR,OAAO;EAGT,MAAM,aAAa,QAAQ,KAAK,CAAC;EACjC,MAAM,aAAa,QAAQ,KAAK,CAAC;EAEjC,IAAI,cAAc,YAAY;GAC5B,MAAM,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC;GAEjC,IAAI,SAAS,GACX,OAAO;GAGT;EACF;EAEA,IAAI,eAAe,YACjB,OAAO,aAAa,KAAK;EAG3B,IAAI,MAAM,GACR,OAAO,IAAI,IAAI,KAAK;CAExB;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,QAAgB,SAA0B;CACvE,MAAM,eAAe,aAAa,MAAM;CACxC,MAAM,gBAAgB,aAAa,OAAO;CAE1C,IAAI,CAAC,gBAAgB,CAAC,eACpB,OAAO;CAGT,KAAK,IAAI,QAAQ,GAAG,QAAQ,GAAG,SAAS;EACtC,MAAM,aAAa,aAAa,KAAK;EACrC,MAAM,cAAc,cAAc,KAAK;EAOvC,IAAI,eAAe,aACjB,QAAQ,cAAc,MAAM,eAAe;CAE/C;CAGA,MAAM,iBAAiB,aAAa,WAAW,WAAW;CAC1D,MAAM,kBAAkB,cAAc,WAAW,WAAW;CAE5D,IAAI,kBAAkB,iBACpB,OAAO;CAGT,IAAI,mBAAmB,iBACrB,OAAO;CAGT,OAAO,kBAAkB,aAAa,YAAY,cAAc,UAAU,IAAI;AAChF"}
@@ -40,6 +40,7 @@ function lowerStage3Decorators() {
40
40
  enforce: "pre",
41
41
  async transform(code, id) {
42
42
  const [filepath] = id.split("?");
43
+ if (filepath === void 0) return null;
43
44
  if (!/\.tsx?$/.test(filepath) || filepath.includes("/node_modules/")) return null;
44
45
  if (!/(^|\n)\s*@[A-Za-z_$]/.test(code)) return null;
45
46
  const result = await transform(code, {
@@ -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/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
+ {"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 // `split` always yields a first element, so this holds. Returning `null`\n // — the plugin's own \"not my file\" answer — rather than defaulting:\n // `?? \"\"` would fail the `.tsx?` test and reach the same outcome by\n // accident, and a transform plugin that guesses at a module id is how a\n // file silently skips decorator lowering and fails at runtime instead.\n if (filepath === undefined) return null;\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;GAO/B,IAAI,aAAa,QAAW,OAAO;GAEnC,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"}
package/package.json CHANGED
@@ -25,13 +25,13 @@
25
25
  "@mongez/slug": "^1.0.7",
26
26
  "@mongez/supportive-is": "^2.1.4",
27
27
  "@mongez/time-wizard": "^1.0.6",
28
- "@warlock.js/auth": "5.6.0",
29
- "@warlock.js/cache": "5.6.0",
30
- "@warlock.js/cascade": "5.6.0",
31
- "@warlock.js/context": "5.6.0",
32
- "@warlock.js/logger": "5.6.0",
33
- "@warlock.js/seal": "5.6.0",
34
- "@warlock.js/fs": "5.6.0",
28
+ "@warlock.js/auth": "5.8.0",
29
+ "@warlock.js/cache": "5.8.0",
30
+ "@warlock.js/cascade": "5.8.0",
31
+ "@warlock.js/context": "5.8.0",
32
+ "@warlock.js/logger": "5.8.0",
33
+ "@warlock.js/seal": "5.8.0",
34
+ "@warlock.js/fs": "5.8.0",
35
35
  "chokidar": "^5.0.0",
36
36
  "dayjs": "^1.11.19",
37
37
  "es-module-lexer": "^2.0.0",
@@ -57,10 +57,10 @@
57
57
  "react": "^19.2.3",
58
58
  "react-dom": "^19.2.3",
59
59
  "@react-email/render": "^2.0.5",
60
- "@warlock.js/herald": "5.6.0",
61
- "@warlock.js/ai": "5.6.0",
62
- "@warlock.js/access": "5.6.0",
63
- "@warlock.js/notifications": "5.6.0"
60
+ "@warlock.js/herald": "5.8.0",
61
+ "@warlock.js/ai": "5.8.0",
62
+ "@warlock.js/access": "5.8.0",
63
+ "@warlock.js/notifications": "5.8.0"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
66
  "sharp": {
@@ -123,7 +123,7 @@
123
123
  ],
124
124
  "author": "hassanzohdy",
125
125
  "license": "MIT",
126
- "version": "5.6.0",
126
+ "version": "5.8.0",
127
127
  "type": "module",
128
128
  "main": "./esm/index.mjs",
129
129
  "module": "./esm/index.mjs",