@idevconn/create-icore 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/cli.js +509 -353
  2. package/dist/index.cjs +644 -366
  3. package/dist/index.d.cts +9 -1
  4. package/dist/index.d.ts +9 -1
  5. package/dist/index.js +635 -358
  6. package/dist/manifest/audit.js +122 -0
  7. package/package.json +1 -1
  8. package/templates/apps/api/package.json +2 -2
  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 +4 -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");
@@ -228,42 +254,26 @@ async function stripTsconfigPath(targetDir, alias) {
228
254
  } catch {
229
255
  }
230
256
  }
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"
257
+ async function removeFirebaseAdminLib(targetDir) {
258
+ await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
259
+ await stripTsconfigPath(targetDir, "@icore/firebase-admin");
260
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/auth/package.json"), [
261
+ "@icore/firebase-admin"
262
+ ]);
263
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/upload/package.json"), [
264
+ "@icore/firebase-admin"
265
+ ]);
266
+ await stripDeps((0, import_node_path2.join)(targetDir, "apps/microservices/notes/package.json"), [
267
+ "@icore/firebase-admin"
252
268
  ]);
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
269
  }
261
- async function removePaymentStack(targetDir) {
270
+ async function removeUploadStack(targetDir) {
262
271
  const paths = [
263
- "apps/microservices/payment",
264
- "apps/microservices/payment-e2e",
265
- "libs/payment-client",
266
- "apps/api/src/app/payment"
272
+ "apps/microservices/upload",
273
+ "apps/microservices/upload-e2e",
274
+ "libs/storage-strategies",
275
+ "libs/upload-client",
276
+ "apps/api/src/app/storage"
267
277
  ];
268
278
  for (const p2 of paths) {
269
279
  await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
@@ -271,322 +281,459 @@ async function removePaymentStack(targetDir) {
271
281
  const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
272
282
  try {
273
283
  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, "");
284
+ const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
275
285
  await (0, import_promises2.writeFile)(appModulePath, next);
276
286
  } catch {
277
287
  }
278
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
279
- "@icore/payment-client",
280
- "@idevconn/payment"
281
- ]);
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
- }
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
- ]) {
299
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
300
- }
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
- const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
288
+ const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
304
289
  try {
305
- 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, "");
307
- await (0, import_promises2.writeFile)(appModulePath, next);
290
+ const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
291
+ const next = env.split("\n").filter(
292
+ (line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
293
+ ).join("\n");
294
+ await (0, import_promises2.writeFile)(gatewayEnv, next);
308
295
  } catch {
309
296
  }
310
297
  await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
311
- "@icore/notes-client",
312
- "@casl/ability"
298
+ "@icore/upload-client",
299
+ "@types/multer"
313
300
  ]);
314
- await stripGatewayTransport(targetDir, "NOTES");
315
- const mainTsPath = (0, import_node_path2.join)(targetDir, "apps/api/src/main.ts");
316
- 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);
320
- } catch {
321
- }
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 {
301
+ }
302
+
303
+ // src/manifest/wire-features.ts
304
+ var import_promises4 = require("fs/promises");
305
+ var import_node_path4 = require("path");
306
+
307
+ // src/manifest/index.ts
308
+ var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
309
+ var MANIFEST = {
310
+ auth: {
311
+ supabase: {
312
+ libDirs: ["libs/auth-strategies/supabase"],
313
+ deps: { "@supabase/supabase-js": "^2.106.2" },
314
+ tsPaths: { "@icore/auth-supabase": ["libs/auth-strategies/supabase/src/index.ts"] },
315
+ nestModule: {
316
+ importFrom: "@icore/auth-supabase",
317
+ symbol: "SupabaseAuthModule",
318
+ into: "auth"
319
+ },
320
+ appTests: [
321
+ "apps/microservices/auth/src/app/__tests__/auth.controller.supabase.integration.unit.test.ts"
322
+ ]
323
+ },
324
+ firebase: {
325
+ libDirs: ["libs/auth-strategies/firebase"],
326
+ deps: {},
327
+ tsPaths: { "@icore/auth-firebase": ["libs/auth-strategies/firebase/src/index.ts"] },
328
+ nestModule: {
329
+ importFrom: "@icore/auth-firebase",
330
+ symbol: "FirebaseAuthModule",
331
+ into: "auth"
332
+ },
333
+ appTests: [
334
+ "apps/microservices/auth/src/app/__tests__/auth.controller.firebase.integration.unit.test.ts"
335
+ ]
336
+ },
337
+ mongodb: {
338
+ libDirs: ["libs/auth-strategies/mongodb"],
339
+ deps: { mongoose: "^9.6.3" },
340
+ tsPaths: { "@icore/auth-mongodb": ["libs/auth-strategies/mongodb/src/index.ts"] },
341
+ nestModule: { importFrom: "@icore/auth-mongodb", symbol: "MongoDbAuthModule", into: "auth" }
342
+ }
343
+ },
344
+ storage: {
345
+ supabase: {
346
+ libDirs: ["libs/storage-strategies/supabase"],
347
+ deps: { "@supabase/supabase-js": "^2.106.2" },
348
+ tsPaths: { "@icore/storage-supabase": ["libs/storage-strategies/supabase/src/index.ts"] },
349
+ nestModule: {
350
+ importFrom: "@icore/storage-supabase",
351
+ symbol: "SupabaseStorageModule",
352
+ into: "upload"
353
+ }
354
+ },
355
+ firebase: {
356
+ libDirs: ["libs/storage-strategies/firebase"],
357
+ deps: {},
358
+ tsPaths: { "@icore/storage-firebase": ["libs/storage-strategies/firebase/src/index.ts"] },
359
+ nestModule: {
360
+ importFrom: "@icore/storage-firebase",
361
+ symbol: "FirebaseStorageModule",
362
+ into: "upload"
363
+ }
364
+ },
365
+ cloudinary: {
366
+ libDirs: ["libs/storage-strategies/cloudinary"],
367
+ deps: { cloudinary: "^2.10.0" },
368
+ tsPaths: { "@icore/storage-cloudinary": ["libs/storage-strategies/cloudinary/src/index.ts"] },
369
+ nestModule: {
370
+ importFrom: "@icore/storage-cloudinary",
371
+ symbol: "CloudinaryStorageModule",
372
+ into: "upload"
373
+ }
374
+ },
375
+ mongodb: {
376
+ libDirs: ["libs/storage-strategies/mongodb"],
377
+ deps: { mongoose: "^9.6.3" },
378
+ tsPaths: { "@icore/storage-mongodb": ["libs/storage-strategies/mongodb/src/index.ts"] },
379
+ nestModule: {
380
+ importFrom: "@icore/storage-mongodb",
381
+ symbol: "MongoDbStorageModule",
382
+ into: "upload"
383
+ }
384
+ }
385
+ },
386
+ db: {
387
+ supabase: {
388
+ libDirs: ["libs/db-strategies/supabase"],
389
+ deps: { "@supabase/supabase-js": "^2.106.2" },
390
+ tsPaths: { "@icore/db-supabase": ["libs/db-strategies/supabase/src/index.ts"] },
391
+ nestModule: { importFrom: "@icore/db-supabase", symbol: "SupabaseDbModule", into: "notes" }
392
+ },
393
+ firebase: {
394
+ libDirs: ["libs/db-strategies/firestore"],
395
+ deps: {},
396
+ tsPaths: { "@icore/db-firestore": ["libs/db-strategies/firestore/src/index.ts"] },
397
+ nestModule: { importFrom: "@icore/db-firestore", symbol: "FirestoreDbModule", into: "notes" }
398
+ },
399
+ mongodb: {
400
+ libDirs: ["libs/db-strategies/mongodb"],
401
+ deps: { mongoose: "^9.6.3" },
402
+ tsPaths: { "@icore/db-mongodb": ["libs/db-strategies/mongodb/src/index.ts"] },
403
+ nestModule: { importFrom: "@icore/db-mongodb", symbol: "MongoDbDbModule", into: "notes" }
404
+ }
405
+ },
406
+ feature: {
407
+ notes: {
408
+ libDirs: [
409
+ "apps/microservices/notes",
410
+ "apps/microservices/notes-e2e",
411
+ "libs/notes-client",
412
+ "libs/db-strategies",
413
+ "apps/api/src/app/notes",
414
+ "apps/client/src/components/notes",
415
+ "apps/client/src/routes/_dashboard/notes.tsx",
416
+ "apps/client/src/queries/notes.ts"
417
+ ],
418
+ deps: { "@icore/notes-client": "*", "@casl/ability": "^7.0.0" },
419
+ tsPaths: { "@icore/notes-client": ["libs/notes-client/src/index.ts"] },
420
+ gatewayModule: { importFrom: "./notes/notes.module", symbol: "NotesModule" },
421
+ gatewayService: { name: "notes", prefix: "NOTES" },
422
+ clientNav: { route: "/notes", labelKey: "nav.notes", iconName: "notes" }
423
+ },
424
+ payment: {
425
+ libDirs: [
426
+ "apps/microservices/payment",
427
+ "apps/microservices/payment-e2e",
428
+ "libs/payment-client",
429
+ "apps/api/src/app/payment"
430
+ ],
431
+ deps: { "@icore/payment-client": "*", "@idevconn/payment": "^1.2.0" },
432
+ tsPaths: { "@icore/payment-client": ["libs/payment-client/src/index.ts"] },
433
+ gatewayModule: { importFrom: "./payment/payment.module", symbol: "PaymentModule" },
434
+ gatewayService: { name: "payment", prefix: "PAYMENT" }
435
+ },
436
+ jobs: {
437
+ libDirs: [
438
+ "apps/microservices/jobs",
439
+ "libs/jobs-client",
440
+ "apps/api/src/app/admin",
441
+ "Dockerfile.ms-jobs"
442
+ ],
443
+ deps: {
444
+ "@icore/jobs-client": "*",
445
+ "@bull-board/api": "^7.1.5",
446
+ "@bull-board/express": "^7.1.5"
447
+ },
448
+ tsPaths: { "@icore/jobs-client": ["libs/jobs-client/src/index.ts"] },
449
+ gatewayModule: { importFrom: "./admin/admin.module", symbol: "AdminModule" },
450
+ dockerService: "jobs"
451
+ }
452
+ },
453
+ ui: { shadcn: EMPTY, antd: EMPTY, mui: EMPTY },
454
+ transport: { tcp: EMPTY, redis: EMPTY, nats: EMPTY, mqtt: EMPTY, rmq: EMPTY, kafka: EMPTY },
455
+ shared: {
456
+ firebaseAdmin: {
457
+ libDirs: ["libs/firebase-admin"],
458
+ deps: { "firebase-admin": "^13.10.0" },
459
+ tsPaths: { "@icore/firebase-admin": ["libs/firebase-admin/src/index.ts"] }
460
+ }
328
461
  }
329
- const siderPath = (0, import_node_path2.join)(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
462
+ };
463
+
464
+ // src/manifest/wire-provider.ts
465
+ var import_promises3 = require("fs/promises");
466
+ var import_node_path3 = require("path");
467
+ async function writeProvider(targetDir, axis, provider) {
468
+ const nestModule = axis.section[provider]?.nestModule;
469
+ if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
470
+ const { importFrom, symbol } = nestModule;
471
+ const content = `import { ${symbol} } from '${importFrom}';
472
+
473
+ const ENV_PATH = '${axis.envPath}';
474
+
475
+ export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
476
+ `;
477
+ await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, axis.providerFile), content);
478
+ }
479
+ async function stripJsonKeys(path, drop) {
330
480
  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);
481
+ const pkg = JSON.parse(await (0, import_promises3.readFile)(path, "utf8"));
482
+ for (const field of ["dependencies", "devDependencies"]) {
483
+ const deps = pkg[field];
484
+ if (!deps) continue;
485
+ for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
486
+ }
487
+ await (0, import_promises3.writeFile)(path, JSON.stringify(pkg, null, 2) + "\n");
343
488
  } catch {
344
489
  }
345
- const keysPath = (0, import_node_path2.join)(targetDir, "libs/template-shared/src/lib/i18n/keys.ts");
490
+ }
491
+ async function stripTsconfigKeys(targetDir, aliases) {
492
+ const path = (0, import_node_path3.join)(targetDir, "tsconfig.base.json");
346
493
  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);
494
+ const parsed = JSON.parse(await (0, import_promises3.readFile)(path, "utf8"));
495
+ const paths = parsed.compilerOptions?.paths;
496
+ if (paths) for (const a of aliases) delete paths[a];
497
+ await (0, import_promises3.writeFile)(path, JSON.stringify(parsed, null, 2) + "\n");
350
498
  } catch {
351
499
  }
352
500
  }
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
- }
501
+ async function cleanupUnusedAxis(targetDir, axis, chosen) {
502
+ for (const provider of Object.keys(axis.section)) {
503
+ if (provider === chosen) continue;
504
+ const unit = axis.section[provider];
505
+ for (const dir of unit.libDirs)
506
+ await (0, import_promises3.rm)((0, import_node_path3.join)(targetDir, dir), { recursive: true, force: true });
507
+ for (const t of unit.appTests ?? []) await (0, import_promises3.rm)((0, import_node_path3.join)(targetDir, t), { force: true });
508
+ const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
509
+ await stripJsonKeys((0, import_node_path3.join)(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
510
+ await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
411
511
  }
412
512
  }
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");
513
+
514
+ // src/manifest/wire-features.ts
515
+ var FEATURES_MODULE = "apps/api/src/app/features.module.ts";
516
+ var GATEWAY_SERVICES = "apps/api/src/app/gateway-services.ts";
517
+ var API_PKG = "apps/api/package.json";
518
+ var FEATURES = MANIFEST.feature;
519
+ function selectedFeatures(opts) {
520
+ const out = [];
521
+ if (opts.example === "notes") out.push("notes");
522
+ if (opts.payment !== "none") out.push("payment");
523
+ if (opts.jobs !== "none") out.push("jobs");
524
+ return out;
525
+ }
526
+ async function writeFeaturesWiring(targetDir, opts) {
527
+ const chosen = selectedFeatures(opts);
528
+ const mods = chosen.map((k) => FEATURES[k].gatewayModule).filter((m) => !!m);
529
+ const imports = mods.map((m) => `import { ${m.symbol} } from '${m.importFrom}';`).join("\n");
530
+ const symbols = mods.map((m) => m.symbol).join(", ");
531
+ const featuresModule = `import { Module } from '@nestjs/common';
532
+ ` + (imports ? imports + "\n" : "") + `
533
+ @Module({
534
+ imports: [${symbols}],
535
+ })
536
+ export class FeaturesModule {}
537
+ `;
538
+ await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, FEATURES_MODULE), featuresModule);
539
+ const services = [{ name: "auth", prefix: "AUTH" }];
540
+ if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
541
+ for (const k of chosen) {
542
+ const svc = FEATURES[k].gatewayService;
543
+ if (svc) services.push(svc);
448
544
  }
545
+ const entries = services.map((s) => ` { name: '${s.name}', prefix: '${s.prefix}' },`).join("\n");
546
+ const gatewayServices = `/** Microservices the gateway proxies. Generated by create-icore. */
547
+ export const GATEWAY_SERVICES = [
548
+ ${entries}
549
+ ];
550
+ `;
551
+ await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, GATEWAY_SERVICES), gatewayServices);
552
+ }
553
+ async function stripJobsDockerCompose(targetDir) {
554
+ const composePath = (0, import_node_path4.join)(targetDir, "docker-compose.yml");
449
555
  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);
556
+ const compose = await (0, import_promises4.readFile)(composePath, "utf8");
557
+ 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, "");
558
+ await (0, import_promises4.writeFile)(composePath, next);
484
559
  } catch {
485
560
  }
486
561
  }
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
- }
562
+ async function cleanupUnusedFeatures(targetDir, opts) {
563
+ const chosen = new Set(selectedFeatures(opts));
564
+ for (const key of ["notes", "payment", "jobs"]) {
565
+ if (chosen.has(key)) continue;
566
+ const unit = FEATURES[key];
567
+ for (const dir of unit.libDirs)
568
+ await (0, import_promises4.rm)((0, import_node_path4.join)(targetDir, dir), { recursive: true, force: true });
569
+ const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
570
+ await stripJsonKeys((0, import_node_path4.join)(targetDir, API_PKG), (k) => dropKeys.has(k));
571
+ await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
572
+ if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
573
+ if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
509
574
  }
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
- }
575
+ }
576
+
577
+ // src/manifest/wire-client.ts
578
+ var import_promises5 = require("fs/promises");
579
+ var import_node_path5 = require("path");
580
+ var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
581
+ var BASE_NAV = [
582
+ { to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
583
+ ];
584
+ var PROFILE_NAV = { to: "/profile", labelKey: "nav.profile", iconName: "profile" };
585
+ function renderEntry(n) {
586
+ const parts = [`to: '${n.to}'`, `labelKey: '${n.labelKey}'`, `iconName: '${n.iconName}'`];
587
+ if (n.exact) parts.push("exact: true");
588
+ return ` { ${parts.join(", ")} },`;
589
+ }
590
+ async function writeNavConfig(targetDir, opts) {
591
+ const entries = [...BASE_NAV];
592
+ const notesNav = MANIFEST.feature.notes.clientNav;
593
+ if (opts.example === "notes" && notesNav) {
594
+ entries.push({
595
+ to: notesNav.route,
596
+ labelKey: notesNav.labelKey,
597
+ iconName: notesNav.iconName,
598
+ exact: notesNav.exact
599
+ });
545
600
  }
601
+ entries.push(PROFILE_NAV);
602
+ const content = `/**
603
+ * Sidebar navigation. UI-agnostic: each LayoutSider maps \`iconName\` to its own
604
+ * icon library. Generated by create-icore.
605
+ */
606
+ export interface NavItem {
607
+ to: string;
608
+ labelKey: string;
609
+ iconName: 'dashboard' | 'notes' | 'profile';
610
+ exact?: boolean;
546
611
  }
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");
612
+
613
+ export const NAV_CONFIG: NavItem[] = [
614
+ ` + entries.map(renderEntry).join("\n") + `
615
+ ];
616
+ `;
617
+ await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, NAV_CONFIG_FILE), content);
550
618
  }
551
- async function removeUploadStack(targetDir) {
552
- const paths = [
553
- "apps/microservices/upload",
554
- "apps/microservices/upload-e2e",
555
- "libs/storage-strategies",
556
- "libs/upload-client",
557
- "apps/api/src/app/storage"
558
- ];
559
- for (const p2 of paths) {
560
- await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
619
+
620
+ // src/manifest/blueprint.ts
621
+ var import_promises6 = require("fs/promises");
622
+ var import_node_path6 = require("path");
623
+ async function writeBlueprintJson(targetDir, opts) {
624
+ const blueprint = {
625
+ schemaVersion: 1,
626
+ projectName: opts.projectName,
627
+ authProvider: opts.authProvider,
628
+ dbProvider: opts.dbProvider,
629
+ upload: opts.upload,
630
+ payment: opts.payment,
631
+ jobs: opts.jobs,
632
+ example: opts.example,
633
+ ui: opts.ui,
634
+ transport: opts.transport,
635
+ packageManager: opts.packageManager
636
+ };
637
+ await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
638
+ }
639
+ async function writeJson(targetDir, rel, data) {
640
+ await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
641
+ }
642
+ async function writeServiceBlueprints(targetDir, opts) {
643
+ const t = opts.transport;
644
+ await writeJson(targetDir, "apps/microservices/auth", {
645
+ schemaVersion: 1,
646
+ service: "auth",
647
+ authProvider: opts.authProvider,
648
+ transport: t
649
+ });
650
+ if (opts.upload !== "none") {
651
+ await writeJson(targetDir, "apps/microservices/upload", {
652
+ schemaVersion: 1,
653
+ service: "upload",
654
+ storageProvider: opts.upload,
655
+ transport: t
656
+ });
561
657
  }
562
- const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
563
- try {
564
- const appModule = await (0, import_promises2.readFile)(appModulePath, "utf8");
565
- const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
566
- await (0, import_promises2.writeFile)(appModulePath, next);
567
- } catch {
658
+ if (opts.example !== "none") {
659
+ await writeJson(targetDir, "apps/microservices/notes", {
660
+ schemaVersion: 1,
661
+ service: "notes",
662
+ dbProvider: opts.dbProvider,
663
+ transport: t
664
+ });
568
665
  }
569
- const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
570
- try {
571
- const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
572
- const next = env.split("\n").filter(
573
- (line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
574
- ).join("\n");
575
- await (0, import_promises2.writeFile)(gatewayEnv, next);
576
- } catch {
666
+ if (opts.payment !== "none") {
667
+ await writeJson(targetDir, "apps/microservices/payment", {
668
+ schemaVersion: 1,
669
+ service: "payment",
670
+ paymentProvider: opts.payment,
671
+ transport: t
672
+ });
577
673
  }
578
- await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
579
- "@icore/upload-client",
580
- "@types/multer"
581
- ]);
674
+ if (opts.jobs !== "none") {
675
+ await writeJson(targetDir, "apps/microservices/jobs", {
676
+ schemaVersion: 1,
677
+ service: "jobs",
678
+ jobsProvider: opts.jobs
679
+ });
680
+ }
681
+ const features = [];
682
+ if (opts.example !== "none") features.push("notes");
683
+ if (opts.payment !== "none") features.push("payment");
684
+ if (opts.jobs !== "none") features.push("jobs");
685
+ await writeJson(targetDir, "apps/api", {
686
+ schemaVersion: 1,
687
+ service: "api",
688
+ features,
689
+ transport: t
690
+ });
691
+ await writeJson(targetDir, "apps/client", {
692
+ schemaVersion: 1,
693
+ service: "client",
694
+ ui: opts.ui
695
+ });
582
696
  }
583
697
 
698
+ // src/manifest/wire-auth.ts
699
+ var AUTH = {
700
+ section: MANIFEST.auth,
701
+ providerFile: "apps/microservices/auth/src/app/auth.provider.ts",
702
+ exportConst: "AuthProviderModule",
703
+ msPackageJson: "apps/microservices/auth/package.json",
704
+ envPath: "apps/microservices/auth/.env"
705
+ };
706
+ var writeAuthProvider = (targetDir, provider) => writeProvider(targetDir, AUTH, provider);
707
+ var cleanupUnusedAuth = (targetDir, chosen) => cleanupUnusedAxis(targetDir, AUTH, chosen);
708
+
709
+ // src/manifest/wire-storage.ts
710
+ var STORAGE = {
711
+ section: MANIFEST.storage,
712
+ providerFile: "apps/microservices/upload/src/app/storage.provider.ts",
713
+ exportConst: "StorageProviderModule",
714
+ msPackageJson: "apps/microservices/upload/package.json",
715
+ envPath: "apps/microservices/upload/.env"
716
+ };
717
+ var writeStorageProvider = (targetDir, provider) => writeProvider(targetDir, STORAGE, provider);
718
+ var cleanupUnusedStorage = (targetDir, chosen) => cleanupUnusedAxis(targetDir, STORAGE, chosen);
719
+
720
+ // src/manifest/wire-db.ts
721
+ var DB = {
722
+ section: MANIFEST.db,
723
+ providerFile: "apps/microservices/notes/src/app/db.provider.ts",
724
+ exportConst: "DbProviderModule",
725
+ msPackageJson: "apps/microservices/notes/package.json",
726
+ envPath: "apps/microservices/notes/.env"
727
+ };
728
+ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, provider);
729
+ var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
730
+
584
731
  // src/lib/scaffold-pkg.ts
585
- var import_promises3 = require("fs/promises");
586
- var import_node_path3 = require("path");
732
+ var import_promises7 = require("fs/promises");
733
+ var import_node_path7 = require("path");
587
734
  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"));
735
+ const pkgPath = (0, import_node_path7.join)(targetDir, "package.json");
736
+ const pkg = JSON.parse(await (0, import_promises7.readFile)(pkgPath, "utf8"));
590
737
  const workspaces = pkg.workspaces ?? [];
591
738
  const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
592
739
  const allowBuilds = [
@@ -607,40 +754,40 @@ ${packagesBlock}
607
754
  allowBuilds:
608
755
  ${allowBuilds}
609
756
  `;
610
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "pnpm-workspace.yaml"), content);
757
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "pnpm-workspace.yaml"), content);
611
758
  }
612
759
  async function rewritePnpmWorkspaceDeps(targetDir) {
613
- async function walk(dir) {
760
+ async function walk2(dir) {
614
761
  const found = [];
615
762
  let entries;
616
763
  try {
617
- entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
764
+ entries = await (0, import_promises7.readdir)(dir, { withFileTypes: true });
618
765
  } catch {
619
766
  return found;
620
767
  }
621
768
  for (const e of entries) {
622
769
  if (e.isDirectory() && e.name !== "node_modules") {
623
- found.push(...await walk((0, import_node_path3.join)(dir, e.name)));
770
+ found.push(...await walk2((0, import_node_path7.join)(dir, e.name)));
624
771
  } else if (e.isFile() && e.name === "package.json") {
625
- found.push((0, import_node_path3.join)(dir, e.name));
772
+ found.push((0, import_node_path7.join)(dir, e.name));
626
773
  }
627
774
  }
628
775
  return found;
629
776
  }
630
- const pkgFiles = await walk(targetDir);
777
+ const pkgFiles = await walk2(targetDir);
631
778
  for (const f of pkgFiles) {
632
779
  try {
633
- const raw = await (0, import_promises3.readFile)(f, "utf8");
780
+ const raw = await (0, import_promises7.readFile)(f, "utf8");
634
781
  const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
635
- if (next !== raw) await (0, import_promises3.writeFile)(f, next);
782
+ if (next !== raw) await (0, import_promises7.writeFile)(f, next);
636
783
  } catch {
637
784
  }
638
785
  }
639
786
  }
640
787
  async function patchGitignoreForPm(targetDir, pm) {
641
- const giPath = (0, import_node_path3.join)(targetDir, ".gitignore");
788
+ const giPath = (0, import_node_path7.join)(targetDir, ".gitignore");
642
789
  try {
643
- let src = await (0, import_promises3.readFile)(giPath, "utf8");
790
+ let src = await (0, import_promises7.readFile)(giPath, "utf8");
644
791
  src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
645
792
  if (pm !== "yarn") {
646
793
  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 +802,7 @@ async function patchGitignoreForPm(targetDir, pm) {
655
802
  src += "\n# npm\nnpm-debug.log*\n";
656
803
  }
657
804
  }
658
- await (0, import_promises3.writeFile)(giPath, src);
805
+ await (0, import_promises7.writeFile)(giPath, src);
659
806
  } catch {
660
807
  }
661
808
  }
@@ -670,7 +817,7 @@ async function writeAiFiles(targetDir, opts) {
670
817
  if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
671
818
  const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
672
819
  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");
820
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
674
821
  const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
675
822
  const readme = `# ${opts.projectName}
676
823
 
@@ -720,7 +867,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
720
867
 
721
868
  Apache-2.0
722
869
  `;
723
- await (0, import_promises3.writeFile)((0, import_node_path3.join)(targetDir, "README.md"), readme);
870
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "README.md"), readme);
724
871
  const agents = `# ${opts.projectName} \u2014 Agent Instructions
725
872
 
726
873
  ## Stack snapshot
@@ -801,8 +948,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
801
948
  - Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
802
949
  - Run: \`${nx} test <project>\`
803
950
  `;
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 });
951
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "AGENTS.md"), agents);
952
+ await (0, import_promises7.mkdir)((0, import_node_path7.join)(targetDir, ".claude"), { recursive: true });
806
953
  const mcpServers = {
807
954
  nx: {
808
955
  command: "npx",
@@ -843,8 +990,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
843
990
  ]
844
991
  }
845
992
  };
846
- await (0, import_promises3.writeFile)(
847
- (0, import_node_path3.join)(targetDir, ".claude", "settings.json"),
993
+ await (0, import_promises7.writeFile)(
994
+ (0, import_node_path7.join)(targetDir, ".claude", "settings.json"),
848
995
  JSON.stringify(settings, null, 2) + "\n"
849
996
  );
850
997
  }
@@ -864,30 +1011,30 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
864
1011
  ".vscode"
865
1012
  ]);
866
1013
  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 });
1014
+ await (0, import_promises8.mkdir)(dest, { recursive: true });
1015
+ const entries = await (0, import_promises8.readdir)(src, { withFileTypes: true });
869
1016
  for (const entry of entries) {
870
1017
  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);
1018
+ const s = (0, import_node_path8.join)(src, entry.name);
1019
+ const d = (0, import_node_path8.join)(dest, entry.name);
873
1020
  if (entry.isDirectory()) await copyTree(s, d);
874
- else if (entry.isFile()) await (0, import_promises4.copyFile)(s, d);
1021
+ else if (entry.isFile()) await (0, import_promises8.copyFile)(s, d);
875
1022
  }
876
1023
  }
877
1024
  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");
1025
+ const templatesRoot = (0, import_node_path8.join)(targetDir, "apps/templates");
1026
+ const chosen = (0, import_node_path8.join)(templatesRoot, `client-${opts.ui}`);
1027
+ const destClient = (0, import_node_path8.join)(targetDir, "apps/client");
881
1028
  let chosenUi = opts.ui;
882
1029
  try {
883
- const s = await (0, import_promises4.stat)(chosen);
1030
+ const s = await (0, import_promises8.stat)(chosen);
884
1031
  if (!s.isDirectory()) throw new Error("not a dir");
885
1032
  await copyTree(chosen, destClient);
886
1033
  } catch {
887
1034
  chosenUi = "shadcn";
888
- await copyTree((0, import_node_path4.join)(templatesRoot, "client-shadcn"), destClient);
1035
+ await copyTree((0, import_node_path8.join)(templatesRoot, "client-shadcn"), destClient);
889
1036
  }
890
- await (0, import_promises4.rm)(templatesRoot, { recursive: true, force: true });
1037
+ await (0, import_promises8.rm)(templatesRoot, { recursive: true, force: true });
891
1038
  await rewriteClientPaths(destClient, chosenUi);
892
1039
  }
893
1040
  async function rewriteClientPaths(clientDir, ui) {
@@ -900,11 +1047,11 @@ async function rewriteClientPaths(clientDir, ui) {
900
1047
  "eslint.config.mjs"
901
1048
  ];
902
1049
  for (const rel of candidates) {
903
- const path = (0, import_node_path4.join)(clientDir, rel);
1050
+ const path = (0, import_node_path8.join)(clientDir, rel);
904
1051
  try {
905
- const raw = await (0, import_promises4.readFile)(path, "utf8");
1052
+ const raw = await (0, import_promises8.readFile)(path, "utf8");
906
1053
  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);
1054
+ if (next !== raw) await (0, import_promises8.writeFile)(path, next);
908
1055
  } catch {
909
1056
  }
910
1057
  }
@@ -920,12 +1067,12 @@ function gitInit(cwd, projectName) {
920
1067
  }
921
1068
  function resolveYarnBin(cwd) {
922
1069
  try {
923
- const yarnrc = (0, import_node_fs.readFileSync)((0, import_node_path4.join)(cwd, ".yarnrc.yml"), "utf8");
1070
+ const yarnrc = (0, import_node_fs.readFileSync)((0, import_node_path8.join)(cwd, ".yarnrc.yml"), "utf8");
924
1071
  const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
925
- if (match?.[1]) return (0, import_node_path4.join)(cwd, match[1].trim());
1072
+ if (match?.[1]) return (0, import_node_path8.join)(cwd, match[1].trim());
926
1073
  } catch {
927
1074
  }
928
- return (0, import_node_path4.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
1075
+ return (0, import_node_path8.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
929
1076
  }
930
1077
  function runInstall(cwd, pm) {
931
1078
  if (pm === "yarn") {
@@ -948,19 +1095,29 @@ async function scaffold(opts, templatesDir) {
948
1095
  await selectClientTemplate(opts.targetDir, opts);
949
1096
  await writeClientEnv(opts.targetDir);
950
1097
  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);
1098
+ await cleanupUnusedFeatures(opts.targetDir, opts);
1099
+ await writeFeaturesWiring(opts.targetDir, opts);
1100
+ await writeNavConfig(opts.targetDir, opts);
1101
+ await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
1102
+ await writeAuthProvider(opts.targetDir, opts.authProvider);
1103
+ if (opts.upload !== "none") {
1104
+ await cleanupUnusedStorage(opts.targetDir, opts.upload);
1105
+ await writeStorageProvider(opts.targetDir, opts.upload);
1106
+ }
1107
+ if (opts.example !== "none") {
1108
+ await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
1109
+ await writeDbProvider(opts.targetDir, opts.dbProvider);
1110
+ }
957
1111
  const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
958
1112
  if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
1113
+ await pruneRootProviderDeps(opts.targetDir, opts);
1114
+ await writeBlueprintJson(opts.targetDir, opts);
1115
+ await writeServiceBlueprints(opts.targetDir, opts);
959
1116
  if (opts.packageManager === "yarn") {
960
- await (0, import_promises4.writeFile)((0, import_node_path4.join)(opts.targetDir, "yarn.lock"), "");
1117
+ await (0, import_promises8.writeFile)((0, import_node_path8.join)(opts.targetDir, "yarn.lock"), "");
961
1118
  } 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 });
1119
+ await (0, import_promises8.rm)((0, import_node_path8.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
1120
+ await (0, import_promises8.rm)((0, import_node_path8.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
964
1121
  }
965
1122
  if (opts.packageManager === "pnpm") {
966
1123
  await writePnpmWorkspace(opts.targetDir);
@@ -974,9 +1131,9 @@ async function scaffold(opts, templatesDir) {
974
1131
 
975
1132
  // src/lib/prompts.ts
976
1133
  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");
1134
+ var import_node_path9 = require("path");
1135
+ var import_promises9 = require("fs/promises");
1136
+ var import_node_path10 = require("path");
980
1137
  var import_node_url = require("url");
981
1138
  function detectPackageManager() {
982
1139
  const ua = process.env["npm_config_user_agent"] ?? "";
@@ -987,8 +1144,8 @@ function detectPackageManager() {
987
1144
  }
988
1145
  async function readSelfVersion() {
989
1146
  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");
1147
+ const here = (0, import_node_path10.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
1148
+ const pkgRaw = await (0, import_promises9.readFile)((0, import_node_path10.join)(here, "..", "package.json"), "utf8");
992
1149
  const pkg = JSON.parse(pkgRaw);
993
1150
  return pkg.version ?? null;
994
1151
  } catch {
@@ -1185,7 +1342,7 @@ Re-run with @latest to refresh:
1185
1342
  }) === false;
1186
1343
  return {
1187
1344
  projectName,
1188
- targetDir: (0, import_node_path5.resolve)(cwd, projectName),
1345
+ targetDir: (0, import_node_path9.resolve)(cwd, projectName),
1189
1346
  authProvider,
1190
1347
  dbProvider,
1191
1348
  upload,
@@ -1199,8 +1356,129 @@ Re-run with @latest to refresh:
1199
1356
  install
1200
1357
  };
1201
1358
  }
1359
+
1360
+ // src/manifest/audit.ts
1361
+ var import_promises10 = require("fs/promises");
1362
+ var import_node_path11 = require("path");
1363
+ var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
1364
+ async function walk(dir, out = []) {
1365
+ const entries = await (0, import_promises10.readdir)(dir, { withFileTypes: true });
1366
+ for (const e of entries) {
1367
+ if (e.isDirectory()) {
1368
+ if (!IGNORE_DIRS.has(e.name)) await walk((0, import_node_path11.join)(dir, e.name), out);
1369
+ } else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
1370
+ out.push((0, import_node_path11.join)(dir, e.name));
1371
+ }
1372
+ }
1373
+ return out;
1374
+ }
1375
+ async function tsconfigAliases(dir) {
1376
+ try {
1377
+ const raw = await (0, import_promises10.readFile)((0, import_node_path11.join)(dir, "tsconfig.base.json"), "utf8");
1378
+ const aliases = /* @__PURE__ */ new Set();
1379
+ for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
1380
+ if (m[1]) aliases.add(m[1]);
1381
+ }
1382
+ return aliases;
1383
+ } catch {
1384
+ return /* @__PURE__ */ new Set();
1385
+ }
1386
+ }
1387
+ var PROVIDER_SDKS = {
1388
+ supabase: ["@supabase/supabase-js"],
1389
+ cloudinary: ["cloudinary"],
1390
+ mongodb: ["mongoose"],
1391
+ firebase: ["firebase-admin", "@icore/firebase-admin"]
1392
+ };
1393
+ async function readBlueprint(dir) {
1394
+ try {
1395
+ return JSON.parse(await (0, import_promises10.readFile)((0, import_node_path11.join)(dir, "blueprint.json"), "utf8"));
1396
+ } catch {
1397
+ return null;
1398
+ }
1399
+ }
1400
+ function forbiddenFromBlueprint(bp) {
1401
+ const chosen = new Set(
1402
+ [bp.authProvider, bp.dbProvider, bp.upload].filter((p2) => Boolean(p2))
1403
+ );
1404
+ const forbidden = [];
1405
+ for (const [provider, sdks] of Object.entries(PROVIDER_SDKS)) {
1406
+ if (!chosen.has(provider)) forbidden.push(...sdks);
1407
+ }
1408
+ return forbidden;
1409
+ }
1410
+ async function allPackageJsons(dir) {
1411
+ const out = [];
1412
+ const root = (0, import_node_path11.join)(dir, "package.json");
1413
+ out.push(root);
1414
+ async function walk2(d) {
1415
+ let entries;
1416
+ try {
1417
+ entries = await (0, import_promises10.readdir)(d, { withFileTypes: true });
1418
+ } catch {
1419
+ return;
1420
+ }
1421
+ for (const e of entries) {
1422
+ if (e.isDirectory()) {
1423
+ if (!IGNORE_DIRS.has(e.name)) await walk2((0, import_node_path11.join)(d, e.name));
1424
+ } else if (e.name === "package.json") {
1425
+ out.push((0, import_node_path11.join)(d, e.name));
1426
+ }
1427
+ }
1428
+ }
1429
+ await walk2((0, import_node_path11.join)(dir, "apps"));
1430
+ return out;
1431
+ }
1432
+ async function depKeys(pkgPath) {
1433
+ try {
1434
+ const pkg = JSON.parse(await (0, import_promises10.readFile)(pkgPath, "utf8"));
1435
+ return /* @__PURE__ */ new Set([
1436
+ ...Object.keys(pkg.dependencies ?? {}),
1437
+ ...Object.keys(pkg.devDependencies ?? {})
1438
+ ]);
1439
+ } catch {
1440
+ return /* @__PURE__ */ new Set();
1441
+ }
1442
+ }
1443
+ var ICORE_IMPORT = /(?:from|import\()\s*['"](@icore\/[a-z0-9.-]+)/g;
1444
+ async function auditProject(dir, opts = {}) {
1445
+ const violations = [];
1446
+ const aliases = await tsconfigAliases(dir);
1447
+ for (const file of await walk(dir)) {
1448
+ const src = await (0, import_promises10.readFile)(file, "utf8");
1449
+ for (const m of src.matchAll(ICORE_IMPORT)) {
1450
+ const alias = m[1];
1451
+ if (alias && !aliases.has(alias)) {
1452
+ violations.push({
1453
+ kind: "import-of-absent-lib",
1454
+ detail: `${file} imports ${alias} (no tsconfig path \u2192 lib absent)`
1455
+ });
1456
+ }
1457
+ }
1458
+ }
1459
+ const bp = await readBlueprint(dir);
1460
+ const forbidden = /* @__PURE__ */ new Set([
1461
+ ...opts.forbiddenDeps ?? [],
1462
+ ...bp ? forbiddenFromBlueprint(bp) : []
1463
+ ]);
1464
+ if (forbidden.size > 0) {
1465
+ for (const pkgPath of await allPackageJsons(dir)) {
1466
+ const deps = await depKeys(pkgPath);
1467
+ for (const f of forbidden) {
1468
+ if (deps.has(f)) {
1469
+ violations.push({
1470
+ kind: "forbidden-dep",
1471
+ detail: `${pkgPath} keeps forbidden dep ${f}`
1472
+ });
1473
+ }
1474
+ }
1475
+ }
1476
+ }
1477
+ return violations;
1478
+ }
1202
1479
  // Annotate the CommonJS export names for ESM import in node:
1203
1480
  0 && (module.exports = {
1481
+ auditProject,
1204
1482
  collectOptions,
1205
1483
  pmRun,
1206
1484
  scaffold