@idevconn/create-icore 0.9.2 → 0.10.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 (39) hide show
  1. package/dist/cli.js +832 -130
  2. package/dist/index.cjs +848 -149
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +841 -142
  6. package/package.json +4 -4
  7. package/templates/.yarn/releases/{yarn-4.16.0.cjs → yarn-4.17.0.cjs} +326 -326
  8. package/templates/.yarnrc.yml +1 -1
  9. package/templates/apps/api/package.json +6 -6
  10. package/templates/apps/microservices/auth/package.json +4 -4
  11. package/templates/apps/microservices/jobs/package.json +5 -5
  12. package/templates/apps/microservices/notes/package.json +5 -5
  13. package/templates/apps/microservices/payment/package.json +4 -4
  14. package/templates/apps/microservices/upload/package.json +6 -6
  15. package/templates/apps/templates/client-antd/package.json +2 -2
  16. package/templates/apps/templates/client-mui/package.json +4 -4
  17. package/templates/apps/templates/client-shadcn/package.json +7 -7
  18. package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -2
  19. package/templates/libs/auth-client/package.json +3 -3
  20. package/templates/libs/auth-strategies/firebase/package.json +4 -4
  21. package/templates/libs/auth-strategies/mongodb/package.json +4 -4
  22. package/templates/libs/auth-strategies/supabase/package.json +5 -5
  23. package/templates/libs/db-strategies/firestore/package.json +4 -4
  24. package/templates/libs/db-strategies/mongodb/package.json +3 -3
  25. package/templates/libs/db-strategies/supabase/package.json +5 -5
  26. package/templates/libs/firebase-admin/package.json +1 -1
  27. package/templates/libs/jobs-client/package.json +4 -4
  28. package/templates/libs/notes-client/package.json +3 -3
  29. package/templates/libs/payment-client/package.json +3 -3
  30. package/templates/libs/shared/package.json +2 -2
  31. package/templates/libs/storage-strategies/cloudinary/package.json +4 -4
  32. package/templates/libs/storage-strategies/firebase/package.json +4 -4
  33. package/templates/libs/storage-strategies/mongodb/package.json +3 -3
  34. package/templates/libs/storage-strategies/supabase/package.json +5 -5
  35. package/templates/libs/template-shared/package.json +8 -8
  36. package/templates/libs/upload-client/package.json +3 -3
  37. package/templates/libs/vite-plugins/package.json +3 -3
  38. package/templates/package.json +32 -32
  39. package/templates/tools/create-icore/_template-shell/package.json +32 -32
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ function pmRun(pm, script) {
4
4
  }
5
5
 
6
6
  // src/lib/scaffold.ts
7
- import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile6, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
7
+ import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile7, rmdir as rmdir2, stat, writeFile as writeFile9, rm as rm5 } from "fs/promises";
8
8
  import { readFileSync } from "fs";
9
- import { join as join8 } from "path";
9
+ import { join as join9 } from "path";
10
10
  import { spawnSync } from "child_process";
11
11
 
12
12
  // src/lib/scaffold-env.ts
@@ -107,6 +107,30 @@ async function rewriteRootPackageJson(targetDir, opts) {
107
107
  }
108
108
  }
109
109
  delete pkg.pnpm;
110
+ const noMs = opts.authProvider === "none" && opts.upload === "none" && opts.payment === "none" && opts.jobs === "none" && opts.example === "none";
111
+ const ws = pkg.workspaces;
112
+ if (ws) {
113
+ pkg.workspaces = ws.filter((entry) => {
114
+ if (entry === "apps/templates/*") return false;
115
+ if (entry === "apps/microservices/*" && noMs) return false;
116
+ if (entry === "libs/auth-strategies/*" && opts.authProvider === "none") return false;
117
+ if (entry === "libs/storage-strategies/*" && opts.upload === "none") return false;
118
+ if (entry === "libs/db-strategies/*" && opts.dbProvider === "none") return false;
119
+ return true;
120
+ });
121
+ }
122
+ const rootDeps = pkg.dependencies ?? {};
123
+ const rootDevDeps = pkg.devDependencies ?? {};
124
+ if (opts.authProvider === "none") {
125
+ delete rootDeps["cookie-parser"];
126
+ delete rootDevDeps["@types/cookie-parser"];
127
+ }
128
+ if (opts.upload === "none") {
129
+ delete rootDevDeps["@types/multer"];
130
+ }
131
+ if (noMs) {
132
+ delete rootDeps["@nestjs/microservices"];
133
+ }
110
134
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
111
135
  }
112
136
  async function writeAuthEnv(targetDir, opts) {
@@ -148,6 +172,9 @@ async function writeGatewayEnv(targetDir, opts) {
148
172
  for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
149
173
  next = uncommentTransportEnv(next, prefix, opts.transport);
150
174
  }
175
+ if (opts.authProvider === "none") {
176
+ next = next.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
177
+ }
151
178
  await writeFile(join(targetDir, "apps/api/.env"), next);
152
179
  }
153
180
  async function writeRootEnv(targetDir, opts) {
@@ -229,59 +256,26 @@ async function removeFirebaseAdminLib(targetDir) {
229
256
  "@icore/firebase-admin"
230
257
  ]);
231
258
  }
232
- async function removeAuthStack(targetDir) {
233
- const rmPaths = [
234
- "apps/microservices/auth",
235
- "libs/auth-strategies",
236
- "libs/auth-client",
237
- "Dockerfile.ms-auth",
238
- "apps/api/src/app/auth",
239
- "apps/api/src/app/profile",
240
- "apps/api/src/app/abilities",
241
- "apps/client/src/components/auth",
242
- "apps/client/src/routes/login.tsx",
243
- "apps/client/src/routes/auth.callback.tsx",
244
- "apps/client/src/routes/auth.oauth.callback.tsx",
245
- "apps/client/src/routes/_dashboard/profile.tsx"
246
- ];
247
- for (const p2 of rmPaths) {
248
- await rm(join2(targetDir, p2), { recursive: true, force: true });
249
- }
250
- const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
259
+ async function removeStrategiesLib(targetDir) {
260
+ await rm(join2(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
261
+ await rm(join2(targetDir, "libs/shared/src/testing.ts"), { force: true });
262
+ await rm(join2(targetDir, "libs/shared/src/transport.ts"), { force: true });
263
+ await rm(join2(targetDir, "libs/shared/src/__tests__/transport.unit.test.ts"), { force: true });
264
+ const indexPath = join2(targetDir, "libs/shared/src/index.ts");
251
265
  try {
252
- const src = await readFile2(appModulePath, "utf8");
253
- 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, "");
254
- await writeFile2(appModulePath, next);
255
- } catch {
256
- }
257
- const dashboardPath = join2(targetDir, "apps/client/src/routes/_dashboard.tsx");
258
- try {
259
- const src = await readFile2(dashboardPath, "utf8");
260
- const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
261
- await writeFile2(dashboardPath, next);
262
- } catch {
263
- }
264
- for (const alias of [
265
- "@icore/auth-client",
266
- "@icore/auth-supabase",
267
- "@icore/auth-firebase",
268
- "@icore/auth-mongodb"
269
- ]) {
270
- await stripTsconfigPath(targetDir, alias);
271
- }
272
- await stripDeps(join2(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
273
- const gatewayEnv = join2(targetDir, "apps/api/.env");
274
- try {
275
- const env = await readFile2(gatewayEnv, "utf8");
276
- const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
277
- await writeFile2(gatewayEnv, next);
266
+ const src = await readFile2(indexPath, "utf8");
267
+ await writeFile2(
268
+ indexPath,
269
+ src.replace(/^export \* from '\.\/strategies';\n/m, "").replace(/^export \* from '\.\/transport';\n?/m, "")
270
+ );
278
271
  } catch {
279
272
  }
280
- const composePath = join2(targetDir, "docker-compose.yml");
273
+ const pkgPath = join2(targetDir, "libs/shared/package.json");
281
274
  try {
282
- const compose = await readFile2(composePath, "utf8");
283
- 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, "");
284
- await writeFile2(composePath, next);
275
+ const pkg = JSON.parse(await readFile2(pkgPath, "utf8"));
276
+ if (pkg.exports) delete pkg.exports["./testing"];
277
+ if (pkg.dependencies) delete pkg.dependencies["@nestjs/microservices"];
278
+ await writeFile2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
285
279
  } catch {
286
280
  }
287
281
  }
@@ -291,7 +285,8 @@ async function removeUploadStack(targetDir) {
291
285
  "apps/microservices/upload-e2e",
292
286
  "libs/storage-strategies",
293
287
  "libs/upload-client",
294
- "apps/api/src/app/storage"
288
+ "apps/api/src/app/storage",
289
+ "Dockerfile.ms-upload"
295
290
  ];
296
291
  for (const p2 of paths) {
297
292
  await rm(join2(targetDir, p2), { recursive: true, force: true });
@@ -316,11 +311,683 @@ async function removeUploadStack(targetDir) {
316
311
  "@icore/upload-client",
317
312
  "@types/multer"
318
313
  ]);
314
+ const uploadComposePath = join2(targetDir, "docker-compose.yml");
315
+ try {
316
+ const compose = await readFile2(uploadComposePath, "utf8");
317
+ const next = compose.replace(/\n {2}upload:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}upload:\n {8}condition: service_started/g, "").replace(/\n {6}UPLOAD_TRANSPORT:[^\n]*/g, "").replace(/\n {6}UPLOAD_REDIS_URL:[^\n]*/g, "");
318
+ await writeFile2(uploadComposePath, next);
319
+ } catch {
320
+ }
321
+ }
322
+
323
+ // src/lib/scaffold-auth-none.ts
324
+ import { mkdir, rm as rm2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
325
+ import { dirname, join as join3 } from "path";
326
+ async function stripDeps2(pkgPath, names) {
327
+ try {
328
+ const raw = await readFile3(pkgPath, "utf8");
329
+ const pkg = JSON.parse(raw);
330
+ for (const n of names) {
331
+ if (pkg.dependencies) delete pkg.dependencies[n];
332
+ if (pkg.devDependencies) delete pkg.devDependencies[n];
333
+ }
334
+ await writeFile3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
335
+ } catch {
336
+ }
337
+ }
338
+ var AUTH_ONLY_PATHS = [
339
+ "apps/microservices/auth",
340
+ "libs/auth-strategies",
341
+ "libs/auth-client",
342
+ "Dockerfile.ms-auth",
343
+ "apps/api/src/app/auth",
344
+ "apps/api/src/app/profile",
345
+ "apps/api/src/app/abilities",
346
+ "libs/shared/src/abilities",
347
+ "apps/client/src/components/auth",
348
+ "apps/client/src/routes/login.tsx",
349
+ "apps/client/src/routes/auth.callback.tsx",
350
+ "apps/client/src/routes/auth.oauth.callback.tsx",
351
+ "apps/client/src/routes/_dashboard/profile.tsx",
352
+ "libs/template-shared/src/lib/abilities"
353
+ ];
354
+ async function removeAuthOnlyPaths(targetDir) {
355
+ for (const p2 of AUTH_ONLY_PATHS) {
356
+ await rm2(join3(targetDir, p2), { recursive: true, force: true });
357
+ }
358
+ await stripDeps2(join3(targetDir, "apps/api/package.json"), [
359
+ "@icore/auth-client",
360
+ "cookie-parser"
361
+ ]);
362
+ }
363
+ async function stripTsconfigPath2(targetDir, alias) {
364
+ const tsconfigPath = join3(targetDir, "tsconfig.base.json");
365
+ try {
366
+ const src = await readFile3(tsconfigPath, "utf8");
367
+ const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
368
+ const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
369
+ if (pretty !== src) {
370
+ await writeFile3(tsconfigPath, pretty);
371
+ return;
372
+ }
373
+ const parsed = JSON.parse(src);
374
+ if (parsed.compilerOptions?.paths) delete parsed.compilerOptions.paths[alias];
375
+ await writeFile3(tsconfigPath, JSON.stringify(parsed));
376
+ } catch {
377
+ }
378
+ }
379
+ async function removeAuthTsconfigPaths(targetDir) {
380
+ for (const alias of [
381
+ "@icore/auth-client",
382
+ "@icore/auth-supabase",
383
+ "@icore/auth-firebase",
384
+ "@icore/auth-mongodb"
385
+ ]) {
386
+ await stripTsconfigPath2(targetDir, alias);
387
+ }
388
+ }
389
+ async function removeDockerComposeAuthService(targetDir) {
390
+ const composePath = join3(targetDir, "docker-compose.yml");
391
+ try {
392
+ const compose = await readFile3(composePath, "utf8");
393
+ 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, "");
394
+ await writeFile3(composePath, next);
395
+ } catch {
396
+ }
397
+ }
398
+ var GATEWAY_MAIN_TS = `import { Logger } from '@nestjs/common';
399
+ import { NestFactory } from '@nestjs/core';
400
+ import { NestExpressApplication } from '@nestjs/platform-express';
401
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
402
+ import { formatGatewayBanner } from '@icore/shared';
403
+ import { AppModule } from './app/app.module';
404
+ import { GATEWAY_SERVICES } from './app/gateway-services';
405
+ import pkg from '@icore/package.json';
406
+
407
+ const DEFAULT_PORT = 3001;
408
+
409
+ async function bootstrap() {
410
+ const app = await NestFactory.create<NestExpressApplication>(AppModule);
411
+ app.setGlobalPrefix('api');
412
+
413
+ const swaggerConfig = new DocumentBuilder()
414
+ .setTitle('iCore API')
415
+ .setDescription('iCore Gateway HTTP surface')
416
+ .setVersion(pkg.version)
417
+ .addBearerAuth()
418
+ .build();
419
+ const document = SwaggerModule.createDocument(app, swaggerConfig);
420
+ SwaggerModule.setup('api/docs', app, document);
421
+
422
+ const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
423
+ await app.listen(port);
424
+ }
425
+
426
+ bootstrap()
427
+ .then(() => {
428
+ const origin = process.env.API_ORIGIN ?? 'http://localhost';
429
+ const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
430
+ new Logger('API-Bootstrap').log(
431
+ formatGatewayBanner({ port, origin, services: GATEWAY_SERVICES }),
432
+ );
433
+ })
434
+ .catch((err) => {
435
+ new Logger('API-Bootstrap').error(
436
+ 'Gateway bootstrap failed',
437
+ err instanceof Error ? err.stack : err,
438
+ );
439
+ process.exit(1);
440
+ });
441
+ `;
442
+ var GATEWAY_APP_MODULE_TS = `import { join } from 'node:path';
443
+ import { Module } from '@nestjs/common';
444
+ import { ConfigModule } from '@nestjs/config';
445
+ import { ThrottlerModule, seconds } from '@nestjs/throttler';
446
+ import { StorageModule } from './storage/storage.module';
447
+ import { FeaturesModule } from './features.module';
448
+
449
+ @Module({
450
+ imports: [
451
+ ConfigModule.forRoot({
452
+ isGlobal: true,
453
+ envFilePath: [join(process.cwd(), 'apps/api/.env'), join(process.cwd(), '.env')],
454
+ }),
455
+ ThrottlerModule.forRoot([{ name: 'auth-burst', ttl: seconds(60), limit: 10 }]),
456
+ StorageModule,
457
+ FeaturesModule,
458
+ ],
459
+ })
460
+ export class AppModule {}
461
+ `;
462
+ var SHARED_CLIENT_TS = `// Browser-safe subset of @icore/shared.
463
+ // Import from '@icore/shared/client' in client-side code to avoid pulling
464
+ // in NestJS / Node.js-only modules (transport, strategies, contracts).
465
+ export * from './types';
466
+ `;
467
+ var SHARED_INDEX_TS = `export * from './env';
468
+ export * from './bootstrap';
469
+ export * from './jobs';
470
+ export * from './strategies';
471
+ export * from './transport';
472
+ export * from './types';
473
+ `;
474
+ var TEMPLATE_SHARED_INDEX_TS = `export * from './lib/api/create-api.js';
475
+ export * from './lib/stores/auth.store.js';
476
+ export * from './lib/stores/loading.store.js';
477
+ export * from './lib/i18n/create-i18n.js';
478
+ export * from './lib/i18n/keys.js';
479
+ export * from './lib/notify/use-notify.js';
480
+ export * from './lib/draft/index.js';
481
+ export * from './lib/landing/LandingPage.js';
482
+ export * from './lib/stores/theme.store.js';
483
+ `;
484
+ var SHADCN_MAIN_TSX = `import './globals.css';
485
+ import { StrictMode } from 'react';
486
+ import { createRoot } from 'react-dom/client';
487
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
488
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
489
+ import {
490
+ createIcoreApi,
491
+ createIcoreI18n,
492
+ ICORE_LOCALES,
493
+ useThemeStore,
494
+ } from '@icore/template-shared';
495
+ import { I18nextProvider } from 'react-i18next';
496
+ import { Toaster } from 'sonner';
497
+ import { routeTree } from './routeTree.gen';
498
+ import { wireShadcnNotifier } from './lib/notify';
499
+
500
+ const queryClient = new QueryClient({
501
+ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
502
+ });
503
+
504
+ const router = createRouter({ routeTree, context: { queryClient } });
505
+
506
+ declare module '@tanstack/react-router' {
507
+ interface Register {
508
+ router: typeof router;
509
+ }
510
+ }
511
+
512
+ const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
513
+
514
+ // Single shared API instance \u2014 used by every query in src/queries/
515
+ export const api = createIcoreApi({
516
+ baseUrl: import.meta.env.VITE_API_URL ?? '/api',
517
+ });
518
+
519
+ wireShadcnNotifier();
520
+
521
+ // Apply the theme class before React mounts so the first paint is correct
522
+ const applyTheme = (mode: 'light' | 'dark') => {
523
+ document.documentElement.classList.toggle('dark', mode === 'dark');
524
+ };
525
+ applyTheme(useThemeStore.getState().mode);
526
+ useThemeStore.subscribe((s) => applyTheme(s.mode));
527
+
528
+ createRoot(document.getElementById('root')!).render(
529
+ <StrictMode>
530
+ <I18nextProvider i18n={i18n}>
531
+ <QueryClientProvider client={queryClient}>
532
+ <RouterProvider router={router} />
533
+ <Toaster richColors />
534
+ </QueryClientProvider>
535
+ </I18nextProvider>
536
+ </StrictMode>,
537
+ );
538
+ `;
539
+ var SHADCN_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
540
+ import { MainLayout } from '../layouts/MainLayout';
541
+
542
+ export const Route = createFileRoute('/_dashboard')({
543
+ component: () => (
544
+ <MainLayout>
545
+ <Outlet />
546
+ </MainLayout>
547
+ ),
548
+ });
549
+ `;
550
+ var SHADCN_INDEX_TSX = `import { createFileRoute } from '@tanstack/react-router';
551
+ import { LandingPage } from '@icore/template-shared';
552
+
553
+ // All version strings are injected at build time by vite.config.mts
554
+ // (reads root package.json via fs.readFileSync so they stay accurate
555
+ // even when workspace packages are bumped independently).
556
+ const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
557
+
558
+ export const Route = createFileRoute('/')({
559
+ component: () => (
560
+ <LandingPage
561
+ coreVersion={APP_VERSION}
562
+ uiLibrary="shadcn"
563
+ deps={[
564
+ { name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
565
+ { name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
566
+ { name: 'tailwindcss', version: (import.meta.env.VITE_DEP_TAILWINDCSS as string) ?? '?' },
567
+ {
568
+ name: '@tanstack/react-router',
569
+ version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
570
+ },
571
+ {
572
+ name: '@tanstack/react-query',
573
+ version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
574
+ },
575
+ { name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
576
+ { name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
577
+ ]}
578
+ ctaHref="/dashboard"
579
+ ctaLabel="Dashboard \u2192"
580
+ />
581
+ ),
582
+ });
583
+ `;
584
+ var SHADCN_LAYOUT_HEADER_TSX = `import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
585
+ import { ThemeToggle } from '../ThemeToggle';
586
+
587
+ const LOCALES: { code: IcoreLocale; label: string }[] = [
588
+ { code: 'en', label: 'EN' },
589
+ { code: 'ru', label: 'RU' },
590
+ { code: 'he', label: 'HE' },
591
+ ];
592
+
593
+ export function LayoutHeader() {
594
+ function handleLocale(code: IcoreLocale) {
595
+ setStoredLocale(code);
596
+ window.location.reload();
597
+ }
598
+
599
+ return (
600
+ <header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-[--color-border] bg-[--color-background]/80 px-4 backdrop-blur-sm">
601
+ <div className="flex items-center gap-2">
602
+ <div className="flex h-6 w-6 items-center justify-center rounded bg-[--color-primary]">
603
+ <span className="text-xs font-bold text-[--color-primary-foreground]">i</span>
604
+ </div>
605
+ <span className="text-sm font-semibold tracking-tight">iCore</span>
606
+ </div>
607
+
608
+ <div className="flex items-center gap-1">
609
+ <div className="flex items-center rounded-md border border-[--color-border] overflow-hidden mr-2">
610
+ {LOCALES.map(({ code, label }) => (
611
+ <button
612
+ key={code}
613
+ type="button"
614
+ onClick={() => handleLocale(code)}
615
+ className="px-2.5 py-1 text-xs text-[--color-muted-foreground] hover:bg-[--color-muted] hover:text-[--color-foreground] transition-colors cursor-pointer"
616
+ >
617
+ {label}
618
+ </button>
619
+ ))}
620
+ </div>
621
+
622
+ <ThemeToggle />
623
+ </div>
624
+ </header>
625
+ );
626
+ }
627
+ `;
628
+ var ANTD_MAIN_TSX = `import './globals.less';
629
+ import { StrictMode } from 'react';
630
+ import { createRoot } from 'react-dom/client';
631
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
632
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
633
+ import { ConfigProvider, App as AntApp, theme } from 'antd';
634
+ import { I18nextProvider } from 'react-i18next';
635
+ import {
636
+ createIcoreApi,
637
+ createIcoreI18n,
638
+ ICORE_LOCALES,
639
+ useThemeStore,
640
+ } from '@icore/template-shared';
641
+ import { routeTree } from './routeTree.gen';
642
+
643
+ const queryClient = new QueryClient({
644
+ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
645
+ });
646
+
647
+ const router = createRouter({ routeTree, context: { queryClient } });
648
+
649
+ declare module '@tanstack/react-router' {
650
+ interface Register {
651
+ router: typeof router;
652
+ }
653
+ }
654
+
655
+ const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
656
+
657
+ // Single shared API instance \u2014 used by every query in src/queries/
658
+ export const api = createIcoreApi({
659
+ baseUrl: import.meta.env.VITE_API_URL ?? '/api',
660
+ });
661
+
662
+ function Root() {
663
+ const mode = useThemeStore((s) => s.mode);
664
+ const algorithm = mode === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
665
+ return (
666
+ <ConfigProvider theme={{ algorithm, token: { colorPrimary: '#22c55e', colorLink: '#22c55e' } }}>
667
+ <AntApp>
668
+ <QueryClientProvider client={queryClient}>
669
+ <RouterProvider router={router} />
670
+ </QueryClientProvider>
671
+ </AntApp>
672
+ </ConfigProvider>
673
+ );
674
+ }
675
+
676
+ createRoot(document.getElementById('root')!).render(
677
+ <StrictMode>
678
+ <I18nextProvider i18n={i18n}>
679
+ <Root />
680
+ </I18nextProvider>
681
+ </StrictMode>,
682
+ );
683
+ `;
684
+ var ANTD_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
685
+ import { MainLayout } from '../layouts/MainLayout';
686
+
687
+ export const Route = createFileRoute('/_dashboard')({
688
+ component: () => (
689
+ <MainLayout>
690
+ <Outlet />
691
+ </MainLayout>
692
+ ),
693
+ });
694
+ `;
695
+ var ANTD_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
696
+ import { LandingPage } from '@icore/template-shared';
697
+
698
+ // All version strings are injected at build time by vite.config.mts
699
+ // (reads root package.json via fs.readFileSync so they stay accurate
700
+ // even when workspace packages are bumped independently).
701
+ const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
702
+
703
+ export const Route = createFileRoute('/')({
704
+ component: () => (
705
+ <LandingPage
706
+ coreVersion={APP_VERSION}
707
+ uiLibrary="antd"
708
+ deps={[
709
+ { name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
710
+ { name: 'antd', version: (import.meta.env.VITE_DEP_ANTD as string) ?? '?' },
711
+ { name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
712
+ {
713
+ name: '@tanstack/react-router',
714
+ version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
715
+ },
716
+ {
717
+ name: '@tanstack/react-query',
718
+ version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
719
+ },
720
+ { name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
721
+ { name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
722
+ ]}
723
+ ctaHref="/dashboard"
724
+ ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
725
+ />
726
+ ),
727
+ });
728
+ `;
729
+ var ANTD_LAYOUT_HEADER_TSX = `import { Button, Layout, Space } from 'antd';
730
+ import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
731
+ import { ThemeToggle } from '../ThemeToggle';
732
+
733
+ const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
734
+
735
+ const LOCALES: { code: IcoreLocale; label: string }[] = [
736
+ { code: 'en', label: 'EN' },
737
+ { code: 'ru', label: 'RU' },
738
+ { code: 'he', label: 'HE' },
739
+ ];
740
+
741
+ export function LayoutHeader() {
742
+ function handleLocale(code: IcoreLocale) {
743
+ setStoredLocale(code);
744
+ window.location.reload();
745
+ }
746
+
747
+ return (
748
+ <Layout.Header
749
+ style={{
750
+ display: 'flex',
751
+ alignItems: 'center',
752
+ justifyContent: 'space-between',
753
+ padding: '0 24px',
754
+ }}
755
+ >
756
+ <Space>
757
+ <span style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>iCore</span>
758
+ <span
759
+ style={{
760
+ color: 'rgba(255,255,255,0.45)',
761
+ fontSize: 11,
762
+ background: 'rgba(255,255,255,0.1)',
763
+ padding: '1px 6px',
764
+ borderRadius: 4,
765
+ }}
766
+ >
767
+ v{APP_VERSION}
768
+ </span>
769
+ </Space>
770
+
771
+ <Space size="middle">
772
+ <Space size={4}>
773
+ {LOCALES.map(({ code, label }) => (
774
+ <Button
775
+ key={code}
776
+ size="small"
777
+ type="text"
778
+ style={{ color: 'rgba(255,255,255,0.65)' }}
779
+ onClick={() => handleLocale(code)}
780
+ >
781
+ {label}
782
+ </Button>
783
+ ))}
784
+ </Space>
785
+
786
+ <ThemeToggle />
787
+ </Space>
788
+ </Layout.Header>
789
+ );
790
+ }
791
+ `;
792
+ var MUI_MAIN_TSX = `import './globals.css';
793
+ import { StrictMode, useMemo } from 'react';
794
+ import { createRoot } from 'react-dom/client';
795
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
796
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
797
+ import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
798
+ import { I18nextProvider } from 'react-i18next';
799
+ import {
800
+ createIcoreApi,
801
+ createIcoreI18n,
802
+ ICORE_LOCALES,
803
+ useThemeStore,
804
+ } from '@icore/template-shared';
805
+ import { routeTree } from './routeTree.gen';
806
+ import { wireMuiNotifier } from './lib/notify';
807
+
808
+ const queryClient = new QueryClient({
809
+ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
810
+ });
811
+
812
+ const router = createRouter({ routeTree, context: { queryClient } });
813
+
814
+ declare module '@tanstack/react-router' {
815
+ interface Register {
816
+ router: typeof router;
817
+ }
818
+ }
819
+
820
+ const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
821
+
822
+ export const api = createIcoreApi({
823
+ baseUrl: import.meta.env.VITE_API_URL ?? '/api',
824
+ });
825
+
826
+ wireMuiNotifier();
827
+
828
+ function Root() {
829
+ const mode = useThemeStore((s) => s.mode);
830
+ const theme = useMemo(
831
+ () => createTheme({ palette: { mode, primary: { main: '#22c55e' } } }),
832
+ [mode],
833
+ );
834
+ return (
835
+ <ThemeProvider theme={theme}>
836
+ <CssBaseline />
837
+ <QueryClientProvider client={queryClient}>
838
+ <RouterProvider router={router} />
839
+ </QueryClientProvider>
840
+ </ThemeProvider>
841
+ );
842
+ }
843
+
844
+ createRoot(document.getElementById('root')!).render(
845
+ <StrictMode>
846
+ <I18nextProvider i18n={i18n}>
847
+ <Root />
848
+ </I18nextProvider>
849
+ </StrictMode>,
850
+ );
851
+ `;
852
+ var MUI_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
853
+ import { MainLayout } from '../layouts/MainLayout';
854
+
855
+ export const Route = createFileRoute('/_dashboard')({
856
+ component: () => (
857
+ <MainLayout>
858
+ <Outlet />
859
+ </MainLayout>
860
+ ),
861
+ });
862
+ `;
863
+ var MUI_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
864
+ import { LandingPage } from '@icore/template-shared';
865
+
866
+ // All version strings are injected at build time by vite.config.mts
867
+ // (reads root package.json via fs.readFileSync so they stay accurate
868
+ // even when workspace packages are bumped independently).
869
+ const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
870
+
871
+ export const Route = createFileRoute('/')({
872
+ component: () => (
873
+ <LandingPage
874
+ coreVersion={APP_VERSION}
875
+ uiLibrary="mui"
876
+ deps={[
877
+ { name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
878
+ { name: '@mui/material', version: (import.meta.env.VITE_DEP_MUI as string) ?? '?' },
879
+ { name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
880
+ {
881
+ name: '@tanstack/react-router',
882
+ version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
883
+ },
884
+ {
885
+ name: '@tanstack/react-query',
886
+ version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
887
+ },
888
+ { name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
889
+ { name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
890
+ ]}
891
+ ctaHref="/dashboard"
892
+ ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
893
+ />
894
+ ),
895
+ });
896
+ `;
897
+ var MUI_LAYOUT_HEADER_TSX = `import { AppBar, Box, Button, Toolbar, Typography } from '@mui/material';
898
+ import { useTranslation } from 'react-i18next';
899
+ import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
900
+ import { ThemeToggle } from '../ThemeToggle';
901
+
902
+ const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
903
+
904
+ const LOCALES: { code: IcoreLocale; label: string }[] = [
905
+ { code: 'en', label: 'EN' },
906
+ { code: 'ru', label: 'RU' },
907
+ { code: 'he', label: 'HE' },
908
+ ];
909
+
910
+ export function LayoutHeader() {
911
+ const { i18n } = useTranslation();
912
+ const currentLocale = i18n.language as IcoreLocale;
913
+
914
+ function handleLocale(code: IcoreLocale) {
915
+ setStoredLocale(code);
916
+ window.location.reload();
917
+ }
918
+
919
+ return (
920
+ <AppBar position="sticky" color="default" elevation={1}>
921
+ <Toolbar sx={{ justifyContent: 'space-between' }}>
922
+ <Typography variant="h6" component="div" fontWeight={600}>
923
+ iCore{' '}
924
+ <span style={{ opacity: 0.6, fontSize: '0.75em', fontWeight: 400 }}>v{APP_VERSION}</span>
925
+ </Typography>
926
+
927
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
928
+ <Box sx={{ display: 'flex', gap: 0.5 }}>
929
+ {LOCALES.map(({ code, label }) => (
930
+ <Button
931
+ key={code}
932
+ size="small"
933
+ variant={currentLocale === code ? 'contained' : 'text'}
934
+ onClick={() => handleLocale(code)}
935
+ >
936
+ {label}
937
+ </Button>
938
+ ))}
939
+ </Box>
940
+
941
+ <ThemeToggle />
942
+ </Box>
943
+ </Toolbar>
944
+ </AppBar>
945
+ );
946
+ }
947
+ `;
948
+ var COMMON_VARIANTS = {
949
+ "apps/api/src/main.ts": GATEWAY_MAIN_TS,
950
+ "apps/api/src/app/app.module.ts": GATEWAY_APP_MODULE_TS,
951
+ "libs/shared/src/client.ts": SHARED_CLIENT_TS,
952
+ "libs/shared/src/index.ts": SHARED_INDEX_TS,
953
+ "libs/template-shared/src/index.ts": TEMPLATE_SHARED_INDEX_TS
954
+ };
955
+ var UI_VARIANTS = {
956
+ shadcn: {
957
+ "apps/client/src/main.tsx": SHADCN_MAIN_TSX,
958
+ "apps/client/src/routes/_dashboard.tsx": SHADCN_DASHBOARD_TSX,
959
+ "apps/client/src/routes/index.tsx": SHADCN_INDEX_TSX,
960
+ "apps/client/src/components/layout/LayoutHeader.tsx": SHADCN_LAYOUT_HEADER_TSX
961
+ },
962
+ antd: {
963
+ "apps/client/src/main.tsx": ANTD_MAIN_TSX,
964
+ "apps/client/src/routes/_dashboard.tsx": ANTD_DASHBOARD_TSX,
965
+ "apps/client/src/routes/index.tsx": ANTD_INDEX_TSX,
966
+ "apps/client/src/components/layout/LayoutHeader.tsx": ANTD_LAYOUT_HEADER_TSX
967
+ },
968
+ mui: {
969
+ "apps/client/src/main.tsx": MUI_MAIN_TSX,
970
+ "apps/client/src/routes/_dashboard.tsx": MUI_DASHBOARD_TSX,
971
+ "apps/client/src/routes/index.tsx": MUI_INDEX_TSX,
972
+ "apps/client/src/components/layout/LayoutHeader.tsx": MUI_LAYOUT_HEADER_TSX
973
+ }
974
+ };
975
+ async function applyAuthNoneVariants(targetDir, ui) {
976
+ const uiFiles = UI_VARIANTS[ui] ?? UI_VARIANTS["shadcn"];
977
+ const all = { ...COMMON_VARIANTS, ...uiFiles };
978
+ for (const [rel, content] of Object.entries(all)) {
979
+ const dest = join3(targetDir, rel);
980
+ try {
981
+ await mkdir(dirname(dest), { recursive: true });
982
+ await writeFile3(dest, content);
983
+ } catch {
984
+ }
985
+ }
319
986
  }
320
987
 
321
988
  // src/manifest/wire-features.ts
322
- import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
323
- import { join as join4 } from "path";
989
+ import { readFile as readFile5, writeFile as writeFile5, rm as rm4, rmdir, unlink } from "fs/promises";
990
+ import { join as join5 } from "path";
324
991
 
325
992
  // src/manifest/index.ts
326
993
  var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
@@ -480,8 +1147,8 @@ var MANIFEST = {
480
1147
  };
481
1148
 
482
1149
  // src/manifest/wire-provider.ts
483
- import { readFile as readFile3, writeFile as writeFile3, rm as rm2 } from "fs/promises";
484
- import { join as join3 } from "path";
1150
+ import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
1151
+ import { join as join4 } from "path";
485
1152
  async function writeProvider(targetDir, axis, provider) {
486
1153
  const nestModule = axis.section[provider]?.nestModule;
487
1154
  if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
@@ -492,27 +1159,27 @@ const ENV_PATH = '${axis.envPath}';
492
1159
 
493
1160
  export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
494
1161
  `;
495
- await writeFile3(join3(targetDir, axis.providerFile), content);
1162
+ await writeFile4(join4(targetDir, axis.providerFile), content);
496
1163
  }
497
1164
  async function stripJsonKeys(path, drop) {
498
1165
  try {
499
- const pkg = JSON.parse(await readFile3(path, "utf8"));
1166
+ const pkg = JSON.parse(await readFile4(path, "utf8"));
500
1167
  for (const field of ["dependencies", "devDependencies"]) {
501
1168
  const deps = pkg[field];
502
1169
  if (!deps) continue;
503
1170
  for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
504
1171
  }
505
- await writeFile3(path, JSON.stringify(pkg, null, 2) + "\n");
1172
+ await writeFile4(path, JSON.stringify(pkg, null, 2) + "\n");
506
1173
  } catch {
507
1174
  }
508
1175
  }
509
1176
  async function stripTsconfigKeys(targetDir, aliases) {
510
- const path = join3(targetDir, "tsconfig.base.json");
1177
+ const path = join4(targetDir, "tsconfig.base.json");
511
1178
  try {
512
- const parsed = JSON.parse(await readFile3(path, "utf8"));
1179
+ const parsed = JSON.parse(await readFile4(path, "utf8"));
513
1180
  const paths = parsed.compilerOptions?.paths;
514
1181
  if (paths) for (const a of aliases) delete paths[a];
515
- await writeFile3(path, JSON.stringify(parsed, null, 2) + "\n");
1182
+ await writeFile4(path, JSON.stringify(parsed, null, 2) + "\n");
516
1183
  } catch {
517
1184
  }
518
1185
  }
@@ -521,10 +1188,10 @@ async function cleanupUnusedAxis(targetDir, axis, chosen) {
521
1188
  if (provider === chosen) continue;
522
1189
  const unit = axis.section[provider];
523
1190
  for (const dir of unit.libDirs)
524
- await rm2(join3(targetDir, dir), { recursive: true, force: true });
525
- for (const t of unit.appTests ?? []) await rm2(join3(targetDir, t), { force: true });
1191
+ await rm3(join4(targetDir, dir), { recursive: true, force: true });
1192
+ for (const t of unit.appTests ?? []) await rm3(join4(targetDir, t), { force: true });
526
1193
  const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
527
- await stripJsonKeys(join3(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
1194
+ await stripJsonKeys(join4(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
528
1195
  await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
529
1196
  }
530
1197
  }
@@ -553,7 +1220,7 @@ async function writeFeaturesWiring(targetDir, opts) {
553
1220
  })
554
1221
  export class FeaturesModule {}
555
1222
  `;
556
- await writeFile4(join4(targetDir, FEATURES_MODULE), featuresModule);
1223
+ await writeFile5(join5(targetDir, FEATURES_MODULE), featuresModule);
557
1224
  const services = [];
558
1225
  if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
559
1226
  if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
@@ -567,14 +1234,14 @@ export const GATEWAY_SERVICES = [
567
1234
  ${entries}
568
1235
  ];
569
1236
  `;
570
- await writeFile4(join4(targetDir, GATEWAY_SERVICES), gatewayServices);
1237
+ await writeFile5(join5(targetDir, GATEWAY_SERVICES), gatewayServices);
571
1238
  }
572
1239
  async function stripJobsDockerCompose(targetDir) {
573
- const composePath = join4(targetDir, "docker-compose.yml");
1240
+ const composePath = join5(targetDir, "docker-compose.yml");
574
1241
  try {
575
- const compose = await readFile4(composePath, "utf8");
1242
+ const compose = await readFile5(composePath, "utf8");
576
1243
  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, "");
577
- await writeFile4(composePath, next);
1244
+ await writeFile5(composePath, next);
578
1245
  } catch {
579
1246
  }
580
1247
  }
@@ -584,18 +1251,38 @@ async function cleanupUnusedFeatures(targetDir, opts) {
584
1251
  if (chosen.has(key)) continue;
585
1252
  const unit = FEATURES[key];
586
1253
  for (const dir of unit.libDirs)
587
- await rm3(join4(targetDir, dir), { recursive: true, force: true });
1254
+ await rm4(join5(targetDir, dir), { recursive: true, force: true });
588
1255
  const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
589
- await stripJsonKeys(join4(targetDir, API_PKG), (k) => dropKeys.has(k));
1256
+ await stripJsonKeys(join5(targetDir, API_PKG), (k) => dropKeys.has(k));
590
1257
  await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
591
1258
  if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
592
1259
  if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
1260
+ if (key === "jobs") {
1261
+ try {
1262
+ await unlink(join5(targetDir, "libs/shared/src/jobs.ts"));
1263
+ } catch {
1264
+ }
1265
+ try {
1266
+ await unlink(join5(targetDir, "libs/shared/src/__tests__/jobs.unit.test.ts"));
1267
+ } catch {
1268
+ }
1269
+ const sharedIdx = join5(targetDir, "libs/shared/src/index.ts");
1270
+ try {
1271
+ const src = await readFile5(sharedIdx, "utf8");
1272
+ await writeFile5(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
1273
+ } catch {
1274
+ }
1275
+ }
1276
+ }
1277
+ try {
1278
+ await rmdir(join5(targetDir, "apps/client/src/queries"));
1279
+ } catch {
593
1280
  }
594
1281
  }
595
1282
 
596
1283
  // src/manifest/wire-client.ts
597
- import { writeFile as writeFile5 } from "fs/promises";
598
- import { join as join5 } from "path";
1284
+ import { writeFile as writeFile6 } from "fs/promises";
1285
+ import { join as join6 } from "path";
599
1286
  var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
600
1287
  var BASE_NAV = [
601
1288
  { to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
@@ -633,12 +1320,12 @@ export const NAV_CONFIG: NavItem[] = [
633
1320
  ` + entries.map(renderEntry).join("\n") + `
634
1321
  ];
635
1322
  `;
636
- await writeFile5(join5(targetDir, NAV_CONFIG_FILE), content);
1323
+ await writeFile6(join6(targetDir, NAV_CONFIG_FILE), content);
637
1324
  }
638
1325
 
639
1326
  // src/manifest/blueprint.ts
640
- import { writeFile as writeFile6 } from "fs/promises";
641
- import { join as join6 } from "path";
1327
+ import { writeFile as writeFile7 } from "fs/promises";
1328
+ import { join as join7 } from "path";
642
1329
  async function writeBlueprintJson(targetDir, opts) {
643
1330
  const blueprint = {
644
1331
  schemaVersion: 1,
@@ -653,10 +1340,10 @@ async function writeBlueprintJson(targetDir, opts) {
653
1340
  transport: opts.transport,
654
1341
  packageManager: opts.packageManager
655
1342
  };
656
- await writeFile6(join6(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
1343
+ await writeFile7(join7(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
657
1344
  }
658
1345
  async function writeJson(targetDir, rel, data) {
659
- await writeFile6(join6(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
1346
+ await writeFile7(join7(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
660
1347
  }
661
1348
  async function writeServiceBlueprints(targetDir, opts) {
662
1349
  const t = opts.transport;
@@ -750,11 +1437,11 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
750
1437
  var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
751
1438
 
752
1439
  // src/lib/scaffold-pkg.ts
753
- import { readFile as readFile5, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
754
- import { join as join7 } from "path";
1440
+ import { readFile as readFile6, writeFile as writeFile8, mkdir as mkdir2, readdir } from "fs/promises";
1441
+ import { join as join8 } from "path";
755
1442
  async function writePnpmWorkspace(targetDir) {
756
- const pkgPath = join7(targetDir, "package.json");
757
- const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
1443
+ const pkgPath = join8(targetDir, "package.json");
1444
+ const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
758
1445
  const workspaces = pkg.workspaces ?? [];
759
1446
  const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
760
1447
  const allowBuilds = [
@@ -777,7 +1464,7 @@ ${packagesBlock}
777
1464
  allowBuilds:
778
1465
  ${allowBuilds}
779
1466
  `;
780
- await writeFile7(join7(targetDir, "pnpm-workspace.yaml"), content);
1467
+ await writeFile8(join8(targetDir, "pnpm-workspace.yaml"), content);
781
1468
  }
782
1469
  async function rewritePnpmWorkspaceDeps(targetDir) {
783
1470
  async function walk2(dir) {
@@ -790,9 +1477,9 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
790
1477
  }
791
1478
  for (const e of entries) {
792
1479
  if (e.isDirectory() && e.name !== "node_modules") {
793
- found.push(...await walk2(join7(dir, e.name)));
1480
+ found.push(...await walk2(join8(dir, e.name)));
794
1481
  } else if (e.isFile() && e.name === "package.json") {
795
- found.push(join7(dir, e.name));
1482
+ found.push(join8(dir, e.name));
796
1483
  }
797
1484
  }
798
1485
  return found;
@@ -800,17 +1487,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
800
1487
  const pkgFiles = await walk2(targetDir);
801
1488
  for (const f of pkgFiles) {
802
1489
  try {
803
- const raw = await readFile5(f, "utf8");
1490
+ const raw = await readFile6(f, "utf8");
804
1491
  const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
805
- if (next !== raw) await writeFile7(f, next);
1492
+ if (next !== raw) await writeFile8(f, next);
806
1493
  } catch {
807
1494
  }
808
1495
  }
809
1496
  }
810
1497
  async function patchGitignoreForPm(targetDir, pm) {
811
- const giPath = join7(targetDir, ".gitignore");
1498
+ const giPath = join8(targetDir, ".gitignore");
812
1499
  try {
813
- let src = await readFile5(giPath, "utf8");
1500
+ let src = await readFile6(giPath, "utf8");
814
1501
  src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
815
1502
  if (pm !== "yarn") {
816
1503
  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, "");
@@ -825,7 +1512,7 @@ async function patchGitignoreForPm(targetDir, pm) {
825
1512
  src += "\n# npm\nnpm-debug.log*\n";
826
1513
  }
827
1514
  }
828
- await writeFile7(giPath, src);
1515
+ await writeFile8(giPath, src);
829
1516
  } catch {
830
1517
  }
831
1518
  }
@@ -833,14 +1520,15 @@ async function writeAiFiles(targetDir, opts) {
833
1520
  const pm = opts.packageManager;
834
1521
  const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
835
1522
  const devCmd = pmRun(pm, "dev");
836
- const activeMSes = ["auth (port 4001)"];
1523
+ const activeMSes = [];
1524
+ if (opts.authProvider !== "none") activeMSes.push("auth (port 4001)");
837
1525
  if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
838
1526
  if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
839
1527
  if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
840
1528
  if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
841
1529
  const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
842
1530
  const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
843
- await writeFile7(join7(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
1531
+ await writeFile8(join8(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
844
1532
  const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
845
1533
  const readme = `# ${opts.projectName}
846
1534
 
@@ -862,9 +1550,7 @@ async function writeAiFiles(targetDir, opts) {
862
1550
 
863
1551
  \`\`\`bash
864
1552
  # 1. Fill in provider credentials
865
- # apps/microservices/auth/.env
866
- # apps/microservices/upload/.env (if upload is enabled)
867
- # apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
1553
+ ${opts.authProvider !== "none" ? "# apps/microservices/auth/.env\n" : ""}${opts.upload !== "none" ? "# apps/microservices/upload/.env\n" : ""}# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
868
1554
 
869
1555
  # 2. Start everything
870
1556
  ${devCmd}
@@ -890,7 +1576,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
890
1576
 
891
1577
  Apache-2.0
892
1578
  `;
893
- await writeFile7(join7(targetDir, "README.md"), readme);
1579
+ await writeFile8(join8(targetDir, "README.md"), readme);
894
1580
  const agents = `# ${opts.projectName} \u2014 Agent Instructions
895
1581
 
896
1582
  ## Stack snapshot
@@ -960,10 +1646,10 @@ ${nx} g @nx/nest:resource # generate NestJS resource
960
1646
 
961
1647
  | File | Key vars |
962
1648
  |------|----------|
963
- | \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
964
- ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
965
- ` : ""}| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
966
- | \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
1649
+ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
1650
+ ` : ""}${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
1651
+ ` : ""}${opts.example !== "none" ? `| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
1652
+ ` : ""}| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
967
1653
 
968
1654
  ## Testing
969
1655
 
@@ -971,8 +1657,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
971
1657
  - Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
972
1658
  - Run: \`${nx} test <project>\`
973
1659
  `;
974
- await writeFile7(join7(targetDir, "AGENTS.md"), agents);
975
- await mkdir(join7(targetDir, ".claude"), { recursive: true });
1660
+ await writeFile8(join8(targetDir, "AGENTS.md"), agents);
1661
+ await mkdir2(join8(targetDir, ".claude"), { recursive: true });
976
1662
  const mcpServers = {
977
1663
  nx: {
978
1664
  command: "npx",
@@ -1013,8 +1699,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
1013
1699
  ]
1014
1700
  }
1015
1701
  };
1016
- await writeFile7(
1017
- join7(targetDir, ".claude", "settings.json"),
1702
+ await writeFile8(
1703
+ join8(targetDir, ".claude", "settings.json"),
1018
1704
  JSON.stringify(settings, null, 2) + "\n"
1019
1705
  );
1020
1706
  }
@@ -1034,20 +1720,20 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
1034
1720
  ".vscode"
1035
1721
  ]);
1036
1722
  async function copyTree(src, dest) {
1037
- await mkdir2(dest, { recursive: true });
1723
+ await mkdir3(dest, { recursive: true });
1038
1724
  const entries = await readdir2(src, { withFileTypes: true });
1039
1725
  for (const entry of entries) {
1040
1726
  if (IGNORE_TOP.has(entry.name)) continue;
1041
- const s = join8(src, entry.name);
1042
- const d = join8(dest, entry.name);
1727
+ const s = join9(src, entry.name);
1728
+ const d = join9(dest, entry.name);
1043
1729
  if (entry.isDirectory()) await copyTree(s, d);
1044
1730
  else if (entry.isFile()) await copyFile(s, d);
1045
1731
  }
1046
1732
  }
1047
1733
  async function selectClientTemplate(targetDir, opts) {
1048
- const templatesRoot = join8(targetDir, "apps/templates");
1049
- const chosen = join8(templatesRoot, `client-${opts.ui}`);
1050
- const destClient = join8(targetDir, "apps/client");
1734
+ const templatesRoot = join9(targetDir, "apps/templates");
1735
+ const chosen = join9(templatesRoot, `client-${opts.ui}`);
1736
+ const destClient = join9(targetDir, "apps/client");
1051
1737
  let chosenUi = opts.ui;
1052
1738
  try {
1053
1739
  const s = await stat(chosen);
@@ -1055,9 +1741,9 @@ async function selectClientTemplate(targetDir, opts) {
1055
1741
  await copyTree(chosen, destClient);
1056
1742
  } catch {
1057
1743
  chosenUi = "shadcn";
1058
- await copyTree(join8(templatesRoot, "client-shadcn"), destClient);
1744
+ await copyTree(join9(templatesRoot, "client-shadcn"), destClient);
1059
1745
  }
1060
- await rm4(templatesRoot, { recursive: true, force: true });
1746
+ await rm5(templatesRoot, { recursive: true, force: true });
1061
1747
  await rewriteClientPaths(destClient, chosenUi);
1062
1748
  }
1063
1749
  async function rewriteClientPaths(clientDir, ui) {
@@ -1070,11 +1756,11 @@ async function rewriteClientPaths(clientDir, ui) {
1070
1756
  "eslint.config.mjs"
1071
1757
  ];
1072
1758
  for (const rel of candidates) {
1073
- const path = join8(clientDir, rel);
1759
+ const path = join9(clientDir, rel);
1074
1760
  try {
1075
- const raw = await readFile6(path, "utf8");
1761
+ const raw = await readFile7(path, "utf8");
1076
1762
  const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
1077
- if (next !== raw) await writeFile8(path, next);
1763
+ if (next !== raw) await writeFile9(path, next);
1078
1764
  } catch {
1079
1765
  }
1080
1766
  }
@@ -1090,12 +1776,12 @@ function gitInit(cwd, projectName) {
1090
1776
  }
1091
1777
  function resolveYarnBin(cwd) {
1092
1778
  try {
1093
- const yarnrc = readFileSync(join8(cwd, ".yarnrc.yml"), "utf8");
1779
+ const yarnrc = readFileSync(join9(cwd, ".yarnrc.yml"), "utf8");
1094
1780
  const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
1095
- if (match?.[1]) return join8(cwd, match[1].trim());
1781
+ if (match?.[1]) return join9(cwd, match[1].trim());
1096
1782
  } catch {
1097
1783
  }
1098
- return join8(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
1784
+ return join9(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
1099
1785
  }
1100
1786
  function runInstall(cwd, pm) {
1101
1787
  if (pm === "yarn") {
@@ -1106,7 +1792,8 @@ function runInstall(cwd, pm) {
1106
1792
  spawnSync("pnpm", ["install"], { cwd, stdio: "inherit" });
1107
1793
  }
1108
1794
  }
1109
- async function scaffold(opts, templatesDir) {
1795
+ async function scaffold(rawOpts, templatesDir) {
1796
+ const opts = rawOpts.authProvider === "none" && rawOpts.example !== "none" ? { ...rawOpts, example: "none" } : rawOpts;
1110
1797
  await copyTree(templatesDir, opts.targetDir);
1111
1798
  await rewriteRootPackageJson(opts.targetDir, opts);
1112
1799
  if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
@@ -1117,6 +1804,10 @@ async function scaffold(opts, templatesDir) {
1117
1804
  await writeRootEnv(opts.targetDir, opts);
1118
1805
  await selectClientTemplate(opts.targetDir, opts);
1119
1806
  await writeClientEnv(opts.targetDir);
1807
+ if (opts.authProvider === "none") {
1808
+ await removeAuthOnlyPaths(opts.targetDir);
1809
+ await applyAuthNoneVariants(opts.targetDir, opts.ui);
1810
+ }
1120
1811
  if (opts.upload === "none") await removeUploadStack(opts.targetDir);
1121
1812
  await cleanupUnusedFeatures(opts.targetDir, opts);
1122
1813
  await writeFeaturesWiring(opts.targetDir, opts);
@@ -1125,7 +1816,8 @@ async function scaffold(opts, templatesDir) {
1125
1816
  await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
1126
1817
  await writeAuthProvider(opts.targetDir, opts.authProvider);
1127
1818
  } else {
1128
- await removeAuthStack(opts.targetDir);
1819
+ await removeAuthTsconfigPaths(opts.targetDir);
1820
+ await removeDockerComposeAuthService(opts.targetDir);
1129
1821
  }
1130
1822
  if (opts.upload !== "none") {
1131
1823
  await cleanupUnusedStorage(opts.targetDir, opts.upload);
@@ -1138,13 +1830,20 @@ async function scaffold(opts, templatesDir) {
1138
1830
  await writeDbProvider(opts.targetDir, opts.dbProvider);
1139
1831
  }
1140
1832
  await pruneRootProviderDeps(opts.targetDir, opts);
1833
+ if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none" && opts.payment === "none") {
1834
+ await removeStrategiesLib(opts.targetDir);
1835
+ }
1836
+ try {
1837
+ await rmdir2(join9(opts.targetDir, "apps/microservices"));
1838
+ } catch {
1839
+ }
1141
1840
  await writeBlueprintJson(opts.targetDir, opts);
1142
1841
  await writeServiceBlueprints(opts.targetDir, opts);
1143
1842
  if (opts.packageManager === "yarn") {
1144
- await writeFile8(join8(opts.targetDir, "yarn.lock"), "");
1843
+ await writeFile9(join9(opts.targetDir, "yarn.lock"), "");
1145
1844
  } else {
1146
- await rm4(join8(opts.targetDir, ".yarn"), { recursive: true, force: true });
1147
- await rm4(join8(opts.targetDir, ".yarnrc.yml"), { force: true });
1845
+ await rm5(join9(opts.targetDir, ".yarn"), { recursive: true, force: true });
1846
+ await rm5(join9(opts.targetDir, ".yarnrc.yml"), { force: true });
1148
1847
  }
1149
1848
  if (opts.packageManager === "pnpm") {
1150
1849
  await writePnpmWorkspace(opts.targetDir);
@@ -1159,12 +1858,12 @@ async function scaffold(opts, templatesDir) {
1159
1858
  // src/lib/prompts.ts
1160
1859
  import * as p from "@clack/prompts";
1161
1860
  import { resolve } from "path";
1162
- import { readFile as readFile8 } from "fs/promises";
1163
- import { dirname, join as join9 } from "path";
1861
+ import { readFile as readFile9 } from "fs/promises";
1862
+ import { dirname as dirname2, join as join10 } from "path";
1164
1863
  import { fileURLToPath } from "url";
1165
1864
 
1166
1865
  // src/lib/config.ts
1167
- import { readFile as readFile7 } from "fs/promises";
1866
+ import { readFile as readFile8 } from "fs/promises";
1168
1867
  var ConfigFileError = class extends Error {
1169
1868
  constructor(message) {
1170
1869
  super(message);
@@ -1235,7 +1934,7 @@ function validateConfig(raw) {
1235
1934
  async function loadConfig(filePath) {
1236
1935
  let raw;
1237
1936
  try {
1238
- raw = await readFile7(filePath, "utf8");
1937
+ raw = await readFile8(filePath, "utf8");
1239
1938
  } catch {
1240
1939
  throw new ConfigFileError(`config file not found: ${filePath}`);
1241
1940
  }
@@ -1260,8 +1959,8 @@ function detectPackageManager() {
1260
1959
  }
1261
1960
  async function readSelfVersion() {
1262
1961
  try {
1263
- const here = dirname(fileURLToPath(import.meta.url));
1264
- const pkgRaw = await readFile8(join9(here, "..", "package.json"), "utf8");
1962
+ const here = dirname2(fileURLToPath(import.meta.url));
1963
+ const pkgRaw = await readFile9(join10(here, "..", "package.json"), "utf8");
1265
1964
  const pkg = JSON.parse(pkgRaw);
1266
1965
  return pkg.version ?? null;
1267
1966
  } catch {
@@ -1485,23 +2184,23 @@ Re-run with @latest to refresh:
1485
2184
  }
1486
2185
 
1487
2186
  // src/manifest/audit.ts
1488
- import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
1489
- import { join as join10 } from "path";
2187
+ import { readFile as readFile10, readdir as readdir3 } from "fs/promises";
2188
+ import { join as join11 } from "path";
1490
2189
  var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
1491
2190
  async function walk(dir, out = []) {
1492
2191
  const entries = await readdir3(dir, { withFileTypes: true });
1493
2192
  for (const e of entries) {
1494
2193
  if (e.isDirectory()) {
1495
- if (!IGNORE_DIRS.has(e.name)) await walk(join10(dir, e.name), out);
2194
+ if (!IGNORE_DIRS.has(e.name)) await walk(join11(dir, e.name), out);
1496
2195
  } else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
1497
- out.push(join10(dir, e.name));
2196
+ out.push(join11(dir, e.name));
1498
2197
  }
1499
2198
  }
1500
2199
  return out;
1501
2200
  }
1502
2201
  async function tsconfigAliases(dir) {
1503
2202
  try {
1504
- const raw = await readFile9(join10(dir, "tsconfig.base.json"), "utf8");
2203
+ const raw = await readFile10(join11(dir, "tsconfig.base.json"), "utf8");
1505
2204
  const aliases = /* @__PURE__ */ new Set();
1506
2205
  for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
1507
2206
  if (m[1]) aliases.add(m[1]);
@@ -1519,7 +2218,7 @@ var PROVIDER_SDKS = {
1519
2218
  };
1520
2219
  async function readBlueprint(dir) {
1521
2220
  try {
1522
- return JSON.parse(await readFile9(join10(dir, "blueprint.json"), "utf8"));
2221
+ return JSON.parse(await readFile10(join11(dir, "blueprint.json"), "utf8"));
1523
2222
  } catch {
1524
2223
  return null;
1525
2224
  }
@@ -1536,7 +2235,7 @@ function forbiddenFromBlueprint(bp) {
1536
2235
  }
1537
2236
  async function allPackageJsons(dir) {
1538
2237
  const out = [];
1539
- const root = join10(dir, "package.json");
2238
+ const root = join11(dir, "package.json");
1540
2239
  out.push(root);
1541
2240
  async function walk2(d) {
1542
2241
  let entries;
@@ -1547,18 +2246,18 @@ async function allPackageJsons(dir) {
1547
2246
  }
1548
2247
  for (const e of entries) {
1549
2248
  if (e.isDirectory()) {
1550
- if (!IGNORE_DIRS.has(e.name)) await walk2(join10(d, e.name));
2249
+ if (!IGNORE_DIRS.has(e.name)) await walk2(join11(d, e.name));
1551
2250
  } else if (e.name === "package.json") {
1552
- out.push(join10(d, e.name));
2251
+ out.push(join11(d, e.name));
1553
2252
  }
1554
2253
  }
1555
2254
  }
1556
- await walk2(join10(dir, "apps"));
2255
+ await walk2(join11(dir, "apps"));
1557
2256
  return out;
1558
2257
  }
1559
2258
  async function depKeys(pkgPath) {
1560
2259
  try {
1561
- const pkg = JSON.parse(await readFile9(pkgPath, "utf8"));
2260
+ const pkg = JSON.parse(await readFile10(pkgPath, "utf8"));
1562
2261
  return /* @__PURE__ */ new Set([
1563
2262
  ...Object.keys(pkg.dependencies ?? {}),
1564
2263
  ...Object.keys(pkg.devDependencies ?? {})
@@ -1572,7 +2271,7 @@ async function auditProject(dir, opts = {}) {
1572
2271
  const violations = [];
1573
2272
  const aliases = await tsconfigAliases(dir);
1574
2273
  for (const file of await walk(dir)) {
1575
- const src = await readFile9(file, "utf8");
2274
+ const src = await readFile10(file, "utf8");
1576
2275
  for (const m of src.matchAll(ICORE_IMPORT)) {
1577
2276
  const alias = m[1];
1578
2277
  if (alias && !aliases.has(alias)) {