@donotdev/cli 0.0.14 → 0.0.15

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 (151) hide show
  1. package/dependencies-matrix.json +356 -88
  2. package/dist/bin/commands/agent-setup.js +7 -1
  3. package/dist/bin/commands/build.js +118 -38
  4. package/dist/bin/commands/bump.js +74 -28
  5. package/dist/bin/commands/cacheout.js +37 -9
  6. package/dist/bin/commands/create-app.js +222 -115
  7. package/dist/bin/commands/create-project.js +455 -140
  8. package/dist/bin/commands/deploy.js +1736 -697
  9. package/dist/bin/commands/dev.js +138 -23
  10. package/dist/bin/commands/emu.js +215 -58
  11. package/dist/bin/commands/format.js +37 -9
  12. package/dist/bin/commands/lint.js +37 -9
  13. package/dist/bin/commands/preview.js +142 -23
  14. package/dist/bin/commands/supabase-setup.d.ts +6 -0
  15. package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
  16. package/dist/bin/commands/supabase-setup.js +7 -0
  17. package/dist/bin/commands/supabase-setup.js.map +1 -0
  18. package/dist/bin/commands/sync-secrets.js +211 -34
  19. package/dist/bin/commands/type-check.d.ts +14 -0
  20. package/dist/bin/commands/type-check.d.ts.map +1 -0
  21. package/dist/bin/commands/type-check.js +314 -0
  22. package/dist/bin/commands/type-check.js.map +1 -0
  23. package/dist/bin/commands/wai.js +3 -1
  24. package/dist/bin/dndev.js +27 -2
  25. package/dist/bin/donotdev.js +27 -2
  26. package/dist/index.js +3960 -3015
  27. package/package.json +2 -2
  28. package/templates/app-demo/src/App.tsx.example +1 -0
  29. package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
  30. package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
  31. package/templates/app-demo/src/themes.css.example +5 -12
  32. package/templates/app-expo/.env.example +64 -0
  33. package/templates/app-expo/.expo/README.md.example +5 -0
  34. package/templates/app-expo/.gitignore.example +36 -0
  35. package/templates/app-expo/README.md.example +58 -0
  36. package/templates/app-expo/app/.gitkeep +2 -0
  37. package/templates/app-expo/app/_layout.tsx.example +41 -0
  38. package/templates/app-expo/app/form.tsx.example +52 -0
  39. package/templates/app-expo/app/index.tsx.example +89 -0
  40. package/templates/app-expo/app/list.tsx.example +32 -0
  41. package/templates/app-expo/app/profile.tsx.example +76 -0
  42. package/templates/app-expo/app/signin.tsx.example +53 -0
  43. package/templates/app-expo/app.json.example +39 -0
  44. package/templates/app-expo/babel.config.js.example +10 -0
  45. package/templates/app-expo/eas.json.example +20 -0
  46. package/templates/app-expo/expo-env.d.ts.example +4 -0
  47. package/templates/app-expo/metro.config.js.example +20 -0
  48. package/templates/app-expo/service-account-key.json.example +12 -0
  49. package/templates/app-expo/tsconfig.json.example +19 -0
  50. package/templates/app-next/.env.example +4 -33
  51. package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
  52. package/templates/app-next/src/app/layout.tsx.example +7 -6
  53. package/templates/app-next/src/globals.css.example +2 -11
  54. package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
  55. package/templates/app-next/src/themes.css.example +10 -13
  56. package/templates/app-vite/.env.example +3 -32
  57. package/templates/app-vite/index.html.example +2 -24
  58. package/templates/app-vite/src/App.tsx.example +2 -0
  59. package/templates/app-vite/src/globals.css.example +2 -12
  60. package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
  61. package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
  62. package/templates/app-vite/src/themes.css.example +109 -79
  63. package/templates/app-vite/vercel.json.example +11 -0
  64. package/templates/functions-firebase/build.mjs.example +2 -72
  65. package/templates/functions-firebase/functions-firebase/.env.example.example +23 -25
  66. package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
  67. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  68. package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
  69. package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
  70. package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
  71. package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
  72. package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
  73. package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
  74. package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
  75. package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
  76. package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
  77. package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
  78. package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
  79. package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
  80. package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
  81. package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
  82. package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
  83. package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
  84. package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
  85. package/templates/functions-vercel/vercel.json.example +1 -1
  86. package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
  87. package/templates/github/workflows/firebase-deploy.yml.example +1 -1
  88. package/templates/overlay-firebase/env.fragment.example +34 -0
  89. package/templates/overlay-firebase/env.fragment.expo.example +34 -0
  90. package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
  91. package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
  92. package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
  93. package/templates/overlay-supabase/env.fragment.example +7 -0
  94. package/templates/overlay-supabase/env.fragment.expo.example +7 -0
  95. package/templates/overlay-supabase/env.fragment.nextjs.example +7 -0
  96. package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
  97. package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
  98. package/templates/overlay-supabase/vercel.headers.example +23 -0
  99. package/templates/overlay-supabase/vercel.json.example +22 -0
  100. package/templates/overlay-vercel/env.fragment.example +34 -0
  101. package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
  102. package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
  103. package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
  104. package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
  105. package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
  106. package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
  107. package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
  108. package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
  109. package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
  110. package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
  111. package/templates/root-consumer/.clinerules.example +1 -0
  112. package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
  113. package/templates/root-consumer/.cursorrules.example +1 -0
  114. package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
  115. package/templates/root-consumer/.windsurfrules.example +1 -0
  116. package/templates/root-consumer/AI.md.example +29 -123
  117. package/templates/root-consumer/CLAUDE.md.example +1 -134
  118. package/templates/root-consumer/CONVENTIONS.md.example +1 -0
  119. package/templates/root-consumer/GEMINI.md.example +1 -0
  120. package/templates/root-consumer/firebase.json.example +1 -1
  121. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +20 -0
  122. package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
  123. package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
  124. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +99 -30
  125. package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
  126. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +143 -12
  127. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +9 -3
  128. package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
  129. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
  130. package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
  131. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
  132. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
  133. package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
  134. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
  135. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
  136. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
  137. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
  138. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +47 -11
  139. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
  140. package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
  141. package/templates/app-payload/.env.example +0 -28
  142. package/templates/app-payload/README.md.example +0 -233
  143. package/templates/app-payload/collections/Company.ts.example +0 -125
  144. package/templates/app-payload/collections/Hero.ts.example +0 -62
  145. package/templates/app-payload/collections/Media.ts.example +0 -41
  146. package/templates/app-payload/collections/Products.ts.example +0 -115
  147. package/templates/app-payload/collections/Services.ts.example +0 -104
  148. package/templates/app-payload/collections/Testimonials.ts.example +0 -92
  149. package/templates/app-payload/collections/Users.ts.example +0 -35
  150. package/templates/app-payload/src/server.ts.example +0 -79
  151. package/templates/app-payload/tsconfig.json.example +0 -24
@@ -7511,11 +7511,39 @@ var init_PathResolver = __esm({
7511
7511
  });
7512
7512
 
7513
7513
  // packages/tooling/src/utils/errors.ts
7514
- var DoNotDevError;
7514
+ var DO_NOT_DEV_ERROR_CODES, DoNotDevError;
7515
7515
  var init_errors = __esm({
7516
7516
  "packages/tooling/src/utils/errors.ts"() {
7517
7517
  "use strict";
7518
7518
  init_utils();
7519
+ DO_NOT_DEV_ERROR_CODES = {
7520
+ CONFIGURATION_ERROR: "configuration-error",
7521
+ CONFIG_NOT_FOUND: "config-not-found",
7522
+ CONFIG_INVALID: "config-invalid",
7523
+ PATH_RESOLUTION_ERROR: "path-resolution-error",
7524
+ FILE_OPERATION_ERROR: "file-operation-error",
7525
+ FILE_NOT_FOUND: "file-not-found",
7526
+ PERMISSION_DENIED: "permission-denied",
7527
+ GENERATION_ERROR: "generation-error",
7528
+ TEMPLATE_ERROR: "template-error",
7529
+ TEMPLATE_NOT_FOUND: "template-not-found",
7530
+ CLI_EXECUTION_ERROR: "cli-execution-error",
7531
+ COMMAND_NOT_FOUND: "command-not-found",
7532
+ COMMAND_FAILED: "command-failed",
7533
+ VALIDATION_ERROR: "validation-error",
7534
+ SCHEMA_ERROR: "schema-error",
7535
+ DEPENDENCY_ERROR: "dependency-error",
7536
+ DEPENDENCY_NOT_FOUND: "dependency-not-found",
7537
+ DEPENDENCY_VERSION_ERROR: "dependency-version-error",
7538
+ INVALID_ARGUMENT: "invalid-argument",
7539
+ MISSING_ARGUMENT: "missing-argument",
7540
+ MISSING_PROJECT_ID: "missing-project-id",
7541
+ FIREBASE_CLI_ERROR: "firebase-cli-error",
7542
+ DEPLOYMENT_FAILED: "deployment-failed",
7543
+ OPERATION_CANCELLED: "operation-cancelled",
7544
+ TIMEOUT_ERROR: "timeout-error",
7545
+ UNKNOWN_ERROR: "unknown-error"
7546
+ };
7519
7547
  DoNotDevError = class _DoNotDevError extends Error {
7520
7548
  /** The error code categorizing this error */
7521
7549
  code;
@@ -7535,7 +7563,7 @@ var init_errors = __esm({
7535
7563
  * @param {Record<string, any>} [options.context] - Additional context data
7536
7564
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7537
7565
  */
7538
- constructor(message, code = "unknown-error", options) {
7566
+ constructor(message, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7539
7567
  super(message);
7540
7568
  this.name = "DoNotDevError";
7541
7569
  this.code = code;
@@ -7566,7 +7594,7 @@ var init_errors = __esm({
7566
7594
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7567
7595
  * @returns {DoNotDevError} New DoNotDev error wrapping the original
7568
7596
  */
7569
- static from(error2, context, code = "unknown-error", options) {
7597
+ static from(error2, context, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7570
7598
  if (!(error2 instanceof Error)) {
7571
7599
  return new _DoNotDevError(
7572
7600
  `Unknown error: ${String(error2)}`,
@@ -7603,21 +7631,21 @@ var init_errors = __esm({
7603
7631
  }
7604
7632
  const message = error2.message.toLowerCase();
7605
7633
  if (error2.name === "ValidationError" || message.includes("validation")) {
7606
- return "validation-error";
7634
+ return DO_NOT_DEV_ERROR_CODES.VALIDATION_ERROR;
7607
7635
  }
7608
7636
  if (message.includes("not found") || message.includes("no such file")) {
7609
- return "file-not-found";
7637
+ return DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND;
7610
7638
  }
7611
7639
  if (message.includes("permission") || message.includes("access denied")) {
7612
- return "permission-denied";
7640
+ return DO_NOT_DEV_ERROR_CODES.PERMISSION_DENIED;
7613
7641
  }
7614
7642
  if (message.includes("timeout") || message.includes("timed out")) {
7615
- return "timeout-error";
7643
+ return DO_NOT_DEV_ERROR_CODES.TIMEOUT_ERROR;
7616
7644
  }
7617
7645
  if (message.includes("dependency") || message.includes("module not found")) {
7618
- return "dependency-error";
7646
+ return DO_NOT_DEV_ERROR_CODES.DEPENDENCY_ERROR;
7619
7647
  }
7620
- return "unknown-error";
7648
+ return DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR;
7621
7649
  }
7622
7650
  };
7623
7651
  }
@@ -7659,6 +7687,34 @@ var init_pathResolver = __esm({
7659
7687
  }
7660
7688
  });
7661
7689
 
7690
+ // packages/tooling/src/utils/typed-file-operations.ts
7691
+ function readPackageJson(filePath) {
7692
+ if (!pathExists(filePath)) {
7693
+ throw new DoNotDevError(
7694
+ `package.json not found: ${filePath}`,
7695
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7696
+ { context: { filePath } }
7697
+ );
7698
+ }
7699
+ const content = readSync(filePath, { format: "json" });
7700
+ if (!content || typeof content !== "object" || !("name" in content)) {
7701
+ throw new DoNotDevError(
7702
+ `Invalid package.json: ${filePath}`,
7703
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7704
+ { context: { filePath } }
7705
+ );
7706
+ }
7707
+ return content;
7708
+ }
7709
+ var init_typed_file_operations = __esm({
7710
+ "packages/tooling/src/utils/typed-file-operations.ts"() {
7711
+ "use strict";
7712
+ init_utils();
7713
+ init_pathResolver();
7714
+ init_errors();
7715
+ }
7716
+ });
7717
+
7662
7718
  // packages/tooling/src/bundler/utils.ts
7663
7719
  import { Buffer as Buffer2 } from "node:buffer";
7664
7720
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
@@ -7702,6 +7758,7 @@ init_dist2();
7702
7758
  // packages/tooling/src/utils/app-detection.ts
7703
7759
  init_utils();
7704
7760
  init_pathResolver();
7761
+ init_typed_file_operations();
7705
7762
  function detectApps(projectRoot) {
7706
7763
  const apps = [];
7707
7764
  const appsDir = joinPath(projectRoot, "apps");
@@ -7722,9 +7779,7 @@ function detectApps(projectRoot) {
7722
7779
  const packageJsonPath = joinPath(projectRoot, "package.json");
7723
7780
  if (pathExists(packageJsonPath)) {
7724
7781
  try {
7725
- const packageJson = readSync(packageJsonPath, {
7726
- format: "json"
7727
- });
7782
+ const packageJson = readPackageJson(packageJsonPath);
7728
7783
  if (packageJson && (packageJson.dependencies?.vite || packageJson.devDependencies?.vite || packageJson.dependencies?.next || packageJson.devDependencies?.next)) {
7729
7784
  const pathParts = projectRoot.split(/[/\\]/);
7730
7785
  const appName = pathParts[pathParts.length - 1] || "app";
@@ -7744,10 +7799,10 @@ function analyzeApp(appPath, appName) {
7744
7799
  if (!pathExists(packageJsonPath)) {
7745
7800
  return null;
7746
7801
  }
7747
- let packageJson;
7802
+ let packageJson = null;
7748
7803
  try {
7749
- packageJson = readSync(packageJsonPath, { format: "json" });
7750
- if (!packageJson) {
7804
+ packageJson = readPackageJson(packageJsonPath);
7805
+ if (!packageJson.name) {
7751
7806
  return null;
7752
7807
  }
7753
7808
  } catch {
@@ -7762,14 +7817,14 @@ function analyzeApp(appPath, appName) {
7762
7817
  const functionsPath = joinPath(appPath, "functions");
7763
7818
  const functionsStat = statSync2(functionsPath);
7764
7819
  const hasFunctions = pathExists(functionsPath) && (functionsStat?.isDirectory() ?? false);
7765
- let platform;
7820
+ let platform2;
7766
7821
  if (hasFunctions) {
7767
7822
  const firebaseJsonPath = joinPath(functionsPath, "firebase.json");
7768
7823
  const vercelJsonPath = joinPath(appPath, "vercel.json");
7769
7824
  if (pathExists(firebaseJsonPath)) {
7770
- platform = "firebase";
7825
+ platform2 = "firebase";
7771
7826
  } else if (pathExists(vercelJsonPath)) {
7772
- platform = "vercel";
7827
+ platform2 = "vercel";
7773
7828
  }
7774
7829
  }
7775
7830
  return {
@@ -7780,7 +7835,7 @@ function analyzeApp(appPath, appName) {
7780
7835
  framework,
7781
7836
  hasFunctions,
7782
7837
  functionsPath: hasFunctions ? functionsPath : void 0,
7783
- platform
7838
+ platform: platform2
7784
7839
  };
7785
7840
  }
7786
7841
 
@@ -7822,7 +7877,8 @@ async function selectApp(projectRoot, appName) {
7822
7877
 
7823
7878
  // packages/tooling/src/apps/dev.ts
7824
7879
  init_utils();
7825
- import { spawnSync as spawnSync2 } from "node:child_process";
7880
+ import { spawn, execSync as execSync2 } from "node:child_process";
7881
+ import { platform } from "node:os";
7826
7882
  init_cli_output();
7827
7883
  init_errors();
7828
7884
  init_pathResolver();
@@ -7838,6 +7894,12 @@ function createAppEnv(appPath) {
7838
7894
  }
7839
7895
 
7840
7896
  // packages/tooling/src/apps/dev.ts
7897
+ var SIGNAL_EXIT_CODES = {
7898
+ SIGINT: 130,
7899
+ // 128 + 2
7900
+ SIGTERM: 143
7901
+ // 128 + 15
7902
+ };
7841
7903
  async function main() {
7842
7904
  const args = process.argv.slice(2);
7843
7905
  const appName = args[0] && args[0] !== "dev" ? args[0] : void 0;
@@ -7855,12 +7917,65 @@ async function main() {
7855
7917
  log.info(`Starting dev server for ${app.name}...
7856
7918
  `);
7857
7919
  const turboArgs = ["dev", "--filter", app.packageName];
7858
- const result = spawnSync2("bunx", ["turbo", ...turboArgs], {
7920
+ const isWindows = platform() === "win32";
7921
+ const childProcess = spawn("bunx", ["turbo", ...turboArgs], {
7859
7922
  stdio: "inherit",
7860
7923
  env: createAppEnv(app.path),
7861
- cwd: projectRoot
7924
+ cwd: projectRoot,
7925
+ shell: isWindows
7926
+ });
7927
+ const cleanup = (signal) => {
7928
+ if (!childProcess.pid) return;
7929
+ log.debug(`Received ${signal}, cleaning up processes...`);
7930
+ if (isWindows) {
7931
+ try {
7932
+ execSync2(`taskkill /F /T /PID ${childProcess.pid}`, {
7933
+ stdio: "ignore",
7934
+ timeout: 2e3
7935
+ });
7936
+ } catch {
7937
+ }
7938
+ } else {
7939
+ try {
7940
+ process.kill(childProcess.pid, signal);
7941
+ } catch {
7942
+ }
7943
+ try {
7944
+ execSync2(`pkill -P ${childProcess.pid}`, {
7945
+ stdio: "ignore",
7946
+ timeout: 1e3
7947
+ });
7948
+ } catch {
7949
+ }
7950
+ }
7951
+ };
7952
+ const signals = ["SIGINT", "SIGTERM"];
7953
+ const handlers = [];
7954
+ for (const signal of signals) {
7955
+ const handler = () => {
7956
+ cleanup(signal);
7957
+ handlers.forEach((h2) => h2());
7958
+ process.exit(SIGNAL_EXIT_CODES[signal] ?? 130);
7959
+ };
7960
+ process.on(signal, handler);
7961
+ handlers.push(() => process.removeListener(signal, handler));
7962
+ }
7963
+ return new Promise((resolve4) => {
7964
+ childProcess.on("exit", (code, signal) => {
7965
+ handlers.forEach((h2) => h2());
7966
+ if (signal) {
7967
+ log.debug(`Process killed by signal: ${signal}`);
7968
+ resolve4(SIGNAL_EXIT_CODES[signal] ?? 130);
7969
+ } else {
7970
+ resolve4(code ?? 0);
7971
+ }
7972
+ });
7973
+ childProcess.on("error", (error2) => {
7974
+ handlers.forEach((h2) => h2());
7975
+ log.error(`Failed to start dev server: ${error2.message}`);
7976
+ resolve4(1);
7977
+ });
7862
7978
  });
7863
- return result.status || 0;
7864
7979
  }
7865
7980
  export {
7866
7981
  main
@@ -7594,11 +7594,39 @@ var init_PathResolver = __esm({
7594
7594
  });
7595
7595
 
7596
7596
  // packages/tooling/src/utils/errors.ts
7597
- var DoNotDevError;
7597
+ var DO_NOT_DEV_ERROR_CODES, DoNotDevError;
7598
7598
  var init_errors = __esm({
7599
7599
  "packages/tooling/src/utils/errors.ts"() {
7600
7600
  "use strict";
7601
7601
  init_utils();
7602
+ DO_NOT_DEV_ERROR_CODES = {
7603
+ CONFIGURATION_ERROR: "configuration-error",
7604
+ CONFIG_NOT_FOUND: "config-not-found",
7605
+ CONFIG_INVALID: "config-invalid",
7606
+ PATH_RESOLUTION_ERROR: "path-resolution-error",
7607
+ FILE_OPERATION_ERROR: "file-operation-error",
7608
+ FILE_NOT_FOUND: "file-not-found",
7609
+ PERMISSION_DENIED: "permission-denied",
7610
+ GENERATION_ERROR: "generation-error",
7611
+ TEMPLATE_ERROR: "template-error",
7612
+ TEMPLATE_NOT_FOUND: "template-not-found",
7613
+ CLI_EXECUTION_ERROR: "cli-execution-error",
7614
+ COMMAND_NOT_FOUND: "command-not-found",
7615
+ COMMAND_FAILED: "command-failed",
7616
+ VALIDATION_ERROR: "validation-error",
7617
+ SCHEMA_ERROR: "schema-error",
7618
+ DEPENDENCY_ERROR: "dependency-error",
7619
+ DEPENDENCY_NOT_FOUND: "dependency-not-found",
7620
+ DEPENDENCY_VERSION_ERROR: "dependency-version-error",
7621
+ INVALID_ARGUMENT: "invalid-argument",
7622
+ MISSING_ARGUMENT: "missing-argument",
7623
+ MISSING_PROJECT_ID: "missing-project-id",
7624
+ FIREBASE_CLI_ERROR: "firebase-cli-error",
7625
+ DEPLOYMENT_FAILED: "deployment-failed",
7626
+ OPERATION_CANCELLED: "operation-cancelled",
7627
+ TIMEOUT_ERROR: "timeout-error",
7628
+ UNKNOWN_ERROR: "unknown-error"
7629
+ };
7602
7630
  DoNotDevError = class _DoNotDevError extends Error {
7603
7631
  /** The error code categorizing this error */
7604
7632
  code;
@@ -7618,7 +7646,7 @@ var init_errors = __esm({
7618
7646
  * @param {Record<string, any>} [options.context] - Additional context data
7619
7647
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7620
7648
  */
7621
- constructor(message, code = "unknown-error", options) {
7649
+ constructor(message, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7622
7650
  super(message);
7623
7651
  this.name = "DoNotDevError";
7624
7652
  this.code = code;
@@ -7649,7 +7677,7 @@ var init_errors = __esm({
7649
7677
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7650
7678
  * @returns {DoNotDevError} New DoNotDev error wrapping the original
7651
7679
  */
7652
- static from(error2, context, code = "unknown-error", options) {
7680
+ static from(error2, context, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7653
7681
  if (!(error2 instanceof Error)) {
7654
7682
  return new _DoNotDevError(
7655
7683
  `Unknown error: ${String(error2)}`,
@@ -7686,21 +7714,21 @@ var init_errors = __esm({
7686
7714
  }
7687
7715
  const message = error2.message.toLowerCase();
7688
7716
  if (error2.name === "ValidationError" || message.includes("validation")) {
7689
- return "validation-error";
7717
+ return DO_NOT_DEV_ERROR_CODES.VALIDATION_ERROR;
7690
7718
  }
7691
7719
  if (message.includes("not found") || message.includes("no such file")) {
7692
- return "file-not-found";
7720
+ return DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND;
7693
7721
  }
7694
7722
  if (message.includes("permission") || message.includes("access denied")) {
7695
- return "permission-denied";
7723
+ return DO_NOT_DEV_ERROR_CODES.PERMISSION_DENIED;
7696
7724
  }
7697
7725
  if (message.includes("timeout") || message.includes("timed out")) {
7698
- return "timeout-error";
7726
+ return DO_NOT_DEV_ERROR_CODES.TIMEOUT_ERROR;
7699
7727
  }
7700
7728
  if (message.includes("dependency") || message.includes("module not found")) {
7701
- return "dependency-error";
7729
+ return DO_NOT_DEV_ERROR_CODES.DEPENDENCY_ERROR;
7702
7730
  }
7703
- return "unknown-error";
7731
+ return DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR;
7704
7732
  }
7705
7733
  };
7706
7734
  }
@@ -7743,6 +7771,52 @@ var init_pathResolver = __esm({
7743
7771
  }
7744
7772
  });
7745
7773
 
7774
+ // packages/tooling/src/utils/typed-file-operations.ts
7775
+ function readFirebaseJson(filePath) {
7776
+ if (!pathExists(filePath)) {
7777
+ throw new DoNotDevError(
7778
+ `firebase.json not found: ${filePath}`,
7779
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7780
+ { context: { filePath } }
7781
+ );
7782
+ }
7783
+ const content = readSync(filePath, { format: "json" });
7784
+ if (!content || typeof content !== "object") {
7785
+ throw new DoNotDevError(
7786
+ `Invalid firebase.json: ${filePath}`,
7787
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7788
+ { context: { filePath } }
7789
+ );
7790
+ }
7791
+ return content;
7792
+ }
7793
+ function readPackageJson(filePath) {
7794
+ if (!pathExists(filePath)) {
7795
+ throw new DoNotDevError(
7796
+ `package.json not found: ${filePath}`,
7797
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7798
+ { context: { filePath } }
7799
+ );
7800
+ }
7801
+ const content = readSync(filePath, { format: "json" });
7802
+ if (!content || typeof content !== "object" || !("name" in content)) {
7803
+ throw new DoNotDevError(
7804
+ `Invalid package.json: ${filePath}`,
7805
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7806
+ { context: { filePath } }
7807
+ );
7808
+ }
7809
+ return content;
7810
+ }
7811
+ var init_typed_file_operations = __esm({
7812
+ "packages/tooling/src/utils/typed-file-operations.ts"() {
7813
+ "use strict";
7814
+ init_utils();
7815
+ init_pathResolver();
7816
+ init_errors();
7817
+ }
7818
+ });
7819
+
7746
7820
  // packages/tooling/src/bundler/utils.ts
7747
7821
  import { Buffer as Buffer2 } from "node:buffer";
7748
7822
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
@@ -7770,6 +7844,33 @@ var init_utils = __esm({
7770
7844
  }
7771
7845
  });
7772
7846
 
7847
+ // packages/tooling/src/utils/cli-input.ts
7848
+ async function askForMultiSelection(message, choices, defaultIndices = []) {
7849
+ const options = choices.map((choice) => ({
7850
+ value: choice.value,
7851
+ label: choice.title,
7852
+ hint: choice.hint
7853
+ }));
7854
+ const initialValues = defaultIndices.map((i) => choices[i]?.value).filter(Boolean);
7855
+ const result = await fe({
7856
+ message,
7857
+ options,
7858
+ initialValues: initialValues.length > 0 ? initialValues : void 0
7859
+ });
7860
+ if (pD(result)) {
7861
+ xe("Operation cancelled.");
7862
+ process.exit(0);
7863
+ }
7864
+ return result;
7865
+ }
7866
+ var init_cli_input = __esm({
7867
+ "packages/tooling/src/utils/cli-input.ts"() {
7868
+ "use strict";
7869
+ init_utils();
7870
+ init_dist2();
7871
+ }
7872
+ });
7873
+
7773
7874
  // packages/cli/src/bin/commands/emu.ts
7774
7875
  init_utils();
7775
7876
 
@@ -7786,6 +7887,7 @@ init_dist2();
7786
7887
  // packages/tooling/src/utils/app-detection.ts
7787
7888
  init_utils();
7788
7889
  init_pathResolver();
7890
+ init_typed_file_operations();
7789
7891
  function detectApps(projectRoot) {
7790
7892
  const apps = [];
7791
7893
  const appsDir = joinPath(projectRoot, "apps");
@@ -7806,9 +7908,7 @@ function detectApps(projectRoot) {
7806
7908
  const packageJsonPath = joinPath(projectRoot, "package.json");
7807
7909
  if (pathExists(packageJsonPath)) {
7808
7910
  try {
7809
- const packageJson = readSync(packageJsonPath, {
7810
- format: "json"
7811
- });
7911
+ const packageJson = readPackageJson(packageJsonPath);
7812
7912
  if (packageJson && (packageJson.dependencies?.vite || packageJson.devDependencies?.vite || packageJson.dependencies?.next || packageJson.devDependencies?.next)) {
7813
7913
  const pathParts = projectRoot.split(/[/\\]/);
7814
7914
  const appName = pathParts[pathParts.length - 1] || "app";
@@ -7828,10 +7928,10 @@ function analyzeApp(appPath, appName) {
7828
7928
  if (!pathExists(packageJsonPath)) {
7829
7929
  return null;
7830
7930
  }
7831
- let packageJson;
7931
+ let packageJson = null;
7832
7932
  try {
7833
- packageJson = readSync(packageJsonPath, { format: "json" });
7834
- if (!packageJson) {
7933
+ packageJson = readPackageJson(packageJsonPath);
7934
+ if (!packageJson.name) {
7835
7935
  return null;
7836
7936
  }
7837
7937
  } catch {
@@ -7846,14 +7946,14 @@ function analyzeApp(appPath, appName) {
7846
7946
  const functionsPath = joinPath(appPath, "functions");
7847
7947
  const functionsStat = statSync2(functionsPath);
7848
7948
  const hasFunctions = pathExists(functionsPath) && (functionsStat?.isDirectory() ?? false);
7849
- let platform;
7949
+ let platform2;
7850
7950
  if (hasFunctions) {
7851
7951
  const firebaseJsonPath = joinPath(functionsPath, "firebase.json");
7852
7952
  const vercelJsonPath = joinPath(appPath, "vercel.json");
7853
7953
  if (pathExists(firebaseJsonPath)) {
7854
- platform = "firebase";
7954
+ platform2 = "firebase";
7855
7955
  } else if (pathExists(vercelJsonPath)) {
7856
- platform = "vercel";
7956
+ platform2 = "vercel";
7857
7957
  }
7858
7958
  }
7859
7959
  return {
@@ -7864,7 +7964,7 @@ function analyzeApp(appPath, appName) {
7864
7964
  framework,
7865
7965
  hasFunctions,
7866
7966
  functionsPath: hasFunctions ? functionsPath : void 0,
7867
- platform
7967
+ platform: platform2
7868
7968
  };
7869
7969
  }
7870
7970
 
@@ -7904,28 +8004,6 @@ async function selectApp(projectRoot, appName) {
7904
8004
  return apps.find((a) => a.name === selected) || null;
7905
8005
  }
7906
8006
 
7907
- // packages/tooling/src/utils/cli-input.ts
7908
- init_utils();
7909
- init_dist2();
7910
- async function askForMultiSelection(message, choices, defaultIndices = []) {
7911
- const options = choices.map((choice) => ({
7912
- value: choice.value,
7913
- label: choice.title,
7914
- hint: choice.hint
7915
- }));
7916
- const initialValues = defaultIndices.map((i) => choices[i]?.value).filter(Boolean);
7917
- const result = await fe({
7918
- message,
7919
- options,
7920
- initialValues: initialValues.length > 0 ? initialValues : void 0
7921
- });
7922
- if (pD(result)) {
7923
- xe("Operation cancelled.");
7924
- process.exit(0);
7925
- }
7926
- return result;
7927
- }
7928
-
7929
8007
  // packages/tooling/src/utils/spawn-utils.ts
7930
8008
  init_utils();
7931
8009
  import { spawnSync, execSync } from "node:child_process";
@@ -7970,16 +8048,24 @@ function createEmulatorEnv(appPath, framework = "vite", options) {
7970
8048
 
7971
8049
  // packages/tooling/src/apps/emu.ts
7972
8050
  init_utils();
7973
- import { spawnSync as spawnSync2, execSync as execSync2 } from "node:child_process";
8051
+ import {
8052
+ spawn,
8053
+ spawnSync as spawnSync2,
8054
+ execSync as execSync2
8055
+ } from "node:child_process";
8056
+ import { platform } from "node:os";
8057
+ init_cli_input();
7974
8058
  init_cli_output();
7975
8059
  init_errors();
7976
8060
  init_pathResolver();
7977
8061
  init_pathResolver();
8062
+ init_typed_file_operations();
7978
8063
  function discoverFirebaseProjectId(appPath) {
7979
8064
  const firebasercPath = joinPath(appPath, ".firebaserc");
7980
8065
  if (pathExists(firebasercPath)) {
7981
8066
  try {
7982
- const firebaserc = readSync(firebasercPath, { format: "json" });
8067
+ const firebasercRaw = readSync(firebasercPath, { format: "json" });
8068
+ const firebaserc = firebasercRaw && typeof firebasercRaw === "object" ? firebasercRaw : null;
7983
8069
  if (firebaserc?.projects?.default) {
7984
8070
  return firebaserc.projects.default;
7985
8071
  }
@@ -8014,12 +8100,18 @@ function killPorts(ports) {
8014
8100
  }
8015
8101
  } else {
8016
8102
  try {
8017
- const pid = execSync2(`lsof -ti:${port}`, {
8103
+ const output = execSync2(`lsof -ti:${port}`, {
8018
8104
  encoding: "utf-8"
8019
8105
  }).trim();
8020
- if (pid) {
8021
- execSync2(`kill -9 ${pid}`, { stdio: "ignore" });
8022
- log.debug(`Killed process ${pid} on port ${port}`);
8106
+ if (output) {
8107
+ const pids = output.split("\n").filter(Boolean);
8108
+ for (const pid of pids) {
8109
+ try {
8110
+ execSync2(`kill -9 ${pid}`, { stdio: "ignore" });
8111
+ log.debug(`Killed process ${pid} on port ${port}`);
8112
+ } catch {
8113
+ }
8114
+ }
8023
8115
  }
8024
8116
  } catch {
8025
8117
  }
@@ -8206,8 +8298,10 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
8206
8298
  let emulatorConfig = null;
8207
8299
  if (pathExists(appFirebaseJson)) {
8208
8300
  try {
8209
- const firebaseConfig = readSync(appFirebaseJson, { format: "json" });
8210
- emulatorConfig = firebaseConfig?.emulators;
8301
+ const firebaseConfig = readFirebaseJson(appFirebaseJson);
8302
+ if (firebaseConfig.emulators) {
8303
+ emulatorConfig = firebaseConfig.emulators;
8304
+ }
8211
8305
  } catch {
8212
8306
  }
8213
8307
  }
@@ -8244,7 +8338,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
8244
8338
  const concurrentlyArgs = [
8245
8339
  "--kill-others-on-fail",
8246
8340
  "--prefix-colors",
8247
- "cyan,green,yellow",
8341
+ useStripe ? "cyan,green,yellow" : "cyan,green",
8248
8342
  "--prefix",
8249
8343
  "[{name}]",
8250
8344
  "--names",
@@ -8262,15 +8356,78 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
8262
8356
  log.info(" stripe trigger checkout.session.completed");
8263
8357
  log.info("");
8264
8358
  }
8265
- const result = spawnSync2("bunx", ["concurrently", ...concurrentlyArgs], {
8266
- stdio: "inherit",
8267
- env: createEmulatorEnv(app.path, app.framework, {
8268
- useEmulatorAuth: useAuth,
8269
- useEmulatorFirestore: useFirestore
8270
- }),
8271
- cwd: app.path
8359
+ const isWindows = platform() === "win32";
8360
+ const childProcess = spawn(
8361
+ "bunx",
8362
+ ["concurrently", ...concurrentlyArgs],
8363
+ {
8364
+ stdio: "inherit",
8365
+ env: createEmulatorEnv(app.path, app.framework, {
8366
+ useEmulatorAuth: useAuth,
8367
+ useEmulatorFirestore: useFirestore
8368
+ }),
8369
+ cwd: app.path,
8370
+ shell: isWindows
8371
+ }
8372
+ );
8373
+ const SIGNAL_EXIT_CODES = {
8374
+ SIGINT: 130,
8375
+ // 128 + 2
8376
+ SIGTERM: 143
8377
+ // 128 + 15
8378
+ };
8379
+ const cleanup = (signal) => {
8380
+ if (!childProcess.pid) return;
8381
+ log.debug(`Received ${signal}, cleaning up processes...`);
8382
+ if (isWindows) {
8383
+ try {
8384
+ execSync2(`taskkill /F /T /PID ${childProcess.pid}`, {
8385
+ stdio: "ignore",
8386
+ timeout: 2e3
8387
+ });
8388
+ } catch {
8389
+ }
8390
+ } else {
8391
+ try {
8392
+ process.kill(childProcess.pid, signal);
8393
+ } catch {
8394
+ }
8395
+ try {
8396
+ execSync2(`pkill -P ${childProcess.pid}`, {
8397
+ stdio: "ignore",
8398
+ timeout: 1e3
8399
+ });
8400
+ } catch {
8401
+ }
8402
+ }
8403
+ };
8404
+ const signals = ["SIGINT", "SIGTERM"];
8405
+ const handlers = [];
8406
+ for (const signal of signals) {
8407
+ const handler = () => {
8408
+ cleanup(signal);
8409
+ handlers.forEach((h2) => h2());
8410
+ process.exit(SIGNAL_EXIT_CODES[signal] ?? 130);
8411
+ };
8412
+ process.on(signal, handler);
8413
+ handlers.push(() => process.removeListener(signal, handler));
8414
+ }
8415
+ return new Promise((resolve4) => {
8416
+ childProcess.on("exit", (code, signal) => {
8417
+ handlers.forEach((h2) => h2());
8418
+ if (signal) {
8419
+ log.debug(`Process killed by signal: ${signal}`);
8420
+ resolve4(SIGNAL_EXIT_CODES[signal] ?? 130);
8421
+ } else {
8422
+ resolve4(code ?? 0);
8423
+ }
8424
+ });
8425
+ childProcess.on("error", (error2) => {
8426
+ handlers.forEach((h2) => h2());
8427
+ log.error(`Failed to start emulators: ${error2.message}`);
8428
+ resolve4(1);
8429
+ });
8272
8430
  });
8273
- return result.status || 0;
8274
8431
  }
8275
8432
  export {
8276
8433
  main