@idevconn/create-icore 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,15 +19,16 @@ npm i -g @idevconn/create-icore && create-icore my-saas
19
19
 
20
20
  ## Flags
21
21
 
22
- | Flag | Values | Default | Notes |
23
- | -------------- | -------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
24
- | `--auth` | `supabase` \| `firebase` | prompted | Auth provider |
25
- | `--db` | `supabase` \| `firebase` | prompted | Database backend. Fully independent of `--auth` — mix-and-match combos like `--auth=firebase --db=supabase` are first-class. |
26
- | `--upload` | `supabase` \| `firebase` \| `cloudinary` \| `none` | prompted | File upload provider. Use `none` to remove the upload microservice entirely. |
27
- | `--ui` | `shadcn` \| `antd` \| `mui` | `shadcn` | UI library. All three are fully implemented: `shadcn` (Tailwind 4 + shadcn/ui), `antd` (Ant Design 6), `mui` (MUI 6 / Material Design). |
28
- | `--transport` | `tcp` \| `redis` \| `nats` | `tcp` | Microservice transport |
29
- | `--no-git` | — | git enabled | Skip `git init` |
30
- | `--no-install` | — | install enabled | Skip `yarn install` |
22
+ | Flag | Values | Default | Notes |
23
+ | -------------- | -------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | `--auth` | `supabase` \| `firebase` \| `mongodb` \| `none` | prompted | Auth provider. Use `none` for a minimal SPA with no login system — skips db, example, and transport questions. |
25
+ | `--db` | `supabase` \| `firebase` \| `mongodb` \| `none` | prompted | Database backend. Fully independent of `--auth` — mix-and-match combos like `--auth=firebase --db=supabase` are first-class. Use `none` when `--auth=none` (set automatically). |
26
+ | `--upload` | `supabase` \| `firebase` \| `cloudinary` \| `none` | prompted | File upload provider. Use `none` to remove the upload microservice entirely. |
27
+ | `--ui` | `shadcn` \| `antd` \| `mui` | `shadcn` | UI library. All three are fully implemented: `shadcn` (Tailwind 4 + shadcn/ui), `antd` (Ant Design 6), `mui` (MUI 6 / Material Design). |
28
+ | `--transport` | `tcp` \| `redis` \| `nats` | `tcp` | Microservice transport |
29
+ | `--no-git` | — | git enabled | Skip `git init` |
30
+ | `--no-install` | — | install enabled | Skip `yarn install` |
31
+ | `--config` | path to `.json` file | — | Pre-fill any wizard answer from a JSON file. Missing fields still prompt interactively. CLI flags override config values. See **Non-interactive / CI mode** below. |
31
32
 
32
33
  > **Deprecated:** `--storage` is a deprecated alias for `--upload`. A warning is printed to stderr and the value is forwarded to `--upload`. Remove `--storage` from your scripts.
33
34
 
@@ -52,10 +53,44 @@ npm init @idevconn/icore my-app -- --auth=firebase --db=firebase --upload=cloudi
52
53
  # Skip the upload microservice entirely (--upload=none)
53
54
  npm init @idevconn/icore api-only -- --auth=supabase --db=supabase --upload=none --no-install
54
55
 
56
+ # No auth — simple SPA (no login system, no auth microservice)
57
+ npm init @idevconn/icore spa-app -- --auth=none --upload=none --no-install
58
+
55
59
  # NATS transport
56
60
  npm init @idevconn/icore my-app -- --auth=supabase --db=supabase --upload=supabase --transport=nats
57
61
  ```
58
62
 
63
+ ## Non-interactive / CI mode
64
+
65
+ Pass `--config <path>` to skip individual prompts using a JSON file. Any field omitted from the file is still asked interactively. Individual CLI flags always override config file values.
66
+
67
+ ```json
68
+ {
69
+ "projectName": "demo-saas",
70
+ "authProvider": "supabase",
71
+ "dbProvider": "supabase",
72
+ "upload": "cloudinary",
73
+ "payment": "none",
74
+ "jobs": "bullmq",
75
+ "example": "notes",
76
+ "ui": "shadcn",
77
+ "transport": "nats",
78
+ "packageManager": "yarn",
79
+ "initGit": true,
80
+ "install": false
81
+ }
82
+ ```
83
+
84
+ ```bash
85
+ # Fully non-interactive — all fields in config, no prompts
86
+ npx @idevconn/create-icore --config ./my-config.json
87
+
88
+ # CLI flag overrides config value (firebase wins over supabase in the file)
89
+ npx @idevconn/create-icore --auth firebase --config ./my-config.json
90
+ ```
91
+
92
+ Field names mirror the TypeScript `CreateIcoreOptions` type. Unknown fields are silently ignored. `targetDir` is always derived from `projectName` + the working directory and is ignored if present.
93
+
59
94
  ## Building
60
95
 
61
96
  Run `nx build create-icore` to build the library.
package/dist/cli.js CHANGED
@@ -10,9 +10,98 @@ import * as p2 from "@clack/prompts";
10
10
  // src/lib/prompts.ts
11
11
  import * as p from "@clack/prompts";
12
12
  import { resolve } from "path";
13
- import { readFile } from "fs/promises";
13
+ import { readFile as readFile2 } from "fs/promises";
14
14
  import { dirname, join } from "path";
15
15
  import { fileURLToPath } from "url";
16
+
17
+ // src/lib/config.ts
18
+ import { readFile } from "fs/promises";
19
+ var ConfigFileError = class extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "ConfigFileError";
23
+ }
24
+ };
25
+ var AUTH_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
26
+ var DB_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
27
+ var UPLOAD_PROVIDERS = [
28
+ "supabase",
29
+ "firebase",
30
+ "cloudinary",
31
+ "mongodb",
32
+ "none"
33
+ ];
34
+ var PAYMENT_PROVIDERS = ["paypal", "none"];
35
+ var JOBS_PROVIDERS = ["bullmq", "none"];
36
+ var EXAMPLE_MODES = ["notes", "none"];
37
+ var UI_LIBRARIES = ["shadcn", "antd", "mui"];
38
+ var MS_TRANSPORTS = ["tcp", "redis", "nats", "mqtt", "rmq", "kafka"];
39
+ var PACKAGE_MANAGERS = ["yarn", "npm", "pnpm"];
40
+ function assertEnum(field, value, valid) {
41
+ if (typeof value !== "string" || !valid.includes(value)) {
42
+ throw new ConfigFileError(
43
+ `config field "${field}" got "${String(value)}", expected one of: ${valid.join(", ")}`
44
+ );
45
+ }
46
+ return value;
47
+ }
48
+ function assertBoolean(field, value) {
49
+ if (typeof value !== "boolean") {
50
+ throw new ConfigFileError(`config field "${field}" must be a boolean, got ${typeof value}`);
51
+ }
52
+ return value;
53
+ }
54
+ function validateConfig(raw) {
55
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
56
+ throw new ConfigFileError("config file must be a JSON object");
57
+ }
58
+ const obj = raw;
59
+ const result = {};
60
+ if ("projectName" in obj) {
61
+ const v = obj["projectName"];
62
+ if (typeof v !== "string" || !/^[a-z0-9-]+$/i.test(v)) {
63
+ throw new ConfigFileError(
64
+ `config field "projectName" must match /^[a-z0-9-]+$/i, got "${String(v)}"`
65
+ );
66
+ }
67
+ result.projectName = v;
68
+ }
69
+ if ("authProvider" in obj)
70
+ result.authProvider = assertEnum("authProvider", obj["authProvider"], AUTH_PROVIDERS);
71
+ if ("dbProvider" in obj)
72
+ result.dbProvider = assertEnum("dbProvider", obj["dbProvider"], DB_PROVIDERS);
73
+ if ("upload" in obj) result.upload = assertEnum("upload", obj["upload"], UPLOAD_PROVIDERS);
74
+ if ("payment" in obj) result.payment = assertEnum("payment", obj["payment"], PAYMENT_PROVIDERS);
75
+ if ("jobs" in obj) result.jobs = assertEnum("jobs", obj["jobs"], JOBS_PROVIDERS);
76
+ if ("example" in obj) result.example = assertEnum("example", obj["example"], EXAMPLE_MODES);
77
+ if ("ui" in obj) result.ui = assertEnum("ui", obj["ui"], UI_LIBRARIES);
78
+ if ("transport" in obj)
79
+ result.transport = assertEnum("transport", obj["transport"], MS_TRANSPORTS);
80
+ if ("packageManager" in obj)
81
+ result.packageManager = assertEnum("packageManager", obj["packageManager"], PACKAGE_MANAGERS);
82
+ if ("initGit" in obj) result.initGit = assertBoolean("initGit", obj["initGit"]);
83
+ if ("install" in obj) result.install = assertBoolean("install", obj["install"]);
84
+ return result;
85
+ }
86
+ async function loadConfig(filePath) {
87
+ let raw;
88
+ try {
89
+ raw = await readFile(filePath, "utf8");
90
+ } catch {
91
+ throw new ConfigFileError(`config file not found: ${filePath}`);
92
+ }
93
+ let parsed;
94
+ try {
95
+ parsed = JSON.parse(raw);
96
+ } catch (e) {
97
+ throw new ConfigFileError(
98
+ `config file is not valid JSON: ${e instanceof Error ? e.message : String(e)}`
99
+ );
100
+ }
101
+ return validateConfig(parsed);
102
+ }
103
+
104
+ // src/lib/prompts.ts
16
105
  function detectPackageManager() {
17
106
  const ua = process.env["npm_config_user_agent"] ?? "";
18
107
  if (ua.startsWith("yarn/")) return "yarn";
@@ -23,7 +112,7 @@ function detectPackageManager() {
23
112
  async function readSelfVersion() {
24
113
  try {
25
114
  const here2 = dirname(fileURLToPath(import.meta.url));
26
- const pkgRaw = await readFile(join(here2, "..", "package.json"), "utf8");
115
+ const pkgRaw = await readFile2(join(here2, "..", "package.json"), "utf8");
27
116
  const pkg = JSON.parse(pkgRaw);
28
117
  return pkg.version ?? null;
29
118
  } catch {
@@ -97,12 +186,21 @@ function parseFlags(argv) {
97
186
  case "no-install":
98
187
  out.install = false;
99
188
  break;
189
+ case "config":
190
+ out._configPath = v;
191
+ break;
100
192
  }
101
193
  }
102
194
  return out;
103
195
  }
104
196
  async function collectOptions({ argv, cwd }) {
105
197
  const flags = parseFlags(argv);
198
+ const configPath = flags._configPath;
199
+ delete flags._configPath;
200
+ if (configPath) {
201
+ const configValues = await loadConfig(configPath);
202
+ Object.assign(flags, { ...configValues, ...flags });
203
+ }
106
204
  const [selfVersion, latestVersion] = await Promise.all([readSelfVersion(), fetchLatestVersion()]);
107
205
  const versionTag = selfVersion ? ` v${selfVersion}` : "";
108
206
  p.intro(`iCore${versionTag} \u2014 bootstrap a new project`);
@@ -125,11 +223,12 @@ Re-run with @latest to refresh:
125
223
  options: [
126
224
  { value: "supabase", label: "Supabase" },
127
225
  { value: "firebase", label: "Firebase" },
128
- { value: "mongodb", label: "MongoDB (Custom Auth)" }
226
+ { value: "mongodb", label: "MongoDB (Custom Auth)" },
227
+ { value: "none", label: "None \u2014 no login, open API (simple SPA)" }
129
228
  ]
130
229
  });
131
230
  if (p.isCancel(authProvider)) throw new Error("cancelled");
132
- const dbProvider = flags.dbProvider ?? await p.select({
231
+ const dbProvider = authProvider === "none" ? "none" : flags.dbProvider ?? await p.select({
133
232
  message: "Database backend",
134
233
  options: [
135
234
  { value: "supabase", label: "Supabase Postgres" },
@@ -168,7 +267,7 @@ Re-run with @latest to refresh:
168
267
  initialValue: "none"
169
268
  });
170
269
  if (p.isCancel(jobs)) throw new Error("cancelled");
171
- const example = flags.example ?? await p.select({
270
+ const example = authProvider === "none" ? "none" : flags.example ?? await p.select({
172
271
  message: "Include notes sample feature? (CRUD demo \u2014 remove before production)",
173
272
  options: [
174
273
  { value: "notes", label: "Yes \u2014 include notes sample" },
@@ -193,7 +292,8 @@ Re-run with @latest to refresh:
193
292
  initialValue: "shadcn"
194
293
  });
195
294
  if (p.isCancel(ui)) throw new Error("cancelled");
196
- const transport = flags.transport ?? await p.select({
295
+ const noMicroservices = authProvider === "none" && upload === "none" && payment === "none";
296
+ const transport = flags.transport ?? (noMicroservices ? "tcp" : await p.select({
197
297
  message: "Microservice transport",
198
298
  options: [
199
299
  { value: "tcp", label: "TCP (default, no broker required)" },
@@ -204,7 +304,7 @@ Re-run with @latest to refresh:
204
304
  { value: "kafka", label: "Kafka" }
205
305
  ],
206
306
  initialValue: "tcp"
207
- });
307
+ }));
208
308
  if (p.isCancel(transport)) throw new Error("cancelled");
209
309
  const packageManager = flags.packageManager ?? detectPackageManager();
210
310
  if (packageManager === "yarn") {
@@ -236,13 +336,13 @@ Re-run with @latest to refresh:
236
336
  }
237
337
 
238
338
  // src/lib/scaffold.ts
239
- import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile7, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
339
+ import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile8, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
240
340
  import { readFileSync } from "fs";
241
341
  import { join as join9 } from "path";
242
342
  import { spawnSync } from "child_process";
243
343
 
244
344
  // src/lib/scaffold-env.ts
245
- import { readFile as readFile2, writeFile } from "fs/promises";
345
+ import { readFile as readFile3, writeFile } from "fs/promises";
246
346
  import { join as join2 } from "path";
247
347
  var TRANSPORT_ENV_TOKEN = {
248
348
  redis: "REDIS",
@@ -271,7 +371,7 @@ function uncommentTransportEnv(text2, prefix, transport) {
271
371
  async function stripGatewayTransport(targetDir, prefix) {
272
372
  const gatewayEnv = join2(targetDir, "apps/api/.env");
273
373
  try {
274
- const env = await readFile2(gatewayEnv, "utf8");
374
+ const env = await readFile3(gatewayEnv, "utf8");
275
375
  const next = env.split("\n").filter(
276
376
  (line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
277
377
  ).join("\n");
@@ -294,7 +394,7 @@ async function pruneRootProviderDeps(targetDir, opts) {
294
394
  if (drop.size === 0) return;
295
395
  const pkgPath = join2(targetDir, "package.json");
296
396
  try {
297
- const pkg = JSON.parse(await readFile2(pkgPath, "utf8"));
397
+ const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
298
398
  for (const field of ["dependencies", "devDependencies"]) {
299
399
  const deps = pkg[field];
300
400
  if (!deps) continue;
@@ -306,7 +406,7 @@ async function pruneRootProviderDeps(targetDir, opts) {
306
406
  }
307
407
  async function rewriteRootPackageJson(targetDir, opts) {
308
408
  const pkgPath = join2(targetDir, "package.json");
309
- const raw = await readFile2(pkgPath, "utf8");
409
+ const raw = await readFile3(pkgPath, "utf8");
310
410
  const pkg = JSON.parse(raw);
311
411
  pkg["name"] = opts.projectName;
312
412
  pkg["version"] = "0.0.1";
@@ -321,11 +421,16 @@ async function rewriteRootPackageJson(targetDir, opts) {
321
421
  const deps = pkg["dependencies"] ??= {};
322
422
  Object.assign(deps, MONGODB_DEPS);
323
423
  }
424
+ if (opts.authProvider === "mongodb") {
425
+ const devDeps = pkg["devDependencies"] ??= {};
426
+ devDeps["@types/bcrypt"] = "^6.0.0";
427
+ devDeps["@types/jsonwebtoken"] = "^9.0.10";
428
+ }
324
429
  if (opts.packageManager !== "yarn") {
325
430
  delete pkg.packageManager;
326
431
  } else {
327
432
  try {
328
- const yarnrc = await readFile2(join2(targetDir, ".yarnrc.yml"), "utf8");
433
+ const yarnrc = await readFile3(join2(targetDir, ".yarnrc.yml"), "utf8");
329
434
  const match = yarnrc.match(/^yarnPath:\s*.+yarn-(\d+\.\d+\.\d+)\.cjs/m);
330
435
  if (match?.[1]) {
331
436
  pkg["packageManager"] = `yarn@${match[1]}`;
@@ -338,7 +443,7 @@ async function rewriteRootPackageJson(targetDir, opts) {
338
443
  }
339
444
  async function writeAuthEnv(targetDir, opts) {
340
445
  const envExample = join2(targetDir, "apps/microservices/auth/.env.example");
341
- const env = await readFile2(envExample, "utf8");
446
+ const env = await readFile3(envExample, "utf8");
342
447
  let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
343
448
  next = uncommentTransportEnv(next, "AUTH", opts.transport);
344
449
  if (opts.authProvider === "mongodb") {
@@ -349,7 +454,7 @@ async function writeAuthEnv(targetDir, opts) {
349
454
  async function writeUploadEnv(targetDir, opts) {
350
455
  if (opts.upload === "none") return;
351
456
  const envExample = join2(targetDir, "apps/microservices/upload/.env.example");
352
- const env = await readFile2(envExample, "utf8");
457
+ const env = await readFile3(envExample, "utf8");
353
458
  let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
354
459
  next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
355
460
  if (opts.upload === "mongodb") {
@@ -361,7 +466,7 @@ async function writeNotesEnv(targetDir, opts) {
361
466
  if (opts.example === "none") return;
362
467
  const envExample = join2(targetDir, "apps/microservices/notes/.env.example");
363
468
  try {
364
- const env = await readFile2(envExample, "utf8");
469
+ const env = await readFile3(envExample, "utf8");
365
470
  let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
366
471
  next = uncommentTransportEnv(next, "NOTES", opts.transport);
367
472
  await writeFile(join2(targetDir, "apps/microservices/notes/.env"), next);
@@ -370,7 +475,7 @@ async function writeNotesEnv(targetDir, opts) {
370
475
  }
371
476
  async function writeGatewayEnv(targetDir, opts) {
372
477
  const envExample = join2(targetDir, "apps/api/.env.example");
373
- const env = await readFile2(envExample, "utf8");
478
+ const env = await readFile3(envExample, "utf8");
374
479
  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}`);
375
480
  for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
376
481
  next = uncommentTransportEnv(next, prefix, opts.transport);
@@ -393,7 +498,7 @@ async function writeRootEnv(targetDir, opts) {
393
498
  async function writeClientEnv(targetDir) {
394
499
  const envExample = join2(targetDir, "apps/client/.env.example");
395
500
  try {
396
- const env = await readFile2(envExample, "utf8");
501
+ const env = await readFile3(envExample, "utf8");
397
502
  await writeFile(join2(targetDir, "apps/client/.env"), env);
398
503
  } catch {
399
504
  }
@@ -402,7 +507,7 @@ async function writePaymentEnv(targetDir, opts) {
402
507
  if (opts.payment === "none") return;
403
508
  const envExample = join2(targetDir, "apps/microservices/payment/.env.example");
404
509
  try {
405
- const env = await readFile2(envExample, "utf8");
510
+ const env = await readFile3(envExample, "utf8");
406
511
  let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
407
512
  next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
408
513
  await writeFile(join2(targetDir, "apps/microservices/payment/.env"), next);
@@ -411,11 +516,11 @@ async function writePaymentEnv(targetDir, opts) {
411
516
  }
412
517
 
413
518
  // src/lib/scaffold-strip.ts
414
- import { readFile as readFile3, writeFile as writeFile2, rm } from "fs/promises";
519
+ import { readFile as readFile4, writeFile as writeFile2, rm } from "fs/promises";
415
520
  import { join as join3 } from "path";
416
521
  async function stripDeps(pkgPath, names) {
417
522
  try {
418
- const raw = await readFile3(pkgPath, "utf8");
523
+ const raw = await readFile4(pkgPath, "utf8");
419
524
  const pkg = JSON.parse(raw);
420
525
  for (const n of names) {
421
526
  if (pkg.dependencies) delete pkg.dependencies[n];
@@ -428,7 +533,7 @@ async function stripDeps(pkgPath, names) {
428
533
  async function stripTsconfigPath(targetDir, alias) {
429
534
  const tsconfigPath = join3(targetDir, "tsconfig.base.json");
430
535
  try {
431
- const src = await readFile3(tsconfigPath, "utf8");
536
+ const src = await readFile4(tsconfigPath, "utf8");
432
537
  const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
433
538
  const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
434
539
  if (pretty !== src) {
@@ -456,6 +561,62 @@ async function removeFirebaseAdminLib(targetDir) {
456
561
  "@icore/firebase-admin"
457
562
  ]);
458
563
  }
564
+ async function removeAuthStack(targetDir) {
565
+ const rmPaths = [
566
+ "apps/microservices/auth",
567
+ "libs/auth-strategies",
568
+ "libs/auth-client",
569
+ "Dockerfile.ms-auth",
570
+ "apps/api/src/app/auth",
571
+ "apps/api/src/app/profile",
572
+ "apps/api/src/app/abilities",
573
+ "apps/client/src/components/auth",
574
+ "apps/client/src/routes/login.tsx",
575
+ "apps/client/src/routes/auth.callback.tsx",
576
+ "apps/client/src/routes/auth.oauth.callback.tsx",
577
+ "apps/client/src/routes/_dashboard/profile.tsx"
578
+ ];
579
+ for (const p3 of rmPaths) {
580
+ await rm(join3(targetDir, p3), { recursive: true, force: true });
581
+ }
582
+ const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
583
+ try {
584
+ const src = await readFile4(appModulePath, "utf8");
585
+ const next = src.replace(/^import \{ AuthModule \} from '\.\/auth\/auth\.module';\n/m, "").replace(/^import \{ ProfileModule \} from '\.\/profile\/profile\.module';\n/m, "").replace(/^import \{ AbilitiesModule \} from '\.\/abilities\/abilities\.module';\n/m, "").replace(/\bAuthModule,\s*/g, "").replace(/,\s*AuthModule\b/g, "").replace(/\bProfileModule,\s*/g, "").replace(/,\s*ProfileModule\b/g, "").replace(/\bAbilitiesModule,\s*/g, "").replace(/,\s*AbilitiesModule\b/g, "");
586
+ await writeFile2(appModulePath, next);
587
+ } catch {
588
+ }
589
+ const dashboardPath = join3(targetDir, "apps/client/src/routes/_dashboard.tsx");
590
+ try {
591
+ const src = await readFile4(dashboardPath, "utf8");
592
+ const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
593
+ await writeFile2(dashboardPath, next);
594
+ } catch {
595
+ }
596
+ for (const alias of [
597
+ "@icore/auth-client",
598
+ "@icore/auth-supabase",
599
+ "@icore/auth-firebase",
600
+ "@icore/auth-mongodb"
601
+ ]) {
602
+ await stripTsconfigPath(targetDir, alias);
603
+ }
604
+ await stripDeps(join3(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
605
+ const gatewayEnv = join3(targetDir, "apps/api/.env");
606
+ try {
607
+ const env = await readFile4(gatewayEnv, "utf8");
608
+ const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
609
+ await writeFile2(gatewayEnv, next);
610
+ } catch {
611
+ }
612
+ const composePath = join3(targetDir, "docker-compose.yml");
613
+ try {
614
+ const compose = await readFile4(composePath, "utf8");
615
+ const next = compose.replace(/\n {2}auth:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}auth:\n {8}condition: service_started/g, "").replace(/\n {6}AUTH_TRANSPORT:[^\n]*/g, "").replace(/\n {6}AUTH_REDIS_URL:[^\n]*/g, "");
616
+ await writeFile2(composePath, next);
617
+ } catch {
618
+ }
619
+ }
459
620
  async function removeUploadStack(targetDir) {
460
621
  const paths = [
461
622
  "apps/microservices/upload",
@@ -469,14 +630,14 @@ async function removeUploadStack(targetDir) {
469
630
  }
470
631
  const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
471
632
  try {
472
- const appModule = await readFile3(appModulePath, "utf8");
633
+ const appModule = await readFile4(appModulePath, "utf8");
473
634
  const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
474
635
  await writeFile2(appModulePath, next);
475
636
  } catch {
476
637
  }
477
638
  const gatewayEnv = join3(targetDir, "apps/api/.env");
478
639
  try {
479
- const env = await readFile3(gatewayEnv, "utf8");
640
+ const env = await readFile4(gatewayEnv, "utf8");
480
641
  const next = env.split("\n").filter(
481
642
  (line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
482
643
  ).join("\n");
@@ -490,7 +651,7 @@ async function removeUploadStack(targetDir) {
490
651
  }
491
652
 
492
653
  // src/manifest/wire-features.ts
493
- import { readFile as readFile5, writeFile as writeFile4, rm as rm3 } from "fs/promises";
654
+ import { readFile as readFile6, writeFile as writeFile4, rm as rm3 } from "fs/promises";
494
655
  import { join as join5 } from "path";
495
656
 
496
657
  // src/manifest/index.ts
@@ -651,7 +812,7 @@ var MANIFEST = {
651
812
  };
652
813
 
653
814
  // src/manifest/wire-provider.ts
654
- import { readFile as readFile4, writeFile as writeFile3, rm as rm2 } from "fs/promises";
815
+ import { readFile as readFile5, writeFile as writeFile3, rm as rm2 } from "fs/promises";
655
816
  import { join as join4 } from "path";
656
817
  async function writeProvider(targetDir, axis, provider) {
657
818
  const nestModule = axis.section[provider]?.nestModule;
@@ -667,7 +828,7 @@ export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
667
828
  }
668
829
  async function stripJsonKeys(path, drop) {
669
830
  try {
670
- const pkg = JSON.parse(await readFile4(path, "utf8"));
831
+ const pkg = JSON.parse(await readFile5(path, "utf8"));
671
832
  for (const field of ["dependencies", "devDependencies"]) {
672
833
  const deps = pkg[field];
673
834
  if (!deps) continue;
@@ -680,7 +841,7 @@ async function stripJsonKeys(path, drop) {
680
841
  async function stripTsconfigKeys(targetDir, aliases) {
681
842
  const path = join4(targetDir, "tsconfig.base.json");
682
843
  try {
683
- const parsed = JSON.parse(await readFile4(path, "utf8"));
844
+ const parsed = JSON.parse(await readFile5(path, "utf8"));
684
845
  const paths = parsed.compilerOptions?.paths;
685
846
  if (paths) for (const a of aliases) delete paths[a];
686
847
  await writeFile3(path, JSON.stringify(parsed, null, 2) + "\n");
@@ -725,7 +886,8 @@ async function writeFeaturesWiring(targetDir, opts) {
725
886
  export class FeaturesModule {}
726
887
  `;
727
888
  await writeFile4(join5(targetDir, FEATURES_MODULE), featuresModule);
728
- const services = [{ name: "auth", prefix: "AUTH" }];
889
+ const services = [];
890
+ if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
729
891
  if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
730
892
  for (const k of chosen) {
731
893
  const svc = FEATURES[k].gatewayService;
@@ -742,7 +904,7 @@ ${entries}
742
904
  async function stripJobsDockerCompose(targetDir) {
743
905
  const composePath = join5(targetDir, "docker-compose.yml");
744
906
  try {
745
- const compose = await readFile5(composePath, "utf8");
907
+ const compose = await readFile6(composePath, "utf8");
746
908
  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, "");
747
909
  await writeFile4(composePath, next);
748
910
  } catch {
@@ -830,12 +992,14 @@ async function writeJson(targetDir, rel, data) {
830
992
  }
831
993
  async function writeServiceBlueprints(targetDir, opts) {
832
994
  const t = opts.transport;
833
- await writeJson(targetDir, "apps/microservices/auth", {
834
- schemaVersion: 1,
835
- service: "auth",
836
- authProvider: opts.authProvider,
837
- transport: t
838
- });
995
+ if (opts.authProvider !== "none") {
996
+ await writeJson(targetDir, "apps/microservices/auth", {
997
+ schemaVersion: 1,
998
+ service: "auth",
999
+ authProvider: opts.authProvider,
1000
+ transport: t
1001
+ });
1002
+ }
839
1003
  if (opts.upload !== "none") {
840
1004
  await writeJson(targetDir, "apps/microservices/upload", {
841
1005
  schemaVersion: 1,
@@ -918,7 +1082,7 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
918
1082
  var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
919
1083
 
920
1084
  // src/lib/scaffold-pkg.ts
921
- import { readFile as readFile6, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
1085
+ import { readFile as readFile7, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
922
1086
  import { join as join8 } from "path";
923
1087
 
924
1088
  // src/lib/options.ts
@@ -929,7 +1093,7 @@ function pmRun(pm, script) {
929
1093
  // src/lib/scaffold-pkg.ts
930
1094
  async function writePnpmWorkspace(targetDir) {
931
1095
  const pkgPath = join8(targetDir, "package.json");
932
- const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
1096
+ const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
933
1097
  const workspaces = pkg.workspaces ?? [];
934
1098
  const packagesBlock = workspaces.map((p3) => ` - '${p3}'`).join("\n");
935
1099
  const allowBuilds = [
@@ -938,7 +1102,9 @@ async function writePnpmWorkspace(targetDir) {
938
1102
  "@parcel/watcher",
939
1103
  "@scarf/scarf",
940
1104
  "@swc/core",
1105
+ "bcrypt",
941
1106
  "less",
1107
+ "mongodb-memory-server",
942
1108
  "msgpackr-extract",
943
1109
  "nx",
944
1110
  "protobufjs",
@@ -973,7 +1139,7 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
973
1139
  const pkgFiles = await walk(targetDir);
974
1140
  for (const f of pkgFiles) {
975
1141
  try {
976
- const raw = await readFile6(f, "utf8");
1142
+ const raw = await readFile7(f, "utf8");
977
1143
  const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
978
1144
  if (next !== raw) await writeFile7(f, next);
979
1145
  } catch {
@@ -983,7 +1149,7 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
983
1149
  async function patchGitignoreForPm(targetDir, pm) {
984
1150
  const giPath = join8(targetDir, ".gitignore");
985
1151
  try {
986
- let src = await readFile6(giPath, "utf8");
1152
+ let src = await readFile7(giPath, "utf8");
987
1153
  src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
988
1154
  if (pm !== "yarn") {
989
1155
  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, "");
@@ -1245,7 +1411,7 @@ async function rewriteClientPaths(clientDir, ui) {
1245
1411
  for (const rel of candidates) {
1246
1412
  const path = join9(clientDir, rel);
1247
1413
  try {
1248
- const raw = await readFile7(path, "utf8");
1414
+ const raw = await readFile8(path, "utf8");
1249
1415
  const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
1250
1416
  if (next !== raw) await writeFile8(path, next);
1251
1417
  } catch {
@@ -1282,7 +1448,7 @@ function runInstall(cwd, pm) {
1282
1448
  async function scaffold(opts, templatesDir2) {
1283
1449
  await copyTree(templatesDir2, opts.targetDir);
1284
1450
  await rewriteRootPackageJson(opts.targetDir, opts);
1285
- await writeAuthEnv(opts.targetDir, opts);
1451
+ if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
1286
1452
  await writeUploadEnv(opts.targetDir, opts);
1287
1453
  await writeNotesEnv(opts.targetDir, opts);
1288
1454
  await writePaymentEnv(opts.targetDir, opts);
@@ -1294,18 +1460,22 @@ async function scaffold(opts, templatesDir2) {
1294
1460
  await cleanupUnusedFeatures(opts.targetDir, opts);
1295
1461
  await writeFeaturesWiring(opts.targetDir, opts);
1296
1462
  await writeNavConfig(opts.targetDir, opts);
1297
- await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
1298
- await writeAuthProvider(opts.targetDir, opts.authProvider);
1463
+ if (opts.authProvider !== "none") {
1464
+ await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
1465
+ await writeAuthProvider(opts.targetDir, opts.authProvider);
1466
+ } else {
1467
+ await removeAuthStack(opts.targetDir);
1468
+ }
1299
1469
  if (opts.upload !== "none") {
1300
1470
  await cleanupUnusedStorage(opts.targetDir, opts.upload);
1301
1471
  await writeStorageProvider(opts.targetDir, opts.upload);
1302
1472
  }
1303
- if (opts.example !== "none") {
1304
- await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
1305
- await writeDbProvider(opts.targetDir, opts.dbProvider);
1306
- }
1307
1473
  const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
1308
1474
  if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
1475
+ await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
1476
+ if (opts.dbProvider !== "none" && opts.example !== "none") {
1477
+ await writeDbProvider(opts.targetDir, opts.dbProvider);
1478
+ }
1309
1479
  await pruneRootProviderDeps(opts.targetDir, opts);
1310
1480
  await writeBlueprintJson(opts.targetDir, opts);
1311
1481
  await writeServiceBlueprints(opts.targetDir, opts);