@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
@@ -16,6 +16,10 @@ var __esm = (fn, res) => function __init() {
16
16
  var __commonJS = (cb, mod) => function __require2() {
17
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
18
  };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
19
23
  var __copyProps = (to, from, except, desc) => {
20
24
  if (from && typeof from === "object" || typeof from === "function") {
21
25
  for (let key of __getOwnPropNames(from))
@@ -7687,11 +7691,39 @@ var init_PathResolver = __esm({
7687
7691
  });
7688
7692
 
7689
7693
  // packages/tooling/src/utils/errors.ts
7690
- var DoNotDevError;
7694
+ var DO_NOT_DEV_ERROR_CODES, DoNotDevError;
7691
7695
  var init_errors = __esm({
7692
7696
  "packages/tooling/src/utils/errors.ts"() {
7693
7697
  "use strict";
7694
7698
  init_utils();
7699
+ DO_NOT_DEV_ERROR_CODES = {
7700
+ CONFIGURATION_ERROR: "configuration-error",
7701
+ CONFIG_NOT_FOUND: "config-not-found",
7702
+ CONFIG_INVALID: "config-invalid",
7703
+ PATH_RESOLUTION_ERROR: "path-resolution-error",
7704
+ FILE_OPERATION_ERROR: "file-operation-error",
7705
+ FILE_NOT_FOUND: "file-not-found",
7706
+ PERMISSION_DENIED: "permission-denied",
7707
+ GENERATION_ERROR: "generation-error",
7708
+ TEMPLATE_ERROR: "template-error",
7709
+ TEMPLATE_NOT_FOUND: "template-not-found",
7710
+ CLI_EXECUTION_ERROR: "cli-execution-error",
7711
+ COMMAND_NOT_FOUND: "command-not-found",
7712
+ COMMAND_FAILED: "command-failed",
7713
+ VALIDATION_ERROR: "validation-error",
7714
+ SCHEMA_ERROR: "schema-error",
7715
+ DEPENDENCY_ERROR: "dependency-error",
7716
+ DEPENDENCY_NOT_FOUND: "dependency-not-found",
7717
+ DEPENDENCY_VERSION_ERROR: "dependency-version-error",
7718
+ INVALID_ARGUMENT: "invalid-argument",
7719
+ MISSING_ARGUMENT: "missing-argument",
7720
+ MISSING_PROJECT_ID: "missing-project-id",
7721
+ FIREBASE_CLI_ERROR: "firebase-cli-error",
7722
+ DEPLOYMENT_FAILED: "deployment-failed",
7723
+ OPERATION_CANCELLED: "operation-cancelled",
7724
+ TIMEOUT_ERROR: "timeout-error",
7725
+ UNKNOWN_ERROR: "unknown-error"
7726
+ };
7695
7727
  DoNotDevError = class _DoNotDevError extends Error {
7696
7728
  /** The error code categorizing this error */
7697
7729
  code;
@@ -7711,7 +7743,7 @@ var init_errors = __esm({
7711
7743
  * @param {Record<string, any>} [options.context] - Additional context data
7712
7744
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7713
7745
  */
7714
- constructor(message, code = "unknown-error", options) {
7746
+ constructor(message, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7715
7747
  super(message);
7716
7748
  this.name = "DoNotDevError";
7717
7749
  this.code = code;
@@ -7742,7 +7774,7 @@ var init_errors = __esm({
7742
7774
  * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7743
7775
  * @returns {DoNotDevError} New DoNotDev error wrapping the original
7744
7776
  */
7745
- static from(error2, context, code = "unknown-error", options) {
7777
+ static from(error2, context, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7746
7778
  if (!(error2 instanceof Error)) {
7747
7779
  return new _DoNotDevError(
7748
7780
  `Unknown error: ${String(error2)}`,
@@ -7779,21 +7811,21 @@ var init_errors = __esm({
7779
7811
  }
7780
7812
  const message = error2.message.toLowerCase();
7781
7813
  if (error2.name === "ValidationError" || message.includes("validation")) {
7782
- return "validation-error";
7814
+ return DO_NOT_DEV_ERROR_CODES.VALIDATION_ERROR;
7783
7815
  }
7784
7816
  if (message.includes("not found") || message.includes("no such file")) {
7785
- return "file-not-found";
7817
+ return DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND;
7786
7818
  }
7787
7819
  if (message.includes("permission") || message.includes("access denied")) {
7788
- return "permission-denied";
7820
+ return DO_NOT_DEV_ERROR_CODES.PERMISSION_DENIED;
7789
7821
  }
7790
7822
  if (message.includes("timeout") || message.includes("timed out")) {
7791
- return "timeout-error";
7823
+ return DO_NOT_DEV_ERROR_CODES.TIMEOUT_ERROR;
7792
7824
  }
7793
7825
  if (message.includes("dependency") || message.includes("module not found")) {
7794
- return "dependency-error";
7826
+ return DO_NOT_DEV_ERROR_CODES.DEPENDENCY_ERROR;
7795
7827
  }
7796
- return "unknown-error";
7828
+ return DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR;
7797
7829
  }
7798
7830
  };
7799
7831
  }
@@ -7815,7 +7847,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
7815
7847
  function readdirSync2(dirPath, options) {
7816
7848
  return pathResolverInstance.readdirSync(dirPath, options);
7817
7849
  }
7818
- var pathResolverInstance, resolvePackage, normalizePath, pathExists, readSync, writeSync, removeSync, statSync2, ensureDirSync, joinPath;
7850
+ var pathResolverInstance, resolvePackage, normalizePath, pathExists, readSync, write, writeSync, remove, removeSync, statSync2, ensureDir, ensureDirSync, getRelativePathBetween, getDirname, joinPath;
7819
7851
  var init_pathResolver = __esm({
7820
7852
  "packages/tooling/src/utils/pathResolver.ts"() {
7821
7853
  "use strict";
@@ -7828,16 +7860,111 @@ var init_pathResolver = __esm({
7828
7860
  };
7829
7861
  pathExists = (filePath, silent = false) => pathResolverInstance.pathExists(filePath, silent);
7830
7862
  readSync = (filePath, options) => pathResolverInstance.readSync(filePath, options);
7863
+ write = async (filePath, content, options) => pathResolverInstance.write(filePath, content, options);
7831
7864
  writeSync = (filePath, content, options) => pathResolverInstance.writeSync(filePath, content, options);
7865
+ remove = async (path) => pathResolverInstance.remove(path);
7832
7866
  removeSync = (path) => pathResolverInstance.removeSync(path);
7833
7867
  statSync2 = (filePath) => pathResolverInstance.statSync(filePath);
7868
+ ensureDir = async (dirPath) => pathResolverInstance.ensureDir(dirPath);
7834
7869
  ensureDirSync = (dirPath) => pathResolverInstance.ensureDirSync(dirPath);
7870
+ getRelativePathBetween = (from, to) => {
7871
+ return normalizePath(relative2(from, to));
7872
+ };
7873
+ getDirname = (filePath) => {
7874
+ return normalizePath(dirname2(filePath));
7875
+ };
7835
7876
  joinPath = (...pathSegments) => {
7836
7877
  return normalizePath(...pathSegments);
7837
7878
  };
7838
7879
  }
7839
7880
  });
7840
7881
 
7882
+ // packages/tooling/src/utils/typed-file-operations.ts
7883
+ function readFirebaseJson(filePath) {
7884
+ if (!pathExists(filePath)) {
7885
+ throw new DoNotDevError(
7886
+ `firebase.json not found: ${filePath}`,
7887
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7888
+ { context: { filePath } }
7889
+ );
7890
+ }
7891
+ const content = readSync(filePath, { format: "json" });
7892
+ if (!content || typeof content !== "object") {
7893
+ throw new DoNotDevError(
7894
+ `Invalid firebase.json: ${filePath}`,
7895
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7896
+ { context: { filePath } }
7897
+ );
7898
+ }
7899
+ return content;
7900
+ }
7901
+ function readFirebaserc(filePath) {
7902
+ if (!pathExists(filePath)) {
7903
+ return { projects: {} };
7904
+ }
7905
+ const content = readSync(filePath, { format: "json" });
7906
+ if (!content || typeof content !== "object") {
7907
+ throw new DoNotDevError(
7908
+ `Invalid .firebaserc: ${filePath}`,
7909
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7910
+ { context: { filePath } }
7911
+ );
7912
+ }
7913
+ return content;
7914
+ }
7915
+ function readPackageJson(filePath) {
7916
+ if (!pathExists(filePath)) {
7917
+ throw new DoNotDevError(
7918
+ `package.json not found: ${filePath}`,
7919
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7920
+ { context: { filePath } }
7921
+ );
7922
+ }
7923
+ const content = readSync(filePath, { format: "json" });
7924
+ if (!content || typeof content !== "object" || !("name" in content)) {
7925
+ throw new DoNotDevError(
7926
+ `Invalid package.json: ${filePath}`,
7927
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7928
+ { context: { filePath } }
7929
+ );
7930
+ }
7931
+ return content;
7932
+ }
7933
+ function readServiceAccountKey(filePath) {
7934
+ if (!pathExists(filePath)) {
7935
+ throw new DoNotDevError(
7936
+ `Service account key not found: ${filePath}`,
7937
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
7938
+ { context: { filePath } }
7939
+ );
7940
+ }
7941
+ const content = readSync(filePath, { format: "json" });
7942
+ if (!content || typeof content !== "object") {
7943
+ throw new DoNotDevError(
7944
+ `Invalid service account key: ${filePath}`,
7945
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7946
+ { context: { filePath } }
7947
+ );
7948
+ }
7949
+ const key = content;
7950
+ if (!key.project_id || !key.private_key || !key.client_email) {
7951
+ throw new DoNotDevError(
7952
+ `Invalid service account key: missing required fields`,
7953
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7954
+ { context: { filePath, missingFields: ["project_id", "private_key", "client_email"].filter((f) => !key[f]) } }
7955
+ );
7956
+ }
7957
+ return content;
7958
+ }
7959
+ var init_typed_file_operations = __esm({
7960
+ "packages/tooling/src/utils/typed-file-operations.ts"() {
7961
+ "use strict";
7962
+ init_utils();
7963
+ init_pathResolver();
7964
+ init_errors();
7965
+ }
7966
+ });
7967
+
7841
7968
  // packages/tooling/src/bundler/utils.ts
7842
7969
  import { Buffer as Buffer2 } from "node:buffer";
7843
7970
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
@@ -7865,129 +7992,466 @@ var init_utils = __esm({
7865
7992
  }
7866
7993
  });
7867
7994
 
7868
- // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
7869
- var require_identity = __commonJS({
7870
- "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports) {
7995
+ // packages/tooling/src/utils/cli-input.ts
7996
+ async function askForInput(message, defaultValue = "") {
7997
+ const result = await he({
7998
+ message,
7999
+ placeholder: defaultValue || void 0,
8000
+ defaultValue: defaultValue || void 0
8001
+ });
8002
+ if (pD(result)) {
8003
+ xe("Operation cancelled.");
8004
+ process.exit(0);
8005
+ }
8006
+ return result;
8007
+ }
8008
+ async function askForConfirmation(message, defaultValue = false) {
8009
+ const result = await ye({
8010
+ message,
8011
+ initialValue: defaultValue
8012
+ });
8013
+ if (pD(result)) {
8014
+ xe("Operation cancelled.");
8015
+ process.exit(0);
8016
+ }
8017
+ return result;
8018
+ }
8019
+ async function askForSelection(message, choices, defaultValue = 0) {
8020
+ const options = choices.map((choice) => ({
8021
+ value: choice.value,
8022
+ label: choice.title,
8023
+ hint: choice.hint
8024
+ }));
8025
+ const result = await ve({
8026
+ message,
8027
+ options,
8028
+ initialValue: choices[defaultValue]?.value
8029
+ });
8030
+ if (pD(result)) {
8031
+ xe("Operation cancelled.");
8032
+ process.exit(0);
8033
+ }
8034
+ return result;
8035
+ }
8036
+ var init_cli_input = __esm({
8037
+ "packages/tooling/src/utils/cli-input.ts"() {
7871
8038
  "use strict";
7872
8039
  init_utils();
7873
- var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
7874
- var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
7875
- var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
7876
- var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
7877
- var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
7878
- var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
7879
- var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
7880
- var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
7881
- var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
7882
- var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
7883
- var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
7884
- var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
7885
- var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
7886
- function isCollection(node) {
7887
- if (node && typeof node === "object")
7888
- switch (node[NODE_TYPE]) {
7889
- case MAP:
7890
- case SEQ:
7891
- return true;
7892
- }
7893
- return false;
7894
- }
7895
- function isNode(node) {
7896
- if (node && typeof node === "object")
7897
- switch (node[NODE_TYPE]) {
7898
- case ALIAS:
7899
- case MAP:
7900
- case SCALAR:
7901
- case SEQ:
7902
- return true;
7903
- }
7904
- return false;
7905
- }
7906
- var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
7907
- exports.ALIAS = ALIAS;
7908
- exports.DOC = DOC;
7909
- exports.MAP = MAP;
7910
- exports.NODE_TYPE = NODE_TYPE;
7911
- exports.PAIR = PAIR;
7912
- exports.SCALAR = SCALAR;
7913
- exports.SEQ = SEQ;
7914
- exports.hasAnchor = hasAnchor;
7915
- exports.isAlias = isAlias;
7916
- exports.isCollection = isCollection;
7917
- exports.isDocument = isDocument;
7918
- exports.isMap = isMap;
7919
- exports.isNode = isNode;
7920
- exports.isPair = isPair;
7921
- exports.isScalar = isScalar;
7922
- exports.isSeq = isSeq;
8040
+ init_dist2();
7923
8041
  }
7924
8042
  });
7925
8043
 
7926
- // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js
7927
- var require_visit = __commonJS({
7928
- "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js"(exports) {
7929
- "use strict";
7930
- init_utils();
7931
- var identity = require_identity();
7932
- var BREAK = /* @__PURE__ */ Symbol("break visit");
7933
- var SKIP = /* @__PURE__ */ Symbol("skip children");
7934
- var REMOVE = /* @__PURE__ */ Symbol("remove node");
7935
- function visit(node, visitor) {
7936
- const visitor_ = initVisitor(visitor);
7937
- if (identity.isDocument(node)) {
7938
- const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));
7939
- if (cd === REMOVE)
7940
- node.contents = null;
7941
- } else
7942
- visit_(null, node, visitor_, Object.freeze([]));
7943
- }
7944
- visit.BREAK = BREAK;
7945
- visit.SKIP = SKIP;
7946
- visit.REMOVE = REMOVE;
7947
- function visit_(key, node, visitor, path) {
7948
- const ctrl = callVisitor(key, node, visitor, path);
7949
- if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
7950
- replaceNode(key, path, ctrl);
7951
- return visit_(key, ctrl, visitor, path);
7952
- }
7953
- if (typeof ctrl !== "symbol") {
7954
- if (identity.isCollection(node)) {
7955
- path = Object.freeze(path.concat(node));
7956
- for (let i = 0; i < node.items.length; ++i) {
7957
- const ci = visit_(i, node.items[i], visitor, path);
7958
- if (typeof ci === "number")
7959
- i = ci - 1;
7960
- else if (ci === BREAK)
7961
- return BREAK;
7962
- else if (ci === REMOVE) {
7963
- node.items.splice(i, 1);
7964
- i -= 1;
8044
+ // packages/tooling/src/utils/cli-tools.ts
8045
+ import { spawnSync as spawnSync2 } from "node:child_process";
8046
+ function checkCLI(command, args = ["--version"], useCache = true) {
8047
+ const cacheKey = `${command}:${args.join(",")}`;
8048
+ if (useCache && cliCache.has(cacheKey)) {
8049
+ return cliCache.get(cacheKey);
8050
+ }
8051
+ try {
8052
+ let result;
8053
+ if (process.platform === "win32") {
8054
+ const username = process.env.USERNAME || process.env.USER || "";
8055
+ const programFiles = process.env.ProgramFiles || "C:\\Program Files";
8056
+ const commonPaths = [
8057
+ `${programFiles}\\nodejs\\${command}.ps1`,
8058
+ `${programFiles}\\nodejs\\${command}.cmd`,
8059
+ `${programFiles}\\nodejs\\${command}`,
8060
+ `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.ps1`,
8061
+ `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.cmd`,
8062
+ `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}`,
8063
+ process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.ps1` : null,
8064
+ process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.cmd` : null,
8065
+ process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}` : null
8066
+ ].filter(Boolean);
8067
+ for (const fullPath of commonPaths) {
8068
+ try {
8069
+ const testResult = spawnSync2(
8070
+ "powershell.exe",
8071
+ [
8072
+ "-NoProfile",
8073
+ "-ExecutionPolicy",
8074
+ "Bypass",
8075
+ "-Command",
8076
+ `& '${fullPath}' ${args.join(" ")}`
8077
+ ],
8078
+ {
8079
+ stdio: "pipe",
8080
+ shell: false
7965
8081
  }
8082
+ );
8083
+ if (testResult.status === 0) {
8084
+ cliCache.set(cacheKey, true);
8085
+ return true;
7966
8086
  }
7967
- } else if (identity.isPair(node)) {
7968
- path = Object.freeze(path.concat(node));
7969
- const ck = visit_("key", node.key, visitor, path);
7970
- if (ck === BREAK)
7971
- return BREAK;
7972
- else if (ck === REMOVE)
7973
- node.key = null;
7974
- const cv = visit_("value", node.value, visitor, path);
7975
- if (cv === BREAK)
7976
- return BREAK;
7977
- else if (cv === REMOVE)
7978
- node.value = null;
8087
+ } catch {
8088
+ continue;
7979
8089
  }
7980
8090
  }
7981
- return ctrl;
7982
- }
7983
- async function visitAsync(node, visitor) {
7984
- const visitor_ = initVisitor(visitor);
7985
- if (identity.isDocument(node)) {
7986
- const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node]));
7987
- if (cd === REMOVE)
7988
- node.contents = null;
7989
- } else
7990
- await visitAsync_(null, node, visitor_, Object.freeze([]));
8091
+ result = spawnSync2(
8092
+ "powershell.exe",
8093
+ [
8094
+ "-NoProfile",
8095
+ "-ExecutionPolicy",
8096
+ "Bypass",
8097
+ "-Command",
8098
+ `${command} ${args.join(" ")}`
8099
+ ],
8100
+ {
8101
+ stdio: "pipe",
8102
+ shell: false
8103
+ }
8104
+ );
8105
+ } else {
8106
+ result = spawnSync2(command, args, {
8107
+ stdio: "pipe",
8108
+ shell: true
8109
+ });
8110
+ }
8111
+ const isAvailable = result.status === 0;
8112
+ cliCache.set(cacheKey, isAvailable);
8113
+ return isAvailable;
8114
+ } catch (err) {
8115
+ cliCache.set(cacheKey, false);
8116
+ return false;
8117
+ }
8118
+ }
8119
+ function checkBun() {
8120
+ return checkCLI("bun");
8121
+ }
8122
+ function checkNpm() {
8123
+ return checkCLI("npm");
8124
+ }
8125
+ function checkYarn() {
8126
+ return checkCLI("yarn");
8127
+ }
8128
+ function checkFirebase() {
8129
+ return checkCLI("firebase");
8130
+ }
8131
+ function checkSentry() {
8132
+ return checkCLI("sentry-cli");
8133
+ }
8134
+ function checkGitHub() {
8135
+ return checkCLI("gh");
8136
+ }
8137
+ function checkGit() {
8138
+ return checkCLI("git");
8139
+ }
8140
+ function checkTsx() {
8141
+ return checkCLI("tsx");
8142
+ }
8143
+ function getCLIInstallInstructions(tool) {
8144
+ const platform = process.platform;
8145
+ const instructions = {
8146
+ [CLI_TOOLS.BUN]: {
8147
+ win32: [
8148
+ 'powershell -c "irm bun.sh/install.ps1 | iex"',
8149
+ "Or download from: https://bun.sh"
8150
+ ],
8151
+ darwin: [
8152
+ "curl -fsSL https://bun.sh/install | bash",
8153
+ "Or: brew install bun"
8154
+ ],
8155
+ linux: [
8156
+ "curl -fsSL https://bun.sh/install | bash",
8157
+ "Or check your package manager (apt, dnf, pacman, etc.)"
8158
+ ]
8159
+ },
8160
+ [CLI_TOOLS.NPM]: {
8161
+ win32: [
8162
+ "Install Node.js from: https://nodejs.org",
8163
+ "npm comes bundled with Node.js"
8164
+ ],
8165
+ darwin: [
8166
+ "Install Node.js from: https://nodejs.org",
8167
+ "Or: brew install node",
8168
+ "npm comes bundled with Node.js"
8169
+ ],
8170
+ linux: [
8171
+ "Install Node.js from: https://nodejs.org",
8172
+ "Or use your package manager: dnf install nodejs, apt install nodejs, etc.",
8173
+ "npm comes bundled with Node.js"
8174
+ ]
8175
+ },
8176
+ [CLI_TOOLS.YARN]: {
8177
+ win32: ["npm install -g yarn", "Or: choco install yarn"],
8178
+ darwin: ["npm install -g yarn", "Or: brew install yarn"],
8179
+ linux: ["npm install -g yarn", "Or check your package manager"]
8180
+ },
8181
+ [CLI_TOOLS.FIREBASE]: {
8182
+ win32: [
8183
+ "npm install -g firebase-tools",
8184
+ "Or download standalone binary: https://firebase.google.com/docs/cli#windows-standalone-binary"
8185
+ ],
8186
+ darwin: [
8187
+ "npm install -g firebase-tools",
8188
+ "Or: brew install firebase-cli"
8189
+ ],
8190
+ linux: [
8191
+ "npm install -g firebase-tools",
8192
+ "Or check your package manager (dnf, apt, pacman, etc.)"
8193
+ ]
8194
+ },
8195
+ [CLI_TOOLS.SUPABASE]: {
8196
+ win32: [
8197
+ "npm install -g supabase",
8198
+ "Or download from: https://github.com/supabase/cli/releases"
8199
+ ],
8200
+ darwin: [
8201
+ "brew install supabase/tap/supabase",
8202
+ "Or: npm install -g supabase"
8203
+ ],
8204
+ linux: [
8205
+ "npm install -g supabase",
8206
+ "Or see: https://supabase.com/docs/guides/cli"
8207
+ ]
8208
+ },
8209
+ [CLI_TOOLS.VERCEL]: {
8210
+ win32: ["npm install -g vercel", "Or: npx vercel (no install)"],
8211
+ darwin: ["npm install -g vercel", "Or: brew install vercel", "Or: npx vercel"],
8212
+ linux: ["npm install -g vercel", "Or: npx vercel"]
8213
+ },
8214
+ [CLI_TOOLS.SENTRY_CLI]: {
8215
+ win32: [
8216
+ "npm install -g @sentry/cli",
8217
+ "Or download from: https://github.com/getsentry/sentry-cli/releases"
8218
+ ],
8219
+ darwin: [
8220
+ "npm install -g @sentry/cli",
8221
+ "Or: brew install getsentry/tools/sentry-cli"
8222
+ ],
8223
+ linux: [
8224
+ "npm install -g @sentry/cli",
8225
+ "Or: curl -sL https://sentry.io/get-cli/ | bash"
8226
+ ]
8227
+ },
8228
+ [CLI_TOOLS.GH]: {
8229
+ win32: [
8230
+ "Download from: https://cli.github.com",
8231
+ "Or: winget install --id GitHub.cli",
8232
+ "Or: choco install gh"
8233
+ ],
8234
+ darwin: ["brew install gh", "Or download from: https://cli.github.com"],
8235
+ linux: [
8236
+ "See: https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
8237
+ "dnf install gh",
8238
+ "apt install gh",
8239
+ "Or download from: https://cli.github.com"
8240
+ ]
8241
+ },
8242
+ [CLI_TOOLS.GIT]: {
8243
+ win32: [
8244
+ "Download from: https://git-scm.com/download/win",
8245
+ "Or: winget install --id Git.Git"
8246
+ ],
8247
+ darwin: ["brew install git", "Or: xcode-select --install"],
8248
+ linux: [
8249
+ "Use your package manager: dnf install git, apt install git, etc."
8250
+ ]
8251
+ },
8252
+ [CLI_TOOLS.TSX]: {
8253
+ win32: ["npm install -g tsx"],
8254
+ darwin: ["npm install -g tsx"],
8255
+ linux: ["npm install -g tsx"]
8256
+ }
8257
+ };
8258
+ const platformInstructions = instructions[tool]?.[platform] || instructions[tool]?.linux || [];
8259
+ return platformInstructions.join("\n \u2022 ");
8260
+ }
8261
+ function formatCLIMissingError(tool, additionalContext) {
8262
+ const toolName = tool === CLI_TOOLS.SENTRY_CLI ? "Sentry CLI" : tool.toUpperCase();
8263
+ M2.error(`${toolName} is not installed or not in PATH.`);
8264
+ const instructions = getCLIInstallInstructions(tool);
8265
+ if (instructions) {
8266
+ M2.warn("Install it with:");
8267
+ M2.message(` \u2022 ${instructions}`);
8268
+ }
8269
+ if (additionalContext) {
8270
+ M2.info(`
8271
+ ${additionalContext}`);
8272
+ }
8273
+ const platform = process.platform;
8274
+ M2.info("\nIf already installed, ensure it's in your PATH:");
8275
+ if (platform === "win32") {
8276
+ M2.message(" \u2022 Check with: where " + tool);
8277
+ M2.message(
8278
+ " \u2022 Common location: C:\\Users\\<username>\\AppData\\Roaming\\npm\\"
8279
+ );
8280
+ } else {
8281
+ M2.message(" \u2022 Check with: which " + tool);
8282
+ M2.message(" \u2022 Common locations:");
8283
+ M2.message(" - ~/.nvm/versions/node/*/bin/" + tool);
8284
+ M2.message(" - ~/.bun/bin/" + tool);
8285
+ M2.message(" - /usr/local/bin/" + tool);
8286
+ M2.message(" - /usr/bin/" + tool);
8287
+ }
8288
+ }
8289
+ function requireCLI(tool, additionalContext) {
8290
+ const checkers = {
8291
+ [CLI_TOOLS.BUN]: checkBun,
8292
+ [CLI_TOOLS.NPM]: checkNpm,
8293
+ [CLI_TOOLS.YARN]: checkYarn,
8294
+ [CLI_TOOLS.FIREBASE]: checkFirebase,
8295
+ [CLI_TOOLS.SUPABASE]: () => checkCLI(CLI_TOOLS.SUPABASE),
8296
+ [CLI_TOOLS.VERCEL]: () => checkCLI(CLI_TOOLS.VERCEL),
8297
+ [CLI_TOOLS.SENTRY_CLI]: checkSentry,
8298
+ [CLI_TOOLS.GH]: checkGitHub,
8299
+ [CLI_TOOLS.GIT]: checkGit,
8300
+ [CLI_TOOLS.TSX]: checkTsx
8301
+ };
8302
+ cliCache.delete(`${tool}:--version`);
8303
+ const isAvailable = checkers[tool]?.();
8304
+ if (!isAvailable) {
8305
+ formatCLIMissingError(tool, additionalContext);
8306
+ process.exit(1);
8307
+ }
8308
+ return true;
8309
+ }
8310
+ var CLI_TOOLS, cliCache;
8311
+ var init_cli_tools = __esm({
8312
+ "packages/tooling/src/utils/cli-tools.ts"() {
8313
+ "use strict";
8314
+ init_utils();
8315
+ init_dist2();
8316
+ CLI_TOOLS = {
8317
+ BUN: "bun",
8318
+ NPM: "npm",
8319
+ YARN: "yarn",
8320
+ FIREBASE: "firebase",
8321
+ SUPABASE: "supabase",
8322
+ VERCEL: "vercel",
8323
+ SENTRY_CLI: "sentry-cli",
8324
+ GH: "gh",
8325
+ GIT: "git",
8326
+ TSX: "tsx"
8327
+ };
8328
+ cliCache = /* @__PURE__ */ new Map();
8329
+ }
8330
+ });
8331
+
8332
+ // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
8333
+ var require_identity = __commonJS({
8334
+ "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports) {
8335
+ "use strict";
8336
+ init_utils();
8337
+ var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
8338
+ var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
8339
+ var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
8340
+ var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
8341
+ var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
8342
+ var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
8343
+ var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
8344
+ var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
8345
+ var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
8346
+ var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
8347
+ var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
8348
+ var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
8349
+ var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
8350
+ function isCollection(node) {
8351
+ if (node && typeof node === "object")
8352
+ switch (node[NODE_TYPE]) {
8353
+ case MAP:
8354
+ case SEQ:
8355
+ return true;
8356
+ }
8357
+ return false;
8358
+ }
8359
+ function isNode(node) {
8360
+ if (node && typeof node === "object")
8361
+ switch (node[NODE_TYPE]) {
8362
+ case ALIAS:
8363
+ case MAP:
8364
+ case SCALAR:
8365
+ case SEQ:
8366
+ return true;
8367
+ }
8368
+ return false;
8369
+ }
8370
+ var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
8371
+ exports.ALIAS = ALIAS;
8372
+ exports.DOC = DOC;
8373
+ exports.MAP = MAP;
8374
+ exports.NODE_TYPE = NODE_TYPE;
8375
+ exports.PAIR = PAIR;
8376
+ exports.SCALAR = SCALAR;
8377
+ exports.SEQ = SEQ;
8378
+ exports.hasAnchor = hasAnchor;
8379
+ exports.isAlias = isAlias;
8380
+ exports.isCollection = isCollection;
8381
+ exports.isDocument = isDocument;
8382
+ exports.isMap = isMap;
8383
+ exports.isNode = isNode;
8384
+ exports.isPair = isPair;
8385
+ exports.isScalar = isScalar;
8386
+ exports.isSeq = isSeq;
8387
+ }
8388
+ });
8389
+
8390
+ // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js
8391
+ var require_visit = __commonJS({
8392
+ "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js"(exports) {
8393
+ "use strict";
8394
+ init_utils();
8395
+ var identity = require_identity();
8396
+ var BREAK = /* @__PURE__ */ Symbol("break visit");
8397
+ var SKIP = /* @__PURE__ */ Symbol("skip children");
8398
+ var REMOVE = /* @__PURE__ */ Symbol("remove node");
8399
+ function visit(node, visitor) {
8400
+ const visitor_ = initVisitor(visitor);
8401
+ if (identity.isDocument(node)) {
8402
+ const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));
8403
+ if (cd === REMOVE)
8404
+ node.contents = null;
8405
+ } else
8406
+ visit_(null, node, visitor_, Object.freeze([]));
8407
+ }
8408
+ visit.BREAK = BREAK;
8409
+ visit.SKIP = SKIP;
8410
+ visit.REMOVE = REMOVE;
8411
+ function visit_(key, node, visitor, path) {
8412
+ const ctrl = callVisitor(key, node, visitor, path);
8413
+ if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
8414
+ replaceNode(key, path, ctrl);
8415
+ return visit_(key, ctrl, visitor, path);
8416
+ }
8417
+ if (typeof ctrl !== "symbol") {
8418
+ if (identity.isCollection(node)) {
8419
+ path = Object.freeze(path.concat(node));
8420
+ for (let i = 0; i < node.items.length; ++i) {
8421
+ const ci = visit_(i, node.items[i], visitor, path);
8422
+ if (typeof ci === "number")
8423
+ i = ci - 1;
8424
+ else if (ci === BREAK)
8425
+ return BREAK;
8426
+ else if (ci === REMOVE) {
8427
+ node.items.splice(i, 1);
8428
+ i -= 1;
8429
+ }
8430
+ }
8431
+ } else if (identity.isPair(node)) {
8432
+ path = Object.freeze(path.concat(node));
8433
+ const ck = visit_("key", node.key, visitor, path);
8434
+ if (ck === BREAK)
8435
+ return BREAK;
8436
+ else if (ck === REMOVE)
8437
+ node.key = null;
8438
+ const cv = visit_("value", node.value, visitor, path);
8439
+ if (cv === BREAK)
8440
+ return BREAK;
8441
+ else if (cv === REMOVE)
8442
+ node.value = null;
8443
+ }
8444
+ }
8445
+ return ctrl;
8446
+ }
8447
+ async function visitAsync(node, visitor) {
8448
+ const visitor_ = initVisitor(visitor);
8449
+ if (identity.isDocument(node)) {
8450
+ const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node]));
8451
+ if (cd === REMOVE)
8452
+ node.contents = null;
8453
+ } else
8454
+ await visitAsync_(null, node, visitor_, Object.freeze([]));
7991
8455
  }
7992
8456
  visitAsync.BREAK = BREAK;
7993
8457
  visitAsync.SKIP = SKIP;
@@ -15215,450 +15679,819 @@ var require_dist = __commonJS({
15215
15679
  exports.YAMLSeq = YAMLSeq.YAMLSeq;
15216
15680
  exports.CST = cst;
15217
15681
  exports.Lexer = lexer.Lexer;
15218
- exports.LineCounter = lineCounter.LineCounter;
15219
- exports.Parser = parser.Parser;
15220
- exports.parse = publicApi.parse;
15221
- exports.parseAllDocuments = publicApi.parseAllDocuments;
15222
- exports.parseDocument = publicApi.parseDocument;
15223
- exports.stringify = publicApi.stringify;
15224
- exports.visit = visit.visit;
15225
- exports.visitAsync = visit.visitAsync;
15226
- }
15227
- });
15228
-
15229
- // packages/cli/src/bin/commands/deploy.ts
15230
- init_utils();
15231
-
15232
- // packages/tooling/src/index.ts
15233
- init_utils();
15234
-
15235
- // packages/tooling/src/cli/index.ts
15236
- init_utils();
15237
-
15238
- // packages/tooling/src/utils/cli-input.ts
15239
- init_utils();
15240
- init_dist2();
15241
- async function askForInput(message, defaultValue = "") {
15242
- const result = await he({
15243
- message,
15244
- placeholder: defaultValue || void 0,
15245
- defaultValue: defaultValue || void 0
15246
- });
15247
- if (pD(result)) {
15248
- xe("Operation cancelled.");
15249
- process.exit(0);
15250
- }
15251
- return result;
15252
- }
15253
- async function askForConfirmation(message, defaultValue = false) {
15254
- const result = await ye({
15255
- message,
15256
- initialValue: defaultValue
15257
- });
15258
- if (pD(result)) {
15259
- xe("Operation cancelled.");
15260
- process.exit(0);
15261
- }
15262
- return result;
15263
- }
15264
- async function askForSelection(message, choices, defaultValue = 0) {
15265
- const options = choices.map((choice) => ({
15266
- value: choice.value,
15267
- label: choice.title,
15268
- hint: choice.hint
15269
- }));
15270
- const result = await ve({
15271
- message,
15272
- options,
15273
- initialValue: choices[defaultValue]?.value
15274
- });
15275
- if (pD(result)) {
15276
- xe("Operation cancelled.");
15277
- process.exit(0);
15682
+ exports.LineCounter = lineCounter.LineCounter;
15683
+ exports.Parser = parser.Parser;
15684
+ exports.parse = publicApi.parse;
15685
+ exports.parseAllDocuments = publicApi.parseAllDocuments;
15686
+ exports.parseDocument = publicApi.parseDocument;
15687
+ exports.stringify = publicApi.stringify;
15688
+ exports.visit = visit.visit;
15689
+ exports.visitAsync = visit.visitAsync;
15278
15690
  }
15279
- return result;
15280
- }
15691
+ });
15281
15692
 
15282
- // packages/tooling/src/utils/spawn-utils.ts
15283
- init_utils();
15284
- init_cli_output();
15285
- init_errors();
15286
- init_pathResolver();
15287
- import { spawnSync, execSync } from "node:child_process";
15288
- function clearConflictingAuth(env) {
15289
- const cleaned = { ...env };
15290
- delete cleaned.FIREBASE_TOKEN;
15291
- delete cleaned.FIREBASE_AUTH_TOKEN;
15292
- return cleaned;
15293
- }
15294
- function createDeployEnv(serviceAccountPath) {
15295
- return clearConflictingAuth({
15296
- ...process.env,
15297
- GOOGLE_APPLICATION_CREDENTIALS: serviceAccountPath,
15298
- NODE_OPTIONS: "",
15299
- NODE_ENV: "production"
15693
+ // packages/tooling/src/apps/sync-secrets.ts
15694
+ var sync_secrets_exports = {};
15695
+ __export(sync_secrets_exports, {
15696
+ default: () => sync_secrets_default,
15697
+ main: () => main
15698
+ });
15699
+ import { spawnSync as spawnSync5 } from "node:child_process";
15700
+ function parseEnvFile(filePath) {
15701
+ if (!pathExists(filePath)) {
15702
+ throw new DoNotDevError(
15703
+ `Environment file not found: ${filePath}`,
15704
+ "file-not-found",
15705
+ { context: { filePath } }
15706
+ );
15707
+ }
15708
+ const contentRaw = readSync(filePath, { format: "text" });
15709
+ const content = typeof contentRaw === "string" ? contentRaw : null;
15710
+ if (!content) {
15711
+ throw new Error(`Failed to read secrets file: ${filePath}`);
15712
+ }
15713
+ const secrets = {};
15714
+ content.split("\n").forEach((line, index) => {
15715
+ const trimmedLine = line.trim();
15716
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
15717
+ return;
15718
+ }
15719
+ const equalIndex = trimmedLine.indexOf("=");
15720
+ if (equalIndex === -1) {
15721
+ log.warn(`Invalid line format at line ${index + 1}: ${trimmedLine}`);
15722
+ return;
15723
+ }
15724
+ const key = trimmedLine.substring(0, equalIndex).trim();
15725
+ const value = trimmedLine.substring(equalIndex + 1).trim();
15726
+ const cleanValue = value.replace(/^["']|["']$/g, "");
15727
+ if (key && cleanValue) {
15728
+ secrets[key] = cleanValue;
15729
+ }
15300
15730
  });
15731
+ return secrets;
15301
15732
  }
15302
- function executeFirebaseCommand(args, options) {
15303
- const deployEnv = createDeployEnv(options.serviceAccountPath);
15304
- const spawnOptions = {
15305
- cwd: options.cwd,
15306
- stdio: "inherit",
15307
- env: deployEnv
15308
- };
15309
- let result;
15310
- result = spawnSync("firebase", args, { ...spawnOptions, shell: true });
15311
- if (result.error) {
15312
- return {
15313
- success: false,
15314
- status: 1,
15315
- error: result.error
15316
- };
15733
+ function detectPlatform() {
15734
+ const currentDir = process.cwd();
15735
+ if (pathExists(joinPath(currentDir, "firebase.json"))) {
15736
+ return "firebase";
15317
15737
  }
15318
- const errorOutput = result.stderr?.toString() || result.stdout?.toString() || "";
15319
- return {
15320
- success: result.status === 0,
15321
- status: result.status ?? 1,
15322
- errorOutput
15323
- };
15738
+ if (pathExists(joinPath(currentDir, "vercel.json")) || pathExists(joinPath(currentDir, ".vercel"))) {
15739
+ return "vercel";
15740
+ }
15741
+ log.warn("No platform detected, defaulting to Firebase");
15742
+ return "firebase";
15324
15743
  }
15325
- function buildFirebaseDeployArgs(deployTargets, projectId, debug, force) {
15326
- const targets = Array.isArray(deployTargets) ? deployTargets : [deployTargets];
15327
- const targetString = targets.join(",");
15328
- const args = ["deploy", "--only", targetString, "--non-interactive"];
15329
- if (projectId) {
15330
- args.push("--project", projectId);
15744
+ function findFirebaseProjectDir(envFilePath) {
15745
+ const currentDir = process.cwd();
15746
+ const resolvedPath = joinPath(currentDir, envFilePath);
15747
+ const envFileDir = getDirname(resolvedPath);
15748
+ const appsMatch = envFileDir.match(/apps[\\/]([^\\/]+)[\\/]functions/);
15749
+ if (appsMatch && appsMatch[1]) {
15750
+ const appDir = joinPath(currentDir, "apps", appsMatch[1]);
15751
+ if (pathExists(joinPath(appDir, "firebase.json"))) {
15752
+ return appDir;
15753
+ }
15331
15754
  }
15332
- if (debug) {
15333
- args.push("--debug");
15755
+ let searchDir = envFileDir;
15756
+ const rootDir = getDirname(currentDir);
15757
+ while (searchDir !== rootDir && searchDir !== getDirname(searchDir)) {
15758
+ if (pathExists(joinPath(searchDir, "firebase.json"))) {
15759
+ return searchDir;
15760
+ }
15761
+ const parentDir = getDirname(searchDir);
15762
+ if (parentDir === searchDir) break;
15763
+ searchDir = parentDir;
15334
15764
  }
15335
- if (force) {
15336
- args.push("--force");
15765
+ return currentDir;
15766
+ }
15767
+ function isFirebaseReservedKey(key) {
15768
+ const reservedPrefixes = ["X_GOOGLE_", "FIREBASE_", "EXT_", "GCLOUD_"];
15769
+ const configOnlyKeys = [
15770
+ "FIREBASE_REGION",
15771
+ "FIREBASE_PROJECT_ID",
15772
+ "FIREBASE_AUTH_DOMAIN"
15773
+ ];
15774
+ if (configOnlyKeys.includes(key)) {
15775
+ return true;
15337
15776
  }
15338
- return args;
15777
+ return reservedPrefixes.some((prefix) => key.startsWith(prefix));
15339
15778
  }
15340
- function analyzeDeploymentError(errorOutput) {
15341
- const isRetryError = errorOutput.includes("retries exhausted") || errorOutput.includes("retry");
15342
- const isHashError = errorOutput.includes("hash") || errorOutput.includes("content hash");
15343
- const isAuthError = errorOutput.includes("401") || errorOutput.includes("authentication") || errorOutput.includes("credentials");
15344
- const tips = [];
15345
- if (isRetryError || isHashError) {
15346
- tips.push("Try deploying with --debug flag for more details");
15347
- tips.push("Disable VPN if active - VPNs can corrupt file uploads");
15348
- tips.push("Clear Firebase cache: Remove .firebase directory");
15349
- tips.push(
15350
- "Ensure no file watchers or antivirus are modifying files during upload"
15351
- );
15779
+ function detectFirebaseProjectId(cwd) {
15780
+ const searchDir = cwd || process.cwd();
15781
+ const firebasercPath = joinPath(searchDir, ".firebaserc");
15782
+ if (pathExists(firebasercPath)) {
15783
+ try {
15784
+ const firebasercRaw = readSync(firebasercPath, { format: "json" });
15785
+ const firebaserc = firebasercRaw && typeof firebasercRaw === "object" ? firebasercRaw : null;
15786
+ if (firebaserc && firebaserc.projects?.default) {
15787
+ return firebaserc.projects.default;
15788
+ }
15789
+ } catch {
15790
+ }
15352
15791
  }
15353
- if (isAuthError) {
15354
- tips.push("Verify service account has required IAM roles");
15355
- tips.push("Check service account key file is valid JSON");
15356
- tips.push("Ensure FIREBASE_TOKEN and FIREBASE_AUTH_TOKEN are cleared");
15792
+ let current = searchDir;
15793
+ const root = getDirname(current);
15794
+ while (current !== root) {
15795
+ const firebasercPath2 = joinPath(current, ".firebaserc");
15796
+ if (pathExists(firebasercPath2)) {
15797
+ try {
15798
+ const firebasercRaw = readSync(firebasercPath2, { format: "json" });
15799
+ const firebaserc = firebasercRaw && typeof firebasercRaw === "object" ? firebasercRaw : null;
15800
+ if (firebaserc && firebaserc.projects?.default) {
15801
+ return firebaserc.projects.default;
15802
+ }
15803
+ } catch {
15804
+ }
15805
+ }
15806
+ const parent = getDirname(current);
15807
+ if (parent === current) break;
15808
+ current = parent;
15357
15809
  }
15358
- return { isRetryError, isHashError, isAuthError, tips };
15810
+ return void 0;
15359
15811
  }
15360
- function logTroubleshootingTips(tips) {
15361
- if (tips.length > 0) {
15362
- log.warn("\nTroubleshooting Tips:");
15363
- tips.forEach((tip) => log.warn(` - ${tip}`));
15364
- log.warn("");
15812
+ function detectAppsWithFunctions() {
15813
+ const currentDir = process.cwd();
15814
+ const appsDir = joinPath(currentDir, "apps");
15815
+ if (!pathExists(appsDir)) {
15816
+ return [];
15817
+ }
15818
+ const apps = [];
15819
+ try {
15820
+ const appsList = readdirSync2(appsDir).filter((item) => {
15821
+ const itemPath = joinPath(appsDir, item);
15822
+ const stat = statSync2(itemPath);
15823
+ return stat?.isDirectory() === true;
15824
+ });
15825
+ for (const app of appsList) {
15826
+ const functionsEnvFile = joinPath(appsDir, app, "functions", ".env");
15827
+ if (pathExists(functionsEnvFile)) {
15828
+ apps.push({
15829
+ name: app,
15830
+ dir: joinPath(appsDir, app),
15831
+ envFile: functionsEnvFile
15832
+ });
15833
+ }
15834
+ }
15835
+ } catch {
15365
15836
  }
15837
+ return apps.sort((a, b3) => a.name.localeCompare(b3.name));
15366
15838
  }
15367
- function handleDeploymentFailure(result, command, serviceAccountPath) {
15368
- const analysis = analyzeDeploymentError(result.errorOutput || "");
15369
- logTroubleshootingTips(analysis.tips);
15370
- throw new DoNotDevError(
15371
- `Firebase deployment failed with exit code ${result.status}`,
15372
- "command-failed",
15373
- {
15374
- context: {
15375
- command,
15376
- serviceAccountPath,
15377
- troubleshootingTips: analysis.tips.length > 0 ? analysis.tips : void 0
15839
+ async function setFirebaseSecret(key, value, projectId, dryRun = false, cwd) {
15840
+ if (isFirebaseReservedKey(key)) {
15841
+ const configOnlyKeys = [
15842
+ "FIREBASE_REGION",
15843
+ "FIREBASE_PROJECT_ID",
15844
+ "FIREBASE_AUTH_DOMAIN"
15845
+ ];
15846
+ if (configOnlyKeys.includes(key)) {
15847
+ log.debug(
15848
+ `\u23ED\uFE0F Skipping ${key}: Configuration variable (stays in .env, not synced as secret)`
15849
+ );
15850
+ } else {
15851
+ log.warn(
15852
+ `\u23ED\uFE0F Skipping ${key}: Reserved Firebase prefix (X_GOOGLE_, FIREBASE_, EXT_, GCLOUD_)`
15853
+ );
15854
+ }
15855
+ return;
15856
+ }
15857
+ const finalProjectId = projectId || detectFirebaseProjectId(cwd);
15858
+ if (dryRun) {
15859
+ log.info(
15860
+ `[DRY RUN] Would set Firebase secret: ${key}${finalProjectId ? ` (project: ${finalProjectId})` : ""}`
15861
+ );
15862
+ return;
15863
+ }
15864
+ if (!finalProjectId) {
15865
+ throw new DoNotDevError(
15866
+ `Firebase project ID required for secret '${key}'. Use --project <project_id> or ensure .firebaserc exists with a default project.`,
15867
+ "missing-project-id",
15868
+ {
15869
+ context: {
15870
+ platform: "firebase",
15871
+ key,
15872
+ cwd: cwd || process.cwd(),
15873
+ suggestion: "Run: dn sync-secrets --project <project_id> or create .firebaserc with default project"
15874
+ }
15875
+ }
15876
+ );
15877
+ }
15878
+ try {
15879
+ log.info(`Setting Firebase secret: ${key} (project: ${finalProjectId})`);
15880
+ const tempDir = joinPath(process.cwd(), ".tmp");
15881
+ await ensureDir(tempDir);
15882
+ const tempFile = joinPath(tempDir, `secret-${key}-${Date.now()}.txt`);
15883
+ await write(tempFile, value);
15884
+ try {
15885
+ const args = ["functions:secrets:set", key, "--project", finalProjectId];
15886
+ const firebaseCmd = process.platform === "win32" ? "firebase.cmd" : "firebase";
15887
+ const deployEnv = {
15888
+ ...process.env,
15889
+ NODE_OPTIONS: ""
15890
+ // Clear to avoid conflicts
15891
+ };
15892
+ const result = spawnSync5(firebaseCmd, args, {
15893
+ input: value,
15894
+ encoding: "utf8",
15895
+ stdio: ["pipe", "pipe", "pipe"],
15896
+ env: deployEnv,
15897
+ cwd: cwd || process.cwd(),
15898
+ shell: false
15899
+ });
15900
+ if (result.error) {
15901
+ throw result.error;
15902
+ }
15903
+ if (result.status !== 0) {
15904
+ const stderr = result.stderr?.toString() || "";
15905
+ const stdout = result.stdout?.toString() || "";
15906
+ const errorOutput = stderr || stdout;
15907
+ const errorLower = errorOutput.toLowerCase();
15908
+ if (errorLower.includes("not logged in") || errorLower.includes("authentication required") || errorLower.includes("please login") || errorLower.includes("run firebase login") || errorLower.includes("unauthorized") || errorOutput.includes("401")) {
15909
+ throw new DoNotDevError(
15910
+ `Firebase CLI authentication required. Please run 'firebase login' to authenticate.`,
15911
+ "permission-denied",
15912
+ {
15913
+ context: {
15914
+ platform: "firebase",
15915
+ key,
15916
+ error: errorOutput,
15917
+ suggestion: "Run: firebase login"
15918
+ }
15919
+ }
15920
+ );
15921
+ }
15922
+ if (errorOutput.includes("403") || errorOutput.includes("Permission denied") || errorOutput.includes("secretmanager.secrets.get") || errorOutput.includes("secretmanager.secrets.create")) {
15923
+ const permissionError = new DoNotDevError(
15924
+ `Permission denied: Missing required Google Cloud permissions for secret '${key}'. Required permissions: secretmanager.secrets.get, secretmanager.secrets.create, secretmanager.versions.add. Ensure you're logged in with 'firebase login' and your account has the 'Secret Manager Admin' role.`,
15925
+ "permission-denied",
15926
+ {
15927
+ context: {
15928
+ platform: "firebase",
15929
+ key,
15930
+ error: errorOutput,
15931
+ suggestion: 'Run: firebase login && gcloud projects add-iam-policy-binding donotdevelopp --member="user:YOUR_EMAIL" --role="roles/secretmanager.admin"'
15932
+ }
15933
+ }
15934
+ );
15935
+ throw permissionError;
15936
+ }
15937
+ if (errorOutput.includes("No currently active project")) {
15938
+ throw new DoNotDevError(
15939
+ `Firebase CLI exited with code ${result.status}: No active project. Please specify the project using '--project <project_id>' or set an active project with 'firebase use --add'.`,
15940
+ "firebase-cli-error",
15941
+ {
15942
+ context: {
15943
+ platform: "firebase",
15944
+ key,
15945
+ error: errorOutput,
15946
+ suggestion: `Run 'dn sync-secrets --project ${finalProjectId}' or 'firebase use --add ${finalProjectId}'`
15947
+ }
15948
+ }
15949
+ );
15950
+ }
15951
+ throw new Error(
15952
+ `Firebase CLI exited with code ${result.status}
15953
+ ${errorOutput}`
15954
+ );
15955
+ }
15956
+ log.success(`Firebase secret ${key} set successfully`);
15957
+ } finally {
15958
+ if (pathExists(tempFile)) {
15959
+ await remove(tempFile);
15378
15960
  }
15379
15961
  }
15380
- );
15962
+ } catch (err) {
15963
+ log.error(`Failed to set Firebase secret ${key}:`, err);
15964
+ throw new DoNotDevError(
15965
+ `Failed to set Firebase secret ${key}`,
15966
+ "command-failed",
15967
+ {
15968
+ context: {
15969
+ platform: "firebase",
15970
+ key,
15971
+ error: err instanceof Error ? err.message : String(err)
15972
+ }
15973
+ }
15974
+ );
15975
+ }
15381
15976
  }
15382
- function safeRemove(path, options = {}) {
15383
- if (!pathExists(path)) {
15384
- return false;
15977
+ function setVercelSecret(key, value, projectId, dryRun = false) {
15978
+ if (dryRun) {
15979
+ log.info(`[DRY RUN] Would set Vercel environment variable: ${key}`);
15980
+ return;
15385
15981
  }
15386
15982
  try {
15387
- removeSync(path);
15388
- if (options.logSuccess) {
15389
- log.debug(options.successMessage ?? `Removed: ${path}`);
15983
+ log.info(`Setting Vercel environment variable: ${key}`);
15984
+ const args = ["env", "add", key];
15985
+ if (projectId) {
15986
+ args.push("--project", projectId);
15987
+ }
15988
+ const result = spawnSync5("vercel", args, {
15989
+ input: value,
15990
+ encoding: "utf8",
15991
+ stdio: ["pipe", "inherit", "inherit"]
15992
+ });
15993
+ if (result.error) {
15994
+ throw result.error;
15390
15995
  }
15391
- return true;
15392
- } catch (error2) {
15393
- const message = options.failureMessage ?? `Could not remove ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`;
15394
- if (options.silent !== false) {
15395
- log.warn(message);
15996
+ if (result.status !== 0) {
15997
+ throw new Error(`Vercel CLI exited with code ${result.status}`);
15396
15998
  }
15397
- return false;
15999
+ log.success(`Vercel environment variable ${key} set successfully`);
16000
+ } catch (err) {
16001
+ log.error(`Failed to set Vercel environment variable ${key}:`, err);
16002
+ throw new DoNotDevError(
16003
+ `Failed to set Vercel environment variable ${key}`,
16004
+ "command-failed",
16005
+ {
16006
+ context: {
16007
+ platform: "vercel",
16008
+ key,
16009
+ error: err instanceof Error ? err.message : String(err)
16010
+ }
16011
+ }
16012
+ );
15398
16013
  }
15399
16014
  }
15400
-
15401
- // packages/tooling/src/apps/deploy.ts
15402
- init_utils();
15403
- import { execSync as execSync3 } from "node:child_process";
15404
-
15405
- // packages/tooling/src/apps/deploy-frontend.ts
15406
- init_utils();
15407
-
15408
- // packages/tooling/src/utils/cli-tools.ts
15409
- init_utils();
15410
- init_dist2();
15411
- import { spawnSync as spawnSync2 } from "node:child_process";
15412
- var cliCache = /* @__PURE__ */ new Map();
15413
- function checkCLI(command, args = ["--version"], useCache = true) {
15414
- const cacheKey = `${command}:${args.join(",")}`;
15415
- if (useCache && cliCache.has(cacheKey)) {
15416
- return cliCache.get(cacheKey);
16015
+ function detectGitHubRepo() {
16016
+ try {
16017
+ const result = spawnSync5("git", ["remote", "get-url", "origin"], {
16018
+ encoding: "utf8",
16019
+ stdio: ["pipe", "pipe", "pipe"]
16020
+ });
16021
+ if (result.status !== 0 || !result.stdout) return void 0;
16022
+ const url = result.stdout.trim();
16023
+ const sshMatch = url.match(/github\.com[:/]([^/]+\/[^/.]+)/);
16024
+ if (sshMatch) return sshMatch[1];
16025
+ const httpsMatch = url.match(/github\.com\/([^/]+\/[^/.]+)/);
16026
+ if (httpsMatch) return httpsMatch[1];
16027
+ return void 0;
16028
+ } catch {
16029
+ return void 0;
16030
+ }
16031
+ }
16032
+ function setGitHubSecret(key, value, repo, dryRun = false) {
16033
+ if (dryRun) {
16034
+ log.info(
16035
+ `[DRY RUN] Would set GitHub secret: ${key}${repo ? ` (repo: ${repo})` : ""}`
16036
+ );
16037
+ return;
15417
16038
  }
15418
16039
  try {
15419
- let result;
15420
- if (process.platform === "win32") {
15421
- const username = process.env.USERNAME || process.env.USER || "";
15422
- const programFiles = process.env.ProgramFiles || "C:\\Program Files";
15423
- const commonPaths = [
15424
- `${programFiles}\\nodejs\\${command}.ps1`,
15425
- `${programFiles}\\nodejs\\${command}.cmd`,
15426
- `${programFiles}\\nodejs\\${command}`,
15427
- `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.ps1`,
15428
- `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.cmd`,
15429
- `C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}`,
15430
- process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.ps1` : null,
15431
- process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.cmd` : null,
15432
- process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}` : null
15433
- ].filter(Boolean);
15434
- for (const fullPath of commonPaths) {
15435
- try {
15436
- const testResult = spawnSync2(
15437
- "powershell.exe",
15438
- [
15439
- "-NoProfile",
15440
- "-ExecutionPolicy",
15441
- "Bypass",
15442
- "-Command",
15443
- `& '${fullPath}' ${args.join(" ")}`
15444
- ],
15445
- {
15446
- stdio: "pipe",
15447
- shell: false
15448
- }
16040
+ log.info(`Setting GitHub secret: ${key}${repo ? ` (repo: ${repo})` : ""}`);
16041
+ const args = ["secret", "set", key];
16042
+ if (repo) {
16043
+ args.push("--repo", repo);
16044
+ }
16045
+ const result = spawnSync5("gh", args, {
16046
+ input: value,
16047
+ encoding: "utf8",
16048
+ stdio: ["pipe", "pipe", "pipe"]
16049
+ });
16050
+ if (result.error) {
16051
+ throw result.error;
16052
+ }
16053
+ if (result.status !== 0) {
16054
+ const stderr = result.stderr?.toString() || "";
16055
+ const stdout = result.stdout?.toString() || "";
16056
+ const errorOutput = stderr || stdout;
16057
+ if (errorOutput.includes("not logged in") || errorOutput.includes("authentication")) {
16058
+ throw new DoNotDevError(
16059
+ `GitHub CLI authentication required. Run 'gh auth login' to authenticate.`,
16060
+ "permission-denied",
16061
+ { context: { key, error: errorOutput } }
16062
+ );
16063
+ }
16064
+ throw new Error(
16065
+ `gh CLI exited with code ${result.status}
16066
+ ${errorOutput}`
16067
+ );
16068
+ }
16069
+ log.success(`GitHub secret ${key} set successfully`);
16070
+ } catch (err) {
16071
+ if (err instanceof DoNotDevError) throw err;
16072
+ log.error(`Failed to set GitHub secret ${key}:`, err);
16073
+ throw new DoNotDevError(
16074
+ `Failed to set GitHub secret ${key}`,
16075
+ "command-failed",
16076
+ {
16077
+ context: {
16078
+ target: "github",
16079
+ key,
16080
+ error: err instanceof Error ? err.message : String(err)
16081
+ }
16082
+ }
16083
+ );
16084
+ }
16085
+ }
16086
+ function uploadServiceAccountToGitHub(appDir, repo, dryRun = false, staging = false) {
16087
+ const fileName = staging ? "service-account-key.staging.json" : "service-account-key.json";
16088
+ const secretName = staging ? "FIREBASE_SERVICE_ACCOUNT_STAGING" : "FIREBASE_SERVICE_ACCOUNT";
16089
+ const filePath = joinPath(appDir, fileName);
16090
+ if (!pathExists(filePath)) {
16091
+ log.debug(`No ${fileName} found in ${appDir}, skipping GitHub upload`);
16092
+ return;
16093
+ }
16094
+ const contentRaw = readSync(filePath, { format: "text" });
16095
+ const content = typeof contentRaw === "string" ? contentRaw : null;
16096
+ if (!content) return;
16097
+ const encoded = Buffer2.from(content).toString("base64");
16098
+ setGitHubSecret(secretName, encoded, repo, dryRun);
16099
+ }
16100
+ async function main(options = {}) {
16101
+ const config = {
16102
+ envFile: options.envFile || ".env",
16103
+ platform: options.platform,
16104
+ projectId: options.projectId,
16105
+ vercelProjectId: options.vercelProjectId,
16106
+ target: options.target || "runtime",
16107
+ repo: options.repo,
16108
+ dryRun: options.dryRun ?? false,
16109
+ verbose: options.verbose ?? false
16110
+ };
16111
+ if (process.argv.includes("--help")) {
16112
+ log.info(`
16113
+ Secrets Sync Tool
16114
+
16115
+ Usage: bun run sync-secrets [options]
16116
+
16117
+ Options:
16118
+ --env-file <path> Path to .env file (default: .env)
16119
+ --target <target> Sync target: 'runtime' (default) or 'github'
16120
+ --platform <platform> Platform to sync to: 'firebase' or 'vercel' (runtime target only, auto-detected)
16121
+ --project <id> Firebase project ID (for Firebase platform)
16122
+ --vercel-project <id> Vercel project ID (for Vercel platform)
16123
+ --repo <owner/repo> GitHub repository (github target only, auto-detected from git remote)
16124
+ --dry-run, --dry Show what would be done without actually setting secrets
16125
+ --verbose Show detailed output
16126
+ --help Show this help message
16127
+
16128
+ Examples:
16129
+ dndev sync-secrets # sync to Firebase/Vercel runtime
16130
+ dndev sync-secrets --target github # sync to GitHub Secrets
16131
+ dndev sync-secrets --target github --repo owner/repo # sync to specific repo
16132
+ dndev sync-secrets --platform=firebase
16133
+ dndev sync-secrets --dry-run
16134
+ dndev sync-secrets --env-file .env.production --project my-project
16135
+ `);
16136
+ return 0;
16137
+ }
16138
+ try {
16139
+ const currentDir = process.cwd();
16140
+ let firebaseProjectDir;
16141
+ if (!options.envFile || config.envFile === ".env") {
16142
+ const appsWithFunctions = detectAppsWithFunctions();
16143
+ if (appsWithFunctions.length > 0) {
16144
+ let selectedApp;
16145
+ if (appsWithFunctions.length === 1) {
16146
+ selectedApp = appsWithFunctions[0];
16147
+ Me(`Using app: ${selectedApp.name}`, "Auto-detected");
16148
+ } else {
16149
+ const appName = await askForSelection(
16150
+ "Which app would you like to sync secrets for?",
16151
+ appsWithFunctions.map((app) => ({
16152
+ title: app.name,
16153
+ value: app.name,
16154
+ hint: `functions/.env in ${app.name}`
16155
+ }))
15449
16156
  );
15450
- if (testResult.status === 0) {
15451
- cliCache.set(cacheKey, true);
15452
- return true;
16157
+ selectedApp = appsWithFunctions.find((a) => a.name === appName);
16158
+ }
16159
+ if (selectedApp) {
16160
+ const envFileAbsolute = normalizePath(selectedApp.envFile);
16161
+ const envFileRelative = getRelativePathBetween(
16162
+ currentDir,
16163
+ envFileAbsolute
16164
+ );
16165
+ config.envFile = envFileRelative;
16166
+ firebaseProjectDir = selectedApp.dir;
16167
+ if (!config.projectId) {
16168
+ config.projectId = detectFirebaseProjectId(selectedApp.dir);
15453
16169
  }
15454
- } catch {
15455
- continue;
15456
16170
  }
15457
16171
  }
15458
- result = spawnSync2(
15459
- "powershell.exe",
15460
- [
15461
- "-NoProfile",
15462
- "-ExecutionPolicy",
15463
- "Bypass",
15464
- "-Command",
15465
- `${command} ${args.join(" ")}`
15466
- ],
15467
- {
15468
- stdio: "pipe",
15469
- shell: false
15470
- }
16172
+ }
16173
+ const envFilePath = joinPath(currentDir, config.envFile);
16174
+ const syncTarget = config.target || "runtime";
16175
+ if (syncTarget === "github") {
16176
+ const repo = config.repo || detectGitHubRepo();
16177
+ if (!repo) {
16178
+ log.error(
16179
+ "Could not detect GitHub repository. Use --repo owner/repo or ensure a GitHub remote is configured."
16180
+ );
16181
+ return 1;
16182
+ }
16183
+ log.info(`Syncing secrets to GitHub repository: ${repo}`);
16184
+ const secrets2 = parseEnvFile(envFilePath);
16185
+ const secretKeys2 = Object.keys(secrets2);
16186
+ if (secretKeys2.length === 0) {
16187
+ log.info("No secrets found in .env file");
16188
+ } else {
16189
+ log.info(`Found ${secretKeys2.length} secrets to sync to GitHub:`);
16190
+ secretKeys2.forEach((key) => log.info(` ${key}`));
16191
+ for (const [key, value] of Object.entries(secrets2)) {
16192
+ setGitHubSecret(key, value, repo, config.dryRun);
16193
+ }
16194
+ }
16195
+ if (firebaseProjectDir) {
16196
+ uploadServiceAccountToGitHub(
16197
+ firebaseProjectDir,
16198
+ repo,
16199
+ config.dryRun,
16200
+ false
16201
+ );
16202
+ uploadServiceAccountToGitHub(
16203
+ firebaseProjectDir,
16204
+ repo,
16205
+ config.dryRun,
16206
+ true
16207
+ );
16208
+ } else {
16209
+ uploadServiceAccountToGitHub(currentDir, repo, config.dryRun, false);
16210
+ uploadServiceAccountToGitHub(currentDir, repo, config.dryRun, true);
16211
+ }
16212
+ if (config.dryRun) {
16213
+ log.success("\nDry run completed. No GitHub secrets were set.");
16214
+ } else {
16215
+ log.success("\nAll secrets synced to GitHub successfully!");
16216
+ }
16217
+ return 0;
16218
+ }
16219
+ const platform = config.platform || detectPlatform();
16220
+ log.info(`Reading secrets from: ${envFilePath}`);
16221
+ if (config.verbose) {
16222
+ log.debug(`Working directory: ${currentDir}`);
16223
+ log.debug(`Environment file: ${envFilePath}`);
16224
+ log.debug(`Platform: ${platform}`);
16225
+ log.debug(`Dry run mode: ${config.dryRun}`);
16226
+ }
16227
+ const secrets = parseEnvFile(envFilePath);
16228
+ const secretKeys = Object.keys(secrets);
16229
+ if (secretKeys.length === 0) {
16230
+ log.info("No secrets found in .env file");
16231
+ return 0;
16232
+ }
16233
+ log.info(`Found ${secretKeys.length} secrets to sync to ${platform}:`);
16234
+ secretKeys.forEach((key) => {
16235
+ if (config.verbose) {
16236
+ const value = secrets[key];
16237
+ log.debug(
16238
+ ` ${key}: ${value ? value.substring(0, 10) + "..." : "undefined"}`
16239
+ );
16240
+ } else {
16241
+ log.info(` ${key}`);
16242
+ }
16243
+ });
16244
+ const finalFirebaseProjectDir = platform === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
16245
+ if (config.verbose && finalFirebaseProjectDir) {
16246
+ log.debug(`Firebase project directory: ${finalFirebaseProjectDir}`);
16247
+ }
16248
+ for (const [key, value] of Object.entries(secrets)) {
16249
+ if (platform === "firebase") {
16250
+ await setFirebaseSecret(
16251
+ key,
16252
+ value,
16253
+ config.projectId,
16254
+ config.dryRun,
16255
+ finalFirebaseProjectDir
16256
+ );
16257
+ } else if (platform === "vercel") {
16258
+ setVercelSecret(key, value, config.vercelProjectId, config.dryRun);
16259
+ }
16260
+ }
16261
+ if (config.dryRun) {
16262
+ log.success(
16263
+ `
16264
+ Dry run completed. No secrets were actually set to ${platform}.`
15471
16265
  );
15472
16266
  } else {
15473
- result = spawnSync2(command, args, {
15474
- stdio: "pipe",
15475
- shell: true
15476
- });
16267
+ log.success(`
16268
+ All secrets synced successfully to ${platform}!`);
15477
16269
  }
15478
- const isAvailable = result.status === 0;
15479
- cliCache.set(cacheKey, isAvailable);
15480
- return isAvailable;
16270
+ return 0;
15481
16271
  } catch (err) {
15482
- cliCache.set(cacheKey, false);
15483
- return false;
16272
+ log.error(
16273
+ "Error syncing secrets:",
16274
+ DoNotDevError.from(err, "Sync-secrets failed", "cli-execution-error")
16275
+ );
16276
+ return 1;
15484
16277
  }
15485
16278
  }
15486
- function checkBun() {
15487
- return checkCLI("bun");
15488
- }
15489
- function checkNpm() {
15490
- return checkCLI("npm");
15491
- }
15492
- function checkYarn() {
15493
- return checkCLI("yarn");
15494
- }
15495
- function checkFirebase() {
15496
- return checkCLI("firebase");
15497
- }
15498
- function checkSentry() {
15499
- return checkCLI("sentry-cli");
15500
- }
15501
- function checkGitHub() {
15502
- return checkCLI("gh");
16279
+ var sync_secrets_default;
16280
+ var init_sync_secrets = __esm({
16281
+ "packages/tooling/src/apps/sync-secrets.ts"() {
16282
+ "use strict";
16283
+ init_utils();
16284
+ init_cli_input();
16285
+ init_cli_output();
16286
+ init_errors();
16287
+ init_pathResolver();
16288
+ sync_secrets_default = main;
16289
+ }
16290
+ });
16291
+
16292
+ // packages/tooling/src/apps/deploy-supabase-functions.ts
16293
+ var deploy_supabase_functions_exports = {};
16294
+ __export(deploy_supabase_functions_exports, {
16295
+ deploySupabaseFunctions: () => deploySupabaseFunctions
16296
+ });
16297
+ import { execSync as execSync4 } from "node:child_process";
16298
+ async function deploySupabaseFunctions(appDir, config) {
16299
+ const supabaseDir = joinPath(appDir, "supabase");
16300
+ const functionsDir = joinPath(supabaseDir, "functions");
16301
+ if (!pathExists(functionsDir)) {
16302
+ log.warn("No supabase/functions/ directory found. Skipping Supabase functions deployment.");
16303
+ return;
16304
+ }
16305
+ requireCLI(
16306
+ CLI_TOOLS.SUPABASE,
16307
+ "Supabase CLI is required to deploy Edge Functions.\nInstall: https://supabase.com/docs/guides/cli"
16308
+ );
16309
+ const functionDirs = readdirSync2(functionsDir).filter((item) => {
16310
+ const itemPath = joinPath(functionsDir, item);
16311
+ const stat = statSync2(itemPath);
16312
+ return stat?.isDirectory() === true;
16313
+ }).filter((dir) => {
16314
+ const indexPath = joinPath(functionsDir, dir, "index.ts");
16315
+ return pathExists(indexPath);
16316
+ });
16317
+ if (functionDirs.length === 0) {
16318
+ log.warn("No Edge Functions found in supabase/functions/. Skipping deployment.");
16319
+ return;
16320
+ }
16321
+ log.info(`Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`);
16322
+ const s = Y2();
16323
+ for (const functionName of functionDirs) {
16324
+ s.start(`Deploying Edge Function: ${functionName}...`);
16325
+ try {
16326
+ execSync4(`supabase functions deploy ${functionName}`, {
16327
+ cwd: supabaseDir,
16328
+ stdio: config.verbose ? "inherit" : "pipe",
16329
+ env: {
16330
+ ...process.env,
16331
+ DNDEV_APP_ROOT: appDir
16332
+ }
16333
+ });
16334
+ s.stop(`Deployed: ${functionName}`);
16335
+ } catch (error2) {
16336
+ s.stop(`Failed to deploy: ${functionName}`);
16337
+ throw new DoNotDevError(
16338
+ `Failed to deploy Supabase Edge Function: ${functionName}`,
16339
+ "deployment-failed",
16340
+ { context: { functionName, error: error2 instanceof Error ? error2.message : String(error2) } }
16341
+ );
16342
+ }
16343
+ }
16344
+ log.success(`Successfully deployed ${functionDirs.length} Edge Function(s)`);
15503
16345
  }
15504
- function checkGit() {
15505
- return checkCLI("git");
16346
+ var init_deploy_supabase_functions = __esm({
16347
+ "packages/tooling/src/apps/deploy-supabase-functions.ts"() {
16348
+ "use strict";
16349
+ init_utils();
16350
+ init_pathResolver();
16351
+ init_cli_output();
16352
+ init_cli_tools();
16353
+ init_errors();
16354
+ }
16355
+ });
16356
+
16357
+ // packages/cli/src/bin/commands/deploy.ts
16358
+ init_utils();
16359
+
16360
+ // packages/tooling/src/index.ts
16361
+ init_utils();
16362
+
16363
+ // packages/tooling/src/cli/index.ts
16364
+ init_utils();
16365
+
16366
+ // packages/tooling/src/utils/spawn-utils.ts
16367
+ init_utils();
16368
+ init_cli_output();
16369
+ init_errors();
16370
+ init_pathResolver();
16371
+ import { spawnSync, execSync } from "node:child_process";
16372
+ function clearConflictingAuth(env) {
16373
+ const cleaned = { ...env };
16374
+ delete cleaned.FIREBASE_TOKEN;
16375
+ delete cleaned.FIREBASE_AUTH_TOKEN;
16376
+ return cleaned;
15506
16377
  }
15507
- function checkTsx() {
15508
- return checkCLI("tsx");
16378
+ function createDeployEnv(serviceAccountPath) {
16379
+ return clearConflictingAuth({
16380
+ ...process.env,
16381
+ GOOGLE_APPLICATION_CREDENTIALS: serviceAccountPath,
16382
+ NODE_OPTIONS: "",
16383
+ NODE_ENV: "production"
16384
+ });
15509
16385
  }
15510
- function getCLIInstallInstructions(tool) {
15511
- const platform = process.platform;
15512
- const instructions = {
15513
- bun: {
15514
- win32: [
15515
- 'powershell -c "irm bun.sh/install.ps1 | iex"',
15516
- "Or download from: https://bun.sh"
15517
- ],
15518
- darwin: [
15519
- "curl -fsSL https://bun.sh/install | bash",
15520
- "Or: brew install bun"
15521
- ],
15522
- linux: [
15523
- "curl -fsSL https://bun.sh/install | bash",
15524
- "Or check your package manager (apt, dnf, pacman, etc.)"
15525
- ]
15526
- },
15527
- npm: {
15528
- win32: [
15529
- "Install Node.js from: https://nodejs.org",
15530
- "npm comes bundled with Node.js"
15531
- ],
15532
- darwin: [
15533
- "Install Node.js from: https://nodejs.org",
15534
- "Or: brew install node",
15535
- "npm comes bundled with Node.js"
15536
- ],
15537
- linux: [
15538
- "Install Node.js from: https://nodejs.org",
15539
- "Or use your package manager: dnf install nodejs, apt install nodejs, etc.",
15540
- "npm comes bundled with Node.js"
15541
- ]
15542
- },
15543
- yarn: {
15544
- win32: ["npm install -g yarn", "Or: choco install yarn"],
15545
- darwin: ["npm install -g yarn", "Or: brew install yarn"],
15546
- linux: ["npm install -g yarn", "Or check your package manager"]
15547
- },
15548
- firebase: {
15549
- win32: [
15550
- "npm install -g firebase-tools",
15551
- "Or download standalone binary: https://firebase.google.com/docs/cli#windows-standalone-binary"
15552
- ],
15553
- darwin: [
15554
- "npm install -g firebase-tools",
15555
- "Or: brew install firebase-cli"
15556
- ],
15557
- linux: [
15558
- "npm install -g firebase-tools",
15559
- "Or check your package manager (dnf, apt, pacman, etc.)"
15560
- ]
15561
- },
15562
- "sentry-cli": {
15563
- win32: [
15564
- "npm install -g @sentry/cli",
15565
- "Or download from: https://github.com/getsentry/sentry-cli/releases"
15566
- ],
15567
- darwin: [
15568
- "npm install -g @sentry/cli",
15569
- "Or: brew install getsentry/tools/sentry-cli"
15570
- ],
15571
- linux: [
15572
- "npm install -g @sentry/cli",
15573
- "Or: curl -sL https://sentry.io/get-cli/ | bash"
15574
- ]
15575
- },
15576
- gh: {
15577
- win32: [
15578
- "Download from: https://cli.github.com",
15579
- "Or: winget install --id GitHub.cli",
15580
- "Or: choco install gh"
15581
- ],
15582
- darwin: ["brew install gh", "Or download from: https://cli.github.com"],
15583
- linux: [
15584
- "See: https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
15585
- "dnf install gh",
15586
- "apt install gh",
15587
- "Or download from: https://cli.github.com"
15588
- ]
15589
- },
15590
- git: {
15591
- win32: [
15592
- "Download from: https://git-scm.com/download/win",
15593
- "Or: winget install --id Git.Git"
15594
- ],
15595
- darwin: ["brew install git", "Or: xcode-select --install"],
15596
- linux: [
15597
- "Use your package manager: dnf install git, apt install git, etc."
15598
- ]
15599
- },
15600
- tsx: {
15601
- win32: ["npm install -g tsx"],
15602
- darwin: ["npm install -g tsx"],
15603
- linux: ["npm install -g tsx"]
15604
- }
16386
+ function executeFirebaseCommand(args, options) {
16387
+ const deployEnv = createDeployEnv(options.serviceAccountPath);
16388
+ const spawnOptions = {
16389
+ cwd: options.cwd,
16390
+ stdio: "inherit",
16391
+ env: deployEnv
16392
+ };
16393
+ let result;
16394
+ result = spawnSync("firebase", args, { ...spawnOptions, shell: true });
16395
+ if (result.error) {
16396
+ return {
16397
+ success: false,
16398
+ status: 1,
16399
+ error: result.error
16400
+ };
16401
+ }
16402
+ const errorOutput = result.stderr?.toString() || result.stdout?.toString() || "";
16403
+ return {
16404
+ success: result.status === 0,
16405
+ status: result.status ?? 1,
16406
+ errorOutput
15605
16407
  };
15606
- const platformInstructions = instructions[tool]?.[platform] || instructions[tool]?.linux || [];
15607
- return platformInstructions.join("\n \u2022 ");
15608
16408
  }
15609
- function formatCLIMissingError(tool, additionalContext) {
15610
- const toolName = tool === "sentry-cli" ? "Sentry CLI" : tool.toUpperCase();
15611
- M2.error(`${toolName} is not installed or not in PATH.`);
15612
- const instructions = getCLIInstallInstructions(tool);
15613
- if (instructions) {
15614
- M2.warn("Install it with:");
15615
- M2.message(` \u2022 ${instructions}`);
16409
+ function buildFirebaseDeployArgs(deployTargets, projectId, debug, force) {
16410
+ const targets = Array.isArray(deployTargets) ? deployTargets : [deployTargets];
16411
+ const targetString = targets.join(",");
16412
+ const args = ["deploy", "--only", targetString, "--non-interactive"];
16413
+ if (projectId) {
16414
+ args.push("--project", projectId);
15616
16415
  }
15617
- if (additionalContext) {
15618
- M2.info(`
15619
- ${additionalContext}`);
16416
+ if (debug) {
16417
+ args.push("--debug");
15620
16418
  }
15621
- const platform = process.platform;
15622
- M2.info("\nIf already installed, ensure it's in your PATH:");
15623
- if (platform === "win32") {
15624
- M2.message(" \u2022 Check with: where " + tool);
15625
- M2.message(
15626
- " \u2022 Common location: C:\\Users\\<username>\\AppData\\Roaming\\npm\\"
16419
+ if (force) {
16420
+ args.push("--force");
16421
+ }
16422
+ return args;
16423
+ }
16424
+ function analyzeDeploymentError(errorOutput) {
16425
+ const isRetryError = errorOutput.includes("retries exhausted") || errorOutput.includes("retry");
16426
+ const isHashError = errorOutput.includes("hash") || errorOutput.includes("content hash");
16427
+ const isAuthError = errorOutput.includes("401") || errorOutput.includes("authentication") || errorOutput.includes("credentials");
16428
+ const tips = [];
16429
+ if (isRetryError || isHashError) {
16430
+ tips.push("Try deploying with --debug flag for more details");
16431
+ tips.push("Disable VPN if active - VPNs can corrupt file uploads");
16432
+ tips.push("Clear Firebase cache: Remove .firebase directory");
16433
+ tips.push(
16434
+ "Ensure no file watchers or antivirus are modifying files during upload"
15627
16435
  );
15628
- } else {
15629
- M2.message(" \u2022 Check with: which " + tool);
15630
- M2.message(" \u2022 Common locations:");
15631
- M2.message(" - ~/.nvm/versions/node/*/bin/" + tool);
15632
- M2.message(" - ~/.bun/bin/" + tool);
15633
- M2.message(" - /usr/local/bin/" + tool);
15634
- M2.message(" - /usr/bin/" + tool);
15635
16436
  }
16437
+ if (isAuthError) {
16438
+ tips.push("Verify service account has required IAM roles");
16439
+ tips.push("Check service account key file is valid JSON");
16440
+ tips.push("Ensure FIREBASE_TOKEN and FIREBASE_AUTH_TOKEN are cleared");
16441
+ }
16442
+ return { isRetryError, isHashError, isAuthError, tips };
15636
16443
  }
15637
- function requireCLI(tool, additionalContext) {
15638
- const checkers = {
15639
- bun: checkBun,
15640
- npm: checkNpm,
15641
- yarn: checkYarn,
15642
- firebase: checkFirebase,
15643
- "sentry-cli": checkSentry,
15644
- gh: checkGitHub,
15645
- git: checkGit,
15646
- tsx: checkTsx
15647
- };
15648
- cliCache.delete(`${tool}:--version`);
15649
- const isAvailable = checkers[tool]?.();
15650
- if (!isAvailable) {
15651
- formatCLIMissingError(tool, additionalContext);
15652
- process.exit(1);
16444
+ function logTroubleshootingTips(tips) {
16445
+ if (tips.length > 0) {
16446
+ log.warn("\nTroubleshooting Tips:");
16447
+ tips.forEach((tip) => log.warn(` - ${tip}`));
16448
+ log.warn("");
16449
+ }
16450
+ }
16451
+ function handleDeploymentFailure(result, command, serviceAccountPath) {
16452
+ const analysis = analyzeDeploymentError(result.errorOutput || "");
16453
+ logTroubleshootingTips(analysis.tips);
16454
+ throw new DoNotDevError(
16455
+ `Firebase deployment failed with exit code ${result.status}`,
16456
+ "command-failed",
16457
+ {
16458
+ context: {
16459
+ command,
16460
+ serviceAccountPath,
16461
+ troubleshootingTips: analysis.tips.length > 0 ? analysis.tips : void 0
16462
+ }
16463
+ }
16464
+ );
16465
+ }
16466
+ function safeRemove(path, options = {}) {
16467
+ if (!pathExists(path)) {
16468
+ return false;
16469
+ }
16470
+ try {
16471
+ removeSync(path);
16472
+ if (options.logSuccess) {
16473
+ log.debug(options.successMessage ?? `Removed: ${path}`);
16474
+ }
16475
+ return true;
16476
+ } catch (error2) {
16477
+ const message = options.failureMessage ?? `Could not remove ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`;
16478
+ if (options.silent !== false) {
16479
+ log.warn(message);
16480
+ }
16481
+ return false;
15653
16482
  }
15654
- return true;
15655
16483
  }
15656
16484
 
16485
+ // packages/tooling/src/apps/deploy.ts
16486
+ init_utils();
16487
+ import { execSync as execSync5 } from "node:child_process";
16488
+
15657
16489
  // packages/tooling/src/apps/deploy-frontend.ts
16490
+ init_utils();
15658
16491
  async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
15659
16492
  const s = Y2();
15660
16493
  s.start("Deploying frontend to Firebase Hosting...");
15661
- const args = buildFirebaseDeployArgs("hosting", projectId, config.debug);
16494
+ const args = buildFirebaseDeployArgs("hosting", projectId, config.debug, config.force ?? true);
15662
16495
  const result = executeFirebaseCommand(args, {
15663
16496
  cwd: appDir,
15664
16497
  serviceAccountPath,
@@ -15680,11 +16513,31 @@ async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
15680
16513
  s.stop("Frontend deployed successfully");
15681
16514
  }
15682
16515
 
16516
+ // packages/tooling/src/apps/deploy-vercel-frontend.ts
16517
+ init_utils();
16518
+ import { execSync as execSync2 } from "node:child_process";
16519
+ async function deployVercelFrontend(appDir, _config) {
16520
+ const s = Y2();
16521
+ s.start("Deploying frontend to Vercel...");
16522
+ try {
16523
+ execSync2("npx vercel --prod --yes", {
16524
+ cwd: appDir,
16525
+ stdio: "inherit"
16526
+ });
16527
+ s.stop("Frontend deployed to Vercel");
16528
+ } catch (err) {
16529
+ s.stop("Vercel deployment failed");
16530
+ throw err;
16531
+ }
16532
+ }
16533
+
15683
16534
  // packages/tooling/src/apps/deploy-functions.ts
15684
16535
  init_utils();
15685
16536
  var import_yaml = __toESM(require_dist(), 1);
15686
- import { execSync as execSync2, spawnSync as spawnSync3 } from "node:child_process";
16537
+ import { execSync as execSync3, spawnSync as spawnSync3 } from "node:child_process";
15687
16538
  init_pathResolver();
16539
+ init_cli_tools();
16540
+ init_typed_file_operations();
15688
16541
  function backupFiles(functionsDir) {
15689
16542
  const backupDir = joinPath(functionsDir, ".deploy-backup");
15690
16543
  const packageJsonPath = joinPath(functionsDir, "package.json");
@@ -15694,14 +16547,16 @@ function backupFiles(functionsDir) {
15694
16547
  const backupPackageJson = joinPath(backupDir, "package.json");
15695
16548
  const backupPackageLockJson = joinPath(backupDir, "package-lock.json");
15696
16549
  if (pathExists(packageJsonPath)) {
15697
- const content = readSync(packageJsonPath, { format: "text" });
16550
+ const contentRaw = readSync(packageJsonPath, { format: "text" });
16551
+ const content = typeof contentRaw === "string" ? contentRaw : null;
15698
16552
  if (!content) {
15699
16553
  throw new Error(`Failed to read package.json: ${packageJsonPath}`);
15700
16554
  }
15701
16555
  writeSync(backupPackageJson, content, { overwrite: true });
15702
16556
  }
15703
16557
  if (pathExists(packageLockJsonPath)) {
15704
- const content = readSync(packageLockJsonPath, { format: "text" });
16558
+ const contentRaw = readSync(packageLockJsonPath, { format: "text" });
16559
+ const content = typeof contentRaw === "string" ? contentRaw : null;
15705
16560
  if (!content) {
15706
16561
  throw new Error(
15707
16562
  `Failed to read package-lock.json: ${packageLockJsonPath}`
@@ -15728,8 +16583,8 @@ function generateCleanPackageJson(functionsDir) {
15728
16583
  "file-not-found"
15729
16584
  );
15730
16585
  }
15731
- const packageJson = readSync(packageJsonPath, { format: "json" });
15732
- if (!packageJson) {
16586
+ const packageJson = readPackageJson(packageJsonPath);
16587
+ if (!packageJson.name) {
15733
16588
  throw new Error(
15734
16589
  `package.json is empty or invalid JSON at ${packageJsonPath}`
15735
16590
  );
@@ -15821,7 +16676,9 @@ function getDeployedFunctionNames(functionsDir) {
15821
16676
  }
15822
16677
  return Object.keys(parsed.endpoints);
15823
16678
  } catch (error2) {
15824
- log.warn(`Failed to parse functions.yaml: ${error2 instanceof Error ? error2.message : String(error2)}`);
16679
+ log.warn(
16680
+ `Failed to parse functions.yaml: ${error2 instanceof Error ? error2.message : String(error2)}`
16681
+ );
15825
16682
  return [];
15826
16683
  }
15827
16684
  }
@@ -15831,12 +16688,16 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
15831
16688
  }
15832
16689
  if (!checkCLI("gcloud", ["--version"])) {
15833
16690
  log.warn("gcloud CLI not found. Skipping Cloud Run IAM updates.");
15834
- log.warn("Functions may require manual IAM configuration for CORS preflight.");
16691
+ log.warn(
16692
+ "Functions may require manual IAM configuration for CORS preflight."
16693
+ );
15835
16694
  log.warn("Install gcloud: https://cloud.google.com/sdk/docs/install");
15836
16695
  return;
15837
16696
  }
15838
16697
  const s = Y2();
15839
- s.start(`Updating Cloud Run IAM for ${functionNames.length} functions (CORS preflight)...`);
16698
+ s.start(
16699
+ `Updating Cloud Run IAM for ${functionNames.length} functions (CORS preflight)...`
16700
+ );
15840
16701
  const deployEnv = createDeployEnv(serviceAccountPath);
15841
16702
  let successCount = 0;
15842
16703
  let failCount = 0;
@@ -15875,7 +16736,9 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
15875
16736
  }
15876
16737
  } catch (error2) {
15877
16738
  failCount++;
15878
- log.warn(`Error updating IAM for ${funcName}: ${error2 instanceof Error ? error2.message : String(error2)}`);
16739
+ log.warn(
16740
+ `Error updating IAM for ${funcName}: ${error2 instanceof Error ? error2.message : String(error2)}`
16741
+ );
15879
16742
  }
15880
16743
  }
15881
16744
  if (successCount > 0) {
@@ -15884,7 +16747,9 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
15884
16747
  s.stop("Cloud Run IAM update completed with errors");
15885
16748
  }
15886
16749
  if (failCount > 0) {
15887
- log.warn(`${failCount} function(s) failed IAM update. They may require manual configuration.`);
16750
+ log.warn(
16751
+ `${failCount} function(s) failed IAM update. They may require manual configuration.`
16752
+ );
15888
16753
  }
15889
16754
  }
15890
16755
  function getFirebaseRegion(functionsDir) {
@@ -15925,7 +16790,11 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
15925
16790
  const envContent = readSync(envPath, { format: "text" });
15926
16791
  if (!envContent) return;
15927
16792
  const reservedPrefixes = ["X_GOOGLE_", "FIREBASE_", "EXT_", "GCLOUD_"];
15928
- const configOnlyKeys = ["FIREBASE_REGION", "FIREBASE_PROJECT_ID", "FIREBASE_AUTH_DOMAIN"];
16793
+ const configOnlyKeys = [
16794
+ "FIREBASE_REGION",
16795
+ "FIREBASE_PROJECT_ID",
16796
+ "FIREBASE_AUTH_DOMAIN"
16797
+ ];
15929
16798
  const secrets = [];
15930
16799
  for (const line of envContent.split("\n")) {
15931
16800
  const trimmed = line.trim();
@@ -15944,7 +16813,9 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
15944
16813
  return;
15945
16814
  }
15946
16815
  const s = Y2();
15947
- s.start(`Syncing ${secrets.length} secret(s) to Firebase Secret Manager...`);
16816
+ s.start(
16817
+ `Syncing ${secrets.length} secret(s) to Firebase Secret Manager...`
16818
+ );
15948
16819
  const firebaseCmd = process.platform === "win32" ? "firebase.cmd" : "firebase";
15949
16820
  let successCount = 0;
15950
16821
  let failCount = 0;
@@ -15984,8 +16855,12 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
15984
16855
  log.warn('Could not sync secrets. Run "dn sync-secrets" manually.');
15985
16856
  }
15986
16857
  } catch (error2) {
15987
- log.debug(`Could not sync secrets: ${error2 instanceof Error ? error2.message : String(error2)}`);
15988
- log.info('Run "dn sync-secrets" manually to sync secrets to Firebase Secret Manager.');
16858
+ log.debug(
16859
+ `Could not sync secrets: ${error2 instanceof Error ? error2.message : String(error2)}`
16860
+ );
16861
+ log.info(
16862
+ 'Run "dn sync-secrets" manually to sync secrets to Firebase Secret Manager.'
16863
+ );
15989
16864
  }
15990
16865
  }
15991
16866
  async function deployFunctions(appDir, serviceAccountPath, projectId, config) {
@@ -16028,7 +16903,7 @@ To fix this, run:
16028
16903
  const s2 = Y2();
16029
16904
  s2.start("Building functions...");
16030
16905
  try {
16031
- execSync2("bun run build", {
16906
+ execSync3("bun run build", {
16032
16907
  cwd: functionsDir,
16033
16908
  stdio: config.verbose ? "inherit" : "pipe"
16034
16909
  });
@@ -16046,7 +16921,8 @@ To fix this, run:
16046
16921
  "functions",
16047
16922
  projectId,
16048
16923
  config.debug,
16049
- config.force
16924
+ config.force ?? true
16925
+ // Default --force for functions (auto-sets artifact cleanup policy)
16050
16926
  );
16051
16927
  const result = executeFirebaseCommand(args, {
16052
16928
  cwd: firebaseProjectDir,
@@ -16070,7 +16946,13 @@ To fix this, run:
16070
16946
  const functionNames = getDeployedFunctionNames(functionsDir);
16071
16947
  if (functionNames.length > 0) {
16072
16948
  const region = getFirebaseRegion(functionsDir);
16073
- updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath, config.verbose ?? false);
16949
+ updateCloudRunIAM(
16950
+ functionNames,
16951
+ projectId,
16952
+ region,
16953
+ serviceAccountPath,
16954
+ config.verbose ?? false
16955
+ );
16074
16956
  }
16075
16957
  await autoSyncSecrets(functionsDir, projectId, config);
16076
16958
  } finally {
@@ -16097,7 +16979,7 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
16097
16979
  const targetNames = targets.join(", ");
16098
16980
  const s = Y2();
16099
16981
  s.start(`Deploying ${targetNames}...`);
16100
- const args = buildFirebaseDeployArgs(targets, projectId, config.debug);
16982
+ const args = buildFirebaseDeployArgs(targets, projectId, config.debug, config.force ?? true);
16101
16983
  const result = executeFirebaseCommand(args, {
16102
16984
  cwd: appDir,
16103
16985
  serviceAccountPath,
@@ -16123,6 +17005,27 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
16123
17005
  init_utils();
16124
17006
  import { spawnSync as spawnSync4 } from "node:child_process";
16125
17007
  init_pathResolver();
17008
+ init_typed_file_operations();
17009
+
17010
+ // packages/tooling/src/utils/error-handling.ts
17011
+ init_utils();
17012
+ function isError(error2) {
17013
+ return error2 instanceof Error;
17014
+ }
17015
+ function getErrorMessage(error2) {
17016
+ if (isError(error2)) {
17017
+ return error2.message;
17018
+ }
17019
+ if (typeof error2 === "string") {
17020
+ return error2;
17021
+ }
17022
+ if (error2 && typeof error2 === "object" && "message" in error2) {
17023
+ return String(error2.message);
17024
+ }
17025
+ return String(error2);
17026
+ }
17027
+
17028
+ // packages/tooling/src/apps/deploy-utils.ts
16126
17029
  function detectAvailableApps() {
16127
17030
  const currentDir = process.cwd();
16128
17031
  const appsDir = joinPath(currentDir, "apps");
@@ -16134,8 +17037,11 @@ function detectAvailableApps() {
16134
17037
  const stat = statSync2(itemPath);
16135
17038
  return stat?.isDirectory() === true;
16136
17039
  }).filter((app) => {
16137
- const firebaseJsonPath = joinPath(appsDir, app, "firebase.json");
16138
- return pathExists(firebaseJsonPath);
17040
+ const appDir = joinPath(appsDir, app);
17041
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
17042
+ const supabaseDir = joinPath(appDir, "supabase");
17043
+ const vercelJsonPath = joinPath(appDir, "vercel.json");
17044
+ return pathExists(firebaseJsonPath) || pathExists(supabaseDir) || pathExists(vercelJsonPath);
16139
17045
  }).sort();
16140
17046
  }
16141
17047
  function detectAppDir(appName) {
@@ -16174,40 +17080,7 @@ function validateServiceAccount(appDir, options) {
16174
17080
  };
16175
17081
  }
16176
17082
  try {
16177
- const keyData = readSync(keyPath, { format: "json" });
16178
- if (!keyData) {
16179
- return {
16180
- valid: false,
16181
- info: {
16182
- path: keyPath,
16183
- projectId: "",
16184
- clientEmail: "",
16185
- valid: false,
16186
- errors: ["Service account key file is empty or invalid JSON"]
16187
- }
16188
- };
16189
- }
16190
- if (!keyData.project_id) {
16191
- errors.push("Missing required field: project_id");
16192
- }
16193
- if (!keyData.private_key) {
16194
- errors.push("Missing required field: private_key");
16195
- }
16196
- if (!keyData.client_email) {
16197
- errors.push("Missing required field: client_email");
16198
- }
16199
- if (errors.length > 0) {
16200
- return {
16201
- valid: false,
16202
- info: {
16203
- path: keyPath,
16204
- projectId: keyData.project_id || "",
16205
- clientEmail: keyData.client_email || "",
16206
- valid: false,
16207
- errors
16208
- }
16209
- };
16210
- }
17083
+ const keyData = readServiceAccountKey(keyPath);
16211
17084
  return {
16212
17085
  valid: true,
16213
17086
  info: {
@@ -16226,9 +17099,7 @@ function validateServiceAccount(appDir, options) {
16226
17099
  projectId: "",
16227
17100
  clientEmail: "",
16228
17101
  valid: false,
16229
- errors: [
16230
- `Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
16231
- ]
17102
+ errors: [`Invalid service account key: ${getErrorMessage(error2)}`]
16232
17103
  }
16233
17104
  };
16234
17105
  }
@@ -16247,12 +17118,7 @@ function validateFirebaseJson(appDir) {
16247
17118
  };
16248
17119
  }
16249
17120
  try {
16250
- const config = readSync(firebaseJsonPath, { format: "json" });
16251
- if (!config) {
16252
- throw new Error(
16253
- `firebase.json is empty or invalid JSON at ${firebaseJsonPath}`
16254
- );
16255
- }
17121
+ const config = readFirebaseJson(firebaseJsonPath);
16256
17122
  const hasHosting = !!config.hosting;
16257
17123
  const hasFunctions = !!config.functions && Array.isArray(config.functions);
16258
17124
  const hasFirestoreRules = !!config.firestore?.rules && pathExists(joinPath(appDir, config.firestore.rules));
@@ -16276,9 +17142,7 @@ function validateFirebaseJson(appDir) {
16276
17142
  hasFirestoreRules: false,
16277
17143
  hasFirestoreIndexes: false,
16278
17144
  hasStorageRules: false,
16279
- errors: [
16280
- `Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
16281
- ]
17145
+ errors: [`Invalid firebase.json: ${getErrorMessage(error2)}`]
16282
17146
  };
16283
17147
  }
16284
17148
  }
@@ -16304,11 +17168,11 @@ function readStagingProject(startDir) {
16304
17168
  const firebasercPath = joinPath(current, ".firebaserc");
16305
17169
  if (pathExists(firebasercPath)) {
16306
17170
  try {
16307
- const firebaserc = readSync(firebasercPath, { format: "json" });
17171
+ const firebaserc = readFirebaserc(firebasercPath);
16308
17172
  if (firebaserc?.projects?.staging) {
16309
17173
  return firebaserc.projects.staging;
16310
17174
  }
16311
- } catch {
17175
+ } catch (error2) {
16312
17176
  }
16313
17177
  }
16314
17178
  const parent = joinPath(current, "..");
@@ -16394,12 +17258,87 @@ function clearFirebaseCache(appDir) {
16394
17258
  }
16395
17259
  }
16396
17260
 
17261
+ // packages/tooling/src/utils/provider-detection.ts
17262
+ init_utils();
17263
+ init_pathResolver();
17264
+ function detectProvider(appDir) {
17265
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
17266
+ const supabaseDir = joinPath(appDir, "supabase");
17267
+ const supabaseConfigPath = joinPath(supabaseDir, "config.toml");
17268
+ const supabaseFunctionsDir = joinPath(supabaseDir, "functions");
17269
+ const supabaseMigrationsDir = joinPath(supabaseDir, "migrations");
17270
+ const hasFirebase = pathExists(firebaseJsonPath);
17271
+ const hasSupabase = pathExists(supabaseDir) || pathExists(supabaseConfigPath);
17272
+ let provider = "unknown";
17273
+ if (hasFirebase && hasSupabase) {
17274
+ provider = "both";
17275
+ } else if (hasFirebase) {
17276
+ provider = "firebase";
17277
+ } else if (hasSupabase) {
17278
+ provider = "supabase";
17279
+ }
17280
+ let firebaseConfig;
17281
+ if (hasFirebase) {
17282
+ try {
17283
+ const config = readSync(firebaseJsonPath, { format: "json" });
17284
+ if (config && typeof config === "object" && "projectId" in config) {
17285
+ const firebaseConfigObj = config;
17286
+ firebaseConfig = {
17287
+ projectId: firebaseConfigObj.projectId,
17288
+ hasHosting: !!firebaseConfigObj.hosting,
17289
+ hasFunctions: !!firebaseConfigObj.functions,
17290
+ hasFirestoreRules: !!firebaseConfigObj.firestore?.rules,
17291
+ hasStorageRules: !!firebaseConfigObj.storage?.rules
17292
+ };
17293
+ } else {
17294
+ throw new Error("Invalid firebase.json structure");
17295
+ }
17296
+ } catch {
17297
+ firebaseConfig = {
17298
+ hasHosting: false,
17299
+ hasFunctions: false,
17300
+ hasFirestoreRules: false,
17301
+ hasStorageRules: false
17302
+ };
17303
+ }
17304
+ }
17305
+ let supabaseConfig;
17306
+ if (hasSupabase) {
17307
+ let url;
17308
+ try {
17309
+ const envPath = joinPath(appDir, ".env");
17310
+ if (pathExists(envPath)) {
17311
+ const envContent = readSync(envPath);
17312
+ const urlMatch = envContent.match(/VITE_SUPABASE_URL=(.+)/);
17313
+ if (urlMatch && urlMatch[1]) {
17314
+ url = urlMatch[1].trim();
17315
+ }
17316
+ }
17317
+ } catch {
17318
+ }
17319
+ supabaseConfig = {
17320
+ url,
17321
+ hasFunctions: pathExists(supabaseFunctionsDir),
17322
+ hasMigrations: pathExists(supabaseMigrationsDir)
17323
+ };
17324
+ }
17325
+ return {
17326
+ provider,
17327
+ hasFirebase,
17328
+ hasSupabase,
17329
+ firebaseConfig,
17330
+ supabaseConfig
17331
+ };
17332
+ }
17333
+
16397
17334
  // packages/tooling/src/apps/deploy.ts
17335
+ init_cli_input();
16398
17336
  init_cli_output();
16399
17337
  init_cli_output();
17338
+ init_cli_tools();
16400
17339
  init_errors();
16401
17340
  init_pathResolver();
16402
- async function main(options = {}) {
17341
+ async function main2(options = {}) {
16403
17342
  const isStaging = options.staging ?? false;
16404
17343
  const config = {
16405
17344
  app: options.app,
@@ -16407,27 +17346,43 @@ async function main(options = {}) {
16407
17346
  verbose: options.verbose ?? false,
16408
17347
  skipBuild: options.skipBuild ?? false,
16409
17348
  debug: options.debug ?? false,
16410
- force: options.force ?? false,
17349
+ force: options.force,
16411
17350
  staging: isStaging
16412
17351
  };
16413
17352
  try {
16414
- Ie(isStaging ? "DoNotDev Staging Deployment" : "DoNotDev Firebase Deployment");
16415
- requireCLI(
16416
- "firebase",
16417
- "This script uses service account authentication.\nThe Firebase CLI is only needed as a deployment tool, not for authentication."
17353
+ const projectRoot = process.cwd();
17354
+ const providerInfo = detectProvider(projectRoot);
17355
+ Ie(
17356
+ isStaging ? "DoNotDev Staging Deployment" : providerInfo.provider === "supabase" ? "DoNotDev Supabase Deployment" : providerInfo.provider === "both" ? "DoNotDev Deployment (Firebase + Supabase)" : "DoNotDev Firebase Deployment"
16418
17357
  );
17358
+ if (providerInfo.hasFirebase) {
17359
+ requireCLI(
17360
+ CLI_TOOLS.FIREBASE,
17361
+ "This script uses service account authentication.\nThe Firebase CLI is only needed as a deployment tool, not for authentication."
17362
+ );
17363
+ }
17364
+ if (providerInfo.hasSupabase && providerInfo.supabaseConfig?.hasFunctions) {
17365
+ requireCLI(
17366
+ CLI_TOOLS.SUPABASE,
17367
+ "Supabase CLI is required to deploy Edge Functions.\nInstall: https://supabase.com/docs/guides/cli"
17368
+ );
17369
+ }
16419
17370
  const COMMAND_NAMES = ["deploy", "staging", "uat"];
16420
17371
  const cliArgs = process.argv.slice(2);
16421
17372
  let appName = config.app || (cliArgs[0] && !cliArgs[0].startsWith("--") && !COMMAND_NAMES.includes(cliArgs[0]) ? cliArgs[0] : void 0);
16422
17373
  if (!appName) {
16423
17374
  const availableApps = detectAvailableApps();
16424
17375
  if (availableApps.length === 0) {
16425
- log.info("No apps with firebase.json found in current directory.");
16426
- log.info(
16427
- "To deploy, ensure your app has a firebase.json configuration."
16428
- );
16429
- log.info("Run from a DoNotDev project, or create one with: dndev init");
16430
- return;
17376
+ if (providerInfo.hasSupabase && !providerInfo.hasFirebase) {
17377
+ log.info("Supabase project detected. Deploying Supabase resources...");
17378
+ } else {
17379
+ log.info("No apps with firebase.json or supabase/ directory found.");
17380
+ log.info(
17381
+ "To deploy, ensure your app has a firebase.json or supabase/ configuration."
17382
+ );
17383
+ log.info("Run from a DoNotDev project, or create one with: dndev init");
17384
+ return;
17385
+ }
16431
17386
  }
16432
17387
  if (availableApps.length === 1) {
16433
17388
  appName = availableApps[0];
@@ -16443,55 +17398,92 @@ async function main(options = {}) {
16443
17398
  log.error("App name is required but was not provided.");
16444
17399
  process.exit(1);
16445
17400
  }
16446
- const appDir = detectAppDir(appName);
17401
+ const appDir = appName ? detectAppDir(appName) : projectRoot;
17402
+ const appProviderInfo = detectProvider(appDir);
17403
+ const hasVercelConfig = pathExists(joinPath(appDir, "vercel.json"));
16447
17404
  let deploymentType = config.deploymentType;
16448
17405
  if (!deploymentType) {
16449
- const firebaseConfig2 = validateFirebaseJson(appDir);
16450
17406
  const choices = [];
16451
- if (firebaseConfig2.hasHosting) {
16452
- choices.push({
16453
- title: "Frontend only",
16454
- value: "frontend",
16455
- hint: "Deploy hosting"
16456
- });
16457
- }
16458
- if (firebaseConfig2.hasFunctions) {
16459
- choices.push({
16460
- title: "Functions only",
16461
- value: "functions",
16462
- hint: "Deploy cloud functions"
16463
- });
16464
- }
16465
- const hasRules = firebaseConfig2.hasFirestoreRules || firebaseConfig2.hasFirestoreIndexes || firebaseConfig2.hasStorageRules;
16466
- if (hasRules) {
16467
- choices.push({
16468
- title: "Rules only",
16469
- value: "rules",
16470
- hint: "Deploy Firestore/Storage rules"
16471
- });
17407
+ if (appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig) {
17408
+ const fbConfig = appProviderInfo.firebaseConfig;
17409
+ if (fbConfig.hasHosting) {
17410
+ choices.push({
17411
+ title: "Frontend only (Firebase Hosting)",
17412
+ value: "frontend",
17413
+ hint: "Deploy Firebase hosting"
17414
+ });
17415
+ }
17416
+ if (fbConfig.hasFunctions) {
17417
+ choices.push({
17418
+ title: "Functions only (Firebase Cloud Functions)",
17419
+ value: "functions",
17420
+ hint: "Deploy Firebase cloud functions"
17421
+ });
17422
+ }
17423
+ const hasRules = fbConfig.hasFirestoreRules || fbConfig.hasStorageRules;
17424
+ if (hasRules) {
17425
+ choices.push({
17426
+ title: "Rules only (Firestore/Storage)",
17427
+ value: "rules",
17428
+ hint: "Deploy Firestore/Storage rules"
17429
+ });
17430
+ }
17431
+ if (fbConfig.hasHosting && fbConfig.hasFunctions) {
17432
+ choices.push({
17433
+ title: "Frontend + Functions (Firebase)",
17434
+ value: "both",
17435
+ hint: "Deploy Firebase hosting and functions"
17436
+ });
17437
+ }
17438
+ const deployableKindsCount = [
17439
+ fbConfig.hasHosting,
17440
+ fbConfig.hasFunctions,
17441
+ hasRules
17442
+ ].filter(Boolean).length;
17443
+ if (deployableKindsCount > 1) {
17444
+ choices.push({
17445
+ title: "All (Firebase)",
17446
+ value: "all",
17447
+ hint: "Deploy everything Firebase (hosting, functions, rules)"
17448
+ });
17449
+ }
16472
17450
  }
16473
- if (firebaseConfig2.hasHosting && firebaseConfig2.hasFunctions) {
17451
+ if (hasVercelConfig) {
17452
+ const fbFrontendIdx = choices.findIndex(
17453
+ (c) => c.value === "frontend" && c.title.includes("Firebase")
17454
+ );
17455
+ if (fbFrontendIdx !== -1) choices.splice(fbFrontendIdx, 1);
17456
+ const fbBothIdx = choices.findIndex(
17457
+ (c) => c.value === "both" && c.title.includes("Firebase")
17458
+ );
17459
+ if (fbBothIdx !== -1) choices.splice(fbBothIdx, 1);
16474
17460
  choices.push({
16475
- title: "Frontend + Functions",
16476
- value: "both",
16477
- hint: "Deploy hosting and functions"
17461
+ title: "Frontend only (Vercel)",
17462
+ value: "frontend",
17463
+ hint: "Deploy frontend to Vercel"
16478
17464
  });
17465
+ const hasFunctions = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasFunctions || appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig?.hasFunctions;
17466
+ if (hasFunctions) {
17467
+ choices.push({
17468
+ title: "Frontend + Functions (Vercel + backend)",
17469
+ value: "both",
17470
+ hint: "Deploy frontend to Vercel and functions to backend provider"
17471
+ });
17472
+ }
16479
17473
  }
16480
- const deployableKindsCount = [
16481
- firebaseConfig2.hasHosting,
16482
- firebaseConfig2.hasFunctions,
16483
- hasRules
16484
- ].filter(Boolean).length;
16485
- if (deployableKindsCount > 1) {
16486
- choices.push({
16487
- title: "All",
16488
- value: "all",
16489
- hint: "Deploy everything (hosting, functions, rules)"
16490
- });
17474
+ if (appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig) {
17475
+ const sbConfig = appProviderInfo.supabaseConfig;
17476
+ if (sbConfig.hasFunctions) {
17477
+ choices.push({
17478
+ title: "Edge Functions only (Supabase)",
17479
+ value: "functions",
17480
+ hint: "Deploy Supabase Edge Functions"
17481
+ });
17482
+ }
16491
17483
  }
16492
17484
  if (choices.length === 0) {
16493
17485
  log.error(
16494
- "No deployment targets found. firebase.json must have hosting, functions, or rules configuration."
17486
+ "No deployment targets found. Ensure your project has:\n - Firebase: firebase.json with hosting/functions/rules\n - Supabase: supabase/functions/ directory"
16495
17487
  );
16496
17488
  process.exit(1);
16497
17489
  }
@@ -16505,71 +17497,78 @@ async function main(options = {}) {
16505
17497
  );
16506
17498
  }
16507
17499
  }
16508
- const serviceAccountResult = validateServiceAccount(appDir, { staging: isStaging });
16509
- if (!serviceAccountResult.valid) {
16510
- const firebaseConfig2 = validateFirebaseJson(appDir);
16511
- await showServiceAccountError(
16512
- serviceAccountResult.info,
16513
- firebaseConfig2.projectId
16514
- );
16515
- process.exit(1);
16516
- }
16517
- const serviceAccountPath = normalizePath(serviceAccountResult.info.path);
16518
- let projectId = config.project;
16519
- if (!projectId && isStaging) {
16520
- projectId = readStagingProject(appDir) ?? void 0;
16521
- if (!projectId) {
16522
- log.error(
16523
- 'Staging project not found. Add "staging" alias to .firebaserc:\n { "projects": { "default": "my-app", "staging": "my-app-uat" } }'
17500
+ let serviceAccountPath;
17501
+ let projectId;
17502
+ const needsFirebase = appProviderInfo.hasFirebase && (deploymentType === "frontend" && !hasVercelConfig || deploymentType === "functions" || deploymentType === "rules" || deploymentType === "both" || deploymentType === "all");
17503
+ if (needsFirebase) {
17504
+ const serviceAccountResult = validateServiceAccount(appDir, {
17505
+ staging: isStaging
17506
+ });
17507
+ if (!serviceAccountResult.valid) {
17508
+ const firebaseConfig2 = validateFirebaseJson(appDir);
17509
+ await showServiceAccountError(
17510
+ serviceAccountResult.info,
17511
+ firebaseConfig2.projectId
16524
17512
  );
16525
17513
  process.exit(1);
16526
17514
  }
16527
- }
16528
- if (!projectId) {
16529
- projectId = serviceAccountResult.info.projectId || (() => {
16530
- const firebaseConfig2 = validateFirebaseJson(appDir);
16531
- return firebaseConfig2.projectId;
16532
- })();
16533
- }
16534
- if (!projectId) {
16535
- const inputProjectId = await askForInput(
16536
- "Firebase project ID not detected. Please enter it:"
16537
- );
16538
- if (!inputProjectId) {
16539
- log.error("Project ID is required for deployment.");
17515
+ serviceAccountPath = normalizePath(serviceAccountResult.info.path);
17516
+ projectId = config.project;
17517
+ if (!projectId && isStaging) {
17518
+ projectId = readStagingProject(appDir) ?? void 0;
17519
+ if (!projectId) {
17520
+ log.error(
17521
+ 'Staging project not found. Add "staging" alias to .firebaserc:\n { "projects": { "default": "my-app", "staging": "my-app-uat" } }'
17522
+ );
17523
+ process.exit(1);
17524
+ }
17525
+ }
17526
+ if (!projectId) {
17527
+ const fallbackConfig = validateFirebaseJson(appDir);
17528
+ projectId = serviceAccountResult.info.projectId || fallbackConfig.projectId;
17529
+ }
17530
+ if (!projectId) {
17531
+ const inputProjectId = await askForInput(
17532
+ "Firebase project ID not detected. Please enter it:"
17533
+ );
17534
+ if (!inputProjectId) {
17535
+ log.error("Project ID is required for deployment.");
17536
+ process.exit(1);
17537
+ }
17538
+ config.project = inputProjectId;
17539
+ } else {
17540
+ config.project = projectId;
17541
+ }
17542
+ const firebaseConfig = await validateFirebaseJson(appDir);
17543
+ if (!firebaseConfig.valid) {
17544
+ showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
16540
17545
  process.exit(1);
16541
17546
  }
16542
- config.project = inputProjectId;
16543
- } else {
16544
- config.project = projectId;
16545
- }
16546
- const firebaseConfig = validateFirebaseJson(appDir);
16547
- if (!firebaseConfig.valid) {
16548
- showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
16549
- process.exit(1);
16550
17547
  }
16551
- const shouldDeployFrontend = (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all") && firebaseConfig.hasHosting;
16552
- const shouldDeployFunctions = (deploymentType === "functions" || deploymentType === "both" || deploymentType === "all") && firebaseConfig.hasFunctions;
16553
- const shouldDeployRules = (deploymentType === "rules" || deploymentType === "all") && (firebaseConfig.hasFirestoreRules || firebaseConfig.hasFirestoreIndexes || firebaseConfig.hasStorageRules);
16554
- if (deploymentType === "frontend" && !firebaseConfig.hasHosting) {
17548
+ const shouldDeployFirebaseFrontend = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasHosting && !hasVercelConfig && (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all");
17549
+ const shouldDeployFirebaseFunctions = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasFunctions && (deploymentType === "functions" || deploymentType === "both" || deploymentType === "all");
17550
+ const shouldDeployFirebaseRules = appProviderInfo.hasFirebase && (deploymentType === "rules" || deploymentType === "all") && (appProviderInfo.firebaseConfig?.hasFirestoreRules || appProviderInfo.firebaseConfig?.hasStorageRules);
17551
+ const shouldDeploySupabaseFunctions = appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig?.hasFunctions && deploymentType === "functions";
17552
+ const shouldDeployVercelFrontend = hasVercelConfig && (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all");
17553
+ if (deploymentType === "frontend" && !shouldDeployFirebaseFrontend && !shouldDeployVercelFrontend) {
16555
17554
  log.error(
16556
- "firebase.json does not contain hosting configuration, but frontend deployment was requested."
17555
+ "No hosting configuration found. Ensure firebase.json has hosting config, or (for Supabase apps) vercel.json exists."
16557
17556
  );
16558
17557
  process.exit(1);
16559
17558
  }
16560
- if (deploymentType === "functions" && !firebaseConfig.hasFunctions) {
17559
+ if (deploymentType === "functions" && !shouldDeployFirebaseFunctions && !shouldDeploySupabaseFunctions) {
16561
17560
  log.error(
16562
- "firebase.json does not contain functions configuration, but functions deployment was requested."
17561
+ "No functions configuration found. Ensure firebase.json has functions config or supabase/functions/ directory exists."
16563
17562
  );
16564
17563
  process.exit(1);
16565
17564
  }
16566
- if (deploymentType === "rules" && !shouldDeployRules) {
17565
+ if (deploymentType === "rules" && !shouldDeployFirebaseRules) {
16567
17566
  log.error(
16568
17567
  "firebase.json does not contain rules configuration, but rules deployment was requested."
16569
17568
  );
16570
17569
  process.exit(1);
16571
17570
  }
16572
- if (shouldDeployFrontend) {
17571
+ if (shouldDeployFirebaseFrontend || shouldDeployVercelFrontend) {
16573
17572
  const buildStatus = validateBuild(appDir);
16574
17573
  let shouldBuild = false;
16575
17574
  if (!buildStatus.exists || buildStatus.isEmpty) {
@@ -16596,9 +17595,11 @@ async function main(options = {}) {
16596
17595
  if (shouldBuild) {
16597
17596
  const s = Y2();
16598
17597
  const buildCmd = isStaging ? "bun run build -- --mode staging" : "bun run build";
16599
- s.start(isStaging ? "Building (staging mode)..." : "Building application...");
17598
+ s.start(
17599
+ isStaging ? "Building (staging mode)..." : "Building application..."
17600
+ );
16600
17601
  try {
16601
- execSync3(buildCmd, {
17602
+ execSync5(buildCmd, {
16602
17603
  cwd: appDir,
16603
17604
  stdio: "inherit",
16604
17605
  env: {
@@ -16613,19 +17614,52 @@ async function main(options = {}) {
16613
17614
  }
16614
17615
  }
16615
17616
  }
16616
- clearFirebaseCache(appDir);
17617
+ if (appProviderInfo.hasFirebase) {
17618
+ clearFirebaseCache(appDir);
17619
+ }
16617
17620
  const envLabel = isStaging ? " (STAGING)" : "";
16618
- Me(
16619
- `App: ${appName}
16620
- Project: ${config.project}${envLabel}
16621
- Deployment: ${deploymentType}
16622
- Service Account: ${serviceAccountResult.info.clientEmail}`,
16623
- "Deployment Configuration"
16624
- );
16625
- if (shouldDeployFrontend) {
17621
+ const providerLabel = appProviderInfo.provider === "both" ? " (Firebase + Supabase)" : appProviderInfo.provider === "supabase" ? " (Supabase)" : " (Firebase)";
17622
+ const configNote = [
17623
+ `App: ${appName || "root"}`,
17624
+ ...config.project ? [`Project: ${config.project}${envLabel}`] : [],
17625
+ `Provider: ${appProviderInfo.provider}${providerLabel}`,
17626
+ `Deployment: ${deploymentType}`,
17627
+ ...serviceAccountPath ? [`Service Account: ${serviceAccountPath}`] : []
17628
+ ].join("\n");
17629
+ Me(configNote, "Deployment Configuration");
17630
+ if (shouldDeployVercelFrontend) {
17631
+ requireCLI(
17632
+ CLI_TOOLS.VERCEL,
17633
+ "Vercel CLI is required to deploy the frontend.\nInstall: npm i -g vercel or use npx vercel"
17634
+ );
17635
+ }
17636
+ if (shouldDeployFirebaseFrontend && serviceAccountPath && config.project) {
16626
17637
  await deployFrontend(appDir, serviceAccountPath, config.project, config);
16627
17638
  }
16628
- if (shouldDeployFunctions) {
17639
+ if (shouldDeployVercelFrontend) {
17640
+ await deployVercelFrontend(appDir, config);
17641
+ }
17642
+ if (shouldDeployFirebaseFunctions && serviceAccountPath && config.project) {
17643
+ const functionsEnvPath = joinPath(appDir, "functions", ".env");
17644
+ if (pathExists(functionsEnvPath)) {
17645
+ const shouldSync = await askForConfirmation(
17646
+ "Sync runtime secrets (functions/.env) before deploying?"
17647
+ );
17648
+ if (shouldSync) {
17649
+ const { main: syncSecrets } = await Promise.resolve().then(() => (init_sync_secrets(), sync_secrets_exports));
17650
+ const syncResult = await syncSecrets({
17651
+ envFile: normalizePath(functionsEnvPath),
17652
+ projectId: config.project,
17653
+ dryRun: false,
17654
+ verbose: config.verbose
17655
+ });
17656
+ if (syncResult !== 0) {
17657
+ log.warn(
17658
+ "Secrets sync returned non-zero. Functions may not have latest secrets at runtime."
17659
+ );
17660
+ }
17661
+ }
17662
+ }
16629
17663
  await deployFunctions(
16630
17664
  appDir,
16631
17665
  serviceAccountPath,
@@ -16633,13 +17667,18 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
16633
17667
  config
16634
17668
  );
16635
17669
  }
16636
- if (shouldDeployRules) {
17670
+ if (shouldDeployFirebaseRules && serviceAccountPath && config.project && appProviderInfo.firebaseConfig) {
16637
17671
  await deployRules(appDir, serviceAccountPath, config.project, config, {
16638
- firestore: firebaseConfig.hasFirestoreRules,
16639
- firestoreIndexes: firebaseConfig.hasFirestoreIndexes,
16640
- storage: firebaseConfig.hasStorageRules
17672
+ firestore: appProviderInfo.firebaseConfig.hasFirestoreRules,
17673
+ firestoreIndexes: false,
17674
+ // TODO: detect this
17675
+ storage: appProviderInfo.firebaseConfig.hasStorageRules
16641
17676
  });
16642
17677
  }
17678
+ if (shouldDeploySupabaseFunctions) {
17679
+ const { deploySupabaseFunctions: deploySupabaseFunctions2 } = await Promise.resolve().then(() => (init_deploy_supabase_functions(), deploy_supabase_functions_exports));
17680
+ await deploySupabaseFunctions2(appDir, config);
17681
+ }
16643
17682
  Se("Deployment completed successfully!");
16644
17683
  } catch (error2) {
16645
17684
  if (error2 instanceof DoNotDevError) {
@@ -16656,7 +17695,7 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
16656
17695
  }
16657
17696
  }
16658
17697
  export {
16659
- main
17698
+ main2 as main
16660
17699
  };
16661
17700
  /*! Bundled license information:
16662
17701