@idevconn/create-icore 0.7.2 → 0.9.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 (87) hide show
  1. package/README.md +44 -9
  2. package/dist/cli.js +689 -363
  3. package/dist/index.cjs +806 -358
  4. package/dist/index.d.cts +12 -3
  5. package/dist/index.d.ts +12 -3
  6. package/dist/index.js +798 -351
  7. package/dist/manifest/audit.js +122 -0
  8. package/package.json +1 -1
  9. package/templates/apps/api/src/app/app.module.ts +2 -6
  10. package/templates/apps/api/src/app/features.module.ts +9 -0
  11. package/templates/apps/api/src/app/gateway-services.ts +7 -0
  12. package/templates/apps/api/src/main.ts +1 -5
  13. package/templates/apps/microservices/auth/src/app/app.module.ts +4 -93
  14. package/templates/apps/microservices/auth/src/app/auth.provider.ts +9 -0
  15. package/templates/apps/microservices/notes/src/app/app.module.ts +4 -86
  16. package/templates/apps/microservices/notes/src/app/db.provider.ts +9 -0
  17. package/templates/apps/microservices/upload/src/app/app.module.ts +4 -140
  18. package/templates/apps/microservices/upload/src/app/storage.provider.ts +9 -0
  19. package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +15 -23
  20. package/templates/apps/templates/client-antd/src/nav.config.ts +17 -0
  21. package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +19 -20
  22. package/templates/apps/templates/client-mui/src/nav.config.ts +17 -0
  23. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +20 -16
  24. package/templates/apps/templates/client-shadcn/src/nav.config.ts +17 -0
  25. package/templates/libs/auth-strategies/firebase/eslint.config.mjs +1 -0
  26. package/templates/libs/auth-strategies/firebase/package.json +4 -0
  27. package/templates/libs/auth-strategies/firebase/src/index.ts +1 -0
  28. package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.module.unit.test.ts +49 -0
  29. package/templates/libs/auth-strategies/firebase/src/lib/firebase-auth.module.ts +41 -0
  30. package/templates/libs/auth-strategies/firebase/tsconfig.json +2 -0
  31. package/templates/libs/auth-strategies/mongodb/package.json +8 -1
  32. package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
  33. package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.module.unit.test.ts +16 -0
  34. package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.module.ts +45 -0
  35. package/templates/libs/auth-strategies/mongodb/tsconfig.json +2 -0
  36. package/templates/libs/auth-strategies/supabase/eslint.config.mjs +1 -0
  37. package/templates/libs/auth-strategies/supabase/package.json +3 -0
  38. package/templates/libs/auth-strategies/supabase/src/index.ts +1 -0
  39. package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.module.unit.test.ts +43 -0
  40. package/templates/libs/auth-strategies/supabase/src/lib/supabase-auth.module.ts +41 -0
  41. package/templates/libs/auth-strategies/supabase/tsconfig.json +2 -0
  42. package/templates/libs/db-strategies/firestore/eslint.config.mjs +1 -1
  43. package/templates/libs/db-strategies/firestore/package.json +4 -0
  44. package/templates/libs/db-strategies/firestore/src/index.ts +1 -0
  45. package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.module.unit.test.ts +37 -0
  46. package/templates/libs/db-strategies/firestore/src/lib/firestore-db.module.ts +41 -0
  47. package/templates/libs/db-strategies/firestore/tsconfig.json +2 -0
  48. package/templates/libs/db-strategies/mongodb/package.json +4 -1
  49. package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
  50. package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.module.unit.test.ts +14 -0
  51. package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.module.ts +41 -0
  52. package/templates/libs/db-strategies/mongodb/tsconfig.json +2 -0
  53. package/templates/libs/db-strategies/supabase/eslint.config.mjs +6 -1
  54. package/templates/libs/db-strategies/supabase/package.json +3 -0
  55. package/templates/libs/db-strategies/supabase/src/index.ts +1 -0
  56. package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.module.unit.test.ts +32 -0
  57. package/templates/libs/db-strategies/supabase/src/lib/supabase-db.module.ts +41 -0
  58. package/templates/libs/db-strategies/supabase/tsconfig.json +2 -0
  59. package/templates/libs/shared/src/strategies/__tests__/provide-strategy.unit.test.ts +73 -0
  60. package/templates/libs/shared/src/strategies/index.ts +1 -0
  61. package/templates/libs/shared/src/strategies/provide-strategy.ts +44 -0
  62. package/templates/libs/storage-strategies/cloudinary/eslint.config.mjs +1 -1
  63. package/templates/libs/storage-strategies/cloudinary/package.json +4 -0
  64. package/templates/libs/storage-strategies/cloudinary/src/index.ts +1 -0
  65. package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.module.unit.test.ts +40 -0
  66. package/templates/libs/storage-strategies/cloudinary/src/lib/cloudinary-storage.module.ts +85 -0
  67. package/templates/libs/storage-strategies/cloudinary/tsconfig.json +2 -0
  68. package/templates/libs/storage-strategies/firebase/eslint.config.mjs +1 -1
  69. package/templates/libs/storage-strategies/firebase/package.json +4 -0
  70. package/templates/libs/storage-strategies/firebase/src/index.ts +1 -0
  71. package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.module.unit.test.ts +42 -0
  72. package/templates/libs/storage-strategies/firebase/src/lib/firebase-storage.module.ts +46 -0
  73. package/templates/libs/storage-strategies/firebase/tsconfig.json +2 -0
  74. package/templates/libs/storage-strategies/mongodb/package.json +4 -1
  75. package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
  76. package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.module.unit.test.ts +14 -0
  77. package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.module.ts +41 -0
  78. package/templates/libs/storage-strategies/mongodb/tsconfig.json +2 -0
  79. package/templates/libs/storage-strategies/supabase/eslint.config.mjs +1 -0
  80. package/templates/libs/storage-strategies/supabase/package.json +3 -0
  81. package/templates/libs/storage-strategies/supabase/src/index.ts +1 -0
  82. package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.module.unit.test.ts +46 -0
  83. package/templates/libs/storage-strategies/supabase/src/lib/supabase-storage.module.ts +46 -0
  84. package/templates/libs/storage-strategies/supabase/tsconfig.json +2 -0
  85. package/templates/package.json +1 -1
  86. package/templates/tools/create-icore/_template-shell/package.json +1 -1
  87. package/templates/tsconfig.base.json +1 -1
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ auditProject: () => auditProject,
33
34
  collectOptions: () => collectOptions,
34
35
  pmRun: () => pmRun,
35
36
  scaffold: () => scaffold
@@ -46,9 +47,9 @@ function pmRun(pm, script) {
46
47
  }
47
48
 
48
49
  // src/lib/scaffold.ts
49
- var import_promises4 = require("fs/promises");
50
+ var import_promises8 = require("fs/promises");
50
51
  var import_node_fs = require("fs");
51
- var import_node_path4 = require("path");
52
+ var import_node_path8 = require("path");
52
53
  var import_node_child_process = require("child_process");
53
54
 
54
55
  // src/lib/scaffold-env.ts
@@ -89,6 +90,31 @@ async function stripGatewayTransport(targetDir, prefix) {
89
90
  } catch {
90
91
  }
91
92
  }
93
+ var ROOT_PROVIDER_SDKS = {
94
+ supabase: ["@supabase/supabase-js"],
95
+ cloudinary: ["cloudinary"],
96
+ mongodb: ["mongoose"],
97
+ firebase: ["firebase-admin"]
98
+ };
99
+ async function pruneRootProviderDeps(targetDir, opts) {
100
+ const chosen = /* @__PURE__ */ new Set([opts.authProvider, opts.dbProvider, opts.upload]);
101
+ const drop = /* @__PURE__ */ new Set();
102
+ for (const [provider, sdks] of Object.entries(ROOT_PROVIDER_SDKS)) {
103
+ if (!chosen.has(provider)) for (const sdk of sdks) drop.add(sdk);
104
+ }
105
+ if (drop.size === 0) return;
106
+ const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
107
+ try {
108
+ const pkg = JSON.parse(await (0, import_promises.readFile)(pkgPath, "utf8"));
109
+ for (const field of ["dependencies", "devDependencies"]) {
110
+ const deps = pkg[field];
111
+ if (!deps) continue;
112
+ for (const sdk of drop) delete deps[sdk];
113
+ }
114
+ await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
115
+ } catch {
116
+ }
117
+ }
92
118
  async function rewriteRootPackageJson(targetDir, opts) {
93
119
  const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
94
120
  const raw = await (0, import_promises.readFile)(pkgPath, "utf8");
@@ -106,6 +132,11 @@ async function rewriteRootPackageJson(targetDir, opts) {
106
132
  const deps = pkg["dependencies"] ??= {};
107
133
  Object.assign(deps, MONGODB_DEPS);
108
134
  }
135
+ if (opts.authProvider === "mongodb") {
136
+ const devDeps = pkg["devDependencies"] ??= {};
137
+ devDeps["@types/bcrypt"] = "^6.0.0";
138
+ devDeps["@types/jsonwebtoken"] = "^9.0.10";
139
+ }
109
140
  if (opts.packageManager !== "yarn") {
110
141
  delete pkg.packageManager;
111
142
  } else {
@@ -228,326 +259,75 @@ async function stripTsconfigPath(targetDir, alias) {
228
259
  } catch {
229
260
  }
230
261
  }
231
- async function removeJobsStack(targetDir) {
232
- const paths = [
233
- "apps/microservices/jobs",
234
- "libs/jobs-client",
235
- "apps/api/src/app/admin",
236
- "Dockerfile.ms-jobs"
237
- ];
238
- for (const p2 of paths) {
239
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
240
- }
241
- const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
242
- try {
243
- const appModule = await (0, import_promises2.readFile)(appModulePath, "utf8");
244
- const next = appModule.replace(/^import \{ AdminModule \} from '\.\/admin\/admin\.module';\n/m, "").replace(/,\s*AdminModule/g, "");
245
- await (0, import_promises2.writeFile)(appModulePath, next);
246
- } catch {
247
- }
248
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
249
- "@icore/jobs-client",
250
- "@bull-board/api",
251
- "@bull-board/express"
262
+ async function removeFirebaseAdminLib(targetDir) {
263
+ await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
264
+ await stripTsconfigPath(targetDir, "@icore/firebase-admin");
265
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/auth/package.json"), [
266
+ "@icore/firebase-admin"
252
267
  ]);
253
- const composePath = (0, import_node_path2.join)(targetDir, "docker-compose.yml");
254
- try {
255
- const compose = await (0, import_promises2.readFile)(composePath, "utf8");
256
- const next = compose.replace(/\n {2}jobs:[\s\S]+?(?=\n {2}\w+:|\nnetworks:)/m, "\n").replace(/\n {6}jobs:\n {8}condition: service_started/g, "").replace(/\n {6}JOBS_REDIS_URL:[^\n]*/g, "");
257
- await (0, import_promises2.writeFile)(composePath, next);
258
- } catch {
259
- }
260
- }
261
- async function removePaymentStack(targetDir) {
262
- const paths = [
263
- "apps/microservices/payment",
264
- "apps/microservices/payment-e2e",
265
- "libs/payment-client",
266
- "apps/api/src/app/payment"
267
- ];
268
- for (const p2 of paths) {
269
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
270
- }
271
- const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
272
- try {
273
- const appModule = await (0, import_promises2.readFile)(appModulePath, "utf8");
274
- const next = appModule.replace(/^import \{ PaymentModule \} from '\.\/payment\/payment\.module';\n/m, "").replace(/,\s*PaymentModule/g, "");
275
- await (0, import_promises2.writeFile)(appModulePath, next);
276
- } catch {
277
- }
278
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
279
- "@icore/payment-client",
280
- "@idevconn/payment"
268
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
269
+ "@icore/firebase-admin"
270
+ ]);
271
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/notes/package.json"), [
272
+ "@icore/firebase-admin"
281
273
  ]);
282
- await stripGatewayTransport(targetDir, "PAYMENT");
283
- const mainTsPath = (0, import_node_path2.join)(targetDir, "apps/api/src/main.ts");
284
- try {
285
- const src = await (0, import_promises2.readFile)(mainTsPath, "utf8");
286
- const next = src.replace(/\n\s*\{ name: 'payment', prefix: 'PAYMENT' \},/, "");
287
- await (0, import_promises2.writeFile)(mainTsPath, next);
288
- } catch {
289
- }
290
274
  }
291
- async function removeNotesStack(targetDir) {
292
- for (const p2 of [
293
- "apps/microservices/notes",
294
- "apps/microservices/notes-e2e",
295
- "libs/notes-client",
296
- "apps/api/src/app/notes",
297
- "apps/client/src/components/notes"
298
- ]) {
275
+ async function removeAuthStack(targetDir) {
276
+ const rmPaths = [
277
+ "apps/microservices/auth",
278
+ "libs/auth-strategies",
279
+ "libs/auth-client",
280
+ "Dockerfile.ms-auth",
281
+ "apps/api/src/app/auth",
282
+ "apps/api/src/app/profile",
283
+ "apps/api/src/app/abilities",
284
+ "apps/client/src/components/auth",
285
+ "apps/client/src/routes/login.tsx",
286
+ "apps/client/src/routes/auth.callback.tsx",
287
+ "apps/client/src/routes/auth.oauth.callback.tsx",
288
+ "apps/client/src/routes/_dashboard/profile.tsx"
289
+ ];
290
+ for (const p2 of rmPaths) {
299
291
  await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
300
292
  }
301
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "apps/client/src/routes/_dashboard/notes.tsx"), { force: true });
302
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "apps/client/src/queries/notes.ts"), { force: true });
303
293
  const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
304
294
  try {
305
295
  const src = await (0, import_promises2.readFile)(appModulePath, "utf8");
306
- const next = src.replace(/^import \{ NotesModule \} from '\.\/notes\/notes\.module';\n/m, "").replace(/,\s*NotesModule/g, "");
296
+ const next = src.replace(/^import \{ AuthModule \} from '\.\/auth\/auth\.module';\n/m, "").replace(/^import \{ ProfileModule \} from '\.\/profile\/profile\.module';\n/m, "").replace(/^import \{ AbilitiesModule \} from '\.\/abilities\/abilities\.module';\n/m, "").replace(/\bAuthModule,\s*/g, "").replace(/,\s*AuthModule\b/g, "").replace(/\bProfileModule,\s*/g, "").replace(/,\s*ProfileModule\b/g, "").replace(/\bAbilitiesModule,\s*/g, "").replace(/,\s*AbilitiesModule\b/g, "");
307
297
  await (0, import_promises2.writeFile)(appModulePath, next);
308
298
  } catch {
309
299
  }
310
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
311
- "@icore/notes-client",
312
- "@casl/ability"
313
- ]);
314
- await stripGatewayTransport(targetDir, "NOTES");
315
- const mainTsPath = (0, import_node_path2.join)(targetDir, "apps/api/src/main.ts");
300
+ const dashboardPath = (0, import_node_path2.join)(targetDir, "apps/client/src/routes/_dashboard.tsx");
316
301
  try {
317
- const src = await (0, import_promises2.readFile)(mainTsPath, "utf8");
318
- const next = src.replace(/\n\s*\{ name: 'notes', prefix: 'NOTES' \},/, "");
319
- await (0, import_promises2.writeFile)(mainTsPath, next);
302
+ const src = await (0, import_promises2.readFile)(dashboardPath, "utf8");
303
+ const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
304
+ await (0, import_promises2.writeFile)(dashboardPath, next);
320
305
  } catch {
321
306
  }
322
- const tsconfigPath = (0, import_node_path2.join)(targetDir, "tsconfig.base.json");
323
- try {
324
- const src = await (0, import_promises2.readFile)(tsconfigPath, "utf8");
325
- const next = src.replace(/^\s*"@icore\/notes-client": \[[^\]]*\],?\n/m, "");
326
- await (0, import_promises2.writeFile)(tsconfigPath, next);
327
- } catch {
328
- }
329
- const siderPath = (0, import_node_path2.join)(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
330
- try {
331
- const src = await (0, import_promises2.readFile)(siderPath, "utf8");
332
- const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
333
- "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
334
- "const selectedKey = pathname.includes('/profile')"
335
- ).replace(
336
- /\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
337
- ""
338
- ).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
339
- /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
340
- ""
341
- ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
342
- await (0, import_promises2.writeFile)(siderPath, next);
343
- } catch {
307
+ for (const alias of [
308
+ "@icore/auth-client",
309
+ "@icore/auth-supabase",
310
+ "@icore/auth-firebase",
311
+ "@icore/auth-mongodb"
312
+ ]) {
313
+ await stripTsconfigPath(targetDir, alias);
344
314
  }
345
- const keysPath = (0, import_node_path2.join)(targetDir, "libs/template-shared/src/lib/i18n/keys.ts");
315
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
316
+ const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
346
317
  try {
347
- const src = await (0, import_promises2.readFile)(keysPath, "utf8");
348
- const next = src.replace(/^\s{4}notes: \{\n(?:\s+.*\n)*?\s{4}\},\n/m, "");
349
- await (0, import_promises2.writeFile)(keysPath, next);
318
+ const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
319
+ const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
320
+ await (0, import_promises2.writeFile)(gatewayEnv, next);
350
321
  } catch {
351
322
  }
352
- }
353
- async function removeUnusedAuthStrategies(targetDir, authProvider) {
354
- const modulePath = (0, import_node_path2.join)(targetDir, "apps/microservices/auth/src/app/app.module.ts");
355
- const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbAuth\(connection, cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
356
- if (authProvider === "supabase") {
357
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
358
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
359
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/auth/package.json"), [
360
- "@icore/auth-firebase",
361
- "@icore/firebase-admin",
362
- "@icore/auth-mongodb"
363
- ]);
364
- await stripTsconfigPath(targetDir, "@icore/auth-firebase");
365
- await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
366
- try {
367
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
368
- const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
369
- /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
370
- ""
371
- ).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeSupabaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
372
- await (0, import_promises2.writeFile)(modulePath, next);
373
- } catch {
374
- }
375
- }
376
- if (authProvider === "firebase") {
377
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
378
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
379
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/auth/package.json"), [
380
- "@icore/auth-supabase",
381
- "@icore/auth-mongodb"
382
- ]);
383
- await stripTsconfigPath(targetDir, "@icore/auth-supabase");
384
- await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
385
- try {
386
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
387
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
388
- /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
389
- ""
390
- ).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeFirebaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
391
- await (0, import_promises2.writeFile)(modulePath, next);
392
- } catch {
393
- }
394
- }
395
- if (authProvider === "mongodb") {
396
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
397
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
398
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/auth/package.json"), [
399
- "@icore/auth-supabase",
400
- "@icore/auth-firebase",
401
- "@icore/firebase-admin"
402
- ]);
403
- await stripTsconfigPath(targetDir, "@icore/auth-supabase");
404
- await stripTsconfigPath(targetDir, "@icore/auth-firebase");
405
- try {
406
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
407
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(AUTH_BRANCH, "return makeMongoDbAuth(connection, cfg);");
408
- await (0, import_promises2.writeFile)(modulePath, next);
409
- } catch {
410
- }
411
- }
412
- }
413
- async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
414
- if (uploadProvider === "none") return;
415
- const modulePath = (0, import_node_path2.join)(targetDir, "apps/microservices/upload/src/app/app.module.ts");
416
- const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbStorage\(connection\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
417
- if (uploadProvider !== "firebase") {
418
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
419
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
420
- "@icore/storage-firebase",
421
- "@icore/firebase-admin"
422
- ]);
423
- await stripTsconfigPath(targetDir, "@icore/storage-firebase");
424
- }
425
- if (uploadProvider !== "cloudinary") {
426
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/storage-strategies/cloudinary"), {
427
- recursive: true,
428
- force: true
429
- });
430
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
431
- "@icore/storage-cloudinary"
432
- ]);
433
- await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
434
- }
435
- if (uploadProvider !== "supabase") {
436
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
437
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
438
- "@icore/storage-supabase"
439
- ]);
440
- await stripTsconfigPath(targetDir, "@icore/storage-supabase");
441
- }
442
- if (uploadProvider !== "mongodb") {
443
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/storage-strategies/mongodb"), { recursive: true, force: true });
444
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
445
- "@icore/storage-mongodb"
446
- ]);
447
- await stripTsconfigPath(targetDir, "@icore/storage-mongodb");
448
- }
323
+ const composePath = (0, import_node_path2.join)(targetDir, "docker-compose.yml");
449
324
  try {
450
- let src = await (0, import_promises2.readFile)(modulePath, "utf8");
451
- if (uploadProvider !== "firebase") {
452
- src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(
453
- /^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/gm,
454
- ""
455
- ).replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/gm, "");
456
- }
457
- if (uploadProvider !== "cloudinary") {
458
- src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/gm, "").replace(
459
- /^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/gm,
460
- ""
461
- ).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/gm, "");
462
- }
463
- if (uploadProvider !== "supabase") {
464
- src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(
465
- /^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/gm,
466
- ""
467
- ).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/gm, "");
468
- }
469
- if (uploadProvider !== "mongodb") {
470
- src = src.replace(
471
- /^import \{[^}]*MongoDbStorageStrategy[^}]*\} from '@icore\/storage-mongodb';\n/gm,
472
- ""
473
- ).replace(
474
- /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
475
- ""
476
- ).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeMongoDbStorage[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "");
477
- }
478
- const chosenReturn = uploadProvider === "mongodb" ? `return makeMongoDbStorage(connection);` : `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
479
- src = src.replace(STORAGE_BRANCH, chosenReturn);
480
- if (uploadProvider !== "mongodb") {
481
- src = src.replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
482
- }
483
- await (0, import_promises2.writeFile)(modulePath, src);
325
+ const compose = await (0, import_promises2.readFile)(composePath, "utf8");
326
+ const next = compose.replace(/\n {2}auth:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}auth:\n {8}condition: service_started/g, "").replace(/\n {6}AUTH_TRANSPORT:[^\n]*/g, "").replace(/\n {6}AUTH_REDIS_URL:[^\n]*/g, "");
327
+ await (0, import_promises2.writeFile)(composePath, next);
484
328
  } catch {
485
329
  }
486
330
  }
487
- async function removeUnusedDbStrategies(targetDir, dbProvider) {
488
- const modulePath = (0, import_node_path2.join)(targetDir, "apps/microservices/notes/src/app/app.module.ts");
489
- const DB_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDb\(connection\);\n\s*return makeFirestoreDB\(cfg\);/m;
490
- if (dbProvider === "supabase") {
491
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
492
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
493
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/notes/package.json"), [
494
- "@icore/db-firestore",
495
- "@icore/firebase-admin",
496
- "@icore/db-mongodb"
497
- ]);
498
- await stripTsconfigPath(targetDir, "@icore/db-firestore");
499
- await stripTsconfigPath(targetDir, "@icore/db-mongodb");
500
- try {
501
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
502
- const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
503
- /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
504
- ""
505
- ).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(DB_BRANCH, "return makeSupabaseDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
506
- await (0, import_promises2.writeFile)(modulePath, next);
507
- } catch {
508
- }
509
- }
510
- if (dbProvider === "firebase") {
511
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
512
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
513
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/notes/package.json"), [
514
- "@icore/db-supabase",
515
- "@icore/db-mongodb"
516
- ]);
517
- await stripTsconfigPath(targetDir, "@icore/db-supabase");
518
- await stripTsconfigPath(targetDir, "@icore/db-mongodb");
519
- try {
520
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
521
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
522
- /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
523
- ""
524
- ).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeFirestoreDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
525
- await (0, import_promises2.writeFile)(modulePath, next);
526
- } catch {
527
- }
528
- }
529
- if (dbProvider === "mongodb") {
530
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
531
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
532
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/notes/package.json"), [
533
- "@icore/db-supabase",
534
- "@icore/db-firestore",
535
- "@icore/firebase-admin"
536
- ]);
537
- await stripTsconfigPath(targetDir, "@icore/db-supabase");
538
- await stripTsconfigPath(targetDir, "@icore/db-firestore");
539
- try {
540
- const src = await (0, import_promises2.readFile)(modulePath, "utf8");
541
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeMongoDb(connection);");
542
- await (0, import_promises2.writeFile)(modulePath, next);
543
- } catch {
544
- }
545
- }
546
- }
547
- async function removeFirebaseAdminLib(targetDir) {
548
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
549
- await stripTsconfigPath(targetDir, "@icore/firebase-admin");
550
- }
551
331
  async function removeUploadStack(targetDir) {
552
332
  const paths = [
553
333
  "apps/microservices/upload",
@@ -581,12 +361,443 @@ async function removeUploadStack(targetDir) {
581
361
  ]);
582
362
  }
583
363
 
584
- // src/lib/scaffold-pkg.ts
364
+ // src/manifest/wire-features.ts
365
+ var import_promises4 = require("fs/promises");
366
+ var import_node_path4 = require("path");
367
+
368
+ // src/manifest/index.ts
369
+ var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
370
+ var MANIFEST = {
371
+ auth: {
372
+ supabase: {
373
+ libDirs: ["libs/auth-strategies/supabase"],
374
+ deps: { "@supabase/supabase-js": "^2.106.2" },
375
+ tsPaths: { "@icore/auth-supabase": ["libs/auth-strategies/supabase/src/index.ts"] },
376
+ nestModule: {
377
+ importFrom: "@icore/auth-supabase",
378
+ symbol: "SupabaseAuthModule",
379
+ into: "auth"
380
+ },
381
+ appTests: [
382
+ "apps/microservices/auth/src/app/__tests__/auth.controller.supabase.integration.unit.test.ts"
383
+ ]
384
+ },
385
+ firebase: {
386
+ libDirs: ["libs/auth-strategies/firebase"],
387
+ deps: {},
388
+ tsPaths: { "@icore/auth-firebase": ["libs/auth-strategies/firebase/src/index.ts"] },
389
+ nestModule: {
390
+ importFrom: "@icore/auth-firebase",
391
+ symbol: "FirebaseAuthModule",
392
+ into: "auth"
393
+ },
394
+ appTests: [
395
+ "apps/microservices/auth/src/app/__tests__/auth.controller.firebase.integration.unit.test.ts"
396
+ ]
397
+ },
398
+ mongodb: {
399
+ libDirs: ["libs/auth-strategies/mongodb"],
400
+ deps: { mongoose: "^9.6.3" },
401
+ tsPaths: { "@icore/auth-mongodb": ["libs/auth-strategies/mongodb/src/index.ts"] },
402
+ nestModule: { importFrom: "@icore/auth-mongodb", symbol: "MongoDbAuthModule", into: "auth" }
403
+ }
404
+ },
405
+ storage: {
406
+ supabase: {
407
+ libDirs: ["libs/storage-strategies/supabase"],
408
+ deps: { "@supabase/supabase-js": "^2.106.2" },
409
+ tsPaths: { "@icore/storage-supabase": ["libs/storage-strategies/supabase/src/index.ts"] },
410
+ nestModule: {
411
+ importFrom: "@icore/storage-supabase",
412
+ symbol: "SupabaseStorageModule",
413
+ into: "upload"
414
+ }
415
+ },
416
+ firebase: {
417
+ libDirs: ["libs/storage-strategies/firebase"],
418
+ deps: {},
419
+ tsPaths: { "@icore/storage-firebase": ["libs/storage-strategies/firebase/src/index.ts"] },
420
+ nestModule: {
421
+ importFrom: "@icore/storage-firebase",
422
+ symbol: "FirebaseStorageModule",
423
+ into: "upload"
424
+ }
425
+ },
426
+ cloudinary: {
427
+ libDirs: ["libs/storage-strategies/cloudinary"],
428
+ deps: { cloudinary: "^2.10.0" },
429
+ tsPaths: { "@icore/storage-cloudinary": ["libs/storage-strategies/cloudinary/src/index.ts"] },
430
+ nestModule: {
431
+ importFrom: "@icore/storage-cloudinary",
432
+ symbol: "CloudinaryStorageModule",
433
+ into: "upload"
434
+ }
435
+ },
436
+ mongodb: {
437
+ libDirs: ["libs/storage-strategies/mongodb"],
438
+ deps: { mongoose: "^9.6.3" },
439
+ tsPaths: { "@icore/storage-mongodb": ["libs/storage-strategies/mongodb/src/index.ts"] },
440
+ nestModule: {
441
+ importFrom: "@icore/storage-mongodb",
442
+ symbol: "MongoDbStorageModule",
443
+ into: "upload"
444
+ }
445
+ }
446
+ },
447
+ db: {
448
+ supabase: {
449
+ libDirs: ["libs/db-strategies/supabase"],
450
+ deps: { "@supabase/supabase-js": "^2.106.2" },
451
+ tsPaths: { "@icore/db-supabase": ["libs/db-strategies/supabase/src/index.ts"] },
452
+ nestModule: { importFrom: "@icore/db-supabase", symbol: "SupabaseDbModule", into: "notes" }
453
+ },
454
+ firebase: {
455
+ libDirs: ["libs/db-strategies/firestore"],
456
+ deps: {},
457
+ tsPaths: { "@icore/db-firestore": ["libs/db-strategies/firestore/src/index.ts"] },
458
+ nestModule: { importFrom: "@icore/db-firestore", symbol: "FirestoreDbModule", into: "notes" }
459
+ },
460
+ mongodb: {
461
+ libDirs: ["libs/db-strategies/mongodb"],
462
+ deps: { mongoose: "^9.6.3" },
463
+ tsPaths: { "@icore/db-mongodb": ["libs/db-strategies/mongodb/src/index.ts"] },
464
+ nestModule: { importFrom: "@icore/db-mongodb", symbol: "MongoDbDbModule", into: "notes" }
465
+ }
466
+ },
467
+ feature: {
468
+ notes: {
469
+ libDirs: [
470
+ "apps/microservices/notes",
471
+ "apps/microservices/notes-e2e",
472
+ "libs/notes-client",
473
+ "libs/db-strategies",
474
+ "apps/api/src/app/notes",
475
+ "apps/client/src/components/notes",
476
+ "apps/client/src/routes/_dashboard/notes.tsx",
477
+ "apps/client/src/queries/notes.ts"
478
+ ],
479
+ deps: { "@icore/notes-client": "*", "@casl/ability": "^7.0.0" },
480
+ tsPaths: { "@icore/notes-client": ["libs/notes-client/src/index.ts"] },
481
+ gatewayModule: { importFrom: "./notes/notes.module", symbol: "NotesModule" },
482
+ gatewayService: { name: "notes", prefix: "NOTES" },
483
+ clientNav: { route: "/notes", labelKey: "nav.notes", iconName: "notes" }
484
+ },
485
+ payment: {
486
+ libDirs: [
487
+ "apps/microservices/payment",
488
+ "apps/microservices/payment-e2e",
489
+ "libs/payment-client",
490
+ "apps/api/src/app/payment"
491
+ ],
492
+ deps: { "@icore/payment-client": "*", "@idevconn/payment": "^1.2.0" },
493
+ tsPaths: { "@icore/payment-client": ["libs/payment-client/src/index.ts"] },
494
+ gatewayModule: { importFrom: "./payment/payment.module", symbol: "PaymentModule" },
495
+ gatewayService: { name: "payment", prefix: "PAYMENT" }
496
+ },
497
+ jobs: {
498
+ libDirs: [
499
+ "apps/microservices/jobs",
500
+ "libs/jobs-client",
501
+ "apps/api/src/app/admin",
502
+ "Dockerfile.ms-jobs"
503
+ ],
504
+ deps: {
505
+ "@icore/jobs-client": "*",
506
+ "@bull-board/api": "^7.1.5",
507
+ "@bull-board/express": "^7.1.5"
508
+ },
509
+ tsPaths: { "@icore/jobs-client": ["libs/jobs-client/src/index.ts"] },
510
+ gatewayModule: { importFrom: "./admin/admin.module", symbol: "AdminModule" },
511
+ dockerService: "jobs"
512
+ }
513
+ },
514
+ ui: { shadcn: EMPTY, antd: EMPTY, mui: EMPTY },
515
+ transport: { tcp: EMPTY, redis: EMPTY, nats: EMPTY, mqtt: EMPTY, rmq: EMPTY, kafka: EMPTY },
516
+ shared: {
517
+ firebaseAdmin: {
518
+ libDirs: ["libs/firebase-admin"],
519
+ deps: { "firebase-admin": "^13.10.0" },
520
+ tsPaths: { "@icore/firebase-admin": ["libs/firebase-admin/src/index.ts"] }
521
+ }
522
+ }
523
+ };
524
+
525
+ // src/manifest/wire-provider.ts
585
526
  var import_promises3 = require("fs/promises");
586
527
  var import_node_path3 = require("path");
528
+ async function writeProvider(targetDir, axis, provider) {
529
+ const nestModule = axis.section[provider]?.nestModule;
530
+ if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
531
+ const { importFrom, symbol } = nestModule;
532
+ const content = `import { ${symbol} } from '${importFrom}';
533
+
534
+ const ENV_PATH = '${axis.envPath}';
535
+
536
+ export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
537
+ `;
538
+ await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, axis.providerFile), content);
539
+ }
540
+ async function stripJsonKeys(path, drop) {
541
+ try {
542
+ const pkg = JSON.parse(await (0, import_promises3.readFile)(path, "utf8"));
543
+ for (const field of ["dependencies", "devDependencies"]) {
544
+ const deps = pkg[field];
545
+ if (!deps) continue;
546
+ for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
547
+ }
548
+ await (0, import_promises3.writeFile)(path, JSON.stringify(pkg, null, 2) + "\n");
549
+ } catch {
550
+ }
551
+ }
552
+ async function stripTsconfigKeys(targetDir, aliases) {
553
+ const path = (0, import_node_path3.join)(targetDir, "tsconfig.base.json");
554
+ try {
555
+ const parsed = JSON.parse(await (0, import_promises3.readFile)(path, "utf8"));
556
+ const paths = parsed.compilerOptions?.paths;
557
+ if (paths) for (const a of aliases) delete paths[a];
558
+ await (0, import_promises3.writeFile)(path, JSON.stringify(parsed, null, 2) + "\n");
559
+ } catch {
560
+ }
561
+ }
562
+ async function cleanupUnusedAxis(targetDir, axis, chosen) {
563
+ for (const provider of Object.keys(axis.section)) {
564
+ if (provider === chosen) continue;
565
+ const unit = axis.section[provider];
566
+ for (const dir of unit.libDirs)
567
+ await (0, import_promises3.rm)((0, import_node_path3.join)(targetDir, dir), { recursive: true, force: true });
568
+ for (const t of unit.appTests ?? []) await (0, import_promises3.rm)((0, import_node_path3.join)(targetDir, t), { force: true });
569
+ const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
570
+ await stripJsonKeys((0, import_node_path3.join)(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
571
+ await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
572
+ }
573
+ }
574
+
575
+ // src/manifest/wire-features.ts
576
+ var FEATURES_MODULE = "apps/api/src/app/features.module.ts";
577
+ var GATEWAY_SERVICES = "apps/api/src/app/gateway-services.ts";
578
+ var API_PKG = "apps/api/package.json";
579
+ var FEATURES = MANIFEST.feature;
580
+ function selectedFeatures(opts) {
581
+ const out = [];
582
+ if (opts.example === "notes") out.push("notes");
583
+ if (opts.payment !== "none") out.push("payment");
584
+ if (opts.jobs !== "none") out.push("jobs");
585
+ return out;
586
+ }
587
+ async function writeFeaturesWiring(targetDir, opts) {
588
+ const chosen = selectedFeatures(opts);
589
+ const mods = chosen.map((k) => FEATURES[k].gatewayModule).filter((m) => !!m);
590
+ const imports = mods.map((m) => `import { ${m.symbol} } from '${m.importFrom}';`).join("\n");
591
+ const symbols = mods.map((m) => m.symbol).join(", ");
592
+ const featuresModule = `import { Module } from '@nestjs/common';
593
+ ` + (imports ? imports + "\n" : "") + `
594
+ @Module({
595
+ imports: [${symbols}],
596
+ })
597
+ export class FeaturesModule {}
598
+ `;
599
+ await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, FEATURES_MODULE), featuresModule);
600
+ const services = [];
601
+ if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
602
+ if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
603
+ for (const k of chosen) {
604
+ const svc = FEATURES[k].gatewayService;
605
+ if (svc) services.push(svc);
606
+ }
607
+ const entries = services.map((s) => ` { name: '${s.name}', prefix: '${s.prefix}' },`).join("\n");
608
+ const gatewayServices = `/** Microservices the gateway proxies. Generated by create-icore. */
609
+ export const GATEWAY_SERVICES = [
610
+ ${entries}
611
+ ];
612
+ `;
613
+ await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, GATEWAY_SERVICES), gatewayServices);
614
+ }
615
+ async function stripJobsDockerCompose(targetDir) {
616
+ const composePath = (0, import_node_path4.join)(targetDir, "docker-compose.yml");
617
+ try {
618
+ const compose = await (0, import_promises4.readFile)(composePath, "utf8");
619
+ const next = compose.replace(/\n {2}jobs:[\s\S]+?(?=\n {2}\w+:|\nnetworks:)/m, "\n").replace(/\n {6}jobs:\n {8}condition: service_started/g, "").replace(/\n {6}JOBS_REDIS_URL:[^\n]*/g, "");
620
+ await (0, import_promises4.writeFile)(composePath, next);
621
+ } catch {
622
+ }
623
+ }
624
+ async function cleanupUnusedFeatures(targetDir, opts) {
625
+ const chosen = new Set(selectedFeatures(opts));
626
+ for (const key of ["notes", "payment", "jobs"]) {
627
+ if (chosen.has(key)) continue;
628
+ const unit = FEATURES[key];
629
+ for (const dir of unit.libDirs)
630
+ await (0, import_promises4.rm)((0, import_node_path4.join)(targetDir, dir), { recursive: true, force: true });
631
+ const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
632
+ await stripJsonKeys((0, import_node_path4.join)(targetDir, API_PKG), (k) => dropKeys.has(k));
633
+ await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
634
+ if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
635
+ if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
636
+ }
637
+ }
638
+
639
+ // src/manifest/wire-client.ts
640
+ var import_promises5 = require("fs/promises");
641
+ var import_node_path5 = require("path");
642
+ var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
643
+ var BASE_NAV = [
644
+ { to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
645
+ ];
646
+ var PROFILE_NAV = { to: "/profile", labelKey: "nav.profile", iconName: "profile" };
647
+ function renderEntry(n) {
648
+ const parts = [`to: '${n.to}'`, `labelKey: '${n.labelKey}'`, `iconName: '${n.iconName}'`];
649
+ if (n.exact) parts.push("exact: true");
650
+ return ` { ${parts.join(", ")} },`;
651
+ }
652
+ async function writeNavConfig(targetDir, opts) {
653
+ const entries = [...BASE_NAV];
654
+ const notesNav = MANIFEST.feature.notes.clientNav;
655
+ if (opts.example === "notes" && notesNav) {
656
+ entries.push({
657
+ to: notesNav.route,
658
+ labelKey: notesNav.labelKey,
659
+ iconName: notesNav.iconName,
660
+ exact: notesNav.exact
661
+ });
662
+ }
663
+ entries.push(PROFILE_NAV);
664
+ const content = `/**
665
+ * Sidebar navigation. UI-agnostic: each LayoutSider maps \`iconName\` to its own
666
+ * icon library. Generated by create-icore.
667
+ */
668
+ export interface NavItem {
669
+ to: string;
670
+ labelKey: string;
671
+ iconName: 'dashboard' | 'notes' | 'profile';
672
+ exact?: boolean;
673
+ }
674
+
675
+ export const NAV_CONFIG: NavItem[] = [
676
+ ` + entries.map(renderEntry).join("\n") + `
677
+ ];
678
+ `;
679
+ await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, NAV_CONFIG_FILE), content);
680
+ }
681
+
682
+ // src/manifest/blueprint.ts
683
+ var import_promises6 = require("fs/promises");
684
+ var import_node_path6 = require("path");
685
+ async function writeBlueprintJson(targetDir, opts) {
686
+ const blueprint = {
687
+ schemaVersion: 1,
688
+ projectName: opts.projectName,
689
+ authProvider: opts.authProvider,
690
+ dbProvider: opts.dbProvider,
691
+ upload: opts.upload,
692
+ payment: opts.payment,
693
+ jobs: opts.jobs,
694
+ example: opts.example,
695
+ ui: opts.ui,
696
+ transport: opts.transport,
697
+ packageManager: opts.packageManager
698
+ };
699
+ await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
700
+ }
701
+ async function writeJson(targetDir, rel, data) {
702
+ await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
703
+ }
704
+ async function writeServiceBlueprints(targetDir, opts) {
705
+ const t = opts.transport;
706
+ if (opts.authProvider !== "none") {
707
+ await writeJson(targetDir, "apps/microservices/auth", {
708
+ schemaVersion: 1,
709
+ service: "auth",
710
+ authProvider: opts.authProvider,
711
+ transport: t
712
+ });
713
+ }
714
+ if (opts.upload !== "none") {
715
+ await writeJson(targetDir, "apps/microservices/upload", {
716
+ schemaVersion: 1,
717
+ service: "upload",
718
+ storageProvider: opts.upload,
719
+ transport: t
720
+ });
721
+ }
722
+ if (opts.example !== "none") {
723
+ await writeJson(targetDir, "apps/microservices/notes", {
724
+ schemaVersion: 1,
725
+ service: "notes",
726
+ dbProvider: opts.dbProvider,
727
+ transport: t
728
+ });
729
+ }
730
+ if (opts.payment !== "none") {
731
+ await writeJson(targetDir, "apps/microservices/payment", {
732
+ schemaVersion: 1,
733
+ service: "payment",
734
+ paymentProvider: opts.payment,
735
+ transport: t
736
+ });
737
+ }
738
+ if (opts.jobs !== "none") {
739
+ await writeJson(targetDir, "apps/microservices/jobs", {
740
+ schemaVersion: 1,
741
+ service: "jobs",
742
+ jobsProvider: opts.jobs
743
+ });
744
+ }
745
+ const features = [];
746
+ if (opts.example !== "none") features.push("notes");
747
+ if (opts.payment !== "none") features.push("payment");
748
+ if (opts.jobs !== "none") features.push("jobs");
749
+ await writeJson(targetDir, "apps/api", {
750
+ schemaVersion: 1,
751
+ service: "api",
752
+ features,
753
+ transport: t
754
+ });
755
+ await writeJson(targetDir, "apps/client", {
756
+ schemaVersion: 1,
757
+ service: "client",
758
+ ui: opts.ui
759
+ });
760
+ }
761
+
762
+ // src/manifest/wire-auth.ts
763
+ var AUTH = {
764
+ section: MANIFEST.auth,
765
+ providerFile: "apps/microservices/auth/src/app/auth.provider.ts",
766
+ exportConst: "AuthProviderModule",
767
+ msPackageJson: "apps/microservices/auth/package.json",
768
+ envPath: "apps/microservices/auth/.env"
769
+ };
770
+ var writeAuthProvider = (targetDir, provider) => writeProvider(targetDir, AUTH, provider);
771
+ var cleanupUnusedAuth = (targetDir, chosen) => cleanupUnusedAxis(targetDir, AUTH, chosen);
772
+
773
+ // src/manifest/wire-storage.ts
774
+ var STORAGE = {
775
+ section: MANIFEST.storage,
776
+ providerFile: "apps/microservices/upload/src/app/storage.provider.ts",
777
+ exportConst: "StorageProviderModule",
778
+ msPackageJson: "apps/microservices/upload/package.json",
779
+ envPath: "apps/microservices/upload/.env"
780
+ };
781
+ var writeStorageProvider = (targetDir, provider) => writeProvider(targetDir, STORAGE, provider);
782
+ var cleanupUnusedStorage = (targetDir, chosen) => cleanupUnusedAxis(targetDir, STORAGE, chosen);
783
+
784
+ // src/manifest/wire-db.ts
785
+ var DB = {
786
+ section: MANIFEST.db,
787
+ providerFile: "apps/microservices/notes/src/app/db.provider.ts",
788
+ exportConst: "DbProviderModule",
789
+ msPackageJson: "apps/microservices/notes/package.json",
790
+ envPath: "apps/microservices/notes/.env"
791
+ };
792
+ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, provider);
793
+ var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
794
+
795
+ // src/lib/scaffold-pkg.ts
796
+ var import_promises7 = require("fs/promises");
797
+ var import_node_path7 = require("path");
587
798
  async function writePnpmWorkspace(targetDir) {
588
- const pkgPath = (0, import_node_path3.join)(targetDir, "package.json");
589
- const pkg = JSON.parse(await (0, import_promises3.readFile)(pkgPath, "utf8"));
799
+ const pkgPath = (0, import_node_path7.join)(targetDir, "package.json");
800
+ const pkg = JSON.parse(await (0, import_promises7.readFile)(pkgPath, "utf8"));
590
801
  const workspaces = pkg.workspaces ?? [];
591
802
  const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
592
803
  const allowBuilds = [
@@ -595,7 +806,9 @@ async function writePnpmWorkspace(targetDir) {
595
806
  "@parcel/watcher",
596
807
  "@scarf/scarf",
597
808
  "@swc/core",
809
+ "bcrypt",
598
810
  "less",
811
+ "mongodb-memory-server",
599
812
  "msgpackr-extract",
600
813
  "nx",
601
814
  "protobufjs",
@@ -607,40 +820,40 @@ ${packagesBlock}
607
820
  allowBuilds:
608
821
  ${allowBuilds}
609
822
  `;
610
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "pnpm-workspace.yaml"), content);
823
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "pnpm-workspace.yaml"), content);
611
824
  }
612
825
  async function rewritePnpmWorkspaceDeps(targetDir) {
613
- async function walk(dir) {
826
+ async function walk2(dir) {
614
827
  const found = [];
615
828
  let entries;
616
829
  try {
617
- entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
830
+ entries = await (0, import_promises7.readdir)(dir, { withFileTypes: true });
618
831
  } catch {
619
832
  return found;
620
833
  }
621
834
  for (const e of entries) {
622
835
  if (e.isDirectory() && e.name !== "node_modules") {
623
- found.push(...await walk((0, import_node_path3.join)(dir, e.name)));
836
+ found.push(...await walk2((0, import_node_path7.join)(dir, e.name)));
624
837
  } else if (e.isFile() && e.name === "package.json") {
625
- found.push((0, import_node_path3.join)(dir, e.name));
838
+ found.push((0, import_node_path7.join)(dir, e.name));
626
839
  }
627
840
  }
628
841
  return found;
629
842
  }
630
- const pkgFiles = await walk(targetDir);
843
+ const pkgFiles = await walk2(targetDir);
631
844
  for (const f of pkgFiles) {
632
845
  try {
633
- const raw = await (0, import_promises3.readFile)(f, "utf8");
846
+ const raw = await (0, import_promises7.readFile)(f, "utf8");
634
847
  const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
635
- if (next !== raw) await (0, import_promises3.writeFile)(f, next);
848
+ if (next !== raw) await (0, import_promises7.writeFile)(f, next);
636
849
  } catch {
637
850
  }
638
851
  }
639
852
  }
640
853
  async function patchGitignoreForPm(targetDir, pm) {
641
- const giPath = (0, import_node_path3.join)(targetDir, ".gitignore");
854
+ const giPath = (0, import_node_path7.join)(targetDir, ".gitignore");
642
855
  try {
643
- let src = await (0, import_promises3.readFile)(giPath, "utf8");
856
+ let src = await (0, import_promises7.readFile)(giPath, "utf8");
644
857
  src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
645
858
  if (pm !== "yarn") {
646
859
  src = src.replace(/^\.yarn\/\*\s*\n/m, "").replace(/^!\.yarn\/patches\s*\n/m, "").replace(/^!\.yarn\/plugins\s*\n/m, "").replace(/^!\.yarn\/releases\s*\n/m, "").replace(/^!\.yarn\/sdks\s*\n/m, "").replace(/^!\.yarn\/versions\s*\n/m, "").replace(/^\.pnp\.\*\s*\n/m, "");
@@ -655,7 +868,7 @@ async function patchGitignoreForPm(targetDir, pm) {
655
868
  src += "\n# npm\nnpm-debug.log*\n";
656
869
  }
657
870
  }
658
- await (0, import_promises3.writeFile)(giPath, src);
871
+ await (0, import_promises7.writeFile)(giPath, src);
659
872
  } catch {
660
873
  }
661
874
  }
@@ -670,7 +883,7 @@ async function writeAiFiles(targetDir, opts) {
670
883
  if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
671
884
  const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
672
885
  const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
673
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
886
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
674
887
  const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
675
888
  const readme = `# ${opts.projectName}
676
889
 
@@ -720,7 +933,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
720
933
 
721
934
  Apache-2.0
722
935
  `;
723
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "README.md"), readme);
936
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "README.md"), readme);
724
937
  const agents = `# ${opts.projectName} \u2014 Agent Instructions
725
938
 
726
939
  ## Stack snapshot
@@ -801,8 +1014,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
801
1014
  - Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
802
1015
  - Run: \`${nx} test <project>\`
803
1016
  `;
804
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "AGENTS.md"), agents);
805
- await (0, import_promises3.mkdir)((0, import_node_path3.join)(targetDir, ".claude"), { recursive: true });
1017
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "AGENTS.md"), agents);
1018
+ await (0, import_promises7.mkdir)((0, import_node_path7.join)(targetDir, ".claude"), { recursive: true });
806
1019
  const mcpServers = {
807
1020
  nx: {
808
1021
  command: "npx",
@@ -843,8 +1056,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
843
1056
  ]
844
1057
  }
845
1058
  };
846
- await (0, import_promises3.writeFile)(
847
- (0, import_node_path3.join)(targetDir, ".claude", "settings.json"),
1059
+ await (0, import_promises7.writeFile)(
1060
+ (0, import_node_path7.join)(targetDir, ".claude", "settings.json"),
848
1061
  JSON.stringify(settings, null, 2) + "\n"
849
1062
  );
850
1063
  }
@@ -864,30 +1077,30 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
864
1077
  ".vscode"
865
1078
  ]);
866
1079
  async function copyTree(src, dest) {
867
- await (0, import_promises4.mkdir)(dest, { recursive: true });
868
- const entries = await (0, import_promises4.readdir)(src, { withFileTypes: true });
1080
+ await (0, import_promises8.mkdir)(dest, { recursive: true });
1081
+ const entries = await (0, import_promises8.readdir)(src, { withFileTypes: true });
869
1082
  for (const entry of entries) {
870
1083
  if (IGNORE_TOP.has(entry.name)) continue;
871
- const s = (0, import_node_path4.join)(src, entry.name);
872
- const d = (0, import_node_path4.join)(dest, entry.name);
1084
+ const s = (0, import_node_path8.join)(src, entry.name);
1085
+ const d = (0, import_node_path8.join)(dest, entry.name);
873
1086
  if (entry.isDirectory()) await copyTree(s, d);
874
- else if (entry.isFile()) await (0, import_promises4.copyFile)(s, d);
1087
+ else if (entry.isFile()) await (0, import_promises8.copyFile)(s, d);
875
1088
  }
876
1089
  }
877
1090
  async function selectClientTemplate(targetDir, opts) {
878
- const templatesRoot = (0, import_node_path4.join)(targetDir, "apps/templates");
879
- const chosen = (0, import_node_path4.join)(templatesRoot, `client-${opts.ui}`);
880
- const destClient = (0, import_node_path4.join)(targetDir, "apps/client");
1091
+ const templatesRoot = (0, import_node_path8.join)(targetDir, "apps/templates");
1092
+ const chosen = (0, import_node_path8.join)(templatesRoot, `client-${opts.ui}`);
1093
+ const destClient = (0, import_node_path8.join)(targetDir, "apps/client");
881
1094
  let chosenUi = opts.ui;
882
1095
  try {
883
- const s = await (0, import_promises4.stat)(chosen);
1096
+ const s = await (0, import_promises8.stat)(chosen);
884
1097
  if (!s.isDirectory()) throw new Error("not a dir");
885
1098
  await copyTree(chosen, destClient);
886
1099
  } catch {
887
1100
  chosenUi = "shadcn";
888
- await copyTree((0, import_node_path4.join)(templatesRoot, "client-shadcn"), destClient);
1101
+ await copyTree((0, import_node_path8.join)(templatesRoot, "client-shadcn"), destClient);
889
1102
  }
890
- await (0, import_promises4.rm)(templatesRoot, { recursive: true, force: true });
1103
+ await (0, import_promises8.rm)(templatesRoot, { recursive: true, force: true });
891
1104
  await rewriteClientPaths(destClient, chosenUi);
892
1105
  }
893
1106
  async function rewriteClientPaths(clientDir, ui) {
@@ -900,11 +1113,11 @@ async function rewriteClientPaths(clientDir, ui) {
900
1113
  "eslint.config.mjs"
901
1114
  ];
902
1115
  for (const rel of candidates) {
903
- const path = (0, import_node_path4.join)(clientDir, rel);
1116
+ const path = (0, import_node_path8.join)(clientDir, rel);
904
1117
  try {
905
- const raw = await (0, import_promises4.readFile)(path, "utf8");
1118
+ const raw = await (0, import_promises8.readFile)(path, "utf8");
906
1119
  const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
907
- if (next !== raw) await (0, import_promises4.writeFile)(path, next);
1120
+ if (next !== raw) await (0, import_promises8.writeFile)(path, next);
908
1121
  } catch {
909
1122
  }
910
1123
  }
@@ -920,12 +1133,12 @@ function gitInit(cwd, projectName) {
920
1133
  }
921
1134
  function resolveYarnBin(cwd) {
922
1135
  try {
923
- const yarnrc = (0, import_node_fs.readFileSync)((0, import_node_path4.join)(cwd, ".yarnrc.yml"), "utf8");
1136
+ const yarnrc = (0, import_node_fs.readFileSync)((0, import_node_path8.join)(cwd, ".yarnrc.yml"), "utf8");
924
1137
  const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
925
- if (match?.[1]) return (0, import_node_path4.join)(cwd, match[1].trim());
1138
+ if (match?.[1]) return (0, import_node_path8.join)(cwd, match[1].trim());
926
1139
  } catch {
927
1140
  }
928
- return (0, import_node_path4.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
1141
+ return (0, import_node_path8.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
929
1142
  }
930
1143
  function runInstall(cwd, pm) {
931
1144
  if (pm === "yarn") {
@@ -939,7 +1152,7 @@ function runInstall(cwd, pm) {
939
1152
  async function scaffold(opts, templatesDir) {
940
1153
  await copyTree(templatesDir, opts.targetDir);
941
1154
  await rewriteRootPackageJson(opts.targetDir, opts);
942
- await writeAuthEnv(opts.targetDir, opts);
1155
+ if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
943
1156
  await writeUploadEnv(opts.targetDir, opts);
944
1157
  await writeNotesEnv(opts.targetDir, opts);
945
1158
  await writePaymentEnv(opts.targetDir, opts);
@@ -948,19 +1161,33 @@ async function scaffold(opts, templatesDir) {
948
1161
  await selectClientTemplate(opts.targetDir, opts);
949
1162
  await writeClientEnv(opts.targetDir);
950
1163
  if (opts.upload === "none") await removeUploadStack(opts.targetDir);
951
- if (opts.payment === "none") await removePaymentStack(opts.targetDir);
952
- if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
953
- if (opts.example === "none") await removeNotesStack(opts.targetDir);
954
- await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
955
- await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
956
- await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
1164
+ await cleanupUnusedFeatures(opts.targetDir, opts);
1165
+ await writeFeaturesWiring(opts.targetDir, opts);
1166
+ await writeNavConfig(opts.targetDir, opts);
1167
+ if (opts.authProvider !== "none") {
1168
+ await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
1169
+ await writeAuthProvider(opts.targetDir, opts.authProvider);
1170
+ } else {
1171
+ await removeAuthStack(opts.targetDir);
1172
+ }
1173
+ if (opts.upload !== "none") {
1174
+ await cleanupUnusedStorage(opts.targetDir, opts.upload);
1175
+ await writeStorageProvider(opts.targetDir, opts.upload);
1176
+ }
957
1177
  const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
958
1178
  if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
1179
+ await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
1180
+ if (opts.dbProvider !== "none" && opts.example !== "none") {
1181
+ await writeDbProvider(opts.targetDir, opts.dbProvider);
1182
+ }
1183
+ await pruneRootProviderDeps(opts.targetDir, opts);
1184
+ await writeBlueprintJson(opts.targetDir, opts);
1185
+ await writeServiceBlueprints(opts.targetDir, opts);
959
1186
  if (opts.packageManager === "yarn") {
960
- await (0, import_promises4.writeFile)((0, import_node_path4.join)(opts.targetDir, "yarn.lock"), "");
1187
+ await (0, import_promises8.writeFile)((0, import_node_path8.join)(opts.targetDir, "yarn.lock"), "");
961
1188
  } else {
962
- await (0, import_promises4.rm)((0, import_node_path4.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
963
- await (0, import_promises4.rm)((0, import_node_path4.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
1189
+ await (0, import_promises8.rm)((0, import_node_path8.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
1190
+ await (0, import_promises8.rm)((0, import_node_path8.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
964
1191
  }
965
1192
  if (opts.packageManager === "pnpm") {
966
1193
  await writePnpmWorkspace(opts.targetDir);
@@ -974,10 +1201,99 @@ async function scaffold(opts, templatesDir) {
974
1201
 
975
1202
  // src/lib/prompts.ts
976
1203
  var p = __toESM(require("@clack/prompts"), 1);
977
- var import_node_path5 = require("path");
978
- var import_promises5 = require("fs/promises");
979
- var import_node_path6 = require("path");
1204
+ var import_node_path9 = require("path");
1205
+ var import_promises10 = require("fs/promises");
1206
+ var import_node_path10 = require("path");
980
1207
  var import_node_url = require("url");
1208
+
1209
+ // src/lib/config.ts
1210
+ var import_promises9 = require("fs/promises");
1211
+ var ConfigFileError = class extends Error {
1212
+ constructor(message) {
1213
+ super(message);
1214
+ this.name = "ConfigFileError";
1215
+ }
1216
+ };
1217
+ var AUTH_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
1218
+ var DB_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
1219
+ var UPLOAD_PROVIDERS = [
1220
+ "supabase",
1221
+ "firebase",
1222
+ "cloudinary",
1223
+ "mongodb",
1224
+ "none"
1225
+ ];
1226
+ var PAYMENT_PROVIDERS = ["paypal", "none"];
1227
+ var JOBS_PROVIDERS = ["bullmq", "none"];
1228
+ var EXAMPLE_MODES = ["notes", "none"];
1229
+ var UI_LIBRARIES = ["shadcn", "antd", "mui"];
1230
+ var MS_TRANSPORTS = ["tcp", "redis", "nats", "mqtt", "rmq", "kafka"];
1231
+ var PACKAGE_MANAGERS = ["yarn", "npm", "pnpm"];
1232
+ function assertEnum(field, value, valid) {
1233
+ if (typeof value !== "string" || !valid.includes(value)) {
1234
+ throw new ConfigFileError(
1235
+ `config field "${field}" got "${String(value)}", expected one of: ${valid.join(", ")}`
1236
+ );
1237
+ }
1238
+ return value;
1239
+ }
1240
+ function assertBoolean(field, value) {
1241
+ if (typeof value !== "boolean") {
1242
+ throw new ConfigFileError(`config field "${field}" must be a boolean, got ${typeof value}`);
1243
+ }
1244
+ return value;
1245
+ }
1246
+ function validateConfig(raw) {
1247
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1248
+ throw new ConfigFileError("config file must be a JSON object");
1249
+ }
1250
+ const obj = raw;
1251
+ const result = {};
1252
+ if ("projectName" in obj) {
1253
+ const v = obj["projectName"];
1254
+ if (typeof v !== "string" || !/^[a-z0-9-]+$/i.test(v)) {
1255
+ throw new ConfigFileError(
1256
+ `config field "projectName" must match /^[a-z0-9-]+$/i, got "${String(v)}"`
1257
+ );
1258
+ }
1259
+ result.projectName = v;
1260
+ }
1261
+ if ("authProvider" in obj)
1262
+ result.authProvider = assertEnum("authProvider", obj["authProvider"], AUTH_PROVIDERS);
1263
+ if ("dbProvider" in obj)
1264
+ result.dbProvider = assertEnum("dbProvider", obj["dbProvider"], DB_PROVIDERS);
1265
+ if ("upload" in obj) result.upload = assertEnum("upload", obj["upload"], UPLOAD_PROVIDERS);
1266
+ if ("payment" in obj) result.payment = assertEnum("payment", obj["payment"], PAYMENT_PROVIDERS);
1267
+ if ("jobs" in obj) result.jobs = assertEnum("jobs", obj["jobs"], JOBS_PROVIDERS);
1268
+ if ("example" in obj) result.example = assertEnum("example", obj["example"], EXAMPLE_MODES);
1269
+ if ("ui" in obj) result.ui = assertEnum("ui", obj["ui"], UI_LIBRARIES);
1270
+ if ("transport" in obj)
1271
+ result.transport = assertEnum("transport", obj["transport"], MS_TRANSPORTS);
1272
+ if ("packageManager" in obj)
1273
+ result.packageManager = assertEnum("packageManager", obj["packageManager"], PACKAGE_MANAGERS);
1274
+ if ("initGit" in obj) result.initGit = assertBoolean("initGit", obj["initGit"]);
1275
+ if ("install" in obj) result.install = assertBoolean("install", obj["install"]);
1276
+ return result;
1277
+ }
1278
+ async function loadConfig(filePath) {
1279
+ let raw;
1280
+ try {
1281
+ raw = await (0, import_promises9.readFile)(filePath, "utf8");
1282
+ } catch {
1283
+ throw new ConfigFileError(`config file not found: ${filePath}`);
1284
+ }
1285
+ let parsed;
1286
+ try {
1287
+ parsed = JSON.parse(raw);
1288
+ } catch (e) {
1289
+ throw new ConfigFileError(
1290
+ `config file is not valid JSON: ${e instanceof Error ? e.message : String(e)}`
1291
+ );
1292
+ }
1293
+ return validateConfig(parsed);
1294
+ }
1295
+
1296
+ // src/lib/prompts.ts
981
1297
  function detectPackageManager() {
982
1298
  const ua = process.env["npm_config_user_agent"] ?? "";
983
1299
  if (ua.startsWith("yarn/")) return "yarn";
@@ -987,8 +1303,8 @@ function detectPackageManager() {
987
1303
  }
988
1304
  async function readSelfVersion() {
989
1305
  try {
990
- const here = (0, import_node_path6.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
991
- const pkgRaw = await (0, import_promises5.readFile)((0, import_node_path6.join)(here, "..", "package.json"), "utf8");
1306
+ const here = (0, import_node_path10.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
1307
+ const pkgRaw = await (0, import_promises10.readFile)((0, import_node_path10.join)(here, "..", "package.json"), "utf8");
992
1308
  const pkg = JSON.parse(pkgRaw);
993
1309
  return pkg.version ?? null;
994
1310
  } catch {
@@ -1062,12 +1378,21 @@ function parseFlags(argv) {
1062
1378
  case "no-install":
1063
1379
  out.install = false;
1064
1380
  break;
1381
+ case "config":
1382
+ out._configPath = v;
1383
+ break;
1065
1384
  }
1066
1385
  }
1067
1386
  return out;
1068
1387
  }
1069
1388
  async function collectOptions({ argv, cwd }) {
1070
1389
  const flags = parseFlags(argv);
1390
+ const configPath = flags._configPath;
1391
+ delete flags._configPath;
1392
+ if (configPath) {
1393
+ const configValues = await loadConfig(configPath);
1394
+ Object.assign(flags, { ...configValues, ...flags });
1395
+ }
1071
1396
  const [selfVersion, latestVersion] = await Promise.all([readSelfVersion(), fetchLatestVersion()]);
1072
1397
  const versionTag = selfVersion ? ` v${selfVersion}` : "";
1073
1398
  p.intro(`iCore${versionTag} \u2014 bootstrap a new project`);
@@ -1090,11 +1415,12 @@ Re-run with @latest to refresh:
1090
1415
  options: [
1091
1416
  { value: "supabase", label: "Supabase" },
1092
1417
  { value: "firebase", label: "Firebase" },
1093
- { value: "mongodb", label: "MongoDB (Custom Auth)" }
1418
+ { value: "mongodb", label: "MongoDB (Custom Auth)" },
1419
+ { value: "none", label: "None \u2014 no login, open API (simple SPA)" }
1094
1420
  ]
1095
1421
  });
1096
1422
  if (p.isCancel(authProvider)) throw new Error("cancelled");
1097
- const dbProvider = flags.dbProvider ?? await p.select({
1423
+ const dbProvider = authProvider === "none" ? "none" : flags.dbProvider ?? await p.select({
1098
1424
  message: "Database backend",
1099
1425
  options: [
1100
1426
  { value: "supabase", label: "Supabase Postgres" },
@@ -1133,7 +1459,7 @@ Re-run with @latest to refresh:
1133
1459
  initialValue: "none"
1134
1460
  });
1135
1461
  if (p.isCancel(jobs)) throw new Error("cancelled");
1136
- const example = flags.example ?? await p.select({
1462
+ const example = authProvider === "none" ? "none" : flags.example ?? await p.select({
1137
1463
  message: "Include notes sample feature? (CRUD demo \u2014 remove before production)",
1138
1464
  options: [
1139
1465
  { value: "notes", label: "Yes \u2014 include notes sample" },
@@ -1158,7 +1484,8 @@ Re-run with @latest to refresh:
1158
1484
  initialValue: "shadcn"
1159
1485
  });
1160
1486
  if (p.isCancel(ui)) throw new Error("cancelled");
1161
- const transport = flags.transport ?? await p.select({
1487
+ const noMicroservices = authProvider === "none" && upload === "none" && payment === "none";
1488
+ const transport = flags.transport ?? (noMicroservices ? "tcp" : await p.select({
1162
1489
  message: "Microservice transport",
1163
1490
  options: [
1164
1491
  { value: "tcp", label: "TCP (default, no broker required)" },
@@ -1169,7 +1496,7 @@ Re-run with @latest to refresh:
1169
1496
  { value: "kafka", label: "Kafka" }
1170
1497
  ],
1171
1498
  initialValue: "tcp"
1172
- });
1499
+ }));
1173
1500
  if (p.isCancel(transport)) throw new Error("cancelled");
1174
1501
  const packageManager = flags.packageManager ?? detectPackageManager();
1175
1502
  if (packageManager === "yarn") {
@@ -1185,7 +1512,7 @@ Re-run with @latest to refresh:
1185
1512
  }) === false;
1186
1513
  return {
1187
1514
  projectName,
1188
- targetDir: (0, import_node_path5.resolve)(cwd, projectName),
1515
+ targetDir: (0, import_node_path9.resolve)(cwd, projectName),
1189
1516
  authProvider,
1190
1517
  dbProvider,
1191
1518
  upload,
@@ -1199,8 +1526,129 @@ Re-run with @latest to refresh:
1199
1526
  install
1200
1527
  };
1201
1528
  }
1529
+
1530
+ // src/manifest/audit.ts
1531
+ var import_promises11 = require("fs/promises");
1532
+ var import_node_path11 = require("path");
1533
+ var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
1534
+ async function walk(dir, out = []) {
1535
+ const entries = await (0, import_promises11.readdir)(dir, { withFileTypes: true });
1536
+ for (const e of entries) {
1537
+ if (e.isDirectory()) {
1538
+ if (!IGNORE_DIRS.has(e.name)) await walk((0, import_node_path11.join)(dir, e.name), out);
1539
+ } else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
1540
+ out.push((0, import_node_path11.join)(dir, e.name));
1541
+ }
1542
+ }
1543
+ return out;
1544
+ }
1545
+ async function tsconfigAliases(dir) {
1546
+ try {
1547
+ const raw = await (0, import_promises11.readFile)((0, import_node_path11.join)(dir, "tsconfig.base.json"), "utf8");
1548
+ const aliases = /* @__PURE__ */ new Set();
1549
+ for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
1550
+ if (m[1]) aliases.add(m[1]);
1551
+ }
1552
+ return aliases;
1553
+ } catch {
1554
+ return /* @__PURE__ */ new Set();
1555
+ }
1556
+ }
1557
+ var PROVIDER_SDKS = {
1558
+ supabase: ["@supabase/supabase-js"],
1559
+ cloudinary: ["cloudinary"],
1560
+ mongodb: ["mongoose"],
1561
+ firebase: ["firebase-admin", "@icore/firebase-admin"]
1562
+ };
1563
+ async function readBlueprint(dir) {
1564
+ try {
1565
+ return JSON.parse(await (0, import_promises11.readFile)((0, import_node_path11.join)(dir, "blueprint.json"), "utf8"));
1566
+ } catch {
1567
+ return null;
1568
+ }
1569
+ }
1570
+ function forbiddenFromBlueprint(bp) {
1571
+ const chosen = new Set(
1572
+ [bp.authProvider, bp.dbProvider, bp.upload].filter((p2) => Boolean(p2))
1573
+ );
1574
+ const forbidden = [];
1575
+ for (const [provider, sdks] of Object.entries(PROVIDER_SDKS)) {
1576
+ if (!chosen.has(provider)) forbidden.push(...sdks);
1577
+ }
1578
+ return forbidden;
1579
+ }
1580
+ async function allPackageJsons(dir) {
1581
+ const out = [];
1582
+ const root = (0, import_node_path11.join)(dir, "package.json");
1583
+ out.push(root);
1584
+ async function walk2(d) {
1585
+ let entries;
1586
+ try {
1587
+ entries = await (0, import_promises11.readdir)(d, { withFileTypes: true });
1588
+ } catch {
1589
+ return;
1590
+ }
1591
+ for (const e of entries) {
1592
+ if (e.isDirectory()) {
1593
+ if (!IGNORE_DIRS.has(e.name)) await walk2((0, import_node_path11.join)(d, e.name));
1594
+ } else if (e.name === "package.json") {
1595
+ out.push((0, import_node_path11.join)(d, e.name));
1596
+ }
1597
+ }
1598
+ }
1599
+ await walk2((0, import_node_path11.join)(dir, "apps"));
1600
+ return out;
1601
+ }
1602
+ async function depKeys(pkgPath) {
1603
+ try {
1604
+ const pkg = JSON.parse(await (0, import_promises11.readFile)(pkgPath, "utf8"));
1605
+ return /* @__PURE__ */ new Set([
1606
+ ...Object.keys(pkg.dependencies ?? {}),
1607
+ ...Object.keys(pkg.devDependencies ?? {})
1608
+ ]);
1609
+ } catch {
1610
+ return /* @__PURE__ */ new Set();
1611
+ }
1612
+ }
1613
+ var ICORE_IMPORT = /(?:from|import\()\s*['"](@icore\/[a-z0-9.-]+)/g;
1614
+ async function auditProject(dir, opts = {}) {
1615
+ const violations = [];
1616
+ const aliases = await tsconfigAliases(dir);
1617
+ for (const file of await walk(dir)) {
1618
+ const src = await (0, import_promises11.readFile)(file, "utf8");
1619
+ for (const m of src.matchAll(ICORE_IMPORT)) {
1620
+ const alias = m[1];
1621
+ if (alias && !aliases.has(alias)) {
1622
+ violations.push({
1623
+ kind: "import-of-absent-lib",
1624
+ detail: `${file} imports ${alias} (no tsconfig path \u2192 lib absent)`
1625
+ });
1626
+ }
1627
+ }
1628
+ }
1629
+ const bp = await readBlueprint(dir);
1630
+ const forbidden = /* @__PURE__ */ new Set([
1631
+ ...opts.forbiddenDeps ?? [],
1632
+ ...bp ? forbiddenFromBlueprint(bp) : []
1633
+ ]);
1634
+ if (forbidden.size > 0) {
1635
+ for (const pkgPath of await allPackageJsons(dir)) {
1636
+ const deps = await depKeys(pkgPath);
1637
+ for (const f of forbidden) {
1638
+ if (deps.has(f)) {
1639
+ violations.push({
1640
+ kind: "forbidden-dep",
1641
+ detail: `${pkgPath} keeps forbidden dep ${f}`
1642
+ });
1643
+ }
1644
+ }
1645
+ }
1646
+ }
1647
+ return violations;
1648
+ }
1202
1649
  // Annotate the CommonJS export names for ESM import in node:
1203
1650
  0 && (module.exports = {
1651
+ auditProject,
1204
1652
  collectOptions,
1205
1653
  pmRun,
1206
1654
  scaffold