@warlock.js/core 5.0.1 → 5.1.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 (120) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/esm/cli/cli-commands.manager.mjs +36 -1
  3. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  4. package/esm/cli/commands/dev-server.command.mjs +1 -1
  5. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs +207 -0
  6. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -0
  7. package/esm/cli/commands/doctor/checks/config.check.mjs +5 -2
  8. package/esm/cli/commands/doctor/checks/config.check.mjs.map +1 -1
  9. package/esm/cli/commands/doctor/checks/connectors.check.mjs +38 -12
  10. package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
  11. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +8 -4
  12. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
  13. package/esm/cli/commands/doctor/checks/health.check.mjs +77 -11
  14. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
  15. package/esm/cli/commands/doctor/checks/index.mjs +5 -0
  16. package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
  17. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +116 -53
  18. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  19. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs +27 -9
  20. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -1
  21. package/esm/cli/commands/doctor/checks/routes.check.mjs +51 -12
  22. package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
  23. package/esm/cli/commands/doctor/doctor-command.action.mjs +16 -6
  24. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
  25. package/esm/cli/commands/doctor/run-checks.mjs +15 -6
  26. package/esm/cli/commands/doctor/run-checks.mjs.map +1 -1
  27. package/esm/cli/commands/doctor.command.mjs +17 -9
  28. package/esm/cli/commands/doctor.command.mjs.map +1 -1
  29. package/esm/connectors/describe-server-address.mjs +89 -6
  30. package/esm/connectors/describe-server-address.mjs.map +1 -1
  31. package/esm/connectors/http-connector.d.mts +18 -0
  32. package/esm/connectors/http-connector.d.mts.map +1 -1
  33. package/esm/connectors/http-connector.mjs +36 -3
  34. package/esm/connectors/http-connector.mjs.map +1 -1
  35. package/esm/dev-server/dev-logger.mjs +1 -4
  36. package/esm/dev-server/dev-logger.mjs.map +1 -1
  37. package/esm/dev-server/development-server.mjs +3 -7
  38. package/esm/dev-server/development-server.mjs.map +1 -1
  39. package/esm/dev-server/module-loader.mjs +1 -1
  40. package/esm/dev-server/ready-block.mjs +62 -0
  41. package/esm/dev-server/ready-block.mjs.map +1 -0
  42. package/esm/generations/add-command.action.mjs +2 -575
  43. package/esm/generations/add-command.action.mjs.map +1 -1
  44. package/esm/generations/features/access.feature.mjs +76 -0
  45. package/esm/generations/features/access.feature.mjs.map +1 -0
  46. package/esm/generations/features/ai-anthropic.feature.mjs +10 -0
  47. package/esm/generations/features/ai-anthropic.feature.mjs.map +1 -0
  48. package/esm/generations/features/ai-bedrock.feature.mjs +10 -0
  49. package/esm/generations/features/ai-bedrock.feature.mjs.map +1 -0
  50. package/esm/generations/features/ai-google.feature.mjs +10 -0
  51. package/esm/generations/features/ai-google.feature.mjs.map +1 -0
  52. package/esm/generations/features/ai-ollama.feature.mjs +10 -0
  53. package/esm/generations/features/ai-ollama.feature.mjs.map +1 -0
  54. package/esm/generations/features/ai-openai.feature.mjs +10 -0
  55. package/esm/generations/features/ai-openai.feature.mjs.map +1 -0
  56. package/esm/generations/features/ai-panoptic.feature.mjs +13 -0
  57. package/esm/generations/features/ai-panoptic.feature.mjs.map +1 -0
  58. package/esm/generations/features/ai-tools.feature.mjs +13 -0
  59. package/esm/generations/features/ai-tools.feature.mjs.map +1 -0
  60. package/esm/generations/features/ai-workspace.feature.mjs +13 -0
  61. package/esm/generations/features/ai-workspace.feature.mjs.map +1 -0
  62. package/esm/generations/features/ai.feature.mjs +15 -0
  63. package/esm/generations/features/ai.feature.mjs.map +1 -0
  64. package/esm/generations/features/herald.feature.mjs +19 -0
  65. package/esm/generations/features/herald.feature.mjs.map +1 -0
  66. package/esm/generations/features/image.feature.mjs +9 -0
  67. package/esm/generations/features/image.feature.mjs.map +1 -0
  68. package/esm/generations/features/index.mjs +72 -0
  69. package/esm/generations/features/index.mjs.map +1 -0
  70. package/esm/generations/features/mail.feature.mjs +10 -0
  71. package/esm/generations/features/mail.feature.mjs.map +1 -0
  72. package/esm/generations/features/mongodb.feature.mjs +9 -0
  73. package/esm/generations/features/mongodb.feature.mjs.map +1 -0
  74. package/esm/generations/features/mysql.feature.mjs +9 -0
  75. package/esm/generations/features/mysql.feature.mjs.map +1 -0
  76. package/esm/generations/features/notifications.feature.mjs +43 -0
  77. package/esm/generations/features/notifications.feature.mjs.map +1 -0
  78. package/esm/generations/features/postgres.feature.mjs +9 -0
  79. package/esm/generations/features/postgres.feature.mjs.map +1 -0
  80. package/esm/generations/features/react-email.feature.mjs +70 -0
  81. package/esm/generations/features/react-email.feature.mjs.map +1 -0
  82. package/esm/generations/features/react.feature.mjs +16 -0
  83. package/esm/generations/features/react.feature.mjs.map +1 -0
  84. package/esm/generations/features/redis.feature.mjs +9 -0
  85. package/esm/generations/features/redis.feature.mjs.map +1 -0
  86. package/esm/generations/features/s3.feature.mjs +13 -0
  87. package/esm/generations/features/s3.feature.mjs.map +1 -0
  88. package/esm/generations/features/scheduler.feature.mjs +9 -0
  89. package/esm/generations/features/scheduler.feature.mjs.map +1 -0
  90. package/esm/generations/features/ses.feature.mjs +9 -0
  91. package/esm/generations/features/ses.feature.mjs.map +1 -0
  92. package/esm/generations/features/shadcn.feature.mjs +397 -0
  93. package/esm/generations/features/shadcn.feature.mjs.map +1 -0
  94. package/esm/generations/features/shared/link-ai-package-import.mjs +37 -0
  95. package/esm/generations/features/shared/link-ai-package-import.mjs.map +1 -0
  96. package/esm/generations/features/shared/migration-timestamp.mjs +17 -0
  97. package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -0
  98. package/esm/generations/features/socket.feature.mjs +15 -0
  99. package/esm/generations/features/socket.feature.mjs.map +1 -0
  100. package/esm/generations/features/tailwind.feature.mjs +174 -0
  101. package/esm/generations/features/tailwind.feature.mjs.map +1 -0
  102. package/esm/generations/features/test.feature.mjs +96 -0
  103. package/esm/generations/features/test.feature.mjs.map +1 -0
  104. package/esm/generations/features/web.feature.mjs +179 -0
  105. package/esm/generations/features/web.feature.mjs.map +1 -0
  106. package/esm/generations/stubs.mjs +95 -8
  107. package/esm/generations/stubs.mjs.map +1 -1
  108. package/esm/http/index.d.mts +1 -0
  109. package/esm/http/index.mjs +1 -0
  110. package/esm/http/ready-report.d.mts +56 -0
  111. package/esm/http/ready-report.d.mts.map +1 -0
  112. package/esm/http/ready-report.mjs +24 -0
  113. package/esm/http/ready-report.mjs.map +1 -0
  114. package/esm/index.d.mts +2 -1
  115. package/esm/index.mjs +2 -1
  116. package/llms-full.txt +322 -1
  117. package/llms.txt +2 -0
  118. package/package.json +12 -12
  119. package/skills/use-request-locals/SKILL.md +24 -1
  120. package/skills/warlock-conventions/SKILL.md +5 -1
@@ -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 \"../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 webHomePageStub,\r\n webRootStub,\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 before EVERY test file)\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 * Test Setup - runs before EVERY test file\r\n *\r\n * Vitest runs setupFiles before each test file and rebuilds the module\r\n * registry with it, so this pair boots and closes the test runtime once per\r\n * test file.\r\n *\r\n * setupTest() is called with no options on purpose: an explicit connectors\r\n * value outranks tests.connectors from src/config/tests.ts, so passing one\r\n * here would erase your project config. Omitting it leaves the config in\r\n * charge.\r\n *\r\n * afterAll(teardownTest) is the other half of the pair: whoever calls\r\n * setupTest() owns closing it in the same runtime context.\r\n */\r\nimport { setupTest, teardownTest } from \"@warlock.js/core/tests\";\r\nimport { afterAll } from \"vitest\";\r\n\r\nawait setupTest();\r\n\r\nafterAll(teardownTest);\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 test file\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\n/**\r\n * Register the WebConnector in `warlock.config.ts`, and ONLY there.\r\n *\r\n * It belongs to the config array or to app code, never both. Both halves are\r\n * registered before app code loads — the CLI preloader in dev, the generated\r\n * entry in production — so also calling `connectorsManager.register(...)` in\r\n * `src/app/main.ts` boots the connector twice and installs every page route\r\n * twice. That surfaces at PRODUCTION boot as `Route name \"...\" is already\r\n * taken`, because pages and API routes share one route-name namespace.\r\n *\r\n * The config array is the half to prefer: `warlock build` reads the same array\r\n * to drain each connector's build contribution, so \"built for\" and \"boots with\"\r\n * cannot drift.\r\n *\r\n * String surgery rather than a TypeScript parse: `warlock.config.ts` is an\r\n * app-owned file that may carry any formatting, and a parse-and-print would\r\n * reformat the parts we did not come to change.\r\n */\r\nasync function registerWebConnector(): Promise<void> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n console.log(\r\n `${colors.yellowBright(\"warlock.config.ts\")} not found — add this yourself:\\n` +\r\n ` import { webConnector } from \"@warlock.js/web/connector\";\\n` +\r\n ` export default defineConfig({ connectors: [webConnector()] });`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n const current = await getFileAsync(configPath);\r\n\r\n if (current.includes(\"webConnector\")) {\r\n console.log(`${colors.yellowBright(\"webConnector\")} already registered, skipping...`);\r\n\r\n return;\r\n }\r\n\r\n const importLine = 'import { webConnector } from \"@warlock.js/web/connector\";';\r\n let next = current.includes(importLine) ? current : `${importLine}\\n${current}`;\r\n\r\n // An existing `connectors: [` gains one entry; otherwise the key is added to\r\n // the object `defineConfig` receives.\r\n if (/connectors:\\s*\\[/.test(next)) {\r\n next = next.replace(/connectors:\\s*\\[/, \"connectors: [webConnector(),\");\r\n } else if (next.includes(\"defineConfig({\")) {\r\n next = next.replace(\"defineConfig({\", \"defineConfig({\\n connectors: [webConnector()],\");\r\n } else {\r\n console.log(\r\n `${colors.yellowBright(\"warlock.config.ts\")} has no recognisable defineConfig({...}) — ` +\r\n \"add `connectors: [webConnector()]` yourself.\",\r\n );\r\n\r\n return;\r\n }\r\n\r\n await putFileAsync(configPath, next);\r\n console.log(`${colors.green(\"✓\")} Registered webConnector in warlock.config.ts`);\r\n}\r\n\r\n/**\r\n * The app routes file the project template registers `GET /` in. Only this one\r\n * path is inspected: `warlock add web` is not a codebase-wide route auditor, and\r\n * a project that keeps its routes elsewhere lands on the `absent` outcome below,\r\n * which writes the page exactly as before.\r\n */\r\nconst APP_ROUTES_FILE = \"app/shared/routes.ts\";\r\n\r\n/**\r\n * A TOP-LEVEL `router.get(\"/\", ...)` — anchored at column 0 on purpose.\r\n *\r\n * Routes nested in a `router.group({ prefix: \"/x\" }, ...)` are indented by every\r\n * formatter this codebase runs, and their real path is `/x`, not `/`. Anchoring\r\n * is what keeps the notifications feature's own `router.get(\"/\", ...)` (inside\r\n * the `/notifications` group) from reading as a homepage collision.\r\n *\r\n * Only the path literal is captured. The handler — a bare identifier in the\r\n * template, but possibly an inline arrow spanning lines — is never matched, so\r\n * the rewrite below cannot damage it.\r\n */\r\nconst TOP_LEVEL_ROOT_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/\\1/gm;\r\n\r\n/**\r\n * Whether `/welcome` is already spoken for, so relocating onto it would trade\r\n * one duplicate-route 500 for another.\r\n */\r\nconst TOP_LEVEL_WELCOME_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/welcome\\1/m;\r\n\r\ntype HomeRouteCollision =\r\n /** No app routes file, or nothing claims `/` — write the page as normal. */\r\n | { outcome: \"absent\" }\r\n /** The template's `GET /` was moved to `/welcome`; the page is safe to write. */\r\n | { outcome: \"relocated\" }\r\n /** Something claims `/` that we will not rewrite. The page is NOT written. */\r\n | { outcome: \"conflict\"; reason: string }\r\n /** We tried to relocate and could not. The page is NOT written. */\r\n | { outcome: \"failed\"; reason: string };\r\n\r\n/**\r\n * Make room for a page that declares `route = \"/\"`.\r\n *\r\n * The project template registers `router.get(\"/\", homePageController)` and the\r\n * page stub declares `route = \"/\"`. Fastify rejects the second registration\r\n * (`Method 'GET' already declared for route '/'`) and the homepage 500s at\r\n * request time — so `warlock add web` cannot just write the page and hope.\r\n *\r\n * Of the three ways out, this RELOCATES the JSON route to `/welcome` rather than\r\n * deleting it or refusing to scaffold:\r\n *\r\n * - Deleting the controller is what the scaffolder's own `react` feature does,\r\n * but it may do that: it owns the file it is deleting, seconds after writing\r\n * it. `warlock add web` runs against a project a human has been living in, and\r\n * silently unlinking their code is not a thing an `add` command gets to do.\r\n * - Writing the page anyway and printing a warning ships a project whose\r\n * homepage 500s. A warning above a broken app is still a broken app.\r\n * - Relocating keeps BOTH surfaces working: the React homepage takes `/`, the\r\n * JSON welcome answers at `/welcome`, and no line of user code disappears.\r\n *\r\n * Only the exact top-level shape is rewritten, and only the path literal inside\r\n * it. Anything else that claims `/` is reported and left completely alone — we\r\n * do not guess at code we cannot recognise.\r\n */\r\nasync function relocateConflictingHomeRoute(): Promise<HomeRouteCollision> {\r\n const routesPath = srcPath(APP_ROUTES_FILE);\r\n\r\n // Not every project comes from the template. No file is not a problem.\r\n if (!(await fileExistsAsync(routesPath))) {\r\n return { outcome: \"absent\" };\r\n }\r\n\r\n let current: string;\r\n\r\n try {\r\n current = await getFileAsync(routesPath);\r\n } catch (error) {\r\n return {\r\n outcome: \"failed\",\r\n reason: `could not be read (${(error as Error).message})`,\r\n };\r\n }\r\n\r\n const matches = current.match(TOP_LEVEL_ROOT_GET) ?? [];\r\n\r\n if (matches.length === 0) {\r\n return { outcome: \"absent\" };\r\n }\r\n\r\n if (matches.length > 1) {\r\n return {\r\n outcome: \"conflict\",\r\n reason: `declares ${matches.length} top-level GET \"/\" routes`,\r\n };\r\n }\r\n\r\n if (TOP_LEVEL_WELCOME_GET.test(current)) {\r\n return {\r\n outcome: \"conflict\",\r\n reason: 'already declares GET \"/welcome\", so the usual relocation target is taken',\r\n };\r\n }\r\n\r\n const next = current.replace(TOP_LEVEL_ROOT_GET, (match, quote: string) =>\r\n match.replace(`${quote}/${quote}`, `${quote}/welcome${quote}`),\r\n );\r\n\r\n if (next === current) {\r\n return { outcome: \"conflict\", reason: 'its GET \"/\" route could not be rewritten' };\r\n }\r\n\r\n try {\r\n await putFileAsync(routesPath, next);\r\n } catch (error) {\r\n return {\r\n outcome: \"failed\",\r\n reason: `could not be written (${(error as Error).message})`,\r\n };\r\n }\r\n\r\n return { outcome: \"relocated\" };\r\n}\r\n\r\n/**\r\n * Scaffold the smallest page layer that renders, and register the connector.\r\n *\r\n * `src/web/root.tsx` is the sentinel for \"already scaffolded\" — the framework\r\n * ships a default root, so its presence means a human has been here.\r\n */\r\nasync function completeWebInstallation(_options: CommandActionData) {\r\n const rootFile = srcPath(\"web/root.tsx\");\r\n\r\n if (await fileExistsAsync(rootFile)) {\r\n console.log(`${colors.yellowBright(\"src/web\")} already scaffolded, skipping...`);\r\n } else {\r\n await ensureDirectoryAsync(srcPath(\"web\"));\r\n await putFileAsync(rootFile, webRootStub);\r\n console.log(`${colors.green(\"✓\")} Created src/web/root.tsx`);\r\n\r\n const collision = await relocateConflictingHomeRoute();\r\n\r\n if (collision.outcome === \"relocated\") {\r\n console.log(\r\n `${colors.green(\"✓\")} Moved the existing ${colors.yellowBright('GET \"/\"')} route to ` +\r\n `${colors.yellowBright('\"/welcome\"')} in ${colors.yellowBright(`src/${APP_ROUTES_FILE}`)} — ` +\r\n \"the new page owns `/` now, and the JSON welcome route still answers at /welcome.\",\r\n );\r\n }\r\n\r\n // The page is written ONLY when `/` is provably free. Writing it while\r\n // another handler holds `/` produces a homepage that 500s on first request,\r\n // which is precisely the outcome a scaffolder must never hand back.\r\n if (collision.outcome === \"conflict\" || collision.outcome === \"failed\") {\r\n const verb = collision.outcome === \"failed\" ? colors.redBright(\"✗\") : colors.yellowBright(\"!\");\r\n\r\n console.log(\r\n `${verb} Did not create src/web/home.page.tsx: ` +\r\n `${colors.yellowBright(`src/${APP_ROUTES_FILE}`)} ${collision.reason}.\\n` +\r\n ` The page stub declares ${colors.yellowBright('route = \"/\"')}, and two handlers on one ` +\r\n \"path is a 500 at request time, not a startup error.\\n\" +\r\n ` Free up ${colors.yellowBright('GET \"/\"')} in that file — move it to a path of its own, ` +\r\n \"or remove it — then create src/web/home.page.tsx yourself. Giving the page a `route` other \" +\r\n \"than `/` works too.\",\r\n );\r\n\r\n // Non-zero on BOTH branches. The page layer this command exists to\r\n // scaffold was not scaffolded, and a 0 here is the exact \"looked like it\r\n // worked\" signal that put `/` in this state to begin with — a conflict we\r\n // declined to guess at is still an incomplete install, not a success.\r\n //\r\n // `exitCode` rather than `exit(1)`: the connector below still has to be\r\n // registered, and any other feature in the same `warlock add` invocation\r\n // still has to install, or the project is left half-wired on top of this.\r\n process.exitCode = 1;\r\n } else {\r\n await putFileAsync(srcPath(\"web/home.page.tsx\"), webHomePageStub);\r\n console.log(`${colors.green(\"✓\")} Created src/web/home.page.tsx`);\r\n }\r\n }\r\n\r\n await registerWebConnector();\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 web: {\r\n description:\r\n \"Installs @warlock.js/web — SSR React pages served by the Warlock HTTP server. Scaffolds src/web (root.tsx + a home page) and registers the WebConnector in warlock.config.ts. Pages are opt-in: a Warlock app is an API until you add this.\",\r\n dependencies: {\r\n \"@warlock.js/web\": \"~4.0.0\",\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 // Loaded through `await import()` by the dev server only, so both are\r\n // optional peers of `web` rather than hard dependencies.\r\n vite: \"^7.3.5\",\r\n \"@vitejs/plugin-react\": \"^5.2.0\",\r\n },\r\n onExecuting: completeWebInstallation,\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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"}
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 fileExistsAsync,\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 { featuresMap } from \"./features\";\r\n\r\nexport { featuresMap };\r\nexport type { FeatureDefinition } from \"./features\";\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\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"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import { srcPath } from "../../utils/paths.mjs";
2
+ import "../../utils/index.mjs";
3
+ import { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub } from "../stubs.mjs";
4
+ import { migrationTimestamp } from "./shared/migration-timestamp.mjs";
5
+ import { colors } from "@mongez/copper";
6
+ import { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from "@warlock.js/fs";
7
+
8
+ //#region ../core/src/generations/features/access.feature.ts
9
+ async function registerAccessLocale() {
10
+ const localesPath = srcPath("app/shared/utils/locales.ts");
11
+ const accessLocale = `groupedTranslations("access", {
12
+ errors: {
13
+ forbidden: {
14
+ en: "You do not have permission to perform this action.",
15
+ ar: "ليس لديك صلاحية لتنفيذ هذا الإجراء.",
16
+ },
17
+ },
18
+ });
19
+ `;
20
+ if (await fileExistsAsync(localesPath)) {
21
+ const current = await getFileAsync(localesPath);
22
+ if (current.includes(`groupedTranslations("access"`)) {
23
+ console.log(`${colors.yellowBright("access")} locale already registered, skipping...`);
24
+ return;
25
+ }
26
+ await putFileAsync(localesPath, `${current.includes("groupedTranslations(") ? "" : `import { groupedTranslations } from "@warlock.js/core";\n\n`}${current.trimEnd()}\n\n${accessLocale}`);
27
+ console.log(`${colors.green("✓")} Registered the access locale in src/app/shared/utils/locales.ts`);
28
+ return;
29
+ }
30
+ await ensureDirectoryAsync(srcPath("app/shared/utils"));
31
+ await putFileAsync(localesPath, `import { groupedTranslations } from "@warlock.js/core";\n\n${accessLocale}`);
32
+ console.log(`${colors.green("✓")} Created src/app/shared/utils/locales.ts with the access locale`);
33
+ }
34
+ async function scaffoldAccessFiles() {
35
+ const resolverPath = srcPath("app/access/services/access-resolver.ts");
36
+ if (await fileExistsAsync(resolverPath)) {
37
+ console.log(`${colors.yellowBright("src/app/access")} already scaffolded, skipping resolver + role tables...`);
38
+ return;
39
+ }
40
+ await ensureDirectoryAsync(srcPath("app/access/models/role"));
41
+ await putFileAsync(srcPath("app/access/models/role/role.model.ts"), accessRoleModelStub);
42
+ await putFileAsync(srcPath("app/access/models/role/index.ts"), accessRoleModelIndexStub);
43
+ console.log(`${colors.green("✓")} Created src/app/access/models/role`);
44
+ await ensureDirectoryAsync(srcPath("app/access/models/role/migrations"));
45
+ const roleMigrationFile = `${migrationTimestamp()}-role.migration.ts`;
46
+ await putFileAsync(srcPath("app/access/models/role/migrations", roleMigrationFile), accessRoleMigrationStub);
47
+ console.log(`${colors.green("✓")} Created src/app/access/models/role/migrations/${roleMigrationFile}`);
48
+ await ensureDirectoryAsync(srcPath("app/access/models/user-role"));
49
+ await putFileAsync(srcPath("app/access/models/user-role/user-role.model.ts"), accessUserRoleModelStub);
50
+ await putFileAsync(srcPath("app/access/models/user-role/index.ts"), accessUserRoleModelIndexStub);
51
+ console.log(`${colors.green("✓")} Created src/app/access/models/user-role`);
52
+ await ensureDirectoryAsync(srcPath("app/access/models/user-role/migrations"));
53
+ const userRoleMigrationFile = `${migrationTimestamp(1)}-user-role.migration.ts`;
54
+ await putFileAsync(srcPath("app/access/models/user-role/migrations", userRoleMigrationFile), accessUserRoleMigrationStub);
55
+ console.log(`${colors.green("✓")} Created src/app/access/models/user-role/migrations/${userRoleMigrationFile}`);
56
+ await ensureDirectoryAsync(srcPath("app/access/services"));
57
+ await putFileAsync(resolverPath, accessResolverStub);
58
+ console.log(`${colors.green("✓")} Created src/app/access/services/access-resolver.ts`);
59
+ }
60
+ async function completeAccessInstallation(_options) {
61
+ await registerAccessLocale();
62
+ await scaffoldAccessFiles();
63
+ }
64
+ const accessFeature = {
65
+ description: "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",
66
+ dependencies: { "@warlock.js/access": "~4.0.0" },
67
+ ejectConfig: {
68
+ content: accessConfigStub,
69
+ name: "access"
70
+ },
71
+ onExecuting: completeAccessInstallation
72
+ };
73
+
74
+ //#endregion
75
+ export { accessFeature };
76
+ //# sourceMappingURL=access.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/access.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n putFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { CommandActionData } from \"../../commands/types\";\r\nimport { srcPath } from \"../../utils\";\r\nimport {\r\n accessConfigStub,\r\n accessResolverStub,\r\n accessRoleMigrationStub,\r\n accessRoleModelIndexStub,\r\n accessRoleModelStub,\r\n accessUserRoleMigrationStub,\r\n accessUserRoleModelIndexStub,\r\n accessUserRoleModelStub,\r\n} from \"../stubs\";\r\nimport { migrationTimestamp } from \"./shared/migration-timestamp\";\r\nimport { FeatureDefinition } from \"./types\";\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\nexport const accessFeature: FeatureDefinition = {\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"],"mappings":";;;;;;;;AAsBA,eAAe,uBAAuB;CAIpC,MAAM,cAAc,QAAQ,6BAA6B;CAEzD,MAAM,eAAe;;;;;;;;;CAUrB,IAAI,MAAM,gBAAgB,WAAW,GAAG;EACtC,MAAM,UAAU,MAAM,aAAa,WAAW;EAE9C,IAAI,QAAQ,SAAS,8BAA8B,GAAG;GACpD,QAAQ,IAAI,GAAG,OAAO,aAAa,QAAQ,EAAE,wCAAwC;GAErF;EACF;EAOA,MAAM,aAAa,aAAa,GAFjB,QAAQ,SAAS,sBAAsB,IAAI,KAAK,gEAEnB,QAAQ,QAAQ,EAAE,MAAM,cAAc;EAElF,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iEACvB;EAEA;CACF;CAEA,MAAM,qBAAqB,QAAQ,kBAAkB,CAAC;CAEtD,MAAM,aACJ,aACA,8DAA8D,cAChE;CAEA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,gEAAgE;AACnG;AAEA,eAAe,sBAAsB;CAKnC,MAAM,eAAe,QAAQ,wCAAwC;CAErE,IAAI,MAAM,gBAAgB,YAAY,GAAG;EACvC,QAAQ,IACN,GAAG,OAAO,aAAa,gBAAgB,EAAE,wDAC3C;EAEA;CACF;CAIA,MAAM,qBAAqB,QAAQ,wBAAwB,CAAC;CAC5D,MAAM,aAAa,QAAQ,sCAAsC,GAAG,mBAAmB;CACvF,MAAM,aAAa,QAAQ,iCAAiC,GAAG,wBAAwB;CACvF,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oCAAoC;CAErE,MAAM,qBAAqB,QAAQ,mCAAmC,CAAC;CAOvE,MAAM,oBAAoB,GAAG,mBAAmB,EAAE;CAClD,MAAM,aACJ,QAAQ,qCAAqC,iBAAiB,GAC9D,uBACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iDAAiD,mBACxE;CAKA,MAAM,qBAAqB,QAAQ,6BAA6B,CAAC;CACjE,MAAM,aACJ,QAAQ,gDAAgD,GACxD,uBACF;CACA,MAAM,aACJ,QAAQ,sCAAsC,GAC9C,4BACF;CACA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yCAAyC;CAE1E,MAAM,qBAAqB,QAAQ,wCAAwC,CAAC;CAE5E,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,EAAE;CACvD,MAAM,aACJ,QAAQ,0CAA0C,qBAAqB,GACvE,2BACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,sDAAsD,uBAC7E;CAIA,MAAM,qBAAqB,QAAQ,qBAAqB,CAAC;CACzD,MAAM,aAAa,cAAc,kBAAkB;CACnD,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oDAAoD;AACvF;AAEA,eAAe,2BAA2B,UAA6B;CACrE,MAAM,qBAAqB;CAC3B,MAAM,oBAAoB;AAC5B;AAEA,MAAa,gBAAmC;CAC9C,aACE;CACF,cAAc,EACZ,sBAAsB,SACxB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;CACA,aAAa;AACf"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/ai-anthropic.feature.ts
2
+ const aiAnthropicFeature = {
3
+ description: "Anthropic (Claude) provider for @warlock.js/ai (pulls the core ai package)",
4
+ requires: ["ai"],
5
+ dependencies: { "@warlock.js/ai-anthropic": "~4.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { aiAnthropicFeature };
10
+ //# sourceMappingURL=ai-anthropic.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-anthropic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-anthropic.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiAnthropicFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,qBAAwC;CACnD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,SAC9B;AACF"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/ai-bedrock.feature.ts
2
+ const aiBedrockFeature = {
3
+ description: "AWS Bedrock provider for @warlock.js/ai (pulls the core ai package)",
4
+ requires: ["ai"],
5
+ dependencies: { "@warlock.js/ai-bedrock": "~4.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { aiBedrockFeature };
10
+ //# sourceMappingURL=ai-bedrock.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-bedrock.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-bedrock.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiBedrockFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,0BAA0B,SAC5B;AACF"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/ai-google.feature.ts
2
+ const aiGoogleFeature = {
3
+ description: "Google (Gemini) provider for @warlock.js/ai (pulls the core ai package)",
4
+ requires: ["ai"],
5
+ dependencies: { "@warlock.js/ai-google": "~4.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { aiGoogleFeature };
10
+ //# sourceMappingURL=ai-google.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-google.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-google.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiGoogleFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,SAC3B;AACF"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/ai-ollama.feature.ts
2
+ const aiOllamaFeature = {
3
+ description: "Ollama provider for @warlock.js/ai (pulls the core ai package)",
4
+ requires: ["ai"],
5
+ dependencies: { "@warlock.js/ai-ollama": "~4.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { aiOllamaFeature };
10
+ //# sourceMappingURL=ai-ollama.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-ollama.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-ollama.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiOllamaFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,SAC3B;AACF"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/ai-openai.feature.ts
2
+ const aiOpenaiFeature = {
3
+ description: "OpenAI provider for @warlock.js/ai (pulls the core ai package)",
4
+ requires: ["ai"],
5
+ dependencies: { "@warlock.js/ai-openai": "~4.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { aiOpenaiFeature };
10
+ //# sourceMappingURL=ai-openai.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-openai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-openai.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiOpenaiFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,SAC3B;AACF"}
@@ -0,0 +1,13 @@
1
+ import { linkAiPackageImport } from "./shared/link-ai-package-import.mjs";
2
+
3
+ //#region ../core/src/generations/features/ai-panoptic.feature.ts
4
+ const aiPanopticFeature = {
5
+ description: "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)",
6
+ requires: ["ai"],
7
+ dependencies: { "@warlock.js/ai-panoptic": "~4.0.0" },
8
+ onExecuting: () => linkAiPackageImport("@warlock.js/ai-panoptic")
9
+ };
10
+
11
+ //#endregion
12
+ export { aiPanopticFeature };
13
+ //# sourceMappingURL=ai-panoptic.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-panoptic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-panoptic.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiPanopticFeature: FeatureDefinition = {\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"],"mappings":";;;AAGA,MAAa,oBAAuC;CAClD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,2BAA2B,SAC7B;CACA,mBAAmB,oBAAoB,yBAAyB;AAClE"}
@@ -0,0 +1,13 @@
1
+ import { linkAiPackageImport } from "./shared/link-ai-package-import.mjs";
2
+
3
+ //#region ../core/src/generations/features/ai-tools.feature.ts
4
+ const aiToolsFeature = {
5
+ description: "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)",
6
+ requires: ["ai"],
7
+ dependencies: { "@warlock.js/ai-tools": "~4.0.0" },
8
+ onExecuting: () => linkAiPackageImport("@warlock.js/ai-tools")
9
+ };
10
+
11
+ //#endregion
12
+ export { aiToolsFeature };
13
+ //# sourceMappingURL=ai-tools.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-tools.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-tools.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiToolsFeature: FeatureDefinition = {\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"],"mappings":";;;AAGA,MAAa,iBAAoC;CAC/C,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,wBAAwB,SAC1B;CACA,mBAAmB,oBAAoB,sBAAsB;AAC/D"}
@@ -0,0 +1,13 @@
1
+ import { linkAiPackageImport } from "./shared/link-ai-package-import.mjs";
2
+
3
+ //#region ../core/src/generations/features/ai-workspace.feature.ts
4
+ const aiWorkspaceFeature = {
5
+ description: "Installs @warlock.js/ai-workspace — a policy-jailed filesystem + shell workspace for coding agents, as ai.workspace (pulls the core ai package)",
6
+ requires: ["ai"],
7
+ dependencies: { "@warlock.js/ai-workspace": "~4.0.0" },
8
+ onExecuting: () => linkAiPackageImport("@warlock.js/ai-workspace")
9
+ };
10
+
11
+ //#endregion
12
+ export { aiWorkspaceFeature };
13
+ //# sourceMappingURL=ai-workspace.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-workspace.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-workspace.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiWorkspaceFeature: FeatureDefinition = {\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"],"mappings":";;;AAGA,MAAa,qBAAwC;CACnD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,SAC9B;CACA,mBAAmB,oBAAoB,0BAA0B;AACnE"}
@@ -0,0 +1,15 @@
1
+ import { aiConfigStub } from "../stubs.mjs";
2
+
3
+ //#region ../core/src/generations/features/ai.feature.ts
4
+ const aiFeature = {
5
+ description: "Installs @warlock.js/ai — the core AI toolkit (agents, tools, workflows)",
6
+ dependencies: { "@warlock.js/ai": "~4.0.0" },
7
+ ejectConfig: {
8
+ content: aiConfigStub,
9
+ name: "ai"
10
+ }
11
+ };
12
+
13
+ //#endregion
14
+ export { aiFeature };
15
+ //# sourceMappingURL=ai.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai.feature.ts"],"sourcesContent":["import { aiConfigStub } from \"../stubs\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const aiFeature: FeatureDefinition = {\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"],"mappings":";;;AAGA,MAAa,YAA+B;CAC1C,aAAa;CACb,cAAc,EACZ,kBAAkB,SACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
@@ -0,0 +1,19 @@
1
+ import { communicatorsConfigStub } from "../stubs.mjs";
2
+
3
+ //#region ../core/src/generations/features/herald.feature.ts
4
+ const heraldFeature = {
5
+ description: "Installs herald for message broker (Herald Package)",
6
+ dependencies: {
7
+ "@warlock.js/herald": "~4.0.0",
8
+ amqplib: "^0.10.0"
9
+ },
10
+ devDependencies: { "@types/amqplib": "^0.10.0" },
11
+ ejectConfig: {
12
+ content: communicatorsConfigStub,
13
+ name: "herald"
14
+ }
15
+ };
16
+
17
+ //#endregion
18
+ export { heraldFeature };
19
+ //# sourceMappingURL=herald.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"herald.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/herald.feature.ts"],"sourcesContent":["import { communicatorsConfigStub } from \"../stubs\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const heraldFeature: FeatureDefinition = {\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"],"mappings":";;;AAGA,MAAa,gBAAmC;CAC9C,aAAa;CACb,cAAc;EACZ,sBAAsB;EACtB,SAAS;CACX;CACA,iBAAiB,EACf,kBAAkB,UACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
@@ -0,0 +1,9 @@
1
+ //#region ../core/src/generations/features/image.feature.ts
2
+ const imageFeature = {
3
+ description: "Installs sharp for image processing",
4
+ dependencies: { sharp: "^0.34.5" }
5
+ };
6
+
7
+ //#endregion
8
+ export { imageFeature };
9
+ //# sourceMappingURL=image.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/image.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const imageFeature: FeatureDefinition = {\r\n description: \"Installs sharp for image processing\",\r\n dependencies: {\r\n sharp: \"^0.34.5\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,OAAO,UACT;AACF"}
@@ -0,0 +1,72 @@
1
+ import { accessFeature } from "./access.feature.mjs";
2
+ import { aiAnthropicFeature } from "./ai-anthropic.feature.mjs";
3
+ import { aiBedrockFeature } from "./ai-bedrock.feature.mjs";
4
+ import { aiGoogleFeature } from "./ai-google.feature.mjs";
5
+ import { aiOllamaFeature } from "./ai-ollama.feature.mjs";
6
+ import { aiOpenaiFeature } from "./ai-openai.feature.mjs";
7
+ import { aiPanopticFeature } from "./ai-panoptic.feature.mjs";
8
+ import { aiToolsFeature } from "./ai-tools.feature.mjs";
9
+ import { aiWorkspaceFeature } from "./ai-workspace.feature.mjs";
10
+ import { aiFeature } from "./ai.feature.mjs";
11
+ import { heraldFeature } from "./herald.feature.mjs";
12
+ import { imageFeature } from "./image.feature.mjs";
13
+ import { mailFeature } from "./mail.feature.mjs";
14
+ import { mongodbFeature } from "./mongodb.feature.mjs";
15
+ import { mysqlFeature } from "./mysql.feature.mjs";
16
+ import { notificationsFeature } from "./notifications.feature.mjs";
17
+ import { postgresFeature } from "./postgres.feature.mjs";
18
+ import { reactEmailFeature } from "./react-email.feature.mjs";
19
+ import { reactFeature } from "./react.feature.mjs";
20
+ import { redisFeature } from "./redis.feature.mjs";
21
+ import { s3Feature } from "./s3.feature.mjs";
22
+ import { schedulerFeature } from "./scheduler.feature.mjs";
23
+ import { sesFeature } from "./ses.feature.mjs";
24
+ import { shadcnFeature } from "./shadcn.feature.mjs";
25
+ import { socketFeature } from "./socket.feature.mjs";
26
+ import { tailwindFeature } from "./tailwind.feature.mjs";
27
+ import { testFeature } from "./test.feature.mjs";
28
+ import { webFeature } from "./web.feature.mjs";
29
+
30
+ //#region ../core/src/generations/features/index.ts
31
+ /**
32
+ * The feature registry `warlock add` dispatches against.
33
+ *
34
+ * This file is an INDEX, nothing more: every entry lives in its own module
35
+ * alongside the `onExecuting` body it runs. Key order is load-bearing — it is
36
+ * the order `--list` prints and the order the "not allowed" error lists — so
37
+ * add new features in the place they should appear, not alphabetically.
38
+ */
39
+ const featuresMap = {
40
+ "react-email": reactEmailFeature,
41
+ react: reactFeature,
42
+ image: imageFeature,
43
+ mail: mailFeature,
44
+ ses: sesFeature,
45
+ mongodb: mongodbFeature,
46
+ scheduler: schedulerFeature,
47
+ postgres: postgresFeature,
48
+ mysql: mysqlFeature,
49
+ redis: redisFeature,
50
+ s3: s3Feature,
51
+ test: testFeature,
52
+ web: webFeature,
53
+ tailwind: tailwindFeature,
54
+ shadcn: shadcnFeature,
55
+ herald: heraldFeature,
56
+ socket: socketFeature,
57
+ notifications: notificationsFeature,
58
+ access: accessFeature,
59
+ ai: aiFeature,
60
+ "ai-openai": aiOpenaiFeature,
61
+ "ai-google": aiGoogleFeature,
62
+ "ai-anthropic": aiAnthropicFeature,
63
+ "ai-bedrock": aiBedrockFeature,
64
+ "ai-ollama": aiOllamaFeature,
65
+ "ai-tools": aiToolsFeature,
66
+ "ai-panoptic": aiPanopticFeature,
67
+ "ai-workspace": aiWorkspaceFeature
68
+ };
69
+
70
+ //#endregion
71
+ export { featuresMap };
72
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/index.ts"],"sourcesContent":["import { accessFeature } from \"./access.feature\";\r\nimport { aiAnthropicFeature } from \"./ai-anthropic.feature\";\r\nimport { aiBedrockFeature } from \"./ai-bedrock.feature\";\r\nimport { aiGoogleFeature } from \"./ai-google.feature\";\r\nimport { aiOllamaFeature } from \"./ai-ollama.feature\";\r\nimport { aiOpenaiFeature } from \"./ai-openai.feature\";\r\nimport { aiPanopticFeature } from \"./ai-panoptic.feature\";\r\nimport { aiToolsFeature } from \"./ai-tools.feature\";\r\nimport { aiWorkspaceFeature } from \"./ai-workspace.feature\";\r\nimport { aiFeature } from \"./ai.feature\";\r\nimport { heraldFeature } from \"./herald.feature\";\r\nimport { imageFeature } from \"./image.feature\";\r\nimport { mailFeature } from \"./mail.feature\";\r\nimport { mongodbFeature } from \"./mongodb.feature\";\r\nimport { mysqlFeature } from \"./mysql.feature\";\r\nimport { notificationsFeature } from \"./notifications.feature\";\r\nimport { postgresFeature } from \"./postgres.feature\";\r\nimport { reactEmailFeature } from \"./react-email.feature\";\r\nimport { reactFeature } from \"./react.feature\";\r\nimport { redisFeature } from \"./redis.feature\";\r\nimport { s3Feature } from \"./s3.feature\";\r\nimport { schedulerFeature } from \"./scheduler.feature\";\r\nimport { sesFeature } from \"./ses.feature\";\r\nimport { shadcnFeature } from \"./shadcn.feature\";\r\nimport { socketFeature } from \"./socket.feature\";\r\nimport { tailwindFeature } from \"./tailwind.feature\";\r\nimport { testFeature } from \"./test.feature\";\r\nimport { FeatureDefinition } from \"./types\";\r\nimport { webFeature } from \"./web.feature\";\r\n\r\nexport type { FeatureDefinition } from \"./types\";\r\n\r\n/**\r\n * The feature registry `warlock add` dispatches against.\r\n *\r\n * This file is an INDEX, nothing more: every entry lives in its own module\r\n * alongside the `onExecuting` body it runs. Key order is load-bearing — it is\r\n * the order `--list` prints and the order the \"not allowed\" error lists — so\r\n * add new features in the place they should appear, not alphabetically.\r\n */\r\nexport const featuresMap: Record<string, FeatureDefinition> = {\r\n \"react-email\": reactEmailFeature,\r\n react: reactFeature,\r\n image: imageFeature,\r\n mail: mailFeature,\r\n ses: sesFeature,\r\n mongodb: mongodbFeature,\r\n scheduler: schedulerFeature,\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: postgresFeature,\r\n mysql: mysqlFeature,\r\n redis: redisFeature,\r\n s3: s3Feature,\r\n test: testFeature,\r\n web: webFeature,\r\n // Directly after `web`, and only there: it `requires` it, it is useless\r\n // without it, and a reader scanning `--list` for the page stack should meet\r\n // the two together rather than find styling filed between queues and sockets.\r\n tailwind: tailwindFeature,\r\n // Immediately after `tailwind`, for the same reason `tailwind` follows `web`:\r\n // it `requires` it, it appends to the stylesheet that feature creates, and the\r\n // three of them are one stack a reader should meet in build order.\r\n shadcn: shadcnFeature,\r\n herald: heraldFeature,\r\n socket: socketFeature,\r\n notifications: notificationsFeature,\r\n access: accessFeature,\r\n ai: aiFeature,\r\n \"ai-openai\": aiOpenaiFeature,\r\n \"ai-google\": aiGoogleFeature,\r\n \"ai-anthropic\": aiAnthropicFeature,\r\n \"ai-bedrock\": aiBedrockFeature,\r\n \"ai-ollama\": aiOllamaFeature,\r\n \"ai-tools\": aiToolsFeature,\r\n \"ai-panoptic\": aiPanopticFeature,\r\n \"ai-workspace\": aiWorkspaceFeature,\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,cAAiD;CAC5D,eAAe;CACf,OAAO;CACP,OAAO;CACP,MAAM;CACN,KAAK;CACL,SAAS;CACT,WAAW;CAGX,UAAU;CACV,OAAO;CACP,OAAO;CACP,IAAI;CACJ,MAAM;CACN,KAAK;CAIL,UAAU;CAIV,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,eAAe;CACf,QAAQ;CACR,IAAI;CACJ,aAAa;CACb,aAAa;CACb,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,YAAY;CACZ,eAAe;CACf,gBAAgB;AAClB"}
@@ -0,0 +1,10 @@
1
+ //#region ../core/src/generations/features/mail.feature.ts
2
+ const mailFeature = {
3
+ description: "Installs nodemailer for sending emails",
4
+ dependencies: { nodemailer: "^8.0.5" },
5
+ devDependencies: { "@types/nodemailer": "^8.0.0" }
6
+ };
7
+
8
+ //#endregion
9
+ export { mailFeature };
10
+ //# sourceMappingURL=mail.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mail.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const mailFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,cAAiC;CAC5C,aAAa;CACb,cAAc,EACZ,YAAY,SACd;CACA,iBAAiB,EACf,qBAAqB,SACvB;AACF"}
@@ -0,0 +1,9 @@
1
+ //#region ../core/src/generations/features/mongodb.feature.ts
2
+ const mongodbFeature = {
3
+ description: "Installs mongodb driver for database driver (Cascade Package)",
4
+ dependencies: { mongodb: "^7.0.0" }
5
+ };
6
+
7
+ //#endregion
8
+ export { mongodbFeature };
9
+ //# sourceMappingURL=mongodb.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongodb.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mongodb.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const mongodbFeature: FeatureDefinition = {\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"],"mappings":";AAEA,MAAa,iBAAoC;CAC/C,aAAa;CACb,cAAc,EACZ,SAAS,SACX;AACF"}