@donotdev/cli 0.0.18 → 0.0.20

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 (150) hide show
  1. package/dependencies-matrix.json +42 -55
  2. package/dist/bin/commands/bump.js +5 -2
  3. package/dist/bin/commands/coach.js +8177 -0
  4. package/dist/bin/commands/create-app.js +6 -6
  5. package/dist/bin/commands/create-project.js +23 -9
  6. package/dist/bin/commands/deploy.js +99 -59
  7. package/dist/bin/commands/doctor.js +243 -698
  8. package/dist/bin/commands/emu.js +2 -2
  9. package/dist/bin/commands/format.js +4 -1
  10. package/dist/bin/commands/get-demo.js +8351 -0
  11. package/dist/bin/commands/make-admin.js +773 -152
  12. package/dist/bin/commands/setup.js +524 -1713
  13. package/dist/bin/commands/staging.js +17870 -0
  14. package/dist/bin/commands/sync-secrets.js +2 -11
  15. package/dist/bin/commands/type-check.js +7738 -1712
  16. package/dist/bin/dndev.js +868 -199
  17. package/dist/bin/donotdev.js +868 -199
  18. package/dist/index.js +127 -67
  19. package/package.json +1 -1
  20. package/templates/app-demo/index.html.example +147 -10
  21. package/templates/app-demo/public/apple-touch-icon.png.example +0 -0
  22. package/templates/app-demo/public/favicon.svg.example +1 -0
  23. package/templates/app-demo/public/icon-192x192.png.example +0 -0
  24. package/templates/app-demo/public/icon-512x512.png.example +0 -0
  25. package/templates/app-demo/src/App.tsx.example +7 -11
  26. package/templates/app-demo/src/config/app.ts.example +13 -48
  27. package/templates/app-demo/src/entities/booking.ts.example +75 -0
  28. package/templates/app-demo/src/entities/onboarding.ts.example +160 -0
  29. package/templates/app-demo/src/entities/product.ts.example +50 -0
  30. package/templates/app-demo/src/entities/quote.ts.example +70 -0
  31. package/templates/app-demo/src/globals.css.example +5 -1
  32. package/templates/app-demo/src/main.tsx.example +13 -7
  33. package/templates/app-demo/src/pages/ChangelogPage.tsx.example +41 -0
  34. package/templates/app-demo/src/pages/ConditionalFormPage.tsx.example +88 -0
  35. package/templates/app-demo/src/pages/DashboardPage.tsx.example +17 -0
  36. package/templates/app-demo/src/pages/HomePage.tsx.example +339 -60
  37. package/templates/app-demo/src/pages/OnboardingPage.tsx.example +47 -0
  38. package/templates/app-demo/src/pages/PricingPage.tsx.example +41 -0
  39. package/templates/app-demo/src/pages/ProductsPage.tsx.example +19 -0
  40. package/templates/app-demo/src/pages/ProfilePage.tsx.example +18 -0
  41. package/templates/app-demo/src/pages/SettingsPage.tsx.example +17 -0
  42. package/templates/app-demo/src/pages/ShowcaseDetailPage.tsx.example +118 -0
  43. package/templates/app-demo/src/pages/ShowcasePage.tsx.example +93 -0
  44. package/templates/app-demo/src/pages/components/ComponentRenderer.tsx.example +147 -51
  45. package/templates/app-demo/src/pages/components/ComponentsData.tsx.example +103 -21
  46. package/templates/app-demo/src/pages/components/componentConfig.ts.example +139 -59
  47. package/templates/app-demo/src/pages/legal/LegalPage.tsx.example +25 -0
  48. package/templates/app-demo/src/pages/legal/PrivacyPage.tsx.example +23 -0
  49. package/templates/app-demo/src/pages/legal/TermsPage.tsx.example +23 -0
  50. package/templates/app-demo/src/themes.css.example +289 -77
  51. package/templates/app-demo/stats.html.example +4949 -0
  52. package/templates/app-demo/tsconfig.json.example +1 -1
  53. package/templates/app-demo/vite.config.ts.example +23 -48
  54. package/templates/app-expo/README.md.example +1 -1
  55. package/templates/app-expo/app/index.tsx.example +1 -1
  56. package/templates/app-next/src/locales/home_en.json.example +6 -6
  57. package/templates/app-vite/src/locales/home_en.json.example +6 -6
  58. package/templates/app-vite/src/pages/HomePage.tsx.example +8 -10
  59. package/templates/overlay-firebase/env.fragment.example +1 -1
  60. package/templates/overlay-firebase/env.fragment.expo.example +1 -1
  61. package/templates/overlay-firebase/env.fragment.nextjs.example +1 -1
  62. package/templates/overlay-supabase/env.fragment.example +1 -1
  63. package/templates/overlay-supabase/env.fragment.expo.example +1 -1
  64. package/templates/overlay-supabase/env.fragment.nextjs.example +1 -1
  65. package/templates/overlay-vercel/env.fragment.example +1 -1
  66. package/templates/overlay-vercel/env.fragment.nextjs.example +1 -1
  67. package/templates/root-consumer/AI.md.example +4 -3
  68. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +21 -6
  69. package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +16 -179
  70. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +19 -21
  71. package/templates/root-consumer/guides/dndev/GOTCHAS.md.example +14 -3
  72. package/templates/root-consumer/guides/dndev/INDEX.md.example +2 -2
  73. package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
  74. package/templates/root-consumer/guides/dndev/SETUP_BLOG.md.example +19 -2
  75. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +35 -1
  76. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +17 -12
  77. package/templates/root-consumer/guides/dndev/SETUP_LAYOUTS.md.example +32 -0
  78. package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +1 -1
  79. package/templates/root-consumer/guides/dndev/SETUP_PAGES.md.example +19 -15
  80. package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +2 -2
  81. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +17 -12
  82. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +37 -16
  83. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +18 -18
  84. package/templates/root-consumer/guides/dndev/advanced/COOKIE_REFERENCE.md.example +252 -252
  85. package/templates/root-consumer/guides/dndev/advanced/VERSION_CONTROL.md.example +174 -174
  86. package/templates/root-consumer/guides/dndev/essences_reference.css.example +119 -2
  87. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +14 -0
  88. package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +6 -0
  89. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +14 -0
  90. package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +4 -5
  91. package/templates/root-consumer/guides/wai-way/page_patterns.md.example +2 -2
  92. package/dist/bin/commands/agent-setup.d.ts +0 -6
  93. package/dist/bin/commands/agent-setup.d.ts.map +0 -1
  94. package/dist/bin/commands/agent-setup.js.map +0 -1
  95. package/dist/bin/commands/build.d.ts +0 -11
  96. package/dist/bin/commands/build.d.ts.map +0 -1
  97. package/dist/bin/commands/build.js.map +0 -1
  98. package/dist/bin/commands/bump.d.ts +0 -11
  99. package/dist/bin/commands/bump.d.ts.map +0 -1
  100. package/dist/bin/commands/bump.js.map +0 -1
  101. package/dist/bin/commands/cacheout.d.ts +0 -11
  102. package/dist/bin/commands/cacheout.d.ts.map +0 -1
  103. package/dist/bin/commands/cacheout.js.map +0 -1
  104. package/dist/bin/commands/create-app.d.ts +0 -11
  105. package/dist/bin/commands/create-app.d.ts.map +0 -1
  106. package/dist/bin/commands/create-app.js.map +0 -1
  107. package/dist/bin/commands/create-project.d.ts +0 -11
  108. package/dist/bin/commands/create-project.d.ts.map +0 -1
  109. package/dist/bin/commands/create-project.js.map +0 -1
  110. package/dist/bin/commands/deploy.d.ts +0 -11
  111. package/dist/bin/commands/deploy.d.ts.map +0 -1
  112. package/dist/bin/commands/deploy.js.map +0 -1
  113. package/dist/bin/commands/dev.d.ts +0 -11
  114. package/dist/bin/commands/dev.d.ts.map +0 -1
  115. package/dist/bin/commands/dev.js.map +0 -1
  116. package/dist/bin/commands/doctor.d.ts +0 -6
  117. package/dist/bin/commands/doctor.d.ts.map +0 -1
  118. package/dist/bin/commands/doctor.js.map +0 -1
  119. package/dist/bin/commands/emu.d.ts +0 -11
  120. package/dist/bin/commands/emu.d.ts.map +0 -1
  121. package/dist/bin/commands/emu.js.map +0 -1
  122. package/dist/bin/commands/format.d.ts +0 -11
  123. package/dist/bin/commands/format.d.ts.map +0 -1
  124. package/dist/bin/commands/format.js.map +0 -1
  125. package/dist/bin/commands/make-admin.d.ts +0 -11
  126. package/dist/bin/commands/make-admin.d.ts.map +0 -1
  127. package/dist/bin/commands/make-admin.js.map +0 -1
  128. package/dist/bin/commands/preview.d.ts +0 -11
  129. package/dist/bin/commands/preview.d.ts.map +0 -1
  130. package/dist/bin/commands/preview.js.map +0 -1
  131. package/dist/bin/commands/setup.d.ts +0 -6
  132. package/dist/bin/commands/setup.d.ts.map +0 -1
  133. package/dist/bin/commands/setup.js.map +0 -1
  134. package/dist/bin/commands/sync-secrets.d.ts +0 -11
  135. package/dist/bin/commands/sync-secrets.d.ts.map +0 -1
  136. package/dist/bin/commands/sync-secrets.js.map +0 -1
  137. package/dist/bin/commands/type-check.d.ts +0 -14
  138. package/dist/bin/commands/type-check.d.ts.map +0 -1
  139. package/dist/bin/commands/type-check.js.map +0 -1
  140. package/dist/bin/commands/wai.d.ts +0 -11
  141. package/dist/bin/commands/wai.d.ts.map +0 -1
  142. package/dist/bin/commands/wai.js.map +0 -1
  143. package/dist/index.d.ts +0 -8
  144. package/dist/index.d.ts.map +0 -1
  145. package/dist/index.js.map +0 -1
  146. package/templates/app-demo/src/components/ThemeToggle.tsx.example +0 -48
  147. package/templates/app-demo/src/pages/DetailPage.tsx.example +0 -103
  148. package/templates/app-demo/src/pages/FullPage.tsx.example +0 -142
  149. package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +0 -266
  150. package/templates/app-demo/src/pages/components/LayoutRoute.tsx.example +0 -20
@@ -281,7 +281,7 @@ function fD({ input: e2 = j, output: u2 = M, overwrite: t = true, hideCursor: F2
281
281
  e2.off("keypress", i), F2 && u2.write(import_sisteransi.cursor.show), e2.isTTY && !AD && e2.setRawMode(false), s.terminal = false, s.close();
282
282
  };
283
283
  }
284
- var import_sisteransi, import_picocolors, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, dD, A, kD, $D, H, SD, TD, jD, U, MD, OD, PD, J, LD, RD;
284
+ var import_sisteransi, import_picocolors, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, dD, A, OD, PD, J, LD, RD;
285
285
  var init_dist = __esm({
286
286
  "node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/node_modules/@clack/core/dist/index.mjs"() {
287
287
  init_utils();
@@ -519,63 +519,6 @@ var init_dist = __esm({
519
519
  }
520
520
  };
521
521
  A = /* @__PURE__ */ new WeakMap();
522
- kD = Object.defineProperty;
523
- $D = (e2, u2, t) => u2 in e2 ? kD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
524
- H = (e2, u2, t) => ($D(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
525
- SD = class extends x {
526
- constructor(u2) {
527
- super(u2, false), H(this, "options"), H(this, "cursor", 0), this.options = u2.options, this.value = [...u2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: t }) => t === u2.cursorAt), 0), this.on("key", (t) => {
528
- t === "a" && this.toggleAll();
529
- }), this.on("cursor", (t) => {
530
- switch (t) {
531
- case "left":
532
- case "up":
533
- this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
534
- break;
535
- case "down":
536
- case "right":
537
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
538
- break;
539
- case "space":
540
- this.toggleValue();
541
- break;
542
- }
543
- });
544
- }
545
- get _value() {
546
- return this.options[this.cursor].value;
547
- }
548
- toggleAll() {
549
- const u2 = this.value.length === this.options.length;
550
- this.value = u2 ? [] : this.options.map((t) => t.value);
551
- }
552
- toggleValue() {
553
- const u2 = this.value.includes(this._value);
554
- this.value = u2 ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
555
- }
556
- };
557
- TD = Object.defineProperty;
558
- jD = (e2, u2, t) => u2 in e2 ? TD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
559
- U = (e2, u2, t) => (jD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
560
- MD = class extends x {
561
- constructor({ mask: u2, ...t }) {
562
- super(t), U(this, "valueWithCursor", ""), U(this, "_mask", "\u2022"), this._mask = u2 ?? "\u2022", this.on("finalize", () => {
563
- this.valueWithCursor = this.masked;
564
- }), this.on("value", () => {
565
- if (this.cursor >= this.value.length) this.valueWithCursor = `${this.masked}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`;
566
- else {
567
- const F2 = this.masked.slice(0, this.cursor), s = this.masked.slice(this.cursor);
568
- this.valueWithCursor = `${F2}${import_picocolors.default.inverse(s[0])}${s.slice(1)}`;
569
- }
570
- });
571
- }
572
- get cursor() {
573
- return this._cursor;
574
- }
575
- get masked() {
576
- return this.value.replaceAll(/./g, this._mask);
577
- }
578
- };
579
522
  OD = Object.defineProperty;
580
523
  PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
581
524
  J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
@@ -627,7 +570,7 @@ import y2 from "node:process";
627
570
  function ce() {
628
571
  return y2.platform !== "win32" ? y2.env.TERM !== "linux" : !!y2.env.CI || !!y2.env.WT_SESSION || !!y2.env.TERMINUS_SUBLIME || y2.env.ConEmuTask === "{cmd::Cmder}" || y2.env.TERM_PROGRAM === "Terminus-Sublime" || y2.env.TERM_PROGRAM === "vscode" || y2.env.TERM === "xterm-256color" || y2.env.TERM === "alacritty" || y2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
629
572
  }
630
- var import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2, G2, he, ge, ye, ve, fe, Me, xe, Ie, Se, M2, J2, Y2;
573
+ var import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2, G2, he, ye, ve, Me, xe, Ie, Se, M2, J2, Y2;
631
574
  var init_dist2 = __esm({
632
575
  "node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/dist/index.mjs"() {
633
576
  init_utils();
@@ -656,7 +599,7 @@ var init_dist2 = __esm({
656
599
  pe = u("\u256F", "+");
657
600
  q = u("\u25CF", "\u2022");
658
601
  D = u("\u25C6", "*");
659
- U2 = u("\u25B2", "!");
602
+ U = u("\u25B2", "!");
660
603
  K2 = u("\u25A0", "x");
661
604
  b2 = (t) => {
662
605
  switch (t) {
@@ -699,27 +642,6 @@ ${import_picocolors2.default.gray(o)}` : ""}`;
699
642
  default:
700
643
  return `${n}${import_picocolors2.default.cyan(o)} ${i}
701
644
  ${import_picocolors2.default.cyan(d2)}
702
- `;
703
- }
704
- } }).prompt();
705
- ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
706
- const n = `${import_picocolors2.default.gray(o)}
707
- ${b2(this.state)} ${t.message}
708
- `, r2 = this.valueWithCursor, i = this.masked;
709
- switch (this.state) {
710
- case "error":
711
- return `${n.trim()}
712
- ${import_picocolors2.default.yellow(o)} ${i}
713
- ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)}
714
- `;
715
- case "submit":
716
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(i)}`;
717
- case "cancel":
718
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i ?? ""))}${i ? `
719
- ${import_picocolors2.default.gray(o)}` : ""}`;
720
- default:
721
- return `${n}${import_picocolors2.default.cyan(o)} ${r2}
722
- ${import_picocolors2.default.cyan(d2)}
723
645
  `;
724
646
  }
725
647
  } }).prompt();
@@ -770,46 +692,6 @@ ${import_picocolors2.default.gray(o)}`;
770
692
  return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ cursor: this.cursor, options: this.options, maxItems: t.maxItems, style: (i, s) => n(i, s ? "active" : "inactive") }).join(`
771
693
  ${import_picocolors2.default.cyan(o)} `)}
772
694
  ${import_picocolors2.default.cyan(d2)}
773
- `;
774
- }
775
- } }).prompt();
776
- };
777
- fe = (t) => {
778
- const n = (r2, i) => {
779
- const s = r2.label ?? String(r2.value);
780
- return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
781
- };
782
- return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, validate(r2) {
783
- if (this.required && r2.length === 0) return `Please select at least one option.
784
- ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
785
- }, render() {
786
- const r2 = `${import_picocolors2.default.gray(o)}
787
- ${b2(this.state)} ${t.message}
788
- `, i = (s, c) => {
789
- const a = this.value.includes(s.value);
790
- return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
791
- };
792
- switch (this.state) {
793
- case "submit":
794
- return `${r2}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
795
- case "cancel": {
796
- const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
797
- return `${r2}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
798
- ${import_picocolors2.default.gray(o)}` : ""}`;
799
- }
800
- case "error": {
801
- const s = this.error.split(`
802
- `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
803
- `);
804
- return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
805
- ${import_picocolors2.default.yellow(o)} `)}
806
- ${s}
807
- `;
808
- }
809
- default:
810
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
811
- ${import_picocolors2.default.cyan(o)} `)}
812
- ${import_picocolors2.default.cyan(d2)}
813
695
  `;
814
696
  }
815
697
  } }).prompt();
@@ -861,7 +743,7 @@ ${import_picocolors2.default.gray(d2)} ${t}
861
743
  }, step: (t) => {
862
744
  M2.message(t, { symbol: import_picocolors2.default.green(C) });
863
745
  }, warn: (t) => {
864
- M2.message(t, { symbol: import_picocolors2.default.yellow(U2) });
746
+ M2.message(t, { symbol: import_picocolors2.default.yellow(U) });
865
747
  }, warning: (t) => {
866
748
  M2.warn(t);
867
749
  }, error: (t) => {
@@ -888,7 +770,7 @@ ${import_picocolors2.default.gray(d2)} ${t}
888
770
  }, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
889
771
  const h2 = (performance.now() - m2) / 1e3, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
890
772
  return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
891
- }, H2 = (m2 = "") => {
773
+ }, H = (m2 = "") => {
892
774
  a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
893
775
  `);
894
776
  let h2 = 0, w2 = 0;
@@ -911,7 +793,7 @@ ${import_picocolors2.default.gray(d2)} ${t}
911
793
  `) : process.stdout.write(`${w2} ${l2}
912
794
  `), E(), s();
913
795
  };
914
- return { start: H2, stop: N2, message: (m2 = "") => {
796
+ return { start: H, stop: N2, message: (m2 = "") => {
915
797
  l2 = R2(m2 ?? l2);
916
798
  } };
917
799
  };
@@ -8361,24 +8243,6 @@ async function askForSelection(message, choices, defaultValue = 0) {
8361
8243
  }
8362
8244
  return result;
8363
8245
  }
8364
- async function askForMultiSelection(message, choices, defaultIndices = []) {
8365
- const options = choices.map((choice) => ({
8366
- value: choice.value,
8367
- label: choice.title,
8368
- hint: choice.hint
8369
- }));
8370
- const initialValues = defaultIndices.map((i) => choices[i]?.value).filter(Boolean);
8371
- const result = await fe({
8372
- message,
8373
- options,
8374
- initialValues: initialValues.length > 0 ? initialValues : void 0
8375
- });
8376
- if (pD(result)) {
8377
- xe("Operation cancelled.");
8378
- process.exit(0);
8379
- }
8380
- return result;
8381
- }
8382
8246
  var init_cli_input = __esm({
8383
8247
  "packages/tooling/src/utils/cli-input.ts"() {
8384
8248
  "use strict";
@@ -8405,14 +8269,27 @@ function readEnvVar(filePath, varName) {
8405
8269
  }
8406
8270
  return null;
8407
8271
  }
8408
- function resolveVercelToken(appDir) {
8409
- if (process.env.VERCEL_TOKEN) return process.env.VERCEL_TOKEN;
8410
- const fromEnv = readEnvVar(joinPath(appDir, ".env"), "VERCEL_TOKEN");
8411
- if (fromEnv) return fromEnv;
8412
- const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), "VERCEL_TOKEN");
8272
+ function resolveVercelVar(appDir, varName) {
8273
+ if (process.env[varName]) return process.env[varName];
8274
+ const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), varName);
8413
8275
  if (fromLocal) return fromLocal;
8276
+ const fromEnv = readEnvVar(joinPath(appDir, ".env"), varName);
8277
+ if (fromEnv) return fromEnv;
8414
8278
  return null;
8415
8279
  }
8280
+ function resolveVercelCredentials(appDir) {
8281
+ const token = resolveVercelVar(appDir, "VERCEL_TOKEN");
8282
+ const orgId = resolveVercelVar(appDir, "VERCEL_ORG_ID");
8283
+ const projectId = resolveVercelVar(appDir, "VERCEL_PROJECT_ID");
8284
+ const missing = [];
8285
+ if (!token) missing.push("VERCEL_TOKEN");
8286
+ if (!orgId) missing.push("VERCEL_ORG_ID");
8287
+ if (!projectId) missing.push("VERCEL_PROJECT_ID");
8288
+ if (missing.length > 0) return { missing };
8289
+ return {
8290
+ credentials: { token, orgId, projectId }
8291
+ };
8292
+ }
8416
8293
  var init_vercel_token = __esm({
8417
8294
  "packages/tooling/src/cli/setup/vercel-token.ts"() {
8418
8295
  "use strict";
@@ -8444,6 +8321,67 @@ var init_error_handling = __esm({
8444
8321
  }
8445
8322
  });
8446
8323
 
8324
+ // packages/tooling/src/utils/cross-app-detection.ts
8325
+ function detectBackendFromProviders(appDir) {
8326
+ for (const candidate of PROVIDERS_CANDIDATES) {
8327
+ const filePath = joinPath(appDir, candidate);
8328
+ if (!pathExists(filePath)) continue;
8329
+ const content = readSync(filePath, { format: "text" });
8330
+ if (typeof content !== "string") continue;
8331
+ if (content.includes("@donotdev/firebase") || content.includes("FirebaseAuth") || content.includes("firebaseAuth")) {
8332
+ return "firebase";
8333
+ }
8334
+ if (content.includes("@donotdev/supabase") || content.includes("SupabaseAuth") || content.includes("supabaseAuth")) {
8335
+ return "supabase";
8336
+ }
8337
+ break;
8338
+ }
8339
+ return null;
8340
+ }
8341
+ function analyzeProjectTopology(projectRoot) {
8342
+ const apps = detectApps(projectRoot);
8343
+ const appBackends = /* @__PURE__ */ new Map();
8344
+ const backendOwners = /* @__PURE__ */ new Map();
8345
+ for (const app of apps) {
8346
+ let backend = app.platform === "firebase" || app.platform === "supabase" ? app.platform : null;
8347
+ if (!backend) {
8348
+ backend = detectBackendFromProviders(app.path);
8349
+ }
8350
+ appBackends.set(app.name, backend);
8351
+ if (pathExists(joinPath(app.path, "supabase", "config.toml"))) {
8352
+ backendOwners.set("supabase", app);
8353
+ }
8354
+ if (pathExists(joinPath(app.path, "firebase.json")) || pathExists(joinPath(app.path, "functions", "firebase.json"))) {
8355
+ backendOwners.set("firebase", app);
8356
+ }
8357
+ }
8358
+ if (!backendOwners.has("firebase") && (pathExists(joinPath(projectRoot, "firebase.json")) || pathExists(joinPath(projectRoot, ".firebaserc")))) {
8359
+ }
8360
+ return { apps, appBackends, backendOwners };
8361
+ }
8362
+ function findBackendApp(topology, appName) {
8363
+ const backend = topology.appBackends.get(appName);
8364
+ if (!backend) return void 0;
8365
+ return topology.backendOwners.get(backend);
8366
+ }
8367
+ var PROVIDERS_CANDIDATES;
8368
+ var init_cross_app_detection = __esm({
8369
+ "packages/tooling/src/utils/cross-app-detection.ts"() {
8370
+ "use strict";
8371
+ init_utils();
8372
+ init_app_detection();
8373
+ init_pathResolver();
8374
+ PROVIDERS_CANDIDATES = [
8375
+ "src/config/providers.ts",
8376
+ "src/config/providers.tsx",
8377
+ "src/providers.ts",
8378
+ "src/providers.tsx",
8379
+ "src/lib/providers.ts",
8380
+ "src/lib/providers.tsx"
8381
+ ];
8382
+ }
8383
+ });
8384
+
8447
8385
  // packages/tooling/src/cli/setup/types.ts
8448
8386
  function computeOverallStatus(steps) {
8449
8387
  const hasFailure = steps.some((s) => s.status === "failed");
@@ -8523,7 +8461,7 @@ function isFirebaseInstalled() {
8523
8461
  async function checkPrerequisites() {
8524
8462
  if (!isFirebaseInstalled()) {
8525
8463
  log.error("Firebase CLI is not installed.");
8526
- log.info("Install it with: npm install -g firebase-tools");
8464
+ log.info("Install it with: bun install -g firebase-tools");
8527
8465
  process.exit(1);
8528
8466
  }
8529
8467
  if (!isFirebaseLoggedIn()) {
@@ -8974,15 +8912,40 @@ var init_firebase = __esm({
8974
8912
  });
8975
8913
 
8976
8914
  // packages/tooling/src/utils/supabase-management.ts
8977
- import { execSync } from "node:child_process";
8978
- function extractProjectRef(supabaseUrl) {
8979
- const match = supabaseUrl.match(/https?:\/\/([a-z0-9]+)\.supabase\.co/);
8980
- if (!match?.[1]) {
8981
- throw new Error(
8982
- `Cannot extract project ref from URL: ${supabaseUrl}. Expected format: https://<ref>.supabase.co`
8983
- );
8915
+ import { execSync, spawnSync as spawnSync2 } from "node:child_process";
8916
+ function stripQuotes(value) {
8917
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
8918
+ return value.slice(1, -1);
8919
+ }
8920
+ return value;
8921
+ }
8922
+ function detectDbUrl(appDir) {
8923
+ const envPaths = [
8924
+ joinPath(appDir, "supabase", "functions", ".env"),
8925
+ joinPath(appDir, "functions", ".env"),
8926
+ joinPath(appDir, ".env.local"),
8927
+ joinPath(appDir, ".env")
8928
+ ];
8929
+ for (const envPath of envPaths) {
8930
+ if (!pathExists(envPath)) continue;
8931
+ try {
8932
+ const content = readSync(envPath);
8933
+ if (!content) continue;
8934
+ for (const line of content.split("\n")) {
8935
+ const trimmed = line.trim();
8936
+ if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
8937
+ const eqIdx = trimmed.indexOf("=");
8938
+ const key = trimmed.slice(0, eqIdx).trim();
8939
+ const value = stripQuotes(trimmed.slice(eqIdx + 1).trim());
8940
+ if (key === "SUPABASE_DB_URL" && value) {
8941
+ return value;
8942
+ }
8943
+ }
8944
+ } catch {
8945
+ continue;
8946
+ }
8984
8947
  }
8985
- return match[1];
8948
+ return null;
8986
8949
  }
8987
8950
  async function cleanupOldEntityMigrations(supabaseDir, latestFile) {
8988
8951
  const migrationsDir = joinPath(supabaseDir, "migrations");
@@ -9040,35 +9003,38 @@ async function executeMigration(options) {
9040
9003
  const projectRoot = dirname4(supabaseDir);
9041
9004
  try {
9042
9005
  const cmd = getSupabaseCmd();
9043
- execSync(`${cmd} db push --include-all`, {
9006
+ const parts = cmd.split(" ");
9007
+ const bin = parts[0];
9008
+ const baseArgs = [
9009
+ ...parts.slice(1),
9010
+ "db",
9011
+ "push",
9012
+ "--include-all",
9013
+ "--db-url",
9014
+ options.dbUrl
9015
+ ];
9016
+ const result = spawnSync2(bin, baseArgs, {
9044
9017
  cwd: projectRoot,
9045
9018
  stdio: ["pipe", "pipe", "pipe"],
9046
9019
  input: "Y\n",
9047
9020
  env: { ...process.env }
9048
9021
  });
9022
+ if (result.status !== 0) {
9023
+ const stderr = result.stderr?.toString() || "";
9024
+ throw new Error(stderr || `Process exited with code ${result.status}`);
9025
+ }
9049
9026
  } catch (error2) {
9050
- const message = error2 instanceof Error ? error2.message : String(error2);
9027
+ const raw = error2 instanceof Error ? error2.message : String(error2);
9028
+ const sanitized = raw.replace(
9029
+ /postgresql:\/\/[^\s"']*/g,
9030
+ "postgresql://***"
9031
+ );
9051
9032
  throw new Error(
9052
- `Failed to push migrations: ${message}
9053
- Apply manually: copy the generated .sql content into the Supabase Dashboard SQL Editor.`
9033
+ `Failed to push migrations: ${sanitized}
9034
+ Check your SUPABASE_DB_URL (format: postgresql://postgres:<password>@db.<ref>.supabase.co:5432/postgres)`
9054
9035
  );
9055
9036
  }
9056
9037
  }
9057
- function ensureProjectLinked(appDir, projectRef) {
9058
- const linkMarker = joinPath(appDir, ".supabase");
9059
- if (pathExists(linkMarker)) return;
9060
- const cmd = getSupabaseCmd();
9061
- try {
9062
- execSync(`${cmd} link --project-ref ${projectRef}`, {
9063
- cwd: appDir,
9064
- stdio: "pipe",
9065
- env: { ...process.env }
9066
- });
9067
- } catch (error2) {
9068
- const message = error2 instanceof Error ? error2.message : String(error2);
9069
- throw new Error(`Failed to link Supabase project: ${message}`);
9070
- }
9071
- }
9072
9038
  function getSupabaseCmd() {
9073
9039
  try {
9074
9040
  execSync("supabase --version", { stdio: "pipe" });
@@ -9694,6 +9660,9 @@ function roleToSqlCondition(role) {
9694
9660
  case "super":
9695
9661
  return "(auth.jwt()->'app_metadata'->>'role') = 'super'";
9696
9662
  default:
9663
+ log.warn(
9664
+ `Unknown access role "${role}" \u2014 defaulting to deny (false). Use guest|user|admin|super.`
9665
+ );
9697
9666
  return "false";
9698
9667
  }
9699
9668
  }
@@ -10288,30 +10257,32 @@ async function main3(options = {}) {
10288
10257
  const supabaseUrl = existingConfig.url;
10289
10258
  log.success(`Supabase URL: ${supabaseUrl}`);
10290
10259
  const secretKey = detectSecretKey(appDir);
10291
- if (secretKey) {
10292
- log.success("Secret key detected");
10293
- } else {
10260
+ const dbUrl = detectDbUrl(appDir);
10261
+ if (secretKey) log.success("Secret key detected");
10262
+ else log.error("Missing SUPABASE_SECRET_KEY in supabase/functions/.env");
10263
+ if (dbUrl) log.success("DB URL detected");
10264
+ else log.error("Missing SUPABASE_DB_URL in supabase/functions/.env");
10265
+ if (!secretKey || !dbUrl) {
10294
10266
  const functionsEnvPath = joinPath(supabaseDir, "functions", ".env");
10267
+ const missing = [];
10268
+ if (!secretKey) missing.push(" SUPABASE_SECRET_KEY=your-service-role-key");
10269
+ if (!dbUrl)
10270
+ missing.push(
10271
+ " SUPABASE_DB_URL=postgresql://postgres:...@db.<ref>.supabase.co:5432/postgres"
10272
+ );
10295
10273
  Me(
10296
10274
  [
10297
10275
  `Fill in ${functionsEnvPath}:`,
10298
- " SUPABASE_SECRET_KEY=your-service-role-key",
10276
+ ...missing,
10299
10277
  "",
10300
- "Get this from: https://supabase.com/dashboard > Settings > API > service_role key",
10278
+ "Get these from: https://supabase.com/dashboard > Settings > Database",
10301
10279
  "",
10302
- "Without it, migrations cannot be pushed to the remote database."
10280
+ "Then re-run: dndev setup supabase"
10303
10281
  ].join("\n"),
10304
- "Missing Secret Key"
10305
- );
10306
- }
10307
- try {
10308
- const projectRef = extractProjectRef(supabaseUrl);
10309
- ensureProjectLinked(appDir, projectRef);
10310
- log.success("Supabase CLI linked");
10311
- } catch (error2) {
10312
- log.warn(
10313
- `Could not link Supabase project: ${error2 instanceof Error ? error2.message : String(error2)}`
10282
+ "Missing Credentials"
10314
10283
  );
10284
+ Se("Setup aborted.");
10285
+ return;
10315
10286
  }
10316
10287
  const entitiesDir = joinPath(projectRoot, "entities");
10317
10288
  const hasEntities = pathExists(entitiesDir) && pathExists(joinPath(entitiesDir, "index.ts"));
@@ -10364,25 +10335,21 @@ async function main3(options = {}) {
10364
10335
  }
10365
10336
  }
10366
10337
  let migrationsPushed = false;
10367
- if (!secretKey) {
10368
- log.info("Skipping migration push (no secret key).");
10369
- } else {
10370
- const shouldPush = await askForConfirmation(
10371
- "Push migrations to remote database?",
10372
- true
10373
- );
10374
- if (shouldPush) {
10375
- const s = Y2();
10376
- s.start("Pushing migrations...");
10377
- try {
10378
- await executeMigration({ supabaseDir });
10379
- s.stop("Migrations pushed successfully");
10380
- migrationsPushed = true;
10381
- } catch (error2) {
10382
- s.stop("Failed to push migrations");
10383
- log.error(`${error2 instanceof Error ? error2.message : String(error2)}`);
10384
- log.info("Apply manually via the Supabase Dashboard SQL Editor.");
10385
- }
10338
+ const shouldPush = await askForConfirmation(
10339
+ "Push migrations to remote database?",
10340
+ true
10341
+ );
10342
+ if (shouldPush) {
10343
+ const s = Y2();
10344
+ s.start("Pushing migrations...");
10345
+ try {
10346
+ await executeMigration({ supabaseDir, dbUrl });
10347
+ s.stop("Migrations pushed successfully");
10348
+ migrationsPushed = true;
10349
+ } catch (error2) {
10350
+ s.stop("Failed to push migrations");
10351
+ log.error(`${error2 instanceof Error ? error2.message : String(error2)}`);
10352
+ log.info("Apply manually via the Supabase Dashboard SQL Editor.");
10386
10353
  }
10387
10354
  }
10388
10355
  try {
@@ -10441,96 +10408,121 @@ var init_supabase = __esm({
10441
10408
  async run(ctx) {
10442
10409
  const steps = [];
10443
10410
  const framework = ctx.app?.framework === "nextjs" ? "nextjs" : "vite";
10411
+ const supabaseDir = joinPath(ctx.appDir, "supabase");
10444
10412
  const existingConfig = detectPublicConfig(ctx.appDir, framework);
10445
10413
  if (!existingConfig) {
10446
- const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
10447
- const envPath = joinPath(ctx.appDir, ".env");
10448
- Me(
10449
- [
10450
- `Fill in ${envPath}:`,
10451
- ` ${prefix}SUPABASE_URL=https://your-project.supabase.co`,
10452
- ` ${prefix}SUPABASE_PUBLIC_KEY=sb_publishable_...`,
10453
- "",
10454
- "Get these from: https://supabase.com/dashboard > Settings > API",
10455
- "",
10456
- "Then re-run: dndev setup supabase"
10457
- ].join("\n"),
10458
- "Missing Public Credentials"
10459
- );
10460
10414
  steps.push({
10461
10415
  name: "Credentials",
10462
10416
  status: "failed",
10463
- message: "Missing Supabase credentials in .env",
10464
- coachingTopic: "supabase-credentials"
10417
+ message: "Missing Supabase URL or public key in .env"
10465
10418
  });
10466
10419
  return { provider: "supabase", steps, overallStatus: "failed" };
10467
10420
  }
10468
- const supabaseUrl = existingConfig.url;
10469
- steps.push({
10470
- name: "Public credentials",
10471
- status: "success",
10472
- message: `URL: ${supabaseUrl}`
10473
- });
10474
10421
  const secretKey = detectSecretKey(ctx.appDir);
10475
- if (secretKey) {
10422
+ const dbUrl = detectDbUrl(ctx.appDir);
10423
+ const missingSecrets = [];
10424
+ if (!secretKey) missingSecrets.push("SUPABASE_SECRET_KEY");
10425
+ if (!dbUrl) missingSecrets.push("SUPABASE_DB_URL");
10426
+ if (missingSecrets.length > 0) {
10476
10427
  steps.push({
10477
- name: "Secret key",
10478
- status: "success",
10479
- message: "Detected in functions/.env"
10428
+ name: "Credentials",
10429
+ status: "failed",
10430
+ message: `Missing in functions/.env: ${missingSecrets.join(", ")}`
10480
10431
  });
10432
+ return { provider: "supabase", steps, overallStatus: "failed" };
10433
+ }
10434
+ steps.push({
10435
+ name: "Credentials",
10436
+ status: "success",
10437
+ message: `${existingConfig.url}`
10438
+ });
10439
+ const entitiesDir = joinPath(ctx.projectRoot, "entities");
10440
+ const hasEntities = pathExists(entitiesDir) && pathExists(joinPath(entitiesDir, "index.ts"));
10441
+ let generatedEntityFile = null;
10442
+ if (hasEntities) {
10443
+ try {
10444
+ const { SqlGenerator: SqlGenerator2 } = await Promise.resolve().then(() => (init_sql_generator(), sql_generator_exports));
10445
+ const generator = new SqlGenerator2({
10446
+ entityDir: entitiesDir,
10447
+ outputDir: joinPath(supabaseDir, "migrations"),
10448
+ singleFile: true
10449
+ });
10450
+ const s = Y2();
10451
+ s.start("Generating SQL from entities...");
10452
+ const result = await generator.run();
10453
+ if (result.success) {
10454
+ s.stop("SQL generated");
10455
+ generatedEntityFile = result.generatedFiles[0] ?? null;
10456
+ steps.push({
10457
+ name: "SQL generation",
10458
+ status: "success",
10459
+ message: `${result.filesGenerated} migration file(s)`
10460
+ });
10461
+ } else {
10462
+ s.stop("SQL generation failed");
10463
+ steps.push({
10464
+ name: "SQL generation",
10465
+ status: "failed",
10466
+ message: "Generator returned failure"
10467
+ });
10468
+ }
10469
+ } catch (error2) {
10470
+ steps.push({
10471
+ name: "SQL generation",
10472
+ status: "failed",
10473
+ message: getErrorMessage(error2)
10474
+ });
10475
+ }
10481
10476
  } else {
10482
- const supabaseDir = joinPath(ctx.appDir, "supabase");
10483
- const functionsEnvPath = joinPath(supabaseDir, "functions", ".env");
10484
- Me(
10485
- [
10486
- `Fill in ${functionsEnvPath}:`,
10487
- " SUPABASE_SECRET_KEY=your-service-role-key",
10488
- "",
10489
- "Get this from: https://supabase.com/dashboard > Settings > API > service_role key"
10490
- ].join("\n"),
10491
- "Missing Secret Key"
10492
- );
10493
10477
  steps.push({
10494
- name: "Secret key",
10495
- status: "needs-manual",
10496
- message: "Secret key not found \u2014 needed for migrations",
10497
- coachingTopic: "supabase-secret-key"
10478
+ name: "SQL generation",
10479
+ status: "skipped",
10480
+ message: "No entities/ directory"
10498
10481
  });
10499
10482
  }
10500
- if (!ctx.dryRun) {
10483
+ if (generatedEntityFile) {
10484
+ try {
10485
+ await cleanupOldEntityMigrations(supabaseDir, generatedEntityFile);
10486
+ } catch {
10487
+ }
10488
+ }
10489
+ const migrationsDir = joinPath(supabaseDir, "migrations");
10490
+ if (pathExists(migrationsDir)) {
10491
+ const s = Y2();
10492
+ s.start("Pushing migrations...");
10501
10493
  try {
10502
- const projectRef = extractProjectRef(supabaseUrl);
10503
- ensureProjectLinked(ctx.appDir, projectRef);
10494
+ await executeMigration({ supabaseDir, dbUrl });
10495
+ s.stop("Migrations pushed");
10504
10496
  steps.push({
10505
- name: "CLI link",
10497
+ name: "DB migrations",
10506
10498
  status: "success",
10507
- message: "Supabase CLI linked"
10499
+ message: "All migrations applied"
10508
10500
  });
10509
- } catch (err) {
10501
+ } catch (error2) {
10502
+ s.stop("Migration push failed");
10510
10503
  steps.push({
10511
- name: "CLI link",
10504
+ name: "DB migrations",
10512
10505
  status: "failed",
10513
- message: getErrorMessage(err)
10506
+ message: getErrorMessage(error2)
10507
+ });
10508
+ }
10509
+ }
10510
+ if (hasEntities) {
10511
+ try {
10512
+ await generateCrudFunction(supabaseDir, ctx.projectRoot);
10513
+ steps.push({
10514
+ name: "CRUD function",
10515
+ status: "success",
10516
+ message: "Entity imports patched"
10517
+ });
10518
+ } catch (error2) {
10519
+ steps.push({
10520
+ name: "CRUD function",
10521
+ status: "failed",
10522
+ message: getErrorMessage(error2)
10514
10523
  });
10515
10524
  }
10516
- } else {
10517
- steps.push({ name: "CLI link", status: "skipped", message: "Dry run" });
10518
10525
  }
10519
- Me(
10520
- [
10521
- "If you use custom RLS policies beyond the defaults,",
10522
- "review them in the Supabase Dashboard:",
10523
- "",
10524
- "https://supabase.com/dashboard > Authentication > Policies"
10525
- ].join("\n"),
10526
- "Row Level Security"
10527
- );
10528
- steps.push({
10529
- name: "RLS policies",
10530
- status: "needs-manual",
10531
- message: "Review custom RLS policies in dashboard",
10532
- coachingTopic: "supabase-rls"
10533
- });
10534
10526
  return {
10535
10527
  provider: "supabase",
10536
10528
  steps,
@@ -10546,27 +10538,13 @@ var vercel_exports = {};
10546
10538
  __export(vercel_exports, {
10547
10539
  vercelWizard: () => vercelWizard
10548
10540
  });
10549
- import { spawnSync as spawnSync2 } from "node:child_process";
10550
- function isVercelInstalled() {
10551
- try {
10552
- const result = spawnSync2("vercel", ["--version"], {
10553
- stdio: "pipe",
10554
- encoding: "utf-8"
10555
- });
10556
- return result.status === 0;
10557
- } catch {
10558
- return false;
10559
- }
10560
- }
10561
10541
  var vercelWizard;
10562
10542
  var init_vercel = __esm({
10563
10543
  "packages/tooling/src/cli/setup/vercel.ts"() {
10564
10544
  "use strict";
10565
10545
  init_utils();
10566
10546
  init_cli_output();
10567
- init_cli_output();
10568
10547
  init_pathResolver();
10569
- init_error_handling();
10570
10548
  init_types();
10571
10549
  init_vercel_token();
10572
10550
  vercelWizard = {
@@ -10577,108 +10555,22 @@ var init_vercel = __esm({
10577
10555
  },
10578
10556
  async run(ctx) {
10579
10557
  const steps = [];
10580
- if (!isVercelInstalled()) {
10581
- log.error("Vercel CLI is not installed.");
10582
- log.info("Install it with: npm install -g vercel");
10583
- steps.push({
10584
- name: "Vercel CLI",
10585
- status: "failed",
10586
- message: "Not installed. Run: npm install -g vercel"
10587
- });
10588
- return { provider: "vercel", steps, overallStatus: "failed" };
10589
- }
10590
- steps.push({ name: "Vercel CLI", status: "success", message: "Installed" });
10591
- const token = resolveVercelToken(ctx.appDir);
10592
- if (token) {
10593
- log.success("VERCEL_TOKEN detected \u2014 no login required");
10558
+ const result = resolveVercelCredentials(ctx.appDir);
10559
+ if (result.credentials) {
10560
+ log.success("Vercel credentials detected");
10594
10561
  steps.push({
10595
- name: "Auth",
10562
+ name: "Credentials",
10596
10563
  status: "success",
10597
- message: "Token-based auth (VERCEL_TOKEN)"
10598
- });
10599
- } else {
10600
- Me(
10601
- [
10602
- "No VERCEL_TOKEN found. Add to .env or .env.local:",
10603
- " VERCEL_TOKEN=your_token_here",
10604
- "",
10605
- "Your customer generates it at:",
10606
- " https://vercel.com/account/tokens",
10607
- " \u2192 Scope to their team for isolation",
10608
- "",
10609
- "This way you deploy to THEIR Vercel without logging in."
10610
- ].join("\n"),
10611
- "Vercel Authentication"
10612
- );
10613
- steps.push({
10614
- name: "Auth",
10615
- status: "needs-manual",
10616
- message: "Drop VERCEL_TOKEN in .env (get it from customer)",
10617
- coachingTopic: "vercel-token"
10618
- });
10619
- }
10620
- if (token && !ctx.dryRun) {
10621
- const s = Y2();
10622
- s.start("Linking Vercel project...");
10623
- try {
10624
- const result = spawnSync2(
10625
- "vercel",
10626
- ["link", "--yes", "--token", token],
10627
- {
10628
- cwd: ctx.appDir,
10629
- stdio: ["inherit", "pipe", "pipe"],
10630
- encoding: "utf-8",
10631
- timeout: 3e4
10632
- }
10633
- );
10634
- if (result.status === 0) {
10635
- s.stop("Vercel project linked");
10636
- steps.push({
10637
- name: "Project link",
10638
- status: "success",
10639
- message: "Linked to Vercel project"
10640
- });
10641
- } else {
10642
- s.stop("Vercel link failed");
10643
- const stderr = result.stderr?.trim() || "Unknown error";
10644
- steps.push({
10645
- name: "Project link",
10646
- status: "failed",
10647
- message: stderr
10648
- });
10649
- }
10650
- } catch (err) {
10651
- s.stop("Vercel link failed");
10652
- steps.push({
10653
- name: "Project link",
10654
- status: "failed",
10655
- message: getErrorMessage(err)
10656
- });
10657
- }
10658
- } else if (!token) {
10659
- steps.push({
10660
- name: "Project link",
10661
- status: "skipped",
10662
- message: "No token \u2014 cannot link"
10564
+ message: "VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID found"
10663
10565
  });
10664
10566
  } else {
10567
+ log.error(`Missing in .env.local: ${result.missing.join(", ")}`);
10665
10568
  steps.push({
10666
- name: "Project link",
10667
- status: "skipped",
10668
- message: "Dry run"
10569
+ name: "Credentials",
10570
+ status: "failed",
10571
+ message: `Missing: ${result.missing.join(", ")}`
10669
10572
  });
10670
10573
  }
10671
- Me(
10672
- [
10673
- "Once VERCEL_TOKEN is in .env:",
10674
- " dndev deploy \u2192 picks it up automatically",
10675
- " dndev deploy:web \u2192 deploy specific app",
10676
- "",
10677
- "deploy.ts reads vercel.json + injects the token.",
10678
- 'No separate "vercel deploy" needed.'
10679
- ].join("\n"),
10680
- "Deployment"
10681
- );
10682
10574
  return {
10683
10575
  provider: "vercel",
10684
10576
  steps,
@@ -10689,868 +10581,77 @@ var init_vercel = __esm({
10689
10581
  }
10690
10582
  });
10691
10583
 
10692
- // packages/tooling/src/cli/setup/stripe.ts
10693
- var stripe_exports = {};
10694
- __export(stripe_exports, {
10695
- stripeWizard: () => stripeWizard
10696
- });
10697
- function isValidPublishableKey(key) {
10698
- return key.startsWith("pk_test_") || key.startsWith("pk_live_");
10699
- }
10700
- function isValidSecretKey(key) {
10701
- return key.startsWith("sk_test_") || key.startsWith("sk_live_") || key.startsWith("rk_test_") || key.startsWith("rk_live_");
10702
- }
10703
- function isValidWebhookSecret(key) {
10704
- return key.startsWith("whsec_");
10705
- }
10706
- function readEnvValue(envPath, varName) {
10707
- if (!pathExists(envPath)) return null;
10708
- const content = readSync(envPath, { format: "text" });
10709
- if (typeof content !== "string") return null;
10710
- for (const line of content.split(/\r?\n/)) {
10711
- const trimmed = line.trim();
10712
- if (!trimmed || trimmed.startsWith("#")) continue;
10713
- if (trimmed.startsWith(`${varName}=`)) {
10714
- return trimmed.substring(`${varName}=`.length).trim();
10715
- }
10716
- }
10717
- return null;
10718
- }
10719
- function writeEnvVar(envPath, varName, value) {
10720
- let content = "";
10721
- if (pathExists(envPath)) {
10722
- const raw = readSync(envPath, { format: "text" });
10723
- content = typeof raw === "string" ? raw : "";
10724
- }
10725
- const lines = content.split("\n");
10726
- let replaced = false;
10727
- const updated = lines.map((line) => {
10728
- if (line.trim().startsWith(`${varName}=`) || line.trim().startsWith(`# ${varName}=`)) {
10729
- replaced = true;
10730
- return `${varName}=${value}`;
10731
- }
10732
- return line;
10733
- });
10734
- if (!replaced) {
10735
- updated.push(`${varName}=${value}`);
10736
- }
10737
- write(envPath, updated.join("\n"));
10738
- }
10739
- var stripeWizard;
10740
- var init_stripe = __esm({
10741
- "packages/tooling/src/cli/setup/stripe.ts"() {
10742
- "use strict";
10743
- init_utils();
10744
- init_dist2();
10745
- init_cli_output();
10746
- init_cli_output();
10747
- init_pathResolver();
10748
- init_cli_input();
10749
- init_types();
10750
- stripeWizard = {
10751
- id: "stripe",
10752
- name: "Stripe",
10753
- isRelevant(ctx) {
10754
- const envPath = joinPath(ctx.appDir, ".env");
10755
- if (pathExists(envPath)) {
10756
- const content = readSync(envPath, { format: "text" });
10757
- if (typeof content === "string" && content.includes("STRIPE_"))
10758
- return true;
10759
- }
10760
- const pkgPath = joinPath(ctx.appDir, "package.json");
10761
- if (pathExists(pkgPath)) {
10762
- const pkg = readSync(pkgPath, { format: "json" });
10763
- if (pkg && typeof pkg === "object") {
10764
- const deps = {
10765
- ...pkg.dependencies
10766
- };
10767
- if (deps["@donotdev/billing"]) return true;
10768
- }
10769
- }
10770
- return false;
10771
- },
10772
- async run(ctx) {
10773
- const steps = [];
10774
- const framework = ctx.app?.framework === "nextjs" ? "nextjs" : "vite";
10775
- const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
10776
- const envPath = joinPath(ctx.appDir, ".env");
10777
- const existingPk = readEnvValue(envPath, `${prefix}STRIPE_PUBLISHABLE_KEY`);
10778
- let publishableKey = existingPk;
10779
- if (existingPk && isValidPublishableKey(existingPk)) {
10780
- steps.push({
10781
- name: "Publishable key",
10782
- status: "success",
10783
- message: "Found in .env"
10784
- });
10785
- } else {
10786
- const pk = await askForInput(
10787
- "Stripe publishable key (pk_test_... or pk_live_...)",
10788
- ""
10789
- );
10790
- if (isValidPublishableKey(pk)) {
10791
- if (!ctx.dryRun) {
10792
- writeEnvVar(envPath, `${prefix}STRIPE_PUBLISHABLE_KEY`, pk);
10793
- log.success("Wrote STRIPE_PUBLISHABLE_KEY to .env");
10794
- }
10795
- publishableKey = pk;
10796
- steps.push({
10797
- name: "Publishable key",
10798
- status: "success",
10799
- message: "Written to .env"
10800
- });
10801
- } else {
10802
- steps.push({
10803
- name: "Publishable key",
10804
- status: "failed",
10805
- message: "Invalid format \u2014 must start with pk_test_ or pk_live_"
10806
- });
10807
- }
10808
- }
10809
- const functionsEnvPath = joinPath(
10810
- ctx.appDir,
10811
- "supabase",
10812
- "functions",
10813
- ".env"
10814
- );
10815
- const altFunctionsEnvPath = joinPath(ctx.appDir, "functions", ".env");
10816
- const secretEnvPath = pathExists(functionsEnvPath) ? functionsEnvPath : altFunctionsEnvPath;
10817
- const existingSk = readEnvValue(secretEnvPath, "STRIPE_SECRET_KEY");
10818
- if (existingSk && isValidSecretKey(existingSk)) {
10819
- steps.push({
10820
- name: "Secret key",
10821
- status: "success",
10822
- message: "Found in functions/.env"
10823
- });
10824
- } else {
10825
- const sk = await ge({
10826
- message: "Stripe secret key (sk_test_... or sk_live_...):"
10827
- });
10828
- if (typeof sk === "string" && isValidSecretKey(sk)) {
10829
- if (!ctx.dryRun) {
10830
- writeEnvVar(secretEnvPath, "STRIPE_SECRET_KEY", sk);
10831
- log.success("Wrote STRIPE_SECRET_KEY to functions/.env");
10832
- }
10833
- steps.push({
10834
- name: "Secret key",
10835
- status: "success",
10836
- message: "Written to functions/.env"
10837
- });
10838
- } else {
10839
- steps.push({
10840
- name: "Secret key",
10841
- status: "failed",
10842
- message: "STRIPE_SECRET_KEY has invalid format"
10843
- });
10844
- }
10845
- }
10846
- const existingWh = readEnvValue(secretEnvPath, "STRIPE_WEBHOOK_SECRET");
10847
- if (existingWh && isValidWebhookSecret(existingWh)) {
10848
- steps.push({
10849
- name: "Webhook secret",
10850
- status: "success",
10851
- message: "Found in functions/.env"
10852
- });
10853
- } else {
10854
- Me(
10855
- [
10856
- "Set up your Stripe webhook endpoint:",
10857
- "",
10858
- "1. Go to https://dashboard.stripe.com/webhooks",
10859
- '2. Click "Add endpoint"',
10860
- "3. Set URL to your functions endpoint:",
10861
- " e.g., https://your-project.supabase.co/functions/v1/stripe-webhook",
10862
- "4. Select events: checkout.session.completed, customer.subscription.*",
10863
- "5. Copy the webhook signing secret (whsec_...)",
10864
- "",
10865
- `Then add to ${secretEnvPath}:`,
10866
- " STRIPE_WEBHOOK_SECRET=whsec_..."
10867
- ].join("\n"),
10868
- "Webhook Setup"
10869
- );
10870
- steps.push({
10871
- name: "Webhook secret",
10872
- status: "needs-manual",
10873
- message: "Configure webhook endpoint in Stripe Dashboard",
10874
- coachingTopic: "stripe-webhook"
10875
- });
10876
- }
10877
- return {
10878
- provider: "stripe",
10879
- steps,
10880
- overallStatus: computeOverallStatus(steps)
10881
- };
10882
- }
10883
- };
10884
- }
10885
- });
10886
-
10887
- // packages/tooling/src/cli/setup/oauth.ts
10888
- var oauth_exports = {};
10889
- __export(oauth_exports, {
10890
- oauthWizard: () => oauthWizard
10891
- });
10892
- function getRedirectUri(ctx, provider) {
10893
- if (provider === "firebase") {
10894
- const rcPath = joinPath(ctx.projectRoot, ".firebaserc");
10895
- if (pathExists(rcPath)) {
10896
- try {
10897
- const raw = readSync(rcPath, { format: "text" });
10898
- const rc = JSON.parse(typeof raw === "string" ? raw : "{}");
10899
- const projectId = rc?.projects?.default;
10900
- if (projectId) {
10901
- return `https://${projectId}.firebaseapp.com/__/auth/handler`;
10902
- }
10903
- } catch {
10904
- }
10905
- }
10906
- return "https://<YOUR_PROJECT_ID>.firebaseapp.com/__/auth/handler";
10907
- }
10908
- const envPath = joinPath(ctx.appDir, ".env");
10909
- if (pathExists(envPath)) {
10910
- const content = readSync(envPath, { format: "text" });
10911
- if (typeof content === "string") {
10912
- const match = content.match(/(?:VITE_|NEXT_PUBLIC_)SUPABASE_URL=(.+)/);
10913
- if (match?.[1]) {
10914
- return `${match[1].trim()}/auth/v1/callback`;
10915
- }
10916
- }
10917
- }
10918
- return "https://<YOUR_PROJECT_REF>.supabase.co/auth/v1/callback";
10919
- }
10920
- var OAUTH_PROVIDERS, oauthWizard;
10921
- var init_oauth = __esm({
10922
- "packages/tooling/src/cli/setup/oauth.ts"() {
10923
- "use strict";
10924
- init_utils();
10925
- init_cli_output();
10926
- init_pathResolver();
10927
- init_cli_input();
10928
- init_types();
10929
- OAUTH_PROVIDERS = [
10930
- {
10931
- id: "google",
10932
- name: "Google",
10933
- consoleUrl: "https://console.cloud.google.com/apis/credentials",
10934
- instructions: [
10935
- "1. Go to Google Cloud Console > APIs & Services > Credentials",
10936
- "2. Create OAuth 2.0 Client ID (Web application)",
10937
- "3. Add authorized redirect URI (see below)",
10938
- "4. Copy Client ID + Client Secret"
10939
- ]
10940
- },
10941
- {
10942
- id: "github",
10943
- name: "GitHub",
10944
- consoleUrl: "https://github.com/settings/developers",
10945
- instructions: [
10946
- "1. Go to GitHub > Settings > Developer settings > OAuth Apps",
10947
- '2. Click "New OAuth App"',
10948
- "3. Set Authorization callback URL (see below)",
10949
- "4. Copy Client ID + Client Secret"
10950
- ]
10951
- },
10952
- {
10953
- id: "apple",
10954
- name: "Apple",
10955
- consoleUrl: "https://developer.apple.com/account/resources/identifiers/list/serviceId",
10956
- instructions: [
10957
- "1. Go to Apple Developer > Certificates, Identifiers & Profiles",
10958
- "2. Create a Services ID",
10959
- '3. Enable "Sign In with Apple"',
10960
- "4. Configure redirect URL (see below)",
10961
- "5. Create a private key for Sign In with Apple"
10962
- ]
10963
- }
10964
- ];
10965
- oauthWizard = {
10966
- id: "oauth",
10967
- name: "OAuth Providers",
10968
- isRelevant(ctx) {
10969
- const pkgPath = joinPath(ctx.appDir, "package.json");
10970
- if (pathExists(pkgPath)) {
10971
- const pkg = readSync(pkgPath, { format: "json" });
10972
- if (pkg && typeof pkg === "object") {
10973
- const deps = pkg.dependencies;
10974
- if (deps?.["@donotdev/auth"] || deps?.["@donotdev/oauth"]) return true;
10975
- }
10976
- }
10977
- return false;
10978
- },
10979
- async run(ctx) {
10980
- const steps = [];
10981
- const hasFirebase = pathExists(joinPath(ctx.projectRoot, ".firebaserc")) || pathExists(joinPath(ctx.appDir, "firebase.json"));
10982
- const hasSupabase = pathExists(joinPath(ctx.appDir, "supabase"));
10983
- const backendProvider = hasFirebase ? "firebase" : "supabase";
10984
- const redirectUri = getRedirectUri(ctx, backendProvider);
10985
- const selected = await askForMultiSelection(
10986
- "Which OAuth providers do you want to configure?",
10987
- OAUTH_PROVIDERS.map((p2) => ({
10988
- title: p2.name,
10989
- value: p2.id
10990
- }))
10991
- );
10992
- if (selected.length === 0) {
10993
- steps.push({
10994
- name: "OAuth",
10995
- status: "skipped",
10996
- message: "No providers selected"
10997
- });
10998
- return { provider: "oauth", steps, overallStatus: "success" };
10999
- }
11000
- for (const providerId of selected) {
11001
- const provider = OAUTH_PROVIDERS.find((p2) => p2.id === providerId);
11002
- if (!provider) continue;
11003
- Me(
11004
- [
11005
- ...provider.instructions,
11006
- "",
11007
- `Redirect URI: ${redirectUri}`,
11008
- "",
11009
- `Console: ${provider.consoleUrl}`,
11010
- "",
11011
- hasSupabase ? `Then enable "${provider.name}" in Supabase Dashboard > Authentication > Providers` : `Then enable "${provider.name}" in Firebase Console > Authentication > Sign-in method`
11012
- ].join("\n"),
11013
- `${provider.name} OAuth Setup`
11014
- );
11015
- steps.push({
11016
- name: `${provider.name} OAuth`,
11017
- status: "needs-manual",
11018
- message: `Register app in ${provider.name} console + enable provider`,
11019
- coachingTopic: `oauth-${provider.id}`
11020
- });
11021
- }
11022
- return {
11023
- provider: "oauth",
11024
- steps,
11025
- overallStatus: computeOverallStatus(steps)
11026
- };
11027
- }
11028
- };
11029
- }
11030
- });
11031
-
11032
- // packages/tooling/src/cli/setup/auth.ts
11033
- var auth_exports = {};
11034
- __export(auth_exports, {
11035
- authWizard: () => authWizard
11036
- });
11037
- function detectAuthProviders(ctx) {
11038
- const providers = ["email"];
11039
- const srcDir = joinPath(ctx.appDir, "src");
11040
- if (!pathExists(srcDir)) return providers;
11041
- const candidates = [
11042
- joinPath(srcDir, "providers.ts"),
11043
- joinPath(srcDir, "providers.tsx"),
11044
- joinPath(srcDir, "config", "providers.ts"),
11045
- joinPath(srcDir, "lib", "providers.ts")
11046
- ];
11047
- for (const candidate of candidates) {
11048
- if (!pathExists(candidate)) continue;
11049
- const content = readSync(candidate, { format: "text" });
11050
- if (typeof content !== "string") continue;
11051
- if (content.includes("google") || content.includes("Google"))
11052
- providers.push("google");
11053
- if (content.includes("github") || content.includes("GitHub"))
11054
- providers.push("github");
11055
- if (content.includes("apple") || content.includes("Apple"))
11056
- providers.push("apple");
11057
- if (content.includes("facebook") || content.includes("Facebook"))
11058
- providers.push("facebook");
11059
- break;
11060
- }
11061
- return [...new Set(providers)];
11062
- }
11063
- var authWizard;
11064
- var init_auth = __esm({
11065
- "packages/tooling/src/cli/setup/auth.ts"() {
11066
- "use strict";
11067
- init_utils();
11068
- init_cli_output();
11069
- init_pathResolver();
11070
- init_types();
11071
- authWizard = {
11072
- id: "auth",
11073
- name: "Authentication",
11074
- isRelevant(ctx) {
11075
- const pkgPath = joinPath(ctx.appDir, "package.json");
11076
- if (pathExists(pkgPath)) {
11077
- const pkg = readSync(pkgPath, { format: "json" });
11078
- if (pkg && typeof pkg === "object") {
11079
- const deps = pkg.dependencies;
11080
- if (deps?.["@donotdev/auth"]) return true;
11081
- }
11082
- }
11083
- return false;
11084
- },
11085
- async run(ctx) {
11086
- const steps = [];
11087
- const detectedProviders = detectAuthProviders(ctx);
11088
- const hasFirebase = pathExists(joinPath(ctx.projectRoot, ".firebaserc")) || pathExists(joinPath(ctx.appDir, "firebase.json"));
11089
- const hasSupabase = pathExists(joinPath(ctx.appDir, "supabase"));
11090
- steps.push({
11091
- name: "Detected providers",
11092
- status: "success",
11093
- message: `Found: ${detectedProviders.join(", ")}`
11094
- });
11095
- if (hasFirebase) {
11096
- const rcPath = joinPath(ctx.projectRoot, ".firebaserc");
11097
- let projectId = "<YOUR_PROJECT_ID>";
11098
- if (pathExists(rcPath)) {
11099
- try {
11100
- const raw = readSync(rcPath, { format: "text" });
11101
- const rc = JSON.parse(typeof raw === "string" ? raw : "{}");
11102
- if (rc?.projects?.default) projectId = rc.projects.default;
11103
- } catch {
11104
- }
11105
- }
11106
- const providerList = detectedProviders.map((p2) => ` - ${p2.charAt(0).toUpperCase() + p2.slice(1)}`).join("\n");
11107
- Me(
11108
- [
11109
- "Enable these auth providers in Firebase Console:",
11110
- `https://console.firebase.google.com/project/${projectId}/authentication/providers`,
11111
- "",
11112
- providerList,
11113
- "",
11114
- "For each provider:",
11115
- "\u2192 Click the provider \u2192 Enable \u2192 Save",
11116
- "\u2192 For OAuth providers, paste the Client ID + Secret from the provider console"
11117
- ].join("\n"),
11118
- "Firebase Authentication"
11119
- );
11120
- steps.push({
11121
- name: "Firebase Auth",
11122
- status: "needs-manual",
11123
- message: "Enable auth providers in Firebase Console",
11124
- coachingTopic: "auth-firebase"
11125
- });
11126
- }
11127
- if (hasSupabase) {
11128
- const providerList = detectedProviders.map((p2) => ` - ${p2.charAt(0).toUpperCase() + p2.slice(1)}`).join("\n");
11129
- Me(
11130
- [
11131
- "Enable these auth providers in Supabase Dashboard:",
11132
- "https://supabase.com/dashboard > Authentication > Providers",
11133
- "",
11134
- providerList,
11135
- "",
11136
- "For each provider:",
11137
- "\u2192 Toggle the provider on",
11138
- "\u2192 For OAuth providers, paste the Client ID + Secret from the provider console"
11139
- ].join("\n"),
11140
- "Supabase Authentication"
11141
- );
11142
- steps.push({
11143
- name: "Supabase Auth",
11144
- status: "needs-manual",
11145
- message: "Enable auth providers in Supabase Dashboard",
11146
- coachingTopic: "auth-supabase"
11147
- });
11148
- }
11149
- return {
11150
- provider: "auth",
11151
- steps,
11152
- overallStatus: computeOverallStatus(steps)
11153
- };
11154
- }
11155
- };
11156
- }
11157
- });
10584
+ // packages/cli/src/bin/commands/setup.ts
10585
+ init_utils();
11158
10586
 
11159
- // packages/tooling/src/cli/doctor/check-env.ts
11160
- var check_env_exports = {};
11161
- __export(check_env_exports, {
11162
- envCheck: () => envCheck,
11163
- parseEnvFile: () => parseEnvFile
11164
- });
11165
- function parseEnvFile(filePath) {
11166
- const map = /* @__PURE__ */ new Map();
11167
- if (!pathExists(filePath)) return map;
11168
- const content = readSync(filePath, { format: "text" });
11169
- if (typeof content !== "string") return map;
11170
- for (const line of content.split(/\r?\n/)) {
11171
- const trimmed = line.trim();
11172
- if (!trimmed || trimmed.startsWith("#")) continue;
11173
- const eqIdx = trimmed.indexOf("=");
11174
- if (eqIdx < 0) continue;
11175
- const key = trimmed.substring(0, eqIdx).trim();
11176
- const value = trimmed.substring(eqIdx + 1).trim();
11177
- if (key) map.set(key, value);
11178
- }
11179
- return map;
11180
- }
11181
- var envCheck;
11182
- var init_check_env = __esm({
11183
- "packages/tooling/src/cli/doctor/check-env.ts"() {
11184
- "use strict";
11185
- init_utils();
11186
- init_pathResolver();
11187
- envCheck = {
11188
- id: "env",
11189
- name: "Environment Files",
11190
- isRelevant(_ctx) {
11191
- return true;
11192
- },
11193
- async run(ctx) {
11194
- const results = [];
11195
- const envPath = joinPath(ctx.appDir, ".env");
11196
- const examplePath = joinPath(ctx.appDir, ".env.example");
11197
- if (!pathExists(envPath)) {
11198
- results.push({
11199
- name: ".env file",
11200
- status: "fail",
11201
- message: "Not found \u2014 run dndev setup"
11202
- });
11203
- return results;
11204
- }
11205
- results.push({ name: ".env file", status: "pass", message: "Found" });
11206
- if (pathExists(examplePath)) {
11207
- const example = parseEnvFile(examplePath);
11208
- const actual = parseEnvFile(envPath);
11209
- const missing = [];
11210
- const empty = [];
11211
- for (const [key] of example) {
11212
- if (!actual.has(key)) {
11213
- missing.push(key);
11214
- } else if (!actual.get(key)) {
11215
- empty.push(key);
11216
- }
11217
- }
11218
- if (missing.length > 0) {
11219
- results.push({
11220
- name: "Missing vars",
11221
- status: "fail",
11222
- message: `${missing.length} var(s) in .env.example not in .env: ${missing.join(", ")}`
11223
- });
11224
- }
11225
- if (empty.length > 0) {
11226
- results.push({
11227
- name: "Empty vars",
11228
- status: "warn",
11229
- message: `${empty.length} var(s) are empty: ${empty.join(", ")}`
11230
- });
11231
- }
11232
- if (missing.length === 0 && empty.length === 0) {
11233
- results.push({
11234
- name: ".env vs .env.example",
11235
- status: "pass",
11236
- message: `All ${example.size} expected vars present`
11237
- });
11238
- }
11239
- }
11240
- const gitignorePath = joinPath(ctx.projectRoot, ".gitignore");
11241
- if (pathExists(gitignorePath)) {
11242
- const content = readSync(gitignorePath, { format: "text" });
11243
- if (typeof content === "string") {
11244
- const hasEnvIgnore = content.split("\n").some((line) => {
11245
- const trimmed = line.trim();
11246
- if (!trimmed || trimmed.startsWith("#")) return false;
11247
- return /^\.env(\*|\..*)?$/.test(trimmed);
11248
- });
11249
- if (hasEnvIgnore) {
11250
- results.push({
11251
- name: ".gitignore",
11252
- status: "pass",
11253
- message: ".env is gitignored"
11254
- });
11255
- } else {
11256
- results.push({
11257
- name: ".gitignore",
11258
- status: "warn",
11259
- message: ".env might not be gitignored \u2014 verify manually"
11260
- });
11261
- }
11262
- }
11263
- }
11264
- return results;
11265
- }
11266
- };
11267
- }
11268
- });
10587
+ // packages/tooling/src/index.ts
10588
+ init_utils();
11269
10589
 
11270
- // packages/tooling/src/cli/doctor/check-firebase.ts
11271
- var check_firebase_exports = {};
11272
- __export(check_firebase_exports, {
11273
- firebaseCheck: () => firebaseCheck
11274
- });
11275
- import { spawnSync as spawnSync3 } from "node:child_process";
11276
- var firebaseCheck;
11277
- var init_check_firebase = __esm({
11278
- "packages/tooling/src/cli/doctor/check-firebase.ts"() {
11279
- "use strict";
11280
- init_utils();
11281
- init_pathResolver();
11282
- firebaseCheck = {
11283
- id: "firebase",
11284
- name: "Firebase",
11285
- isRelevant(ctx) {
11286
- return pathExists(joinPath(ctx.appDir, "firebase.json")) || pathExists(joinPath(ctx.projectRoot, "firebase.json")) || pathExists(joinPath(ctx.projectRoot, ".firebaserc"));
11287
- },
11288
- async run(ctx) {
11289
- const results = [];
11290
- try {
11291
- const result = spawnSync3("firebase", ["--version"], {
11292
- stdio: "pipe",
11293
- encoding: "utf-8",
11294
- timeout: 1e4
11295
- });
11296
- if (result.status === 0) {
11297
- results.push({
11298
- name: "Firebase CLI",
11299
- status: "pass",
11300
- message: result.stdout.trim()
11301
- });
11302
- } else {
11303
- results.push({
11304
- name: "Firebase CLI",
11305
- status: "fail",
11306
- message: "Not installed"
11307
- });
11308
- }
11309
- } catch {
11310
- results.push({
11311
- name: "Firebase CLI",
11312
- status: "fail",
11313
- message: "Not installed"
11314
- });
11315
- }
11316
- const rcPath = joinPath(ctx.projectRoot, ".firebaserc");
11317
- if (pathExists(rcPath)) {
11318
- try {
11319
- const raw = readSync(rcPath, { format: "text" });
11320
- if (typeof raw !== "string") throw new Error("Not a string");
11321
- const rc = JSON.parse(raw);
11322
- const projects = rc?.projects;
11323
- const projectId = projects && typeof projects === "object" ? projects.default : void 0;
11324
- if (projectId && typeof projectId === "string") {
11325
- results.push({
11326
- name: ".firebaserc",
11327
- status: "pass",
11328
- message: `Project: ${projectId}`
11329
- });
11330
- } else {
11331
- results.push({
11332
- name: ".firebaserc",
11333
- status: "warn",
11334
- message: "No default project set"
11335
- });
11336
- }
11337
- } catch {
11338
- results.push({
11339
- name: ".firebaserc",
11340
- status: "warn",
11341
- message: "Invalid JSON"
11342
- });
11343
- }
11344
- } else {
11345
- results.push({
11346
- name: ".firebaserc",
11347
- status: "fail",
11348
- message: "Missing \u2014 run dndev setup firebase"
11349
- });
11350
- }
11351
- const framework = ctx.app?.framework ?? "vite";
11352
- const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
11353
- const envPath = joinPath(ctx.appDir, ".env");
11354
- if (pathExists(envPath)) {
11355
- const content = readSync(envPath, { format: "text" });
11356
- if (typeof content === "string") {
11357
- const requiredKeys = [
11358
- "FIREBASE_API_KEY",
11359
- "FIREBASE_PROJECT_ID",
11360
- "FIREBASE_AUTH_DOMAIN"
11361
- ];
11362
- const missing = requiredKeys.filter(
11363
- (key) => !content.includes(`${prefix}${key}=`)
11364
- );
11365
- if (missing.length === 0) {
11366
- results.push({
11367
- name: "Firebase .env",
11368
- status: "pass",
11369
- message: "All required keys present"
11370
- });
11371
- } else {
11372
- results.push({
11373
- name: "Firebase .env",
11374
- status: "fail",
11375
- message: `Missing: ${missing.join(", ")}`
11376
- });
11377
- }
11378
- }
11379
- } else {
11380
- results.push({
11381
- name: "Firebase .env",
11382
- status: "fail",
11383
- message: ".env file not found"
11384
- });
11385
- }
11386
- const saKeyPaths = [
11387
- joinPath(ctx.appDir, "service-account-key.json"),
11388
- joinPath(ctx.projectRoot, "service-account-key.json")
11389
- ];
11390
- const saFound = saKeyPaths.some((p2) => pathExists(p2));
11391
- if (saFound) {
11392
- results.push({
11393
- name: "Service account key",
11394
- status: "pass",
11395
- message: "Found"
11396
- });
11397
- } else {
11398
- results.push({
11399
- name: "Service account key",
11400
- status: "warn",
11401
- message: "Not found (needed for deploy/emu)"
11402
- });
11403
- }
11404
- return results;
11405
- }
11406
- };
11407
- }
11408
- });
10590
+ // packages/tooling/src/cli/index.ts
10591
+ init_utils();
11409
10592
 
11410
- // packages/tooling/src/cli/doctor/check-supabase.ts
11411
- var check_supabase_exports = {};
11412
- __export(check_supabase_exports, {
11413
- supabaseCheck: () => supabaseCheck
11414
- });
11415
- import { spawnSync as spawnSync4 } from "node:child_process";
11416
- var supabaseCheck;
11417
- var init_check_supabase = __esm({
11418
- "packages/tooling/src/cli/doctor/check-supabase.ts"() {
11419
- "use strict";
11420
- init_utils();
11421
- init_pathResolver();
11422
- init_supabase();
11423
- supabaseCheck = {
11424
- id: "supabase",
11425
- name: "Supabase",
11426
- isRelevant(ctx) {
11427
- return pathExists(joinPath(ctx.appDir, "supabase")) || pathExists(joinPath(ctx.appDir, "supabase", "config.toml"));
11428
- },
11429
- async run(ctx) {
11430
- const results = [];
11431
- const framework = ctx.app?.framework === "nextjs" ? "nextjs" : "vite";
11432
- const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
11433
- try {
11434
- const result = spawnSync4("supabase", ["--version"], {
11435
- stdio: "pipe",
11436
- encoding: "utf-8",
11437
- timeout: 1e4
11438
- });
11439
- if (result.status === 0) {
11440
- results.push({
11441
- name: "Supabase CLI",
11442
- status: "pass",
11443
- message: result.stdout.trim()
11444
- });
11445
- } else {
11446
- results.push({
11447
- name: "Supabase CLI",
11448
- status: "warn",
11449
- message: "Not installed (needed for migrations)"
11450
- });
11451
- }
11452
- } catch {
11453
- results.push({
11454
- name: "Supabase CLI",
11455
- status: "warn",
11456
- message: "Not installed"
11457
- });
11458
- }
11459
- const configPath = joinPath(ctx.appDir, "supabase", "config.toml");
11460
- if (pathExists(configPath)) {
11461
- results.push({ name: "config.toml", status: "pass", message: "Found" });
11462
- } else {
11463
- results.push({
11464
- name: "config.toml",
11465
- status: "warn",
11466
- message: "Missing (expected at supabase/config.toml)"
11467
- });
11468
- }
11469
- const envPath = joinPath(ctx.appDir, ".env");
11470
- if (pathExists(envPath)) {
11471
- const content = readSync(envPath, { format: "text" });
11472
- if (typeof content === "string") {
11473
- const urlMatch = content.match(
11474
- new RegExp(`${prefix}SUPABASE_URL=(.+)`)
11475
- );
11476
- const url = urlMatch?.[1]?.trim();
11477
- if (url && isValidSupabaseUrl(url)) {
11478
- results.push({ name: "Supabase URL", status: "pass", message: url });
11479
- } else if (url) {
11480
- results.push({
11481
- name: "Supabase URL",
11482
- status: "fail",
11483
- message: "Invalid URL format"
11484
- });
11485
- } else {
11486
- results.push({
11487
- name: "Supabase URL",
11488
- status: "fail",
11489
- message: "Missing in .env"
11490
- });
11491
- }
11492
- const keyMatch = content.match(
11493
- new RegExp(`${prefix}SUPABASE_(?:PUBLIC_KEY|ANON_KEY)=(.+)`)
11494
- );
11495
- const key = keyMatch?.[1]?.trim();
11496
- if (key && isValidPublicKey(key)) {
11497
- results.push({
11498
- name: "Supabase public key",
11499
- status: "pass",
11500
- message: "Present"
11501
- });
11502
- } else {
11503
- results.push({
11504
- name: "Supabase public key",
11505
- status: "fail",
11506
- message: "Missing in .env"
11507
- });
11508
- }
11509
- }
11510
- } else {
11511
- results.push({
11512
- name: "Supabase .env",
11513
- status: "fail",
11514
- message: ".env file not found"
11515
- });
11516
- }
11517
- const functionsEnvCandidates = [
11518
- joinPath(ctx.appDir, "supabase", "functions", ".env"),
11519
- joinPath(ctx.appDir, "functions", ".env")
11520
- ];
11521
- let secretFound = false;
11522
- for (const fenvPath of functionsEnvCandidates) {
11523
- if (!pathExists(fenvPath)) continue;
11524
- const content = readSync(fenvPath, { format: "text" });
11525
- if (typeof content === "string" && content.match(/SUPABASE_(?:SECRET|SERVICE_ROLE)_KEY=.+/)) {
11526
- secretFound = true;
11527
- results.push({
11528
- name: "Secret key",
11529
- status: "pass",
11530
- message: `Found in ${fenvPath.split("/").pop()}`
11531
- });
11532
- break;
11533
- }
11534
- }
11535
- if (!secretFound) {
11536
- results.push({
11537
- name: "Secret key",
11538
- status: "warn",
11539
- message: "Not found (needed for migrations + deploy)"
11540
- });
11541
- }
11542
- return results;
11543
- }
11544
- };
11545
- }
11546
- });
10593
+ // packages/tooling/src/cli/setup/setup.ts
10594
+ init_utils();
10595
+ init_cli_output();
10596
+ init_pathResolver();
10597
+ init_cross_app_detection();
11547
10598
 
11548
- // packages/tooling/src/cli/doctor/check-stripe.ts
11549
- var check_stripe_exports = {};
11550
- __export(check_stripe_exports, {
11551
- stripeCheck: () => stripeCheck
11552
- });
11553
- function readEnvValue2(envPath, varName) {
10599
+ // packages/tooling/src/cli/setup/validate-env.ts
10600
+ init_utils();
10601
+ init_pathResolver();
10602
+ init_cli_output();
10603
+ var FIREBASE_VARS = [
10604
+ {
10605
+ provider: "Firebase",
10606
+ varName: "FIREBASE_API_KEY",
10607
+ envFile: ".env",
10608
+ needsPrefix: true
10609
+ },
10610
+ {
10611
+ provider: "Firebase",
10612
+ varName: "FIREBASE_PROJECT_ID",
10613
+ envFile: ".env",
10614
+ needsPrefix: true
10615
+ },
10616
+ {
10617
+ provider: "Firebase",
10618
+ varName: "FIREBASE_AUTH_DOMAIN",
10619
+ envFile: ".env",
10620
+ needsPrefix: true
10621
+ }
10622
+ ];
10623
+ var SUPABASE_VARS = [
10624
+ {
10625
+ provider: "Supabase",
10626
+ varName: "SUPABASE_URL",
10627
+ envFile: ".env",
10628
+ needsPrefix: true
10629
+ },
10630
+ {
10631
+ provider: "Supabase",
10632
+ varName: "SUPABASE_PUBLIC_KEY",
10633
+ envFile: ".env",
10634
+ needsPrefix: true
10635
+ }
10636
+ ];
10637
+ var SUPABASE_SECRET_VARS = [
10638
+ {
10639
+ provider: "Supabase",
10640
+ varName: "SUPABASE_SECRET_KEY",
10641
+ envFile: "supabase/functions/.env"
10642
+ },
10643
+ {
10644
+ provider: "Supabase",
10645
+ varName: "SUPABASE_DB_URL",
10646
+ envFile: "supabase/functions/.env"
10647
+ }
10648
+ ];
10649
+ var VERCEL_VARS = [
10650
+ { provider: "Vercel", varName: "VERCEL_TOKEN", envFile: ".env.local" },
10651
+ { provider: "Vercel", varName: "VERCEL_ORG_ID", envFile: ".env.local" },
10652
+ { provider: "Vercel", varName: "VERCEL_PROJECT_ID", envFile: ".env.local" }
10653
+ ];
10654
+ function readEnvValue(envPath, varName) {
11554
10655
  if (!pathExists(envPath)) return null;
11555
10656
  const content = readSync(envPath, { format: "text" });
11556
10657
  if (typeof content !== "string") return null;
@@ -11558,520 +10659,230 @@ function readEnvValue2(envPath, varName) {
11558
10659
  const trimmed = line.trim();
11559
10660
  if (!trimmed || trimmed.startsWith("#")) continue;
11560
10661
  if (trimmed.startsWith(`${varName}=`)) {
11561
- let val = trimmed.substring(`${varName}=`.length).trim();
11562
- if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
11563
- val = val.slice(1, -1);
11564
- }
11565
- return val || null;
10662
+ return trimmed.substring(`${varName}=`.length).trim();
11566
10663
  }
11567
10664
  }
11568
10665
  return null;
11569
10666
  }
11570
- function findFunctionsEnv(ctx) {
11571
- const candidates = [
11572
- joinPath(ctx.appDir, "supabase", "functions", ".env"),
11573
- joinPath(ctx.appDir, "functions", ".env")
11574
- ];
11575
- return candidates.find((p2) => pathExists(p2)) ?? null;
10667
+ function isProviderRelevant(ctx, provider) {
10668
+ switch (provider) {
10669
+ case "Firebase":
10670
+ return pathExists(joinPath(ctx.appDir, "firebase.json")) || pathExists(joinPath(ctx.projectRoot, "firebase.json")) || pathExists(joinPath(ctx.projectRoot, ".firebaserc"));
10671
+ case "Supabase":
10672
+ return pathExists(joinPath(ctx.appDir, "supabase")) || pathExists(joinPath(ctx.appDir, "supabase", "config.toml"));
10673
+ case "Vercel":
10674
+ return pathExists(joinPath(ctx.appDir, "vercel.json"));
10675
+ default:
10676
+ return false;
10677
+ }
11576
10678
  }
11577
- var stripeCheck;
11578
- var init_check_stripe = __esm({
11579
- "packages/tooling/src/cli/doctor/check-stripe.ts"() {
11580
- "use strict";
11581
- init_utils();
11582
- init_pathResolver();
11583
- stripeCheck = {
11584
- id: "stripe",
11585
- name: "Stripe",
11586
- isRelevant(ctx) {
11587
- const pkgPath = joinPath(ctx.appDir, "package.json");
11588
- if (pathExists(pkgPath)) {
11589
- const pkg = readSync(pkgPath, { format: "json" });
11590
- if (pkg && typeof pkg === "object") {
11591
- const deps = pkg.dependencies;
11592
- if (deps?.["@donotdev/billing"]) return true;
11593
- }
11594
- }
11595
- const envPath = joinPath(ctx.appDir, ".env");
11596
- if (pathExists(envPath)) {
11597
- const content = readSync(envPath, { format: "text" });
11598
- if (typeof content === "string" && content.includes("STRIPE_PUBLISHABLE_KEY"))
11599
- return true;
11600
- }
11601
- return false;
11602
- },
11603
- async run(ctx) {
11604
- const results = [];
11605
- const framework = ctx.app?.framework ?? "vite";
11606
- const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
11607
- const envPath = joinPath(ctx.appDir, ".env");
11608
- const functionsEnvPath = findFunctionsEnv(ctx);
11609
- const pk = readEnvValue2(envPath, `${prefix}STRIPE_PUBLISHABLE_KEY`);
11610
- let pkMode = null;
11611
- if (pk) {
11612
- if (pk.startsWith("pk_test_") || pk.startsWith("pk_live_")) {
11613
- pkMode = pk.startsWith("pk_test_") ? "test" : "live";
11614
- results.push({
11615
- name: "Publishable key",
11616
- status: "pass",
11617
- message: `Valid (${pkMode} mode)`
11618
- });
11619
- } else {
11620
- results.push({
11621
- name: "Publishable key",
11622
- status: "fail",
11623
- message: "Invalid format \u2014 must start with pk_test_ or pk_live_"
11624
- });
11625
- }
11626
- } else {
11627
- results.push({
11628
- name: "Publishable key",
11629
- status: "fail",
11630
- message: "Missing in .env"
11631
- });
11632
- }
11633
- let skMode = null;
11634
- if (functionsEnvPath) {
11635
- const sk = readEnvValue2(functionsEnvPath, "STRIPE_SECRET_KEY");
11636
- if (sk) {
11637
- if (sk.startsWith("sk_test_") || sk.startsWith("sk_live_") || sk.startsWith("rk_test_") || sk.startsWith("rk_live_")) {
11638
- skMode = sk.includes("_test_") ? "test" : "live";
11639
- results.push({
11640
- name: "Secret key",
11641
- status: "pass",
11642
- message: `Valid (${skMode} mode)`
11643
- });
11644
- } else {
11645
- results.push({
11646
- name: "Secret key",
11647
- status: "fail",
11648
- message: "STRIPE_SECRET_KEY has invalid format"
11649
- });
11650
- }
11651
- } else {
11652
- results.push({
11653
- name: "Secret key",
11654
- status: "warn",
11655
- message: "Not found in functions/.env"
11656
- });
11657
- }
11658
- } else {
11659
- results.push({
11660
- name: "Secret key",
11661
- status: "warn",
11662
- message: "No functions/.env found"
11663
- });
11664
- }
11665
- if (functionsEnvPath) {
11666
- const wh = readEnvValue2(functionsEnvPath, "STRIPE_WEBHOOK_SECRET");
11667
- if (wh) {
11668
- if (wh.startsWith("whsec_")) {
11669
- results.push({
11670
- name: "Webhook secret",
11671
- status: "pass",
11672
- message: "Valid format"
11673
- });
11674
- } else {
11675
- results.push({
11676
- name: "Webhook secret",
11677
- status: "fail",
11678
- message: "STRIPE_WEBHOOK_SECRET has invalid format"
11679
- });
11680
- }
11681
- } else {
11682
- results.push({
11683
- name: "Webhook secret",
11684
- status: "warn",
11685
- message: "Not found \u2014 webhooks won't verify"
11686
- });
11687
- }
11688
- }
11689
- if (pkMode && skMode) {
11690
- if (pkMode !== skMode) {
11691
- results.push({
11692
- name: "Key mode",
11693
- status: "warn",
11694
- message: `Publishable key is ${pkMode} but secret key is ${skMode}`
11695
- });
11696
- } else {
11697
- results.push({
11698
- name: "Key mode",
11699
- status: "pass",
11700
- message: `Both keys are ${pkMode}`
11701
- });
11702
- }
11703
- }
11704
- return results;
11705
- }
11706
- };
10679
+ function validateRequiredEnvVars(ctx) {
10680
+ const missing = [];
10681
+ const framework = ctx.app?.framework === "nextjs" ? "nextjs" : "vite";
10682
+ const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
10683
+ const requirements = [];
10684
+ if (isProviderRelevant(ctx, "Firebase")) {
10685
+ requirements.push(...FIREBASE_VARS);
10686
+ }
10687
+ if (isProviderRelevant(ctx, "Supabase")) {
10688
+ requirements.push(...SUPABASE_VARS);
10689
+ const sbFunctionsEnv = joinPath(
10690
+ ctx.appDir,
10691
+ "supabase",
10692
+ "functions",
10693
+ ".env"
10694
+ );
10695
+ const altFunctionsEnv = joinPath(ctx.appDir, "functions", ".env");
10696
+ if (pathExists(joinPath(ctx.appDir, "supabase", "functions")) || pathExists(sbFunctionsEnv)) {
10697
+ requirements.push(...SUPABASE_SECRET_VARS);
10698
+ } else if (pathExists(altFunctionsEnv)) {
10699
+ requirements.push({
10700
+ provider: "Supabase",
10701
+ varName: "SUPABASE_SECRET_KEY",
10702
+ envFile: "functions/.env"
10703
+ });
10704
+ }
11707
10705
  }
11708
- });
11709
-
11710
- // packages/tooling/src/cli/doctor/check-auth.ts
11711
- var check_auth_exports = {};
11712
- __export(check_auth_exports, {
11713
- authCheck: () => authCheck
11714
- });
11715
- var authCheck;
11716
- var init_check_auth = __esm({
11717
- "packages/tooling/src/cli/doctor/check-auth.ts"() {
11718
- "use strict";
11719
- init_utils();
11720
- init_pathResolver();
11721
- authCheck = {
11722
- id: "auth",
11723
- name: "Authentication",
11724
- isRelevant(ctx) {
11725
- const pkgPath = joinPath(ctx.appDir, "package.json");
11726
- if (pathExists(pkgPath)) {
11727
- const content = readSync(pkgPath, { format: "text" });
11728
- if (typeof content === "string" && content.includes("@donotdev/auth"))
11729
- return true;
11730
- }
11731
- return false;
11732
- },
11733
- async run(ctx) {
11734
- const results = [];
11735
- results.push({
11736
- name: "@donotdev/auth",
11737
- status: "pass",
11738
- message: "Installed"
10706
+ if (isProviderRelevant(ctx, "Vercel")) {
10707
+ requirements.push(...VERCEL_VARS);
10708
+ }
10709
+ for (const req of requirements) {
10710
+ const actualVarName = req.needsPrefix ? `${prefix}${req.varName}` : req.varName;
10711
+ const envFilePath = joinPath(ctx.appDir, req.envFile);
10712
+ if (req.varName === "SUPABASE_PUBLIC_KEY") {
10713
+ const pkValue = readEnvValue(envFilePath, actualVarName);
10714
+ const anonValue = readEnvValue(envFilePath, `${prefix}SUPABASE_ANON_KEY`);
10715
+ if ((!pkValue || pkValue === "") && (!anonValue || anonValue === "")) {
10716
+ missing.push({
10717
+ provider: req.provider,
10718
+ varName: actualVarName,
10719
+ envFile: req.envFile,
10720
+ status: pathExists(envFilePath) ? "empty" : "missing"
11739
10721
  });
11740
- const candidates = [
11741
- joinPath(ctx.appDir, "src", "providers.ts"),
11742
- joinPath(ctx.appDir, "src", "providers.tsx"),
11743
- joinPath(ctx.appDir, "src", "config", "providers.ts"),
11744
- joinPath(ctx.appDir, "src", "lib", "providers.ts")
11745
- ];
11746
- const providerFile = candidates.find((c) => pathExists(c));
11747
- if (providerFile) {
11748
- results.push({
11749
- name: "Providers config",
11750
- status: "pass",
11751
- message: `Found: ${providerFile.split("/").slice(-2).join("/")}`
11752
- });
11753
- const content = readSync(providerFile, { format: "text" });
11754
- if (typeof content === "string") {
11755
- const providers = [];
11756
- if (content.includes("email") || content.includes("Email"))
11757
- providers.push("email");
11758
- if (content.includes("google") || content.includes("Google"))
11759
- providers.push("google");
11760
- if (content.includes("github") || content.includes("GitHub"))
11761
- providers.push("github");
11762
- if (content.includes("apple") || content.includes("Apple"))
11763
- providers.push("apple");
11764
- if (providers.length > 0) {
11765
- results.push({
11766
- name: "Auth providers",
11767
- status: "pass",
11768
- message: `Configured: ${providers.join(", ")}`
11769
- });
11770
- }
11771
- }
11772
- } else {
11773
- results.push({
11774
- name: "Providers config",
11775
- status: "warn",
11776
- message: "No providers.ts found \u2014 auth may not work"
11777
- });
11778
- }
11779
- const hasFirebase = pathExists(joinPath(ctx.projectRoot, ".firebaserc"));
11780
- const hasSupabase = pathExists(joinPath(ctx.appDir, "supabase"));
11781
- if (hasFirebase) {
11782
- results.push({
11783
- name: "Backend",
11784
- status: "pass",
11785
- message: "Firebase Auth (verify providers are enabled in console)",
11786
- detail: "Run dndev setup auth for setup coaching"
11787
- });
11788
- } else if (hasSupabase) {
11789
- results.push({
11790
- name: "Backend",
11791
- status: "pass",
11792
- message: "Supabase Auth (verify providers are enabled in dashboard)",
11793
- detail: "Run dndev setup auth for setup coaching"
11794
- });
11795
- } else {
11796
- results.push({
11797
- name: "Backend",
11798
- status: "warn",
11799
- message: "No backend detected for auth"
11800
- });
11801
- }
11802
- return results;
11803
- }
11804
- };
11805
- }
11806
- });
11807
-
11808
- // packages/tooling/src/cli/doctor/doctor.ts
11809
- var doctor_exports = {};
11810
- __export(doctor_exports, {
11811
- default: () => doctor_default,
11812
- main: () => main4
11813
- });
11814
- function renderResults(allResults, verbose) {
11815
- const lines = [];
11816
- for (const { checkName, results } of allResults) {
11817
- lines.push(checkName);
11818
- for (const result of results) {
11819
- const icon = STATUS_ICONS[result.status];
11820
- lines.push(` ${icon} ${result.name}: ${result.message}`);
11821
- if (verbose && result.detail) {
11822
- lines.push(` ${result.detail}`);
11823
10722
  }
10723
+ continue;
11824
10724
  }
11825
- lines.push("");
11826
- }
11827
- Me(lines.join("\n"), "Health Check Results");
11828
- }
11829
- async function main4(options = {}) {
11830
- Ie("DoNotDev Doctor");
11831
- const projectRoot = process.cwd();
11832
- let appDir = projectRoot;
11833
- let app = null;
11834
- const appsDir = joinPath(projectRoot, "apps");
11835
- if (pathExists(appsDir)) {
11836
- try {
11837
- app = await selectApp(projectRoot, options.app);
11838
- if (app) {
11839
- appDir = app.path;
11840
- }
11841
- } catch {
10725
+ const value = readEnvValue(envFilePath, actualVarName);
10726
+ if (value === null) {
10727
+ missing.push({
10728
+ provider: req.provider,
10729
+ varName: actualVarName,
10730
+ envFile: req.envFile,
10731
+ status: pathExists(envFilePath) ? "missing" : "missing"
10732
+ });
10733
+ } else if (value === "") {
10734
+ missing.push({
10735
+ provider: req.provider,
10736
+ varName: actualVarName,
10737
+ envFile: req.envFile,
10738
+ status: "empty"
10739
+ });
11842
10740
  }
11843
10741
  }
11844
- const ctx = {
11845
- projectRoot,
11846
- appDir,
11847
- app,
11848
- verbose: options.verbose
11849
- };
11850
- const allResults = [];
11851
- let hasFail = false;
11852
- const registry = options.check ? CHECK_REGISTRY.filter((e2) => e2.id === options.check) : CHECK_REGISTRY;
11853
- if (options.check && registry.length === 0) {
11854
- log.error(
11855
- `Unknown check: ${options.check}. Available: ${CHECK_REGISTRY.map((e2) => e2.id).join(", ")}`
11856
- );
11857
- Se("Doctor aborted.");
11858
- return 1;
10742
+ return missing;
10743
+ }
10744
+ function validateAllApps(apps, _topology, projectRoot) {
10745
+ const results = [];
10746
+ for (const app of apps) {
10747
+ const ctx = {
10748
+ projectRoot,
10749
+ appDir: app.path,
10750
+ app
10751
+ };
10752
+ const missing = validateRequiredEnvVars(ctx);
10753
+ results.push({ app, missing });
11859
10754
  }
11860
- for (const entry of registry) {
11861
- const check = await entry.load();
11862
- if (!check.isRelevant(ctx)) continue;
11863
- const results = await check.run(ctx);
11864
- allResults.push({ checkName: check.name, results });
11865
- if (results.some((r2) => r2.status === "fail")) {
11866
- hasFail = true;
10755
+ return results;
10756
+ }
10757
+ function printAllValidationResults(results, multiApp) {
10758
+ const allMissing = [];
10759
+ for (const { app, missing } of results) {
10760
+ for (const m2 of missing) {
10761
+ allMissing.push({ appName: app.name, m: m2 });
11867
10762
  }
11868
10763
  }
11869
- if (allResults.length === 0) {
11870
- log.warn("No checks were relevant for this project.");
11871
- Se("Nothing to check.");
11872
- return 0;
10764
+ if (allMissing.length === 0) {
10765
+ log.success("Credentials OK");
10766
+ return true;
11873
10767
  }
11874
- renderResults(allResults, options.verbose ?? false);
11875
- let passes = 0;
11876
- let warnings = 0;
11877
- let fails = 0;
11878
- for (const { results } of allResults) {
11879
- for (const r2 of results) {
11880
- if (r2.status === "pass") passes++;
11881
- else if (r2.status === "warn") warnings++;
11882
- else if (r2.status === "fail") fails++;
11883
- }
11884
- }
11885
- const totalChecks = passes + warnings + fails;
11886
- const summaryParts = [`${passes}/${totalChecks} passed`];
11887
- if (warnings > 0) summaryParts.push(`${warnings} warning(s)`);
11888
- if (fails > 0) summaryParts.push(`${fails} failure(s)`);
11889
- const exitCode = hasFail ? 1 : 0;
11890
- Se(
11891
- hasFail ? `Health check failed: ${summaryParts.join(", ")}` : `All clear: ${summaryParts.join(", ")}`
11892
- );
11893
- return exitCode;
11894
- }
11895
- var CHECK_REGISTRY, STATUS_ICONS, doctor_default;
11896
- var init_doctor = __esm({
11897
- "packages/tooling/src/cli/doctor/doctor.ts"() {
11898
- "use strict";
11899
- init_utils();
11900
- init_cli_output();
11901
- init_app_selector();
11902
- init_pathResolver();
11903
- CHECK_REGISTRY = [
11904
- { id: "env", load: async () => (await Promise.resolve().then(() => (init_check_env(), check_env_exports))).envCheck },
11905
- {
11906
- id: "firebase",
11907
- load: async () => (await Promise.resolve().then(() => (init_check_firebase(), check_firebase_exports))).firebaseCheck
11908
- },
11909
- {
11910
- id: "supabase",
11911
- load: async () => (await Promise.resolve().then(() => (init_check_supabase(), check_supabase_exports))).supabaseCheck
11912
- },
11913
- {
11914
- id: "stripe",
11915
- load: async () => (await Promise.resolve().then(() => (init_check_stripe(), check_stripe_exports))).stripeCheck
11916
- },
11917
- { id: "auth", load: async () => (await Promise.resolve().then(() => (init_check_auth(), check_auth_exports))).authCheck }
11918
- ];
11919
- STATUS_ICONS = {
11920
- pass: "\u2705",
11921
- // green check
11922
- warn: "\u26A0\uFE0F",
11923
- // warning
11924
- fail: "\u274C"
11925
- // red x
11926
- };
11927
- doctor_default = main4;
10768
+ for (const { appName, m: m2 } of allMissing) {
10769
+ const prefix = multiApp ? `${appName}: ` : "";
10770
+ const detail = m2.status === "empty" ? `empty in ${m2.envFile}` : `missing \u2014 ${m2.envFile}`;
10771
+ log.error(` ${prefix}${m2.varName} (${detail})`);
11928
10772
  }
11929
- });
11930
-
11931
- // packages/cli/src/bin/commands/setup.ts
11932
- init_utils();
11933
-
11934
- // packages/tooling/src/index.ts
11935
- init_utils();
11936
-
11937
- // packages/tooling/src/cli/index.ts
11938
- init_utils();
10773
+ log.warn(`Run 'dndev coach' to see where to get them.`);
10774
+ return false;
10775
+ }
11939
10776
 
11940
10777
  // packages/tooling/src/cli/setup/setup.ts
11941
- init_utils();
11942
- init_cli_output();
11943
- init_cli_input();
11944
- init_app_selector();
11945
- init_pathResolver();
11946
10778
  var WIZARD_REGISTRY = {
11947
10779
  firebase: async () => (await Promise.resolve().then(() => (init_firebase(), firebase_exports))).firebaseWizard,
11948
10780
  supabase: async () => (await Promise.resolve().then(() => (init_supabase(), supabase_exports))).supabaseWizard,
11949
- vercel: async () => (await Promise.resolve().then(() => (init_vercel(), vercel_exports))).vercelWizard,
11950
- stripe: async () => (await Promise.resolve().then(() => (init_stripe(), stripe_exports))).stripeWizard,
11951
- oauth: async () => (await Promise.resolve().then(() => (init_oauth(), oauth_exports))).oauthWizard,
11952
- auth: async () => (await Promise.resolve().then(() => (init_auth(), auth_exports))).authWizard
10781
+ vercel: async () => (await Promise.resolve().then(() => (init_vercel(), vercel_exports))).vercelWizard
11953
10782
  };
11954
- var WIZARD_ORDER = [
11955
- "firebase",
11956
- "supabase",
11957
- "vercel",
11958
- "stripe",
11959
- "auth",
11960
- "oauth"
11961
- ];
11962
- var STATUS_ICONS2 = {
10783
+ var WIZARD_ORDER = ["firebase", "supabase", "vercel"];
10784
+ var STATUS_ICONS = {
11963
10785
  success: "\u2705",
11964
- // green check
11965
10786
  skipped: "\u23ED\uFE0F",
11966
- // skip
11967
10787
  failed: "\u274C",
11968
- // red x
11969
10788
  "needs-manual": "\u{1F449}"
11970
- // pointing right
11971
10789
  };
11972
- function renderSummary(results) {
11973
- const lines = [];
11974
- for (const result of results) {
11975
- lines.push(`${result.provider.toUpperCase()} (${result.overallStatus})`);
10790
+ function renderResults(results, multiApp) {
10791
+ for (const { appName, result } of results) {
10792
+ if (multiApp) {
10793
+ log.info(`
10794
+ ${appName} \u2014 ${result.provider}:`);
10795
+ }
11976
10796
  for (const step of result.steps) {
11977
- const icon = STATUS_ICONS2[step.status];
11978
- lines.push(` ${icon} ${step.name}: ${step.message}`);
10797
+ const icon = STATUS_ICONS[step.status];
10798
+ log.info(` ${icon} ${step.name}: ${step.message}`);
11979
10799
  }
11980
- lines.push("");
11981
10800
  }
11982
- Me(lines.join("\n"), "Setup Summary");
11983
10801
  }
11984
- async function main5(options = {}) {
11985
- Ie("DoNotDev Setup");
11986
- const projectRoot = process.cwd();
11987
- let appDir = projectRoot;
11988
- let app = null;
11989
- const appsDir = joinPath(projectRoot, "apps");
11990
- if (pathExists(appsDir)) {
11991
- try {
11992
- app = await selectApp(projectRoot, options.app);
11993
- if (app) {
11994
- appDir = app.path;
11995
- }
11996
- } catch {
11997
- }
11998
- }
11999
- const ctx = {
10802
+ function buildSetupContext(app, projectRoot, topology, options) {
10803
+ const backendApp = findBackendApp(topology, app.name);
10804
+ return {
12000
10805
  projectRoot,
12001
- appDir,
10806
+ appDir: app.path,
12002
10807
  app,
12003
10808
  verbose: options.verbose,
12004
- dryRun: options.dryRun
10809
+ dryRun: options.dryRun,
10810
+ allApps: topology.apps,
10811
+ backendApp
12005
10812
  };
12006
- let wizardIds;
12007
- if (options.provider) {
12008
- const id = options.provider.toLowerCase();
12009
- if (!WIZARD_REGISTRY[id]) {
12010
- log.error(`Unknown provider: ${options.provider}`);
12011
- log.info(`Available: ${Object.keys(WIZARD_REGISTRY).join(", ")}`);
12012
- Se("Setup aborted.");
12013
- return 1;
12014
- }
12015
- wizardIds = [id];
12016
- } else {
12017
- const relevant = [];
12018
- for (const id of WIZARD_ORDER) {
10813
+ }
10814
+ async function main4(options = {}) {
10815
+ const projectRoot = process.cwd();
10816
+ Ie("DoNotDev Setup");
10817
+ const topology = analyzeProjectTopology(projectRoot);
10818
+ let apps = topology.apps;
10819
+ if (apps.length === 0) {
10820
+ apps = [
10821
+ {
10822
+ name: "root",
10823
+ packageName: "root",
10824
+ path: projectRoot,
10825
+ packageJsonPath: joinPath(projectRoot, "package.json"),
10826
+ framework: "vite",
10827
+ hasFunctions: false
10828
+ }
10829
+ ];
10830
+ }
10831
+ const multiApp = apps.length > 1;
10832
+ const validationResults = validateAllApps(apps, topology, projectRoot);
10833
+ const allValid = printAllValidationResults(validationResults, multiApp);
10834
+ if (!allValid) {
10835
+ Se("Fix missing values, then re-run.");
10836
+ return 1;
10837
+ }
10838
+ const allResults = [];
10839
+ for (const app of apps) {
10840
+ const ctx = buildSetupContext(app, projectRoot, topology, options);
10841
+ let wizardIds;
10842
+ if (options.provider) {
10843
+ const id = options.provider.toLowerCase();
10844
+ if (!WIZARD_REGISTRY[id]) {
10845
+ log.error(`Unknown provider: ${options.provider}`);
10846
+ Se("Setup aborted.");
10847
+ return 1;
10848
+ }
10849
+ const wizard = await WIZARD_REGISTRY[id]();
10850
+ wizardIds = wizard.isRelevant(ctx) ? [id] : [];
10851
+ } else {
10852
+ wizardIds = [];
10853
+ for (const id of WIZARD_ORDER) {
10854
+ const loader = WIZARD_REGISTRY[id];
10855
+ if (!loader) continue;
10856
+ const wizard = await loader();
10857
+ if (wizard.isRelevant(ctx)) {
10858
+ wizardIds.push(id);
10859
+ }
10860
+ }
10861
+ }
10862
+ for (const id of wizardIds) {
12019
10863
  const loader = WIZARD_REGISTRY[id];
12020
10864
  if (!loader) continue;
12021
10865
  const wizard = await loader();
12022
- if (wizard.isRelevant(ctx)) {
12023
- relevant.push({ id, wizard });
12024
- }
12025
- }
12026
- if (relevant.length === 0) {
12027
- log.warn("No providers detected in this project.");
12028
- log.info(
12029
- `Available providers: ${Object.keys(WIZARD_REGISTRY).join(", ")}`
12030
- );
12031
- log.info("Run: dndev setup <provider>");
12032
- Se("Nothing to set up.");
12033
- return 0;
12034
- }
12035
- const selected = await askForMultiSelection(
12036
- "Which providers do you want to set up?",
12037
- relevant.map((r2) => ({
12038
- title: r2.wizard.name,
12039
- value: r2.id,
12040
- hint: "detected"
12041
- }))
12042
- );
12043
- if (selected.length === 0) {
12044
- Se("No providers selected.");
12045
- return 0;
10866
+ const result = await wizard.run(ctx);
10867
+ allResults.push({ appName: app.name, result });
12046
10868
  }
12047
- wizardIds = selected;
12048
10869
  }
12049
- const results = [];
12050
- for (const id of wizardIds) {
12051
- const loader = WIZARD_REGISTRY[id];
12052
- if (!loader) continue;
12053
- log.info(`
12054
- Setting up ${id}...`);
12055
- const wizard = await loader();
12056
- const result = await wizard.run(ctx);
12057
- results.push(result);
12058
- }
12059
- renderSummary(results);
12060
- if (!options.skipDoctor) {
12061
- log.info("\nRunning health checks...");
12062
- try {
12063
- const { main: doctor } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
12064
- await doctor({ app: options.app, verbose: options.verbose });
12065
- } catch {
12066
- log.warn("Doctor check skipped (module not available).");
12067
- }
10870
+ if (allResults.length === 0) {
10871
+ log.warn("No providers detected. Run: dndev setup <provider>");
10872
+ Se("Nothing to set up.");
10873
+ return 0;
12068
10874
  }
12069
- const hasFailures = results.some((r2) => r2.overallStatus === "failed");
12070
- Se(hasFailures ? "Setup completed with errors." : "Setup complete.");
10875
+ renderResults(allResults, multiApp);
10876
+ const hasFailures = allResults.some(
10877
+ (r2) => r2.result.overallStatus === "failed"
10878
+ );
10879
+ Se(
10880
+ hasFailures ? "Setup incomplete \u2014 see errors above." : "Setup complete."
10881
+ );
12071
10882
  return hasFailures ? 1 : 0;
12072
10883
  }
12073
10884
  export {
12074
- main5 as main
10885
+ main4 as main
12075
10886
  };
12076
10887
  /*! Bundled license information:
12077
10888