@idevconn/create-icore 0.5.2 → 0.6.1

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 (98) hide show
  1. package/dist/cli.js +109 -45
  2. package/dist/index.cjs +109 -45
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +109 -45
  6. package/package.json +1 -1
  7. package/templates/apps/api/.env.example +20 -0
  8. package/templates/apps/api/package.json +1 -0
  9. package/templates/apps/api/tsconfig.json +6 -1
  10. package/templates/apps/api/webpack.config.js +20 -0
  11. package/templates/apps/microservices/auth/.env.example +5 -0
  12. package/templates/apps/microservices/auth/tsconfig.json +6 -1
  13. package/templates/apps/microservices/auth/webpack.config.js +30 -0
  14. package/templates/apps/microservices/jobs/tsconfig.json +6 -1
  15. package/templates/apps/microservices/jobs/webpack.config.js +30 -0
  16. package/templates/apps/microservices/notes/.env.example +5 -0
  17. package/templates/apps/microservices/notes/tsconfig.json +6 -1
  18. package/templates/apps/microservices/notes/webpack.config.js +30 -0
  19. package/templates/apps/microservices/notes-e2e/src/support/global.d.ts +6 -0
  20. package/templates/apps/microservices/payment/.env.example +5 -0
  21. package/templates/apps/microservices/payment/tsconfig.json +6 -1
  22. package/templates/apps/microservices/payment/webpack.config.js +30 -0
  23. package/templates/apps/microservices/upload/.env.example +5 -0
  24. package/templates/apps/microservices/upload/tsconfig.json +6 -1
  25. package/templates/apps/microservices/upload/webpack.config.js +30 -0
  26. package/templates/apps/templates/client-antd/src/components/AccessDeniedPage.tsx +1 -1
  27. package/templates/apps/templates/client-antd/src/components/layout/LayoutHeader.tsx +1 -1
  28. package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +3 -3
  29. package/templates/apps/templates/client-antd/src/routes/_dashboard/dashboard.tsx +2 -2
  30. package/templates/apps/templates/client-antd/src/routes/_dashboard/notes.tsx +2 -2
  31. package/templates/apps/templates/client-antd/src/routes/_dashboard/profile.tsx +2 -2
  32. package/templates/apps/templates/client-antd/src/routes/auth.callback.tsx +1 -1
  33. package/templates/apps/templates/client-antd/src/routes/auth.oauth.callback.tsx +1 -1
  34. package/templates/apps/templates/client-antd/src/routes/login.tsx +1 -1
  35. package/templates/apps/templates/client-antd/tsconfig.json +6 -1
  36. package/templates/apps/templates/client-antd-e2e/src/icore.spec.ts +2 -2
  37. package/templates/apps/templates/client-mui/src/components/AccessDeniedPage.tsx +1 -1
  38. package/templates/apps/templates/client-mui/src/components/layout/LayoutHeader.tsx +1 -1
  39. package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +3 -15
  40. package/templates/apps/templates/client-mui/src/routes/_dashboard/dashboard.tsx +2 -6
  41. package/templates/apps/templates/client-mui/src/routes/_dashboard/notes.tsx +2 -2
  42. package/templates/apps/templates/client-mui/src/routes/_dashboard/profile.tsx +3 -3
  43. package/templates/apps/templates/client-mui/src/routes/auth.callback.tsx +1 -1
  44. package/templates/apps/templates/client-mui/src/routes/auth.oauth.callback.tsx +1 -1
  45. package/templates/apps/templates/client-mui/src/routes/login.tsx +3 -3
  46. package/templates/apps/templates/client-mui/tsconfig.json +6 -1
  47. package/templates/apps/templates/client-mui-e2e/src/icore.spec.ts +2 -2
  48. package/templates/apps/templates/client-shadcn/src/components/AccessDeniedPage.tsx +1 -1
  49. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +3 -3
  50. package/templates/apps/templates/client-shadcn/src/components/notes/DeleteNoteConfirm.tsx +1 -1
  51. package/templates/apps/templates/client-shadcn/src/components/notes/NoteDialog.tsx +3 -3
  52. package/templates/apps/templates/client-shadcn/src/components/notes/NotesTable.tsx +1 -1
  53. package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -1
  54. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  55. package/templates/apps/templates/client-shadcn/src/components/ui/input.tsx +1 -1
  56. package/templates/apps/templates/client-shadcn/src/components/ui/label.tsx +1 -1
  57. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/dashboard.tsx +3 -9
  58. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/notes.tsx +6 -6
  59. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/profile.tsx +7 -7
  60. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  61. package/templates/apps/templates/client-shadcn/src/routes/auth.oauth.callback.tsx +1 -1
  62. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +4 -4
  63. package/templates/apps/templates/client-shadcn/tsconfig.json +6 -1
  64. package/templates/libs/auth-client/package.json +1 -1
  65. package/templates/libs/auth-strategies/firebase/package.json +1 -1
  66. package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.contract.unit.test.ts +6 -3
  67. package/templates/libs/auth-strategies/supabase/package.json +1 -1
  68. package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.contract.unit.test.ts +5 -2
  69. package/templates/libs/db-strategies/firestore/package.json +1 -1
  70. package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts +1 -2
  71. package/templates/libs/db-strategies/supabase/package.json +1 -1
  72. package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts +1 -2
  73. package/templates/libs/firebase-admin/package.json +1 -1
  74. package/templates/libs/jobs-client/package.json +1 -1
  75. package/templates/libs/notes-client/package.json +1 -1
  76. package/templates/libs/payment-client/package.json +1 -1
  77. package/templates/libs/shared/package.json +3 -3
  78. package/templates/libs/shared/src/__tests__/cross-boundary.unit.test.ts +2 -1
  79. package/templates/libs/shared/src/__tests__/transport.unit.test.ts +47 -8
  80. package/templates/libs/shared/src/abilities/subjects.ts +12 -1
  81. package/templates/libs/shared/src/strategies/__tests__/fake-auth.contract.unit.test.ts +2 -2
  82. package/templates/libs/shared/src/strategies/__tests__/fake-db.contract.unit.test.ts +2 -2
  83. package/templates/libs/shared/src/strategies/__tests__/fake-storage.contract.unit.test.ts +2 -2
  84. package/templates/libs/shared/src/transport.ts +41 -0
  85. package/templates/libs/storage-strategies/cloudinary/package.json +1 -1
  86. package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.contract.unit.test.ts +1 -2
  87. package/templates/libs/storage-strategies/firebase/package.json +1 -1
  88. package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.contract.unit.test.ts +1 -2
  89. package/templates/libs/storage-strategies/supabase/package.json +1 -1
  90. package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.contract.unit.test.ts +1 -2
  91. package/templates/libs/template-shared/package.json +1 -1
  92. package/templates/libs/upload-client/package.json +1 -1
  93. package/templates/libs/vite-plugins/src/index.d.mts +5 -7
  94. package/templates/libs/vite-plugins/src/index.mjs +1 -1
  95. package/templates/libs/vite-plugins/tsconfig.json +2 -1
  96. package/templates/package.json +2 -1
  97. package/templates/tools/create-icore/_template-shell/package.json +2 -1
  98. package/templates/.yarn/releases/yarn-4.5.0.cjs +0 -925
package/dist/index.js CHANGED
@@ -32,6 +32,24 @@ async function copyTree(src, dest) {
32
32
  else if (entry.isFile()) await copyFile(s, d);
33
33
  }
34
34
  }
35
+ var TRANSPORT_ENV_TOKEN = {
36
+ redis: "REDIS",
37
+ nats: "NATS",
38
+ mqtt: "MQTT",
39
+ rmq: "RMQ",
40
+ kafka: "KAFKA"
41
+ };
42
+ var TRANSPORT_DEPS = {
43
+ nats: { nats: "^2.29.3" },
44
+ mqtt: { mqtt: "^5.15.1" },
45
+ rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
46
+ kafka: { kafkajs: "^2.2.4" }
47
+ };
48
+ function uncommentTransportEnv(text2, prefix, transport) {
49
+ const token = TRANSPORT_ENV_TOKEN[transport];
50
+ if (!token) return text2;
51
+ return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
52
+ }
35
53
  async function rewriteRootPackageJson(targetDir, opts) {
36
54
  const pkgPath = join(targetDir, "package.json");
37
55
  const raw = await readFile(pkgPath, "utf8");
@@ -40,38 +58,31 @@ async function rewriteRootPackageJson(targetDir, opts) {
40
58
  pkg["version"] = "0.0.1";
41
59
  pkg["private"] = true;
42
60
  delete pkg.description;
43
- if (opts.transport === "nats") {
61
+ const transportDeps = TRANSPORT_DEPS[opts.transport];
62
+ if (transportDeps) {
44
63
  const deps = pkg["dependencies"] ??= {};
45
- deps["nats"] = "^2.29.3";
64
+ Object.assign(deps, transportDeps);
46
65
  }
47
66
  if (opts.packageManager !== "yarn") {
48
67
  delete pkg.packageManager;
68
+ } else {
69
+ try {
70
+ const yarnrc = await readFile(join(targetDir, ".yarnrc.yml"), "utf8");
71
+ const match = yarnrc.match(/^yarnPath:\s*.+yarn-(\d+\.\d+\.\d+)\.cjs/m);
72
+ if (match?.[1]) {
73
+ pkg["packageManager"] = `yarn@${match[1]}`;
74
+ }
75
+ } catch {
76
+ }
49
77
  }
50
- if (opts.packageManager === "pnpm") {
51
- pkg["pnpm"] = {
52
- onlyBuiltDependencies: [
53
- "@firebase/util",
54
- "@nestjs/core",
55
- "@parcel/watcher",
56
- "@scarf/scarf",
57
- "@swc/core",
58
- "less",
59
- "msgpackr-extract",
60
- "nx",
61
- "protobufjs",
62
- "unrs-resolver"
63
- ]
64
- };
65
- }
78
+ delete pkg.pnpm;
66
79
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
67
80
  }
68
81
  async function writeAuthEnv(targetDir, opts) {
69
82
  const envExample = join(targetDir, "apps/microservices/auth/.env.example");
70
83
  const env = await readFile(envExample, "utf8");
71
84
  let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
72
- if (opts.transport !== "tcp") {
73
- next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1");
74
- }
85
+ next = uncommentTransportEnv(next, "AUTH", opts.transport);
75
86
  await writeFile(join(targetDir, "apps/microservices/auth/.env"), next);
76
87
  }
77
88
  async function writeUploadEnv(targetDir, opts) {
@@ -79,9 +90,7 @@ async function writeUploadEnv(targetDir, opts) {
79
90
  const envExample = join(targetDir, "apps/microservices/upload/.env.example");
80
91
  const env = await readFile(envExample, "utf8");
81
92
  let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
82
- if (opts.transport !== "tcp") {
83
- next = next.replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
84
- }
93
+ next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
85
94
  await writeFile(join(targetDir, "apps/microservices/upload/.env"), next);
86
95
  }
87
96
  async function writeNotesEnv(targetDir, opts) {
@@ -90,9 +99,7 @@ async function writeNotesEnv(targetDir, opts) {
90
99
  try {
91
100
  const env = await readFile(envExample, "utf8");
92
101
  let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
93
- if (opts.transport !== "tcp") {
94
- next = next.replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1");
95
- }
102
+ next = uncommentTransportEnv(next, "NOTES", opts.transport);
96
103
  await writeFile(join(targetDir, "apps/microservices/notes/.env"), next);
97
104
  } catch {
98
105
  }
@@ -101,8 +108,8 @@ async function writeGatewayEnv(targetDir, opts) {
101
108
  const envExample = join(targetDir, "apps/api/.env.example");
102
109
  const env = await readFile(envExample, "utf8");
103
110
  let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`).replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
104
- if (opts.transport !== "tcp") {
105
- next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
111
+ for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
112
+ next = uncommentTransportEnv(next, prefix, opts.transport);
106
113
  }
107
114
  await writeFile(join(targetDir, "apps/api/.env"), next);
108
115
  }
@@ -140,9 +147,7 @@ async function writePaymentEnv(targetDir, opts) {
140
147
  try {
141
148
  const env = await readFile(envExample, "utf8");
142
149
  let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
143
- if (opts.transport !== "tcp") {
144
- next = next.replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
145
- }
150
+ next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
146
151
  await writeFile(join(targetDir, "apps/microservices/payment/.env"), next);
147
152
  } catch {
148
153
  }
@@ -231,7 +236,10 @@ async function removeNotesStack(targetDir) {
231
236
  await writeFile(appModulePath, next);
232
237
  } catch {
233
238
  }
234
- await stripDeps(join(targetDir, "apps/api/package.json"), ["@icore/notes-client"]);
239
+ await stripDeps(join(targetDir, "apps/api/package.json"), [
240
+ "@icore/notes-client",
241
+ "@casl/ability"
242
+ ]);
235
243
  await stripGatewayTransport(targetDir, "NOTES");
236
244
  const tsconfigPath = join(targetDir, "tsconfig.base.json");
237
245
  try {
@@ -243,21 +251,16 @@ async function removeNotesStack(targetDir) {
243
251
  const siderPath = join(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
244
252
  try {
245
253
  const src = await readFile(siderPath, "utf8");
246
- const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
254
+ const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
247
255
  "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
248
256
  "const selectedKey = pathname.includes('/profile')"
249
257
  ).replace(
250
- `
251
- {
252
- key: 'notes',
253
- icon: <FileTextOutlined />,
254
- label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
255
- },`,
258
+ /\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
256
259
  ""
257
260
  ).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
258
- /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/ListItemButton>/,
261
+ /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
259
262
  ""
260
- ).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
263
+ ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
261
264
  await writeFile(siderPath, next);
262
265
  } catch {
263
266
  }
@@ -400,7 +403,7 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
400
403
  await stripTsconfigPath(targetDir, "@icore/db-supabase");
401
404
  try {
402
405
  const src = await readFile(modulePath, "utf8");
403
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(
406
+ const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/m, "").replace(
404
407
  /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
405
408
  "return makeFirestoreDB(cfg);"
406
409
  );
@@ -533,11 +536,69 @@ async function scaffold(opts, templatesDir) {
533
536
  await rm(join(opts.targetDir, ".yarn"), { recursive: true, force: true });
534
537
  await rm(join(opts.targetDir, ".yarnrc.yml"), { force: true });
535
538
  }
539
+ if (opts.packageManager === "pnpm") {
540
+ await writePnpmWorkspace(opts.targetDir);
541
+ await rewritePnpmWorkspaceDeps(opts.targetDir);
542
+ }
536
543
  await patchGitignoreForPm(opts.targetDir, opts.packageManager);
537
544
  await writeAiFiles(opts.targetDir, opts);
538
545
  if (opts.install) runInstall(opts.targetDir, opts.packageManager);
539
546
  if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
540
547
  }
548
+ async function writePnpmWorkspace(targetDir) {
549
+ const pkgPath = join(targetDir, "package.json");
550
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
551
+ const workspaces = pkg.workspaces ?? [];
552
+ const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
553
+ const allowBuilds = [
554
+ "@firebase/util",
555
+ "@nestjs/core",
556
+ "@parcel/watcher",
557
+ "@scarf/scarf",
558
+ "@swc/core",
559
+ "less",
560
+ "msgpackr-extract",
561
+ "nx",
562
+ "protobufjs",
563
+ "unrs-resolver"
564
+ ].map((p2) => ` '${p2}': true`).join("\n");
565
+ const content = `packages:
566
+ ${packagesBlock}
567
+
568
+ allowBuilds:
569
+ ${allowBuilds}
570
+ `;
571
+ await writeFile(join(targetDir, "pnpm-workspace.yaml"), content);
572
+ }
573
+ async function rewritePnpmWorkspaceDeps(targetDir) {
574
+ const { readdir: rd } = await import("fs/promises");
575
+ async function walk(dir) {
576
+ const found = [];
577
+ let entries;
578
+ try {
579
+ entries = await rd(dir, { withFileTypes: true });
580
+ } catch {
581
+ return found;
582
+ }
583
+ for (const e of entries) {
584
+ if (e.isDirectory() && e.name !== "node_modules") {
585
+ found.push(...await walk(join(dir, e.name)));
586
+ } else if (e.isFile() && e.name === "package.json") {
587
+ found.push(join(dir, e.name));
588
+ }
589
+ }
590
+ return found;
591
+ }
592
+ const pkgFiles = await walk(targetDir);
593
+ for (const f of pkgFiles) {
594
+ try {
595
+ const raw = await readFile(f, "utf8");
596
+ const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
597
+ if (next !== raw) await writeFile(f, next);
598
+ } catch {
599
+ }
600
+ }
601
+ }
541
602
  async function patchGitignoreForPm(targetDir, pm) {
542
603
  const giPath = join(targetDir, ".gitignore");
543
604
  try {
@@ -641,7 +702,7 @@ Apache-2.0
641
702
 
642
703
  - **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
643
704
  - **No code without approval**: Propose changes first, wait for go-ahead.
644
- - **\u0417\u0410\u041A\u041E\u041D \u2014 no crash on missing .env**: MS factories must catch config errors, print a boxed banner with ALL missing vars, and return a Fake strategy in dev. In prod (\`NODE_ENV=production\`) throw the same banner. The \`formatEnvBanner\` + \`missingEnv\` helpers from \`@icore/shared\` handle this.
705
+ - **RULE \u2014 no crash on missing .env**: MS factories must catch config errors, print a boxed banner with ALL missing vars, and return a Fake strategy in dev. In prod (\`NODE_ENV=production\`) throw the same banner. The \`formatEnvBanner\` + \`missingEnv\` helpers from \`@icore/shared\` handle this.
645
706
  - **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
646
707
  - **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
647
708
 
@@ -938,7 +999,10 @@ Re-run with @latest to refresh:
938
999
  options: [
939
1000
  { value: "tcp", label: "TCP (default, no broker required)" },
940
1001
  { value: "redis", label: "Redis" },
941
- { value: "nats", label: "NATS" }
1002
+ { value: "nats", label: "NATS" },
1003
+ { value: "mqtt", label: "MQTT" },
1004
+ { value: "rmq", label: "RabbitMQ" },
1005
+ { value: "kafka", label: "Kafka" }
942
1006
  ],
943
1007
  initialValue: "tcp"
944
1008
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idevconn/create-icore",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "description": "Bootstrap a new project from the iCore scaffold (Nx + NestJS + React + Vite + shadcn/Tailwind, swappable auth + storage providers).",
5
5
  "license": "Apache-2.0",
6
6
  "author": "iDEVconn",
@@ -7,6 +7,11 @@ AUTH_HOST=127.0.0.1
7
7
  AUTH_PORT=4001
8
8
  # AUTH_REDIS_URL=redis://localhost:6379
9
9
  # AUTH_NATS_URL=nats://localhost:4222
10
+ # AUTH_MQTT_URL=mqtt://localhost:1883
11
+ # AUTH_RMQ_URL=amqp://localhost:5672
12
+ # AUTH_RMQ_QUEUE=auth_queue
13
+ # AUTH_KAFKA_BROKERS=localhost:9092
14
+ # AUTH_KAFKA_CLIENT_ID=auth
10
15
 
11
16
  # Upload MS transport — must match apps/microservices/upload/.env
12
17
  UPLOAD_TRANSPORT=tcp
@@ -14,6 +19,11 @@ UPLOAD_HOST=127.0.0.1
14
19
  UPLOAD_PORT=4002
15
20
  # UPLOAD_REDIS_URL=redis://localhost:6379
16
21
  # UPLOAD_NATS_URL=nats://localhost:4222
22
+ # UPLOAD_MQTT_URL=mqtt://localhost:1883
23
+ # UPLOAD_RMQ_URL=amqp://localhost:5672
24
+ # UPLOAD_RMQ_QUEUE=upload_queue
25
+ # UPLOAD_KAFKA_BROKERS=localhost:9092
26
+ # UPLOAD_KAFKA_CLIENT_ID=upload
17
27
 
18
28
  # Notes MS transport — must match apps/microservices/notes/.env
19
29
  NOTES_TRANSPORT=tcp
@@ -21,6 +31,11 @@ NOTES_HOST=127.0.0.1
21
31
  NOTES_PORT=4004
22
32
  # NOTES_REDIS_URL=redis://localhost:6379
23
33
  # NOTES_NATS_URL=nats://localhost:4222
34
+ # NOTES_MQTT_URL=mqtt://localhost:1883
35
+ # NOTES_RMQ_URL=amqp://localhost:5672
36
+ # NOTES_RMQ_QUEUE=notes_queue
37
+ # NOTES_KAFKA_BROKERS=localhost:9092
38
+ # NOTES_KAFKA_CLIENT_ID=notes
24
39
 
25
40
  # Payment MS transport — must match apps/microservices/payment/.env
26
41
  PAYMENT_TRANSPORT=tcp
@@ -28,6 +43,11 @@ PAYMENT_HOST=127.0.0.1
28
43
  PAYMENT_PORT=4003
29
44
  # PAYMENT_REDIS_URL=redis://localhost:6379
30
45
  # PAYMENT_NATS_URL=nats://localhost:4222
46
+ # PAYMENT_MQTT_URL=mqtt://localhost:1883
47
+ # PAYMENT_RMQ_URL=amqp://localhost:5672
48
+ # PAYMENT_RMQ_QUEUE=payment_queue
49
+ # PAYMENT_KAFKA_BROKERS=localhost:9092
50
+ # PAYMENT_KAFKA_CLIENT_ID=payment
31
51
 
32
52
  # Per-request multipart file size cap (KB). Default 5120 (5 MB) when unset.
33
53
  MAX_FILE_SIZE_KB=5120
@@ -3,6 +3,7 @@
3
3
  "version": "0.0.1",
4
4
  "private": true,
5
5
  "devDependencies": {
6
+ "@types/express": "^4.17.25",
6
7
  "@types/multer": "*"
7
8
  },
8
9
  "dependencies": {
@@ -11,6 +11,11 @@
11
11
  }
12
12
  ],
13
13
  "compilerOptions": {
14
- "esModuleInterop": true
14
+ "esModuleInterop": true,
15
+ "module": "node16",
16
+ "moduleResolution": "node16",
17
+ "experimentalDecorators": true,
18
+ "emitDecoratorMetadata": true,
19
+ "target": "es2021"
15
20
  }
16
21
  }
@@ -1,4 +1,5 @@
1
1
  const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2
+ const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
2
3
  const { join } = require('path');
3
4
 
4
5
  module.exports = {
@@ -9,6 +10,23 @@ module.exports = {
9
10
  devtoolModuleFilenameTemplate: '[absolute-resource-path]',
10
11
  }),
11
12
  },
13
+ resolve: {
14
+ plugins: [new TsconfigPathsPlugin({ configFile: join(__dirname, 'tsconfig.app.json') })],
15
+ },
16
+ // See microservices/*/webpack.config.js for the @icore/* bundling rationale.
17
+ externals: [
18
+ function ({ request }, callback) {
19
+ if (
20
+ !request ||
21
+ request.startsWith('.') ||
22
+ request.startsWith('/') ||
23
+ request.startsWith('@icore/')
24
+ ) {
25
+ return callback();
26
+ }
27
+ return callback(null, 'commonjs ' + request);
28
+ },
29
+ ],
12
30
  plugins: [
13
31
  new NxAppWebpackPlugin({
14
32
  target: 'node',
@@ -19,6 +37,8 @@ module.exports = {
19
37
  optimization: false,
20
38
  outputHashing: 'none',
21
39
  generatePackageJson: true,
40
+ mergeExternals: true,
41
+ externalDependencies: [],
22
42
  sourceMap: true,
23
43
  }),
24
44
  ],
@@ -4,6 +4,11 @@ AUTH_HOST=127.0.0.1
4
4
  AUTH_PORT=4001
5
5
  # AUTH_REDIS_URL=redis://localhost:6379
6
6
  # AUTH_NATS_URL=nats://localhost:4222
7
+ # AUTH_MQTT_URL=mqtt://localhost:1883
8
+ # AUTH_RMQ_URL=amqp://localhost:5672
9
+ # AUTH_RMQ_QUEUE=auth_queue
10
+ # AUTH_KAFKA_BROKERS=localhost:9092
11
+ # AUTH_KAFKA_CLIENT_ID=auth
7
12
 
8
13
  # Which concrete AuthStrategy to instantiate
9
14
  AUTH_PROVIDER=supabase
@@ -11,6 +11,11 @@
11
11
  }
12
12
  ],
13
13
  "compilerOptions": {
14
- "esModuleInterop": true
14
+ "esModuleInterop": true,
15
+ "module": "node16",
16
+ "moduleResolution": "node16",
17
+ "experimentalDecorators": true,
18
+ "emitDecoratorMetadata": true,
19
+ "target": "es2021"
15
20
  }
16
21
  }
@@ -1,4 +1,5 @@
1
1
  const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2
+ const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
2
3
  const { join } = require('path');
3
4
 
4
5
  module.exports = {
@@ -9,6 +10,31 @@ module.exports = {
9
10
  devtoolModuleFilenameTemplate: '[absolute-resource-path]',
10
11
  }),
11
12
  },
13
+ resolve: {
14
+ // Resolve @icore/* via tsconfig paths so webpack can bundle them inline.
15
+ // nx skips its own tsconfig-paths plugin on "TS solution" workspaces, so we
16
+ // wire the standalone plugin explicitly (it follows extends → tsconfig.base).
17
+ plugins: [new TsconfigPathsPlugin({ configFile: join(__dirname, 'tsconfig.app.json') })],
18
+ },
19
+ // Keep every npm package external EXCEPT @icore/* workspace packages, which
20
+ // are bundled inline. @icore/* are workspace-internal: at runtime the package
21
+ // manager symlinks them to their TS source dir (not the compiled dist), so an
22
+ // external require('@icore/shared') fails ("Cannot find module './env'").
23
+ // Bundling removes runtime workspace resolution entirely — works identically
24
+ // on yarn / npm / pnpm.
25
+ externals: [
26
+ function ({ request }, callback) {
27
+ if (
28
+ !request ||
29
+ request.startsWith('.') ||
30
+ request.startsWith('/') ||
31
+ request.startsWith('@icore/')
32
+ ) {
33
+ return callback(); // bundle inline
34
+ }
35
+ return callback(null, 'commonjs ' + request); // keep external
36
+ },
37
+ ],
12
38
  plugins: [
13
39
  new NxAppWebpackPlugin({
14
40
  target: 'node',
@@ -19,6 +45,10 @@ module.exports = {
19
45
  optimization: false,
20
46
  outputHashing: 'none',
21
47
  generatePackageJson: true,
48
+ // Keep our externals (above) authoritative — do not let the plugin add
49
+ // its own nodeExternals that would re-externalize @icore/*.
50
+ mergeExternals: true,
51
+ externalDependencies: [],
22
52
  sourceMap: true,
23
53
  }),
24
54
  ],
@@ -8,6 +8,11 @@
8
8
  }
9
9
  ],
10
10
  "compilerOptions": {
11
- "esModuleInterop": true
11
+ "esModuleInterop": true,
12
+ "module": "node16",
13
+ "moduleResolution": "node16",
14
+ "experimentalDecorators": true,
15
+ "emitDecoratorMetadata": true,
16
+ "target": "es2021"
12
17
  }
13
18
  }
@@ -1,4 +1,5 @@
1
1
  const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2
+ const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
2
3
  const { join } = require('path');
3
4
 
4
5
  module.exports = {
@@ -9,6 +10,31 @@ module.exports = {
9
10
  devtoolModuleFilenameTemplate: '[absolute-resource-path]',
10
11
  }),
11
12
  },
13
+ resolve: {
14
+ // Resolve @icore/* via tsconfig paths so webpack can bundle them inline.
15
+ // nx skips its own tsconfig-paths plugin on "TS solution" workspaces, so we
16
+ // wire the standalone plugin explicitly (it follows extends → tsconfig.base).
17
+ plugins: [new TsconfigPathsPlugin({ configFile: join(__dirname, 'tsconfig.app.json') })],
18
+ },
19
+ // Keep every npm package external EXCEPT @icore/* workspace packages, which
20
+ // are bundled inline. @icore/* are workspace-internal: at runtime the package
21
+ // manager symlinks them to their TS source dir (not the compiled dist), so an
22
+ // external require('@icore/shared') fails ("Cannot find module './env'").
23
+ // Bundling removes runtime workspace resolution entirely — works identically
24
+ // on yarn / npm / pnpm.
25
+ externals: [
26
+ function ({ request }, callback) {
27
+ if (
28
+ !request ||
29
+ request.startsWith('.') ||
30
+ request.startsWith('/') ||
31
+ request.startsWith('@icore/')
32
+ ) {
33
+ return callback(); // bundle inline
34
+ }
35
+ return callback(null, 'commonjs ' + request); // keep external
36
+ },
37
+ ],
12
38
  plugins: [
13
39
  new NxAppWebpackPlugin({
14
40
  target: 'node',
@@ -19,6 +45,10 @@ module.exports = {
19
45
  optimization: false,
20
46
  outputHashing: 'none',
21
47
  generatePackageJson: true,
48
+ // Keep our externals (above) authoritative — do not let the plugin add
49
+ // its own nodeExternals that would re-externalize @icore/*.
50
+ mergeExternals: true,
51
+ externalDependencies: [],
22
52
  sourceMap: true,
23
53
  }),
24
54
  ],
@@ -4,6 +4,11 @@ NOTES_HOST=127.0.0.1
4
4
  NOTES_PORT=4004
5
5
  # NOTES_REDIS_URL=redis://localhost:6379
6
6
  # NOTES_NATS_URL=nats://localhost:4222
7
+ # NOTES_MQTT_URL=mqtt://localhost:1883
8
+ # NOTES_RMQ_URL=amqp://localhost:5672
9
+ # NOTES_RMQ_QUEUE=notes_queue
10
+ # NOTES_KAFKA_BROKERS=localhost:9092
11
+ # NOTES_KAFKA_CLIENT_ID=notes
7
12
 
8
13
  # Which DB to use — defaults to the root .env DB_PROVIDER if unset.
9
14
  # Supabase Postgres or Firestore. Both honored by NotesController via DBStrategy.
@@ -8,6 +8,11 @@
8
8
  }
9
9
  ],
10
10
  "compilerOptions": {
11
- "esModuleInterop": true
11
+ "esModuleInterop": true,
12
+ "module": "node16",
13
+ "moduleResolution": "node16",
14
+ "experimentalDecorators": true,
15
+ "emitDecoratorMetadata": true,
16
+ "target": "es2021"
12
17
  }
13
18
  }
@@ -1,4 +1,5 @@
1
1
  const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2
+ const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
2
3
  const { join } = require('path');
3
4
 
4
5
  module.exports = {
@@ -9,6 +10,31 @@ module.exports = {
9
10
  devtoolModuleFilenameTemplate: '[absolute-resource-path]',
10
11
  }),
11
12
  },
13
+ resolve: {
14
+ // Resolve @icore/* via tsconfig paths so webpack can bundle them inline.
15
+ // nx skips its own tsconfig-paths plugin on "TS solution" workspaces, so we
16
+ // wire the standalone plugin explicitly (it follows extends → tsconfig.base).
17
+ plugins: [new TsconfigPathsPlugin({ configFile: join(__dirname, 'tsconfig.app.json') })],
18
+ },
19
+ // Keep every npm package external EXCEPT @icore/* workspace packages, which
20
+ // are bundled inline. @icore/* are workspace-internal: at runtime the package
21
+ // manager symlinks them to their TS source dir (not the compiled dist), so an
22
+ // external require('@icore/shared') fails ("Cannot find module './env'").
23
+ // Bundling removes runtime workspace resolution entirely — works identically
24
+ // on yarn / npm / pnpm.
25
+ externals: [
26
+ function ({ request }, callback) {
27
+ if (
28
+ !request ||
29
+ request.startsWith('.') ||
30
+ request.startsWith('/') ||
31
+ request.startsWith('@icore/')
32
+ ) {
33
+ return callback(); // bundle inline
34
+ }
35
+ return callback(null, 'commonjs ' + request); // keep external
36
+ },
37
+ ],
12
38
  plugins: [
13
39
  new NxAppWebpackPlugin({
14
40
  target: 'node',
@@ -19,6 +45,10 @@ module.exports = {
19
45
  optimization: false,
20
46
  outputHashing: 'none',
21
47
  generatePackageJson: true,
48
+ // Keep our externals (above) authoritative — do not let the plugin add
49
+ // its own nodeExternals that would re-externalize @icore/*.
50
+ mergeExternals: true,
51
+ externalDependencies: [],
22
52
  sourceMap: true,
23
53
  }),
24
54
  ],
@@ -0,0 +1,6 @@
1
+ // Augments globalThis so TypeScript accepts custom properties set in
2
+ // global-setup.ts and read in global-teardown.ts (TS7017 fix).
3
+ declare global {
4
+ var __TEARDOWN_MESSAGE__: string;
5
+ }
6
+ export {};
@@ -4,6 +4,11 @@ PAYMENT_HOST=127.0.0.1
4
4
  PAYMENT_PORT=4003
5
5
  # PAYMENT_REDIS_URL=redis://localhost:6379
6
6
  # PAYMENT_NATS_URL=nats://localhost:4222
7
+ # PAYMENT_MQTT_URL=mqtt://localhost:1883
8
+ # PAYMENT_RMQ_URL=amqp://localhost:5672
9
+ # PAYMENT_RMQ_QUEUE=payment_queue
10
+ # PAYMENT_KAFKA_BROKERS=localhost:9092
11
+ # PAYMENT_KAFKA_CLIENT_ID=payment
7
12
 
8
13
  # Which concrete PaymentStrategy to instantiate
9
14
  PAYMENT_PROVIDER=paypal
@@ -8,6 +8,11 @@
8
8
  }
9
9
  ],
10
10
  "compilerOptions": {
11
- "esModuleInterop": true
11
+ "esModuleInterop": true,
12
+ "module": "node16",
13
+ "moduleResolution": "node16",
14
+ "experimentalDecorators": true,
15
+ "emitDecoratorMetadata": true,
16
+ "target": "es2021"
12
17
  }
13
18
  }