@voltro/cli 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/dist/apiBuild-CDE4NXNi.js +2 -0
  3. package/dist/{apiBuild-DDr2aNFd.js → apiBuild-CkpsCqIu.js} +2 -2
  4. package/dist/bin.js +1 -1
  5. package/dist/build-51qBWfYY.js +981 -0
  6. package/dist/{checkCommand-BISqx1OJ.js → checkCommand-C1EbNmwl.js} +1 -1
  7. package/dist/{checkCommand-DUtMWjcR.js → checkCommand-C30J2F18.js} +3 -3
  8. package/dist/{codegenCommand-DnVuDxwT.js → codegenCommand-kS3Twuc9.js} +7 -7
  9. package/dist/{codemodRunner-BlQPfjzA.js → codemodRunner-BKjCwGiN.js} +1127 -895
  10. package/dist/{commands-CRbxgxv0.js → commands-DowWkYtd.js} +21 -21
  11. package/dist/{dashboardCommand-Bf_-Ne3P.js → dashboardCommand-gG1y4PFG.js} +5 -5
  12. package/dist/{dataCommand-BoBJJ-Gb.js → dataCommand-hNOGKz4K.js} +6 -6
  13. package/dist/{dev-DOEJXicj.js → dev-Ce2gEziv.js} +2422 -2419
  14. package/dist/dev-Dff_pdik.js +3 -0
  15. package/dist/{doctorCommand-BrWu67JZ.js → doctorCommand-5ULMpL10.js} +505 -284
  16. package/dist/doctorCommand-B8H2MYCE.js +2 -0
  17. package/dist/{dormancyCommand-xn2y-pJm.js → dormancyCommand-DYgI0Kf-.js} +3 -3
  18. package/dist/{embeddingsCommand-Cn5MbRDM.js → embeddingsCommand-yJivQSrI.js} +2 -2
  19. package/dist/{envCommand-UJmJIbs9.js → envCommand-ORJUf8r_.js} +8 -8
  20. package/dist/{evolveCommand-Db30twUy.js → evolveCommand-DTawtTxf.js} +2 -2
  21. package/dist/index.js +1 -1
  22. package/dist/{infoCommand-BjVXpMlP.js → infoCommand-BFksHunl.js} +1 -1
  23. package/dist/inspect-CZvsmp8t.js +2 -0
  24. package/dist/{inspect-DZnan87F.js → inspect-PcC5J-Dl.js} +6 -7
  25. package/dist/manifestBuild-AUh31bFU.js +2 -0
  26. package/dist/{manifestBuild-DnbFKF6w.js → manifestBuild-BwiS4iYm.js} +1 -1
  27. package/dist/{probeCommand-C5fuN6Z2.js → probeCommand-BUYzc0CT.js} +1 -1
  28. package/dist/{runtimeTrace-BQL_lfz6.js → runtimeTrace-BUk5zMXY.js} +1 -1
  29. package/dist/{sdkgen-CMUPrDjH.js → sdkgen-CAhmmbjx.js} +1 -1
  30. package/dist/serveCommand-BUXUtSF5.js +2 -0
  31. package/dist/{serveCommand-B_w-Mpb_.js → serveCommand-DhtC7WBq.js} +726 -725
  32. package/dist/serveEntry.js +1 -1
  33. package/dist/{start-s25GAIgn.js → start-C75w6ftA.js} +1 -1
  34. package/dist/{start-YaUehtDV.js → start-DNtIlBcn.js} +641 -609
  35. package/dist/startEntry.js +1 -1
  36. package/dist/{updateCommand-C9n_Z_oG.js → updateCommand-CtuD5r1t.js} +1 -1
  37. package/dist/updateCommand-eVKVHj3N.js +2 -0
  38. package/dist/webDev-Bzb8ZFus.js +2 -0
  39. package/dist/{webDev-BgdkyjP6.js → webDev-CBN9KkyQ.js} +154 -146
  40. package/dist/{webhooksCommand-CYXTNvXq.js → webhooksCommand-BPJQOGTe.js} +1 -1
  41. package/package.json +61 -19
  42. package/templates/AGENTS.md +1 -1
  43. package/templates/agent-docs/_index.md +1 -1
  44. package/templates/agent-docs/_manifest.json +1 -1
  45. package/templates/agent-docs/ai.md +174 -0
  46. package/templates/agent-docs/authentication.md +34 -0
  47. package/templates/agent-docs/data.md +30 -0
  48. package/templates/agent-docs/database/transactions.md +48 -0
  49. package/templates/agent-docs/observability.md +13 -2
  50. package/templates/agent-docs/plugins/auth-supabase.md +21 -0
  51. package/templates/agent-docs/plugins/sentry.md +91 -8
  52. package/templates/agent-docs/schema-driven-ui.md +29 -0
  53. package/templates/agent-docs/whats-new.md +112 -105
  54. package/templates/apps/api-ai/package.json +6 -6
  55. package/templates/apps/api-auth/package.json +8 -8
  56. package/templates/apps/api-backend/package.json +7 -7
  57. package/templates/apps/api-backend-deactivation/package.json +7 -7
  58. package/templates/apps/api-backend-mail/package.json +8 -8
  59. package/templates/apps/api-backend-mariadb/package.json +9 -9
  60. package/templates/apps/api-backend-sqlite/package.json +8 -8
  61. package/templates/apps/api-backend-storage/package.json +8 -8
  62. package/templates/apps/api-cms/package.json +9 -9
  63. package/templates/apps/api-collab/package.json +8 -8
  64. package/templates/apps/api-data-advanced/package.json +8 -8
  65. package/templates/apps/api-durable/package.json +8 -8
  66. package/templates/apps/api-feature-flags/package.json +9 -9
  67. package/templates/apps/api-governance/package.json +8 -8
  68. package/templates/apps/api-kv/package.json +8 -8
  69. package/templates/apps/api-moderation/package.json +8 -8
  70. package/templates/apps/api-observability/package.json +8 -8
  71. package/templates/apps/api-ratelimit/package.json +8 -8
  72. package/templates/apps/api-rbac/package.json +8 -8
  73. package/templates/apps/api-rest/package.json +7 -7
  74. package/templates/apps/api-row-history/package.json +8 -8
  75. package/templates/apps/api-saas/package.json +11 -11
  76. package/templates/apps/api-saas-starter/package.json +10 -10
  77. package/templates/apps/api-search/package.json +8 -8
  78. package/templates/apps/api-status/package.json +8 -8
  79. package/templates/apps/api-webhooks/package.json +9 -9
  80. package/templates/apps/changelog/package.json +7 -7
  81. package/templates/apps/edge-functions/package.json +2 -2
  82. package/templates/apps/frontend-admin/package.json +7 -7
  83. package/templates/apps/frontend-app/package.json +8 -8
  84. package/templates/apps/frontend-auth/package.json +7 -7
  85. package/templates/apps/frontend-blank/package.json +6 -6
  86. package/templates/apps/frontend-cms/package.json +8 -8
  87. package/templates/apps/frontend-collab/package.json +9 -9
  88. package/templates/apps/frontend-contact/package.json +7 -7
  89. package/templates/apps/frontend-dashboard/package.json +6 -6
  90. package/templates/apps/frontend-docs/package.json +8 -8
  91. package/templates/apps/frontend-i18n/package.json +6 -6
  92. package/templates/apps/frontend-landing/package.json +7 -7
  93. package/templates/apps/frontend-portal/package.json +7 -7
  94. package/templates/apps/frontend-saas/package.json +7 -7
  95. package/templates/apps/frontend-spa/package.json +6 -6
  96. package/templates/apps/frontend-ssr/package.json +6 -6
  97. package/templates/apps/frontend-ssr-api/package.json +7 -7
  98. package/templates/apps/frontend-static-blog/package.json +8 -8
  99. package/templates/apps/frontend-status/package.json +7 -7
  100. package/templates/apps/mobile-app/package.json +4 -4
  101. package/dist/apiBuild-B83Cb2Rv.js +0 -2
  102. package/dist/build-QKP6Bm0J.js +0 -872
  103. package/dist/dev-Bl9HqtV7.js +0 -3
  104. package/dist/doctorCommand-DrQv9SL3.js +0 -2
  105. package/dist/inspect-B0hL41s0.js +0 -2
  106. package/dist/manifestBuild-UXrnUcXP.js +0 -2
  107. package/dist/serveCommand-Dtb48ffg.js +0 -2
  108. package/dist/updateCommand-DsXEAHbd.js +0 -2
  109. package/dist/webDev-B6ZMX42w.js +0 -2
@@ -1,6 +1,6 @@
1
1
  import { n as e } from "./loadEnv-D9nEOClM.js";
2
2
  import { t } from "./bootRefusal-pgvDxlrU.js";
3
- import { A as n, I as r, M as i, O as a, g as o, j as s, o as c } from "./inspect-DZnan87F.js";
3
+ import { A as n, I as r, M as i, O as a, g as o, j as s, o as c } from "./inspect-PcC5J-Dl.js";
4
4
  import { r as l } from "./appPort-B_HpJ_ck.js";
5
5
  import { M as u, _ as d, c as f, g as p, v as m, y as h } from "./fileConventions-l-RIXbx8.js";
6
6
  import { n as g, t as _ } from "./workspaceDeps-RKEkX92S.js";
@@ -86,15 +86,15 @@ ${n.length > 0 ? n : " // (no public variables declared)"}
86
86
  `, i = P(e, "src");
87
87
  await Fe(i, { recursive: !0 }), await q(P(i, "voltro-env.d.ts"), r, "utf8");
88
88
  }, ht = async (e, t) => {
89
- let n = `${ut}
89
+ let n = JSON.stringify(t, null, 2), r = `${ut}
90
90
  import { __installPublicEnv } from '@voltro/env/public'
91
91
 
92
- export const publicEnvValues = ${JSON.stringify(t, null, 2)} as const
92
+ export const publicEnvValues = ${n} as const
93
93
 
94
94
  // Set the browser/SSR-shared global the \`publicEnv\` accessor reads.
95
95
  __installPublicEnv(publicEnvValues)
96
96
  `;
97
- await Fe(e, { recursive: !0 }), await q(P(e, "env.public.ts"), n, "utf8");
97
+ await Fe(e, { recursive: !0 }), await q(P(e, "env.public.ts"), r, "utf8"), await q(P(e, "env.public.json"), `${n}\n`, "utf8");
98
98
  }, gt = (e, t) => {
99
99
  let n = new Set(Object.keys(Ae(e) ? e.vars : {})), r = /* @__PURE__ */ new Map();
100
100
  for (let [e, i] of (t ?? []).entries()) {
@@ -1298,7 +1298,7 @@ __installPublicEnv(publicEnvValues)
1298
1298
  if (!L(a)) continue;
1299
1299
  let s;
1300
1300
  try {
1301
- let { pages: e } = await Uo(a);
1301
+ let { pages: e } = await Wo(a);
1302
1302
  s = e.map((e) => ({
1303
1303
  pattern: e.pattern,
1304
1304
  params: br(e.pattern)
@@ -2546,12 +2546,20 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2546
2546
  } catch {}
2547
2547
  }
2548
2548
  return [...t].map((e) => ` <link rel="preconnect" href="${e}" crossorigin />`);
2549
- }, Ao = (e) => e.web?.api?.connect === "lazy" ? [" connect: 'lazy',"] : [], jo = (e, t) => e.disableDevtools || !t ? [] : [" ...(Devtools ? { Devtools } : {}),"], Mo = (e) => e.sentry ? ["import { initSentryBrowser } from '@voltro/plugin-sentry/web'"] : [], No = (e) => e.sentry ? [
2550
- "",
2551
- "// Browser-side Sentry (frontend half of the deep integration). Errors +",
2552
- "// browser rpc spans flow to Sentry, sharing the server transactions' trace.",
2553
- `await initSentryBrowser(${JSON.stringify(e.sentry)})`
2554
- ] : [], Po = (e) => e.env ? ["import './env.public'"] : [], Fo = (e) => {
2549
+ }, Ao = (e) => e.web?.api?.connect === "lazy" ? [" connect: 'lazy',"] : [], jo = (e, t) => e.disableDevtools || !t ? [] : [" ...(Devtools ? { Devtools } : {}),"], Mo = (e, t) => {
2550
+ if (!No(e, t)) return [];
2551
+ let n = ["import { initSentryBrowser } from '@voltro/plugin-sentry/web'"], r = typeof e.sentry == "object" ? e.sentry.optionsFrom : void 0;
2552
+ return r !== void 0 && n.push(`import __sentryOptions from '../${r.replace(/^\.?\//, "")}'`), n;
2553
+ }, No = (e, t) => e.sentry !== void 0 || t, Po = (e, t) => {
2554
+ if (!No(e, t)) return [];
2555
+ let { optionsFrom: n, ...r } = e.sentry ?? {};
2556
+ return [
2557
+ "",
2558
+ "// Browser-side Sentry (frontend half of the deep integration). Errors +",
2559
+ "// browser rpc spans flow to Sentry, sharing the server transactions' trace.",
2560
+ n === void 0 ? `await initSentryBrowser(${JSON.stringify(r)})` : `await initSentryBrowser({ ...${JSON.stringify(r)}, ...(await __sentryOptions()) })`
2561
+ ];
2562
+ }, Fo = (e) => e.env ? ["import './env.public'"] : [], Io = (e) => {
2555
2563
  let t = [], n = I(e);
2556
2564
  for (;;) {
2557
2565
  L(P(n, "pnpm-workspace.yaml")) && t.push(n);
@@ -2560,12 +2568,12 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2560
2568
  n = e;
2561
2569
  }
2562
2570
  return t;
2563
- }, Io = re, Lo = (e) => {
2571
+ }, Lo = re, Ro = (e) => {
2564
2572
  if (/\/node_modules\/(?:\.pnpm\/[^/]+\/node_modules\/)?(?:react|react-dom|scheduler)\//.test(e)) return "react";
2565
- }, Ro = /export\s+(?:async\s+)?(?:const|function|let|var)\s+loader\b/, zo = (e) => e.startsWith("(") && e.endsWith(")"), Bo = (e) => e.filter((e) => !zo(e)), Vo = (e) => {
2566
- let t = e.replace(p, "").split("/").filter(Boolean).filter((e) => !zo(e));
2573
+ }, zo = /export\s+(?:async\s+)?(?:const|function|let|var)\s+loader\b/, Bo = (e) => e.startsWith("(") && e.endsWith(")"), Vo = (e) => e.filter((e) => !Bo(e)), Ho = (e) => {
2574
+ let t = e.replace(p, "").split("/").filter(Boolean).filter((e) => !Bo(e));
2567
2575
  return t.length === 0 ? "/" : "/" + t.join("/");
2568
- }, Ho = (e) => `${e.replace(p, "").split("/").filter(Boolean).map((e) => e.replace(/[()]/g, "").replace(/\[\.\.\./g, "_rest_").replace(/[[\]]/g, "_")).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("").replace(/[^A-Za-z0-9_]/g, "_") || "Index"}Page`, $ = (e, t) => e === "" ? `Root${t}` : `${e.split("/").filter(Boolean).map((e) => e.replace(/[()]/g, "").replace(/\[\.\.\./g, "_rest_").replace(/[[\]]/g, "_")).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("").replace(/[^A-Za-z0-9_]/g, "_") || "Root"}${t}`, Uo = async (e) => {
2576
+ }, Uo = (e) => `${e.replace(p, "").split("/").filter(Boolean).map((e) => e.replace(/[()]/g, "").replace(/\[\.\.\./g, "_rest_").replace(/[[\]]/g, "_")).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("").replace(/[^A-Za-z0-9_]/g, "_") || "Index"}Page`, $ = (e, t) => e === "" ? `Root${t}` : `${e.split("/").filter(Boolean).map((e) => e.replace(/[()]/g, "").replace(/\[\.\.\./g, "_rest_").replace(/[[\]]/g, "_")).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("").replace(/[^A-Za-z0-9_]/g, "_") || "Root"}${t}`, Wo = async (e) => {
2569
2577
  let t = [], n = [], r = async (e, i) => {
2570
2578
  let a = [];
2571
2579
  try {
@@ -2573,17 +2581,17 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2573
2581
  } catch {
2574
2582
  return;
2575
2583
  }
2576
- let o = a.filter((e) => e.isDirectory()).map((e) => e.name).sort(), s = a.filter((e) => e.isFile() && e.name.endsWith(".tsx") && !Io(e.name)).map((e) => e.name).sort(), c = new Set(s), l = i.join("/");
2584
+ let o = a.filter((e) => e.isDirectory()).map((e) => e.name).sort(), s = a.filter((e) => e.isFile() && e.name.endsWith(".tsx") && !Lo(e.name)).map((e) => e.name).sort(), c = new Set(s), l = i.join("/");
2577
2585
  if (s.some((e) => d.has(e))) {
2578
2586
  let t = c.has("layout.tsx"), r = !1;
2579
2587
  if (t) try {
2580
2588
  let t = await z.readFile(P(e, "layout.tsx"), "utf8");
2581
- r = Ro.test(t);
2589
+ r = zo.test(t);
2582
2590
  } catch {}
2583
2591
  n.push({
2584
2592
  dir: l,
2585
2593
  chain: i,
2586
- contributesToUrl: i.length === 0 || !zo(i[i.length - 1]),
2594
+ contributesToUrl: i.length === 0 || !Bo(i[i.length - 1]),
2587
2595
  hasLayout: t,
2588
2596
  hasLayoutLoader: r,
2589
2597
  hasError: c.has("error.tsx"),
@@ -2597,8 +2605,8 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2597
2605
  let n = l ? `${l}/${e}` : e;
2598
2606
  t.push({
2599
2607
  file: n,
2600
- pattern: Vo(n),
2601
- ident: Ho(n),
2608
+ pattern: Ho(n),
2609
+ ident: Uo(n),
2602
2610
  dirChain: i
2603
2611
  });
2604
2612
  }
@@ -2617,14 +2625,14 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2617
2625
  dirs: a,
2618
2626
  orphanedSpecials: o
2619
2627
  };
2620
- }, Wo = (e, t) => {
2628
+ }, Go = (e, t) => {
2621
2629
  let n = [];
2622
2630
  for (let r = 0; r <= t.length; r++) {
2623
2631
  let i = t.slice(0, r).join("/"), a = e.find((e) => e.dir === i);
2624
2632
  a && n.push(a);
2625
2633
  }
2626
2634
  return n;
2627
- }, Go = async (e) => {
2635
+ }, Ko = async (e) => {
2628
2636
  let { entries: t, loadLayout: n, loadError: r, loadPending: i, ctx: a } = e, o = {};
2629
2637
  return {
2630
2638
  chain: await Promise.all(t.map(async (e, t) => {
@@ -2648,10 +2656,10 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2648
2656
  })),
2649
2657
  segmentLoaderData: o
2650
2658
  };
2651
- }, Ko = (e) => {
2652
- let t = Bo(e);
2659
+ }, qo = (e) => {
2660
+ let t = Vo(e);
2653
2661
  return t.length === 0 ? "" : "/" + t.join("/");
2654
- }, qo = [
2662
+ }, Jo = [
2655
2663
  "// Hot-reload bridge: keep SSR-injected <link rel=\"stylesheet\"> tags",
2656
2664
  "// in sync with Vite's CSS HMR. Without this, edits to .css files",
2657
2665
  "// only update the JS-injected <style> overlay; the underlying <link>",
@@ -2744,7 +2752,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2744
2752
  " _vdtErrorStart('Build error')",
2745
2753
  " })",
2746
2754
  "}"
2747
- ].join("\n"), Jo = (e) => {
2755
+ ].join("\n"), Yo = (e) => {
2748
2756
  let t = [], n = 0, r = e.split("/").filter(Boolean), i = [];
2749
2757
  for (let e of r) e.startsWith("[[...") && e.endsWith("]]") ? (i.push("(.*)"), t.push(e.slice(5, -2)), n += 2) : e.startsWith("[...") && e.endsWith("]") ? (i.push("(.+)"), t.push(e.slice(4, -1)), n += 3) : e.startsWith("[") && e.endsWith("]") ? (i.push("([^/]+)"), t.push(e.slice(1, -1)), n += 1) : i.push(e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
2750
2758
  let a = i.join("/");
@@ -2753,8 +2761,8 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2753
2761
  paramNames: t,
2754
2762
  priority: n
2755
2763
  };
2756
- }, Yo = (e) => e.map((e) => {
2757
- let { regex: t, paramNames: n, priority: r } = Jo(e.pattern);
2764
+ }, Xo = (e) => e.map((e) => {
2765
+ let { regex: t, paramNames: n, priority: r } = Yo(e.pattern);
2758
2766
  return {
2759
2767
  pattern: e.pattern,
2760
2768
  file: e.file,
@@ -2763,7 +2771,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2763
2771
  paramNames: n,
2764
2772
  priority: r
2765
2773
  };
2766
- }).slice().sort((e, t) => e.priority - t.priority), Xo = (e, t) => {
2774
+ }).slice().sort((e, t) => e.priority - t.priority), Zo = (e, t) => {
2767
2775
  for (let n of t) {
2768
2776
  let t = n.regex.exec(e);
2769
2777
  if (!t) continue;
@@ -2775,7 +2783,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2775
2783
  };
2776
2784
  }
2777
2785
  return null;
2778
- }, Zo = _, Qo = async (e) => {
2786
+ }, Qo = _, $o = async (e) => {
2779
2787
  try {
2780
2788
  let t = await import(G(P(e, "app.config.ts")).href), n = t.default?.port, r = l({
2781
2789
  kind: "api",
@@ -2791,7 +2799,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2791
2799
  kind: "api",
2792
2800
  env: {}
2793
2801
  }).port };
2794
- }, $o = (e, t) => ({
2802
+ }, es = (e, t) => ({
2795
2803
  name: e,
2796
2804
  pkg: t.package,
2797
2805
  browserWsUrl: t.url,
@@ -2799,7 +2807,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2799
2807
  ...t.serverUrl ? { serverOrigin: t.serverUrl } : {},
2800
2808
  ...t.headers ? { headers: t.headers } : {},
2801
2809
  ...t.authHeaders ? { hasAuthHeaders: !0 } : {}
2802
- }), es = (e, t, n) => {
2810
+ }), ts = (e, t, n) => {
2803
2811
  let r = t.transport?.wsPath ?? n.wsPath;
2804
2812
  return {
2805
2813
  name: e,
@@ -2811,7 +2819,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2811
2819
  ...t.headers ? { headers: t.headers } : {},
2812
2820
  ...t.authHeaders ? { hasAuthHeaders: !0 } : {}
2813
2821
  };
2814
- }, ts = (e) => {
2822
+ }, ns = (e) => {
2815
2823
  let t = /* @__PURE__ */ new Map();
2816
2824
  for (let n of e) {
2817
2825
  if (n.proxyTarget === void 0) continue;
@@ -2819,43 +2827,43 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2819
2827
  if (e) throw Error(`[voltro] api ${JSON.stringify(n.name)} and ${JSON.stringify(e)} both resolve the same-origin WS path ${JSON.stringify(n.browserWsUrl)}. A custom transport.wsPath must be unique per origin (one api per origin may use a fixed path). Give each a distinct wsPath, or drop the override to use the default /ws/<name>.`);
2820
2828
  t.set(n.browserWsUrl, n.name);
2821
2829
  }
2822
- }, ns = async (e, t) => {
2830
+ }, rs = async (e, t) => {
2823
2831
  let n = Object.entries(t.apis ?? {}), r = [];
2824
2832
  for (let [t, i] of n) {
2825
2833
  if (i.url) {
2826
- r.push($o(t, i));
2834
+ r.push(es(t, i));
2827
2835
  continue;
2828
2836
  }
2829
- let n = await Zo(e, i.package);
2837
+ let n = await Qo(e, i.package);
2830
2838
  if (!n) throw Error(`[voltro dev] api ${JSON.stringify(t)} (package ${JSON.stringify(i.package)}) is not a workspace dep and has no \`url\` set in app.config.ts. External apis need an explicit \`url\` because the framework can't discover their dev port.`);
2831
- r.push(es(t, i, await Qo(n)));
2839
+ r.push(ts(t, i, await $o(n)));
2832
2840
  }
2833
- return ts(r), r;
2834
- }, rs = (e) => {
2841
+ return ns(r), r;
2842
+ }, is = (e) => {
2835
2843
  if (!(!/^wss?:\/\//.test(e) && !/^https?:\/\//.test(e))) try {
2836
2844
  let t = new URL(e);
2837
2845
  return t.protocol = t.protocol === "wss:" || t.protocol === "https:" ? "https:" : "http:", `${t.protocol}//${t.host}`;
2838
2846
  } catch {
2839
2847
  return;
2840
2848
  }
2841
- }, is = (e) => `VOLTRO_API_ORIGIN_${e.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`, as = (e) => process.env[is(e)] ?? process.env.VOLTRO_API_ORIGIN, os = (e, t) => {
2849
+ }, as = (e) => `VOLTRO_API_ORIGIN_${e.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`, os = (e) => process.env[as(e)] ?? process.env.VOLTRO_API_ORIGIN, ss = (e, t) => {
2842
2850
  if (!e) return;
2843
- let n = as(e.name) ?? e.serverOrigin;
2851
+ let n = os(e.name) ?? e.serverOrigin;
2844
2852
  if (n) {
2845
- let e = rs(n);
2853
+ let e = is(n);
2846
2854
  if (e) return e;
2847
2855
  }
2848
2856
  if (t?.allowDevProxy && e.proxyTarget) {
2849
- let t = rs(e.proxyTarget);
2857
+ let t = is(e.proxyTarget);
2850
2858
  if (t) return t;
2851
2859
  }
2852
- if (!e.proxyTarget && /^wss?:\/\//.test(e.browserWsUrl)) return rs(e.browserWsUrl);
2853
- }, ss = (e, t, n) => {
2860
+ if (!e.proxyTarget && /^wss?:\/\//.test(e.browserWsUrl)) return is(e.browserWsUrl);
2861
+ }, cs = (e, t, n) => {
2854
2862
  if (!e) {
2855
2863
  if (n?.apiName === void 0) return;
2856
2864
  let e = n.apiName;
2857
2865
  return async () => {
2858
- throw Error(`[loader.query] api '${e}' has no server-side origin for SSR. In a split web/api deployment the web pod reaches the api by internal DNS, not the browser's relative wsPath — set ${is(e)} (or VOLTRO_API_ORIGIN, or apis.${e}.serverUrl in app.config.ts) to the api's internal origin, e.g. http://api.<namespace>.svc.cluster.local. The browser transport is unaffected.`);
2866
+ throw Error(`[loader.query] api '${e}' has no server-side origin for SSR. In a split web/api deployment the web pod reaches the api by internal DNS, not the browser's relative wsPath — set ${as(e)} (or VOLTRO_API_ORIGIN, or apis.${e}.serverUrl in app.config.ts) to the api's internal origin, e.g. http://api.<namespace>.svc.cluster.local. The browser transport is unaffected.`);
2859
2867
  };
2860
2868
  }
2861
2869
  return async (r, i) => {
@@ -2878,7 +2886,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2878
2886
  });
2879
2887
  } catch (t) {
2880
2888
  let i = t?.cause?.code ?? String(t?.message ?? t);
2881
- throw Error(`[loader.query] ${r}: could not reach the api at ${e}/rpc (${i}). ${n?.apiName ? `Set ${is(n.apiName)} / VOLTRO_API_ORIGIN` : "Set the api server origin"} to the api's SSR-reachable origin (internal cluster DNS in a split deployment).`, { cause: t });
2889
+ throw Error(`[loader.query] ${r}: could not reach the api at ${e}/rpc (${i}). ${n?.apiName ? `Set ${as(n.apiName)} / VOLTRO_API_ORIGIN` : "Set the api server origin"} to the api's SSR-reachable origin (internal cluster DNS in a split deployment).`, { cause: t });
2882
2890
  }
2883
2891
  if (!o.ok) throw Error(`[loader.query] ${r}: HTTP ${o.status} ${await o.text()}`);
2884
2892
  let s = await o.json();
@@ -2908,7 +2916,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2908
2916
  }
2909
2917
  if (u) return l;
2910
2918
  };
2911
- }, cs = (e, t) => {
2919
+ }, ls = (e, t) => {
2912
2920
  let n = (e) => `${e.replace(/[^a-zA-Z0-9]/g, "_")}AppGroup`, r = (e) => `${e.replace(/[^a-zA-Z0-9]/g, "_")}AppDescriptors`, i = e.map((e) => `import { appGroup as ${n(e.name)}, appDescriptors as ${r(e.name)} } from '${e.pkg}/rpcGroup'`);
2913
2921
  e.some((e) => e.hasAuthHeaders) && (i.push(`import __voltroAppConfig from '${t}'`), i.push("const __voltroAuthHeaders = (__voltroAppConfig as { readonly apis?: Record<string, { readonly authHeaders?: () => Record<string, string> | Promise<Record<string, string>> }> }).apis"));
2914
2922
  let a = (e) => {
@@ -2920,10 +2928,10 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2920
2928
  entryLines: e.map((e) => ` ${JSON.stringify(e.name)}: { ${a(e).join(", ")} },`),
2921
2929
  providerEntryLines: e.map((e) => ` { name: ${JSON.stringify(e.name)}, ${a(e).join(", ")} },`)
2922
2930
  };
2923
- }, ls = async (e, t, n, r = {}) => {
2931
+ }, us = async (e, t, n, r = {}) => {
2924
2932
  let i = t.title ?? t.name ?? "app";
2925
2933
  Gn(e);
2926
- let a = await Vn(e, t.fonts), o = a === null ? [] : [...a.preloads.map((e) => ` <link rel="preload" as="font" type="font/${e.endsWith(".woff2") ? "woff2" : e.endsWith(".woff") ? "woff" : e.endsWith(".otf") ? "otf" : "ttf"}" href="${e}" crossorigin />`), ` <style data-voltro-fonts>${a.css}</style>`], s = t.theme === "dark" ? " class=\"dark\"" : t.theme === "light" ? " class=\"light\"" : "", c = L(P(e, "node_modules", "@voltro", "devtools", "package.json")) || await Do() !== null, l = t.theme === "system" ? [
2934
+ let a = await Vn(e, t.fonts), o = a === null ? [] : [...a.preloads.map((e) => ` <link rel="preload" as="font" type="font/${e.endsWith(".woff2") ? "woff2" : e.endsWith(".woff") ? "woff" : e.endsWith(".otf") ? "otf" : "ttf"}" href="${e}" crossorigin />`), ` <style data-voltro-fonts>${a.css}</style>`], s = t.theme === "dark" ? " class=\"dark\"" : t.theme === "light" ? " class=\"light\"" : "", c = L(P(e, "node_modules", "@voltro", "devtools", "package.json")) || await Do() !== null, l = L(P(e, "node_modules", "@voltro", "plugin-sentry", "package.json")), u = t.theme === "system" ? [
2927
2935
  " <script>",
2928
2936
  " (function () {",
2929
2937
  " var root = document.documentElement",
@@ -2944,7 +2952,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2944
2952
  " }",
2945
2953
  " })()",
2946
2954
  " <\/script>"
2947
- ].join("\n") : "", u = t.timeZone === "viewer" ? [
2955
+ ].join("\n") : "", d = t.timeZone === "viewer" ? [
2948
2956
  " <script>",
2949
2957
  " (function () {",
2950
2958
  " try {",
@@ -2956,7 +2964,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2956
2964
  " } catch (e) {}",
2957
2965
  " })()",
2958
2966
  " <\/script>"
2959
- ].join("\n") : "", d = [
2967
+ ].join("\n") : "", f = [
2960
2968
  "<!doctype html>",
2961
2969
  "<!-- GENERATED BY `voltro dev` — do not edit; will be overwritten on next boot. -->",
2962
2970
  `<html lang="en"${s}>`,
@@ -2967,8 +2975,8 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2967
2975
  " <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />",
2968
2976
  ...ko(n),
2969
2977
  ...o,
2970
- ...l ? [l] : [],
2971
2978
  ...u ? [u] : [],
2979
+ ...d ? [d] : [],
2972
2980
  " </head>",
2973
2981
  " <body>",
2974
2982
  " <div id=\"root\"></div>",
@@ -2976,32 +2984,32 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
2976
2984
  " </body>",
2977
2985
  "</html>",
2978
2986
  ""
2979
- ].join("\n"), f = P(e, "src", "pages"), { pages: p, dirs: m, orphanedSpecials: h } = await Uo(f);
2980
- await x(f, p);
2981
- for (let e of h) Q.warn(`src/pages/${e.dir || "."}/ contains ${e.files.join(", ")} but no page.tsx at or below it — ${e.files.length > 1 ? "those files are" : "that file is"} inert. A layout/error/loading/not-found file only applies to routes in its own directory tree; if this is an ordinary component, rename it so it does not read as a route boundary.`);
2982
- let { imports: g, entryLines: _ } = cs(n, "../app.config"), v, y, ee;
2983
- if (p.length > 0) {
2984
- let n = p.map((e) => ` ${JSON.stringify(e.pattern)}: () => import('../src/pages/${e.file}'),`), r = [];
2985
- for (let e of m) {
2987
+ ].join("\n"), p = P(e, "src", "pages"), { pages: m, dirs: h, orphanedSpecials: g } = await Wo(p);
2988
+ await x(p, m);
2989
+ for (let e of g) Q.warn(`src/pages/${e.dir || "."}/ contains ${e.files.join(", ")} but no page.tsx at or below it — ${e.files.length > 1 ? "those files are" : "that file is"} inert. A layout/error/loading/not-found file only applies to routes in its own directory tree; if this is an ordinary component, rename it so it does not read as a route boundary.`);
2990
+ let { imports: _, entryLines: v } = ls(n, "../app.config"), y, ee, b;
2991
+ if (m.length > 0) {
2992
+ let n = m.map((e) => ` ${JSON.stringify(e.pattern)}: () => import('../src/pages/${e.file}'),`), r = [];
2993
+ for (let e of h) {
2986
2994
  let t = e.dir === "" ? "../src/pages" : `../src/pages/${e.dir}`;
2987
2995
  e.hasLayout && e.hasLayoutLoader ? r.push(`import * as ${$(e.dir, "Layout")}Mod from '${t}/layout.tsx'`) : e.hasLayout && r.push(`import ${$(e.dir, "Layout")} from '${t}/layout.tsx'`), e.hasError && r.push(`import ${$(e.dir, "Error")} from '${t}/error.tsx'`), e.hasPending && r.push(`import ${$(e.dir, "Pending")} from '${t}/loading.tsx'`), e.hasNotFound && r.push(`import ${$(e.dir, "NotFound")} from '${t}/not-found.tsx'`);
2988
2996
  }
2989
- let i = p.map((e) => {
2990
- let t = Wo(m, e.dirChain).map((e) => {
2997
+ let i = m.map((e) => {
2998
+ let t = Go(h, e.dirChain).map((e) => {
2991
2999
  let t = [`id: ${JSON.stringify(e.dir || "/")}`], n = JSON.stringify(e.dir);
2992
3000
  return e.hasLayout && t.push(`Layout: refs.layouts[${n}]`), e.hasLayoutLoader && t.push(`loader: refs.layoutLoaders[${n}]`), e.hasError && t.push(`Error: refs.errors[${n}]`), e.hasPending && t.push(`Pending: refs.pendings[${n}]`), ` { ${t.join(", ")} }`;
2993
3001
  }).join(",\n");
2994
3002
  return ` makePageRoute(${JSON.stringify(e.pattern)}, ${t.length > 0 ? `[\n${t},\n ]` : "undefined"}),`;
2995
- }), a = m.filter((e) => e.hasNotFound).map((e) => {
2996
- let t = Ko(e.chain), n = Wo(m, e.chain).map((e) => {
3003
+ }), a = h.filter((e) => e.hasNotFound).map((e) => {
3004
+ let t = qo(e.chain), n = Go(h, e.chain).map((e) => {
2997
3005
  let t = [`id: ${JSON.stringify(e.dir || "/")}`], n = JSON.stringify(e.dir);
2998
3006
  return e.hasLayout && t.push(`Layout: refs.layouts[${n}]`), e.hasError && t.push(`Error: refs.errors[${n}]`), ` { ${t.join(", ")} }`;
2999
3007
  }).join(",\n"), r = n.length > 0 ? `\n chain: [\n${n},\n ],` : "";
3000
3008
  return ` {\n prefix: ${JSON.stringify(t)},\n Component: refs.notFounds[${JSON.stringify(e.dir)}],${r}\n },`;
3001
- }), o = [], s = [], l = [], u = [], d = [], f = [];
3002
- for (let e of m) {
3009
+ }), o = [], s = [], u = [], d = [], f = [], p = [];
3010
+ for (let e of h) {
3003
3011
  let t = e.dir === "" ? "../src/pages/layout.tsx" : `../src/pages/${e.dir}/layout.tsx`, n = e.dir === "" ? "../src/pages/error.tsx" : `../src/pages/${e.dir}/error.tsx`, r = e.dir === "" ? "../src/pages/loading.tsx" : `../src/pages/${e.dir}/loading.tsx`, i = e.dir === "" ? "../src/pages/not-found.tsx" : `../src/pages/${e.dir}/not-found.tsx`, a = JSON.stringify(e.dir);
3004
- e.hasLayout && e.hasLayoutLoader ? (s.push(` ${a}: ${$(e.dir, "Layout")}Mod.default,`), l.push(` ${a}: ${$(e.dir, "Layout")}Mod.loader,`), o.push({
3012
+ e.hasLayout && e.hasLayoutLoader ? (s.push(` ${a}: ${$(e.dir, "Layout")}Mod.default,`), u.push(` ${a}: ${$(e.dir, "Layout")}Mod.loader,`), o.push({
3005
3013
  relPath: t,
3006
3014
  kind: "layout",
3007
3015
  slotKey: e.dir
@@ -3009,37 +3017,37 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3009
3017
  relPath: t,
3010
3018
  kind: "layout",
3011
3019
  slotKey: e.dir
3012
- })), e.hasError && (u.push(` ${a}: ${$(e.dir, "Error")},`), o.push({
3020
+ })), e.hasError && (d.push(` ${a}: ${$(e.dir, "Error")},`), o.push({
3013
3021
  relPath: n,
3014
3022
  kind: "error",
3015
3023
  slotKey: e.dir
3016
- })), e.hasPending && (d.push(` ${a}: ${$(e.dir, "Pending")},`), o.push({
3024
+ })), e.hasPending && (f.push(` ${a}: ${$(e.dir, "Pending")},`), o.push({
3017
3025
  relPath: r,
3018
3026
  kind: "pending",
3019
3027
  slotKey: e.dir
3020
- })), e.hasNotFound && (f.push(` ${a}: ${$(e.dir, "NotFound")},`), o.push({
3028
+ })), e.hasNotFound && (p.push(` ${a}: ${$(e.dir, "NotFound")},`), o.push({
3021
3029
  relPath: i,
3022
3030
  kind: "notFound",
3023
3031
  slotKey: e.dir
3024
3032
  }));
3025
3033
  }
3026
- for (let e of p) o.push({
3034
+ for (let e of m) o.push({
3027
3035
  relPath: `../src/pages/${e.file}`,
3028
3036
  kind: "page",
3029
3037
  slotKey: e.pattern
3030
3038
  });
3031
- let h = o.map((e) => ` ${JSON.stringify(e.relPath)},`), b = o.map((e, t) => {
3039
+ let g = o.map((e) => ` ${JSON.stringify(e.relPath)},`), x = o.map((e, t) => {
3032
3040
  let n = JSON.stringify(e.slotKey);
3033
3041
  return e.kind === "page" ? ` if (newMods[${t}] && refs.pages[${n}] !== undefined) { refs.pages[${n}] = newMods[${t}]; changed = true }` : ` if (newMods[${t}]?.default) { refs.${e.kind === "layout" ? "layouts" : e.kind === "error" ? "errors" : e.kind === "pending" ? "pendings" : "notFounds"}[${n}] = newMods[${t}].default;${e.kind === "layout" ? ` refs.layoutLoaders[${n}] = newMods[${t}].loader;` : ""} changed = true }`;
3034
- }), x = t.locales ?? [], S = x.length > 0 && x.every((t) => L(P(e, "src", "locales", `${t}.ts`)) || L(P(e, "src", "locales", `${t}.tsx`))), C = S, w = t.defaultLocale ?? x[0] ?? "en", T = t.router?.viewTransitions === !0 ? " viewTransitions" : "";
3035
- if (x.length > 0 && !S && Q.info(`i18n: locales [${x.join(", ")}] declared with no src/locales/<code>.ts — using them for <html lang> and the resolved locale only. Add the catalogs to get <I18nProvider> wired, or keep your own i18n stack.`), x.length > 0 && !x.includes(w)) throw Error(`[voltro dev] app.config.ts: defaultLocale '${w}' must be one of locales [${x.join(", ")}]`);
3036
- let E = C ? [
3042
+ }), S = t.locales ?? [], C = S.length > 0 && S.every((t) => L(P(e, "src", "locales", `${t}.ts`)) || L(P(e, "src", "locales", `${t}.tsx`))), w = C, T = t.defaultLocale ?? S[0] ?? "en", E = t.router?.viewTransitions === !0 ? " viewTransitions" : "";
3043
+ if (S.length > 0 && !C && Q.info(`i18n: locales [${S.join(", ")}] declared with no src/locales/<code>.ts — using them for <html lang> and the resolved locale only. Add the catalogs to get <I18nProvider> wired, or keep your own i18n stack.`), S.length > 0 && !S.includes(T)) throw Error(`[voltro dev] app.config.ts: defaultLocale '${T}' must be one of locales [${S.join(", ")}]`);
3044
+ let D = w ? [
3037
3045
  "import { I18nProvider } from '@voltro/i18n'",
3038
3046
  "import { resolveLocale } from '@voltro/i18n/server'",
3039
3047
  "import { useServerRequest } from '@voltro/web'",
3040
- ...x.map((e) => `import i18nCatalog_${e} from '../src/locales/${e}'`)
3048
+ ...S.map((e) => `import i18nCatalog_${e} from '../src/locales/${e}'`)
3041
3049
  ] : [];
3042
- y = [
3050
+ ee = [
3043
3051
  "// GENERATED BY `voltro dev` — do not edit; will be overwritten on next boot.",
3044
3052
  "// Routes + per-page chain + not-found scopes assembled from src/pages/.",
3045
3053
  "//",
@@ -3076,16 +3084,16 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3076
3084
  ...s,
3077
3085
  " },",
3078
3086
  " layoutLoaders: {",
3079
- ...l,
3087
+ ...u,
3080
3088
  " },",
3081
3089
  " errors: {",
3082
- ...u,
3090
+ ...d,
3083
3091
  " },",
3084
3092
  " pendings: {",
3085
- ...d,
3093
+ ...f,
3086
3094
  " },",
3087
3095
  " notFounds: {",
3088
- ...f,
3096
+ ...p,
3089
3097
  " },",
3090
3098
  " pages: {},",
3091
3099
  "}",
@@ -3150,17 +3158,17 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3150
3158
  "if (import.meta.hot) {",
3151
3159
  " import.meta.hot.accept(",
3152
3160
  " [",
3153
- ...h,
3161
+ ...g,
3154
3162
  " ],",
3155
3163
  " (newMods: Array<Record<string, unknown> | undefined>) => {",
3156
3164
  " let changed = false",
3157
- ...b,
3165
+ ...x,
3158
3166
  " if (changed) bump()",
3159
3167
  " },",
3160
3168
  " )",
3161
3169
  "}",
3162
3170
  ""
3163
- ].join("\n"), v = [
3171
+ ].join("\n"), y = [
3164
3172
  "// GENERATED BY `voltro dev` — do not edit; will be overwritten on next boot.",
3165
3173
  "// The app's root component. It exports `App` and NOTHING ELSE: react-refresh",
3166
3174
  "// only accepts a module whose every export is a component, and a module it",
@@ -3171,16 +3179,16 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3171
3179
  "",
3172
3180
  "import { useMemo, useSyncExternalStore } from 'react'",
3173
3181
  "import { Router } from '@voltro/web'",
3174
- ...E,
3182
+ ...D,
3175
3183
  "import { subscribe, getVersion, buildRoutes, buildNotFounds } from './routeTable'",
3176
3184
  "",
3177
- ...C ? [
3178
- `// i18n auto-wiring (config.locales = [${x.map((e) => JSON.stringify(e)).join(", ")}]).`,
3185
+ ...w ? [
3186
+ `// i18n auto-wiring (config.locales = [${S.map((e) => JSON.stringify(e)).join(", ")}]).`,
3179
3187
  "const I18N_CATALOGS: Record<string, import('@voltro/i18n').MessageCatalog> = {",
3180
- ...x.map((e) => ` ${JSON.stringify(e)}: i18nCatalog_${e},`),
3188
+ ...S.map((e) => ` ${JSON.stringify(e)}: i18nCatalog_${e},`),
3181
3189
  "}",
3182
- `const I18N_SUPPORTED: ReadonlyArray<string> = [${x.map((e) => JSON.stringify(e)).join(", ")}]`,
3183
- `const I18N_DEFAULT = ${JSON.stringify(w)}`,
3190
+ `const I18N_SUPPORTED: ReadonlyArray<string> = [${S.map((e) => JSON.stringify(e)).join(", ")}]`,
3191
+ `const I18N_DEFAULT = ${JSON.stringify(T)}`,
3184
3192
  "",
3185
3193
  "// The zone + clock the SERVER decided for this document, read back off",
3186
3194
  "// <html>. Same contract as <html lang> below and for the same reason:",
@@ -3254,7 +3262,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3254
3262
  " const v = useSyncExternalStore(subscribe, getVersion, getVersion)",
3255
3263
  " const routes = useMemo(() => buildRoutes(), [v])",
3256
3264
  " const notFounds = useMemo(() => buildNotFounds(), [v])",
3257
- ...C ? [
3265
+ ...w ? [
3258
3266
  " const ssr = useServerRequest()",
3259
3267
  " const locale = pickI18nLocale(ssr)",
3260
3268
  " const messages = I18N_CATALOGS[locale] ?? I18N_CATALOGS[I18N_DEFAULT]!",
@@ -3269,26 +3277,26 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3269
3277
  " defaultLocale={I18N_DEFAULT}",
3270
3278
  " {...published}",
3271
3279
  " >",
3272
- ` <Router routes={routes} notFounds={notFounds}${T} />`,
3280
+ ` <Router routes={routes} notFounds={notFounds}${E} />`,
3273
3281
  " </I18nProvider>",
3274
3282
  " )"
3275
- ] : [` return <Router routes={routes} notFounds={notFounds}${T} />`],
3283
+ ] : [` return <Router routes={routes} notFounds={notFounds}${E} />`],
3276
3284
  "}",
3277
3285
  ""
3278
- ].join("\n"), ee = [
3286
+ ].join("\n"), b = [
3279
3287
  "// GENERATED BY `voltro dev` — do not edit; will be overwritten on next boot.",
3280
3288
  "// Entry: imports the generated App component and calls mount() with the",
3281
3289
  "// resolved apis map. Side-effect only — no local component definitions —",
3282
3290
  "// so downstream invalidations (user page edits) terminate at app.tsx.",
3283
3291
  "",
3284
- ...Po(t),
3292
+ ...Fo(t),
3285
3293
  "import { mount } from '@voltro/web'",
3286
- ...g,
3294
+ ..._,
3287
3295
  "import { App } from './app'",
3288
3296
  "import { preloadCurrentRoute } from './routeTable'",
3289
- ...Mo(t),
3297
+ ...Mo(t, l),
3290
3298
  ...Oo(t, c),
3291
- ...No(t),
3299
+ ...Po(t, l),
3292
3300
  "",
3293
3301
  "// Preload the current route's page chunk BEFORE hydration so the first",
3294
3302
  "// client render matches the SSG/SSR-prerendered HTML. Without it, a lazy",
@@ -3303,54 +3311,54 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3303
3311
  "",
3304
3312
  "mount(App, {",
3305
3313
  " apis: {",
3306
- ..._,
3314
+ ...v,
3307
3315
  " },",
3308
3316
  ...jo(t, c),
3309
3317
  ...Ao(t),
3310
3318
  "})",
3311
3319
  "",
3312
- qo
3320
+ Jo
3313
3321
  ].join("\n");
3314
- } else v = "", y = "", ee = [
3322
+ } else y = "", ee = "", b = [
3315
3323
  "// GENERATED BY `voltro dev` — do not edit; will be overwritten on next boot.",
3316
3324
  "// The user's root component lives at ../src/App.tsx; this file imports it",
3317
3325
  "// and calls mount(). Side-effect only, no component definitions, so the",
3318
3326
  "// user's component file remains the Fast Refresh boundary.",
3319
3327
  "",
3320
- ...Po(t),
3328
+ ...Fo(t),
3321
3329
  "import { mount } from '@voltro/web'",
3322
- ...g,
3330
+ ..._,
3323
3331
  "import App from '../src/App'",
3324
- ...Mo(t),
3332
+ ...Mo(t, l),
3325
3333
  ...Oo(t, c),
3326
- ...No(t),
3334
+ ...Po(t, l),
3327
3335
  "",
3328
3336
  "mount(App, {",
3329
3337
  " apis: {",
3330
- ..._,
3338
+ ...v,
3331
3339
  " },",
3332
3340
  ...jo(t, c),
3333
3341
  ...Ao(t),
3334
3342
  "})",
3335
3343
  "",
3336
- qo
3344
+ Jo
3337
3345
  ].join("\n");
3338
- let b = P(e, ".framework");
3339
- await z.mkdir(b, { recursive: !0 }), t.env ? await ht(b, r) : await z.rm(P(b, "env.public.ts"), { force: !0 }), await z.writeFile(P(b, "index.html"), d, "utf8"), await z.writeFile(P(b, "main.tsx"), ee, "utf8"), v ? (await z.writeFile(P(b, "app.tsx"), v, "utf8"), await z.writeFile(P(b, "routeTable.ts"), y, "utf8")) : (await z.rm(P(b, "app.tsx"), { force: !0 }), await z.rm(P(b, "routeTable.ts"), { force: !0 }));
3340
- let S = P(e, ".gitignore"), C = "";
3346
+ let S = P(e, ".framework");
3347
+ await z.mkdir(S, { recursive: !0 }), t.env ? await ht(S, r) : await z.rm(P(S, "env.public.ts"), { force: !0 }), await z.writeFile(P(S, "index.html"), f, "utf8"), await z.writeFile(P(S, "main.tsx"), b, "utf8"), y ? (await z.writeFile(P(S, "app.tsx"), y, "utf8"), await z.writeFile(P(S, "routeTable.ts"), ee, "utf8")) : (await z.rm(P(S, "app.tsx"), { force: !0 }), await z.rm(P(S, "routeTable.ts"), { force: !0 }));
3348
+ let C = P(e, ".gitignore"), w = "";
3341
3349
  try {
3342
- C = await z.readFile(S, "utf8");
3350
+ w = await z.readFile(C, "utf8");
3343
3351
  } catch {}
3344
- let w = ["/.framework/"], T = new Set(C.split("\n").map((e) => e.trim())), E = w.filter((e) => !T.has(e));
3345
- if (E.length > 0) {
3346
- let e = C === "" || C.endsWith("\n") ? "" : "\n";
3352
+ let T = ["/.framework/"], E = new Set(w.split("\n").map((e) => e.trim())), D = T.filter((e) => !E.has(e));
3353
+ if (D.length > 0) {
3354
+ let e = w === "" || w.endsWith("\n") ? "" : "\n";
3347
3355
  try {
3348
- await z.appendFile(S, `${e}${E.join("\n")}\n`, "utf8");
3356
+ await z.appendFile(C, `${e}${D.join("\n")}\n`, "utf8");
3349
3357
  } catch (e) {
3350
- Q.warn(`could not add ${E.join(", ")} to ${S} — add it by hand so the generated directory stays out of git. The build is unaffected.`, { reason: e?.message ?? String(e) });
3358
+ Q.warn(`could not add ${D.join(", ")} to ${C} — add it by hand so the generated directory stays out of git. The build is unaffected.`, { reason: e?.message ?? String(e) });
3351
3359
  }
3352
3360
  }
3353
- }, us = async (e, t) => {
3361
+ }, ds = async (e, t) => {
3354
3362
  try {
3355
3363
  let n = N(e), r = P(n, "project.json");
3356
3364
  if (!L(r)) return;
@@ -3363,26 +3371,26 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3363
3371
  projectJson: r
3364
3372
  });
3365
3373
  } catch {}
3366
- }, ds = (e) => P(e, ".framework", "dist", "server", "appConfig.js"), fs = async (e, t = {}) => {
3367
- let n = ds(e), r = t.preferSource !== !0 && L(n) ? n : P(e, "app.config.ts");
3374
+ }, fs = (e) => P(e, ".framework", "dist", "server", "appConfig.js"), ps = async (e, t = {}) => {
3375
+ let n = fs(e), r = t.preferSource !== !0 && L(n) ? n : P(e, "app.config.ts");
3368
3376
  try {
3369
3377
  let e = (await import(G(r).href)).default;
3370
3378
  return typeof e == "object" && e && "type" in e && e.type === "web" ? e : null;
3371
3379
  } catch {
3372
3380
  return null;
3373
3381
  }
3374
- }, ps = (e) => {
3382
+ }, ms = (e) => {
3375
3383
  let t = e.restRoutes;
3376
3384
  if (t !== void 0 && t.length !== 0) return `app.config.ts declares ${t.length} restRoute(s) on a type:'web' app — REST routes mount on the API process and were silently ignored here, so a health probe hitting one gets the SPA shell (200) and a POST gets 404.\n Move them to the API app's app.config.ts (they mount under the api origin), and call them there;\n for same-origin paths in front of the web app, terminate them at your ingress/proxy instead.`;
3377
- }, ms = async (t) => {
3378
- let u = await fs(t, { preferSource: !0 });
3385
+ }, hs = async (t) => {
3386
+ let u = await ps(t, { preferSource: !0 });
3379
3387
  if (!u) return {
3380
3388
  ran: !1,
3381
3389
  exitCode: 0
3382
3390
  };
3383
3391
  let d = xo(u.health, () => !0);
3384
3392
  {
3385
- let e = ps(u);
3393
+ let e = ms(u);
3386
3394
  if (e !== void 0) return Q.error(`voltro dev (web): refusing to boot — ${e}`), {
3387
3395
  ran: !0,
3388
3396
  exitCode: 1
@@ -3391,7 +3399,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3391
3399
  bi();
3392
3400
  let f = at();
3393
3401
  Qr(f), Q.info("booting voltro dev (web)", { app: u.name ?? "(unnamed)" }), Vt(t, Q);
3394
- let p = await ns(t, u);
3402
+ let p = await rs(t, u);
3395
3403
  for (let e of p) Q.info("api wired", {
3396
3404
  name: e.name,
3397
3405
  pkg: e.pkg,
@@ -3410,7 +3418,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3410
3418
  log: Q,
3411
3419
  generateTypes: !0
3412
3420
  }), h = m.publicValues;
3413
- await ls(t, u, p, h), Q.debug("generated framework entry files", { files: [
3421
+ await us(t, u, p, h), Q.debug("generated framework entry files", { files: [
3414
3422
  "index.html",
3415
3423
  ".framework/main.tsx",
3416
3424
  ...u.env ? [".framework/env.public.ts"] : []
@@ -3431,8 +3439,8 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3431
3439
  pid: process.pid,
3432
3440
  status: "booting",
3433
3441
  logFile: w.path
3434
- }).catch(() => {}), await us(t, x);
3435
- let { pages: T, dirs: E } = await Uo(P(t, "src", "pages")), D = null, te = u.fonts !== void 0 && u.fonts.length > 0 ? yr({
3442
+ }).catch(() => {}), await ds(t, x);
3443
+ let { pages: T, dirs: E } = await Wo(P(t, "src", "pages")), D = null, te = u.fonts !== void 0 && u.fonts.length > 0 ? yr({
3436
3444
  root: t,
3437
3445
  fonts: u.fonts,
3438
3446
  cache: ee(),
@@ -3446,7 +3454,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3446
3454
  }
3447
3455
  }) : null, k = {
3448
3456
  dirs: E,
3449
- ssrRoutes: Yo(T)
3457
+ ssrRoutes: Xo(T)
3450
3458
  }, ne = E.map((e) => ({
3451
3459
  dir: e.dir,
3452
3460
  hasLayout: e.hasLayout,
@@ -3481,12 +3489,12 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3481
3489
  }, B = P(t, "src", "pages"), V = (e) => e.endsWith(".tsx") && (e === B || e.startsWith(B + "/")), H = (e) => e === B || e.startsWith(B + "/"), fe = async (e) => {
3482
3490
  try {
3483
3491
  let n = Date.now();
3484
- await ls(t, u, p, h);
3485
- let { pages: r, dirs: i } = await Uo(B);
3492
+ await us(t, u, p, h);
3493
+ let { pages: r, dirs: i } = await Wo(B);
3486
3494
  await kr({
3487
3495
  appRoot: t,
3488
3496
  pages: r
3489
- }), k.dirs = i, k.ssrRoutes = Yo(r), Q.info("page tree regenerated", {
3497
+ }), k.dirs = i, k.ssrRoutes = Xo(r), Q.info("page tree regenerated", {
3490
3498
  reason: e,
3491
3499
  ms: Date.now() - n,
3492
3500
  routes: k.ssrRoutes.length
@@ -3586,7 +3594,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3586
3594
  }
3587
3595
  try {
3588
3596
  let r = Date.now();
3589
- await ls(t, e, await ns(t, e)), me = e, n.ws && n.ws.send({
3597
+ await us(t, e, await rs(t, e)), me = e, n.ws && n.ws.send({
3590
3598
  type: "full-reload",
3591
3599
  path: "*"
3592
3600
  }), Q.info("app.config.ts soft-regen", {
@@ -3625,7 +3633,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
3625
3633
  async configureServer(e) {
3626
3634
  let n = [];
3627
3635
  for (let r of p) {
3628
- let i = await Zo(t, r.pkg);
3636
+ let i = await Qo(t, r.pkg);
3629
3637
  if (i) {
3630
3638
  n.push(i);
3631
3639
  try {
@@ -4035,12 +4043,12 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4035
4043
  } catch {}
4036
4044
  })(), o), c = mn({
4037
4045
  apiName: p[0]?.name,
4038
- apiOrigin: os(p[0], { allowDevProxy: !0 }),
4046
+ apiOrigin: ss(p[0], { allowDevProxy: !0 }),
4039
4047
  loadDescriptors: async () => {
4040
4048
  let e = p[0];
4041
4049
  if (e !== void 0) return (await n(`${e.pkg}/rpcGroup`)).appDescriptors;
4042
4050
  },
4043
- rpcQuery: (e, t) => ss(os(p[0], { allowDevProxy: !0 }), e, {
4051
+ rpcQuery: (e, t) => cs(ss(p[0], { allowDevProxy: !0 }), e, {
4044
4052
  apiName: p[0]?.name,
4045
4053
  headers: t
4046
4054
  }),
@@ -4094,7 +4102,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4094
4102
  for (let e of n.setCookies) o.appendHeader("set-cookie", Ha(e));
4095
4103
  };
4096
4104
  if (/\.[a-z0-9]+$/i.test(d)) return await h(), c();
4097
- let g = Xo(d, k.ssrRoutes), _ = !1;
4105
+ let g = Zo(d, k.ssrRoutes), _ = !1;
4098
4106
  if (!g) {
4099
4107
  let e = Eo(k.dirs, d);
4100
4108
  if (e === void 0) return await h(), c();
@@ -4130,7 +4138,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4130
4138
  }
4131
4139
  }, C = !1;
4132
4140
  try {
4133
- let w = await x("loadPage", async () => await n(P(t, "src", "pages", g.route.file))), T = _ ? "ssr" : y(w, P("src", "pages", g.route.file)), E = Wo(k.dirs, g.route.dirChain), D = T === "spa" && E.some((e) => e.hasLayout), O = w.interactive ?? "full";
4141
+ let w = await x("loadPage", async () => await n(P(t, "src", "pages", g.route.file))), T = _ ? "ssr" : y(w, P("src", "pages", g.route.file)), E = Go(k.dirs, g.route.dirChain), D = T === "spa" && E.some((e) => e.hasLayout), O = w.interactive ?? "full";
4134
4142
  if (T !== "ssr" && T !== "isr" && !D && O !== "islands") return await h(), c();
4135
4143
  let ne = {};
4136
4144
  for (let [e, t] of Object.entries(r.headers ?? {})) typeof t == "string" ? ne[e] = t : Array.isArray(t) && (ne[e] = t.join(", "));
@@ -4147,14 +4155,14 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4147
4155
  for (let e of A.setCookies) o.appendHeader("set-cookie", Ha(e));
4148
4156
  for (let [e, t] of Object.entries(A.responseHeaders)) o.setHeader(e, t);
4149
4157
  if (T === "isr" && A.cspNonce !== void 0) throw Error(`page '${g.route.pattern}' is renderMode 'isr' while middleware.ts mints a cspNonce — an isr render is CACHED and a cached nonce is a lie the browser enforces. Use renderMode 'ssr' for nonce'd pages, or a hash-based CSP for isr.`);
4150
- let j = T === "isr" ? Ya(A.headers) : A.headers, M = T === "isr" && w.ppr === !0, re = M ? go(j, g.route.pattern) : j, ie = ss(os(p[0], { allowDevProxy: !0 }), j.cookie, {
4158
+ let j = T === "isr" ? Ya(A.headers) : A.headers, M = T === "isr" && w.ppr === !0, re = M ? go(j, g.route.pattern) : j, ie = cs(ss(p[0], { allowDevProxy: !0 }), j.cookie, {
4151
4159
  apiName: p[0]?.name,
4152
4160
  headers: j
4153
4161
  }), ae = new AbortController(), oe = () => ae.abort();
4154
4162
  o.once("close", oe);
4155
4163
  let se = null, ce = [], le = {};
4156
4164
  try {
4157
- (await n("@voltro/web/ssr")).beginStoreSeeds(), await s(), {chain: ce, segmentLoaderData: le} = await x("loadChain", () => Go({
4165
+ (await n("@voltro/web/ssr")).beginStoreSeeds(), await s(), {chain: ce, segmentLoaderData: le} = await x("loadChain", () => Ko({
4158
4166
  entries: E,
4159
4167
  loadLayout: (e) => n(P(t, "src", "pages", e.dir, "layout.tsx")),
4160
4168
  loadError: (e) => n(P(t, "src", "pages", e.dir, "error.tsx")),
@@ -4255,7 +4263,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4255
4263
  for (let t of n) await S(t, e, B);
4256
4264
  });
4257
4265
  let V = (O === "islands" ? await x("islandsEntry", async () => {
4258
- let e = cs(p, "../../app.config"), n = (await jn({
4266
+ let e = ls(p, "../../app.config"), n = (await jn({
4259
4267
  webRoot: t,
4260
4268
  pages: [{
4261
4269
  pattern: g.route.pattern,
@@ -4361,7 +4369,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4361
4369
  let a = m === void 0 ? i : on(i, m);
4362
4370
  if (M && m === void 0 && typeof w.loader == "function") {
4363
4371
  o.setHeader("x-voltro-ppr", "shell+holes"), oo(g.route.pattern), o.write(a);
4364
- let e = ss(os(p[0], { allowDevProxy: !0 }), A.headers.cookie, {
4372
+ let e = cs(ss(p[0], { allowDevProxy: !0 }), A.headers.cookie, {
4365
4373
  apiName: p[0]?.name,
4366
4374
  headers: A.headers
4367
4375
  }), t = new AbortController();
@@ -4421,7 +4429,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4421
4429
  }, Pe = Date.now(), Fe = j.version ?? "?", Ie = await Do(), q = P(t, "node_modules", "@voltro", "ui-shadcn"), J = L(P(q, "package.json")) ? q : null, Le = [], Re = await (async () => {
4422
4430
  let e = [];
4423
4431
  for (let n of p) {
4424
- let r = await Zo(t, n.pkg);
4432
+ let r = await Qo(t, n.pkg);
4425
4433
  r && e.push(r);
4426
4434
  }
4427
4435
  let n = await Da(e);
@@ -4517,7 +4525,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4517
4525
  host: !0,
4518
4526
  allowedHosts: u.allowedHosts === !0 || [...u.allowedHosts ?? []],
4519
4527
  proxy: Se,
4520
- fs: { allow: [t, ...Fo(t)] }
4528
+ fs: { allow: [t, ...Io(t)] }
4521
4529
  },
4522
4530
  appType: "spa",
4523
4531
  cacheDir: P(N, ".vite"),
@@ -4572,8 +4580,8 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4572
4580
  url: `http://localhost:${x}`,
4573
4581
  bootMs: Date.now() - Pe
4574
4582
  }), new Promise(() => {});
4575
- }, hs = async (e) => {
4576
- let t = await fs(e);
4583
+ }, gs = async (e) => {
4584
+ let t = await ps(e);
4577
4585
  return t ? (Q.child("serve").error("`voltro serve` is the production server for an API app — this is a WEB app.\n\n production voltro start .\n development voltro dev .\n\nBoth read the artefacts `voltro build` produces. `serve` cannot: it has no\nSSR bundle to render from, and building a second time here would overwrite\nthe one `voltro build` wrote.", {
4578
4586
  app: t.name ?? "(unnamed)",
4579
4587
  root: e
@@ -4586,4 +4594,4 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
4586
4594
  };
4587
4595
  };
4588
4596
  //#endregion
4589
- export { Qi as $, _o as A, On as At, Ga as B, Bt, ps as C, Gn as Ct, xo as D, En as Dt, So as E, jn as Et, La as F, $t as Ft, ma as G, St as Gt, Ha as H, kt as Ht, Ua as I, Qt as It, ya as J, pt as Jt, pa as K, yt as Kt, Wa as L, Lt, oo as M, un as Mt, Xa as N, mn as Nt, vo as O, kn as Ot, Ya as P, tn as Pt, Zi as Q, st as Qt, Ia as R, zt as Rt, Uo as S, nr as St, Co as T, Rn as Tt, Sa as U, wt as Ut, Fa as V, Vt, _a as W, xt as Wt, Yi as X, ot as Xt, qi as Y, lt as Yt, Xi as Z, at as Zt, es as _, yr as _t, Eo as a, _i as at, hs as b, mr as bt, Go as c, oi as ct, Vo as d, Kr as dt, $i as et, $ as f, qr as ft, $o as g, vr as gt, ns as h, kr as ht, ts as i, Fi as it, go as j, We as jt, yo as k, Tn as kt, ds as l, Qr as lt, fs as m, Pr as mt, os as n, Pi as nt, cs as o, fi as ot, Ho as p, Fr as pt, ha as q, mt as qt, ko as r, Gi as rt, ss as s, ci as st, Ro as t, Ii as tt, Wo as u, Wr as ut, is as v, _r as vt, ls as w, Vn as wt, Lo as x, ar as xt, ms as y, pr as yt, Ra as z, Rt as zt };
4597
+ export { Yi as $, ot as $t, xo as A, En as At, Wa as B, Lt as Bt, gs as C, mr as Ct, us as D, Vn as Dt, ms as E, Gn as Et, oo as F, un as Ft, Ha as G, kt as Gt, Ra as H, Rt as Ht, Xa as I, mn as It, ma as J, St as Jt, Sa as K, wt as Kt, Ya as L, tn as Lt, yo as M, Tn as Mt, _o as N, On as Nt, Co as O, Rn as Ot, go as P, We as Pt, qi as Q, lt as Qt, La as R, $t as Rt, hs as S, pr as St, Wo as T, nr as Tt, Ga as U, Bt as Ut, Ia as V, zt as Vt, Fa as W, Vt as Wt, ha as X, mt as Xt, pa as Y, yt as Yt, ya as Z, pt as Zt, ts as _, Pr as _t, Eo as a, Pi as at, Po as b, yr as bt, Ko as c, _i as ct, Ho as d, oi as dt, at as en, Xi as et, $ as f, Qr as ft, es as g, Fr as gt, rs as h, qr as ht, ns as i, Ii as it, vo as j, kn as jt, So as k, jn as kt, fs as l, fi as lt, ps as m, Kr as mt, ss as n, Qi as nt, ls as o, Gi as ot, Uo as p, Wr as pt, _a as q, xt as qt, ko as r, $i as rt, cs as s, Fi as st, zo as t, st as tn, Zi as tt, Go as u, ci as ut, No as v, kr as vt, Ro as w, ar as wt, as as x, _r as xt, Mo as y, vr as yt, Ua as z, Qt as zt };