@idevconn/create-icore 0.6.3 → 0.7.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 +384 -276
  2. package/dist/index.cjs +385 -277
  3. package/dist/index.d.cts +3 -3
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +382 -274
  6. package/package.json +1 -1
  7. package/templates/.yarn/releases/yarn-4.16.0.cjs +944 -0
  8. package/templates/.yarnrc.yml +1 -1
  9. package/templates/apps/api/src/app/storage/storage.controller.ts +28 -0
  10. package/templates/apps/microservices/auth/src/app/app.module.ts +20 -2
  11. package/templates/apps/microservices/notes/src/app/app.module.ts +17 -2
  12. package/templates/apps/microservices/upload/src/app/app.module.ts +17 -2
  13. package/templates/apps/microservices/upload/src/app/storage.controller.ts +7 -0
  14. package/templates/apps/templates/client-antd/src/components/auth/AuthBrandPanel.tsx +59 -0
  15. package/templates/apps/templates/client-antd/src/components/auth/CheckEmailScreen.tsx +28 -0
  16. package/templates/apps/templates/client-antd/src/components/auth/LoginForm.tsx +116 -0
  17. package/templates/apps/templates/client-antd/src/components/auth/MagicLinkForm.tsx +95 -0
  18. package/templates/apps/templates/client-antd/src/components/auth/RegisterForm.tsx +98 -0
  19. package/templates/apps/templates/client-antd/src/globals.less +6 -0
  20. package/templates/apps/templates/client-antd/src/main.tsx +1 -1
  21. package/templates/apps/templates/client-antd/src/routes/login.tsx +45 -181
  22. package/templates/apps/templates/client-mui/src/components/auth/AuthBrandPanel.tsx +59 -0
  23. package/templates/apps/templates/client-mui/src/components/auth/CheckEmailScreen.tsx +28 -0
  24. package/templates/apps/templates/client-mui/src/components/auth/LoginForm.tsx +141 -0
  25. package/templates/apps/templates/client-mui/src/components/auth/MagicLinkForm.tsx +106 -0
  26. package/templates/apps/templates/client-mui/src/components/auth/RegisterForm.tsx +113 -0
  27. package/templates/apps/templates/client-mui/src/main.tsx +1 -1
  28. package/templates/apps/templates/client-mui/src/routes/login.tsx +50 -186
  29. package/templates/apps/templates/client-shadcn/src/components/auth/AuthBrandPanel.tsx +52 -0
  30. package/templates/apps/templates/client-shadcn/src/components/auth/CheckEmailScreen.tsx +29 -0
  31. package/templates/apps/templates/client-shadcn/src/components/auth/LoginForm.tsx +161 -0
  32. package/templates/apps/templates/client-shadcn/src/components/auth/MagicLinkForm.tsx +110 -0
  33. package/templates/apps/templates/client-shadcn/src/components/auth/RegisterForm.tsx +107 -0
  34. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutHeader.tsx +31 -10
  35. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +22 -27
  36. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  37. package/templates/apps/templates/client-shadcn/src/globals.css +39 -13
  38. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  39. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +55 -165
  40. package/templates/libs/auth-strategies/mongodb/CHANGELOG.md +8 -0
  41. package/templates/libs/auth-strategies/mongodb/README.md +11 -0
  42. package/templates/libs/auth-strategies/mongodb/eslint.config.mjs +19 -0
  43. package/templates/libs/auth-strategies/mongodb/jest.config.cts +10 -0
  44. package/templates/libs/auth-strategies/mongodb/package.json +16 -0
  45. package/templates/libs/auth-strategies/mongodb/project.json +19 -0
  46. package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
  47. package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.strategy.unit.test.ts +42 -0
  48. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.spec.ts +7 -0
  49. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.ts +3 -0
  50. package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.strategy.ts +188 -0
  51. package/templates/libs/auth-strategies/mongodb/tsconfig.json +23 -0
  52. package/templates/libs/auth-strategies/mongodb/tsconfig.lib.json +10 -0
  53. package/templates/libs/auth-strategies/mongodb/tsconfig.spec.json +16 -0
  54. package/templates/libs/db-strategies/mongodb/CHANGELOG.md +7 -0
  55. package/templates/libs/db-strategies/mongodb/README.md +11 -0
  56. package/templates/libs/db-strategies/mongodb/eslint.config.mjs +19 -0
  57. package/templates/libs/db-strategies/mongodb/jest.config.cts +10 -0
  58. package/templates/libs/db-strategies/mongodb/package.json +14 -0
  59. package/templates/libs/db-strategies/mongodb/project.json +19 -0
  60. package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
  61. package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.strategy.unit.test.ts +38 -0
  62. package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.strategy.ts +108 -0
  63. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.spec.ts +7 -0
  64. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.ts +3 -0
  65. package/templates/libs/db-strategies/mongodb/tsconfig.json +23 -0
  66. package/templates/libs/db-strategies/mongodb/tsconfig.lib.json +10 -0
  67. package/templates/libs/db-strategies/mongodb/tsconfig.spec.json +16 -0
  68. package/templates/libs/shared/src/strategies/storage.ts +3 -0
  69. package/templates/libs/storage-strategies/mongodb/CHANGELOG.md +8 -0
  70. package/templates/libs/storage-strategies/mongodb/README.md +11 -0
  71. package/templates/libs/storage-strategies/mongodb/eslint.config.mjs +19 -0
  72. package/templates/libs/storage-strategies/mongodb/jest.config.cts +10 -0
  73. package/templates/libs/storage-strategies/mongodb/package.json +14 -0
  74. package/templates/libs/storage-strategies/mongodb/project.json +19 -0
  75. package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
  76. package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.strategy.unit.test.ts +38 -0
  77. package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.strategy.ts +93 -0
  78. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.spec.ts +7 -0
  79. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.ts +3 -0
  80. package/templates/libs/storage-strategies/mongodb/tsconfig.json +23 -0
  81. package/templates/libs/storage-strategies/mongodb/tsconfig.lib.json +10 -0
  82. package/templates/libs/storage-strategies/mongodb/tsconfig.spec.json +16 -0
  83. package/templates/libs/template-shared/src/lib/i18n/keys.ts +216 -56
  84. package/templates/libs/template-shared/src/lib/stores/theme.store.ts +1 -6
  85. package/templates/libs/upload-client/src/lib/upload-client.service.ts +7 -0
  86. package/templates/tsconfig.base.json +4 -1
  87. package/templates/.yarn/releases/yarn-4.15.0.cjs +0 -940
package/dist/index.js CHANGED
@@ -4,34 +4,14 @@ function pmRun(pm, script) {
4
4
  }
5
5
 
6
6
  // src/lib/scaffold.ts
7
- import { copyFile, mkdir, readdir, readFile, stat, writeFile, rm } from "fs/promises";
7
+ import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile4, stat, writeFile as writeFile4, rm as rm2 } from "fs/promises";
8
8
  import { readFileSync } from "fs";
9
- import { join } from "path";
9
+ import { join as join4 } from "path";
10
10
  import { spawnSync } from "child_process";
11
- var IGNORE_TOP = /* @__PURE__ */ new Set([
12
- ".git",
13
- "node_modules",
14
- ".yarn/cache",
15
- ".yarn/unplugged",
16
- ".yarn/install-state.gz",
17
- ".nx",
18
- "dist",
19
- "tmp",
20
- "coverage",
21
- ".idea",
22
- ".vscode"
23
- ]);
24
- async function copyTree(src, dest) {
25
- await mkdir(dest, { recursive: true });
26
- const entries = await readdir(src, { withFileTypes: true });
27
- for (const entry of entries) {
28
- if (IGNORE_TOP.has(entry.name)) continue;
29
- const s = join(src, entry.name);
30
- const d = join(dest, entry.name);
31
- if (entry.isDirectory()) await copyTree(s, d);
32
- else if (entry.isFile()) await copyFile(s, d);
33
- }
34
- }
11
+
12
+ // src/lib/scaffold-env.ts
13
+ import { readFile, writeFile } from "fs/promises";
14
+ import { join } from "path";
35
15
  var TRANSPORT_ENV_TOKEN = {
36
16
  redis: "REDIS",
37
17
  nats: "NATS",
@@ -45,11 +25,28 @@ var TRANSPORT_DEPS = {
45
25
  rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
46
26
  kafka: { kafkajs: "^2.2.4" }
47
27
  };
28
+ var MONGODB_DEPS = {
29
+ mongoose: "^9.6.3",
30
+ "@nestjs/mongoose": "^11.0.4",
31
+ bcrypt: "^6.0.0",
32
+ jsonwebtoken: "^9.0.3"
33
+ };
48
34
  function uncommentTransportEnv(text2, prefix, transport) {
49
35
  const token = TRANSPORT_ENV_TOKEN[transport];
50
36
  if (!token) return text2;
51
37
  return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
52
38
  }
39
+ async function stripGatewayTransport(targetDir, prefix) {
40
+ const gatewayEnv = join(targetDir, "apps/api/.env");
41
+ try {
42
+ const env = await readFile(gatewayEnv, "utf8");
43
+ const next = env.split("\n").filter(
44
+ (line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
45
+ ).join("\n");
46
+ await writeFile(gatewayEnv, next);
47
+ } catch {
48
+ }
49
+ }
53
50
  async function rewriteRootPackageJson(targetDir, opts) {
54
51
  const pkgPath = join(targetDir, "package.json");
55
52
  const raw = await readFile(pkgPath, "utf8");
@@ -63,6 +60,10 @@ async function rewriteRootPackageJson(targetDir, opts) {
63
60
  const deps = pkg["dependencies"] ??= {};
64
61
  Object.assign(deps, transportDeps);
65
62
  }
63
+ if (opts.authProvider === "mongodb" || opts.dbProvider === "mongodb" || opts.upload === "mongodb") {
64
+ const deps = pkg["dependencies"] ??= {};
65
+ Object.assign(deps, MONGODB_DEPS);
66
+ }
66
67
  if (opts.packageManager !== "yarn") {
67
68
  delete pkg.packageManager;
68
69
  } else {
@@ -83,6 +84,9 @@ async function writeAuthEnv(targetDir, opts) {
83
84
  const env = await readFile(envExample, "utf8");
84
85
  let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
85
86
  next = uncommentTransportEnv(next, "AUTH", opts.transport);
87
+ if (opts.authProvider === "mongodb") {
88
+ next += "\nMONGODB_URI=mongodb://localhost:27017/icore-auth\nJWT_SECRET=change-me-in-production\n";
89
+ }
86
90
  await writeFile(join(targetDir, "apps/microservices/auth/.env"), next);
87
91
  }
88
92
  async function writeUploadEnv(targetDir, opts) {
@@ -91,6 +95,9 @@ async function writeUploadEnv(targetDir, opts) {
91
95
  const env = await readFile(envExample, "utf8");
92
96
  let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
93
97
  next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
98
+ if (opts.upload === "mongodb") {
99
+ next += "\nMONGODB_URI=mongodb://localhost:27017/icore-upload\n";
100
+ }
94
101
  await writeFile(join(targetDir, "apps/microservices/upload/.env"), next);
95
102
  }
96
103
  async function writeNotesEnv(targetDir, opts) {
@@ -120,18 +127,11 @@ async function writeRootEnv(targetDir, opts) {
120
127
  `DB_PROVIDER=${opts.dbProvider}`,
121
128
  ``
122
129
  ];
123
- await writeFile(join(targetDir, ".env"), lines.join("\n"));
124
- }
125
- async function stripGatewayTransport(targetDir, prefix) {
126
- const gatewayEnv = join(targetDir, "apps/api/.env");
127
- try {
128
- const env = await readFile(gatewayEnv, "utf8");
129
- const next = env.split("\n").filter(
130
- (line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
131
- ).join("\n");
132
- await writeFile(gatewayEnv, next);
133
- } catch {
130
+ if (opts.dbProvider === "mongodb") {
131
+ lines.push(`MONGODB_URI=mongodb://localhost:27017/icore-data`);
132
+ lines.push(``);
134
133
  }
134
+ await writeFile(join(targetDir, ".env"), lines.join("\n"));
135
135
  }
136
136
  async function writeClientEnv(targetDir) {
137
137
  const envExample = join(targetDir, "apps/client/.env.example");
@@ -152,15 +152,37 @@ async function writePaymentEnv(targetDir, opts) {
152
152
  } catch {
153
153
  }
154
154
  }
155
+
156
+ // src/lib/scaffold-strip.ts
157
+ import { readFile as readFile2, writeFile as writeFile2, rm } from "fs/promises";
158
+ import { join as join2 } from "path";
155
159
  async function stripDeps(pkgPath, names) {
156
160
  try {
157
- const raw = await readFile(pkgPath, "utf8");
161
+ const raw = await readFile2(pkgPath, "utf8");
158
162
  const pkg = JSON.parse(raw);
159
163
  for (const n of names) {
160
164
  if (pkg.dependencies) delete pkg.dependencies[n];
161
165
  if (pkg.devDependencies) delete pkg.devDependencies[n];
162
166
  }
163
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
167
+ await writeFile2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
168
+ } catch {
169
+ }
170
+ }
171
+ async function stripTsconfigPath(targetDir, alias) {
172
+ const tsconfigPath = join2(targetDir, "tsconfig.base.json");
173
+ try {
174
+ const src = await readFile2(tsconfigPath, "utf8");
175
+ const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
176
+ const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
177
+ if (pretty !== src) {
178
+ await writeFile2(tsconfigPath, pretty);
179
+ return;
180
+ }
181
+ const parsed = JSON.parse(src);
182
+ if (parsed.compilerOptions?.paths) {
183
+ delete parsed.compilerOptions.paths[alias];
184
+ }
185
+ await writeFile2(tsconfigPath, JSON.stringify(parsed));
164
186
  } catch {
165
187
  }
166
188
  }
@@ -172,25 +194,25 @@ async function removeJobsStack(targetDir) {
172
194
  "Dockerfile.ms-jobs"
173
195
  ];
174
196
  for (const p2 of paths) {
175
- await rm(join(targetDir, p2), { recursive: true, force: true });
197
+ await rm(join2(targetDir, p2), { recursive: true, force: true });
176
198
  }
177
- const appModulePath = join(targetDir, "apps/api/src/app/app.module.ts");
199
+ const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
178
200
  try {
179
- const appModule = await readFile(appModulePath, "utf8");
201
+ const appModule = await readFile2(appModulePath, "utf8");
180
202
  const next = appModule.replace(/^import \{ AdminModule \} from '\.\/admin\/admin\.module';\n/m, "").replace(/,\s*AdminModule/g, "");
181
- await writeFile(appModulePath, next);
203
+ await writeFile2(appModulePath, next);
182
204
  } catch {
183
205
  }
184
- await stripDeps(join(targetDir, "apps/api/package.json"), [
206
+ await stripDeps(join2(targetDir, "apps/api/package.json"), [
185
207
  "@icore/jobs-client",
186
208
  "@bull-board/api",
187
209
  "@bull-board/express"
188
210
  ]);
189
- const composePath = join(targetDir, "docker-compose.yml");
211
+ const composePath = join2(targetDir, "docker-compose.yml");
190
212
  try {
191
- const compose = await readFile(composePath, "utf8");
213
+ const compose = await readFile2(composePath, "utf8");
192
214
  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, "");
193
- await writeFile(composePath, next);
215
+ await writeFile2(composePath, next);
194
216
  } catch {
195
217
  }
196
218
  }
@@ -202,25 +224,25 @@ async function removePaymentStack(targetDir) {
202
224
  "apps/api/src/app/payment"
203
225
  ];
204
226
  for (const p2 of paths) {
205
- await rm(join(targetDir, p2), { recursive: true, force: true });
227
+ await rm(join2(targetDir, p2), { recursive: true, force: true });
206
228
  }
207
- const appModulePath = join(targetDir, "apps/api/src/app/app.module.ts");
229
+ const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
208
230
  try {
209
- const appModule = await readFile(appModulePath, "utf8");
231
+ const appModule = await readFile2(appModulePath, "utf8");
210
232
  const next = appModule.replace(/^import \{ PaymentModule \} from '\.\/payment\/payment\.module';\n/m, "").replace(/,\s*PaymentModule/g, "");
211
- await writeFile(appModulePath, next);
233
+ await writeFile2(appModulePath, next);
212
234
  } catch {
213
235
  }
214
- await stripDeps(join(targetDir, "apps/api/package.json"), [
236
+ await stripDeps(join2(targetDir, "apps/api/package.json"), [
215
237
  "@icore/payment-client",
216
238
  "@idevconn/payment"
217
239
  ]);
218
240
  await stripGatewayTransport(targetDir, "PAYMENT");
219
- const mainTsPath = join(targetDir, "apps/api/src/main.ts");
241
+ const mainTsPath = join2(targetDir, "apps/api/src/main.ts");
220
242
  try {
221
- const src = await readFile(mainTsPath, "utf8");
243
+ const src = await readFile2(mainTsPath, "utf8");
222
244
  const next = src.replace(/\n\s*\{ name: 'payment', prefix: 'PAYMENT' \},/, "");
223
- await writeFile(mainTsPath, next);
245
+ await writeFile2(mainTsPath, next);
224
246
  } catch {
225
247
  }
226
248
  }
@@ -232,39 +254,39 @@ async function removeNotesStack(targetDir) {
232
254
  "apps/api/src/app/notes",
233
255
  "apps/client/src/components/notes"
234
256
  ]) {
235
- await rm(join(targetDir, p2), { recursive: true, force: true });
257
+ await rm(join2(targetDir, p2), { recursive: true, force: true });
236
258
  }
237
- await rm(join(targetDir, "apps/client/src/routes/_dashboard/notes.tsx"), { force: true });
238
- await rm(join(targetDir, "apps/client/src/queries/notes.ts"), { force: true });
239
- const appModulePath = join(targetDir, "apps/api/src/app/app.module.ts");
259
+ await rm(join2(targetDir, "apps/client/src/routes/_dashboard/notes.tsx"), { force: true });
260
+ await rm(join2(targetDir, "apps/client/src/queries/notes.ts"), { force: true });
261
+ const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
240
262
  try {
241
- const src = await readFile(appModulePath, "utf8");
263
+ const src = await readFile2(appModulePath, "utf8");
242
264
  const next = src.replace(/^import \{ NotesModule \} from '\.\/notes\/notes\.module';\n/m, "").replace(/,\s*NotesModule/g, "");
243
- await writeFile(appModulePath, next);
265
+ await writeFile2(appModulePath, next);
244
266
  } catch {
245
267
  }
246
- await stripDeps(join(targetDir, "apps/api/package.json"), [
268
+ await stripDeps(join2(targetDir, "apps/api/package.json"), [
247
269
  "@icore/notes-client",
248
270
  "@casl/ability"
249
271
  ]);
250
272
  await stripGatewayTransport(targetDir, "NOTES");
251
- const mainTsPath = join(targetDir, "apps/api/src/main.ts");
273
+ const mainTsPath = join2(targetDir, "apps/api/src/main.ts");
252
274
  try {
253
- const src = await readFile(mainTsPath, "utf8");
275
+ const src = await readFile2(mainTsPath, "utf8");
254
276
  const next = src.replace(/\n\s*\{ name: 'notes', prefix: 'NOTES' \},/, "");
255
- await writeFile(mainTsPath, next);
277
+ await writeFile2(mainTsPath, next);
256
278
  } catch {
257
279
  }
258
- const tsconfigPath = join(targetDir, "tsconfig.base.json");
280
+ const tsconfigPath = join2(targetDir, "tsconfig.base.json");
259
281
  try {
260
- const src = await readFile(tsconfigPath, "utf8");
282
+ const src = await readFile2(tsconfigPath, "utf8");
261
283
  const next = src.replace(/^\s*"@icore\/notes-client": \[[^\]]*\],?\n/m, "");
262
- await writeFile(tsconfigPath, next);
284
+ await writeFile2(tsconfigPath, next);
263
285
  } catch {
264
286
  }
265
- const siderPath = join(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
287
+ const siderPath = join2(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
266
288
  try {
267
- const src = await readFile(siderPath, "utf8");
289
+ const src = await readFile2(siderPath, "utf8");
268
290
  const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
269
291
  "const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
270
292
  "const selectedKey = pathname.includes('/profile')"
@@ -275,159 +297,213 @@ async function removeNotesStack(targetDir) {
275
297
  /\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
276
298
  ""
277
299
  ).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
278
- await writeFile(siderPath, next);
300
+ await writeFile2(siderPath, next);
279
301
  } catch {
280
302
  }
281
- const keysPath = join(targetDir, "libs/template-shared/src/lib/i18n/keys.ts");
303
+ const keysPath = join2(targetDir, "libs/template-shared/src/lib/i18n/keys.ts");
282
304
  try {
283
- const src = await readFile(keysPath, "utf8");
305
+ const src = await readFile2(keysPath, "utf8");
284
306
  const next = src.replace(/^\s{4}notes: \{\n(?:\s+.*\n)*?\s{4}\},\n/m, "");
285
- await writeFile(keysPath, next);
286
- } catch {
287
- }
288
- }
289
- async function stripTsconfigPath(targetDir, alias) {
290
- const tsconfigPath = join(targetDir, "tsconfig.base.json");
291
- try {
292
- const src = await readFile(tsconfigPath, "utf8");
293
- const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
294
- const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
295
- if (pretty !== src) {
296
- await writeFile(tsconfigPath, pretty);
297
- return;
298
- }
299
- const parsed = JSON.parse(src);
300
- if (parsed.compilerOptions?.paths) {
301
- delete parsed.compilerOptions.paths[alias];
302
- }
303
- await writeFile(tsconfigPath, JSON.stringify(parsed));
307
+ await writeFile2(keysPath, next);
304
308
  } catch {
305
309
  }
306
310
  }
307
311
  async function removeUnusedAuthStrategies(targetDir, authProvider) {
308
- const modulePath = join(targetDir, "apps/microservices/auth/src/app/app.module.ts");
309
- const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
312
+ const modulePath = join2(targetDir, "apps/microservices/auth/src/app/app.module.ts");
313
+ const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbAuth\(connection, cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
310
314
  if (authProvider === "supabase") {
311
- await rm(join(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
312
- await stripDeps(join(targetDir, "apps/microservices/auth/package.json"), [
315
+ await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
316
+ await rm(join2(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
317
+ await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
313
318
  "@icore/auth-firebase",
314
- "@icore/firebase-admin"
319
+ "@icore/firebase-admin",
320
+ "@icore/auth-mongodb"
315
321
  ]);
316
322
  await stripTsconfigPath(targetDir, "@icore/auth-firebase");
323
+ await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
317
324
  try {
318
- const src = await readFile(modulePath, "utf8");
319
- const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/m, "").replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/m, "").replace(AUTH_BRANCH, "return makeSupabaseAuth(cfg);");
320
- await writeFile(modulePath, next);
325
+ const src = await readFile2(modulePath, "utf8");
326
+ 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(
327
+ /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
328
+ ""
329
+ ).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\(\)/, "");
330
+ await writeFile2(modulePath, next);
321
331
  } catch {
322
332
  }
323
333
  }
324
334
  if (authProvider === "firebase") {
325
- await rm(join(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
326
- await stripDeps(join(targetDir, "apps/microservices/auth/package.json"), [
327
- "@icore/auth-supabase"
335
+ await rm(join2(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
336
+ await rm(join2(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
337
+ await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
338
+ "@icore/auth-supabase",
339
+ "@icore/auth-mongodb"
328
340
  ]);
329
341
  await stripTsconfigPath(targetDir, "@icore/auth-supabase");
342
+ await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
330
343
  try {
331
- const src = await readFile(modulePath, "utf8");
332
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/m, "").replace(AUTH_BRANCH, "return makeFirebaseAuth(cfg);");
333
- await writeFile(modulePath, next);
344
+ const src = await readFile2(modulePath, "utf8");
345
+ 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(
346
+ /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
347
+ ""
348
+ ).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\(\)/, "");
349
+ await writeFile2(modulePath, next);
350
+ } catch {
351
+ }
352
+ }
353
+ if (authProvider === "mongodb") {
354
+ await rm(join2(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
355
+ await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
356
+ await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
357
+ "@icore/auth-supabase",
358
+ "@icore/auth-firebase",
359
+ "@icore/firebase-admin"
360
+ ]);
361
+ await stripTsconfigPath(targetDir, "@icore/auth-supabase");
362
+ await stripTsconfigPath(targetDir, "@icore/auth-firebase");
363
+ try {
364
+ const src = await readFile2(modulePath, "utf8");
365
+ 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);");
366
+ await writeFile2(modulePath, next);
334
367
  } catch {
335
368
  }
336
369
  }
337
370
  }
338
371
  async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
339
372
  if (uploadProvider === "none") return;
340
- const modulePath = join(targetDir, "apps/microservices/upload/src/app/app.module.ts");
373
+ const modulePath = join2(targetDir, "apps/microservices/upload/src/app/app.module.ts");
374
+ 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;
341
375
  if (uploadProvider !== "firebase") {
342
- await rm(join(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
343
- await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
376
+ await rm(join2(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
377
+ await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
344
378
  "@icore/storage-firebase",
345
379
  "@icore/firebase-admin"
346
380
  ]);
347
381
  await stripTsconfigPath(targetDir, "@icore/storage-firebase");
348
382
  }
349
383
  if (uploadProvider !== "cloudinary") {
350
- await rm(join(targetDir, "libs/storage-strategies/cloudinary"), {
384
+ await rm(join2(targetDir, "libs/storage-strategies/cloudinary"), {
351
385
  recursive: true,
352
386
  force: true
353
387
  });
354
- await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
388
+ await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
355
389
  "@icore/storage-cloudinary"
356
390
  ]);
357
391
  await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
358
392
  }
359
393
  if (uploadProvider !== "supabase") {
360
- await rm(join(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
361
- await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
394
+ await rm(join2(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
395
+ await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
362
396
  "@icore/storage-supabase"
363
397
  ]);
364
398
  await stripTsconfigPath(targetDir, "@icore/storage-supabase");
365
399
  }
400
+ if (uploadProvider !== "mongodb") {
401
+ await rm(join2(targetDir, "libs/storage-strategies/mongodb"), { recursive: true, force: true });
402
+ await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
403
+ "@icore/storage-mongodb"
404
+ ]);
405
+ await stripTsconfigPath(targetDir, "@icore/storage-mongodb");
406
+ }
366
407
  try {
367
- let src = await readFile(modulePath, "utf8");
408
+ let src = await readFile2(modulePath, "utf8");
368
409
  if (uploadProvider !== "firebase") {
369
- src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(
370
- /^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
410
+ src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(
411
+ /^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/gm,
371
412
  ""
372
- ).replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/m, "");
413
+ ).replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/gm, "");
373
414
  }
374
415
  if (uploadProvider !== "cloudinary") {
375
- src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
376
- /^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
416
+ src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/gm, "").replace(
417
+ /^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/gm,
377
418
  ""
378
- ).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/m, "");
419
+ ).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/gm, "");
379
420
  }
380
421
  if (uploadProvider !== "supabase") {
381
- src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
382
- /^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
422
+ src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(
423
+ /^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/gm,
424
+ ""
425
+ ).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/gm, "");
426
+ }
427
+ if (uploadProvider !== "mongodb") {
428
+ src = src.replace(
429
+ /^import \{[^}]*MongoDbStorageStrategy[^}]*\} from '@icore\/storage-mongodb';\n/gm,
430
+ ""
431
+ ).replace(
432
+ /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
383
433
  ""
384
- ).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/m, "");
434
+ ).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, "");
385
435
  }
386
- const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
387
- const chosenReturn = `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
436
+ const chosenReturn = uploadProvider === "mongodb" ? `return makeMongoDbStorage(connection);` : `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
388
437
  src = src.replace(STORAGE_BRANCH, chosenReturn);
389
- await writeFile(modulePath, src);
438
+ if (uploadProvider !== "mongodb") {
439
+ src = src.replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
440
+ }
441
+ await writeFile2(modulePath, src);
390
442
  } catch {
391
443
  }
392
444
  }
393
445
  async function removeUnusedDbStrategies(targetDir, dbProvider) {
394
- const modulePath = join(targetDir, "apps/microservices/notes/src/app/app.module.ts");
446
+ const modulePath = join2(targetDir, "apps/microservices/notes/src/app/app.module.ts");
447
+ const DB_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDb\(connection\);\n\s*return makeFirestoreDB\(cfg\);/m;
395
448
  if (dbProvider === "supabase") {
396
- await rm(join(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
397
- await stripDeps(join(targetDir, "apps/microservices/notes/package.json"), [
449
+ await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
450
+ await rm(join2(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
451
+ await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
398
452
  "@icore/db-firestore",
399
- "@icore/firebase-admin"
453
+ "@icore/firebase-admin",
454
+ "@icore/db-mongodb"
400
455
  ]);
401
456
  await stripTsconfigPath(targetDir, "@icore/db-firestore");
457
+ await stripTsconfigPath(targetDir, "@icore/db-mongodb");
402
458
  try {
403
- const src = await readFile(modulePath, "utf8");
404
- const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(/^ {2}firestore: \[[^\]]*\],\n/m, "").replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/m, "").replace(
405
- /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
406
- "return makeSupabaseDB(cfg);"
407
- );
408
- await writeFile(modulePath, next);
459
+ const src = await readFile2(modulePath, "utf8");
460
+ 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(
461
+ /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
462
+ ""
463
+ ).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\(\)/, "");
464
+ await writeFile2(modulePath, next);
409
465
  } catch {
410
466
  }
411
467
  }
412
468
  if (dbProvider === "firebase") {
413
- await rm(join(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
414
- await stripDeps(join(targetDir, "apps/microservices/notes/package.json"), [
415
- "@icore/db-supabase"
469
+ await rm(join2(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
470
+ await rm(join2(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
471
+ await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
472
+ "@icore/db-supabase",
473
+ "@icore/db-mongodb"
416
474
  ]);
417
475
  await stripTsconfigPath(targetDir, "@icore/db-supabase");
476
+ await stripTsconfigPath(targetDir, "@icore/db-mongodb");
418
477
  try {
419
- const src = await readFile(modulePath, "utf8");
420
- 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(
421
- /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
422
- "return makeFirestoreDB(cfg);"
423
- );
424
- await writeFile(modulePath, next);
478
+ const src = await readFile2(modulePath, "utf8");
479
+ 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(
480
+ /^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
481
+ ""
482
+ ).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\(\)/, "");
483
+ await writeFile2(modulePath, next);
484
+ } catch {
485
+ }
486
+ }
487
+ if (dbProvider === "mongodb") {
488
+ await rm(join2(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
489
+ await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
490
+ await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
491
+ "@icore/db-supabase",
492
+ "@icore/db-firestore",
493
+ "@icore/firebase-admin"
494
+ ]);
495
+ await stripTsconfigPath(targetDir, "@icore/db-supabase");
496
+ await stripTsconfigPath(targetDir, "@icore/db-firestore");
497
+ try {
498
+ const src = await readFile2(modulePath, "utf8");
499
+ 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);");
500
+ await writeFile2(modulePath, next);
425
501
  } catch {
426
502
  }
427
503
  }
428
504
  }
429
505
  async function removeFirebaseAdminLib(targetDir) {
430
- await rm(join(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
506
+ await rm(join2(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
431
507
  await stripTsconfigPath(targetDir, "@icore/firebase-admin");
432
508
  }
433
509
  async function removeUploadStack(targetDir) {
@@ -439,129 +515,36 @@ async function removeUploadStack(targetDir) {
439
515
  "apps/api/src/app/storage"
440
516
  ];
441
517
  for (const p2 of paths) {
442
- await rm(join(targetDir, p2), { recursive: true, force: true });
518
+ await rm(join2(targetDir, p2), { recursive: true, force: true });
443
519
  }
444
- const appModulePath = join(targetDir, "apps/api/src/app/app.module.ts");
520
+ const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
445
521
  try {
446
- const appModule = await readFile(appModulePath, "utf8");
522
+ const appModule = await readFile2(appModulePath, "utf8");
447
523
  const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
448
- await writeFile(appModulePath, next);
524
+ await writeFile2(appModulePath, next);
449
525
  } catch {
450
526
  }
451
- const gatewayEnv = join(targetDir, "apps/api/.env");
527
+ const gatewayEnv = join2(targetDir, "apps/api/.env");
452
528
  try {
453
- const env = await readFile(gatewayEnv, "utf8");
529
+ const env = await readFile2(gatewayEnv, "utf8");
454
530
  const next = env.split("\n").filter(
455
531
  (line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
456
532
  ).join("\n");
457
- await writeFile(gatewayEnv, next);
533
+ await writeFile2(gatewayEnv, next);
458
534
  } catch {
459
535
  }
460
- await stripDeps(join(targetDir, "apps/api/package.json"), [
536
+ await stripDeps(join2(targetDir, "apps/api/package.json"), [
461
537
  "@icore/upload-client",
462
538
  "@types/multer"
463
539
  ]);
464
540
  }
465
- async function selectClientTemplate(targetDir, opts) {
466
- const templatesRoot = join(targetDir, "apps/templates");
467
- const chosen = join(templatesRoot, `client-${opts.ui}`);
468
- const destClient = join(targetDir, "apps/client");
469
- let chosenUi = opts.ui;
470
- try {
471
- const s = await stat(chosen);
472
- if (!s.isDirectory()) throw new Error("not a dir");
473
- await copyTree(chosen, destClient);
474
- } catch {
475
- chosenUi = "shadcn";
476
- await copyTree(join(templatesRoot, "client-shadcn"), destClient);
477
- }
478
- await rm(templatesRoot, { recursive: true, force: true });
479
- await rewriteClientPaths(destClient, chosenUi);
480
- }
481
- async function rewriteClientPaths(clientDir, ui) {
482
- const candidates = [
483
- "vite.config.mts",
484
- "tsconfig.json",
485
- "tsconfig.app.json",
486
- "tsconfig.spec.json",
487
- "project.json",
488
- "eslint.config.mjs"
489
- ];
490
- for (const rel of candidates) {
491
- const path = join(clientDir, rel);
492
- try {
493
- const raw = await readFile(path, "utf8");
494
- const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
495
- if (next !== raw) await writeFile(path, next);
496
- } catch {
497
- }
498
- }
499
- }
500
- function gitInit(cwd, projectName) {
501
- spawnSync("git", ["init"], { cwd, stdio: "inherit" });
502
- spawnSync("git", ["add", "."], { cwd, stdio: "inherit" });
503
- spawnSync(
504
- "git",
505
- ["commit", "-m", `chore: bootstrap ${projectName} from @idevconn/create-icore`],
506
- { cwd, stdio: "inherit" }
507
- );
508
- }
509
- function resolveYarnBin(cwd) {
510
- try {
511
- const yarnrc = readFileSync(join(cwd, ".yarnrc.yml"), "utf8");
512
- const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
513
- if (match?.[1]) return join(cwd, match[1].trim());
514
- } catch {
515
- }
516
- return join(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
517
- }
518
- function runInstall(cwd, pm) {
519
- if (pm === "yarn") {
520
- spawnSync("node", [resolveYarnBin(cwd), "install"], { cwd, stdio: "inherit" });
521
- } else if (pm === "npm") {
522
- spawnSync("npm", ["install"], { cwd, stdio: "inherit" });
523
- } else {
524
- spawnSync("pnpm", ["install"], { cwd, stdio: "inherit" });
525
- }
526
- }
527
- async function scaffold(opts, templatesDir) {
528
- await copyTree(templatesDir, opts.targetDir);
529
- await rewriteRootPackageJson(opts.targetDir, opts);
530
- await writeAuthEnv(opts.targetDir, opts);
531
- await writeUploadEnv(opts.targetDir, opts);
532
- await writeNotesEnv(opts.targetDir, opts);
533
- await writePaymentEnv(opts.targetDir, opts);
534
- await writeGatewayEnv(opts.targetDir, opts);
535
- await writeRootEnv(opts.targetDir, opts);
536
- await selectClientTemplate(opts.targetDir, opts);
537
- await writeClientEnv(opts.targetDir);
538
- if (opts.upload === "none") await removeUploadStack(opts.targetDir);
539
- if (opts.payment === "none") await removePaymentStack(opts.targetDir);
540
- if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
541
- if (opts.example === "none") await removeNotesStack(opts.targetDir);
542
- await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
543
- await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
544
- await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
545
- const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
546
- if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
547
- if (opts.packageManager === "yarn") {
548
- await writeFile(join(opts.targetDir, "yarn.lock"), "");
549
- } else {
550
- await rm(join(opts.targetDir, ".yarn"), { recursive: true, force: true });
551
- await rm(join(opts.targetDir, ".yarnrc.yml"), { force: true });
552
- }
553
- if (opts.packageManager === "pnpm") {
554
- await writePnpmWorkspace(opts.targetDir);
555
- await rewritePnpmWorkspaceDeps(opts.targetDir);
556
- }
557
- await patchGitignoreForPm(opts.targetDir, opts.packageManager);
558
- await writeAiFiles(opts.targetDir, opts);
559
- if (opts.install) runInstall(opts.targetDir, opts.packageManager);
560
- if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
561
- }
541
+
542
+ // src/lib/scaffold-pkg.ts
543
+ import { readFile as readFile3, writeFile as writeFile3, mkdir, readdir } from "fs/promises";
544
+ import { join as join3 } from "path";
562
545
  async function writePnpmWorkspace(targetDir) {
563
- const pkgPath = join(targetDir, "package.json");
564
- const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
546
+ const pkgPath = join3(targetDir, "package.json");
547
+ const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
565
548
  const workspaces = pkg.workspaces ?? [];
566
549
  const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
567
550
  const allowBuilds = [
@@ -582,23 +565,22 @@ ${packagesBlock}
582
565
  allowBuilds:
583
566
  ${allowBuilds}
584
567
  `;
585
- await writeFile(join(targetDir, "pnpm-workspace.yaml"), content);
568
+ await writeFile3(join3(targetDir, "pnpm-workspace.yaml"), content);
586
569
  }
587
570
  async function rewritePnpmWorkspaceDeps(targetDir) {
588
- const { readdir: rd } = await import("fs/promises");
589
571
  async function walk(dir) {
590
572
  const found = [];
591
573
  let entries;
592
574
  try {
593
- entries = await rd(dir, { withFileTypes: true });
575
+ entries = await readdir(dir, { withFileTypes: true });
594
576
  } catch {
595
577
  return found;
596
578
  }
597
579
  for (const e of entries) {
598
580
  if (e.isDirectory() && e.name !== "node_modules") {
599
- found.push(...await walk(join(dir, e.name)));
581
+ found.push(...await walk(join3(dir, e.name)));
600
582
  } else if (e.isFile() && e.name === "package.json") {
601
- found.push(join(dir, e.name));
583
+ found.push(join3(dir, e.name));
602
584
  }
603
585
  }
604
586
  return found;
@@ -606,17 +588,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
606
588
  const pkgFiles = await walk(targetDir);
607
589
  for (const f of pkgFiles) {
608
590
  try {
609
- const raw = await readFile(f, "utf8");
591
+ const raw = await readFile3(f, "utf8");
610
592
  const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
611
- if (next !== raw) await writeFile(f, next);
593
+ if (next !== raw) await writeFile3(f, next);
612
594
  } catch {
613
595
  }
614
596
  }
615
597
  }
616
598
  async function patchGitignoreForPm(targetDir, pm) {
617
- const giPath = join(targetDir, ".gitignore");
599
+ const giPath = join3(targetDir, ".gitignore");
618
600
  try {
619
- let src = await readFile(giPath, "utf8");
601
+ let src = await readFile3(giPath, "utf8");
620
602
  src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
621
603
  if (pm !== "yarn") {
622
604
  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, "");
@@ -631,7 +613,7 @@ async function patchGitignoreForPm(targetDir, pm) {
631
613
  src += "\n# npm\nnpm-debug.log*\n";
632
614
  }
633
615
  }
634
- await writeFile(giPath, src);
616
+ await writeFile3(giPath, src);
635
617
  } catch {
636
618
  }
637
619
  }
@@ -646,7 +628,7 @@ async function writeAiFiles(targetDir, opts) {
646
628
  if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
647
629
  const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
648
630
  const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
649
- await writeFile(join(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
631
+ await writeFile3(join3(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
650
632
  const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
651
633
  const readme = `# ${opts.projectName}
652
634
 
@@ -696,7 +678,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
696
678
 
697
679
  Apache-2.0
698
680
  `;
699
- await writeFile(join(targetDir, "README.md"), readme);
681
+ await writeFile3(join3(targetDir, "README.md"), readme);
700
682
  const agents = `# ${opts.projectName} \u2014 Agent Instructions
701
683
 
702
684
  ## Stack snapshot
@@ -777,8 +759,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
777
759
  - Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
778
760
  - Run: \`${nx} test <project>\`
779
761
  `;
780
- await writeFile(join(targetDir, "AGENTS.md"), agents);
781
- await mkdir(join(targetDir, ".claude"), { recursive: true });
762
+ await writeFile3(join3(targetDir, "AGENTS.md"), agents);
763
+ await mkdir(join3(targetDir, ".claude"), { recursive: true });
782
764
  const mcpServers = {
783
765
  nx: {
784
766
  command: "npx",
@@ -819,17 +801,140 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
819
801
  ]
820
802
  }
821
803
  };
822
- await writeFile(
823
- join(targetDir, ".claude", "settings.json"),
804
+ await writeFile3(
805
+ join3(targetDir, ".claude", "settings.json"),
824
806
  JSON.stringify(settings, null, 2) + "\n"
825
807
  );
826
808
  }
827
809
 
810
+ // src/lib/scaffold.ts
811
+ var IGNORE_TOP = /* @__PURE__ */ new Set([
812
+ ".git",
813
+ "node_modules",
814
+ ".yarn/cache",
815
+ ".yarn/unplugged",
816
+ ".yarn/install-state.gz",
817
+ ".nx",
818
+ "dist",
819
+ "tmp",
820
+ "coverage",
821
+ ".idea",
822
+ ".vscode"
823
+ ]);
824
+ async function copyTree(src, dest) {
825
+ await mkdir2(dest, { recursive: true });
826
+ const entries = await readdir2(src, { withFileTypes: true });
827
+ for (const entry of entries) {
828
+ if (IGNORE_TOP.has(entry.name)) continue;
829
+ const s = join4(src, entry.name);
830
+ const d = join4(dest, entry.name);
831
+ if (entry.isDirectory()) await copyTree(s, d);
832
+ else if (entry.isFile()) await copyFile(s, d);
833
+ }
834
+ }
835
+ async function selectClientTemplate(targetDir, opts) {
836
+ const templatesRoot = join4(targetDir, "apps/templates");
837
+ const chosen = join4(templatesRoot, `client-${opts.ui}`);
838
+ const destClient = join4(targetDir, "apps/client");
839
+ let chosenUi = opts.ui;
840
+ try {
841
+ const s = await stat(chosen);
842
+ if (!s.isDirectory()) throw new Error("not a dir");
843
+ await copyTree(chosen, destClient);
844
+ } catch {
845
+ chosenUi = "shadcn";
846
+ await copyTree(join4(templatesRoot, "client-shadcn"), destClient);
847
+ }
848
+ await rm2(templatesRoot, { recursive: true, force: true });
849
+ await rewriteClientPaths(destClient, chosenUi);
850
+ }
851
+ async function rewriteClientPaths(clientDir, ui) {
852
+ const candidates = [
853
+ "vite.config.mts",
854
+ "tsconfig.json",
855
+ "tsconfig.app.json",
856
+ "tsconfig.spec.json",
857
+ "project.json",
858
+ "eslint.config.mjs"
859
+ ];
860
+ for (const rel of candidates) {
861
+ const path = join4(clientDir, rel);
862
+ try {
863
+ const raw = await readFile4(path, "utf8");
864
+ const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
865
+ if (next !== raw) await writeFile4(path, next);
866
+ } catch {
867
+ }
868
+ }
869
+ }
870
+ function gitInit(cwd, projectName) {
871
+ spawnSync("git", ["init"], { cwd, stdio: "inherit" });
872
+ spawnSync("git", ["add", "."], { cwd, stdio: "inherit" });
873
+ spawnSync(
874
+ "git",
875
+ ["commit", "-m", `chore: bootstrap ${projectName} from @idevconn/create-icore`],
876
+ { cwd, stdio: "inherit" }
877
+ );
878
+ }
879
+ function resolveYarnBin(cwd) {
880
+ try {
881
+ const yarnrc = readFileSync(join4(cwd, ".yarnrc.yml"), "utf8");
882
+ const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
883
+ if (match?.[1]) return join4(cwd, match[1].trim());
884
+ } catch {
885
+ }
886
+ return join4(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
887
+ }
888
+ function runInstall(cwd, pm) {
889
+ if (pm === "yarn") {
890
+ spawnSync("node", [resolveYarnBin(cwd), "install"], { cwd, stdio: "inherit" });
891
+ } else if (pm === "npm") {
892
+ spawnSync("npm", ["install"], { cwd, stdio: "inherit" });
893
+ } else {
894
+ spawnSync("pnpm", ["install"], { cwd, stdio: "inherit" });
895
+ }
896
+ }
897
+ async function scaffold(opts, templatesDir) {
898
+ await copyTree(templatesDir, opts.targetDir);
899
+ await rewriteRootPackageJson(opts.targetDir, opts);
900
+ await writeAuthEnv(opts.targetDir, opts);
901
+ await writeUploadEnv(opts.targetDir, opts);
902
+ await writeNotesEnv(opts.targetDir, opts);
903
+ await writePaymentEnv(opts.targetDir, opts);
904
+ await writeGatewayEnv(opts.targetDir, opts);
905
+ await writeRootEnv(opts.targetDir, opts);
906
+ await selectClientTemplate(opts.targetDir, opts);
907
+ await writeClientEnv(opts.targetDir);
908
+ if (opts.upload === "none") await removeUploadStack(opts.targetDir);
909
+ if (opts.payment === "none") await removePaymentStack(opts.targetDir);
910
+ if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
911
+ if (opts.example === "none") await removeNotesStack(opts.targetDir);
912
+ await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
913
+ await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
914
+ await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
915
+ const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
916
+ if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
917
+ if (opts.packageManager === "yarn") {
918
+ await writeFile4(join4(opts.targetDir, "yarn.lock"), "");
919
+ } else {
920
+ await rm2(join4(opts.targetDir, ".yarn"), { recursive: true, force: true });
921
+ await rm2(join4(opts.targetDir, ".yarnrc.yml"), { force: true });
922
+ }
923
+ if (opts.packageManager === "pnpm") {
924
+ await writePnpmWorkspace(opts.targetDir);
925
+ await rewritePnpmWorkspaceDeps(opts.targetDir);
926
+ }
927
+ await patchGitignoreForPm(opts.targetDir, opts.packageManager);
928
+ await writeAiFiles(opts.targetDir, opts);
929
+ if (opts.install) runInstall(opts.targetDir, opts.packageManager);
930
+ if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
931
+ }
932
+
828
933
  // src/lib/prompts.ts
829
934
  import * as p from "@clack/prompts";
830
935
  import { resolve } from "path";
831
- import { readFile as readFile2 } from "fs/promises";
832
- import { dirname, join as join2 } from "path";
936
+ import { readFile as readFile5 } from "fs/promises";
937
+ import { dirname, join as join5 } from "path";
833
938
  import { fileURLToPath } from "url";
834
939
  function detectPackageManager() {
835
940
  const ua = process.env["npm_config_user_agent"] ?? "";
@@ -841,7 +946,7 @@ function detectPackageManager() {
841
946
  async function readSelfVersion() {
842
947
  try {
843
948
  const here = dirname(fileURLToPath(import.meta.url));
844
- const pkgRaw = await readFile2(join2(here, "..", "package.json"), "utf8");
949
+ const pkgRaw = await readFile5(join5(here, "..", "package.json"), "utf8");
845
950
  const pkg = JSON.parse(pkgRaw);
846
951
  return pkg.version ?? null;
847
952
  } catch {
@@ -942,7 +1047,8 @@ Re-run with @latest to refresh:
942
1047
  message: "Auth provider",
943
1048
  options: [
944
1049
  { value: "supabase", label: "Supabase" },
945
- { value: "firebase", label: "Firebase" }
1050
+ { value: "firebase", label: "Firebase" },
1051
+ { value: "mongodb", label: "MongoDB (Custom Auth)" }
946
1052
  ]
947
1053
  });
948
1054
  if (p.isCancel(authProvider)) throw new Error("cancelled");
@@ -950,7 +1056,8 @@ Re-run with @latest to refresh:
950
1056
  message: "Database backend",
951
1057
  options: [
952
1058
  { value: "supabase", label: "Supabase Postgres" },
953
- { value: "firebase", label: "Firestore" }
1059
+ { value: "firebase", label: "Firestore" },
1060
+ { value: "mongodb", label: "MongoDB" }
954
1061
  ],
955
1062
  initialValue: authProvider
956
1063
  });
@@ -961,6 +1068,7 @@ Re-run with @latest to refresh:
961
1068
  { value: "supabase", label: "Supabase Storage" },
962
1069
  { value: "firebase", label: "Firebase Cloud Storage" },
963
1070
  { value: "cloudinary", label: "Cloudinary" },
1071
+ { value: "mongodb", label: "MongoDB GridFS" },
964
1072
  { value: "none", label: "None \u2014 skip the upload microservice" }
965
1073
  ]
966
1074
  });