@warlock.js/core 4.12.0 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  3. package/esm/cli/commands/add.command.mjs +1 -1
  4. package/esm/cli/commands/add.command.mjs.map +1 -1
  5. package/esm/cli/commands/build.command.mjs +1 -1
  6. package/esm/cli/commands/build.command.mjs.map +1 -1
  7. package/esm/cli/commands/create-database.command.mjs +1 -1
  8. package/esm/cli/commands/create-database.command.mjs.map +1 -1
  9. package/esm/cli/commands/dev-server.command.mjs +1 -1
  10. package/esm/cli/commands/dev-server.command.mjs.map +1 -1
  11. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
  12. package/esm/cli/commands/doctor.command.mjs +1 -1
  13. package/esm/cli/commands/doctor.command.mjs.map +1 -1
  14. package/esm/cli/commands/drop-tables.command.mjs +1 -1
  15. package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generate.command.mjs +1 -1
  17. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
  19. package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
  20. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  21. package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
  22. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  23. package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
  24. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  25. package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
  27. package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
  28. package/esm/cli/commands/generate/utils/path-resolver.mjs +1 -1
  29. package/esm/cli/commands/generate/utils/writer.mjs +1 -1
  30. package/esm/cli/commands/migrate.command.mjs +1 -1
  31. package/esm/cli/commands/migrate.command.mjs.map +1 -1
  32. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  33. package/esm/cli/commands/routes.command.mjs +1 -1
  34. package/esm/cli/commands/routes.command.mjs.map +1 -1
  35. package/esm/cli/commands/seed.command.mjs +1 -1
  36. package/esm/cli/commands/seed.command.mjs.map +1 -1
  37. package/esm/cli/commands/start-production.command.mjs +1 -1
  38. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  39. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  40. package/esm/cli/commands/storage-put.command.mjs +1 -1
  41. package/esm/cli/commands/storage-put.command.mjs.map +1 -1
  42. package/esm/cli/commands/typings-generator.command.mjs +2 -2
  43. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  44. package/esm/cli/commands/update.command.mjs +1 -1
  45. package/esm/cli/commands/update.command.mjs.map +1 -1
  46. package/esm/cli/commands-loader.mjs +1 -1
  47. package/esm/cli/commands-loader.mjs.map +1 -1
  48. package/esm/{cli → commands}/cli-command.d.mts +1 -1
  49. package/esm/commands/cli-command.d.mts.map +1 -0
  50. package/esm/{cli → commands}/cli-command.mjs +1 -1
  51. package/esm/commands/cli-command.mjs.map +1 -0
  52. package/esm/{cli → commands}/types.d.mts +1 -1
  53. package/esm/commands/types.d.mts.map +1 -0
  54. package/esm/config/load-config-files.mjs +1 -1
  55. package/esm/config/load-config-files.mjs.map +1 -1
  56. package/esm/connectors/base-connector.mjs +1 -1
  57. package/esm/connectors/base-connector.mjs.map +1 -1
  58. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  59. package/esm/connectors/connectors-manager.mjs +5 -3
  60. package/esm/connectors/connectors-manager.mjs.map +1 -1
  61. package/esm/connectors/http-connector.d.mts.map +1 -1
  62. package/esm/connectors/http-connector.mjs +0 -2
  63. package/esm/connectors/http-connector.mjs.map +1 -1
  64. package/esm/database/create-database-action.mjs.map +1 -1
  65. package/esm/database/drop-tables-action.mjs.map +1 -1
  66. package/esm/database/migrate-action.mjs +1 -1
  67. package/esm/database/migrate-action.mjs.map +1 -1
  68. package/esm/database/seed-command-action.mjs +1 -1
  69. package/esm/database/seed-command-action.mjs.map +1 -1
  70. package/esm/dev-server/dev-logger.mjs +1 -1
  71. package/esm/dev-server/dev-logger.mjs.map +1 -1
  72. package/esm/dev-server/development-server.mjs +2 -2
  73. package/esm/dev-server/file-event-handler.mjs +1 -1
  74. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  75. package/esm/dev-server/file-manager.mjs +1 -1
  76. package/esm/dev-server/file-manager.mjs.map +1 -1
  77. package/esm/dev-server/file-operations.mjs +1 -1
  78. package/esm/dev-server/file-operations.mjs.map +1 -1
  79. package/esm/dev-server/files-orchestrator.mjs +3 -18
  80. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  81. package/esm/dev-server/files-watcher.mjs +1 -1
  82. package/esm/dev-server/files-watcher.mjs.map +1 -1
  83. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
  84. package/esm/dev-server/layer-executor.mjs +1 -1
  85. package/esm/dev-server/loader/register-loader.mjs +1 -1
  86. package/esm/dev-server/package-json-manager.mjs +1 -1
  87. package/esm/dev-server/package-json-manager.mjs.map +1 -1
  88. package/esm/dev-server/parse-imports.mjs +2 -2
  89. package/esm/dev-server/parse-imports.mjs.map +1 -1
  90. package/esm/dev-server/tsconfig-manager.mjs +1 -1
  91. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  92. package/esm/dev-server/type-generator.mjs +1 -1
  93. package/esm/dev-server/type-generator.mjs.map +1 -1
  94. package/esm/dev-server/utils.mjs +1 -1
  95. package/esm/dev-server/utils.mjs.map +1 -1
  96. package/esm/generations/add-command.action.mjs +3 -3
  97. package/esm/generations/add-command.action.mjs.map +1 -1
  98. package/esm/http/build-cors-options.mjs +32 -0
  99. package/esm/http/build-cors-options.mjs.map +1 -0
  100. package/esm/http/middleware/max-body-size.middleware.d.mts +13 -5
  101. package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
  102. package/esm/http/middleware/max-body-size.middleware.mjs +13 -5
  103. package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
  104. package/esm/http/plugins.d.mts.map +1 -1
  105. package/esm/http/plugins.mjs +2 -9
  106. package/esm/http/plugins.mjs.map +1 -1
  107. package/esm/http/server.d.mts.map +1 -1
  108. package/esm/http/server.mjs +3 -8
  109. package/esm/http/server.mjs.map +1 -1
  110. package/esm/index.d.mts +9 -17
  111. package/esm/index.mjs +4 -15
  112. package/esm/production/production-builder.mjs +51 -3
  113. package/esm/production/production-builder.mjs.map +1 -1
  114. package/esm/production/resolve-build-config.mjs +1 -1
  115. package/esm/production/resolve-build-config.mjs.map +1 -1
  116. package/esm/router/router.d.mts +9 -0
  117. package/esm/router/router.d.mts.map +1 -1
  118. package/esm/router/router.mjs +58 -5
  119. package/esm/router/router.mjs.map +1 -1
  120. package/esm/router/types.d.mts +24 -1
  121. package/esm/router/types.d.mts.map +1 -1
  122. package/esm/tests/index.d.mts +2 -2
  123. package/esm/tests/index.mjs +1 -2
  124. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  125. package/esm/tests/start-http-development-server.mjs +38 -15
  126. package/esm/tests/start-http-development-server.mjs.map +1 -1
  127. package/esm/tests/test-helpers.d.mts.map +1 -1
  128. package/esm/tests/test-helpers.mjs +34 -13
  129. package/esm/tests/test-helpers.mjs.map +1 -1
  130. package/esm/tests/vitest-setup.d.mts +1 -1
  131. package/esm/tests/vitest-setup.d.mts.map +1 -1
  132. package/esm/tests/vitest-setup.mjs +3 -3
  133. package/esm/tests/vitest-setup.mjs.map +1 -1
  134. package/esm/utils/framework-vesion.mjs +1 -1
  135. package/esm/utils/index.d.mts +1 -0
  136. package/esm/utils/index.mjs +1 -0
  137. package/esm/utils/normalized-path.d.mts +55 -0
  138. package/esm/utils/normalized-path.d.mts.map +1 -0
  139. package/esm/{dev-server/path.mjs → utils/normalized-path.mjs} +15 -2
  140. package/esm/utils/normalized-path.mjs.map +1 -0
  141. package/esm/vite/index.d.mts +2 -0
  142. package/esm/vite/index.mjs +1 -1
  143. package/esm/vite/lower-stage3-decorators.d.mts +1 -1
  144. package/esm/vite/lower-stage3-decorators.mjs +1 -1
  145. package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
  146. package/esm/warlock-config/index.mjs +1 -1
  147. package/esm/warlock-config/types.d.mts +34 -1
  148. package/esm/warlock-config/types.d.mts.map +1 -1
  149. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  150. package/esm/warlock-config/warlock-config.manager.mjs +1 -2
  151. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  152. package/llms-full.txt +78 -23
  153. package/llms.txt +3 -3
  154. package/package.json +24 -12
  155. package/skills/lower-stage3-decorators/SKILL.md +4 -2
  156. package/skills/run-app/SKILL.md +6 -0
  157. package/skills/test-http/SKILL.md +29 -10
  158. package/skills/test-service/SKILL.md +12 -8
  159. package/skills/use-middleware/SKILL.md +5 -1
  160. package/esm/cli/cli-command.d.mts.map +0 -1
  161. package/esm/cli/cli-command.mjs.map +0 -1
  162. package/esm/cli/types.d.mts.map +0 -1
  163. package/esm/dev-server/files-orchestrator.d.mts +0 -89
  164. package/esm/dev-server/files-orchestrator.d.mts.map +0 -1
  165. package/esm/dev-server/files-watcher.d.mts +0 -69
  166. package/esm/dev-server/files-watcher.d.mts.map +0 -1
  167. package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts +0 -55
  168. package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts.map +0 -1
  169. package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts +0 -44
  170. package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts.map +0 -1
  171. package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts +0 -60
  172. package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts.map +0 -1
  173. package/esm/dev-server/health-checker/files-healthcare.manager.d.mts +0 -119
  174. package/esm/dev-server/health-checker/files-healthcare.manager.d.mts.map +0 -1
  175. package/esm/dev-server/health-checker/index.mjs +0 -7
  176. package/esm/dev-server/module-loader.d.mts +0 -63
  177. package/esm/dev-server/module-loader.d.mts.map +0 -1
  178. package/esm/dev-server/path.mjs.map +0 -1
  179. package/esm/tests/test-server-port-channel.d.mts +0 -27
  180. package/esm/tests/test-server-port-channel.d.mts.map +0 -1
  181. /package/esm/{cli → commands}/index.d.mts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { execSync } from \"node:child_process\";\r\nimport { CommandActionData } from \"../cli/types\";\r\nimport {\r\n detectPackageManager,\r\n getAddCommand,\r\n type PackageManager,\r\n} from \"../updater/package-manager\";\r\nimport { rootPath, srcPath } from \"../utils\";\r\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\r\nimport {\r\n accessConfigStub,\r\n aiConfigStub,\r\n accessResolverStub,\r\n accessRoleMigrationStub,\r\n accessRoleModelIndexStub,\r\n accessRoleModelStub,\r\n accessUserRoleMigrationStub,\r\n accessUserRoleModelIndexStub,\r\n accessUserRoleModelStub,\r\n communicatorsConfigStub,\r\n notificationControllersStub,\r\n notificationMigrationStub,\r\n notificationModelStub,\r\n notificationRoutesStub,\r\n notificationsConfigStub,\r\n socketConfigStub,\r\n} from \"./stubs\";\r\n\r\n/**\r\n * Build a migration filename timestamp prefix in the framework's\r\n * MM-DD-YYYY_HH-MM-SS form. Cascade infers a migration's createdAt from this\r\n * prefix and orders migrations deterministically by it. Pass `offsetSeconds` to\r\n * stamp sibling migrations created in the same scaffold a second apart so they\r\n * never collide and keep a stable relative order.\r\n */\r\nfunction migrationTimestamp(offsetSeconds = 0): string {\r\n const now = new Date(Date.now() + offsetSeconds * 1000);\r\n const pad = (value: number) => String(value).padStart(2, \"0\");\r\n\r\n return (\r\n `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}_` +\r\n `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`\r\n );\r\n}\r\n\r\nasync function completeTestInstallation(options: CommandActionData) {\r\n // Create test-global-setup.ts (runs once before all tests)\r\n const testGlobalSetupPath = srcPath(\"test-global-setup.ts\");\r\n const testGlobalSetupExists = await fileExistsAsync(testGlobalSetupPath);\r\n\r\n if (!testGlobalSetupExists) {\r\n await putFileAsync(\r\n testGlobalSetupPath,\r\n `/**\r\n * Global Test Setup\r\n *\r\n * Runs ONCE before all test workers.\r\n * Starts the HTTP server for integration tests.\r\n */\r\nimport { startHttpTestServer, stopHttpTestServer } from \"@warlock.js/core\";\r\n\r\nexport async function setup() {\r\n await startHttpTestServer();\r\n}\r\n\r\nexport async function teardown() {\r\n await stopHttpTestServer();\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/test-global-setup.ts`);\r\n }\r\n\r\n // Create test-setup.ts (runs per worker thread)\r\n const testSetupPath = srcPath(\"test-setup.ts\");\r\n const testSetupExists = await fileExistsAsync(testSetupPath);\r\n\r\n if (!testSetupExists) {\r\n await putFileAsync(\r\n testSetupPath,\r\n `/**\r\n * Per-Worker Test Setup\r\n *\r\n * Runs in EACH Vitest worker thread before tests execute.\r\n * Sets up per-worker database and cache connections.\r\n */\r\nimport { setupTest } from \"@warlock.js/core\";\r\n\r\nawait setupTest({ connectors: true });\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/test-setup.ts`);\r\n }\r\n\r\n // Create vite.config.ts\r\n const viteConfigPath = rootPath(\"vite.config.ts\");\r\n const viteConfigExists = await fileExistsAsync(viteConfigPath);\r\n\r\n if (!viteConfigExists) {\r\n await putFileAsync(\r\n viteConfigPath,\r\n `import { lowerStage3Decorators } from \"@warlock.js/core\";\r\nimport mongezVite from \"@mongez/vite\";\r\nimport { defineConfig } from \"vitest/config\";\r\n\r\nexport default defineConfig({\r\n // lowerStage3Decorators MUST come first: it lowers native (@RegisterModel, …)\r\n // decorators with esbuild before oxc / the SSR rewrite can mangle them, so\r\n // decorated Cascade models load under Vitest.\r\n plugins: [lowerStage3Decorators(), mongezVite()],\r\n test: {\r\n globalSetup: \"./src/test-global-setup.ts\", // HTTP server - runs once\r\n setupFiles: [\"./src/test-setup.ts\"], // DB/cache - runs per worker\r\n environment: \"node\",\r\n globals: false,\r\n include: [\"src/app/**/*.test.ts\"],\r\n },\r\n});\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created vite.config.ts`);\r\n }\r\n}\r\n\r\nasync function completeReactEmailInstallation(_options: CommandActionData) {\r\n // 1. Create emails/ folder with a sample component\r\n const emailsFolderPath = rootPath(\"emails\");\r\n const sampleEmailPath = rootPath(\"emails/welcome-email.tsx\");\r\n\r\n if (!(await fileExistsAsync(sampleEmailPath))) {\r\n await ensureDirectoryAsync(emailsFolderPath);\r\n await putFileAsync(\r\n sampleEmailPath,\r\n `import { Body, Container, Head, Html, Text } from \"@react-email/components\";\r\nimport { Tailwind } from \"@react-email/tailwind\";\r\n\r\ninterface WelcomeEmailProps {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Sample welcome email component.\r\n * Preview with: yarn email:preview\r\n */\r\nexport default function WelcomeEmail({ name }: WelcomeEmailProps) {\r\n return (\r\n <Html>\r\n <Head />\r\n <Tailwind>\r\n <Body className=\"bg-gray-100 font-sans\">\r\n <Container className=\"mx-auto max-w-xl py-8 px-4\">\r\n <Text className=\"text-2xl font-bold text-gray-900\">\r\n Welcome, {name}!\r\n </Text>\r\n <Text className=\"text-gray-600 mt-2\">\r\n You're all set. We're glad to have you on board.\r\n </Text>\r\n </Container>\r\n </Body>\r\n </Tailwind>\r\n </Html>\r\n );\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created emails/welcome-email.tsx`);\r\n }\r\n\r\n // 2. Patch tsconfig.json — add \"emails\" to include if missing\r\n const tsconfigPath = rootPath(\"tsconfig.json\");\r\n const tsconfig = await getJsonFileAsync(tsconfigPath);\r\n\r\n if (!tsconfig.include) {\r\n tsconfig.include = [];\r\n }\r\n\r\n if (!tsconfig.include.includes(\"emails\")) {\r\n tsconfig.include.push(\"emails\");\r\n await putJsonFileAsync(tsconfigPath, tsconfig);\r\n console.log(`${colors.green(\"✓\")} Added \"emails\" to tsconfig.json include`);\r\n }\r\n}\r\n\r\nasync function completeNotificationsInstallation(_options: CommandActionData) {\r\n const modelPath = srcPath(\"app/notifications/notification.model.ts\");\r\n\r\n // The model file is the sentinel for \"notifications already scaffolded\" —\r\n // its presence means the migration was created too (timestamped, so we must\r\n // not re-emit a duplicate on a second run).\r\n if (await fileExistsAsync(modelPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/notifications\")} already scaffolded, skipping model + migration...`,\r\n );\r\n return;\r\n }\r\n\r\n // 1. Notification model — extends the package's DatabaseNotification base.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications\"));\r\n await putFileAsync(modelPath, notificationModelStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/notification.model.ts`);\r\n\r\n // 2. Migration — timestamped MM-DD-YYYY_HH-MM-SS prefix so cascade infers its\r\n // createdAt and orders it deterministically (migrate-action discovers\r\n // src/app/*/migrations/*).\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/migrations\"));\r\n\r\n const migrationFile = `${migrationTimestamp()}-notification.migration.ts`;\r\n\r\n await putFileAsync(\r\n srcPath(\"app/notifications/migrations\", migrationFile),\r\n notificationMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/notifications/migrations/${migrationFile}`,\r\n );\r\n\r\n // 3. HTTP surface — the in-app read/dismiss endpoints (routes + controllers),\r\n // gated by authMiddleware. Delete if the app exposes notifications another way.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/controllers\"));\r\n await putFileAsync(\r\n srcPath(\"app/notifications/controllers/notifications.controller.ts\"),\r\n notificationControllersStub,\r\n );\r\n await putFileAsync(srcPath(\"app/notifications/routes.ts\"), notificationRoutesStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/routes.ts + controllers`);\r\n}\r\n\r\nasync function registerAccessLocale() {\r\n // Register the access locale in the project's shared translations file so a\r\n // denied check returns a real sentence, not the raw \"access.errors.forbidden\"\r\n // key. Append when the file exists, create it otherwise; skip if already there.\r\n const localesPath = srcPath(\"app/shared/utils/locales.ts\");\r\n\r\n const accessLocale = `groupedTranslations(\"access\", {\r\n errors: {\r\n forbidden: {\r\n en: \"You do not have permission to perform this action.\",\r\n ar: \"ليس لديك صلاحية لتنفيذ هذا الإجراء.\",\r\n },\r\n },\r\n});\r\n`;\r\n\r\n if (await fileExistsAsync(localesPath)) {\r\n const current = await getFileAsync(localesPath);\r\n\r\n if (current.includes(`groupedTranslations(\"access\"`)) {\r\n console.log(`${colors.yellowBright(\"access\")} locale already registered, skipping...`);\r\n\r\n return;\r\n }\r\n\r\n // The file uses groupedTranslations already iff it calls it — only inject the\r\n // import when no call is present yet.\r\n const importLine = `import { groupedTranslations } from \"@warlock.js/core\";`;\r\n const prefix = current.includes(\"groupedTranslations(\") ? \"\" : `${importLine}\\n\\n`;\r\n\r\n await putFileAsync(localesPath, `${prefix}${current.trimEnd()}\\n\\n${accessLocale}`);\r\n\r\n console.log(\r\n `${colors.green(\"✓\")} Registered the access locale in src/app/shared/utils/locales.ts`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/shared/utils\"));\r\n\r\n await putFileAsync(\r\n localesPath,\r\n `import { groupedTranslations } from \"@warlock.js/core\";\\n\\n${accessLocale}`,\r\n );\r\n\r\n console.log(`${colors.green(\"✓\")} Created src/app/shared/utils/locales.ts with the access locale`);\r\n}\r\n\r\nasync function scaffoldAccessFiles() {\r\n // The resolver file is the sentinel for \"access already scaffolded\" — its\r\n // presence means the role/user-role model folders and their timestamped\r\n // migrations were created too, so we must not re-emit duplicate migrations on\r\n // a second run.\r\n const resolverPath = srcPath(\"app/access/services/access-resolver.ts\");\r\n\r\n if (await fileExistsAsync(resolverPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/access\")} already scaffolded, skipping resolver + role tables...`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n // 1. Role catalog model folder (model + barrel + migration). The catalog row\r\n // is role name → granted permissions; managed at runtime in the DB.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role\"));\r\n await putFileAsync(srcPath(\"app/access/models/role/role.model.ts\"), accessRoleModelStub);\r\n await putFileAsync(srcPath(\"app/access/models/role/index.ts\"), accessRoleModelIndexStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role/migrations\"));\r\n\r\n // Migration filenames carry a MM-DD-YYYY_HH-MM-SS prefix so cascade infers\r\n // their createdAt and orders them deterministically (the migrate action\r\n // discovers src/app/*/models/*/migrations/*). The two tables are independent\r\n // (no FK between them), but the user-role migration is stamped a second later\r\n // so the relative order is stable.\r\n const roleMigrationFile = `${migrationTimestamp()}-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/role/migrations\", roleMigrationFile),\r\n accessRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/role/migrations/${roleMigrationFile}`,\r\n );\r\n\r\n // 2. UserRole assignment model folder (model + barrel + migration). The model\r\n // statics scope an unresolved tenant to GLOBAL rows only (security\r\n // invariant) — see the stub for the reasoning.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role\"));\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/user-role.model.ts\"),\r\n accessUserRoleModelStub,\r\n );\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/index.ts\"),\r\n accessUserRoleModelIndexStub,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/user-role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role/migrations\"));\r\n\r\n const userRoleMigrationFile = `${migrationTimestamp(1)}-user-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/migrations\", userRoleMigrationFile),\r\n accessUserRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/user-role/migrations/${userRoleMigrationFile}`,\r\n );\r\n\r\n // 3. The DatabaseAccessResolver — the one required config seam, wired into\r\n // config/access.ts by the ejected stub.\r\n await ensureDirectoryAsync(srcPath(\"app/access/services\"));\r\n await putFileAsync(resolverPath, accessResolverStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/services/access-resolver.ts`);\r\n}\r\n\r\nasync function completeAccessInstallation(_options: CommandActionData) {\r\n await registerAccessLocale();\r\n await scaffoldAccessFiles();\r\n}\r\n\r\n/**\r\n * Link a satellite AI package into src/config/ai.ts via a side-effect import.\r\n *\r\n * The satellites (@warlock.js/ai-tools, ai-panoptic, ai-workspace) augment the\r\n * `ai` object on import — they register their runtime surface (ai.tools / ai.mcp,\r\n * ai.workspace, panoptic's ai.config({ panoptic }) wiring) AND the matching TS\r\n * declaration-merging so ai.* members resolve. config-loader runs config/ai.ts at\r\n * boot, so dropping a bare `import \"<specifier>\";` at the top of that file is what\r\n * actually loads the augmentation before the ai connector applies the config.\r\n *\r\n * No-op if config/ai.ts is missing (the `ai` feature ejects it) or the import is\r\n * already present. Inserts right after the `warlock:ai-packages` marker when it\r\n * exists so the satellite imports stay grouped; otherwise prepends to the top.\r\n */\r\nasync function linkAiPackageImport(specifier: string): Promise<void> {\r\n const aiConfigPath = srcPath(\"config/ai.ts\");\r\n\r\n if (!(await fileExistsAsync(aiConfigPath))) {\r\n return;\r\n }\r\n\r\n const current = await getFileAsync(aiConfigPath);\r\n const importLine = `import \"${specifier}\";`;\r\n\r\n if (current.includes(importLine)) {\r\n return;\r\n }\r\n\r\n const marker = \"// >>> warlock:ai-packages (auto-managed) >>>\";\r\n let next: string;\r\n\r\n if (current.includes(marker)) {\r\n next = current.replace(marker, `${marker}\\n${importLine}`);\r\n } else {\r\n next = `${importLine}\\n${current}`;\r\n }\r\n\r\n await putFileAsync(aiConfigPath, next);\r\n\r\n console.log(`${colors.green(\"✓\")} Linked ${specifier} in src/config/ai.ts`);\r\n}\r\n\r\nexport const featuresMap: Record<\r\n string,\r\n {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n description: string;\r\n requires?: string[];\r\n script?: Record<string, string>;\r\n onExecuting?: (options: CommandActionData) => Promise<any>;\r\n ejectConfig?: {\r\n content: string;\r\n name: string;\r\n };\r\n }\r\n> = {\r\n \"react-email\": {\r\n description: \"Installs react-email for building email templates with React and Tailwind\",\r\n requires: [\"mail\", \"react\"],\r\n dependencies: {\r\n \"react-email\": \"^5.2.10\",\r\n \"@react-email/components\": \"^1.0.11\",\r\n \"@react-email/render\": \"^2.0.5\",\r\n \"@react-email/tailwind\": \"^2.0.7\",\r\n },\r\n devDependencies: {\r\n \"@react-email/preview-server\": \"5.2.10\",\r\n },\r\n script: {\r\n \"email:preview\": \"npx react-email dev\",\r\n },\r\n onExecuting: completeReactEmailInstallation,\r\n },\r\n react: {\r\n description:\r\n \"Installs React and React dom for rendering React components (non-interactive), useful for sending mails and generating HTML\",\r\n dependencies: {\r\n react: \"^19.2.3\",\r\n \"react-dom\": \"^19.2.3\",\r\n },\r\n devDependencies: {\r\n \"@types/react\": \"^19.2.7\",\r\n \"@types/react-dom\": \"^19.2.3\",\r\n },\r\n },\r\n image: {\r\n description: \"Installs sharp for image processing\",\r\n dependencies: {\r\n sharp: \"^0.34.5\",\r\n },\r\n },\r\n mail: {\r\n description: \"Installs nodemailer for sending emails\",\r\n dependencies: {\r\n nodemailer: \"^8.0.5\",\r\n },\r\n devDependencies: {\r\n \"@types/nodemailer\": \"^8.0.0\",\r\n },\r\n },\r\n ses: {\r\n description: \"Installs AWS SES SDK for sending emails via Amazon SES\",\r\n dependencies: {\r\n \"@aws-sdk/client-sesv2\": \"^3.1025.0\",\r\n },\r\n },\r\n mongodb: {\r\n description: \"Installs mongodb driver for database driver (Cascade Package)\",\r\n dependencies: {\r\n mongodb: \"^7.0.0\",\r\n },\r\n },\r\n scheduler: {\r\n description: \"Installs warlock scheduler for scheduling tasks\",\r\n dependencies: {\r\n \"@warlock.js/scheduler\": \"~4.0.0\",\r\n },\r\n },\r\n // swagger / postman intentionally omitted — those packages do not exist yet;\r\n // they will ship together in the unified @warlock.js/api-docs package.\r\n postgres: {\r\n description: \"Installs pg for Postgres database (Cascade Package)\",\r\n dependencies: {\r\n pg: \"^8.11.0\",\r\n },\r\n },\r\n mysql: {\r\n description: \"Installs mysql2 for MySQL database driver (Cascade Package)\",\r\n dependencies: {\r\n mysql2: \"^3.5.0\",\r\n },\r\n },\r\n redis: {\r\n description: \"Installs redis for Redis cache driver (Cache Package)\",\r\n dependencies: {\r\n redis: \"^4.6.13\",\r\n },\r\n },\r\n s3: {\r\n description: \"Installs AWS SDK for Cloud storage (Storage Package)\",\r\n dependencies: {\r\n \"@aws-sdk/client-s3\": \"^3.955.0\",\r\n \"@aws-sdk/lib-storage\": \"^3.955.0\",\r\n \"@aws-sdk/s3-request-presigner\": \"^3.955.0\",\r\n },\r\n },\r\n test: {\r\n description: \"Installs warlock test for testing\",\r\n onExecuting: completeTestInstallation,\r\n script: {\r\n test: \"vitest run\",\r\n \"test:coverage\": \"vitest run --coverage\",\r\n \"test:ui\": \"vitest --ui\",\r\n \"test:watch\": \"vitest --watch\",\r\n },\r\n devDependencies: {\r\n \"@mongez/vite\": \"^2.0.4\",\r\n vite: \"^8.0.16\",\r\n vitest: \"^4.1.8\",\r\n \"@vitest/coverage-v8\": \"^4.1.8\",\r\n },\r\n },\r\n herald: {\r\n description: \"Installs herald for message broker (Herald Package)\",\r\n dependencies: {\r\n \"@warlock.js/herald\": \"~4.0.0\",\r\n amqplib: \"^0.10.0\",\r\n },\r\n devDependencies: {\r\n \"@types/amqplib\": \"^0.10.0\",\r\n },\r\n ejectConfig: {\r\n content: communicatorsConfigStub,\r\n name: \"herald\",\r\n },\r\n },\r\n socket: {\r\n description: \"Installs socket.io for the realtime socket server (Socket Connector)\",\r\n dependencies: {\r\n \"socket.io\": \"^4.8.3\",\r\n },\r\n ejectConfig: {\r\n content: socketConfigStub,\r\n name: \"socket\",\r\n },\r\n },\r\n notifications: {\r\n description:\r\n \"Installs @warlock.js/notifications — multi-channel notifications (mail + in-app database). Pulls the mail feature, ejects config/notifications.ts, and scaffolds the Notification model + migration plus the recipient-scoped read/dismiss routes + controllers into src/app/notifications\",\r\n // The ejected config wires a `mail` channel by default (needs nodemailer,\r\n // via the `mail` feature); the scaffolded routes are gated by\r\n // `authMiddleware`, so `@warlock.js/auth` is pulled in too.\r\n requires: [\"mail\"],\r\n dependencies: {\r\n \"@warlock.js/notifications\": \"~4.0.0\",\r\n \"@warlock.js/auth\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: notificationsConfigStub,\r\n name: \"notifications\",\r\n },\r\n onExecuting: completeNotificationsInstallation,\r\n },\r\n access: {\r\n description:\r\n \"Installs @warlock.js/access — authorization (RBAC + ABAC): permission checks, ABAC policies, and roles. Ejects config/access.ts, the DatabaseAccessResolver + Role/UserRole models and migrations into src/app/access, and registers the access locale in src/app/shared/utils/locales.ts\",\r\n dependencies: {\r\n \"@warlock.js/access\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: accessConfigStub,\r\n name: \"access\",\r\n },\r\n onExecuting: completeAccessInstallation,\r\n },\r\n ai: {\r\n description: \"Installs @warlock.js/ai — the core AI toolkit (agents, tools, workflows)\",\r\n dependencies: {\r\n \"@warlock.js/ai\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: aiConfigStub,\r\n name: \"ai\",\r\n },\r\n },\r\n \"ai-openai\": {\r\n description: \"OpenAI provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-openai\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-google\": {\r\n description: \"Google (Gemini) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-google\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-anthropic\": {\r\n description: \"Anthropic (Claude) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-anthropic\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-bedrock\": {\r\n description: \"AWS Bedrock provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-bedrock\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-ollama\": {\r\n description: \"Ollama provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-ollama\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-tools\": {\r\n description:\r\n \"Installs @warlock.js/ai-tools — ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + an MCP client/server, under ai.tools.* / ai.mcp (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-tools\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-tools\"),\r\n },\r\n \"ai-panoptic\": {\r\n description:\r\n \"Installs @warlock.js/ai-panoptic — observability for @warlock.js/ai (collector, exporters, zero-setup local dashboard) via ai.config({ panoptic }) (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-panoptic\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-panoptic\"),\r\n },\r\n \"ai-workspace\": {\r\n description:\r\n \"Installs @warlock.js/ai-workspace — a policy-jailed filesystem + shell workspace for coding agents, as ai.workspace (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-workspace\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-workspace\"),\r\n },\r\n};\r\n\r\nexport const allowedFeatures = Object.keys(featuresMap);\r\n\r\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\r\n const resolved: string[] = [];\r\n\r\n for (const feature of features) {\r\n if (visited.has(feature)) continue;\r\n visited.add(feature);\r\n\r\n const def = featuresMap[feature];\r\n\r\n if (def.requires?.length) {\r\n resolved.push(...resolveFeatures(def.requires, visited));\r\n }\r\n\r\n resolved.push(feature);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nexport async function addCommandAction(options: CommandActionData) {\r\n const features = options.args;\r\n const { packageManager, list, noInstall } = options.options;\r\n\r\n if (list) {\r\n console.log(\"Available Features:\");\r\n\r\n for (const feature of allowedFeatures) {\r\n console.log(\r\n `- ${colors.yellowBright(feature)}: ${colors.green(featuresMap[feature].description)}`,\r\n );\r\n }\r\n\r\n process.exit(0);\r\n }\r\n\r\n validateFeatures(features);\r\n\r\n const resolvedFeatures = resolveFeatures(features);\r\n\r\n const dependencies: Record<string, string> = {};\r\n const devDependencies: Record<string, string> = {};\r\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\r\n const scripts: Record<string, string> = {};\r\n\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n Object.assign(dependencies, featurePackages.dependencies);\r\n if (featurePackages.devDependencies) {\r\n Object.assign(devDependencies, featurePackages.devDependencies);\r\n }\r\n\r\n if (featurePackages.ejectConfig) {\r\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\r\n }\r\n\r\n if (featurePackages.script) {\r\n Object.assign(scripts, featurePackages.script);\r\n }\r\n }\r\n\r\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\r\n // a scaffolded project's features match its core version instead of drifting to\r\n // the feature map's static range.\r\n const frameworkVersion = await getWarlockVersion();\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (dependency.startsWith(\"@warlock.js/\")) {\r\n dependencies[dependency] = frameworkVersion;\r\n }\r\n }\r\n\r\n const currentPackageJson = await getJsonFileAsync(rootPath(\"package.json\"));\r\n\r\n // Fresh templates may omit one of the maps — guard before reading.\r\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\r\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\r\n\r\n // Skip anything already present so we never downgrade an existing pin.\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (currentPackageJson.dependencies[dependency]) {\r\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\r\n delete dependencies[dependency];\r\n }\r\n }\r\n\r\n for (const devDependency of Object.keys(devDependencies)) {\r\n if (currentPackageJson.devDependencies[devDependency]) {\r\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\r\n delete devDependencies[devDependency];\r\n }\r\n }\r\n\r\n if (noInstall) {\r\n await recordDependencies(dependencies, devDependencies);\r\n } else {\r\n await installDependencies(packageManager as PackageManager | undefined, dependencies, devDependencies);\r\n }\r\n\r\n for (const [name, config] of Object.entries(ejectConfigs)) {\r\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\r\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\r\n continue;\r\n }\r\n\r\n console.log(`Creating ${colors.magenta(name)} config...`);\r\n\r\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\r\n\r\n console.log(`${colors.green(name)} config created successfully`);\r\n }\r\n\r\n // now loop again over features to execute onExecuting\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n if (featurePackages.onExecuting) {\r\n await featurePackages.onExecuting(options);\r\n }\r\n }\r\n\r\n if (Object.keys(scripts).length > 0) {\r\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync(packageJsonPath);\r\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\r\n }\r\n}\r\n\r\n/**\r\n * Install the resolved dependency sets through the project's package manager.\r\n * Runs two passes (prod then dev) so each lands in the correct section.\r\n */\r\nasync function installDependencies(\r\n packageManager: PackageManager | undefined,\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n // `--package-manager` is optional; without it, fall back to the lockfile.\r\n const packageManagerCommand = getAddCommand(packageManager ?? (await detectPackageManager()));\r\n\r\n if (Object.keys(dependencies).length > 0) {\r\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\r\n\r\n execSync(`${packageManagerCommand} ${Object.keys(dependencies).join(\" \")}`, {\r\n cwd: process.cwd(),\r\n stdio: \"inherit\",\r\n });\r\n\r\n console.log(\r\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\r\n );\r\n }\r\n\r\n if (Object.keys(devDependencies).length > 0) {\r\n console.log(\r\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n\r\n execSync(`${packageManagerCommand} ${Object.keys(devDependencies).join(\" \")} -D`, {\r\n cwd: process.cwd(),\r\n stdio: \"inherit\",\r\n });\r\n\r\n console.log(\r\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write the resolved dependency sets into package.json without installing.\r\n * Used by `--no-install` so a scaffolder can batch every feature into one\r\n * install pass after the command returns. Versions come from the feature map.\r\n */\r\nasync function recordDependencies(\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\r\n return;\r\n }\r\n\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync(packageJsonPath);\r\n\r\n packageJson.dependencies = packageJson.dependencies ?? {};\r\n packageJson.devDependencies = packageJson.devDependencies ?? {};\r\n\r\n Object.assign(packageJson.dependencies, dependencies);\r\n Object.assign(packageJson.devDependencies, devDependencies);\r\n\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\r\n\r\n console.log(\r\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (install skipped via --no-install)`,\r\n );\r\n}\r\n\r\nfunction validateFeatures(features: string[]) {\r\n for (const feature of features) {\r\n if (!allowedFeatures.includes(feature)) {\r\n console.log(\r\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\r\n );\r\n process.exit(1);\r\n }\r\n }\r\n}\r\n\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { execSync } from \"node:child_process\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport {\r\n detectPackageManager,\r\n getAddCommand,\r\n type PackageManager,\r\n} from \"../updater/package-manager\";\r\nimport { rootPath, srcPath } from \"../utils\";\r\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\r\nimport {\r\n accessConfigStub,\r\n aiConfigStub,\r\n accessResolverStub,\r\n accessRoleMigrationStub,\r\n accessRoleModelIndexStub,\r\n accessRoleModelStub,\r\n accessUserRoleMigrationStub,\r\n accessUserRoleModelIndexStub,\r\n accessUserRoleModelStub,\r\n communicatorsConfigStub,\r\n notificationControllersStub,\r\n notificationMigrationStub,\r\n notificationModelStub,\r\n notificationRoutesStub,\r\n notificationsConfigStub,\r\n socketConfigStub,\r\n} from \"./stubs\";\r\n\r\n/**\r\n * The parts of a project `package.json` this action reads or writes. Deliberately\r\n * partial — it describes what we touch, not the whole manifest.\r\n */\r\ntype ProjectPackageJson = {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n scripts?: Record<string, string>;\r\n};\r\n\r\n/**\r\n * The part of a project `tsconfig.json` this action patches.\r\n */\r\ntype ProjectTsConfig = {\r\n include?: string[];\r\n};\r\n\r\n/**\r\n * Build a migration filename timestamp prefix in the framework's\r\n * MM-DD-YYYY_HH-MM-SS form. Cascade infers a migration's createdAt from this\r\n * prefix and orders migrations deterministically by it. Pass `offsetSeconds` to\r\n * stamp sibling migrations created in the same scaffold a second apart so they\r\n * never collide and keep a stable relative order.\r\n */\r\nfunction migrationTimestamp(offsetSeconds = 0): string {\r\n const now = new Date(Date.now() + offsetSeconds * 1000);\r\n const pad = (value: number) => String(value).padStart(2, \"0\");\r\n\r\n return (\r\n `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}_` +\r\n `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`\r\n );\r\n}\r\n\r\nasync function completeTestInstallation(options: CommandActionData) {\r\n // Create test-global-setup.ts (runs once before all tests)\r\n const testGlobalSetupPath = srcPath(\"test-global-setup.ts\");\r\n const testGlobalSetupExists = await fileExistsAsync(testGlobalSetupPath);\r\n\r\n if (!testGlobalSetupExists) {\r\n await putFileAsync(\r\n testGlobalSetupPath,\r\n `/**\r\n * Global Test Setup\r\n *\r\n * Runs ONCE before all test workers.\r\n * Starts the HTTP server for integration tests.\r\n */\r\nimport { startHttpTestServer, stopHttpTestServer } from \"@warlock.js/core/tests\";\r\n\r\nexport async function setup() {\r\n await startHttpTestServer();\r\n}\r\n\r\nexport async function teardown() {\r\n await stopHttpTestServer();\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/test-global-setup.ts`);\r\n }\r\n\r\n // Create test-setup.ts (runs per worker thread)\r\n const testSetupPath = srcPath(\"test-setup.ts\");\r\n const testSetupExists = await fileExistsAsync(testSetupPath);\r\n\r\n if (!testSetupExists) {\r\n await putFileAsync(\r\n testSetupPath,\r\n `/**\r\n * Per-Worker Test Setup\r\n *\r\n * Runs in EACH Vitest worker thread before tests execute.\r\n * Sets up per-worker database and cache connections.\r\n */\r\nimport { setupTest } from \"@warlock.js/core/tests\";\r\n\r\nawait setupTest({ connectors: true });\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/test-setup.ts`);\r\n }\r\n\r\n // Create vite.config.ts\r\n const viteConfigPath = rootPath(\"vite.config.ts\");\r\n const viteConfigExists = await fileExistsAsync(viteConfigPath);\r\n\r\n if (!viteConfigExists) {\r\n await putFileAsync(\r\n viteConfigPath,\r\n `import { lowerStage3Decorators } from \"@warlock.js/core/vite\";\r\nimport mongezVite from \"@mongez/vite\";\r\nimport { defineConfig } from \"vitest/config\";\r\n\r\nexport default defineConfig({\r\n // lowerStage3Decorators MUST come first: it lowers native (@RegisterModel, …)\r\n // decorators with esbuild before oxc / the SSR rewrite can mangle them, so\r\n // decorated Cascade models load under Vitest.\r\n plugins: [lowerStage3Decorators(), mongezVite()],\r\n test: {\r\n globalSetup: \"./src/test-global-setup.ts\", // HTTP server - runs once\r\n setupFiles: [\"./src/test-setup.ts\"], // DB/cache - runs per worker\r\n environment: \"node\",\r\n globals: false,\r\n include: [\"src/app/**/*.test.ts\"],\r\n },\r\n});\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created vite.config.ts`);\r\n }\r\n}\r\n\r\nasync function completeReactEmailInstallation(_options: CommandActionData) {\r\n // 1. Create emails/ folder with a sample component\r\n const emailsFolderPath = rootPath(\"emails\");\r\n const sampleEmailPath = rootPath(\"emails/welcome-email.tsx\");\r\n\r\n if (!(await fileExistsAsync(sampleEmailPath))) {\r\n await ensureDirectoryAsync(emailsFolderPath);\r\n await putFileAsync(\r\n sampleEmailPath,\r\n `import { Body, Container, Head, Html, Text } from \"@react-email/components\";\r\nimport { Tailwind } from \"@react-email/tailwind\";\r\n\r\ninterface WelcomeEmailProps {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Sample welcome email component.\r\n * Preview with: yarn email:preview\r\n */\r\nexport default function WelcomeEmail({ name }: WelcomeEmailProps) {\r\n return (\r\n <Html>\r\n <Head />\r\n <Tailwind>\r\n <Body className=\"bg-gray-100 font-sans\">\r\n <Container className=\"mx-auto max-w-xl py-8 px-4\">\r\n <Text className=\"text-2xl font-bold text-gray-900\">\r\n Welcome, {name}!\r\n </Text>\r\n <Text className=\"text-gray-600 mt-2\">\r\n You're all set. We're glad to have you on board.\r\n </Text>\r\n </Container>\r\n </Body>\r\n </Tailwind>\r\n </Html>\r\n );\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created emails/welcome-email.tsx`);\r\n }\r\n\r\n // 2. Patch tsconfig.json — add \"emails\" to include if missing\r\n const tsconfigPath = rootPath(\"tsconfig.json\");\r\n const tsconfig = await getJsonFileAsync<ProjectTsConfig>(tsconfigPath);\r\n\r\n if (!tsconfig.include) {\r\n tsconfig.include = [];\r\n }\r\n\r\n if (!tsconfig.include.includes(\"emails\")) {\r\n tsconfig.include.push(\"emails\");\r\n await putJsonFileAsync(tsconfigPath, tsconfig);\r\n console.log(`${colors.green(\"✓\")} Added \"emails\" to tsconfig.json include`);\r\n }\r\n}\r\n\r\nasync function completeNotificationsInstallation(_options: CommandActionData) {\r\n const modelPath = srcPath(\"app/notifications/notification.model.ts\");\r\n\r\n // The model file is the sentinel for \"notifications already scaffolded\" —\r\n // its presence means the migration was created too (timestamped, so we must\r\n // not re-emit a duplicate on a second run).\r\n if (await fileExistsAsync(modelPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/notifications\")} already scaffolded, skipping model + migration...`,\r\n );\r\n return;\r\n }\r\n\r\n // 1. Notification model — extends the package's DatabaseNotification base.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications\"));\r\n await putFileAsync(modelPath, notificationModelStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/notification.model.ts`);\r\n\r\n // 2. Migration — timestamped MM-DD-YYYY_HH-MM-SS prefix so cascade infers its\r\n // createdAt and orders it deterministically (migrate-action discovers\r\n // src/app/*/migrations/*).\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/migrations\"));\r\n\r\n const migrationFile = `${migrationTimestamp()}-notification.migration.ts`;\r\n\r\n await putFileAsync(\r\n srcPath(\"app/notifications/migrations\", migrationFile),\r\n notificationMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/notifications/migrations/${migrationFile}`,\r\n );\r\n\r\n // 3. HTTP surface — the in-app read/dismiss endpoints (routes + controllers),\r\n // gated by authMiddleware. Delete if the app exposes notifications another way.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/controllers\"));\r\n await putFileAsync(\r\n srcPath(\"app/notifications/controllers/notifications.controller.ts\"),\r\n notificationControllersStub,\r\n );\r\n await putFileAsync(srcPath(\"app/notifications/routes.ts\"), notificationRoutesStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/routes.ts + controllers`);\r\n}\r\n\r\nasync function registerAccessLocale() {\r\n // Register the access locale in the project's shared translations file so a\r\n // denied check returns a real sentence, not the raw \"access.errors.forbidden\"\r\n // key. Append when the file exists, create it otherwise; skip if already there.\r\n const localesPath = srcPath(\"app/shared/utils/locales.ts\");\r\n\r\n const accessLocale = `groupedTranslations(\"access\", {\r\n errors: {\r\n forbidden: {\r\n en: \"You do not have permission to perform this action.\",\r\n ar: \"ليس لديك صلاحية لتنفيذ هذا الإجراء.\",\r\n },\r\n },\r\n});\r\n`;\r\n\r\n if (await fileExistsAsync(localesPath)) {\r\n const current = await getFileAsync(localesPath);\r\n\r\n if (current.includes(`groupedTranslations(\"access\"`)) {\r\n console.log(`${colors.yellowBright(\"access\")} locale already registered, skipping...`);\r\n\r\n return;\r\n }\r\n\r\n // The file uses groupedTranslations already iff it calls it — only inject the\r\n // import when no call is present yet.\r\n const importLine = `import { groupedTranslations } from \"@warlock.js/core\";`;\r\n const prefix = current.includes(\"groupedTranslations(\") ? \"\" : `${importLine}\\n\\n`;\r\n\r\n await putFileAsync(localesPath, `${prefix}${current.trimEnd()}\\n\\n${accessLocale}`);\r\n\r\n console.log(\r\n `${colors.green(\"✓\")} Registered the access locale in src/app/shared/utils/locales.ts`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/shared/utils\"));\r\n\r\n await putFileAsync(\r\n localesPath,\r\n `import { groupedTranslations } from \"@warlock.js/core\";\\n\\n${accessLocale}`,\r\n );\r\n\r\n console.log(`${colors.green(\"✓\")} Created src/app/shared/utils/locales.ts with the access locale`);\r\n}\r\n\r\nasync function scaffoldAccessFiles() {\r\n // The resolver file is the sentinel for \"access already scaffolded\" — its\r\n // presence means the role/user-role model folders and their timestamped\r\n // migrations were created too, so we must not re-emit duplicate migrations on\r\n // a second run.\r\n const resolverPath = srcPath(\"app/access/services/access-resolver.ts\");\r\n\r\n if (await fileExistsAsync(resolverPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/access\")} already scaffolded, skipping resolver + role tables...`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n // 1. Role catalog model folder (model + barrel + migration). The catalog row\r\n // is role name → granted permissions; managed at runtime in the DB.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role\"));\r\n await putFileAsync(srcPath(\"app/access/models/role/role.model.ts\"), accessRoleModelStub);\r\n await putFileAsync(srcPath(\"app/access/models/role/index.ts\"), accessRoleModelIndexStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role/migrations\"));\r\n\r\n // Migration filenames carry a MM-DD-YYYY_HH-MM-SS prefix so cascade infers\r\n // their createdAt and orders them deterministically (the migrate action\r\n // discovers src/app/*/models/*/migrations/*). The two tables are independent\r\n // (no FK between them), but the user-role migration is stamped a second later\r\n // so the relative order is stable.\r\n const roleMigrationFile = `${migrationTimestamp()}-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/role/migrations\", roleMigrationFile),\r\n accessRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/role/migrations/${roleMigrationFile}`,\r\n );\r\n\r\n // 2. UserRole assignment model folder (model + barrel + migration). The model\r\n // statics scope an unresolved tenant to GLOBAL rows only (security\r\n // invariant) — see the stub for the reasoning.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role\"));\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/user-role.model.ts\"),\r\n accessUserRoleModelStub,\r\n );\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/index.ts\"),\r\n accessUserRoleModelIndexStub,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/user-role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role/migrations\"));\r\n\r\n const userRoleMigrationFile = `${migrationTimestamp(1)}-user-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/migrations\", userRoleMigrationFile),\r\n accessUserRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/user-role/migrations/${userRoleMigrationFile}`,\r\n );\r\n\r\n // 3. The DatabaseAccessResolver — the one required config seam, wired into\r\n // config/access.ts by the ejected stub.\r\n await ensureDirectoryAsync(srcPath(\"app/access/services\"));\r\n await putFileAsync(resolverPath, accessResolverStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/services/access-resolver.ts`);\r\n}\r\n\r\nasync function completeAccessInstallation(_options: CommandActionData) {\r\n await registerAccessLocale();\r\n await scaffoldAccessFiles();\r\n}\r\n\r\n/**\r\n * Link a satellite AI package into src/config/ai.ts via a side-effect import.\r\n *\r\n * The satellites (@warlock.js/ai-tools, ai-panoptic, ai-workspace) augment the\r\n * `ai` object on import — they register their runtime surface (ai.tools / ai.mcp,\r\n * ai.workspace, panoptic's ai.config({ panoptic }) wiring) AND the matching TS\r\n * declaration-merging so ai.* members resolve. config-loader runs config/ai.ts at\r\n * boot, so dropping a bare `import \"<specifier>\";` at the top of that file is what\r\n * actually loads the augmentation before the ai connector applies the config.\r\n *\r\n * No-op if config/ai.ts is missing (the `ai` feature ejects it) or the import is\r\n * already present. Inserts right after the `warlock:ai-packages` marker when it\r\n * exists so the satellite imports stay grouped; otherwise prepends to the top.\r\n */\r\nasync function linkAiPackageImport(specifier: string): Promise<void> {\r\n const aiConfigPath = srcPath(\"config/ai.ts\");\r\n\r\n if (!(await fileExistsAsync(aiConfigPath))) {\r\n return;\r\n }\r\n\r\n const current = await getFileAsync(aiConfigPath);\r\n const importLine = `import \"${specifier}\";`;\r\n\r\n if (current.includes(importLine)) {\r\n return;\r\n }\r\n\r\n const marker = \"// >>> warlock:ai-packages (auto-managed) >>>\";\r\n let next: string;\r\n\r\n if (current.includes(marker)) {\r\n next = current.replace(marker, `${marker}\\n${importLine}`);\r\n } else {\r\n next = `${importLine}\\n${current}`;\r\n }\r\n\r\n await putFileAsync(aiConfigPath, next);\r\n\r\n console.log(`${colors.green(\"✓\")} Linked ${specifier} in src/config/ai.ts`);\r\n}\r\n\r\nexport const featuresMap: Record<\r\n string,\r\n {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n description: string;\r\n requires?: string[];\r\n script?: Record<string, string>;\r\n onExecuting?: (options: CommandActionData) => Promise<any>;\r\n ejectConfig?: {\r\n content: string;\r\n name: string;\r\n };\r\n }\r\n> = {\r\n \"react-email\": {\r\n description: \"Installs react-email for building email templates with React and Tailwind\",\r\n requires: [\"mail\", \"react\"],\r\n dependencies: {\r\n \"react-email\": \"^5.2.10\",\r\n \"@react-email/components\": \"^1.0.11\",\r\n \"@react-email/render\": \"^2.0.5\",\r\n \"@react-email/tailwind\": \"^2.0.7\",\r\n },\r\n devDependencies: {\r\n \"@react-email/preview-server\": \"5.2.10\",\r\n },\r\n script: {\r\n \"email:preview\": \"npx react-email dev\",\r\n },\r\n onExecuting: completeReactEmailInstallation,\r\n },\r\n react: {\r\n description:\r\n \"Installs React and React dom for rendering React components (non-interactive), useful for sending mails and generating HTML\",\r\n dependencies: {\r\n react: \"^19.2.3\",\r\n \"react-dom\": \"^19.2.3\",\r\n },\r\n devDependencies: {\r\n \"@types/react\": \"^19.2.7\",\r\n \"@types/react-dom\": \"^19.2.3\",\r\n },\r\n },\r\n image: {\r\n description: \"Installs sharp for image processing\",\r\n dependencies: {\r\n sharp: \"^0.34.5\",\r\n },\r\n },\r\n mail: {\r\n description: \"Installs nodemailer for sending emails\",\r\n dependencies: {\r\n nodemailer: \"^8.0.5\",\r\n },\r\n devDependencies: {\r\n \"@types/nodemailer\": \"^8.0.0\",\r\n },\r\n },\r\n ses: {\r\n description: \"Installs AWS SES SDK for sending emails via Amazon SES\",\r\n dependencies: {\r\n \"@aws-sdk/client-sesv2\": \"^3.1025.0\",\r\n },\r\n },\r\n mongodb: {\r\n description: \"Installs mongodb driver for database driver (Cascade Package)\",\r\n dependencies: {\r\n mongodb: \"^7.0.0\",\r\n },\r\n },\r\n scheduler: {\r\n description: \"Installs warlock scheduler for scheduling tasks\",\r\n dependencies: {\r\n \"@warlock.js/scheduler\": \"~4.0.0\",\r\n },\r\n },\r\n // swagger / postman intentionally omitted — those packages do not exist yet;\r\n // they will ship together in the unified @warlock.js/api-docs package.\r\n postgres: {\r\n description: \"Installs pg for Postgres database (Cascade Package)\",\r\n dependencies: {\r\n pg: \"^8.11.0\",\r\n },\r\n },\r\n mysql: {\r\n description: \"Installs mysql2 for MySQL database driver (Cascade Package)\",\r\n dependencies: {\r\n mysql2: \"^3.5.0\",\r\n },\r\n },\r\n redis: {\r\n description: \"Installs redis for Redis cache driver (Cache Package)\",\r\n dependencies: {\r\n redis: \"^4.6.13\",\r\n },\r\n },\r\n s3: {\r\n description: \"Installs AWS SDK for Cloud storage (Storage Package)\",\r\n dependencies: {\r\n \"@aws-sdk/client-s3\": \"^3.955.0\",\r\n \"@aws-sdk/lib-storage\": \"^3.955.0\",\r\n \"@aws-sdk/s3-request-presigner\": \"^3.955.0\",\r\n },\r\n },\r\n test: {\r\n description: \"Installs warlock test for testing\",\r\n onExecuting: completeTestInstallation,\r\n script: {\r\n test: \"vitest run\",\r\n \"test:coverage\": \"vitest run --coverage\",\r\n \"test:ui\": \"vitest --ui\",\r\n \"test:watch\": \"vitest --watch\",\r\n },\r\n devDependencies: {\r\n \"@mongez/vite\": \"^2.0.4\",\r\n vite: \"^8.0.16\",\r\n vitest: \"^4.1.8\",\r\n \"@vitest/coverage-v8\": \"^4.1.8\",\r\n },\r\n },\r\n herald: {\r\n description: \"Installs herald for message broker (Herald Package)\",\r\n dependencies: {\r\n \"@warlock.js/herald\": \"~4.0.0\",\r\n amqplib: \"^0.10.0\",\r\n },\r\n devDependencies: {\r\n \"@types/amqplib\": \"^0.10.0\",\r\n },\r\n ejectConfig: {\r\n content: communicatorsConfigStub,\r\n name: \"herald\",\r\n },\r\n },\r\n socket: {\r\n description: \"Installs socket.io for the realtime socket server (Socket Connector)\",\r\n dependencies: {\r\n \"socket.io\": \"^4.8.3\",\r\n },\r\n ejectConfig: {\r\n content: socketConfigStub,\r\n name: \"socket\",\r\n },\r\n },\r\n notifications: {\r\n description:\r\n \"Installs @warlock.js/notifications — multi-channel notifications (mail + in-app database). Pulls the mail feature, ejects config/notifications.ts, and scaffolds the Notification model + migration plus the recipient-scoped read/dismiss routes + controllers into src/app/notifications\",\r\n // The ejected config wires a `mail` channel by default (needs nodemailer,\r\n // via the `mail` feature); the scaffolded routes are gated by\r\n // `authMiddleware`, so `@warlock.js/auth` is pulled in too.\r\n requires: [\"mail\"],\r\n dependencies: {\r\n \"@warlock.js/notifications\": \"~4.0.0\",\r\n \"@warlock.js/auth\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: notificationsConfigStub,\r\n name: \"notifications\",\r\n },\r\n onExecuting: completeNotificationsInstallation,\r\n },\r\n access: {\r\n description:\r\n \"Installs @warlock.js/access — authorization (RBAC + ABAC): permission checks, ABAC policies, and roles. Ejects config/access.ts, the DatabaseAccessResolver + Role/UserRole models and migrations into src/app/access, and registers the access locale in src/app/shared/utils/locales.ts\",\r\n dependencies: {\r\n \"@warlock.js/access\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: accessConfigStub,\r\n name: \"access\",\r\n },\r\n onExecuting: completeAccessInstallation,\r\n },\r\n ai: {\r\n description: \"Installs @warlock.js/ai — the core AI toolkit (agents, tools, workflows)\",\r\n dependencies: {\r\n \"@warlock.js/ai\": \"~4.0.0\",\r\n },\r\n ejectConfig: {\r\n content: aiConfigStub,\r\n name: \"ai\",\r\n },\r\n },\r\n \"ai-openai\": {\r\n description: \"OpenAI provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-openai\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-google\": {\r\n description: \"Google (Gemini) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-google\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-anthropic\": {\r\n description: \"Anthropic (Claude) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-anthropic\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-bedrock\": {\r\n description: \"AWS Bedrock provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-bedrock\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-ollama\": {\r\n description: \"Ollama provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-ollama\": \"~4.0.0\",\r\n },\r\n },\r\n \"ai-tools\": {\r\n description:\r\n \"Installs @warlock.js/ai-tools — ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + an MCP client/server, under ai.tools.* / ai.mcp (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-tools\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-tools\"),\r\n },\r\n \"ai-panoptic\": {\r\n description:\r\n \"Installs @warlock.js/ai-panoptic — observability for @warlock.js/ai (collector, exporters, zero-setup local dashboard) via ai.config({ panoptic }) (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-panoptic\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-panoptic\"),\r\n },\r\n \"ai-workspace\": {\r\n description:\r\n \"Installs @warlock.js/ai-workspace — a policy-jailed filesystem + shell workspace for coding agents, as ai.workspace (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-workspace\": \"~4.0.0\",\r\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-workspace\"),\r\n },\r\n};\r\n\r\nexport const allowedFeatures = Object.keys(featuresMap);\r\n\r\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\r\n const resolved: string[] = [];\r\n\r\n for (const feature of features) {\r\n if (visited.has(feature)) continue;\r\n visited.add(feature);\r\n\r\n const def = featuresMap[feature];\r\n\r\n if (def.requires?.length) {\r\n resolved.push(...resolveFeatures(def.requires, visited));\r\n }\r\n\r\n resolved.push(feature);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nexport async function addCommandAction(options: CommandActionData) {\r\n const features = options.args;\r\n const { packageManager, list, noInstall } = options.options;\r\n\r\n if (list) {\r\n console.log(\"Available Features:\");\r\n\r\n for (const feature of allowedFeatures) {\r\n console.log(\r\n `- ${colors.yellowBright(feature)}: ${colors.green(featuresMap[feature].description)}`,\r\n );\r\n }\r\n\r\n process.exit(0);\r\n }\r\n\r\n validateFeatures(features);\r\n\r\n const resolvedFeatures = resolveFeatures(features);\r\n\r\n const dependencies: Record<string, string> = {};\r\n const devDependencies: Record<string, string> = {};\r\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\r\n const scripts: Record<string, string> = {};\r\n\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n Object.assign(dependencies, featurePackages.dependencies);\r\n if (featurePackages.devDependencies) {\r\n Object.assign(devDependencies, featurePackages.devDependencies);\r\n }\r\n\r\n if (featurePackages.ejectConfig) {\r\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\r\n }\r\n\r\n if (featurePackages.script) {\r\n Object.assign(scripts, featurePackages.script);\r\n }\r\n }\r\n\r\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\r\n // a scaffolded project's features match its core version instead of drifting to\r\n // the feature map's static range.\r\n const frameworkVersion = await getWarlockVersion();\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (dependency.startsWith(\"@warlock.js/\")) {\r\n dependencies[dependency] = frameworkVersion;\r\n }\r\n }\r\n\r\n const currentPackageJson = await getJsonFileAsync<ProjectPackageJson>(rootPath(\"package.json\"));\r\n\r\n // Fresh templates may omit one of the maps — guard before reading.\r\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\r\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\r\n\r\n // Skip anything already present so we never downgrade an existing pin.\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (currentPackageJson.dependencies[dependency]) {\r\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\r\n delete dependencies[dependency];\r\n }\r\n }\r\n\r\n for (const devDependency of Object.keys(devDependencies)) {\r\n if (currentPackageJson.devDependencies[devDependency]) {\r\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\r\n delete devDependencies[devDependency];\r\n }\r\n }\r\n\r\n if (noInstall) {\r\n await recordDependencies(dependencies, devDependencies);\r\n } else {\r\n await installDependencies(packageManager as PackageManager | undefined, dependencies, devDependencies);\r\n }\r\n\r\n for (const [name, config] of Object.entries(ejectConfigs)) {\r\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\r\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\r\n continue;\r\n }\r\n\r\n console.log(`Creating ${colors.magenta(name)} config...`);\r\n\r\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\r\n\r\n console.log(`${colors.green(name)} config created successfully`);\r\n }\r\n\r\n // now loop again over features to execute onExecuting\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n if (featurePackages.onExecuting) {\r\n await featurePackages.onExecuting(options);\r\n }\r\n }\r\n\r\n if (Object.keys(scripts).length > 0) {\r\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\r\n }\r\n}\r\n\r\n/**\r\n * Install the resolved dependency sets through the project's package manager.\r\n * Runs two passes (prod then dev) so each lands in the correct section.\r\n */\r\nasync function installDependencies(\r\n packageManager: PackageManager | undefined,\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n // `--package-manager` is optional; without it, fall back to the lockfile.\r\n const packageManagerCommand = getAddCommand(packageManager ?? (await detectPackageManager()));\r\n\r\n if (Object.keys(dependencies).length > 0) {\r\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\r\n\r\n execSync(`${packageManagerCommand} ${Object.keys(dependencies).join(\" \")}`, {\r\n cwd: process.cwd(),\r\n stdio: \"inherit\",\r\n });\r\n\r\n console.log(\r\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\r\n );\r\n }\r\n\r\n if (Object.keys(devDependencies).length > 0) {\r\n console.log(\r\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n\r\n execSync(`${packageManagerCommand} ${Object.keys(devDependencies).join(\" \")} -D`, {\r\n cwd: process.cwd(),\r\n stdio: \"inherit\",\r\n });\r\n\r\n console.log(\r\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write the resolved dependency sets into package.json without installing.\r\n * Used by `--no-install` so a scaffolder can batch every feature into one\r\n * install pass after the command returns. Versions come from the feature map.\r\n */\r\nasync function recordDependencies(\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\r\n return;\r\n }\r\n\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n\r\n packageJson.dependencies = packageJson.dependencies ?? {};\r\n packageJson.devDependencies = packageJson.devDependencies ?? {};\r\n\r\n Object.assign(packageJson.dependencies, dependencies);\r\n Object.assign(packageJson.devDependencies, devDependencies);\r\n\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\r\n\r\n console.log(\r\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (install skipped via --no-install)`,\r\n );\r\n}\r\n\r\nfunction validateFeatures(features: string[]) {\r\n for (const feature of features) {\r\n if (!allowedFeatures.includes(feature)) {\r\n console.log(\r\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\r\n );\r\n process.exit(1);\r\n }\r\n }\r\n}\r\n\r\n"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import config from "@mongez/config";
2
+
3
+ //#region ../core/src/http/build-cors-options.ts
4
+ /**
5
+ * Framework CORS defaults, applied only where the application has said nothing.
6
+ *
7
+ * `origin: "*"` is deliberately NOT paired with `credentials: true` here —
8
+ * browsers reject that combination for credentialed requests, so shipping both
9
+ * would break the legitimate case while appearing permissive.
10
+ */
11
+ const defaultCorsOptions = {
12
+ origin: "*",
13
+ methods: "*"
14
+ };
15
+ /**
16
+ * Resolve the CORS options handed to `@fastify/cors`.
17
+ *
18
+ * The application's `http.cors` is spread LAST so a configured allow-list wins.
19
+ * Spreading the framework defaults last instead made `http.cors` unreachable —
20
+ * every app got `origin: "*"` no matter what it configured, in every release up
21
+ * to 4.12.0. Keep this order.
22
+ */
23
+ function buildCorsOptions() {
24
+ return {
25
+ ...defaultCorsOptions,
26
+ ...config.get("http.cors", {})
27
+ };
28
+ }
29
+
30
+ //#endregion
31
+ export { buildCorsOptions };
32
+ //# sourceMappingURL=build-cors-options.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-cors-options.mjs","names":[],"sources":["../../../../../../../core/src/http/build-cors-options.ts"],"sourcesContent":["import type { FastifyCorsOptions } from \"@fastify/cors\";\nimport config from \"@mongez/config\";\n\n/**\n * Framework CORS defaults, applied only where the application has said nothing.\n *\n * `origin: \"*\"` is deliberately NOT paired with `credentials: true` here —\n * browsers reject that combination for credentialed requests, so shipping both\n * would break the legitimate case while appearing permissive.\n */\nconst defaultCorsOptions: FastifyCorsOptions = {\n origin: \"*\",\n methods: \"*\",\n};\n\n/**\n * Resolve the CORS options handed to `@fastify/cors`.\n *\n * The application's `http.cors` is spread LAST so a configured allow-list wins.\n * Spreading the framework defaults last instead made `http.cors` unreachable —\n * every app got `origin: \"*\"` no matter what it configured, in every release up\n * to 4.12.0. Keep this order.\n */\nexport function buildCorsOptions(): FastifyCorsOptions {\n return {\n ...defaultCorsOptions,\n ...config.get(\"http.cors\", {}),\n };\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAM,qBAAyC;CAC7C,QAAQ;CACR,SAAS;AACX;;;;;;;;;AAUA,SAAgB,mBAAuC;CACrD,OAAO;EACL,GAAG;EACH,GAAG,OAAO,IAAI,aAAa,CAAC,CAAC;CAC/B;AACF"}
@@ -3,11 +3,19 @@ import { Middleware } from "../../router/types.mjs";
3
3
  /**
4
4
  * Reject requests whose `Content-Length` exceeds the configured cap.
5
5
  *
6
- * Runs before body parsing the rejection lands as a 413 with the request
7
- * body still on the wire (the server reads it to completion regardless; this
8
- * just short-circuits the application stack). For genuine pre-read protection
9
- * you also want `http.bodyLimit` lowered at the Fastify level — middleware
10
- * is a per-route layer on top, not a replacement.
6
+ * ⚠️ **This runs AFTER the body has been parsed, not before it.** Framework
7
+ * middleware executes inside the Fastify route handler, and `@fastify/multipart`
8
+ * is registered with `attachFieldsToBody: true` so by the time this sees the
9
+ * request, the bytes are already resident. It inspects `Content-Length` and
10
+ * answers 413; it does not and cannot prevent the read.
11
+ *
12
+ * **So this is not a resource-exhaustion control.** For that, lower
13
+ * `http.bodyLimit`, or set `bodyLimit` in the route's own `serverOptions` —
14
+ * Fastify enforces those while reading. This middleware is a per-route
15
+ * convenience for returning a consistent 413, not a replacement for either.
16
+ *
17
+ * The same caveat applies to `concurrencyLimit`: it bounds how many requests
18
+ * run at once, not how many bytes are resident.
11
19
  *
12
20
  * @example
13
21
  * import { middleware } from "@warlock.js/core";
@@ -1 +1 @@
1
- {"version":3,"file":"max-body-size.middleware.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/max-body-size.middleware.ts"],"mappings":";;;;;AAyBA;;;;AAAyE;;;;;;;;;;;;;iBAAzD,qBAAA,CAAsB,KAAA,oBAAyB,UAAU"}
1
+ {"version":3,"file":"max-body-size.middleware.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/max-body-size.middleware.ts"],"mappings":";;;;;AAiCA;;;;AAAyE;;;;;;;;;;;;;;;;;;;;;iBAAzD,qBAAA,CAAsB,KAAA,oBAAyB,UAAU"}
@@ -6,11 +6,19 @@ import { parseSize } from "./utils/parse-size.mjs";
6
6
  /**
7
7
  * Reject requests whose `Content-Length` exceeds the configured cap.
8
8
  *
9
- * Runs before body parsing the rejection lands as a 413 with the request
10
- * body still on the wire (the server reads it to completion regardless; this
11
- * just short-circuits the application stack). For genuine pre-read protection
12
- * you also want `http.bodyLimit` lowered at the Fastify level — middleware
13
- * is a per-route layer on top, not a replacement.
9
+ * ⚠️ **This runs AFTER the body has been parsed, not before it.** Framework
10
+ * middleware executes inside the Fastify route handler, and `@fastify/multipart`
11
+ * is registered with `attachFieldsToBody: true` so by the time this sees the
12
+ * request, the bytes are already resident. It inspects `Content-Length` and
13
+ * answers 413; it does not and cannot prevent the read.
14
+ *
15
+ * **So this is not a resource-exhaustion control.** For that, lower
16
+ * `http.bodyLimit`, or set `bodyLimit` in the route's own `serverOptions` —
17
+ * Fastify enforces those while reading. This middleware is a per-route
18
+ * convenience for returning a consistent 413, not a replacement for either.
19
+ *
20
+ * The same caveat applies to `concurrencyLimit`: it bounds how many requests
21
+ * run at once, not how many bytes are resident.
14
22
  *
15
23
  * @example
16
24
  * import { middleware } from "@warlock.js/core";
@@ -1 +1 @@
1
- {"version":3,"file":"max-body-size.middleware.mjs","names":[],"sources":["../../../../../../../../core/src/http/middleware/max-body-size.middleware.ts"],"sourcesContent":["import type { Middleware } from \"../../router\";\nimport { HttpErrorCodes } from \"../error-codes\";\nimport { t } from \"./inject-request-context\";\nimport { parseSize } from \"./utils/parse-size\";\n\n/**\n * Reject requests whose `Content-Length` exceeds the configured cap.\n *\n * Runs before body parsing the rejection lands as a 413 with the request\n * body still on the wire (the server reads it to completion regardless; this\n * just short-circuits the application stack). For genuine pre-read protection\n * you also want `http.bodyLimit` lowered at the Fastify level middleware\n * is a per-route layer on top, not a replacement.\n *\n * @example\n * import { middleware } from \"@warlock.js/core\";\n *\n * router.post(\"/comments\", createCommentController, {\n * middleware: [middleware.maxBodySize(\"8kb\")],\n * });\n *\n * router.post(\"/uploads\", uploadController, {\n * middleware: [middleware.maxBodySize(\"10mb\")],\n * });\n */\nexport function maxBodySizeMiddleware(limit: string | number): Middleware {\n const limitBytes = parseSize(limit);\n\n return (request, response) => {\n const contentLengthHeader = request.header(\"content-length\");\n\n if (!contentLengthHeader) return;\n\n const contentLength = Number(contentLengthHeader);\n\n if (!Number.isFinite(contentLength)) return;\n\n if (contentLength <= limitBytes) return;\n\n response.header(\"Connection\", \"close\");\n\n return response.contentTooLarge({\n error: t(\"http.bodyTooLarge\"),\n errorCode: HttpErrorCodes.BodyTooLarge,\n limit: limitBytes,\n received: contentLength,\n },\n );\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,sBAAsB,OAAoC;CACxE,MAAM,aAAa,UAAU,KAAK;CAElC,QAAQ,SAAS,aAAa;EAC5B,MAAM,sBAAsB,QAAQ,OAAO,gBAAgB;EAE3D,IAAI,CAAC,qBAAqB;EAE1B,MAAM,gBAAgB,OAAO,mBAAmB;EAEhD,IAAI,CAAC,OAAO,SAAS,aAAa,GAAG;EAErC,IAAI,iBAAiB,YAAY;EAEjC,SAAS,OAAO,cAAc,OAAO;EAErC,OAAO,SAAS,gBAAgB;GAC5B,OAAO,EAAE,mBAAmB;GAC5B;GACA,OAAO;GACP,UAAU;EACZ,CACF;CACF;AACF"}
1
+ {"version":3,"file":"max-body-size.middleware.mjs","names":[],"sources":["../../../../../../../../core/src/http/middleware/max-body-size.middleware.ts"],"sourcesContent":["import type { Middleware } from \"../../router\";\nimport { HttpErrorCodes } from \"../error-codes\";\nimport { t } from \"./inject-request-context\";\nimport { parseSize } from \"./utils/parse-size\";\n\n/**\n * Reject requests whose `Content-Length` exceeds the configured cap.\n *\n * ⚠️ **This runs AFTER the body has been parsed, not before it.** Framework\n * middleware executes inside the Fastify route handler, and `@fastify/multipart`\n * is registered with `attachFieldsToBody: true` — so by the time this sees the\n * request, the bytes are already resident. It inspects `Content-Length` and\n * answers 413; it does not and cannot prevent the read.\n *\n * **So this is not a resource-exhaustion control.** For that, lower\n * `http.bodyLimit`, or set `bodyLimit` in the route's own `serverOptions` —\n * Fastify enforces those while reading. This middleware is a per-route\n * convenience for returning a consistent 413, not a replacement for either.\n *\n * The same caveat applies to `concurrencyLimit`: it bounds how many requests\n * run at once, not how many bytes are resident.\n *\n * @example\n * import { middleware } from \"@warlock.js/core\";\n *\n * router.post(\"/comments\", createCommentController, {\n * middleware: [middleware.maxBodySize(\"8kb\")],\n * });\n *\n * router.post(\"/uploads\", uploadController, {\n * middleware: [middleware.maxBodySize(\"10mb\")],\n * });\n */\nexport function maxBodySizeMiddleware(limit: string | number): Middleware {\n const limitBytes = parseSize(limit);\n\n return (request, response) => {\n const contentLengthHeader = request.header(\"content-length\");\n\n if (!contentLengthHeader) return;\n\n const contentLength = Number(contentLengthHeader);\n\n if (!Number.isFinite(contentLength)) return;\n\n if (contentLength <= limitBytes) return;\n\n response.header(\"Connection\", \"close\");\n\n return response.contentTooLarge({\n error: t(\"http.bodyTooLarge\"),\n errorCode: HttpErrorCodes.BodyTooLarge,\n limit: limitBytes,\n received: contentLength,\n },\n );\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,sBAAsB,OAAoC;CACxE,MAAM,aAAa,UAAU,KAAK;CAElC,QAAQ,SAAS,aAAa;EAC5B,MAAM,sBAAsB,QAAQ,OAAO,gBAAgB;EAE3D,IAAI,CAAC,qBAAqB;EAE1B,MAAM,gBAAgB,OAAO,mBAAmB;EAEhD,IAAI,CAAC,OAAO,SAAS,aAAa,GAAG;EAErC,IAAI,iBAAiB,YAAY;EAEjC,SAAS,OAAO,cAAc,OAAO;EAErC,OAAO,SAAS,gBAAgB;GAC5B,OAAO,EAAE,mBAAmB;GAC5B;GACA,OAAO;GACP,UAAU;EACZ,CACF;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.d.mts","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"mappings":";;;iBAWsB,mBAAA,CAAoB,MAAA,EAAQ,eAAA,GAAe,OAAA"}
1
+ {"version":3,"file":"plugins.d.mts","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"mappings":";;;iBAMsB,mBAAA,CAAoB,MAAA,EAAQ,eAAA,GAAe,OAAA"}
@@ -1,23 +1,16 @@
1
1
  import { rootPath } from "../utils/paths.mjs";
2
2
  import "../utils/index.mjs";
3
+ import { buildCorsOptions } from "./build-cors-options.mjs";
3
4
  import config from "@mongez/config";
4
5
  import fastifyMultipart from "@fastify/multipart";
5
6
 
6
7
  //#region ../core/src/http/plugins.ts
7
- const defaultCorsOptions = {
8
- origin: "*",
9
- methods: "*"
10
- };
11
8
  async function registerHttpPlugins(server) {
12
9
  server.register(import("@fastify/rate-limit"), {
13
10
  max: config.get("http.rateLimit.max", 60),
14
11
  timeWindow: config.get("http.rateLimit.duration", 60 * 1e3)
15
12
  });
16
- const corsOptions = {
17
- ...config.get("http.cors", {}),
18
- ...defaultCorsOptions
19
- };
20
- server.register(import("@fastify/cors"), corsOptions);
13
+ server.register(import("@fastify/cors"), buildCorsOptions());
21
14
  server.register(fastifyMultipart, {
22
15
  attachFieldsToBody: true,
23
16
  limits: { fileSize: config.get("http.fileUploadLimit", 10 * 1024 * 1024) }
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.mjs","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"sourcesContent":["import type { FastifyCorsOptions } from \"@fastify/cors\";\nimport fastifyMultipart from \"@fastify/multipart\";\nimport config from \"@mongez/config\";\nimport { rootPath } from \"../utils\";\nimport type { FastifyInstance } from \"./server\";\n\nconst defaultCorsOptions: FastifyCorsOptions = {\n origin: \"*\",\n methods: \"*\",\n};\n\nexport async function registerHttpPlugins(server: FastifyInstance) {\n // 👇🏻 register rate-limit plugin\n server.register(import(\"@fastify/rate-limit\"), {\n // max requests per time window\n max: config.get(\"http.rateLimit.max\", 60),\n // maximum time that is will allow max requests\n timeWindow: config.get(\"http.rateLimit.duration\", 60 * 1000),\n });\n\n // 👇🏻 register cors plugin\n const corsOptions: FastifyCorsOptions | undefined = {\n ...config.get(\"http.cors\", {}),\n ...defaultCorsOptions,\n };\n\n server.register(import(\"@fastify/cors\"), corsOptions);\n\n // 👇🏻 import multipart plugin\n server.register(fastifyMultipart, {\n attachFieldsToBody: true,\n limits: {\n // file size could be up to 10MB\n fileSize: config.get(\"http.fileUploadLimit\", 10 * 1024 * 1024),\n },\n });\n\n server.register(import(\"@fastify/static\"), {\n root: config.get(\"storage.publicRoot\", rootPath(\"public\")),\n prefix: config.get(\"storage.publicPrefix\", \"/public/\"),\n });\n\n // 👇🏻 register cookie plugin\n server.register(import(\"@fastify/cookie\"), {\n secret: config.get(\"http.cookies.secret\"), // Optional: allow signed cookies\n parseOptions: config.get(\"http.cookies.options\", {}),\n });\n}\n"],"mappings":";;;;;;AAMA,MAAM,qBAAyC;CAC7C,QAAQ;CACR,SAAS;AACX;AAEA,eAAsB,oBAAoB,QAAyB;CAEjE,OAAO,SAAS,OAAO,wBAAwB;EAE7C,KAAK,OAAO,IAAI,sBAAsB,EAAE;EAExC,YAAY,OAAO,IAAI,2BAA2B,KAAK,GAAI;CAC7D,CAAC;CAGD,MAAM,cAA8C;EAClD,GAAG,OAAO,IAAI,aAAa,CAAC,CAAC;EAC7B,GAAG;CACL;CAEA,OAAO,SAAS,OAAO,kBAAkB,WAAW;CAGpD,OAAO,SAAS,kBAAkB;EAChC,oBAAoB;EACpB,QAAQ,EAEN,UAAU,OAAO,IAAI,wBAAwB,KAAK,OAAO,IAAI,EAC/D;CACF,CAAC;CAED,OAAO,SAAS,OAAO,oBAAoB;EACzC,MAAM,OAAO,IAAI,sBAAsB,SAAS,QAAQ,CAAC;EACzD,QAAQ,OAAO,IAAI,wBAAwB,UAAU;CACvD,CAAC;CAGD,OAAO,SAAS,OAAO,oBAAoB;EACzC,QAAQ,OAAO,IAAI,qBAAqB;EACxC,cAAc,OAAO,IAAI,wBAAwB,CAAC,CAAC;CACrD,CAAC;AACH"}
1
+ {"version":3,"file":"plugins.mjs","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"sourcesContent":["import fastifyMultipart from \"@fastify/multipart\";\nimport config from \"@mongez/config\";\nimport { rootPath } from \"../utils\";\nimport { buildCorsOptions } from \"./build-cors-options\";\nimport type { FastifyInstance } from \"./server\";\n\nexport async function registerHttpPlugins(server: FastifyInstance) {\n // 👇🏻 register rate-limit plugin\n server.register(import(\"@fastify/rate-limit\"), {\n // max requests per time window\n max: config.get(\"http.rateLimit.max\", 60),\n // maximum time that is will allow max requests\n timeWindow: config.get(\"http.rateLimit.duration\", 60 * 1000),\n });\n\n // 👇🏻 register cors plugin\n server.register(import(\"@fastify/cors\"), buildCorsOptions());\n\n // 👇🏻 import multipart plugin\n server.register(fastifyMultipart, {\n attachFieldsToBody: true,\n limits: {\n // file size could be up to 10MB\n fileSize: config.get(\"http.fileUploadLimit\", 10 * 1024 * 1024),\n },\n });\n\n server.register(import(\"@fastify/static\"), {\n root: config.get(\"storage.publicRoot\", rootPath(\"public\")),\n prefix: config.get(\"storage.publicPrefix\", \"/public/\"),\n });\n\n // 👇🏻 register cookie plugin\n server.register(import(\"@fastify/cookie\"), {\n secret: config.get(\"http.cookies.secret\"), // Optional: allow signed cookies\n parseOptions: config.get(\"http.cookies.options\", {}),\n });\n}\n"],"mappings":";;;;;;;AAMA,eAAsB,oBAAoB,QAAyB;CAEjE,OAAO,SAAS,OAAO,wBAAwB;EAE7C,KAAK,OAAO,IAAI,sBAAsB,EAAE;EAExC,YAAY,OAAO,IAAI,2BAA2B,KAAK,GAAI;CAC7D,CAAC;CAGD,OAAO,SAAS,OAAO,kBAAkB,iBAAiB,CAAC;CAG3D,OAAO,SAAS,kBAAkB;EAChC,oBAAoB;EACpB,QAAQ,EAEN,UAAU,OAAO,IAAI,wBAAwB,KAAK,OAAO,IAAI,EAC/D;CACF,CAAC;CAED,OAAO,SAAS,OAAO,oBAAoB;EACzC,MAAM,OAAO,IAAI,sBAAsB,SAAS,QAAQ,CAAC;EACzD,QAAQ,OAAO,IAAI,wBAAwB,UAAU;CACvD,CAAC;CAGD,OAAO,SAAS,OAAO,oBAAoB;EACzC,QAAQ,OAAO,IAAI,qBAAqB;EACxC,cAAc,OAAO,IAAI,wBAAwB,CAAC,CAAC;CACrD,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.mts","names":[],"sources":["../../../../../../../core/src/http/server.ts"],"mappings":";;;KAGY,iBAAA,GAAkB,UAAU,QAAQ,OAAA;AAAA,iBAYhC,eAAA,CAAgB,OAAA,GAAU,oBAAA,GAAuB,iBAAe;AAZhF;;;AAAA,iBA8BgB,aAAA,IAAiB,iBAAe;AA9BO;AAYvD;;;AAZuD,KAsC3C,cAAA;EAAmB,KAAA,QAAa,OAAO;AAAA;;;AA1B6B;AAkBhF;;;;iBAiBsB,sBAAA,CACpB,MAAA,EAAQ,cAAA,EACR,SAAA,WACC,OAAO"}
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../../../../../../core/src/http/server.ts"],"mappings":";;;KAGY,iBAAA,GAAkB,UAAU,QAAQ,OAAA;AAAA,iBAKhC,eAAA,CAAgB,OAAA,GAAU,oBAAA,GAAuB,iBAAe;AALhF;;;AAAA,iBAiCgB,aAAA,IAAiB,iBAAe;AAjCO;AAKvD;;;AALuD,KAyC3C,cAAA;EAAmB,KAAA,QAAa,OAAO;AAAA;;;AApC6B;AA4BhF;;;;iBAiBsB,sBAAA,CACpB,MAAA,EAAQ,cAAA,EACR,SAAA,WACC,OAAO"}
@@ -2,17 +2,12 @@ import config from "@mongez/config";
2
2
  import Fastify from "fastify";
3
3
 
4
4
  //#region ../core/src/http/server.ts
5
- /**
6
- * Default Fastify body limit. Kept at the historical 200GB so existing apps
7
- * don't regress on upgrade. Override via `http.bodyLimit` in config; for
8
- * per-route caps use the `maxBodySize()` middleware.
9
- */
10
- const DEFAULT_BODY_LIMIT = 200 * 1024 * 1024 * 1024;
11
5
  let server = void 0;
12
6
  function startHttpServer(options) {
7
+ const bodyLimit = config.get("http.bodyLimit") ?? void 0;
13
8
  return server = Fastify({
14
- trustProxy: config.get("http.trustProxy", true),
15
- bodyLimit: config.get("http.bodyLimit", DEFAULT_BODY_LIMIT),
9
+ trustProxy: config.get("http.trustProxy", false),
10
+ ...bodyLimit !== void 0 && { bodyLimit },
16
11
  forceCloseConnections: config.get("http.gracefulShutdown.forceCloseConnections", "idle"),
17
12
  ...options
18
13
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":[],"sources":["../../../../../../../core/src/http/server.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport Fastify, { FastifyServerOptions } from \"fastify\";\r\n\r\nexport type FastifyInstance = ReturnType<typeof Fastify>;\r\n\r\n/**\r\n * Default Fastify body limit. Kept at the historical 200GB so existing apps\r\n * don't regress on upgrade. Override via `http.bodyLimit` in config; for\r\n * per-route caps use the `maxBodySize()` middleware.\r\n */\r\nconst DEFAULT_BODY_LIMIT = 200 * 1024 * 1024 * 1024;\r\n\r\n// Instantiate Fastify server\r\nlet server: FastifyInstance | undefined = undefined;\r\n\r\nexport function startHttpServer(options?: FastifyServerOptions): FastifyInstance {\r\n return (server = Fastify({\r\n // Configurable so deployments NOT behind a trusted proxy can disable\r\n // `X-Forwarded-For` resolution (it's client-settable and spoofable).\r\n // Defaults to `true` to preserve historical behavior for proxied apps.\r\n trustProxy: config.get(\"http.trustProxy\", true),\r\n bodyLimit: config.get(\"http.bodyLimit\", DEFAULT_BODY_LIMIT),\r\n // Close idle keep-alive connections on shutdown while letting in-flight\r\n // requests finish — the basis for graceful draining. Override via\r\n // `http.gracefulShutdown.forceCloseConnections`.\r\n forceCloseConnections: config.get(\"http.gracefulShutdown.forceCloseConnections\", \"idle\"),\r\n ...options,\r\n }));\r\n}\r\n\r\n/**\r\n * Expose the server to be publicly accessible\r\n */\r\nexport function getHttpServer(): FastifyInstance {\r\n return server;\r\n}\r\n\r\n/**\r\n * Minimal shape needed to close a server — lets {@link closeServerWithTimeout}\r\n * be unit-tested with a fake instead of a real Fastify instance.\r\n */\r\nexport type ClosableServer = { close: () => Promise<unknown> };\r\n\r\n/**\r\n * Close a server, bounded by a timeout. Fastify's `close()` stops accepting new\r\n * requests (it answers 503 while closing) and drains the in-flight ones; this\r\n * wraps it so a single stuck request can't hang shutdown forever.\r\n *\r\n * @returns `true` if the server drained cleanly, `false` if the timeout fired first.\r\n */\r\nexport async function closeServerWithTimeout(\r\n server: ClosableServer,\r\n timeoutMs: number,\r\n): Promise<boolean> {\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n const drained = server.close().then(() => true);\r\n\r\n const timedOut = new Promise<boolean>((resolve) => {\r\n timer = setTimeout(() => resolve(false), timeoutMs);\r\n });\r\n\r\n try {\r\n return await Promise.race([drained, timedOut]);\r\n } finally {\r\n if (timer) {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;AAUA,MAAM,qBAAqB,MAAM,OAAO,OAAO;AAG/C,IAAI,SAAsC;AAE1C,SAAgB,gBAAgB,SAAiD;CAC/E,OAAQ,SAAS,QAAQ;EAIvB,YAAY,OAAO,IAAI,mBAAmB,IAAI;EAC9C,WAAW,OAAO,IAAI,kBAAkB,kBAAkB;EAI1D,uBAAuB,OAAO,IAAI,+CAA+C,MAAM;EACvF,GAAG;CACL,CAAC;AACH;;;;AAKA,SAAgB,gBAAiC;CAC/C,OAAO;AACT;;;;;;;;AAeA,eAAsB,uBACpB,QACA,WACkB;CAClB,IAAI;CAEJ,MAAM,UAAU,OAAO,MAAM,CAAC,CAAC,WAAW,IAAI;CAE9C,MAAM,WAAW,IAAI,SAAkB,YAAY;EACjD,QAAQ,iBAAiB,QAAQ,KAAK,GAAG,SAAS;CACpD,CAAC;CAED,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,CAAC;CAC/C,UAAU;EACR,IAAI,OACF,aAAa,KAAK;CAEtB;AACF"}
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../../../../../../../core/src/http/server.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport Fastify, { FastifyServerOptions } from \"fastify\";\r\n\r\nexport type FastifyInstance = ReturnType<typeof Fastify>;\r\n\r\n// Instantiate Fastify server\r\nlet server: FastifyInstance | undefined = undefined;\r\n\r\nexport function startHttpServer(options?: FastifyServerOptions): FastifyInstance {\r\n // `config.set(key, undefined)` stores null rather than unsetting, so an app\r\n // that clears a key would otherwise hand Fastify `null` and crash the boot.\r\n const bodyLimit = config.get(\"http.bodyLimit\") ?? undefined;\r\n\r\n return (server = Fastify({\r\n // `X-Forwarded-For` is client-settable and spoofable, and `request.ip` is\r\n // what @fastify/rate-limit keys its buckets on so trusting it by default\r\n // makes rate limiting bypassable on any deployment NOT behind a proxy that\r\n // strips the header. Apps behind such a proxy opt in explicitly.\r\n trustProxy: config.get(\"http.trustProxy\", false),\r\n // No default: an app that configures nothing keeps Fastify's own 1MB limit\r\n // rather than the historical 200GB, which silently removed the protection\r\n // Fastify provides. Per-route caps go through `serverOptions.bodyLimit`;\r\n // the `maxBodySize()` middleware runs AFTER parsing and cannot reject\r\n // before the bytes are resident.\r\n ...(bodyLimit !== undefined && { bodyLimit }),\r\n // Close idle keep-alive connections on shutdown while letting in-flight\r\n // requests finish — the basis for graceful draining. Override via\r\n // `http.gracefulShutdown.forceCloseConnections`.\r\n forceCloseConnections: config.get(\"http.gracefulShutdown.forceCloseConnections\", \"idle\"),\r\n ...options,\r\n }));\r\n}\r\n\r\n/**\r\n * Expose the server to be publicly accessible\r\n */\r\nexport function getHttpServer(): FastifyInstance {\r\n return server;\r\n}\r\n\r\n/**\r\n * Minimal shape needed to close a server — lets {@link closeServerWithTimeout}\r\n * be unit-tested with a fake instead of a real Fastify instance.\r\n */\r\nexport type ClosableServer = { close: () => Promise<unknown> };\r\n\r\n/**\r\n * Close a server, bounded by a timeout. Fastify's `close()` stops accepting new\r\n * requests (it answers 503 while closing) and drains the in-flight ones; this\r\n * wraps it so a single stuck request can't hang shutdown forever.\r\n *\r\n * @returns `true` if the server drained cleanly, `false` if the timeout fired first.\r\n */\r\nexport async function closeServerWithTimeout(\r\n server: ClosableServer,\r\n timeoutMs: number,\r\n): Promise<boolean> {\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n const drained = server.close().then(() => true);\r\n\r\n const timedOut = new Promise<boolean>((resolve) => {\r\n timer = setTimeout(() => resolve(false), timeoutMs);\r\n });\r\n\r\n try {\r\n return await Promise.race([drained, timedOut]);\r\n } finally {\r\n if (timer) {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;AAMA,IAAI,SAAsC;AAE1C,SAAgB,gBAAgB,SAAiD;CAG/E,MAAM,YAAY,OAAO,IAAI,gBAAgB,KAAK;CAElD,OAAQ,SAAS,QAAQ;EAKvB,YAAY,OAAO,IAAI,mBAAmB,KAAK;EAM/C,GAAI,cAAc,UAAa,EAAE,UAAU;EAI3C,uBAAuB,OAAO,IAAI,+CAA+C,MAAM;EACvF,GAAG;CACL,CAAC;AACH;;;;AAKA,SAAgB,gBAAiC;CAC/C,OAAO;AACT;;;;;;;;AAeA,eAAsB,uBACpB,QACA,WACkB;CAClB,IAAI;CAEJ,MAAM,UAAU,OAAO,MAAM,CAAC,CAAC,WAAW,IAAI;CAE9C,MAAM,WAAW,IAAI,SAAkB,YAAY;EACjD,QAAQ,iBAAiB,QAAQ,KAAK,GAAG,SAAS;CACpD,CAAC;CAED,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,CAAC;CAC/C,UAAU;EACR,IAAI,OACF,aAAa,KAAK;CAEtB;AACF"}
package/esm/index.d.mts CHANGED
@@ -63,6 +63,11 @@ import { NoopChannel } from "./benchmark/channels/noop.channel.mjs";
63
63
  import { bootstrap } from "./bootstrap.mjs";
64
64
  import { displayEnvironmentMode } from "./bootstrap/setup.mjs";
65
65
  import { DatabaseCacheDriver, DatabaseCacheOptions } from "./cache/database-cache-driver.mjs";
66
+ import { ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry } from "./config/types.mjs";
67
+ import { config } from "./config/config-getter.mjs";
68
+ import { registerAppConfig } from "./config/config-handlers.mjs";
69
+ import { setConfig } from "./config/config-setter.mjs";
70
+ import { ConfigSpecialHandlers, configSpecialHandlers } from "./config/config-special-handlers.mjs";
66
71
  import { Connector, ConnectorLifecyclePhase, ConnectorName, ConnectorPriority } from "./connectors/types.mjs";
67
72
  import { BaseConnector } from "./connectors/base-connector.mjs";
68
73
  import { AccessConnector } from "./connectors/access-connector.mjs";
@@ -77,23 +82,14 @@ import { MailerConnector } from "./connectors/mail-connector.mjs";
77
82
  import { NotificationsConnector } from "./connectors/notifications-connector.mjs";
78
83
  import { SocketConnector } from "./connectors/socket-connector.mjs";
79
84
  import { StorageConnector } from "./connectors/storage.connector.mjs";
80
- import { CLICommandAction, CLICommandOption, CLICommandOptions, CLICommandPreload, CLICommandSource, CommandActionData, ResolvedCLICommandOption } from "./cli/types.mjs";
81
- import { CLICommand, command } from "./cli/cli-command.mjs";
82
- import { ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry } from "./config/types.mjs";
83
- import { config } from "./config/config-getter.mjs";
84
- import { registerAppConfig } from "./config/config-handlers.mjs";
85
- import { setConfig } from "./config/config-setter.mjs";
86
- import { ConfigSpecialHandlers, configSpecialHandlers } from "./config/config-special-handlers.mjs";
85
+ import { CLICommandAction, CLICommandOption, CLICommandOptions, CLICommandPreload, CLICommandSource, CommandActionData, ResolvedCLICommandOption } from "./commands/types.mjs";
86
+ import { CLICommand, command } from "./commands/cli-command.mjs";
87
87
  import { ContainerTypes, container } from "./container/index.mjs";
88
88
  import { DatabaseLogModel } from "./database/models/database-log/database-log.mjs";
89
89
  import { SeedClock, SeedContext, SeedRecordRef, SeedResult, SeederMetadata, Track, TrackableModel } from "./database/seeds/types.mjs";
90
90
  import { Seeder, seeder } from "./database/seeds/seeder.mjs";
91
91
  import { SeederDependencyCycleError, UnknownSeederDependencyError } from "./database/seeds/seeder.errors.mjs";
92
92
  import { useComputedModel, useComputedSlug, useHashedPassword } from "./database/utils.mjs";
93
- import { FilesOrchestrator, filesOrchestrator, onCleanup } from "./dev-server/files-orchestrator.mjs";
94
- import { BaseHealthChecker } from "./dev-server/health-checker/checkers/base-health-checker.mjs";
95
- import { EslintHealthChecker } from "./dev-server/health-checker/checkers/eslint-health-checker.mjs";
96
- import { TypescriptHealthChecker } from "./dev-server/health-checker/checkers/typescript-health-checker.mjs";
97
93
  import { decrypt, encrypt } from "./encryption/encrypt.mjs";
98
94
  import { hmacHash } from "./encryption/hash.mjs";
99
95
  import { hashPassword, verifyPassword } from "./encryption/password.mjs";
@@ -120,10 +116,6 @@ import { RegisterResource } from "./resource/register-resource.mjs";
120
116
  import { Restful } from "./restful/restful.mjs";
121
117
  import { SocketOptions } from "./socket/types.mjs";
122
118
  import { getSocketServer } from "./socket/utils.mjs";
123
- import { StartHttpTestServerOptions, isTestServerRunning, startHttpTestServer, stopHttpTestServer } from "./tests/start-http-development-server.mjs";
124
- import { expectJson, getTestServerUrl, parseJsonResponse, testDelete, testGet, testPatch, testPost, testPut, testRequest } from "./tests/test-helpers.mjs";
125
- import { TEST_SERVER_PORT_ENV_KEY, publishTestServerPort, withdrawTestServerPort } from "./tests/test-server-port-channel.mjs";
126
- import { setupTest } from "./tests/vitest-setup.mjs";
127
119
  import { RegisteredUseCase, UseCase, UseCaseAfterMiddleware, UseCaseBeforeMiddleware, UseCaseBroadcastChannel, UseCaseBroadcastEvent, UseCaseBroadcastOption, UseCaseConfigurations, UseCaseContext, UseCaseErrorResult, UseCaseEventsCallbacksMap, UseCaseGuard, UseCaseHandler, UseCaseOnExecutingContext, UseCaseResult, UseCaseRuntimeOptions, UseCaseWithSchema } from "./use-cases/types.mjs";
128
120
  import { useCase } from "./use-cases/use-case.mjs";
129
121
  import { broadcastUseCaseResult } from "./use-cases/use-case-broadcast.mjs";
@@ -133,6 +125,7 @@ import { BadSchemaUseCaseError } from "./use-cases/use-case.errors.mjs";
133
125
  import { $registerUseCase, $unregisterUseCase, addUseCaseHistory, getUseCase, getUseCaseHistory, getUseCases, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls } from "./use-cases/use-cases-registry.mjs";
134
126
  import { appLog } from "./utils/app-log.mjs";
135
127
  import { DatabaseLog, DatabaseLogOptions } from "./utils/database-log.mjs";
128
+ import { Path } from "./utils/normalized-path.mjs";
136
129
  import { fetchLatestVersion } from "./utils/npm-registry.mjs";
137
130
  import { appPath, cachePath, configPath, logsPath, paths, publicPath, rootPath, sanitizePath, srcPath, storagePath, tempPath, uploadsPath, warlockPath } from "./utils/paths.mjs";
138
131
  import { promiseAllObject } from "./utils/promise-all-object.mjs";
@@ -150,14 +143,13 @@ import { uniqueExceptCurrentUserRule } from "./validation/database/unique-except
150
143
  import { FileValidator, uploadedFileMetadataSchema } from "./validation/validators/file-validator.mjs";
151
144
  import { ValidationConfiguration } from "./validation/types.mjs";
152
145
  import { fileExtensionRule, fileRule, fileTypeRule, imageRule } from "./validation/file/file.mjs";
153
- import { DecoratorLoweringPlugin, lowerStage3Decorators } from "./vite/lower-stage3-decorators.mjs";
154
146
  import { WarlockConfig } from "./warlock-config/types.mjs";
155
147
  import { defineConfig } from "./warlock-config/define-config.mjs";
156
148
  import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager } from "./warlock-config/warlock-config.manager.mjs";
157
149
  import { env } from "@mongez/dotenv";
158
150
  import { colors } from "@mongez/copper";
159
151
  export * from "@mongez/localization";
160
- export { $registerUseCase, $unregisterUseCase, AccessConnector, AggregateExpressionInput, AggregateExpressions, AiConnector, AllRepositoryOptions, AppConfigurations, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, BaseHealthChecker, type BenchmarkChannel, type BenchmarkConfigurations, type BenchmarkErrorResult, type BenchmarkOptions, BenchmarkProfiler, type BenchmarkProfilerOptions, type BenchmarkResult, BenchmarkSnapshots, type BenchmarkSnapshotsOptions, type BenchmarkStats, type BenchmarkSuccessResult, BootContext, BootListener, BootSignal, BootSignalType, CLICommand, CLICommandAction, CLICommandOption, CLICommandOptions, CLICommandPreload, CLICommandSource, CacheConnector, type CacheMiddlewareOptions, CachedRepositoryOptions, type CapturedMail, CascadeAdapter, CascadeQueryBuilder, ChunkCallback, ClosableServer, CloudDriver, CloudStorageDriverContract, CloudStorageDriverOptions, CloudStorageFileData, CommandActionData, type ConcurrencyLimitOptions, ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry, ConfigSpecialHandlers, ConflictError, Connector, ConnectorLifecyclePhase, ConnectorName, ConnectorPriority, ConnectorsManager, ConsoleChannel, ContainerTypes, CookieOptions, CursorPaginationOptions, CursorPaginationResult, DOSpacesDriver, DatabaseCacheDriver, type DatabaseCacheOptions, DatabaseConnector, DatabaseLog, DatabaseLogModel, DatabaseLogOptions, DecoratorLoweringPlugin, DefineResourceOptions, DeleteManyResult, EncryptionConfigurations, EncryptionPasswordConfigurations, Environment, EslintHealthChecker, EventSubscription, ExistsExceptCurrentIdRuleOptions, ExistsExceptCurrentUserRuleOptions, FastifyInstance, FileNamingStrategy, FileValidationOptions, FileValidator, FileVisibility, FilesOrchestrator, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, ForbiddenError, GroupByFields, GroupedRoutesOptions, HealthCheck, HealthStatus, HeraldConnector, HttpConfigurations, HttpConnector, HttpError, HttpErrorCodes, type IdempotencyOptions, Image, ImageFormat, ImageInput, ImageTransformCallback, ImageTransformConfig, ImageTransformOptions, type IpFilterOptions, ListOptions, LocalDriver, LocalStorageDriverOptions, LocalizedObject, LogConfigurations, LoggerConnector, MAIL_EVENTS, Mail, type MailAddress, type MailAttachment, type MailConfigurations, MailError, type MailErrorCode, type MailEvents, type MailMode, type MailOptions, type MailPriority, type MailResult, MailerConnector, type MailersConfig, type MaintenanceOptions, Middleware, MiddlewareResponse, MimeTypes, NoopChannel, type NormalizedMail, NotAcceptableError, NotAllowedError, NotificationsConnector, PaginationMode, PaginationResult, PartialMiddleware, PartialPick, PipelineOptions, PortInUseError, PrefixConfig, PrefixOptions, PresignedOptions, PresignedUploadOptions, PutDirectoryOptions, PutDirectoryResult, PutFromUrlOptions, PutOptions, QueryBuilderContract, Queue, R2Driver, R2StorageDriverOptions, type RateLimitOptions, RegisterResource, RegisteredUseCase, RepositoryAdapterContract, RepositoryConfigurations, RepositoryEvent, RepositoryManager, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, Request, RequestContextStore, RequestController, RequestControllerContract, RequestEvent, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestLog, RequestMethod, ResolvedCLICommandOption, Resource, ResourceArraySchema, ResourceCastType, ResourceConstructor, ResourceContract, ResourceFieldBuilder, ResourceFieldBuilderDateOutputOptions, ResourceFieldConfig, ResourceMethod, ResourceNotFoundError, ResourceOutputValueCastType, ResourceSchema, ResourceSelfReference, Response, ResponseBodyValue, ResponseEvent, ResponseSSEController, ResponseSchema, ResponseStatus, ResponseStreamController, Restful, RestfulMiddleware, ReturnedResponse, Route, RouteOptions, RouteResource, Router, RouterGroupCallback, RouterStacks, RuntimeStrategy, S3Driver, type SESConfigurations, type SMTPConfigurations, SafeFetchOptions, SafeFetchResult, SaveAsOptions, SaveMode, SaveOptions, ScopedStorage, ScopedStorageContract, SeedClock, SeedContext, SeedRecordRef, SeedResult, Seeder, SeederDependencyCycleError, SeederMetadata, SendBufferOptions, SendFileOptions, ServerError, ShutdownListener, SocketConnector, SocketOptions, StartHttpTestServerOptions, Storage, StorageConfigurations, StorageConnector, StorageCopyEventPayload, StorageDriverConfig, StorageDriverContextStore, StorageDriverContract, StorageDriverName, StorageDriverRegistry, StorageDriverType, StorageError, StorageErrorOptions, StorageEventHandler, StorageEventPayload, StorageEventType, StorageFile, StorageFileData, StorageFileInfo, StorageManagerContract, StoragePutEventPayload, TEST_SERVER_PORT_ENV_KEY, TemporaryTokenError, TemporaryTokenPayload, TemporaryTokenValidation, Track, TrackableModel, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, TypescriptHealthChecker, UPLOADS_DEFAULTS, UnAuthorizedError, UniqueExceptCurrentIdRuleOptions, UniqueExceptCurrentUserRuleOptions, UnknownSeederDependencyError, UploadedFile, UploadedFileImageOptions, UploadsConfigurations, UseCase, UseCaseAfterMiddleware, UseCaseBeforeMiddleware, UseCaseBroadcastChannel, UseCaseBroadcastEvent, UseCaseBroadcastOption, UseCaseConfigurations, UseCaseContext, UseCaseErrorResult, UseCaseEventsCallbacksMap, UseCaseGuard, UseCaseHandler, UseCaseOnExecutingContext, UseCaseResult, UseCaseRuntimeOptions, UseCaseWithSchema, ValidationConfiguration, WarlockConfig, WarlockConfigManager, WatermarkConfig, WhereOperator, addUseCaseHistory, anyMatch, app, appLog, appPath, assertMailCount, assertMailSent, assertPortIsAvailable, assetsUrl, bootstrap, broadcastUseCaseResult, buildIdempotencyCacheKey, cachePath, captureMail, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, displayEnvironmentMode, encrypt, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, expectJson, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, filesOrchestrator, findMailsBySubject, findMailsTo, fireLifecycleEvent, fromRequest, generateMailId, getDefaultMailConfig, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getSocketServer, getTestMailbox, getTestServerUrl, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, ipMatches, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isTestServerRunning, isUnknownTsExtensionError, isValidIdempotencyKey, loadS3, logResponse, logsPath, lowerStage3Decorators, mailEvents, measure, middleware, onCleanup, parseJsonResponse, parseSize, paths, promiseAllObject, publicPath, publicUrl, publishTestServerPort, registerAppConfig, registerHttpPlugins, renderReact, renderReactMail, requestContext, resetMailConfig, resolveMailConfig, resolveWithinRoot, rootPath, router, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, setBaseUrl, setConfig, setEnvironment, setLogConfigurations, setMailConfigurations, setMailMode, setupTest, sleep, sluggable, srcPath, startHttpServer, startHttpTestServer, stopHttpApplication, stopHttpTestServer, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, t, tempPath, testDelete, testGet, testPatch, testPost, testPut, testRequest, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, withdrawTestServerPort, wrapResponseInDataKey };
152
+ export { $registerUseCase, $unregisterUseCase, AccessConnector, AggregateExpressionInput, AggregateExpressions, AiConnector, AllRepositoryOptions, AppConfigurations, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, type BenchmarkChannel, type BenchmarkConfigurations, type BenchmarkErrorResult, type BenchmarkOptions, BenchmarkProfiler, type BenchmarkProfilerOptions, type BenchmarkResult, BenchmarkSnapshots, type BenchmarkSnapshotsOptions, type BenchmarkStats, type BenchmarkSuccessResult, BootContext, BootListener, BootSignal, BootSignalType, CLICommand, type CLICommandAction, type CLICommandOption, type CLICommandOptions, type CLICommandPreload, type CLICommandSource, CacheConnector, type CacheMiddlewareOptions, CachedRepositoryOptions, type CapturedMail, CascadeAdapter, CascadeQueryBuilder, ChunkCallback, ClosableServer, CloudDriver, CloudStorageDriverContract, CloudStorageDriverOptions, CloudStorageFileData, type CommandActionData, type ConcurrencyLimitOptions, ConfigKey, ConfigKeyRegistry, ConfigName, ConfigRegistry, ConfigSpecialHandlers, ConflictError, Connector, ConnectorLifecyclePhase, ConnectorName, ConnectorPriority, ConnectorsManager, ConsoleChannel, ContainerTypes, CookieOptions, CursorPaginationOptions, CursorPaginationResult, DOSpacesDriver, DatabaseCacheDriver, type DatabaseCacheOptions, DatabaseConnector, DatabaseLog, DatabaseLogModel, DatabaseLogOptions, DefineResourceOptions, DeleteManyResult, EncryptionConfigurations, EncryptionPasswordConfigurations, Environment, EventSubscription, ExistsExceptCurrentIdRuleOptions, ExistsExceptCurrentUserRuleOptions, FastifyInstance, FileNamingStrategy, FileValidationOptions, FileValidator, FileVisibility, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, ForbiddenError, GroupByFields, GroupedRoutesOptions, HealthCheck, HealthStatus, HeraldConnector, HttpConfigurations, HttpConnector, HttpError, HttpErrorCodes, type IdempotencyOptions, Image, ImageFormat, ImageInput, ImageTransformCallback, ImageTransformConfig, ImageTransformOptions, type IpFilterOptions, ListOptions, LocalDriver, LocalStorageDriverOptions, LocalizedObject, LogConfigurations, LoggerConnector, MAIL_EVENTS, Mail, type MailAddress, type MailAttachment, type MailConfigurations, MailError, type MailErrorCode, type MailEvents, type MailMode, type MailOptions, type MailPriority, type MailResult, MailerConnector, type MailersConfig, type MaintenanceOptions, Middleware, MiddlewareResponse, MimeTypes, NoopChannel, type NormalizedMail, NotAcceptableError, NotAllowedError, NotificationsConnector, PaginationMode, PaginationResult, PartialMiddleware, PartialPick, Path, PipelineOptions, PortInUseError, PrefixConfig, PrefixOptions, PresignedOptions, PresignedUploadOptions, PutDirectoryOptions, PutDirectoryResult, PutFromUrlOptions, PutOptions, QueryBuilderContract, Queue, R2Driver, R2StorageDriverOptions, type RateLimitOptions, RegisterResource, RegisteredUseCase, RepositoryAdapterContract, RepositoryConfigurations, RepositoryEvent, RepositoryManager, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, Request, RequestContextStore, RequestController, RequestControllerContract, RequestEvent, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestLog, RequestMethod, type ResolvedCLICommandOption, Resource, ResourceArraySchema, ResourceCastType, ResourceConstructor, ResourceContract, ResourceFieldBuilder, ResourceFieldBuilderDateOutputOptions, ResourceFieldConfig, ResourceMethod, ResourceNotFoundError, ResourceOutputValueCastType, ResourceSchema, ResourceSelfReference, Response, ResponseBodyValue, ResponseEvent, ResponseSSEController, ResponseSchema, ResponseStatus, ResponseStreamController, Restful, RestfulMiddleware, ReturnedResponse, Route, RouteOptions, RouteResource, Router, RouterGroupCallback, RouterStacks, RuntimeStrategy, S3Driver, type SESConfigurations, type SMTPConfigurations, SafeFetchOptions, SafeFetchResult, SaveAsOptions, SaveMode, SaveOptions, ScopedStorage, ScopedStorageContract, SeedClock, SeedContext, SeedRecordRef, SeedResult, Seeder, SeederDependencyCycleError, SeederMetadata, SendBufferOptions, SendFileOptions, ServerError, ShutdownListener, SocketConnector, SocketOptions, Storage, StorageConfigurations, StorageConnector, StorageCopyEventPayload, StorageDriverConfig, StorageDriverContextStore, StorageDriverContract, StorageDriverName, StorageDriverRegistry, StorageDriverType, StorageError, StorageErrorOptions, StorageEventHandler, StorageEventPayload, StorageEventType, StorageFile, StorageFileData, StorageFileInfo, StorageManagerContract, StoragePutEventPayload, TemporaryTokenError, TemporaryTokenPayload, TemporaryTokenValidation, Track, TrackableModel, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, UPLOADS_DEFAULTS, UnAuthorizedError, UniqueExceptCurrentIdRuleOptions, UniqueExceptCurrentUserRuleOptions, UnknownSeederDependencyError, UploadedFile, UploadedFileImageOptions, UploadsConfigurations, UseCase, UseCaseAfterMiddleware, UseCaseBeforeMiddleware, UseCaseBroadcastChannel, UseCaseBroadcastEvent, UseCaseBroadcastOption, UseCaseConfigurations, UseCaseContext, UseCaseErrorResult, UseCaseEventsCallbacksMap, UseCaseGuard, UseCaseHandler, UseCaseOnExecutingContext, UseCaseResult, UseCaseRuntimeOptions, UseCaseWithSchema, ValidationConfiguration, WarlockConfig, WarlockConfigManager, WatermarkConfig, WhereOperator, addUseCaseHistory, anyMatch, app, appLog, appPath, assertMailCount, assertMailSent, assertPortIsAvailable, assetsUrl, bootstrap, broadcastUseCaseResult, buildIdempotencyCacheKey, cachePath, captureMail, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, displayEnvironmentMode, encrypt, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, fromRequest, generateMailId, getDefaultMailConfig, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, ipMatches, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isUnknownTsExtensionError, isValidIdempotencyKey, loadS3, logResponse, logsPath, mailEvents, measure, middleware, parseSize, paths, promiseAllObject, publicPath, publicUrl, registerAppConfig, registerHttpPlugins, renderReact, renderReactMail, requestContext, resetMailConfig, resolveMailConfig, resolveWithinRoot, rootPath, router, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, setBaseUrl, setConfig, setEnvironment, setLogConfigurations, setMailConfigurations, setMailMode, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, t, tempPath, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, wrapResponseInDataKey };
161
153
  import "./config/types.mjs";
162
154
  import "./storage/types.mjs";
163
155
  import "./validation/types.mjs";