@dartix-software-solutions/create-fullstack-app 2.0.12 → 2.0.14

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 (84) hide show
  1. package/dist/backends/fastapi/templates/app/core/config.py.hbs +13 -13
  2. package/dist/backends/fastapi/templates/app/main.py.hbs +31 -31
  3. package/dist/backends/fastapi/templates/app/routers/health.py.hbs +8 -8
  4. package/dist/backends/fastapi/templates/app/routers/users.py.hbs +30 -30
  5. package/dist/backends/fastapi/templates/app/schemas/user.py.hbs +12 -12
  6. package/dist/backends/fastapi/templates/requirements.txt.hbs +6 -6
  7. package/dist/bin/create-fullstack-app.js +1 -1
  8. package/dist/{chunk-36CSXSDC.js → chunk-JQDTKR4K.js} +110 -43
  9. package/dist/databases/mongodb/templates/connection.ts.hbs +15 -15
  10. package/dist/databases/mongodb/templates/health-check.ts.hbs +11 -11
  11. package/dist/databases/mysql/templates/connection.ts.hbs +14 -14
  12. package/dist/databases/mysql/templates/health-check.ts.hbs +12 -12
  13. package/dist/databases/postgres/templates/connection.ts.hbs +14 -14
  14. package/dist/databases/postgres/templates/health-check.ts.hbs +12 -12
  15. package/dist/databases/redis/templates/connection.ts.hbs +14 -14
  16. package/dist/databases/redis/templates/health-check.ts.hbs +10 -10
  17. package/dist/databases/sqlite/templates/connection.ts.hbs +12 -12
  18. package/dist/databases/sqlite/templates/health-check.ts.hbs +10 -10
  19. package/dist/frontend-extras/react-table/templates/components/SampleTable.tsx.hbs +25 -25
  20. package/dist/frontend-extras/react-table/templates/data/sample-table-data.ts.hbs +4 -4
  21. package/dist/frontends/mobile/expo/templates/App.react-navigation.tsx.hbs +10 -10
  22. package/dist/frontends/mobile/expo/templates/app/(auth)/_layout.tsx.hbs +5 -5
  23. package/dist/frontends/mobile/expo/templates/app/(auth)/login.tsx.hbs +3 -3
  24. package/dist/frontends/mobile/expo/templates/app/(auth)/register.tsx.hbs +3 -3
  25. package/dist/frontends/mobile/expo/templates/app/(tabs)/_layout.tsx.hbs +11 -11
  26. package/dist/frontends/mobile/expo/templates/app/(tabs)/index.tsx.hbs +3 -3
  27. package/dist/frontends/mobile/expo/templates/app/(tabs)/profile.tsx.hbs +3 -3
  28. package/dist/frontends/mobile/expo/templates/app/(tabs)/settings.tsx.hbs +3 -3
  29. package/dist/frontends/mobile/expo/templates/app/+not-found.tsx.hbs +14 -14
  30. package/dist/frontends/mobile/expo/templates/app/_layout.tsx.hbs +11 -11
  31. package/dist/frontends/mobile/expo/templates/app/index.tsx.hbs +29 -29
  32. package/dist/frontends/mobile/expo/templates/types/router.ts.hbs +10 -10
  33. package/dist/frontends/mobile/react-native-cli/templates/app.json.hbs +4 -4
  34. package/dist/frontends/mobile/react-native-cli/templates/babel.config.js.hbs +14 -14
  35. package/dist/frontends/mobile/react-native-cli/templates/components/Avatar.tsx.hbs +29 -29
  36. package/dist/frontends/mobile/react-native-cli/templates/types/env.d.ts.hbs +3 -3
  37. package/dist/frontends/web/angular/templates/index.html.hbs +12 -12
  38. package/dist/frontends/web/angular/templates/main.ts.hbs +5 -5
  39. package/dist/frontends/web/angular/templates/pages/dashboard/dashboard.component.html.hbs +3 -3
  40. package/dist/frontends/web/angular/templates/pages/dashboard/dashboard.component.ts.hbs +10 -10
  41. package/dist/frontends/web/angular/templates/styles.css.hbs +11 -11
  42. package/dist/frontends/web/vue/templates/App.vue.hbs +9 -9
  43. package/dist/frontends/web/vue/templates/components/Layout.vue.hbs +17 -17
  44. package/dist/frontends/web/vue/templates/components/LoadingSpinner.vue.hbs +7 -7
  45. package/dist/frontends/web/vue/templates/components/Navbar.vue.hbs +24 -24
  46. package/dist/frontends/web/vue/templates/composables/useApi.ts.hbs +8 -8
  47. package/dist/frontends/web/vue/templates/composables/useAuth.ts.hbs +20 -20
  48. package/dist/frontends/web/vue/templates/env.d.ts.hbs +1 -1
  49. package/dist/frontends/web/vue/templates/index.html.hbs +12 -12
  50. package/dist/frontends/web/vue/templates/lib/config.ts.hbs +2 -2
  51. package/dist/frontends/web/vue/templates/main.ts.hbs +9 -9
  52. package/dist/frontends/web/vue/templates/pages/About.vue.hbs +6 -6
  53. package/dist/frontends/web/vue/templates/pages/Dashboard.vue.hbs +21 -21
  54. package/dist/frontends/web/vue/templates/pages/Home.vue.hbs +19 -19
  55. package/dist/frontends/web/vue/templates/pages/Login.vue.hbs +34 -34
  56. package/dist/frontends/web/vue/templates/pages/NotFound.vue.hbs +7 -7
  57. package/dist/frontends/web/vue/templates/router/index.ts.hbs +34 -34
  58. package/dist/frontends/web/vue/templates/stores/app.ts.hbs +8 -8
  59. package/dist/frontends/web/vue/templates/tsconfig.json.hbs +15 -15
  60. package/dist/frontends/web/vue/templates/vite.config.ts.hbs +6 -6
  61. package/dist/index.js +1 -1
  62. package/dist/orms/prisma/file-map.js +1 -0
  63. package/dist/orms/prisma/meta.js +5 -2
  64. package/dist/orms/prisma/templates/db-client.ts.hbs +10 -1
  65. package/dist/orms/prisma/templates/injection-module-import.hbs +1 -1
  66. package/dist/orms/prisma/templates/injection-module-register.hbs +1 -1
  67. package/dist/orms/prisma/templates/models/user.ts.hbs +3 -3
  68. package/dist/orms/prisma/templates/prisma.config.ts.hbs +12 -0
  69. package/dist/orms/prisma/templates/prisma.module.ts.hbs +9 -9
  70. package/dist/orms/prisma/templates/prisma.service.ts.hbs +24 -13
  71. package/dist/orms/prisma/templates/schema.prisma.hbs +0 -1
  72. package/dist/orms/prisma/templates/services/user.service.nestjs.ts.hbs +16 -16
  73. package/dist/orms/prisma/templates/services/user.service.ts.hbs +10 -10
  74. package/package.json +1 -1
  75. package/dist/plugins/devops/docker/templates/.dockerignore.hbs +0 -5
  76. package/dist/plugins/devops/gitlab-ci/templates/.gitlab-ci.yml.hbs +0 -14
  77. package/dist/plugins/devtools/eslint/templates/.eslintignore.hbs +0 -3
  78. package/dist/plugins/devtools/lint-staged/templates/.lintstagedrc.hbs +0 -5
  79. package/dist/plugins/devtools/prettier/templates/.prettierignore.hbs +0 -4
  80. package/dist/plugins/devtools/prettier/templates/.prettierrc.hbs +0 -6
  81. package/dist/plugins/mobile-navigation/expo-router/templates/.gitkeep +0 -0
  82. package/dist/plugins/testing/detox/templates/.detoxrc.js.hbs +0 -7
  83. package/dist/plugins/testing/maestro/templates/.maestro/home.yaml.hbs +0 -4
  84. package/dist/plugins/testing/maestro/templates/.maestro/login.yaml.hbs +0 -5
@@ -1,13 +1,13 @@
1
- from pydantic_settings import BaseSettings, SettingsConfigDict
2
-
3
-
4
- class Settings(BaseSettings):
5
- model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
6
-
7
- app_name: str = "{{projectName}} API"
8
- port: int = 8000
9
- cors_origin: str = "http://localhost:5173"
10
- debug: bool = True
11
-
12
-
13
- settings = Settings()
1
+ from pydantic_settings import BaseSettings, SettingsConfigDict
2
+
3
+
4
+ class Settings(BaseSettings):
5
+ model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
6
+
7
+ app_name: str = "{{projectName}} API"
8
+ port: int = 8000
9
+ cors_origin: str = "http://localhost:5173"
10
+ debug: bool = True
11
+
12
+
13
+ settings = Settings()
@@ -1,31 +1,31 @@
1
- from contextlib import asynccontextmanager
2
-
3
- from fastapi import FastAPI
4
- from fastapi.middleware.cors import CORSMiddleware
5
-
6
- from app.core.config import settings
7
- from app.routers import health, users
8
-
9
-
10
- @asynccontextmanager
11
- async def lifespan(_app: FastAPI):
12
- yield
13
-
14
-
15
- app = FastAPI(
16
- title=settings.app_name,
17
- lifespan=lifespan,
18
- docs_url="/docs" if settings.debug else None,
19
- redoc_url="/redoc" if settings.debug else None,
20
- )
21
-
22
- app.add_middleware(
23
- CORSMiddleware,
24
- allow_origins=[settings.cors_origin],
25
- allow_credentials=True,
26
- allow_methods=["*"],
27
- allow_headers=["*"],
28
- )
29
-
30
- app.include_router(health.router, tags=["health"])
31
- app.include_router(users.router, prefix="/users", tags=["users"])
1
+ from contextlib import asynccontextmanager
2
+
3
+ from fastapi import FastAPI
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+
6
+ from app.core.config import settings
7
+ from app.routers import health, users
8
+
9
+
10
+ @asynccontextmanager
11
+ async def lifespan(_app: FastAPI):
12
+ yield
13
+
14
+
15
+ app = FastAPI(
16
+ title=settings.app_name,
17
+ lifespan=lifespan,
18
+ docs_url="/docs" if settings.debug else None,
19
+ redoc_url="/redoc" if settings.debug else None,
20
+ )
21
+
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=[settings.cors_origin],
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+ app.include_router(health.router, tags=["health"])
31
+ app.include_router(users.router, prefix="/users", tags=["users"])
@@ -1,8 +1,8 @@
1
- from fastapi import APIRouter
2
-
3
- router = APIRouter()
4
-
5
-
6
- @router.get("/health")
7
- async def health():
8
- return {"status": "ok"}
1
+ from fastapi import APIRouter
2
+
3
+ router = APIRouter()
4
+
5
+
6
+ @router.get("/health")
7
+ async def health():
8
+ return {"status": "ok"}
@@ -1,30 +1,30 @@
1
- from fastapi import APIRouter, HTTPException, status
2
-
3
- from app.schemas.user import UserCreate, UserRead
4
-
5
- router = APIRouter()
6
-
7
- _db: list[UserRead] = [
8
- UserRead(id=1, email="demo@example.com", name="Demo User"),
9
- ]
10
-
11
-
12
- @router.get("/", response_model=list[UserRead])
13
- async def list_users() -> list[UserRead]:
14
- return list(_db)
15
-
16
-
17
- @router.get("/{user_id}", response_model=UserRead)
18
- async def get_user(user_id: int) -> UserRead:
19
- for user in _db:
20
- if user.id == user_id:
21
- return user
22
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
23
-
24
-
25
- @router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
26
- async def create_user(body: UserCreate) -> UserRead:
27
- next_id = max((u.id for u in _db), default=0) + 1
28
- user = UserRead(id=next_id, email=body.email, name=body.name)
29
- _db.append(user)
30
- return user
1
+ from fastapi import APIRouter, HTTPException, status
2
+
3
+ from app.schemas.user import UserCreate, UserRead
4
+
5
+ router = APIRouter()
6
+
7
+ _db: list[UserRead] = [
8
+ UserRead(id=1, email="demo@example.com", name="Demo User"),
9
+ ]
10
+
11
+
12
+ @router.get("/", response_model=list[UserRead])
13
+ async def list_users() -> list[UserRead]:
14
+ return list(_db)
15
+
16
+
17
+ @router.get("/{user_id}", response_model=UserRead)
18
+ async def get_user(user_id: int) -> UserRead:
19
+ for user in _db:
20
+ if user.id == user_id:
21
+ return user
22
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
23
+
24
+
25
+ @router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
26
+ async def create_user(body: UserCreate) -> UserRead:
27
+ next_id = max((u.id for u in _db), default=0) + 1
28
+ user = UserRead(id=next_id, email=body.email, name=body.name)
29
+ _db.append(user)
30
+ return user
@@ -1,12 +1,12 @@
1
- from pydantic import BaseModel, EmailStr, Field
2
-
3
-
4
- class UserRead(BaseModel):
5
- id: int = Field(..., ge=1)
6
- email: EmailStr
7
- name: str
8
-
9
-
10
- class UserCreate(BaseModel):
11
- email: EmailStr
12
- name: str
1
+ from pydantic import BaseModel, EmailStr, Field
2
+
3
+
4
+ class UserRead(BaseModel):
5
+ id: int = Field(..., ge=1)
6
+ email: EmailStr
7
+ name: str
8
+
9
+
10
+ class UserCreate(BaseModel):
11
+ email: EmailStr
12
+ name: str
@@ -1,6 +1,6 @@
1
- fastapi>=0.115.0
2
- uvicorn[standard]>=0.32.0
3
- pydantic-settings>=2.6.0
4
- python-dotenv>=1.0.0
5
- gunicorn>=23.0.0
6
- email-validator>=2.2.0
1
+ fastapi>=0.115.0
2
+ uvicorn[standard]>=0.32.0
3
+ pydantic-settings>=2.6.0
4
+ python-dotenv>=1.0.0
5
+ gunicorn>=23.0.0
6
+ email-validator>=2.2.0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "../chunk-36CSXSDC.js";
4
+ } from "../chunk-JQDTKR4K.js";
5
5
 
6
6
  // src/bin/create-fullstack-app.ts
7
7
  main().catch((error) => {
@@ -1970,11 +1970,12 @@ var SingleAppLayout = class {
1970
1970
  if (isFullstack) {
1971
1971
  const feDir = context.hasMobile ? "mobile" : "client";
1972
1972
  const scripts = {
1973
- dev: `concurrently "npm run dev:${context.hasMobile ? "mobile" : "client"}" "npm run dev:server"`,
1974
- [`dev:${context.hasMobile ? "mobile" : "client"}`]: `cd ${feDir} && npm run dev`,
1975
- "dev:server": "cd server && npm run dev",
1976
- build: `cd ${feDir} && npm run build && cd ../server && npm run build`,
1977
- lint: `cd ${feDir} && npm run lint && cd ../server && npm run lint`
1973
+ "dev:client": `npm run dev --prefix ${feDir}`,
1974
+ "dev:server": "npm run dev --prefix server",
1975
+ "build:client": `npm run build --prefix ${feDir}`,
1976
+ "build:server": "npm run build --prefix server",
1977
+ "lint:client": `npm run lint --prefix ${feDir}`,
1978
+ "lint:server": "npm run lint --prefix server"
1978
1979
  };
1979
1980
  if (context.hasHusky) {
1980
1981
  scripts.prepare = "husky";
@@ -1986,10 +1987,7 @@ var SingleAppLayout = class {
1986
1987
  name: context.projectName,
1987
1988
  version: "0.1.0",
1988
1989
  private: true,
1989
- scripts,
1990
- devDependencies: {
1991
- concurrently: "^9.1.0"
1992
- }
1990
+ scripts
1993
1991
  },
1994
1992
  null,
1995
1993
  2
@@ -2005,7 +2003,6 @@ var SingleAppLayout = class {
2005
2003
  const isFullstack = hasFE && hasBE;
2006
2004
  if (isFullstack) {
2007
2005
  const feDir = context.hasMobile ? "mobile" : "client";
2008
- targets.push({ path: "package.json", target: TARGETS.ROOT, name: context.projectName });
2009
2006
  targets.push({
2010
2007
  path: `${feDir}/package.json`,
2011
2008
  target: TARGETS.FRONTEND,
@@ -2931,34 +2928,36 @@ function resolveDependencies(plugins, packageJsonTargets, context) {
2931
2928
  });
2932
2929
  }
2933
2930
  for (const plugin of plugins) {
2934
- const targetKey = resolveTargetForPlugin(plugin, packageJsonTargets, context);
2935
- if (!targetKey) {
2931
+ const targetKeys = resolveTargetsForPlugin(plugin, packageJsonTargets, context);
2932
+ if (targetKeys.length === 0) {
2936
2933
  logger.warn(
2937
2934
  `Could not resolve target package.json for plugin "${plugin.meta.id}" (category: ${plugin.meta.category})`
2938
2935
  );
2939
2936
  continue;
2940
2937
  }
2941
- const merged = result.get(targetKey);
2942
- if (!merged) continue;
2943
- for (const dep of plugin.meta.deps) {
2944
- if (merged.dependencies[dep.name]) {
2945
- if (merged.dependencies[dep.name] !== dep.version) {
2946
- logger.warn(
2947
- `Dependency version conflict for "${dep.name}": "${merged.dependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
2948
- );
2938
+ for (const targetKey of targetKeys) {
2939
+ const merged = result.get(targetKey);
2940
+ if (!merged) continue;
2941
+ for (const dep of plugin.meta.deps) {
2942
+ if (merged.dependencies[dep.name]) {
2943
+ if (merged.dependencies[dep.name] !== dep.version) {
2944
+ logger.warn(
2945
+ `Dependency version conflict for "${dep.name}": "${merged.dependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
2946
+ );
2947
+ }
2949
2948
  }
2949
+ merged.dependencies[dep.name] = dep.version;
2950
2950
  }
2951
- merged.dependencies[dep.name] = dep.version;
2952
- }
2953
- for (const dep of plugin.meta.devDeps) {
2954
- if (merged.devDependencies[dep.name]) {
2955
- if (merged.devDependencies[dep.name] !== dep.version) {
2956
- logger.warn(
2957
- `Dev dependency version conflict for "${dep.name}": "${merged.devDependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
2958
- );
2951
+ for (const dep of plugin.meta.devDeps) {
2952
+ if (merged.devDependencies[dep.name]) {
2953
+ if (merged.devDependencies[dep.name] !== dep.version) {
2954
+ logger.warn(
2955
+ `Dev dependency version conflict for "${dep.name}": "${merged.devDependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
2956
+ );
2957
+ }
2959
2958
  }
2959
+ merged.devDependencies[dep.name] = dep.version;
2960
2960
  }
2961
- merged.devDependencies[dep.name] = dep.version;
2962
2961
  }
2963
2962
  }
2964
2963
  for (const [key, merged] of result) {
@@ -2969,6 +2968,15 @@ function resolveDependencies(plugins, packageJsonTargets, context) {
2969
2968
  }
2970
2969
  return result;
2971
2970
  }
2971
+ function resolveTargetsForPlugin(plugin, packageJsonTargets, context) {
2972
+ if (plugin.meta.category === "devtools" && context.isSingleApp && context.isFullstack) {
2973
+ const frontendTarget = packageJsonTargets.find((t) => t.target === TARGETS.FRONTEND)?.path;
2974
+ const backendTarget = packageJsonTargets.find((t) => t.target === TARGETS.BACKEND)?.path;
2975
+ return [frontendTarget, backendTarget].filter((p) => Boolean(p));
2976
+ }
2977
+ const single = resolveTargetForPlugin(plugin, packageJsonTargets, context);
2978
+ return single ? [single] : [];
2979
+ }
2972
2980
  function resolveTargetForPlugin(plugin, packageJsonTargets, context) {
2973
2981
  const inferredTarget = inferTargetFromPluginOutputs(plugin, context);
2974
2982
  if (inferredTarget) {
@@ -3612,7 +3620,7 @@ function detectFileCollisions(activePlugins, context, pathResolver) {
3612
3620
 
3613
3621
  // src/generator/post-generate.ts
3614
3622
  import { execa } from "execa";
3615
- import { readFile } from "fs/promises";
3623
+ import { readFile, readdir } from "fs/promises";
3616
3624
  import { join } from "path";
3617
3625
 
3618
3626
  // src/cli/ui/spinner.ts
@@ -3711,8 +3719,9 @@ async function runPostGenerate(outputDir, context, options) {
3711
3719
  if (!options.skipInstall) {
3712
3720
  const installCmd = getInstallCommand(context.packageManager);
3713
3721
  const [cmd, ...args] = installCmd.split(" ");
3714
- const installTargets = getInstallTargets(outputDir, context);
3722
+ const installTargets = await getInstallTargets(outputDir, context);
3715
3723
  try {
3724
+ let installCount = 0;
3716
3725
  for (const target of installTargets) {
3717
3726
  await withSpinner(
3718
3727
  `Installing ${target.label} dependencies with ${context.packageManager}...`,
@@ -3724,8 +3733,9 @@ async function runPostGenerate(outputDir, context, options) {
3724
3733
  },
3725
3734
  `${target.label} dependencies installed`
3726
3735
  );
3736
+ installCount += 1;
3727
3737
  }
3728
- installed = true;
3738
+ installed = installCount > 0;
3729
3739
  } catch (error) {
3730
3740
  logger.warn(`Failed to install dependencies: ${error.message}`);
3731
3741
  logger.info(`You can run "${installCmd}" manually`);
@@ -3758,26 +3768,33 @@ function buildNextSteps(context, installed) {
3758
3768
  steps.push(`Set up database: ${pm} run db:migrate && ${pm} run db:seed`);
3759
3769
  }
3760
3770
  }
3761
- steps.push(`Start development: ${pm} run dev`);
3771
+ if (context.isFullstack) {
3772
+ const feDir = context.hasMobile ? "mobile" : "client";
3773
+ steps.push(`Start frontend: ${pm} run dev --prefix ${feDir}`);
3774
+ steps.push(`Start backend: ${pm} run dev --prefix server`);
3775
+ } else {
3776
+ steps.push(`Start development: ${pm} run dev`);
3777
+ }
3762
3778
  if (context.hasMobile) {
3763
3779
  steps.push("Run on device: Press 'i' for iOS, 'a' for Android, or scan QR code");
3764
3780
  }
3765
3781
  return steps;
3766
3782
  }
3767
- function getInstallTargets(outputDir, context) {
3768
- if (context.isSingleApp && context.isFullstack) {
3769
- const feDir = context.hasMobile ? "mobile" : "client";
3770
- return [
3771
- { label: "root", cwd: outputDir },
3772
- { label: feDir, cwd: join(outputDir, feDir) },
3773
- { label: "server", cwd: join(outputDir, "server") }
3774
- ];
3783
+ async function getInstallTargets(outputDir, context) {
3784
+ const packageDirs = await discoverPackageDirs(outputDir);
3785
+ const targets = [];
3786
+ for (const dir of packageDirs) {
3787
+ if (!await hasInstallableDependencies(dir)) continue;
3788
+ targets.push({
3789
+ label: labelForInstallTarget(dir, outputDir, context),
3790
+ cwd: dir
3791
+ });
3775
3792
  }
3776
- return [{ label: "project", cwd: outputDir }];
3793
+ return targets;
3777
3794
  }
3778
3795
  async function runBootstrapScripts(outputDir, context) {
3779
3796
  const pm = context.packageManager;
3780
- const installTargets = getInstallTargets(outputDir, context);
3797
+ const installTargets = await getInstallTargets(outputDir, context);
3781
3798
  const safeBootstrapScripts = ["db:generate", "generate", "codegen", "graphql:codegen", "types:generate"];
3782
3799
  for (const target of installTargets) {
3783
3800
  try {
@@ -3804,6 +3821,56 @@ async function runBootstrapScripts(outputDir, context) {
3804
3821
  }
3805
3822
  }
3806
3823
  }
3824
+ async function discoverPackageDirs(outputDir) {
3825
+ const dirs = [];
3826
+ await walkForPackageJson(outputDir, outputDir, dirs);
3827
+ const unique = [...new Set(dirs)];
3828
+ unique.sort((a, b) => {
3829
+ if (a === outputDir) return -1;
3830
+ if (b === outputDir) return 1;
3831
+ return a.localeCompare(b);
3832
+ });
3833
+ return unique;
3834
+ }
3835
+ async function walkForPackageJson(root, current, dirs) {
3836
+ const entries = await readdir(current, { withFileTypes: true });
3837
+ let hasPackageJson = false;
3838
+ for (const entry of entries) {
3839
+ if (entry.isFile() && entry.name === "package.json") {
3840
+ hasPackageJson = true;
3841
+ break;
3842
+ }
3843
+ }
3844
+ if (hasPackageJson) {
3845
+ dirs.push(current);
3846
+ }
3847
+ for (const entry of entries) {
3848
+ if (!entry.isDirectory()) continue;
3849
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
3850
+ await walkForPackageJson(root, join(current, entry.name), dirs);
3851
+ }
3852
+ }
3853
+ function labelForInstallTarget(targetDir, outputDir, context) {
3854
+ if (targetDir === outputDir) return "root";
3855
+ if (targetDir === join(outputDir, "server")) return "server";
3856
+ if (targetDir === join(outputDir, "client")) return "client";
3857
+ if (targetDir === join(outputDir, "mobile")) return "mobile";
3858
+ const relative = targetDir.slice(outputDir.length + 1).replace(/\\/g, "/");
3859
+ if (relative) return relative;
3860
+ return context.projectName;
3861
+ }
3862
+ async function hasInstallableDependencies(cwd) {
3863
+ try {
3864
+ const packageJsonPath = join(cwd, "package.json");
3865
+ const raw = await readFile(packageJsonPath, "utf8");
3866
+ const pkg = JSON.parse(raw);
3867
+ const depsCount = Object.keys(pkg.dependencies ?? {}).length;
3868
+ const devDepsCount = Object.keys(pkg.devDependencies ?? {}).length;
3869
+ return depsCount > 0 || devDepsCount > 0;
3870
+ } catch {
3871
+ return false;
3872
+ }
3873
+ }
3807
3874
 
3808
3875
  // src/generator/pipeline.ts
3809
3876
  var TOTAL_STEPS = 16;
@@ -1,15 +1,15 @@
1
- import { MongoClient } from 'mongodb';
2
-
3
- let client: MongoClient | null = null;
4
-
5
- export async function getMongoClient(): Promise<MongoClient> {
6
- if (!client) {
7
- const uri = process.env.MONGODB_URI;
8
- if (!uri) {
9
- throw new Error('MONGODB_URI is not set');
10
- }
11
- client = new MongoClient(uri);
12
- await client.connect();
13
- }
14
- return client;
15
- }
1
+ import { MongoClient } from 'mongodb';
2
+
3
+ let client: MongoClient | null = null;
4
+
5
+ export async function getMongoClient(): Promise<MongoClient> {
6
+ if (!client) {
7
+ const uri = process.env.MONGODB_URI;
8
+ if (!uri) {
9
+ throw new Error('MONGODB_URI is not set');
10
+ }
11
+ client = new MongoClient(uri);
12
+ await client.connect();
13
+ }
14
+ return client;
15
+ }
@@ -1,11 +1,11 @@
1
- import { getMongoClient } from './db-connection.js';
2
-
3
- export async function checkMongodbHealth(): Promise<boolean> {
4
- try {
5
- const c = await getMongoClient();
6
- await c.db().command({ ping: 1 });
7
- return true;
8
- } catch {
9
- return false;
10
- }
11
- }
1
+ import { getMongoClient } from './db-connection.js';
2
+
3
+ export async function checkMongodbHealth(): Promise<boolean> {
4
+ try {
5
+ const c = await getMongoClient();
6
+ await c.db().command({ ping: 1 });
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
@@ -1,14 +1,14 @@
1
- import mysql from 'mysql2/promise';
2
-
3
- let pool: mysql.Pool | null = null;
4
-
5
- export function getMysqlPool(): mysql.Pool {
6
- if (!pool) {
7
- const url = process.env.DATABASE_URL;
8
- if (!url) {
9
- throw new Error('DATABASE_URL is not set');
10
- }
11
- pool = mysql.createPool(url);
12
- }
13
- return pool;
14
- }
1
+ import mysql from 'mysql2/promise';
2
+
3
+ let pool: mysql.Pool | null = null;
4
+
5
+ export function getMysqlPool(): mysql.Pool {
6
+ if (!pool) {
7
+ const url = process.env.DATABASE_URL;
8
+ if (!url) {
9
+ throw new Error('DATABASE_URL is not set');
10
+ }
11
+ pool = mysql.createPool(url);
12
+ }
13
+ return pool;
14
+ }
@@ -1,12 +1,12 @@
1
- import { getMysqlPool } from './db-connection.js';
2
-
3
- export async function checkMysqlHealth(): Promise<boolean> {
4
- try {
5
- const connection = await getMysqlPool().getConnection();
6
- await connection.ping();
7
- connection.release();
8
- return true;
9
- } catch {
10
- return false;
11
- }
12
- }
1
+ import { getMysqlPool } from './db-connection.js';
2
+
3
+ export async function checkMysqlHealth(): Promise<boolean> {
4
+ try {
5
+ const connection = await getMysqlPool().getConnection();
6
+ await connection.ping();
7
+ connection.release();
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
@@ -1,14 +1,14 @@
1
- import pg from 'pg';
2
-
3
- let pool: pg.Pool | null = null;
4
-
5
- export function getPostgresPool(): pg.Pool {
6
- if (!pool) {
7
- const url = process.env.DATABASE_URL;
8
- if (!url) {
9
- throw new Error('DATABASE_URL is not set');
10
- }
11
- pool = new pg.Pool({ connectionString: url });
12
- }
13
- return pool;
14
- }
1
+ import pg from 'pg';
2
+
3
+ let pool: pg.Pool | null = null;
4
+
5
+ export function getPostgresPool(): pg.Pool {
6
+ if (!pool) {
7
+ const url = process.env.DATABASE_URL;
8
+ if (!url) {
9
+ throw new Error('DATABASE_URL is not set');
10
+ }
11
+ pool = new pg.Pool({ connectionString: url });
12
+ }
13
+ return pool;
14
+ }
@@ -1,12 +1,12 @@
1
- import { getPostgresPool } from './db-connection.js';
2
-
3
- export async function checkPostgresHealth(): Promise<boolean> {
4
- try {
5
- const client = await getPostgresPool().connect();
6
- await client.query('SELECT 1');
7
- client.release();
8
- return true;
9
- } catch {
10
- return false;
11
- }
12
- }
1
+ import { getPostgresPool } from './db-connection.js';
2
+
3
+ export async function checkPostgresHealth(): Promise<boolean> {
4
+ try {
5
+ const client = await getPostgresPool().connect();
6
+ await client.query('SELECT 1');
7
+ client.release();
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
@@ -1,14 +1,14 @@
1
- import Redis from 'ioredis';
2
-
3
- let client: Redis | null = null;
4
-
5
- export function getRedis(): Redis {
6
- if (!client) {
7
- const url = process.env.REDIS_URL;
8
- if (!url) {
9
- throw new Error('REDIS_URL is not set');
10
- }
11
- client = new Redis(url);
12
- }
13
- return client;
14
- }
1
+ import Redis from 'ioredis';
2
+
3
+ let client: Redis | null = null;
4
+
5
+ export function getRedis(): Redis {
6
+ if (!client) {
7
+ const url = process.env.REDIS_URL;
8
+ if (!url) {
9
+ throw new Error('REDIS_URL is not set');
10
+ }
11
+ client = new Redis(url);
12
+ }
13
+ return client;
14
+ }