@idevconn/create-icore 0.5.1 → 0.6.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 (85) hide show
  1. package/dist/cli.js +36 -27
  2. package/dist/index.cjs +36 -27
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +36 -27
  6. package/package.json +3 -1
  7. package/templates/apps/api/.env.example +20 -0
  8. package/templates/apps/api/tsconfig.json +6 -1
  9. package/templates/apps/microservices/auth/.env.example +5 -0
  10. package/templates/apps/microservices/auth/tsconfig.json +6 -1
  11. package/templates/apps/microservices/jobs/tsconfig.json +6 -1
  12. package/templates/apps/microservices/notes/.env.example +5 -0
  13. package/templates/apps/microservices/notes/tsconfig.json +6 -1
  14. package/templates/apps/microservices/notes-e2e/src/support/global.d.ts +6 -0
  15. package/templates/apps/microservices/payment/.env.example +5 -0
  16. package/templates/apps/microservices/payment/tsconfig.json +6 -1
  17. package/templates/apps/microservices/upload/.env.example +5 -0
  18. package/templates/apps/microservices/upload/tsconfig.json +6 -1
  19. package/templates/apps/templates/client-antd/src/components/AccessDeniedPage.tsx +1 -1
  20. package/templates/apps/templates/client-antd/src/components/layout/LayoutHeader.tsx +1 -1
  21. package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +3 -3
  22. package/templates/apps/templates/client-antd/src/routes/_dashboard/dashboard.tsx +2 -2
  23. package/templates/apps/templates/client-antd/src/routes/_dashboard/notes.tsx +2 -2
  24. package/templates/apps/templates/client-antd/src/routes/_dashboard/profile.tsx +2 -2
  25. package/templates/apps/templates/client-antd/src/routes/auth.callback.tsx +1 -1
  26. package/templates/apps/templates/client-antd/src/routes/auth.oauth.callback.tsx +1 -1
  27. package/templates/apps/templates/client-antd/src/routes/login.tsx +1 -1
  28. package/templates/apps/templates/client-antd/tsconfig.json +6 -1
  29. package/templates/apps/templates/client-antd-e2e/src/icore.spec.ts +2 -2
  30. package/templates/apps/templates/client-mui/src/components/AccessDeniedPage.tsx +1 -1
  31. package/templates/apps/templates/client-mui/src/components/layout/LayoutHeader.tsx +1 -1
  32. package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +3 -15
  33. package/templates/apps/templates/client-mui/src/routes/_dashboard/dashboard.tsx +2 -6
  34. package/templates/apps/templates/client-mui/src/routes/_dashboard/notes.tsx +2 -2
  35. package/templates/apps/templates/client-mui/src/routes/_dashboard/profile.tsx +3 -3
  36. package/templates/apps/templates/client-mui/src/routes/auth.callback.tsx +1 -1
  37. package/templates/apps/templates/client-mui/src/routes/auth.oauth.callback.tsx +1 -1
  38. package/templates/apps/templates/client-mui/src/routes/login.tsx +3 -3
  39. package/templates/apps/templates/client-mui/tsconfig.json +6 -1
  40. package/templates/apps/templates/client-mui-e2e/src/icore.spec.ts +2 -2
  41. package/templates/apps/templates/client-shadcn/src/components/AccessDeniedPage.tsx +1 -1
  42. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +3 -3
  43. package/templates/apps/templates/client-shadcn/src/components/notes/DeleteNoteConfirm.tsx +1 -1
  44. package/templates/apps/templates/client-shadcn/src/components/notes/NoteDialog.tsx +3 -3
  45. package/templates/apps/templates/client-shadcn/src/components/notes/NotesTable.tsx +1 -1
  46. package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -1
  47. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  48. package/templates/apps/templates/client-shadcn/src/components/ui/input.tsx +1 -1
  49. package/templates/apps/templates/client-shadcn/src/components/ui/label.tsx +1 -1
  50. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/dashboard.tsx +3 -9
  51. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/notes.tsx +6 -6
  52. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/profile.tsx +7 -7
  53. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  54. package/templates/apps/templates/client-shadcn/src/routes/auth.oauth.callback.tsx +1 -1
  55. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +19 -12
  56. package/templates/apps/templates/client-shadcn/tsconfig.json +6 -1
  57. package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.contract.unit.test.ts +7 -4
  58. package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.contract.unit.test.ts +6 -3
  59. package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts +2 -3
  60. package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts +2 -3
  61. package/templates/libs/firebase-admin/tsconfig.json +2 -1
  62. package/templates/libs/jobs-client/tsconfig.json +2 -1
  63. package/templates/libs/notes-client/tsconfig.json +2 -1
  64. package/templates/libs/payment-client/tsconfig.json +2 -1
  65. package/templates/libs/shared/src/__tests__/cross-boundary.unit.test.ts +2 -1
  66. package/templates/libs/shared/src/__tests__/transport.unit.test.ts +47 -8
  67. package/templates/libs/shared/src/abilities/subjects.ts +12 -1
  68. package/templates/libs/shared/src/strategies/__tests__/fake-auth.contract.unit.test.ts +2 -2
  69. package/templates/libs/shared/src/strategies/__tests__/fake-db.contract.unit.test.ts +2 -2
  70. package/templates/libs/shared/src/strategies/__tests__/fake-storage.contract.unit.test.ts +2 -2
  71. package/templates/libs/shared/src/strategies/index.ts +3 -3
  72. package/templates/libs/shared/src/testing.ts +14 -0
  73. package/templates/libs/shared/src/transport.ts +41 -0
  74. package/templates/libs/shared/tsconfig.lib.json +3 -1
  75. package/templates/libs/shared/vitest.config.mts +11 -1
  76. package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.contract.unit.test.ts +2 -3
  77. package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.contract.unit.test.ts +2 -3
  78. package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.contract.unit.test.ts +2 -3
  79. package/templates/libs/vite-plugins/src/index.d.mts +5 -7
  80. package/templates/libs/vite-plugins/src/index.mjs +1 -1
  81. package/templates/libs/vite-plugins/tsconfig.json +2 -1
  82. package/templates/tsconfig.base.json +1 -0
  83. /package/templates/libs/shared/src/strategies/{contract/auth-contract.ts → __tests__/auth.contract.unit.test.ts} +0 -0
  84. /package/templates/libs/shared/src/strategies/{contract/db-contract.ts → __tests__/db.contract.unit.test.ts} +0 -0
  85. /package/templates/libs/shared/src/strategies/{contract/storage-contract.ts → __tests__/storage.contract.unit.test.ts} +0 -0
package/dist/cli.js CHANGED
@@ -195,7 +195,10 @@ Re-run with @latest to refresh:
195
195
  options: [
196
196
  { value: "tcp", label: "TCP (default, no broker required)" },
197
197
  { value: "redis", label: "Redis" },
198
- { value: "nats", label: "NATS" }
198
+ { value: "nats", label: "NATS" },
199
+ { value: "mqtt", label: "MQTT" },
200
+ { value: "rmq", label: "RabbitMQ" },
201
+ { value: "kafka", label: "Kafka" }
199
202
  ],
200
203
  initialValue: "tcp"
201
204
  });
@@ -265,6 +268,24 @@ async function copyTree(src, dest) {
265
268
  else if (entry.isFile()) await copyFile(s, d);
266
269
  }
267
270
  }
271
+ var TRANSPORT_ENV_TOKEN = {
272
+ redis: "REDIS",
273
+ nats: "NATS",
274
+ mqtt: "MQTT",
275
+ rmq: "RMQ",
276
+ kafka: "KAFKA"
277
+ };
278
+ var TRANSPORT_DEPS = {
279
+ nats: { nats: "^2.29.3" },
280
+ mqtt: { mqtt: "^5.15.1" },
281
+ rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
282
+ kafka: { kafkajs: "^2.2.4" }
283
+ };
284
+ function uncommentTransportEnv(text2, prefix, transport) {
285
+ const token = TRANSPORT_ENV_TOKEN[transport];
286
+ if (!token) return text2;
287
+ return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
288
+ }
268
289
  async function rewriteRootPackageJson(targetDir, opts) {
269
290
  const pkgPath = join2(targetDir, "package.json");
270
291
  const raw = await readFile2(pkgPath, "utf8");
@@ -273,9 +294,10 @@ async function rewriteRootPackageJson(targetDir, opts) {
273
294
  pkg["version"] = "0.0.1";
274
295
  pkg["private"] = true;
275
296
  delete pkg.description;
276
- if (opts.transport === "nats") {
297
+ const transportDeps = TRANSPORT_DEPS[opts.transport];
298
+ if (transportDeps) {
277
299
  const deps = pkg["dependencies"] ??= {};
278
- deps["nats"] = "^2.29.3";
300
+ Object.assign(deps, transportDeps);
279
301
  }
280
302
  if (opts.packageManager !== "yarn") {
281
303
  delete pkg.packageManager;
@@ -302,9 +324,7 @@ async function writeAuthEnv(targetDir, opts) {
302
324
  const envExample = join2(targetDir, "apps/microservices/auth/.env.example");
303
325
  const env = await readFile2(envExample, "utf8");
304
326
  let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
305
- if (opts.transport !== "tcp") {
306
- next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1");
307
- }
327
+ next = uncommentTransportEnv(next, "AUTH", opts.transport);
308
328
  await writeFile(join2(targetDir, "apps/microservices/auth/.env"), next);
309
329
  }
310
330
  async function writeUploadEnv(targetDir, opts) {
@@ -312,9 +332,7 @@ async function writeUploadEnv(targetDir, opts) {
312
332
  const envExample = join2(targetDir, "apps/microservices/upload/.env.example");
313
333
  const env = await readFile2(envExample, "utf8");
314
334
  let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
315
- if (opts.transport !== "tcp") {
316
- next = next.replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
317
- }
335
+ next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
318
336
  await writeFile(join2(targetDir, "apps/microservices/upload/.env"), next);
319
337
  }
320
338
  async function writeNotesEnv(targetDir, opts) {
@@ -323,9 +341,7 @@ async function writeNotesEnv(targetDir, opts) {
323
341
  try {
324
342
  const env = await readFile2(envExample, "utf8");
325
343
  let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
326
- if (opts.transport !== "tcp") {
327
- next = next.replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1");
328
- }
344
+ next = uncommentTransportEnv(next, "NOTES", opts.transport);
329
345
  await writeFile(join2(targetDir, "apps/microservices/notes/.env"), next);
330
346
  } catch {
331
347
  }
@@ -334,8 +350,8 @@ async function writeGatewayEnv(targetDir, opts) {
334
350
  const envExample = join2(targetDir, "apps/api/.env.example");
335
351
  const env = await readFile2(envExample, "utf8");
336
352
  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}`);
337
- if (opts.transport !== "tcp") {
338
- 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");
353
+ for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
354
+ next = uncommentTransportEnv(next, prefix, opts.transport);
339
355
  }
340
356
  await writeFile(join2(targetDir, "apps/api/.env"), next);
341
357
  }
@@ -373,9 +389,7 @@ async function writePaymentEnv(targetDir, opts) {
373
389
  try {
374
390
  const env = await readFile2(envExample, "utf8");
375
391
  let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
376
- if (opts.transport !== "tcp") {
377
- next = next.replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
378
- }
392
+ next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
379
393
  await writeFile(join2(targetDir, "apps/microservices/payment/.env"), next);
380
394
  } catch {
381
395
  }
@@ -476,21 +490,16 @@ async function removeNotesStack(targetDir) {
476
490
  const siderPath = join2(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
477
491
  try {
478
492
  const src = await readFile2(siderPath, "utf8");
479
- const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
493
+ const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
480
494
  "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
481
495
  "const selectedKey = pathname.includes('/profile')"
482
496
  ).replace(
483
- `
484
- {
485
- key: 'notes',
486
- icon: <FileTextOutlined />,
487
- label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
488
- },`,
497
+ /\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
489
498
  ""
490
499
  ).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
491
- /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/ListItemButton>/,
500
+ /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
492
501
  ""
493
- ).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
502
+ ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
494
503
  await writeFile(siderPath, next);
495
504
  } catch {
496
505
  }
@@ -874,7 +883,7 @@ Apache-2.0
874
883
 
875
884
  - **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
876
885
  - **No code without approval**: Propose changes first, wait for go-ahead.
877
- - **\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.
886
+ - **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.
878
887
  - **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
879
888
  - **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
880
889
 
package/dist/index.cjs CHANGED
@@ -74,6 +74,24 @@ async function copyTree(src, dest) {
74
74
  else if (entry.isFile()) await (0, import_promises.copyFile)(s, d);
75
75
  }
76
76
  }
77
+ var TRANSPORT_ENV_TOKEN = {
78
+ redis: "REDIS",
79
+ nats: "NATS",
80
+ mqtt: "MQTT",
81
+ rmq: "RMQ",
82
+ kafka: "KAFKA"
83
+ };
84
+ var TRANSPORT_DEPS = {
85
+ nats: { nats: "^2.29.3" },
86
+ mqtt: { mqtt: "^5.15.1" },
87
+ rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
88
+ kafka: { kafkajs: "^2.2.4" }
89
+ };
90
+ function uncommentTransportEnv(text2, prefix, transport) {
91
+ const token = TRANSPORT_ENV_TOKEN[transport];
92
+ if (!token) return text2;
93
+ return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
94
+ }
77
95
  async function rewriteRootPackageJson(targetDir, opts) {
78
96
  const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
79
97
  const raw = await (0, import_promises.readFile)(pkgPath, "utf8");
@@ -82,9 +100,10 @@ async function rewriteRootPackageJson(targetDir, opts) {
82
100
  pkg["version"] = "0.0.1";
83
101
  pkg["private"] = true;
84
102
  delete pkg.description;
85
- if (opts.transport === "nats") {
103
+ const transportDeps = TRANSPORT_DEPS[opts.transport];
104
+ if (transportDeps) {
86
105
  const deps = pkg["dependencies"] ??= {};
87
- deps["nats"] = "^2.29.3";
106
+ Object.assign(deps, transportDeps);
88
107
  }
89
108
  if (opts.packageManager !== "yarn") {
90
109
  delete pkg.packageManager;
@@ -111,9 +130,7 @@ async function writeAuthEnv(targetDir, opts) {
111
130
  const envExample = (0, import_node_path.join)(targetDir, "apps/microservices/auth/.env.example");
112
131
  const env = await (0, import_promises.readFile)(envExample, "utf8");
113
132
  let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
114
- if (opts.transport !== "tcp") {
115
- next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1");
116
- }
133
+ next = uncommentTransportEnv(next, "AUTH", opts.transport);
117
134
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/auth/.env"), next);
118
135
  }
119
136
  async function writeUploadEnv(targetDir, opts) {
@@ -121,9 +138,7 @@ async function writeUploadEnv(targetDir, opts) {
121
138
  const envExample = (0, import_node_path.join)(targetDir, "apps/microservices/upload/.env.example");
122
139
  const env = await (0, import_promises.readFile)(envExample, "utf8");
123
140
  let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
124
- if (opts.transport !== "tcp") {
125
- next = next.replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
126
- }
141
+ next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
127
142
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/upload/.env"), next);
128
143
  }
129
144
  async function writeNotesEnv(targetDir, opts) {
@@ -132,9 +147,7 @@ async function writeNotesEnv(targetDir, opts) {
132
147
  try {
133
148
  const env = await (0, import_promises.readFile)(envExample, "utf8");
134
149
  let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
135
- if (opts.transport !== "tcp") {
136
- next = next.replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1");
137
- }
150
+ next = uncommentTransportEnv(next, "NOTES", opts.transport);
138
151
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/notes/.env"), next);
139
152
  } catch {
140
153
  }
@@ -143,8 +156,8 @@ async function writeGatewayEnv(targetDir, opts) {
143
156
  const envExample = (0, import_node_path.join)(targetDir, "apps/api/.env.example");
144
157
  const env = await (0, import_promises.readFile)(envExample, "utf8");
145
158
  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}`);
146
- if (opts.transport !== "tcp") {
147
- 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");
159
+ for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
160
+ next = uncommentTransportEnv(next, prefix, opts.transport);
148
161
  }
149
162
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
150
163
  }
@@ -182,9 +195,7 @@ async function writePaymentEnv(targetDir, opts) {
182
195
  try {
183
196
  const env = await (0, import_promises.readFile)(envExample, "utf8");
184
197
  let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
185
- if (opts.transport !== "tcp") {
186
- next = next.replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
187
- }
198
+ next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
188
199
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/payment/.env"), next);
189
200
  } catch {
190
201
  }
@@ -285,21 +296,16 @@ async function removeNotesStack(targetDir) {
285
296
  const siderPath = (0, import_node_path.join)(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
286
297
  try {
287
298
  const src = await (0, import_promises.readFile)(siderPath, "utf8");
288
- const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
299
+ const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
289
300
  "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
290
301
  "const selectedKey = pathname.includes('/profile')"
291
302
  ).replace(
292
- `
293
- {
294
- key: 'notes',
295
- icon: <FileTextOutlined />,
296
- label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
297
- },`,
303
+ /\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
298
304
  ""
299
305
  ).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
300
- /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/ListItemButton>/,
306
+ /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
301
307
  ""
302
- ).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
308
+ ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
303
309
  await (0, import_promises.writeFile)(siderPath, next);
304
310
  } catch {
305
311
  }
@@ -683,7 +689,7 @@ Apache-2.0
683
689
 
684
690
  - **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
685
691
  - **No code without approval**: Propose changes first, wait for go-ahead.
686
- - **\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.
692
+ - **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.
687
693
  - **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
688
694
  - **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
689
695
 
@@ -980,7 +986,10 @@ Re-run with @latest to refresh:
980
986
  options: [
981
987
  { value: "tcp", label: "TCP (default, no broker required)" },
982
988
  { value: "redis", label: "Redis" },
983
- { value: "nats", label: "NATS" }
989
+ { value: "nats", label: "NATS" },
990
+ { value: "mqtt", label: "MQTT" },
991
+ { value: "rmq", label: "RabbitMQ" },
992
+ { value: "kafka", label: "Kafka" }
984
993
  ],
985
994
  initialValue: "tcp"
986
995
  });
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ type PaymentProvider = 'paypal' | 'none';
5
5
  type JobsProvider = 'bullmq' | 'none';
6
6
  type ExampleMode = 'notes' | 'none';
7
7
  type UiLibrary = 'shadcn' | 'antd' | 'mui';
8
- type MsTransport = 'tcp' | 'redis' | 'nats';
8
+ type MsTransport = 'tcp' | 'redis' | 'nats' | 'mqtt' | 'rmq' | 'kafka';
9
9
  type PackageManager = 'yarn' | 'npm' | 'pnpm';
10
10
  /**
11
11
  * Returns the correct invocation for a package.json script.
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ type PaymentProvider = 'paypal' | 'none';
5
5
  type JobsProvider = 'bullmq' | 'none';
6
6
  type ExampleMode = 'notes' | 'none';
7
7
  type UiLibrary = 'shadcn' | 'antd' | 'mui';
8
- type MsTransport = 'tcp' | 'redis' | 'nats';
8
+ type MsTransport = 'tcp' | 'redis' | 'nats' | 'mqtt' | 'rmq' | 'kafka';
9
9
  type PackageManager = 'yarn' | 'npm' | 'pnpm';
10
10
  /**
11
11
  * Returns the correct invocation for a package.json script.
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,9 +58,10 @@ 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;
@@ -69,9 +88,7 @@ async function writeAuthEnv(targetDir, opts) {
69
88
  const envExample = join(targetDir, "apps/microservices/auth/.env.example");
70
89
  const env = await readFile(envExample, "utf8");
71
90
  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
- }
91
+ next = uncommentTransportEnv(next, "AUTH", opts.transport);
75
92
  await writeFile(join(targetDir, "apps/microservices/auth/.env"), next);
76
93
  }
77
94
  async function writeUploadEnv(targetDir, opts) {
@@ -79,9 +96,7 @@ async function writeUploadEnv(targetDir, opts) {
79
96
  const envExample = join(targetDir, "apps/microservices/upload/.env.example");
80
97
  const env = await readFile(envExample, "utf8");
81
98
  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
- }
99
+ next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
85
100
  await writeFile(join(targetDir, "apps/microservices/upload/.env"), next);
86
101
  }
87
102
  async function writeNotesEnv(targetDir, opts) {
@@ -90,9 +105,7 @@ async function writeNotesEnv(targetDir, opts) {
90
105
  try {
91
106
  const env = await readFile(envExample, "utf8");
92
107
  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
- }
108
+ next = uncommentTransportEnv(next, "NOTES", opts.transport);
96
109
  await writeFile(join(targetDir, "apps/microservices/notes/.env"), next);
97
110
  } catch {
98
111
  }
@@ -101,8 +114,8 @@ async function writeGatewayEnv(targetDir, opts) {
101
114
  const envExample = join(targetDir, "apps/api/.env.example");
102
115
  const env = await readFile(envExample, "utf8");
103
116
  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");
117
+ for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
118
+ next = uncommentTransportEnv(next, prefix, opts.transport);
106
119
  }
107
120
  await writeFile(join(targetDir, "apps/api/.env"), next);
108
121
  }
@@ -140,9 +153,7 @@ async function writePaymentEnv(targetDir, opts) {
140
153
  try {
141
154
  const env = await readFile(envExample, "utf8");
142
155
  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
- }
156
+ next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
146
157
  await writeFile(join(targetDir, "apps/microservices/payment/.env"), next);
147
158
  } catch {
148
159
  }
@@ -243,21 +254,16 @@ async function removeNotesStack(targetDir) {
243
254
  const siderPath = join(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
244
255
  try {
245
256
  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(
257
+ const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
247
258
  "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
248
259
  "const selectedKey = pathname.includes('/profile')"
249
260
  ).replace(
250
- `
251
- {
252
- key: 'notes',
253
- icon: <FileTextOutlined />,
254
- label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
255
- },`,
261
+ /\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
256
262
  ""
257
263
  ).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>/,
264
+ /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
259
265
  ""
260
- ).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
266
+ ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
261
267
  await writeFile(siderPath, next);
262
268
  } catch {
263
269
  }
@@ -641,7 +647,7 @@ Apache-2.0
641
647
 
642
648
  - **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
643
649
  - **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.
650
+ - **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
651
  - **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
646
652
  - **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
647
653
 
@@ -938,7 +944,10 @@ Re-run with @latest to refresh:
938
944
  options: [
939
945
  { value: "tcp", label: "TCP (default, no broker required)" },
940
946
  { value: "redis", label: "Redis" },
941
- { value: "nats", label: "NATS" }
947
+ { value: "nats", label: "NATS" },
948
+ { value: "mqtt", label: "MQTT" },
949
+ { value: "rmq", label: "RabbitMQ" },
950
+ { value: "kafka", label: "Kafka" }
942
951
  ],
943
952
  initialValue: "tcp"
944
953
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idevconn/create-icore",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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",
@@ -56,6 +56,8 @@
56
56
  "test:watch": "vitest",
57
57
  "lint": "eslint src",
58
58
  "typecheck": "tsc --noEmit",
59
+ "smoke": "npm run build && node scripts/snapshot-templates.mjs && node scripts/smoke-scaffold.mjs --mode=link --projects=shared,firebase-admin,auth,upload,notes,api",
60
+ "smoke:run": "npm run build && node scripts/snapshot-templates.mjs && node scripts/smoke-scaffold.mjs --mode=install --run --projects=shared,firebase-admin,auth,upload,notes,api --services=api,auth,upload,notes",
59
61
  "prepublishOnly": "node scripts/snapshot-templates.mjs && npm run typecheck && npm run test && npm run build"
60
62
  },
61
63
  "dependencies": {
@@ -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
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -4,6 +4,11 @@ UPLOAD_HOST=127.0.0.1
4
4
  UPLOAD_PORT=4002
5
5
  # UPLOAD_REDIS_URL=redis://localhost:6379
6
6
  # UPLOAD_NATS_URL=nats://localhost:4222
7
+ # UPLOAD_MQTT_URL=mqtt://localhost:1883
8
+ # UPLOAD_RMQ_URL=amqp://localhost:5672
9
+ # UPLOAD_RMQ_QUEUE=upload_queue
10
+ # UPLOAD_KAFKA_BROKERS=localhost:9092
11
+ # UPLOAD_KAFKA_CLIENT_ID=upload
7
12
 
8
13
  # Which concrete StorageStrategy to instantiate
9
14
  STORAGE_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
  }