@voltro/cli 0.7.0 → 0.8.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 (60) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/apiBuild-CmtI6Ygh.js +2 -0
  3. package/dist/{apiBuild-deIjJKJm.js → apiBuild-OrDkXGFX.js} +2 -2
  4. package/dist/bin.js +2 -2
  5. package/dist/{commands-BHdc71DG.js → commands-DT4PipBz.js} +666 -526
  6. package/dist/dev-Ca1jdwnt.js +2 -0
  7. package/dist/{dev-Bqyq62Ak.js → dev-Ga65K4jc.js} +469 -469
  8. package/dist/index.js +1 -1
  9. package/dist/{inspectMetrics-BLUBAWLb.js → inspectMetrics-EK0CcHyu.js} +977 -759
  10. package/dist/{serveCommand-Dn6--0Bv.js → serveCommand-CR5xnYZt.js} +5 -5
  11. package/dist/serveEntry.js +3 -3
  12. package/dist/{start-CWZW_4Et.js → start-DuyBQxW3.js} +332 -296
  13. package/dist/startEntry.js +2 -2
  14. package/package.json +17 -17
  15. package/templates/AGENTS.core.md +15 -4
  16. package/templates/AGENTS.md +15 -4
  17. package/templates/agent-docs/authentication.md +7 -4
  18. package/templates/agent-docs/cli.md +35 -4
  19. package/templates/agent-docs/data.md +94 -70
  20. package/templates/agent-docs/database/schema.md +1 -1
  21. package/templates/agent-docs/reference.md +20 -0
  22. package/templates/agent-docs/routing.md +158 -24
  23. package/templates/agent-docs/testing.md +24 -0
  24. package/templates/apps/api-ai/package.json +7 -7
  25. package/templates/apps/api-auth/package.json +8 -8
  26. package/templates/apps/api-backend/package.json +7 -7
  27. package/templates/apps/api-backend-deactivation/package.json +7 -7
  28. package/templates/apps/api-backend-mail/package.json +8 -8
  29. package/templates/apps/api-backend-mariadb/package.json +9 -9
  30. package/templates/apps/api-backend-storage/package.json +8 -8
  31. package/templates/apps/api-data-advanced/package.json +8 -8
  32. package/templates/apps/api-durable/package.json +8 -8
  33. package/templates/apps/api-feature-flags/package.json +9 -9
  34. package/templates/apps/api-governance/package.json +8 -8
  35. package/templates/apps/api-kv/package.json +8 -8
  36. package/templates/apps/api-moderation/package.json +8 -8
  37. package/templates/apps/api-observability/package.json +8 -8
  38. package/templates/apps/api-ratelimit/package.json +8 -8
  39. package/templates/apps/api-rbac/package.json +8 -8
  40. package/templates/apps/api-rest/package.json +7 -7
  41. package/templates/apps/api-saas/package.json +11 -11
  42. package/templates/apps/api-search/package.json +8 -8
  43. package/templates/apps/api-versioning/package.json +8 -8
  44. package/templates/apps/api-webhooks/package.json +8 -8
  45. package/templates/apps/changelog/package.json +6 -6
  46. package/templates/apps/edge-functions/package.json +2 -2
  47. package/templates/apps/frontend-admin/package.json +8 -8
  48. package/templates/apps/frontend-app/package.json +8 -8
  49. package/templates/apps/frontend-blank/package.json +7 -7
  50. package/templates/apps/frontend-contact/package.json +7 -7
  51. package/templates/apps/frontend-dashboard/package.json +7 -7
  52. package/templates/apps/frontend-docs/package.json +7 -7
  53. package/templates/apps/frontend-i18n/package.json +6 -6
  54. package/templates/apps/frontend-landing/package.json +7 -7
  55. package/templates/apps/frontend-spa/package.json +7 -7
  56. package/templates/apps/frontend-ssr/package.json +7 -7
  57. package/templates/apps/frontend-ssr-api/package.json +8 -8
  58. package/templates/apps/frontend-static-blog/package.json +6 -6
  59. package/dist/apiBuild-CSM74oEZ.js +0 -2
  60. package/dist/dev-BYfrYwrT.js +0 -2
@@ -1,9 +1,9 @@
1
- import { _t as e, a as t, b as n, bt as r, c as i, d as a, dt as o, f as s, ft as c, g as l, gt as u, h as d, ht as f, i as p, l as m, mt as h, o as g, pt as _, u as v, ut as y, v as ee, vt as te, yt as ne } from "./inspectMetrics-BLUBAWLb.js";
2
- import { n as re, t as ie } from "./start-CWZW_4Et.js";
3
- import { $ as ae, B as oe, L as se, R as ce, V as le, ct as ue, i as de, l as fe, n as pe, o as me, ot as he, p as ge, tt as _e, ut as ve, z as ye } from "./dev-Bqyq62Ak.js";
1
+ import { C as e, Ct as t, St as n, T as r, Tt as i, _ as a, _t as o, a as s, b as c, bt as l, d as u, f as d, gt as f, ht as p, l as m, o as h, s as g, u as _, vt as v, wt as y, x as ee, xt as te, yt as ne } from "./inspectMetrics-EK0CcHyu.js";
2
+ import { n as re, t as ie } from "./start-DuyBQxW3.js";
3
+ import { $ as ae, B as oe, L as se, R as ce, V as le, ct as ue, i as de, l as fe, n as pe, o as me, ot as he, p as ge, tt as _e, ut as ve, z as ye } from "./dev-Ga65K4jc.js";
4
4
  import { a as b, c as be, i as xe, o as Se, s as Ce } from "./devActivity-1WtIVyHc.js";
5
- import { a as we, t as Te } from "./apiBuild-deIjJKJm.js";
6
- import { n as Ee, r as De } from "./serveCommand-Dn6--0Bv.js";
5
+ import { a as we, t as Te } from "./apiBuild-OrDkXGFX.js";
6
+ import { n as Ee, r as De } from "./serveCommand-CR5xnYZt.js";
7
7
  import { basename as x, dirname as S, isAbsolute as Oe, join as C, relative as w, resolve as T } from "node:path";
8
8
  import { fileURLToPath as ke, pathToFileURL as Ae } from "node:url";
9
9
  import { FileSystem as je } from "@effect/platform";
@@ -344,35 +344,35 @@ external scheduler owns the once-only guarantee.
344
344
  let n = e.replace("<div id=\"root\"></div>", `<div id="root">${t.body}</div>`), r = [
345
345
  t.metaHtml,
346
346
  `<meta name="voltro-interactive" content="${t.interactive}" />`,
347
- `<script type="application/json" id="__voltro_state__">${t.stateJson}<\/script>`
347
+ t.stateScript
348
348
  ].filter((e) => e.length > 0).join("\n ");
349
349
  return n = n.replace("</head>", ` ${r}\n </head>`), t.interactive === "none" && (n = In(n)), n;
350
- }, Rn = async (t) => {
351
- let { root: n, distDir: r, pages: i, dirs: a, vite: o } = t, s = C(r, "index.html"), c = await M.readFile(s, "utf8"), l = C(r, "_voltro", "shell.html");
352
- await M.mkdir(S(l), { recursive: !0 }), await M.writeFile(l, c, "utf8");
353
- let u = C(n, "node_modules", "@voltro", "ui-shadcn"), d = A(C(u, "package.json")) ? u : null, f = [...d ? [
350
+ }, Rn = async (e) => {
351
+ let { root: t, distDir: r, pages: i, dirs: a, vite: o } = e, c = C(r, "index.html"), l = await M.readFile(c, "utf8"), u = C(r, "_voltro", "shell.html");
352
+ await M.mkdir(S(u), { recursive: !0 }), await M.writeFile(u, l, "utf8");
353
+ let d = C(t, "node_modules", "@voltro", "ui-shadcn"), f = A(C(d, "package.json")) ? d : null, p = [...f ? [
354
354
  {
355
355
  find: "@voltro/ui-shadcn/tokens.css",
356
- replacement: C(d, "src/tokens.css")
356
+ replacement: C(f, "src/tokens.css")
357
357
  },
358
358
  {
359
359
  find: "@voltro/ui-shadcn/cn",
360
- replacement: C(d, "src/cn.ts")
360
+ replacement: C(f, "src/cn.ts")
361
361
  },
362
362
  {
363
363
  find: "@voltro/ui-shadcn/brand",
364
- replacement: C(d, "src/brand/voltroLogo.tsx")
364
+ replacement: C(f, "src/brand/voltroLogo.tsx")
365
365
  },
366
366
  {
367
367
  find: "@voltro/ui-shadcn",
368
- replacement: C(d, "src/index.ts")
368
+ replacement: C(f, "src/index.ts")
369
369
  }
370
- ] : [], ...e(n)], m = await o.createServer({
371
- root: C(n, ".framework"),
370
+ ] : [], ...n(t)], m = await o.createServer({
371
+ root: C(t, ".framework"),
372
372
  envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
373
373
  plugins: [(await import("@vitejs/plugin-react")).default(), (await import("@tailwindcss/vite")).default()],
374
374
  resolve: {
375
- alias: f,
375
+ alias: p,
376
376
  dedupe: ["react", "react-dom"]
377
377
  },
378
378
  ssr: { noExternal: ["react-intl"] },
@@ -383,122 +383,130 @@ external scheduler owns the once-only guarantee.
383
383
  },
384
384
  appType: "custom",
385
385
  logLevel: "warn"
386
- }), { renderPageToHtml: h, renderMetaToHtml: g, serialiseStateForInlining: _ } = await m.ssrLoadModule("@voltro/web/ssr"), v = /* @__PURE__ */ new Map();
387
- if (t.locales && t.locales.length > 0 && t.defaultLocale) try {
388
- let e = (await m.ssrLoadModule("@voltro/i18n")).makeSsgWrap;
389
- if (typeof e != "function") throw Error("@voltro/i18n is installed but missing the `makeSsgWrap` export — upgrade @voltro/i18n to a version that ships SSG support.");
390
- for (let r of t.locales) try {
391
- let i = await m.ssrLoadModule(C(n, "src", "locales", `${r}.ts`)), a = i.default ?? i;
392
- v.set(r, e(r, a, t.defaultLocale));
386
+ }), { renderPageToHtml: h, renderMetaToHtml: g, renderRouterStateScript: _, isDeferredLoaderResult: v, assertDeferralSupported: y } = await m.ssrLoadModule("@voltro/web/ssr"), ee = /* @__PURE__ */ new Map();
387
+ if (e.locales && e.locales.length > 0 && e.defaultLocale) try {
388
+ let n = (await m.ssrLoadModule("@voltro/i18n")).makeSsgWrap;
389
+ if (typeof n != "function") throw Error("@voltro/i18n is installed but missing the `makeSsgWrap` export — upgrade @voltro/i18n to a version that ships SSG support.");
390
+ for (let r of e.locales) try {
391
+ let i = await m.ssrLoadModule(C(t, "src", "locales", `${r}.ts`)), a = i.default ?? i;
392
+ ee.set(r, n(r, a, e.defaultLocale));
393
393
  } catch (e) {
394
394
  z.warn(`i18n catalog missing for locale '${r}' — pages rendered at /${r}/* will crash. Add src/locales/${r}.ts.`, {}, e);
395
395
  }
396
396
  } catch (e) {
397
397
  z.warn("i18n wrapper unavailable for SSG — pages that call useT() will fail. Check that @voltro/i18n is installed.", {}, e);
398
398
  }
399
- let y = [], ee = {};
400
- for (let e of i) {
401
- let i = `${C(n, "src", "pages", e.file)}`, o = await m.ssrLoadModule(i);
402
- ee[e.pattern] = re(o);
403
- let s = o.renderMode ?? "static";
404
- if (s !== "static") {
399
+ let te = [], ne = {};
400
+ for (let n of i) {
401
+ let i = `${C(t, "src", "pages", n.file)}`, o = await m.ssrLoadModule(i);
402
+ ne[n.pattern] = re(o);
403
+ let c = o.renderMode ?? "static";
404
+ if (c !== "static") {
405
405
  z.debug("skip page", {
406
- pattern: e.pattern,
407
- reason: `renderMode=${s}`
406
+ pattern: n.pattern,
407
+ reason: `renderMode=${c}`
408
408
  });
409
409
  continue;
410
410
  }
411
- let l;
412
- if (Nn(e.pattern)) {
411
+ let u;
412
+ if (Nn(n.pattern)) {
413
413
  if (typeof o.getStaticPaths != "function") {
414
414
  z.debug("skip page", {
415
- pattern: e.pattern,
415
+ pattern: n.pattern,
416
416
  reason: "no getStaticPaths export"
417
417
  });
418
418
  continue;
419
419
  }
420
420
  try {
421
- let t = await o.getStaticPaths();
422
- if (!Array.isArray(t)) {
423
- z.warn("skip page — getStaticPaths did not return an array", { pattern: e.pattern });
421
+ let e = await o.getStaticPaths();
422
+ if (!Array.isArray(e)) {
423
+ z.warn("skip page — getStaticPaths did not return an array", { pattern: n.pattern });
424
424
  continue;
425
425
  }
426
- l = t.map((t) => ({
427
- params: t.params,
428
- pathname: Pn(e.pattern, t.params)
426
+ u = e.map((e) => ({
427
+ params: e.params,
428
+ pathname: Pn(n.pattern, e.params)
429
429
  }));
430
- } catch (t) {
431
- z.warn("skip page — getStaticPaths threw", { pattern: e.pattern }, t);
430
+ } catch (e) {
431
+ z.warn("skip page — getStaticPaths threw", { pattern: n.pattern }, e);
432
432
  continue;
433
433
  }
434
- if (l.length === 0) {
434
+ if (u.length === 0) {
435
435
  z.debug("skip page", {
436
- pattern: e.pattern,
436
+ pattern: n.pattern,
437
437
  reason: "getStaticPaths returned []"
438
438
  });
439
439
  continue;
440
440
  }
441
- } else l = [{
441
+ } else u = [{
442
442
  params: {},
443
- pathname: e.pattern
443
+ pathname: n.pattern
444
444
  }];
445
- let u = p(a, e.dirChain), d = await Promise.all(u.map(async (e) => {
446
- let t = e.dir === "" ? C(n, "src", "pages") : C(n, "src", "pages", e.dir), r = {};
447
- return e.hasLayout && (r.Layout = (await m.ssrLoadModule(C(t, "layout.tsx"))).default), e.hasError && (r.Error = (await m.ssrLoadModule(C(t, "error.tsx"))).default), e.hasPending && (r.Pending = (await m.ssrLoadModule(C(t, "loading.tsx"))).default), {
445
+ let d = s(a, n.dirChain), f = await Promise.all(d.map(async (e) => {
446
+ let n = e.dir === "" ? C(t, "src", "pages") : C(t, "src", "pages", e.dir), r = {};
447
+ return e.hasLayout && (r.Layout = (await m.ssrLoadModule(C(n, "layout.tsx"))).default), e.hasError && (r.Error = (await m.ssrLoadModule(C(n, "error.tsx"))).default), e.hasPending && (r.Pending = (await m.ssrLoadModule(C(n, "loading.tsx"))).default), {
448
448
  id: e.dir || "/",
449
449
  ...r
450
450
  };
451
- })), f = {
452
- pattern: e.pattern,
451
+ })), p = {
452
+ pattern: n.pattern,
453
453
  Component: o.default,
454
454
  meta: o.meta,
455
455
  loader: o.loader,
456
456
  ErrorBoundary: o.ErrorBoundary,
457
457
  Pending: o.Pending,
458
- renderMode: s,
459
- chain: d
460
- }, te = o.interactive ?? "full";
461
- for (let e of l) {
458
+ renderMode: c,
459
+ chain: f
460
+ }, ie = o.interactive ?? "full";
461
+ for (let t of u) {
462
462
  let n = null;
463
463
  if (typeof o.loader == "function") {
464
- let t = new AbortController();
464
+ let e = new AbortController();
465
465
  try {
466
466
  n = await o.loader({
467
- params: e.params,
468
- pathname: e.pathname,
469
- signal: t.signal
467
+ params: t.params,
468
+ pathname: t.pathname,
469
+ signal: e.signal
470
470
  });
471
- } catch (t) {
472
- if (t instanceof Error && t._voltroControl === "redirect") throw Error(`Static page "${e.pathname}" threw RedirectError at build time. Static pages can't redirect per-request — switch it to renderMode 'ssr'.`);
473
- if (t instanceof Error && t._voltroControl === "not-found") {
474
- z.warn("loader threw NotFoundError at build time — skipping artifact", { pathname: e.pathname });
471
+ } catch (e) {
472
+ if (e instanceof Error && e._voltroControl === "redirect") throw Error(`Static page "${t.pathname}" threw RedirectError at build time. Static pages can't redirect per-request — switch it to renderMode 'ssr'.`);
473
+ if (e instanceof Error && e._voltroControl === "not-found") {
474
+ z.warn("loader threw NotFoundError at build time — skipping artifact", { pathname: t.pathname });
475
475
  continue;
476
476
  }
477
- z.warn("loader threw, falling back to null", { pathname: e.pathname }, t);
477
+ z.warn("loader threw, falling back to null", { pathname: t.pathname }, e);
478
478
  }
479
479
  }
480
- let i = e.params.locale ?? t.defaultLocale ?? "en", a = v.get(i) ?? v.get(t.defaultLocale ?? ""), { html: s, meta: l } = h({
481
- descriptor: f,
482
- params: e.params,
483
- pathname: e.pathname,
480
+ v(n) && y({
481
+ page: t.pathname,
482
+ renderMode: c,
483
+ interactive: ie
484
+ });
485
+ let i = t.params.locale ?? e.defaultLocale ?? "en", a = ee.get(i) ?? ee.get(e.defaultLocale ?? ""), { html: s, meta: u } = h({
486
+ descriptor: p,
487
+ params: t.params,
488
+ pathname: t.pathname,
484
489
  loaderData: n,
485
490
  ...a ? { outerWrap: a } : {},
486
491
  locale: i
487
- }), u = Ln(c, {
492
+ }), d = Ln(l, {
488
493
  body: s,
489
- metaHtml: g(l),
490
- stateJson: _(n),
491
- interactive: te
492
- }), d = Fn(r, e.pathname);
493
- await M.mkdir(S(d), { recursive: !0 }), await M.writeFile(d, u, "utf8"), y.push({
494
- pattern: e.pathname,
495
- outPath: d
494
+ metaHtml: g(u),
495
+ stateScript: _({
496
+ loaderData: n,
497
+ pageLoaderRan: typeof o.loader == "function"
498
+ }),
499
+ interactive: ie
500
+ }), f = Fn(r, t.pathname);
501
+ await M.mkdir(S(f), { recursive: !0 }), await M.writeFile(f, d, "utf8"), te.push({
502
+ pattern: t.pathname,
503
+ outPath: f
496
504
  });
497
505
  }
498
506
  }
499
507
  return await m.close(), {
500
- rendered: y,
501
- pageMeta: ee
508
+ rendered: te,
509
+ pageMeta: ne
502
510
  };
503
511
  }, zn = async (e, t, n, r, i, a) => {
504
512
  let o = C(e, ".framework"), s = [];
@@ -530,12 +538,12 @@ external scheduler owns the once-only guarantee.
530
538
  " const locale = resolveLocale({ cookieHeader: cookieHeader ?? null, acceptLanguageHeader: acceptLanguageHeader ?? null, supported: __I18N_SUPPORTED, defaultLocale: __I18N_DEFAULT })",
531
539
  " return { locale, outerWrap: __i18nWraps[locale] ?? __i18nWraps[__I18N_DEFAULT] }",
532
540
  "}"
533
- ] : [], g = p ? "export const helpers = { renderPageToHtml, renderMetaToHtml, serialiseStateForInlining, parseCookieHeader, i18n: { resolve: __resolveI18n } }" : "export const helpers = { renderPageToHtml, renderMetaToHtml, serialiseStateForInlining, parseCookieHeader }", _ = [
541
+ ] : [], g = "renderPageToHtml, renderPageToStream, renderMetaToHtml, renderRouterStateScript, renderDeferredRegistryScript, prepareDeferredLoaderData, assertDeferralSupported, parseCookieHeader", _ = p ? `export const helpers = { ${g}, i18n: { resolve: __resolveI18n } }` : `export const helpers = { ${g} }`, v = [
534
542
  "// GENERATED BY `voltro build` — do not edit; will be overwritten on next build.",
535
543
  "// Entry for the pre-built SSR bundle. Imported by `voltro start`'s",
536
544
  "// production runtime; bypasses Vite middleware-mode SSR loading.",
537
545
  "",
538
- "import { renderPageToHtml, renderMetaToHtml, serialiseStateForInlining, parseCookieHeader } from '@voltro/web/ssr'",
546
+ "import { renderPageToHtml, renderPageToStream, renderMetaToHtml, renderRouterStateScript, renderDeferredRegistryScript, prepareDeferredLoaderData, assertDeferralSupported, parseCookieHeader } from '@voltro/web/ssr'",
539
547
  ...m,
540
548
  "",
541
549
  "// Lazy page loaders — a page's module (and content) loads only when its",
@@ -557,20 +565,20 @@ external scheduler owns the once-only guarantee.
557
565
  "}",
558
566
  ...h,
559
567
  "",
560
- g,
568
+ _,
561
569
  ""
562
- ].join("\n"), v = C(o, "ssrEntry.ts");
563
- return await M.writeFile(v, _, "utf8"), v;
564
- }, Bn = async (n) => {
565
- let r = await t(n);
566
- if (!r) {
567
- let { loadApiConfig: e } = await import("./dev-BYfrYwrT.js"), t = await e(n);
568
- if (t) {
569
- z.info("building api app", { app: t.name ?? "(unnamed)" });
570
- let { runApiBuild: e, runServeBundleBuild: r } = await import("./apiBuild-CSM74oEZ.js");
571
- await e(n);
570
+ ].join("\n"), y = C(o, "ssrEntry.ts");
571
+ return await M.writeFile(y, v, "utf8"), y;
572
+ }, Bn = async (e) => {
573
+ let t = await h(e);
574
+ if (!t) {
575
+ let { loadApiConfig: t } = await import("./dev-Ca1jdwnt.js"), n = await t(e);
576
+ if (n) {
577
+ z.info("building api app", { app: n.name ?? "(unnamed)" });
578
+ let { runApiBuild: t, runServeBundleBuild: r } = await import("./apiBuild-CmtI6Ygh.js");
579
+ await t(e);
572
580
  try {
573
- await r(n);
581
+ await r(e);
574
582
  } catch (e) {
575
583
  return z.fatal("serve bundle build FAILED — refusing to ship a bundle-less image. Fix the error above (production serves from the bundle, never tsx).", {}, e), {
576
584
  ran: !0,
@@ -587,82 +595,82 @@ external scheduler owns the once-only guarantee.
587
595
  exitCode: 0
588
596
  };
589
597
  }
590
- z.info("building web app", { app: r.name ?? "(unnamed)" });
591
- let { resolved: i } = await _({
592
- rawEnv: r.env,
598
+ z.info("building web app", { app: t.name ?? "(unnamed)" });
599
+ let { resolved: r } = await v({
600
+ rawEnv: t.env,
593
601
  appType: "web",
594
- root: n,
602
+ root: e,
595
603
  log: z,
596
604
  generateTypes: !0
597
605
  });
598
- await a(n, r, await g(n, r), i.publicValues);
599
- let { pages: o } = await v(C(n, "src", "pages"));
600
- await c({
601
- appRoot: n,
602
- pages: o,
606
+ await d(e, t, await g(e, t), r.publicValues);
607
+ let { pages: i } = await u(C(e, "src", "pages"));
608
+ await o({
609
+ appRoot: e,
610
+ pages: i,
603
611
  mode: "prod"
604
612
  });
605
- let s = C(n, ".framework"), l = C(s, "dist"), d = C(n, "node_modules", "@voltro", "ui-shadcn"), f = A(C(d, "package.json")) ? d : null, p = [...f ? [
613
+ let a = C(e, ".framework"), s = C(a, "dist"), c = C(e, "node_modules", "@voltro", "ui-shadcn"), l = A(C(c, "package.json")) ? c : null, f = [...l ? [
606
614
  {
607
615
  find: "@voltro/ui-shadcn/tokens.css",
608
- replacement: C(f, "src/tokens.css")
616
+ replacement: C(l, "src/tokens.css")
609
617
  },
610
618
  {
611
619
  find: "@voltro/ui-shadcn/cn",
612
- replacement: C(f, "src/cn.ts")
620
+ replacement: C(l, "src/cn.ts")
613
621
  },
614
622
  {
615
623
  find: "@voltro/ui-shadcn/brand",
616
- replacement: C(f, "src/brand/voltroLogo.tsx")
624
+ replacement: C(l, "src/brand/voltroLogo.tsx")
617
625
  },
618
626
  {
619
627
  find: "@voltro/ui-shadcn",
620
- replacement: C(f, "src/index.ts")
628
+ replacement: C(l, "src/index.ts")
621
629
  }
622
- ] : [], ...e(n)];
630
+ ] : [], ...n(e)];
623
631
  z.info("running vite build");
624
- let h = await u("vite", () => import("vite")), y = (await import("@vitejs/plugin-react")).default, ee = (await import("@tailwindcss/vite")).default;
625
- await h.build({
626
- root: s,
632
+ let p = await te("vite", () => import("vite")), m = (await import("@vitejs/plugin-react")).default, y = (await import("@tailwindcss/vite")).default;
633
+ await p.build({
634
+ root: a,
627
635
  envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
628
- plugins: [y(), ee()],
636
+ plugins: [m(), y()],
629
637
  resolve: {
630
- alias: p,
638
+ alias: f,
631
639
  dedupe: ["react", "react-dom"]
632
640
  },
633
- publicDir: C(n, "public"),
641
+ publicDir: C(e, "public"),
634
642
  build: {
635
643
  outDir: "dist",
636
644
  emptyOutDir: !0,
637
- rollupOptions: { output: { manualChunks: m } }
645
+ rollupOptions: { output: { manualChunks: _ } }
638
646
  },
639
647
  logLevel: "warn"
640
648
  });
641
- let te = C(n, "src", "pages"), { pages: ne, dirs: re } = await v(te);
649
+ let ee = C(e, "src", "pages"), { pages: ne, dirs: re } = await u(ee);
642
650
  if (ne.length === 0) return z.info("no pages found — client-only bundle written", {
643
- pagesDir: te,
644
- distDir: l
651
+ pagesDir: ee,
652
+ distDir: s
645
653
  }), {
646
654
  ran: !0,
647
655
  exitCode: 0
648
656
  };
649
657
  let { rendered: ie, pageMeta: ae } = await Rn({
650
- root: n,
651
- distDir: l,
658
+ root: e,
659
+ distDir: s,
652
660
  pages: ne,
653
661
  dirs: re,
654
- vite: h,
655
- ...r.locales ? { locales: r.locales } : {},
656
- ...r.defaultLocale ? { defaultLocale: r.defaultLocale } : {}
662
+ vite: p,
663
+ ...t.locales ? { locales: t.locales } : {},
664
+ ...t.defaultLocale ? { defaultLocale: t.defaultLocale } : {}
657
665
  });
658
- z.info("generating .framework/ssrEntry.ts"), await zn(n, ne, re, r.locales, r.defaultLocale, ae), z.info("vite build --ssr → dist/server/");
666
+ z.info("generating .framework/ssrEntry.ts"), await zn(e, ne, re, t.locales, t.defaultLocale, ae), z.info("vite build --ssr → dist/server/");
659
667
  try {
660
- await h.build({
661
- root: s,
668
+ await p.build({
669
+ root: a,
662
670
  envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
663
- plugins: [y(), ee()],
671
+ plugins: [m(), y()],
664
672
  resolve: {
665
- alias: p,
673
+ alias: f,
666
674
  dedupe: ["react", "react-dom"]
667
675
  },
668
676
  ssr: { noExternal: ["react-intl"] },
@@ -680,20 +688,20 @@ external scheduler owns the once-only guarantee.
680
688
  exitCode: 1
681
689
  };
682
690
  }
683
- let oe = C(n, "app.config.ts");
691
+ let oe = C(e, "app.config.ts");
684
692
  if (A(oe)) {
685
693
  z.info("precompiling app.config.ts → dist/server/appConfig.js");
686
694
  try {
687
- await (await u("esbuild", () => import("esbuild"))).build({
695
+ await (await te("esbuild", () => import("esbuild"))).build({
688
696
  entryPoints: [oe],
689
- outfile: C(l, "server", "appConfig.js"),
697
+ outfile: C(s, "server", "appConfig.js"),
690
698
  bundle: !0,
691
699
  format: "esm",
692
700
  platform: "node",
693
701
  packages: "external",
694
- absWorkingDir: n,
702
+ absWorkingDir: e,
695
703
  logLevel: "silent",
696
- ...A(C(n, "tsconfig.json")) ? { tsconfig: C(n, "tsconfig.json") } : {}
704
+ ...A(C(e, "tsconfig.json")) ? { tsconfig: C(e, "tsconfig.json") } : {}
697
705
  });
698
706
  } catch (e) {
699
707
  return z.fatal("app.config precompile FAILED — production `voltro start` would need the tsx loader to read it.", {}, e), {
@@ -707,19 +715,19 @@ external scheduler owns the once-only guarantee.
707
715
  routes: ie.map((e) => e.pattern)
708
716
  });
709
717
  try {
710
- let e = await jn(n);
711
- await M.writeFile(C(l, ".voltro-build.json"), JSON.stringify({
718
+ let t = await jn(e);
719
+ await M.writeFile(C(s, ".voltro-build.json"), JSON.stringify({
712
720
  version: 1,
713
- profile: e
721
+ profile: t
714
722
  }, null, 2), "utf8"), z.info("wrote deploy profile manifest", {
715
- staticSafe: e.staticSafe,
716
- ssr: e.counts.ssr,
717
- isr: e.counts.isr
723
+ staticSafe: t.staticSafe,
724
+ ssr: t.counts.ssr,
725
+ isr: t.counts.isr
718
726
  });
719
727
  } catch (e) {
720
728
  z.warn("could not write .voltro-build.json deploy manifest", {}, e);
721
729
  }
722
- return z.info("build complete", { distDir: l }), {
730
+ return z.info("build complete", { distDir: s }), {
723
731
  ran: !0,
724
732
  exitCode: 0
725
733
  };
@@ -729,7 +737,7 @@ external scheduler owns the once-only guarantee.
729
737
  catch: (e) => e
730
738
  }).pipe(E.orDie);
731
739
  return t.ran ? t.exitCode : (yield* E.logError("no web app found at this path", { hint: "looked for app.config.ts with `type: 'web'`" }), 1);
732
- }), Hn = async (e) => r(Vn(e.find((e) => !e.startsWith("-")) ?? process.cwd()), { scope: "voltro:build" }), Un = (e) => e.source === void 0 ? [] : typeof e.source == "string" ? [e.source] : e.source, Wn = (e) => ({
740
+ }), Hn = async (e) => i(Vn(e.find((e) => !e.startsWith("-")) ?? process.cwd()), { scope: "voltro:build" }), Un = (e) => e.source === void 0 ? [] : typeof e.source == "string" ? [e.source] : e.source, Wn = (e) => ({
733
741
  tables: new Set(e.tables),
734
742
  procedures: new Map(e.procedures.map((e) => [e.tag, e])),
735
743
  scopes: new Set(e.scopes ?? []),
@@ -998,7 +1006,7 @@ external scheduler owns the once-only guarantee.
998
1006
  error: t.message || String(t)
999
1007
  };
1000
1008
  }
1001
- }, lr = async (e) => (await n()).apps.filter((t) => t.kind === "api" && t.status === "live" && t.url.length > 0 && (e === void 0 || t.name === e)), ur = (e) => {
1009
+ }, lr = async (e) => (await r()).apps.filter((t) => t.kind === "api" && t.status === "live" && t.url.length > 0 && (e === void 0 || t.name === e)), ur = (e) => {
1002
1010
  let t = "pretty", n = [];
1003
1011
  for (let r = 0; r < e.length; r++) {
1004
1012
  let i = e[r];
@@ -1070,10 +1078,10 @@ external scheduler owns the once-only guarantee.
1070
1078
  let n = e.indexOf(t);
1071
1079
  return n >= 0 ? e[n + 1] : void 0;
1072
1080
  }, _r = async (e) => {
1073
- process.env[s] = "1";
1081
+ process.env[a] = "1";
1074
1082
  let t = gr(e, "--port") ?? process.env.VOLTRO_DASHBOARD_PORT ?? "5179";
1075
1083
  process.env.VOLTRO_DASHBOARD_PORT = t;
1076
- let n = d(process.cwd());
1084
+ let n = c(process.cwd());
1077
1085
  if (!n) return hr.error("dashboard app not found. Install it with `pnpm add @voltro/dashboard`, or set VOLTRO_DASHBOARD_PATH to a dashboard app directory."), 1;
1078
1086
  let r = A(C(n, ".framework", "dist", "index.html"));
1079
1087
  return hr.info(`serving the Voltro dashboard on :${t}`, {
@@ -1329,7 +1337,7 @@ external scheduler owns the once-only guarantee.
1329
1337
  id: "hand-serialized-date",
1330
1338
  scope: "server",
1331
1339
  smell: "Date converted by hand on the way out of a handler",
1332
- use: "rowSchema(table) in the descriptor's output — `output` IS the serializer (it is the rpc success schema), so a Date column declared as a Date crosses as epoch ms on its own; declaring Schema.Number describes the WIRE type and leaves nothing to convert",
1340
+ use: "timestampMs / timestampMsOrNull from @voltro/database/wire in the descriptor's output struct — `output` IS the serializer (it is the rpc success schema), so a Date field declared as timestampMs crosses as epoch ms on its own; declaring Schema.Number describes the WIRE type and leaves nothing to convert",
1333
1341
  match: (e) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
1334
1342
  }
1335
1343
  ], jr = [
@@ -1607,7 +1615,7 @@ external scheduler owns the once-only guarantee.
1607
1615
  }, Yr = (e) => E.tryPromise({
1608
1616
  try: () => Jr(e),
1609
1617
  catch: (e) => e
1610
- }).pipe(E.orDie), Xr = async (e) => r(Yr(T(e.find((e) => !e.startsWith("-")) ?? process.cwd())), { scope: "voltro:e2e" }), Zr = (e) => {
1618
+ }).pipe(E.orDie), Xr = async (e) => i(Yr(T(e.find((e) => !e.startsWith("-")) ?? process.cwd())), { scope: "voltro:e2e" }), Zr = (e) => {
1611
1619
  let t = {
1612
1620
  tail: 100,
1613
1621
  since: void 0,
@@ -1700,9 +1708,9 @@ external scheduler owns the once-only guarantee.
1700
1708
  }, ei = async (e) => {
1701
1709
  let t = Zr(e);
1702
1710
  if ("error" in t) return t.error !== "help" && console.error(`voltro logs: ${t.error}`), $r(), t.error === "help" ? 0 : 2;
1703
- let r = await n(), i = t.process === void 0 ? r.apps : r.apps.filter((e) => e.name === t.process);
1711
+ let n = await r(), i = t.process === void 0 ? n.apps : n.apps.filter((e) => e.name === t.process);
1704
1712
  if (i.length === 0 && t.process !== void 0) {
1705
- let e = l(t.process);
1713
+ let e = ee(t.process);
1706
1714
  e !== void 0 && (i = [{
1707
1715
  id: `orphan:${t.process}`,
1708
1716
  name: t.process,
@@ -1718,7 +1726,7 @@ external scheduler owns the once-only guarantee.
1718
1726
  if (i.length === 0) {
1719
1727
  if (t.process !== void 0) {
1720
1728
  console.error(`voltro logs: no running process named '${t.process}' and no orphan log file found`), console.error(" active processes:");
1721
- for (let e of r.apps) console.error(` - ${e.name} (${e.kind} @ ${e.url})`);
1729
+ for (let e of n.apps) console.error(` - ${e.name} (${e.kind} @ ${e.url})`);
1722
1730
  return 1;
1723
1731
  }
1724
1732
  return console.error("voltro logs: no running voltro process found"), console.error(" start one with 'voltro dev' (or 'voltro start') and try again."), 1;
@@ -1769,24 +1777,24 @@ external scheduler owns the once-only guarantee.
1769
1777
  } catch (n) {
1770
1778
  return ni(e, t, n.message || String(n));
1771
1779
  }
1772
- }, ni = async (e, t, n) => {
1773
- if (!e.logFile) return {
1780
+ }, ni = async (t, n, r) => {
1781
+ if (!t.logFile) return {
1774
1782
  kind: "err",
1775
- name: e.name,
1776
- error: `${n} (no logFile on entry)`
1783
+ name: t.name,
1784
+ error: `${r} (no logFile on entry)`
1777
1785
  };
1778
1786
  try {
1779
- let n = t.since, r = { max: t.tail };
1780
- n !== void 0 && (r.since = n);
1781
- let i = await ee(e.logFile, r), a = [], o = {
1787
+ let r = n.since, i = { max: n.tail };
1788
+ r !== void 0 && (i.since = r);
1789
+ let a = await e(t.logFile, i), o = [], s = {
1782
1790
  trace: 0,
1783
1791
  debug: 1,
1784
1792
  info: 2,
1785
1793
  warn: 3,
1786
1794
  error: 4,
1787
1795
  fatal: 5
1788
- }, s = t.level === void 0 ? 0 : o[t.level], c = t.scope?.toLowerCase(), l = t.source, u = t.filter?.toLowerCase(), d = t.traceId;
1789
- for (let e of i) o[e.level] < s || l !== void 0 && e.source !== l || c !== void 0 && (e.scope === void 0 || !e.scope.toLowerCase().includes(c)) || u !== void 0 && !e.message.toLowerCase().includes(u) || d !== void 0 && e.fields.traceId !== d || a.push({
1796
+ }, c = n.level === void 0 ? 0 : s[n.level], l = n.scope?.toLowerCase(), u = n.source, d = n.filter?.toLowerCase(), f = n.traceId;
1797
+ for (let e of a) s[e.level] < c || u !== void 0 && e.source !== u || l !== void 0 && (e.scope === void 0 || !e.scope.toLowerCase().includes(l)) || d !== void 0 && !e.message.toLowerCase().includes(d) || f !== void 0 && e.fields.traceId !== f || o.push({
1790
1798
  ts: e.ts,
1791
1799
  level: e.level,
1792
1800
  source: e.source,
@@ -1794,20 +1802,20 @@ external scheduler owns the once-only guarantee.
1794
1802
  fields: e.fields,
1795
1803
  ...e.scope === void 0 ? {} : { scope: e.scope }
1796
1804
  });
1797
- let f = a.length > t.tail ? a.slice(a.length - t.tail) : a;
1805
+ let p = o.length > n.tail ? o.slice(o.length - n.tail) : o;
1798
1806
  return {
1799
1807
  kind: "ok",
1800
- name: e.name,
1801
- records: f.map((t) => ({
1802
- ...t,
1803
- process: e.name
1808
+ name: t.name,
1809
+ records: p.map((e) => ({
1810
+ ...e,
1811
+ process: t.name
1804
1812
  }))
1805
1813
  };
1806
- } catch (t) {
1814
+ } catch (e) {
1807
1815
  return {
1808
1816
  kind: "err",
1809
- name: e.name,
1810
- error: `${n}; file read failed: ${t.message || String(t)}`
1817
+ name: t.name,
1818
+ error: `${r}; file read failed: ${e.message || String(e)}`
1811
1819
  };
1812
1820
  }
1813
1821
  }, ri = {
@@ -1903,7 +1911,7 @@ external scheduler owns the once-only guarantee.
1903
1911
  }, vi = async (e) => {
1904
1912
  let t = await hi(e);
1905
1913
  if (!t) return H.error("no app.config.ts found in the current directory"), 1;
1906
- let n = y(t.name, t.input), r = `${gi("KEY", 32)} ${gi("OWNER", 16)} ${gi("ACCESS", 8)} ${gi("REQ", 4)} SET`;
1914
+ let n = p(t.name, t.input), r = `${gi("KEY", 32)} ${gi("OWNER", 16)} ${gi("ACCESS", 8)} ${gi("REQ", 4)} SET`;
1907
1915
  H.info(`env manifest — ${n.app} (${n.summary.total} vars, ${n.summary.set} set)`), _i(r);
1908
1916
  for (let e of n.entries) {
1909
1917
  let t = e.isSet ? "✓" : e.satisfied ? "·" : "✗";
@@ -1913,7 +1921,7 @@ external scheduler owns the once-only guarantee.
1913
1921
  }, yi = async (e, t) => {
1914
1922
  let n = await hi(e);
1915
1923
  if (!n) return H.error("no app.config.ts found in the current directory"), 1;
1916
- let r = o(n.input, ["Environment for this app — generated from app.config.ts `env` + plugin declarations.", "Regenerate with `voltro env sync`. Copy to `.env` and fill in the blanks."]), i = C(e, ".env.example"), a = await M.readFile(i, "utf8").catch(() => null);
1924
+ let r = f(n.input, ["Environment for this app — generated from app.config.ts `env` + plugin declarations.", "Regenerate with `voltro env sync`. Copy to `.env` and fill in the blanks."]), i = C(e, ".env.example"), a = await M.readFile(i, "utf8").catch(() => null);
1917
1925
  if (a !== null && !a.startsWith(mi) && !t) {
1918
1926
  let t = C(e, ".env.example.generated");
1919
1927
  return await M.writeFile(t, r, "utf8"), H.warn(".env.example exists and is not framework-generated — wrote .env.example.generated instead", { hint: "merge it by hand, or pass --force to overwrite .env.example" }), 0;
@@ -1923,7 +1931,7 @@ external scheduler owns the once-only guarantee.
1923
1931
  let t = await me(e).catch(() => null);
1924
1932
  if (!t) return H.error("no app.config.ts found in the current directory"), 1;
1925
1933
  let n = Ze(t.env) ? t.env : void 0;
1926
- return n ? (t.type === "web" ? (await h(e, n), H.info("wrote src/voltro-env.d.ts (publicEnv augmentation)", { app: t.name ?? "app" })) : (await f(e, n), H.info("wrote env.generated.d.ts (serverEnv augmentation)", { app: t.name ?? "app" })), 0) : (H.warn("no `defineEnv` declared in app.config.ts — nothing to generate", { hint: "add `env: defineEnv({ ... })` to app.config.ts (see @voltro/env)" }), 0);
1934
+ return n ? (t.type === "web" ? (await ne(e, n), H.info("wrote src/voltro-env.d.ts (publicEnv augmentation)", { app: t.name ?? "app" })) : (await l(e, n), H.info("wrote env.generated.d.ts (serverEnv augmentation)", { app: t.name ?? "app" })), 0) : (H.warn("no `defineEnv` declared in app.config.ts — nothing to generate", { hint: "add `env: defineEnv({ ... })` to app.config.ts (see @voltro/env)" }), 0);
1927
1935
  }, xi = () => {
1928
1936
  let e = Xe();
1929
1937
  return H.info(`${e.length} framework env vars for turbo.json globalPassThroughEnv:`), _i(JSON.stringify([...e].sort(), null, 2)), H.info("merge these into the workspace turbo.json `globalPassThroughEnv` array (+ your own app vars)."), 0;
@@ -2205,12 +2213,12 @@ DB_DIALECT=mssql
2205
2213
  }, Zi = async (e) => {
2206
2214
  let t = Yi(e);
2207
2215
  if ("error" in t) return t.error !== "help" && console.error(`voltro traces: ${t.error}`), Xi(), t.error === "help" ? 0 : 2;
2208
- let r = (await n()).apps.filter((e) => e.kind === "api" && e.status === "live" && e.url.length > 0 && (t.process === void 0 || e.name === t.process));
2209
- if (r.length === 0) return console.error(t.process === void 0 ? "voltro traces: no running api processes found" : `voltro traces: no running api named '${t.process}'`), t.process === void 0 && console.error(" start one with 'voltro dev' and try again."), 1;
2216
+ let n = (await r()).apps.filter((e) => e.kind === "api" && e.status === "live" && e.url.length > 0 && (t.process === void 0 || e.name === t.process));
2217
+ if (n.length === 0) return console.error(t.process === void 0 ? "voltro traces: no running api processes found" : `voltro traces: no running api named '${t.process}'`), t.process === void 0 && console.error(" start one with 'voltro dev' and try again."), 1;
2210
2218
  if (t.id !== void 0) {
2211
- let e = r.map((e) => $i(e, t.id)), n = await Promise.all(e), i = n.find((e) => e.kind === "ok" && e.spans.length > 0);
2219
+ let e = n.map((e) => $i(e, t.id)), r = await Promise.all(e), i = r.find((e) => e.kind === "ok" && e.spans.length > 0);
2212
2220
  if (!i || i.kind !== "ok") {
2213
- let e = n.filter((e) => e.kind === "err");
2221
+ let e = r.filter((e) => e.kind === "err");
2214
2222
  if (t.format === "json") console.log(JSON.stringify({
2215
2223
  traceId: t.id,
2216
2224
  spans: [],
@@ -2223,7 +2231,7 @@ DB_DIALECT=mssql
2223
2231
  console.error(`voltro traces: no spans for trace ${t.id}`);
2224
2232
  for (let t of e) console.error(` # ${t.name}: ${t.error}`);
2225
2233
  }
2226
- return +(e.length === r.length);
2234
+ return +(e.length === n.length);
2227
2235
  }
2228
2236
  return t.format === "json" ? (console.log(JSON.stringify({
2229
2237
  traceId: t.id,
@@ -2231,7 +2239,7 @@ DB_DIALECT=mssql
2231
2239
  spans: i.spans
2232
2240
  })), 0) : (ia(t.id, i.name, ea(i.spans, t), t.color), 0);
2233
2241
  }
2234
- let i = r.map((e) => Qi(e, t)), a = await Promise.all(i), o = [], s = [];
2242
+ let i = n.map((e) => Qi(e, t)), a = await Promise.all(i), o = [], s = [];
2235
2243
  for (let e of a) e.kind === "ok" ? o.push(...e.traces.map((t) => ({
2236
2244
  ...t,
2237
2245
  process: e.name
@@ -2246,14 +2254,14 @@ DB_DIALECT=mssql
2246
2254
  traces: c,
2247
2255
  fetchErrors: s
2248
2256
  } : { traces: c };
2249
- return console.log(JSON.stringify(e)), +(s.length === r.length);
2257
+ return console.log(JSON.stringify(e)), +(s.length === n.length);
2250
2258
  }
2251
2259
  ra(c, t.color), c.length === 0 && console.error("voltro traces: no traces matched the given filters.");
2252
2260
  for (let e of s) {
2253
2261
  let n = `# fetch failed: ${e.name}: ${e.error}`;
2254
2262
  console.error(t.color ? O.dim(n) : n);
2255
2263
  }
2256
- return +(s.length === r.length);
2264
+ return +(s.length === n.length);
2257
2265
  }, Qi = async (e, t) => {
2258
2266
  let n = new URLSearchParams();
2259
2267
  n.set("max", String(t.tail)), t.since !== void 0 && n.set("since", String(t.since)), t.errors && n.set("onlyErrors", "1");
@@ -3496,48 +3504,48 @@ DB_DIALECT=mssql
3496
3504
  projectName: i
3497
3505
  };
3498
3506
  }, Zo = async (e) => {
3499
- let { positional: t, flags: n } = Fo(e), r = t[0];
3500
- if (!r) return W.error("add-app: <appName> required", { usage: "voltro add-app <appName> --template <templateId> [--to <projectName>]" }), 2;
3501
- if (!Po(r)) return W.error("add-app: invalid appName — use camelCase or kebab-case", { appName: r }), 2;
3502
- let i = n.template;
3503
- if (typeof i != "string") return W.error("add-app: --template <id> required", { hint: "run `voltro list-templates` to see available ids" }), 2;
3504
- let a = await qa(i);
3505
- if (!a) return W.error("add-app: unknown template", {
3506
- templateId: i,
3507
+ let { positional: n, flags: r } = Fo(e), i = n[0];
3508
+ if (!i) return W.error("add-app: <appName> required", { usage: "voltro add-app <appName> --template <templateId> [--to <projectName>]" }), 2;
3509
+ if (!Po(i)) return W.error("add-app: invalid appName — use camelCase or kebab-case", { appName: i }), 2;
3510
+ let a = r.template;
3511
+ if (typeof a != "string") return W.error("add-app: --template <id> required", { hint: "run `voltro list-templates` to see available ids" }), 2;
3512
+ let o = await qa(a);
3513
+ if (!o) return W.error("add-app: unknown template", {
3514
+ templateId: a,
3507
3515
  hint: "run `voltro list-templates` to see available ids"
3508
3516
  }), 2;
3509
- let o = No(process.cwd()), s = C(o, "apps");
3510
- if (!A(s)) return W.error("add-app: apps/ root not found — not in a voltro workspace?", { projectsRoot: s }), 2;
3511
- let c = Go(await Ko(s), typeof n.to == "string" ? n.to : void 0);
3512
- if (!c.ok) return W.error(`add-app: ${c.error}`, c.hint === void 0 ? {} : { hint: c.hint }), 2;
3513
- let l = c.project, u = C(s, l);
3514
- A(u) || (j(u, { recursive: !0 }), await Ho(u, l, Ro));
3515
- let d = Wa(r), f = C(u, d);
3516
- if (A(f)) return W.error("add-app: target directory already exists", { targetDir: f }), 1;
3517
- let p = null;
3518
- if (a.kind === "web" && (p = await Uo(u), p === null)) {
3519
- let e = (await Bo(u))?.portRange ?? Ro;
3517
+ let s = No(process.cwd()), c = C(s, "apps");
3518
+ if (!A(c)) return W.error("add-app: apps/ root not found — not in a voltro workspace?", { projectsRoot: c }), 2;
3519
+ let l = Go(await Ko(c), typeof r.to == "string" ? r.to : void 0);
3520
+ if (!l.ok) return W.error(`add-app: ${l.error}`, l.hint === void 0 ? {} : { hint: l.hint }), 2;
3521
+ let u = l.project, d = C(c, u);
3522
+ A(d) || (j(d, { recursive: !0 }), await Ho(d, u, Ro));
3523
+ let f = Wa(i), p = C(d, f);
3524
+ if (A(p)) return W.error("add-app: target directory already exists", { targetDir: p }), 1;
3525
+ let m = null;
3526
+ if (o.kind === "web" && (m = await Uo(d), m === null)) {
3527
+ let e = (await Bo(d))?.portRange ?? Ro;
3520
3528
  return W.error("add-app: project portRange is exhausted", {
3521
- project: l,
3529
+ project: u,
3522
3530
  range: `${e.start}-${e.end}`,
3523
3531
  hint: "edit apps/<project>/project.json and widen portRange, or remove unused app entries"
3524
3532
  }), 1;
3525
3533
  }
3526
- let m = Qa(d, l, p ?? 0);
3527
- console.log(`→ scaffolding ${a.id} into ${f}${p === null ? "" : ` (port ${p})`}`);
3528
- let { written: h } = await Za({
3529
- template: a,
3530
- targetDir: f,
3531
- vars: m
3534
+ let h = Qa(f, u, m ?? 0);
3535
+ console.log(`→ scaffolding ${o.id} into ${p}${m === null ? "" : ` (port ${m})`}`);
3536
+ let { written: g } = await Za({
3537
+ template: o,
3538
+ targetDir: p,
3539
+ vars: h
3532
3540
  });
3533
- for (let e of h) console.log(` · ${e}`);
3534
- return await Wo(u, d, a.kind, p === null ? void 0 : p), await te(o, {
3541
+ for (let e of g) console.log(` · ${e}`);
3542
+ return await Wo(d, f, o.kind, m === null ? void 0 : m), await t(s, {
3535
3543
  appTemplates: [{
3536
- dir: f,
3537
- template: a.id
3544
+ dir: p,
3545
+ template: o.id
3538
3546
  }],
3539
3547
  preferPointer: !0
3540
- }).catch(() => void 0), console.log(), console.log(`✓ app '${d}' added to project '${l}' from template '${a.id}'.`), n["no-register"] !== !0 && await Yo(u, l), console.log(), console.log("Next steps:"), console.log(" pnpm install"), a.kind === "serverless" ? (console.log(` voltro serverless list apps/${l}/${d} # what would deploy`), console.log(` voltro serverless dev apps/${l}/${d}/... # run one locally`), console.log(" voltro serverless deploy --target node|cloudflare|scaleway")) : console.log(` pnpm --filter @${l}/${d} dev`), 0;
3548
+ }).catch(() => void 0), console.log(), console.log(`✓ app '${f}' added to project '${u}' from template '${o.id}'.`), r["no-register"] !== !0 && await Yo(d, u), console.log(), console.log("Next steps:"), console.log(" pnpm install"), o.kind === "serverless" ? (console.log(` voltro serverless list apps/${u}/${f} # what would deploy`), console.log(` voltro serverless dev apps/${u}/${f}/... # run one locally`), console.log(" voltro serverless deploy --target node|cloudflare|scaleway")) : console.log(` pnpm --filter @${u}/${f} dev`), 0;
3541
3549
  }, Qo = (e) => {
3542
3550
  if (typeof e != "string") return null;
3543
3551
  let t = /^(\d+)\s*(?:-|:|\.\.)\s*(\d+)$/.exec(e.trim());
@@ -3594,23 +3602,23 @@ DB_DIALECT=mssql
3594
3602
  t.close();
3595
3603
  }
3596
3604
  }, ns = async (e) => {
3597
- let { positional: t, flags: n } = Fo(e), r = No(process.cwd()), i = C(r, "apps"), a = await Ko(i);
3598
- if (a.length > 0) return W.error("create-project: this monorepo already hosts a project — a monorepo has exactly one", {
3599
- existing: a,
3605
+ let { positional: n, flags: r } = Fo(e), i = No(process.cwd()), a = C(i, "apps"), o = await Ko(a);
3606
+ if (o.length > 0) return W.error("create-project: this monorepo already hosts a project — a monorepo has exactly one", {
3607
+ existing: o,
3600
3608
  hint: "use `voltro add-app <name> --template <id>` to add apps to the existing project"
3601
3609
  }), 1;
3602
- A(i) || j(i, { recursive: !0 });
3603
- let o = $o(n), s = t[0] ?? "";
3604
- if (s && !Po(s)) return W.error("create-project: invalid name", { name: s }), 2;
3605
- if (!s) {
3606
- if (o.noInput) return W.error("create-project: name required (use positional arg or run interactively)"), 2;
3607
- s = await Io("Project name", "my-project");
3608
- }
3609
- s = Wa(s);
3610
- let c = C(i, s);
3611
- if (A(c)) return W.error("create-project: target directory already exists", { projectDir: c }), 1;
3612
- let l = o.api ?? null, u = o.web ?? null;
3613
- if (!l && !u) if (o.noInput) l = "api-backend", u = "frontend-blank";
3610
+ A(a) || j(a, { recursive: !0 });
3611
+ let s = $o(r), c = n[0] ?? "";
3612
+ if (c && !Po(c)) return W.error("create-project: invalid name", { name: c }), 2;
3613
+ if (!c) {
3614
+ if (s.noInput) return W.error("create-project: name required (use positional arg or run interactively)"), 2;
3615
+ c = await Io("Project name", "my-project");
3616
+ }
3617
+ c = Wa(c);
3618
+ let l = C(a, c);
3619
+ if (A(l)) return W.error("create-project: target directory already exists", { projectDir: l }), 1;
3620
+ let u = s.api ?? null, d = s.web ?? null;
3621
+ if (!u && !d) if (s.noInput) u = "api-backend", d = "frontend-blank";
3614
3622
  else {
3615
3623
  let e = Zt({
3616
3624
  input: Qt,
@@ -3618,32 +3626,32 @@ DB_DIALECT=mssql
3618
3626
  });
3619
3627
  try {
3620
3628
  let t = (await e.question("Scaffold apps/api (api-backend) and apps/web (frontend-blank)? [Y/n]: ")).trim().toLowerCase();
3621
- (t === "" || t === "y" || t === "yes") && (l = "api-backend", u = "frontend-blank");
3629
+ (t === "" || t === "y" || t === "yes") && (u = "api-backend", d = "frontend-blank");
3622
3630
  } finally {
3623
3631
  e.close();
3624
3632
  }
3625
3633
  }
3626
- let d = l ? await qa(l) : null, f = u ? await qa(u) : null;
3627
- if (l && !d) return W.error("create-project: unknown api template", { template: l }), 2;
3628
- if (u && !f) return W.error("create-project: unknown web template", { template: u }), 2;
3629
- if (d && d.kind !== "api") return W.error("create-project: template kind mismatch", {
3630
- id: d.id,
3631
- kind: d.kind,
3632
- expected: "api"
3633
- }), 2;
3634
- if (f && f.kind !== "web") return W.error("create-project: template kind mismatch", {
3634
+ let f = u ? await qa(u) : null, p = d ? await qa(d) : null;
3635
+ if (u && !f) return W.error("create-project: unknown api template", { template: u }), 2;
3636
+ if (d && !p) return W.error("create-project: unknown web template", { template: d }), 2;
3637
+ if (f && f.kind !== "api") return W.error("create-project: template kind mismatch", {
3635
3638
  id: f.id,
3636
3639
  kind: f.kind,
3640
+ expected: "api"
3641
+ }), 2;
3642
+ if (p && p.kind !== "web") return W.error("create-project: template kind mismatch", {
3643
+ id: p.id,
3644
+ kind: p.kind,
3637
3645
  expected: "web"
3638
3646
  }), 2;
3639
- if (!d && !f) return W.error("create-project: need at least one of --api or --web"), 2;
3640
- let p = Ro;
3641
- if (o.portRange ? p = o.portRange : o.noInput || (p = await ts(Ro)), !zo(p)) return W.error("create-project: invalid port range", { range: `${p.start}-${p.end}` }), 2;
3642
- console.log(`\n→ creating ${c}`), j(c, { recursive: !0 }), await Ho(c, s, p);
3643
- let m = async (e, t) => {
3644
- let n = C(c, e), r = null;
3645
- if (t.kind === "web" && (r = await Uo(c), r === null)) throw Error(`port range ${p.start}-${p.end} exhausted before app '${e}' could be allocated`);
3646
- let i = Qa(e, s, r ?? 0);
3647
+ if (!f && !p) return W.error("create-project: need at least one of --api or --web"), 2;
3648
+ let m = Ro;
3649
+ if (s.portRange ? m = s.portRange : s.noInput || (m = await ts(Ro)), !zo(m)) return W.error("create-project: invalid port range", { range: `${m.start}-${m.end}` }), 2;
3650
+ console.log(`\n→ creating ${l}`), j(l, { recursive: !0 }), await Ho(l, c, m);
3651
+ let h = async (e, t) => {
3652
+ let n = C(l, e), r = null;
3653
+ if (t.kind === "web" && (r = await Uo(l), r === null)) throw Error(`port range ${m.start}-${m.end} exhausted before app '${e}' could be allocated`);
3654
+ let i = Qa(e, c, r ?? 0);
3647
3655
  console.log(`→ ${t.id} → ${e}/${r === null ? "" : ` (port ${r})`}`);
3648
3656
  let { written: a } = await Za({
3649
3657
  template: t,
@@ -3651,54 +3659,54 @@ DB_DIALECT=mssql
3651
3659
  vars: i
3652
3660
  });
3653
3661
  for (let t of a) console.log(` · ${e}/${t}`);
3654
- await Wo(c, e, t.kind, r === null ? void 0 : r);
3662
+ await Wo(l, e, t.kind, r === null ? void 0 : r);
3655
3663
  };
3656
- d && await m("api", d), f && await m("web", f), await te(r, {
3657
- appTemplates: [...d ? [{
3658
- dir: C(c, "api"),
3659
- template: d.id
3660
- }] : [], ...f ? [{
3661
- dir: C(c, "web"),
3664
+ f && await h("api", f), p && await h("web", p), await t(i, {
3665
+ appTemplates: [...f ? [{
3666
+ dir: C(l, "api"),
3662
3667
  template: f.id
3668
+ }] : [], ...p ? [{
3669
+ dir: C(l, "web"),
3670
+ template: p.id
3663
3671
  }] : []],
3664
3672
  preferPointer: !0
3665
3673
  }).catch(() => void 0);
3666
- let h = null;
3667
- if (o.baseline) {
3668
- let e = await ro(o.baseline);
3669
- e ? h = e.id : W.warn("unknown baseline — skipping baseline step", {
3670
- requested: o.baseline,
3674
+ let g = null;
3675
+ if (s.baseline) {
3676
+ let e = await ro(s.baseline);
3677
+ e ? g = e.id : W.warn("unknown baseline — skipping baseline step", {
3678
+ requested: s.baseline,
3671
3679
  available: (await no()).map((e) => e.id)
3672
3680
  });
3673
- } else o.noInput || (h = await es());
3674
- if (h) {
3675
- console.log(), console.log(`→ applying baseline '${h}' at workspace root ${r}`);
3681
+ } else s.noInput || (g = await es());
3682
+ if (g) {
3683
+ console.log(), console.log(`→ applying baseline '${g}' at workspace root ${i}`);
3676
3684
  try {
3677
3685
  let e = await xo({
3678
- root: r,
3679
- baselineId: h,
3680
- projectName: s,
3686
+ root: i,
3687
+ baselineId: g,
3688
+ projectName: c,
3681
3689
  quiet: !0
3682
3690
  });
3683
- console.log(` · ${e.written.length} files written, ${e.removed.length} removed`), e.backupDir && console.log(` · backup: ${e.backupDir.replace(r + "/", "")}`), e.previousBaselineId && e.previousBaselineId !== h && console.log(` · switched from previous baseline '${e.previousBaselineId}'`);
3691
+ console.log(` · ${e.written.length} files written, ${e.removed.length} removed`), e.backupDir && console.log(` · backup: ${e.backupDir.replace(i + "/", "")}`), e.previousBaselineId && e.previousBaselineId !== g && console.log(` · switched from previous baseline '${e.previousBaselineId}'`);
3684
3692
  } catch (e) {
3685
3693
  W.error("baseline apply failed", {
3686
- baselineId: h,
3694
+ baselineId: g,
3687
3695
  error: e instanceof Error ? e.message : String(e)
3688
3696
  });
3689
3697
  }
3690
3698
  }
3691
- if (o.cache === "redis") {
3699
+ if (s.cache === "redis") {
3692
3700
  console.log(), console.log("→ enabling Redis cache backend");
3693
3701
  try {
3694
- let e = await Pi(r);
3702
+ let e = await Pi(i);
3695
3703
  for (let t of e) console.log(` ✓ ${t}`);
3696
3704
  } catch (e) {
3697
3705
  W.error("cache enable failed", { error: e instanceof Error ? e.message : String(e) });
3698
3706
  }
3699
3707
  }
3700
- return console.log(), console.log(`✓ project '${s}' scaffolded under apps/${s}/.`), h && console.log(` baseline '${h}' applied at workspace root.`), n["no-register"] !== !0 && await Yo(c, s), console.log(), console.log("Next steps:"), console.log(" pnpm install"), d && console.log(` pnpm --filter @${s}/api dev`), f && console.log(` pnpm --filter @${s}/web dev`), h === "compose" ? (console.log(" pnpm db:up # postgres only (native dev)"), console.log(" pnpm dev:docker # OR full-stack docker dev")) : h === "helm" && (console.log(" pnpm helm:lint # verify the chart"), console.log(" pnpm helm:install:dev # deploy to a dev cluster")), 0;
3701
- }, rs = (e, t) => (n) => r(E.tryPromise({
3708
+ return console.log(), console.log(`✓ project '${c}' scaffolded under apps/${c}/.`), g && console.log(` baseline '${g}' applied at workspace root.`), r["no-register"] !== !0 && await Yo(l, c), console.log(), console.log("Next steps:"), console.log(" pnpm install"), f && console.log(` pnpm --filter @${c}/api dev`), p && console.log(` pnpm --filter @${c}/web dev`), g === "compose" ? (console.log(" pnpm db:up # postgres only (native dev)"), console.log(" pnpm dev:docker # OR full-stack docker dev")) : g === "helm" && (console.log(" pnpm helm:lint # verify the chart"), console.log(" pnpm helm:install:dev # deploy to a dev cluster")), 0;
3709
+ }, rs = (e, t) => (n) => i(E.tryPromise({
3702
3710
  try: () => e(n),
3703
3711
  catch: (e) => e
3704
3712
  }).pipe(E.orDie), { scope: t }), is = rs(Lo, "voltro:list-templates"), as = rs(Zo, "voltro:add-app"), os = rs(ns, "voltro:create-project"), ss = Xt(tt), cs = (e) => new Promise((t) => setTimeout(t, e)), ls = () => {
@@ -4118,7 +4126,7 @@ replicas = 1
4118
4126
  let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
4119
4127
  if (e === "memory") throw Error("Cannot migrate against an in-memory store. Set DB_DIALECT to one of: postgres, sqlite, mysql, mariadb, mssql, turso.");
4120
4128
  return ue(e);
4121
- }, Js = async (e) => r(Gs(T(e.find((e) => !e.startsWith("-")) ?? process.cwd())), { scope: "voltro:migrate" }), Ys = /\.migration\.ts$/, Xs = async (e, t) => {
4129
+ }, Js = async (e) => i(Gs(T(e.find((e) => !e.startsWith("-")) ?? process.cwd())), { scope: "voltro:migrate" }), Ys = /\.migration\.ts$/, Xs = async (e, t) => {
4122
4130
  let n = [];
4123
4131
  for (let r of e) {
4124
4132
  let e;
@@ -5646,7 +5654,7 @@ replicas = 1
5646
5654
  return yield* E.sync(() => {
5647
5655
  for (let e of Xl(t)) console.log(e);
5648
5656
  }), 0;
5649
- }), Ql = (e) => r(Zl(process.cwd()), { scope: "voltro:deploy" }), $l = (e) => os(e), eu = {
5657
+ }), Ql = (e) => i(Zl(process.cwd()), { scope: "voltro:deploy" }), $l = (e) => os(e), eu = {
5650
5658
  "data-transfer": {
5651
5659
  env: "VOLTRO_DATA_TRANSFER_SECRET",
5652
5660
  encoding: "base64url",
@@ -6308,7 +6316,140 @@ replicas = 1
6308
6316
  "Alternatively, switch the annotation to `PartialUiStrings` if the object",
6309
6317
  "was only ever an override bag."
6310
6318
  ].join("\n")
6311
- }), Iu = /\bsetRowFilter\s*\(/, Lu = /\bresolveRowFilterScope\b/, Ru = [
6319
+ }), Iu = /\bsetRowFilter\s*\(/, Lu = /\bresolveRowFilterScope\b/, Ru = Z({
6320
+ version: "0.7.0",
6321
+ id: "0.7.0/01_row-filter-load-failure",
6322
+ title: "A row filter that fails to load now errors instead of returning zero rows",
6323
+ kind: "manual",
6324
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
6325
+ let t = e.getFullText();
6326
+ return Iu.test(t) || Lu.test(t);
6327
+ }),
6328
+ note: [
6329
+ "Row-level security: a `load` failure is now an ERROR, and is retried first.",
6330
+ "",
6331
+ "What changed and why:",
6332
+ "",
6333
+ " `setRowFilter({ load, predicate })` runs `load` once per request to resolve",
6334
+ " what the subject may see. Previously, ANY `load` failure was answered with a",
6335
+ " predicate matching zero rows — so a single transient DB blip denied every",
6336
+ " constrained table for that request, and the user saw an empty UI with a 200",
6337
+ " status and no error anywhere.",
6338
+ "",
6339
+ " That was wrong in two separate ways, and both are fixed:",
6340
+ "",
6341
+ " 1. No retry. `load` reads your store — for team/shared visibility it MUST —",
6342
+ " which makes it exactly the call that blips. A reaped connection was being",
6343
+ " answered as if it were an authorization fact. `load` now runs under a",
6344
+ " bounded retry (3 attempts, exponential from 20ms) before any decision.",
6345
+ "",
6346
+ " 2. Refusal was expressed as an empty result, which is a lie. \"We could not",
6347
+ " determine your visibility\" and \"you may see nothing\" are different facts,",
6348
+ " and an empty list is byte-identical to legitimate emptiness — the user",
6349
+ " reads \"you have no tickets\", the operator reads a healthy 200, and the",
6350
+ " outage is invisible to both. Refusal is now a typed",
6351
+ " `RowFilterUnavailable` error.",
6352
+ "",
6353
+ "What to do:",
6354
+ "",
6355
+ " 1. DECIDE, per app, what an unresolvable filter should look like to a user.",
6356
+ " This is the one thing this codemod cannot do for you.",
6357
+ "",
6358
+ " • Leave the default (`onLoadError: 'fail'`) if an empty screen would be",
6359
+ " mistaken for real data. This is the right answer for most apps.",
6360
+ " Handle `RowFilterUnavailable` in your UI as an error state (\"could not",
6361
+ " load — retry\"), the same as any other failed request.",
6362
+ "",
6363
+ " • Set `onLoadError: 'deny'` ONLY if you have looked at the screen and",
6364
+ " are content for it to render as empty during an outage:",
6365
+ " setRowFilter({ load, predicate, onLoadError: 'deny' })",
6366
+ " Your `onError` reporter still fires, so the failure stays findable in",
6367
+ " logs even though the response is a 200.",
6368
+ "",
6369
+ " There is deliberately no fail-OPEN option, and there will not be one.",
6370
+ " Serving unfiltered rows when the authorization filter is unavailable leaks",
6371
+ " data precisely when the system is under stress and nobody is watching, and",
6372
+ " it is only safe if every handler still carries the check the row filter",
6373
+ " existed to replace.",
6374
+ "",
6375
+ " 2. Tune the retry if your `load` is unusually slow or unusually cheap:",
6376
+ " setRowFilter({ load, predicate, retry: Schedule.recurs(5) }) // your own",
6377
+ " setRowFilter({ load, predicate, retry: false }) // one attempt",
6378
+ " The default is `DEFAULT_ROW_FILTER_RETRY` (exported from @voltro/runtime).",
6379
+ "",
6380
+ " 3. If you call `resolveRowFilterScope` directly — a custom entrypoint or a test",
6381
+ " harness — it now returns `Effect<RowFilterScope, RowFilterUnavailable>`",
6382
+ " instead of an infallible Effect. Your call sites must handle the error",
6383
+ " channel; `Effect.catchTag('RowFilterUnavailable', …)` or letting it",
6384
+ " propagate are both fine. TypeScript will point at every one.",
6385
+ "",
6386
+ " 4. Check any test that asserted \"a broken load yields an empty result\". Under",
6387
+ " the new default that test now fails with `RowFilterUnavailable` — which is",
6388
+ " the fix working, not a regression.",
6389
+ "",
6390
+ " 5. SUBSCRIPTIONS behave differently now, and this needs no code change but does",
6391
+ " need a client-side look: a re-resolve that fails mid-stream REVOKES the",
6392
+ " subscription and emits an error frame, rather than delivering an empty",
6393
+ " snapshot. An empty snapshot on a live subscription reads to a client as",
6394
+ " \"every row you could see was just deleted\". Make sure your subscription",
6395
+ " error handling surfaces this rather than swallowing it."
6396
+ ].join("\n")
6397
+ }), zu = Z({
6398
+ version: "0.7.0",
6399
+ id: "0.7.0/02_testing-invoke-effect-handlers",
6400
+ title: "@voltro/testing: invoke() runs Effect handlers; executor types widened",
6401
+ kind: "manual",
6402
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
6403
+ let t = e.getFullText();
6404
+ return t.includes("@voltro/testing") ? /\binvoke\s*\(/.test(t) || /\bProcedureExecutor\b/.test(t) : !1;
6405
+ }),
6406
+ note: [
6407
+ "`invoke(descriptor, executor, input, ctx)` now DETECTS an Effect-returning",
6408
+ "handler and runs it, the way the dispatcher does. Two consequences.",
6409
+ "",
6410
+ "1. Remove your run-the-Effect workaround.",
6411
+ "",
6412
+ " // before — the shim apps wrote because invoke handed back the Effect",
6413
+ " const out = await invoke(d, handler, input, ctx)",
6414
+ " const value = Effect.isEffect(out) ? await Effect.runPromise(out) : out",
6415
+ "",
6416
+ " // after",
6417
+ " const value = await invoke(d, handler, input, ctx)",
6418
+ "",
6419
+ " Leaving the shim in place is not merely redundant: `out` is now the",
6420
+ " resolved VALUE, so `Effect.isEffect(out)` is false and the branch is",
6421
+ " dead — but if the value happens to be an Effect (a handler that returns",
6422
+ " a description of work on purpose) the shim will now run it a second",
6423
+ " time. Delete it.",
6424
+ "",
6425
+ " Deleting it also RESTORES your typed errors. `Effect.runPromise` rejects",
6426
+ " with an opaque FiberFailure; `invoke` unwraps the failure and rejects",
6427
+ " with the tagged error itself, so `rejects.toMatchObject({ _tag: … })`",
6428
+ " works for an Effect handler exactly as for an async one.",
6429
+ "",
6430
+ "2. Two type signatures changed. Both accept strictly MORE than before, so",
6431
+ " handlers keep compiling — what can break is code that reads the type of",
6432
+ " a CALL:",
6433
+ "",
6434
+ " • `ProcedureExecutor<Input, Output>` gained an `Effect<Output, E>` arm",
6435
+ " (and an optional third parameter `E`, defaulting to `never`). If you",
6436
+ " call a value typed `ProcedureExecutor<…>` yourself and assign the",
6437
+ " result to `Output`, it no longer narrows — call it through `invoke`,",
6438
+ " or handle the Effect arm.",
6439
+ "",
6440
+ " • `invoke` now infers from the executor's whole return type instead of",
6441
+ " taking `Output` as its second type parameter. An EXPLICIT type",
6442
+ " argument list — `invoke<typeof d, Note>(…)` — now pins the executor's",
6443
+ " RETURN type (so an async handler no longer matches). Drop the explicit",
6444
+ " type arguments; inference gives the same result, and better:",
6445
+ "",
6446
+ " await invoke(d, handler, input, ctx) // resolves to Note",
6447
+ "",
6448
+ "Nothing else about `invoke` changed — guards, the input decode, the",
6449
+ "transaction, the deadlock replay, `afterCommit` and the plugin interceptor",
6450
+ "chain wrap an Effect handler exactly as they wrap an async one."
6451
+ ].join("\n")
6452
+ }), Bu = /\bpasswordHash\b/, Vu = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, Hu = [
6312
6453
  _u,
6313
6454
  vu,
6314
6455
  xu,
@@ -6320,156 +6461,155 @@ replicas = 1
6320
6461
  Au,
6321
6462
  Pu,
6322
6463
  Fu,
6464
+ Ru,
6465
+ zu,
6323
6466
  Z({
6324
- version: "0.7.0",
6325
- id: "0.7.0/01_row-filter-load-failure",
6326
- title: "A row filter that fails to load now errors instead of returning zero rows",
6467
+ version: "0.8.0",
6468
+ id: "0.8.0/01_optional-password-hash",
6469
+ title: "`UserRecord.passwordHash` is optional password-less identities are now first-class",
6327
6470
  kind: "manual",
6328
6471
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
6329
6472
  let t = e.getFullText();
6330
- return Iu.test(t) || Lu.test(t);
6473
+ return Bu.test(t) || Vu.test(t);
6331
6474
  }),
6332
6475
  note: [
6333
- "Row-level security: a `load` failure is now an ERROR, and is retried first.",
6476
+ "`UserRecord.passwordHash` is now `string | null | undefined`, not `string`.",
6334
6477
  "",
6335
6478
  "What changed and why:",
6336
6479
  "",
6337
- " `setRowFilter({ load, predicate })` runs `load` once per request to resolve",
6338
- " what the subject may see. Previously, ANY `load` failure was answered with a",
6339
- " predicate matching zero rows so a single transient DB blip denied every",
6340
- " constrained table for that request, and the user saw an empty UI with a 200",
6341
- " status and no error anywhere.",
6480
+ " `passwordHash` was required, which quietly asserted that every identity has",
6481
+ " a password. SSO-only, magic-link-only, passkey-only and provider-PAT apps",
6482
+ " have none and the only way they could use `subjectFromUser(...)` was to",
6483
+ " invent a fake hash. That is strictly worse than storing nothing: a fake hash",
6484
+ " is a real value sitting in the column a verifier compares against. Several",
6485
+ " apps chose instead to hand-build the `Subject`, losing the helper entirely.",
6486
+ "",
6487
+ " The `users` table column is now nullable to match. That side needs NO action",
6488
+ " from you — it rides the declarative differ on `voltro db apply` / next boot.",
6489
+ "",
6490
+ "Why this can break your build even though nothing was removed:",
6491
+ "",
6492
+ " Widening a type is additive for code that WRITES a `UserRecord` and breaking",
6493
+ " for code that READS one. These compiled before and do not now:",
6494
+ "",
6495
+ " const h: string = user.passwordHash // TS2322",
6496
+ " user.passwordHash.length // TS18048/TS2531",
6497
+ " verifyPassword(pw, user.passwordHash) // TS2345",
6342
6498
  "",
6343
- " That was wrong in two separate ways, and both are fixed:",
6499
+ "What to do TypeScript will point at every site; decide per site:",
6344
6500
  "",
6345
- " 1. No retry. `load` reads your store for team/shared visibility it MUST ",
6346
- " which makes it exactly the call that blips. A reaped connection was being",
6347
- " answered as if it were an authorization fact. `load` now runs under a",
6348
- " bounded retry (3 attempts, exponential from 20ms) before any decision.",
6501
+ " 1. A site that VERIFIES a password. Treat an absent hash as a REFUSAL, and",
6502
+ " make it indistinguishable from a wrong password same status, same body,",
6503
+ " and still burn the same work so the latency does not reveal which accounts",
6504
+ " are password-less:",
6349
6505
  "",
6350
- " 2. Refusal was expressed as an empty result, which is a lie. \"We could not",
6351
- " determine your visibility\" and \"you may see nothing\" are different facts,",
6352
- " and an empty list is byte-identical to legitimate emptiness — the user",
6353
- " reads \"you have no tickets\", the operator reads a healthy 200, and the",
6354
- " outage is invisible to both. Refusal is now a typed",
6355
- " `RowFilterUnavailable` error.",
6506
+ " const stored = user?.passwordHash",
6507
+ " const hasPassword = typeof stored === 'string' && stored.length > 0",
6508
+ " const verdict = hasPassword",
6509
+ " ? yield* verifyPasswordWithRehash(input.password, stored)",
6510
+ " : yield* /* decoy hash + verify */ Effect.succeed({ valid: false as const })",
6511
+ " if (!user || !verdict.valid) return json(401, { error: 'invalid credentials' })",
6356
6512
  "",
6357
- "What to do:",
6513
+ " Note `''` is treated as absent too. The framework's own `handleSignIn`",
6514
+ " already does exactly this; if you use it, you need no change here.",
6358
6515
  "",
6359
- " 1. DECIDE, per app, what an unresolvable filter should look like to a user.",
6360
- " This is the one thing this codemod cannot do for you.",
6516
+ " 2. A site that WRITES a hash (sign-up, password reset, set-password). Nothing",
6517
+ " to do assigning a hash to a previously password-less user works",
6518
+ " unchanged and promotes them to a password user.",
6361
6519
  "",
6362
- " Leave the default (`onLoadError: 'fail'`) if an empty screen would be",
6363
- " mistaken for real data. This is the right answer for most apps.",
6364
- " Handle `RowFilterUnavailable` in your UI as an error state (\"could not",
6365
- " load — retry\"), the same as any other failed request.",
6520
+ " 3. A site that only ever runs for password users. Narrow explicitly and let",
6521
+ " the impossible case be loud, rather than silencing it:",
6366
6522
  "",
6367
- " • Set `onLoadError: 'deny'` ONLY if you have looked at the screen and",
6368
- " are content for it to render as empty during an outage:",
6369
- " setRowFilter({ load, predicate, onLoadError: 'deny' })",
6370
- " Your `onError` reporter still fires, so the failure stays findable in",
6371
- " logs even though the response is a 200.",
6523
+ " if (!user.passwordHash) throw new Error('expected a password user')",
6372
6524
  "",
6373
- " There is deliberately no fail-OPEN option, and there will not be one.",
6374
- " Serving unfiltered rows when the authorization filter is unavailable leaks",
6375
- " data precisely when the system is under stress and nobody is watching, and",
6376
- " it is only safe if every handler still carries the check the row filter",
6377
- " existed to replace.",
6525
+ "What NOT to do, and this is the whole point of a manual note:",
6378
6526
  "",
6379
- " 2. Tune the retry if your `load` is unusually slow or unusually cheap:",
6380
- " setRowFilter({ load, predicate, retry: Schedule.recurs(5) }) // your own",
6381
- " setRowFilter({ load, predicate, retry: false }) // one attempt",
6382
- " The default is `DEFAULT_ROW_FILTER_RETRY` (exported from @voltro/runtime).",
6527
+ " Do NOT write `user.passwordHash!`. It re-asserts the invariant that just",
6528
+ " stopped holding, and the failure surfaces at runtime instead of here.",
6383
6529
  "",
6384
- " 3. If you call `resolveRowFilterScope` directly a custom entrypoint or a test",
6385
- " harness it now returns `Effect<RowFilterScope, RowFilterUnavailable>`",
6386
- " instead of an infallible Effect. Your call sites must handle the error",
6387
- " channel; `Effect.catchTag('RowFilterUnavailable', …)` or letting it",
6388
- " propagate are both fine. TypeScript will point at every one.",
6530
+ " Do NOT write `user.passwordHash ?? ''` (or any placeholder). You are then",
6531
+ " comparing a submitted password against a value you invented. Whether that",
6532
+ " refuses depends on the verifier, and a verifier that returns true for a",
6533
+ " degenerate hash means ANY password signs in as a password-less user. If you",
6534
+ " take one thing from this note: an absent hash must be a refusal decision,",
6535
+ " never a default value.",
6389
6536
  "",
6390
- " 4. Check any test that asserted \"a broken load yields an empty result\". Under",
6391
- " the new default that test now fails with `RowFilterUnavailable` — which is",
6392
- " the fix working, not a regression.",
6537
+ " Do NOT add a `NOT NULL` back to your own users table to dodge the change —",
6538
+ " it forces the fake hash back in at insert time.",
6393
6539
  "",
6394
- " 5. SUBSCRIPTIONS behave differently now, and this needs no code change but does",
6395
- " need a client-side look: a re-resolve that fails mid-stream REVOKES the",
6396
- " subscription and emits an error frame, rather than delivering an empty",
6397
- " snapshot. An empty snapshot on a live subscription reads to a client as",
6398
- " \"every row you could see was just deleted\". Make sure your subscription",
6399
- " error handling surfaces this rather than swallowing it."
6540
+ " 4. If you implement your OWN `UserStore`, widen the field on your record type",
6541
+ " to match and make sure your read path maps a NULL column to `null`/",
6542
+ " `undefined` rather than to `''`-by-coercion."
6400
6543
  ].join("\n")
6401
6544
  }),
6402
6545
  Z({
6403
- version: "0.7.0",
6404
- id: "0.7.0/02_testing-invoke-effect-handlers",
6405
- title: "@voltro/testing: invoke() runs Effect handlers; executor types widened",
6546
+ version: "0.8.0",
6547
+ id: "0.8.0/02_ssr-streams-and-loaderdata",
6548
+ title: "`ssr` pages stream; `useLoaderData<T>()` returns `LoaderData<T>`",
6406
6549
  kind: "manual",
6407
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
6408
- let t = e.getFullText();
6409
- return t.includes("@voltro/testing") ? /\binvoke\s*\(/.test(t) || /\bProcedureExecutor\b/.test(t) : !1;
6410
- }),
6411
6550
  note: [
6412
- "`invoke(descriptor, executor, input, ctx)` now DETECTS an Effect-returning",
6413
- "handler and runs it, the way the dispatcher does. Two consequences.",
6551
+ "Two things changed for server-rendered pages.",
6414
6552
  "",
6415
- "1. Remove your run-the-Effect workaround.",
6553
+ "1. `renderMode: 'ssr'` responses are now STREAMED.",
6416
6554
  "",
6417
- " // before the shim apps wrote because invoke handed back the Effect",
6418
- " const out = await invoke(d, handler, input, ctx)",
6419
- " const value = Effect.isEffect(out) ? await Effect.runPromise(out) : out",
6555
+ " `voltro dev` and `voltro start` send the <head> + page shell first and",
6556
+ " flush each <Suspense> boundary as its data settles. Consequences:",
6420
6557
  "",
6421
- " // after",
6422
- " const value = await invoke(d, handler, input, ctx)",
6558
+ " Re-run `voltro build` before `voltro start`. The prebuilt SSR bundle",
6559
+ " (.framework/dist/server/ssrEntry.js) now has to export the streaming",
6560
+ " helpers; a bundle built by an older CLI will fail at request time.",
6561
+ " If your deploy already runs `voltro build` before `voltro start`,",
6562
+ " nothing to do.",
6563
+ " • `ssr` responses are chunked and carry no `content-length`. If a",
6564
+ " reverse proxy in front of the app buffers responses (nginx",
6565
+ " `proxy_buffering on`, some CDNs), turn buffering off for these",
6566
+ " routes or the streaming win is absorbed by the proxy.",
6567
+ " • Pages with `interactive: 'none'` or `'islands'`, and `isr` /",
6568
+ " `static` pages, are unchanged — still rendered as one buffered",
6569
+ " document.",
6423
6570
  "",
6424
- " Leaving the shim in place is not merely redundant: `out` is now the",
6425
- " resolved VALUE, so `Effect.isEffect(out)` is false and the branch is",
6426
- " dead — but if the value happens to be an Effect (a handler that returns",
6427
- " a description of work on purpose) the shim will now run it a second",
6428
- " time. Delete it.",
6571
+ "2. `useLoaderData<T>()` now returns `LoaderData<T>`.",
6429
6572
  "",
6430
- " Deleting it also RESTORES your typed errors. `Effect.runPromise` rejects",
6431
- " with an opaque FiberFailure; `invoke` unwraps the failure and rejects",
6432
- " with the tagged error itself, so `rejects.toMatchObject({ _tag: })`",
6433
- " works for an Effect handler exactly as for an async one.",
6573
+ " For every concrete `T`, `LoaderData<T>` IS `T` — the overwhelming",
6574
+ " majority of call sites need no change. It differs only for a loader that",
6575
+ " returns the new `defer()`, where it flattens to eager fields as values",
6576
+ " plus deferred fields as promises.",
6434
6577
  "",
6435
- "2. Two type signatures changed. Both accept strictly MORE than before, so",
6436
- " handlers keep compiling — what can break is code that reads the type of",
6437
- " a CALL:",
6578
+ " The one shape that can stop compiling is a call that passes an",
6579
+ " UNRESOLVED generic through, e.g.",
6438
6580
  "",
6439
- " `ProcedureExecutor<Input, Output>` gained an `Effect<Output, E>` arm",
6440
- " (and an optional third parameter `E`, defaulting to `never`). If you",
6441
- " call a value typed `ProcedureExecutor<…>` yourself and assign the",
6442
- " result to `Output`, it no longer narrows — call it through `invoke`,",
6443
- " or handle the Effect arm.",
6581
+ " const usePageData = <D,>(): D => useLoaderData<D>()",
6444
6582
  "",
6445
- " `invoke` now infers from the executor's whole return type instead of",
6446
- " taking `Output` as its second type parameter. An EXPLICIT type",
6447
- " argument list — `invoke<typeof d, Note>(…)` — now pins the executor's",
6448
- " RETURN type (so an async handler no longer matches). Drop the explicit",
6449
- " type arguments; inference gives the same result, and better:",
6583
+ " `LoaderData<D>` cannot be assigned to `D` while `D` is still a type",
6584
+ " parameter. Fix by propagating the mapped type instead of `D`:",
6450
6585
  "",
6451
- " await invoke(d, handler, input, ctx) // resolves to Note",
6586
+ " const usePageData = <D,>(): LoaderData<D> => useLoaderData<D>()",
6452
6587
  "",
6453
- "Nothing else about `invoke` changedguards, the input decode, the",
6454
- "transaction, the deadlock replay, `afterCommit` and the plugin interceptor",
6455
- "chain wrap an Effect handler exactly as they wrap an async one."
6588
+ " Run your app's `typecheck` after upgrading that is the only reliable",
6589
+ " way to find these, which is why this step is written out rather than",
6590
+ " applied automatically.",
6591
+ "",
6592
+ "New in the same release: `defer()` + `<Await>` for streaming one slow field",
6593
+ "of a loader instead of blocking the whole response. See the Loaders & meta",
6594
+ "docs. `defer()` requires `renderMode: 'ssr'` and `interactive: 'full'`;",
6595
+ "every other combination is a hard error naming the page."
6456
6596
  ].join("\n")
6457
6597
  })
6458
- ], zu = (e) => [
6598
+ ], Uu = (e) => [
6459
6599
  `${e}/**/*.ts`,
6460
6600
  `${e}/**/*.tsx`,
6461
6601
  `!${e}/**/node_modules/**`,
6462
6602
  `!${e}/**/dist/**`,
6463
6603
  `!${e}/**/.framework/**`,
6464
6604
  `!${e}/**/*.d.ts`
6465
- ], Bu = (e, t) => {
6605
+ ], Wu = (e, t) => {
6466
6606
  let n = e.split(".").map(Number), r = t.split(".").map(Number);
6467
6607
  for (let e = 0; e < 3; e++) {
6468
6608
  let t = (n[e] ?? 0) - (r[e] ?? 0);
6469
6609
  if (t !== 0) return t;
6470
6610
  }
6471
6611
  return 0;
6472
- }, Vu = (e, t, n = Ru) => n.filter((n) => Bu(e, n.version) < 0 && Bu(n.version, t) <= 0).slice().sort(gu), Hu = (e, t, n = () => {}) => {
6612
+ }, Gu = (e, t, n = Hu) => n.filter((n) => Wu(e, n.version) < 0 && Wu(n.version, t) <= 0).slice().sort(gu), Ku = (e, t, n = () => {}) => {
6473
6613
  let r = [], i = [], a = [];
6474
6614
  for (let o of t) {
6475
6615
  if (!(!o.appliesTo || o.appliesTo({ project: e }))) {
@@ -6506,8 +6646,8 @@ replicas = 1
6506
6646
  manual: i,
6507
6647
  skipped: a
6508
6648
  };
6509
- }, Uu = async (e) => {
6510
- let t = e.log ?? (() => {}), n = Vu(e.from, e.to, e.codemods ?? Ru);
6649
+ }, qu = async (e) => {
6650
+ let t = e.log ?? (() => {}), n = Gu(e.from, e.to, e.codemods ?? Hu);
6511
6651
  if (n.length === 0) return {
6512
6652
  applied: [],
6513
6653
  manual: [],
@@ -6518,24 +6658,24 @@ replicas = 1
6518
6658
  manipulationSettings: { quoteKind: tn.Single }
6519
6659
  }), i = /* @__PURE__ */ new Set();
6520
6660
  for (let t of n) {
6521
- let n = t.scope ? t.scope.flatMap((t) => t.startsWith("!") ? [`!${e.root}/${t.slice(1)}`] : [`${e.root}/${t}`]) : zu(e.root);
6661
+ let n = t.scope ? t.scope.flatMap((t) => t.startsWith("!") ? [`!${e.root}/${t.slice(1)}`] : [`${e.root}/${t}`]) : Uu(e.root);
6522
6662
  for (let e of n) i.add(e);
6523
6663
  }
6524
6664
  r.addSourceFilesAtPaths([...i]);
6525
- let a = Hu(r, n, t);
6665
+ let a = Ku(r, n, t);
6526
6666
  return e.dryRun || await r.save(), a;
6527
- }, Wu = /^(workspace:|catalog:|link:|file:|npm:|portal:)/, Gu = /* @__PURE__ */ new Set([
6667
+ }, Ju = /^(workspace:|catalog:|link:|file:|npm:|portal:)/, Yu = /* @__PURE__ */ new Set([
6528
6668
  "pnpm",
6529
6669
  "npm",
6530
6670
  "yarn",
6531
6671
  "bun"
6532
- ]), Ku = [
6672
+ ]), Xu = [
6533
6673
  ["pnpm-lock.yaml", "pnpm"],
6534
6674
  ["yarn.lock", "yarn"],
6535
6675
  ["bun.lock", "bun"],
6536
6676
  ["bun.lockb", "bun"],
6537
6677
  ["package-lock.json", "npm"]
6538
- ], qu = (e) => {
6678
+ ], Zu = (e) => {
6539
6679
  let t = [], n = T(e);
6540
6680
  for (; t.push(n), !A(C(n, ".git"));) {
6541
6681
  let e = S(n);
@@ -6543,20 +6683,20 @@ replicas = 1
6543
6683
  n = e;
6544
6684
  }
6545
6685
  return t;
6546
- }, Ju = (e) => {
6547
- for (let t of qu(e)) {
6686
+ }, Qu = (e) => {
6687
+ for (let t of Zu(e)) {
6548
6688
  let e = C(t, "package.json");
6549
6689
  if (A(e)) try {
6550
6690
  let t = JSON.parse(N(e, "utf8")).packageManager;
6551
6691
  if (typeof t == "string") {
6552
6692
  let e = t.split("@")[0]?.trim();
6553
- if (e && Gu.has(e)) return e;
6693
+ if (e && Yu.has(e)) return e;
6554
6694
  }
6555
6695
  } catch {}
6556
- for (let [e, n] of Ku) if (A(C(t, e))) return n;
6696
+ for (let [e, n] of Xu) if (A(C(t, e))) return n;
6557
6697
  }
6558
6698
  return "npm";
6559
- }, Yu = /* @__PURE__ */ new Set([
6699
+ }, $u = /* @__PURE__ */ new Set([
6560
6700
  "node_modules",
6561
6701
  ".git",
6562
6702
  "dist",
@@ -6568,8 +6708,8 @@ replicas = 1
6568
6708
  ".turbo",
6569
6709
  ".cache",
6570
6710
  ".output"
6571
- ]), Xu = 4, Zu = (e) => {
6572
- for (let t of qu(e)) {
6711
+ ]), ed = 4, td = (e) => {
6712
+ for (let t of Zu(e)) {
6573
6713
  if (A(C(t, "pnpm-workspace.yaml")) || A(C(t, "pnpm-workspace.yml"))) return t;
6574
6714
  let e = C(t, "package.json");
6575
6715
  if (A(e)) try {
@@ -6577,10 +6717,10 @@ replicas = 1
6577
6717
  if (Array.isArray(n) ? n.length > 0 : typeof n == "object" && n) return t;
6578
6718
  } catch {}
6579
6719
  }
6580
- }, Qu = (e) => {
6720
+ }, nd = (e) => {
6581
6721
  let t = [], n = (e, r) => {
6582
6722
  let i = C(e, "package.json");
6583
- if (A(i) && t.push(i), r >= Xu) return;
6723
+ if (A(i) && t.push(i), r >= ed) return;
6584
6724
  let a;
6585
6725
  try {
6586
6726
  a = Fe(e);
@@ -6588,7 +6728,7 @@ replicas = 1
6588
6728
  return;
6589
6729
  }
6590
6730
  for (let t of a) {
6591
- if (t.startsWith(".") || Yu.has(t)) continue;
6731
+ if (t.startsWith(".") || $u.has(t)) continue;
6592
6732
  let i = C(e, t);
6593
6733
  try {
6594
6734
  if (!Ie(i).isDirectory()) continue;
@@ -6599,7 +6739,7 @@ replicas = 1
6599
6739
  }
6600
6740
  };
6601
6741
  return n(T(e), 0), t;
6602
- }, $u = (e) => {
6742
+ }, rd = (e) => {
6603
6743
  try {
6604
6744
  return nt("git", ["status", "--porcelain"], {
6605
6745
  cwd: e,
@@ -6608,7 +6748,7 @@ replicas = 1
6608
6748
  } catch {
6609
6749
  return !1;
6610
6750
  }
6611
- }, ed = /^\d+\.\d+\.\d+/, td = (e, t) => {
6751
+ }, id = /^\d+\.\d+\.\d+/, ad = (e, t) => {
6612
6752
  try {
6613
6753
  let n = nt(e, [...t], {
6614
6754
  encoding: "utf8",
@@ -6621,14 +6761,14 @@ replicas = 1
6621
6761
  if (!n) return;
6622
6762
  if (n.startsWith("{")) {
6623
6763
  let e = JSON.parse(n).version;
6624
- return typeof e == "string" && ed.test(e) ? e : void 0;
6764
+ return typeof e == "string" && id.test(e) ? e : void 0;
6625
6765
  }
6626
6766
  let r = n.split("\n").map((e) => e.trim()).filter(Boolean).pop();
6627
- return r && ed.test(r) ? r : void 0;
6767
+ return r && id.test(r) ? r : void 0;
6628
6768
  } catch {
6629
6769
  return;
6630
6770
  }
6631
- }, nd = () => {
6771
+ }, od = () => {
6632
6772
  try {
6633
6773
  let e = nt("yarn", ["--version"], {
6634
6774
  encoding: "utf8",
@@ -6642,7 +6782,7 @@ replicas = 1
6642
6782
  } catch {
6643
6783
  return 1;
6644
6784
  }
6645
- }, rd = (e, t, n) => [...t === "pnpm" ? [["pnpm", [
6785
+ }, sd = (e, t, n) => [...t === "pnpm" ? [["pnpm", [
6646
6786
  "view",
6647
6787
  e,
6648
6788
  "version"
@@ -6667,17 +6807,17 @@ replicas = 1
6667
6807
  "view",
6668
6808
  e,
6669
6809
  "version"
6670
- ]]], id = (e, t = "npm") => {
6671
- for (let [n, r] of rd(e, t, t === "yarn" ? nd() : 1)) {
6672
- let e = td(n, r);
6810
+ ]]], cd = (e, t = "npm") => {
6811
+ for (let [n, r] of sd(e, t, t === "yarn" ? od() : 1)) {
6812
+ let e = ad(n, r);
6673
6813
  if (e) return e;
6674
6814
  }
6675
- }, ad = (e) => JSON.parse(N(e, "utf8")), od = (e) => {
6815
+ }, ld = (e) => JSON.parse(N(e, "utf8")), ud = (e) => {
6676
6816
  let t = C(e, "node_modules", "@voltro", "cli", "package.json");
6677
6817
  if (!A(t)) return;
6678
- let n = ad(t).version;
6818
+ let n = ld(t).version;
6679
6819
  return typeof n == "string" ? n : void 0;
6680
- }, sd = (e) => {
6820
+ }, dd = (e) => {
6681
6821
  let t = [];
6682
6822
  for (let n of ["dependencies", "devDependencies"]) {
6683
6823
  let r = e[n];
@@ -6688,7 +6828,7 @@ replicas = 1
6688
6828
  });
6689
6829
  }
6690
6830
  return t;
6691
- }, cd = (e, t, n) => sd(e).map(({ name: e, spec: r }) => Wu.test(r) ? {
6831
+ }, fd = (e, t, n) => dd(e).map(({ name: e, spec: r }) => Ju.test(r) ? {
6692
6832
  name: e,
6693
6833
  from: r,
6694
6834
  to: r,
@@ -6697,22 +6837,22 @@ replicas = 1
6697
6837
  name: e,
6698
6838
  from: r,
6699
6839
  to: `${n ? "" : /^[\^~]/.exec(r)?.[0] ?? ""}${t}`
6700
- }), ld = (e, t) => {
6840
+ }), pd = (e, t) => {
6701
6841
  for (let n of t) if (!n.skipped) for (let t of ["dependencies", "devDependencies"]) {
6702
6842
  let r = e[t];
6703
6843
  r && n.name in r && (r[n.name] = n.to);
6704
6844
  }
6705
- }, ud = (e, t, n) => e.map(({ path: e, pkg: r }) => ({
6845
+ }, md = (e, t, n) => e.map(({ path: e, pkg: r }) => ({
6706
6846
  path: e,
6707
- entries: cd(r, t, n)
6708
- })).filter((e) => e.entries.length > 0), dd = (e, t) => {
6847
+ entries: fd(r, t, n)
6848
+ })).filter((e) => e.entries.length > 0), hd = (e, t) => {
6709
6849
  let n = new Map(e.map((e) => [e.path, e])), r = [];
6710
6850
  for (let e of t) {
6711
6851
  let t = n.get(e.path);
6712
- t && (ld(t.pkg, e.entries), r.push(t));
6852
+ t && (pd(t.pkg, e.entries), r.push(t));
6713
6853
  }
6714
6854
  return r;
6715
- }, fd = (e) => {
6855
+ }, gd = (e) => {
6716
6856
  let t, n, r = process.cwd(), i = [];
6717
6857
  for (let a = 0; a < e.length; a++) {
6718
6858
  let o = e[a];
@@ -6741,22 +6881,22 @@ replicas = 1
6741
6881
  ...t === void 0 ? {} : { to: t },
6742
6882
  ...n === void 0 ? {} : { from: n }
6743
6883
  };
6744
- }, pd = "voltro update [app] [flags]\n\nUpgrade an app to a newer framework release: bump every @voltro/* dependency,\ninstall, and run the codemods shipped with the target version.\n\nIn a workspace (pnpm-workspace.yaml, or a package.json \"workspaces\" field),\nEVERY member package.json that declares @voltro/* is bumped in lockstep and the\ninstall runs once at the workspace root — so api / web / shared packages never\nend up on different framework versions.\n\nFlags:\n --to <version> Target version (default: the latest published version).\n --from <version> Source version — overrides the detected installed version.\n Use it after a hand-edited bump so the codemods for the\n delta you actually crossed still run.\n --root <dir> App directory to update (default: the current directory).\n A positional [app] does the same.\n --dry-run Print the plan — every package.json that would be bumped —\n and write nothing. Allowed on a dirty tree.\n --force Proceed on a dirty working tree, and re-run even when the\n installed version already equals the target.\n --exact Pin exact versions (drop the ^ / ~ range prefix).\n --codemods-only Run ONLY the codemods for [from, to] against the installed\n tree — no version bump, no install. The recovery path after\n a manual bump, or after a failed install. Alias:\n --run-codemods.\n -h, --help Print this help.\n\nExamples:\n voltro update\n voltro update --to 0.6.0 --dry-run\n voltro update --codemods-only --from 0.5.0 --to 0.6.0\n\nAfter updating: run `voltro db apply` (NOT db migrate) to reconcile changed\nframework tables, then your typecheck.", md = (e) => e.includes("--help") || e.includes("-h"), hd = async (e, t = {}) => {
6745
- let n = t.latestVersion ?? id, r = t.isGitClean ?? $u, i = t.install ?? ((e, t) => it(e, ["install"], {
6884
+ }, _d = "voltro update [app] [flags]\n\nUpgrade an app to a newer framework release: bump every @voltro/* dependency,\ninstall, and run the codemods shipped with the target version.\n\nIn a workspace (pnpm-workspace.yaml, or a package.json \"workspaces\" field),\nEVERY member package.json that declares @voltro/* is bumped in lockstep and the\ninstall runs once at the workspace root — so api / web / shared packages never\nend up on different framework versions.\n\nFlags:\n --to <version> Target version (default: the latest published version).\n --from <version> Source version — overrides the detected installed version.\n Use it after a hand-edited bump so the codemods for the\n delta you actually crossed still run.\n --root <dir> App directory to update (default: the current directory).\n A positional [app] does the same.\n --dry-run Print the plan — every package.json that would be bumped —\n and write nothing. Allowed on a dirty tree.\n --force Proceed on a dirty working tree, and re-run even when the\n installed version already equals the target.\n --exact Pin exact versions (drop the ^ / ~ range prefix).\n --codemods-only Run ONLY the codemods for [from, to] against the installed\n tree — no version bump, no install. The recovery path after\n a manual bump, or after a failed install. Alias:\n --run-codemods.\n -h, --help Print this help.\n\nExamples:\n voltro update\n voltro update --to 0.6.0 --dry-run\n voltro update --codemods-only --from 0.5.0 --to 0.6.0\n\nAfter updating: run `voltro db apply` (NOT db migrate) to reconcile changed\nframework tables, then your typecheck.", vd = (e) => e.includes("--help") || e.includes("-h"), yd = async (e, t = {}) => {
6885
+ let n = t.latestVersion ?? cd, r = t.isGitClean ?? rd, i = t.install ?? ((e, t) => it(e, ["install"], {
6746
6886
  cwd: t,
6747
6887
  stdio: "inherit"
6748
6888
  }).status ?? 1);
6749
- if (md(e)) return console.log(pd), 0;
6750
- let a = fd(e), o = C(a.root, "package.json");
6889
+ if (vd(e)) return console.log(_d), 0;
6890
+ let a = gd(e), o = C(a.root, "package.json");
6751
6891
  if (!A(o)) return console.error(`voltro update: no package.json at ${a.root}`), 1;
6752
- let s = ad(o), c = sd(s);
6892
+ let s = ld(o), c = dd(s);
6753
6893
  if (c.length === 0) return console.error("voltro update: no @voltro/* dependencies here — not a Voltro app."), 1;
6754
6894
  if (!a.dryRun && !a.force && !r(a.root)) return console.error("voltro update: working tree is not clean. Commit/stash first so you can review the"), console.error(" codemod diff — or re-run with --force (not recommended) or --dry-run (preview only)."), 1;
6755
- let l = Zu(a.root), u = l ?? a.root, d = () => od(a.root) ?? (l ? od(l) : void 0), f = a.from ?? d() ?? /^\d+\.\d+\.\d+$/.exec(c.find((e) => e.name === "@voltro/cli")?.spec.replace(/^[\^~]/, "") ?? "")?.[0];
6895
+ let l = td(a.root), u = l ?? a.root, d = () => ud(a.root) ?? (l ? ud(l) : void 0), f = a.from ?? d() ?? /^\d+\.\d+\.\d+$/.exec(c.find((e) => e.name === "@voltro/cli")?.spec.replace(/^[\^~]/, "") ?? "")?.[0];
6756
6896
  if (!f) return console.error("voltro update: could not determine the source framework version."), console.error(" Run your package manager install first, or pass --from <version>."), 1;
6757
- let p = Ju(a.root), m = a.to ?? (a.codemodsOnly ? d() : void 0) ?? n("@voltro/cli", p);
6897
+ let p = Qu(a.root), m = a.to ?? (a.codemodsOnly ? d() : void 0) ?? n("@voltro/cli", p);
6758
6898
  if (!m) return console.error(`voltro update: could not resolve the latest @voltro/cli version via ${p}.`), console.error(" Pin an explicit target with --to <version>."), 1;
6759
- if (a.codemodsOnly) return f === m ? (console.log(`voltro update: --codemods-only with from === to (${m}) — no delta, nothing to apply.`), console.log(" Pass --from <earlier-version> to re-run the codemods you skipped."), 0) : (console.log(`voltro update: re-applying codemods ${f} → ${m} (no version bump)…`), _d([
6899
+ if (a.codemodsOnly) return f === m ? (console.log(`voltro update: --codemods-only with from === to (${m}) — no delta, nothing to apply.`), console.log(" Pass --from <earlier-version> to re-run the codemods you skipped."), 0) : (console.log(`voltro update: re-applying codemods ${f} → ${m} (no version bump)…`), xd([
6760
6900
  "--root",
6761
6901
  a.root,
6762
6902
  "--from",
@@ -6766,7 +6906,7 @@ replicas = 1
6766
6906
  ...a.dryRun ? ["--dry-run"] : []
6767
6907
  ]));
6768
6908
  if (f === m && !a.force) return console.log(`voltro update: already on ${m} — nothing to do.`), console.log(" (Bumped @voltro/* by hand? Re-run the skipped codemods with"), console.log(" `voltro update --codemods-only --from <earlier-version>`.)"), 0;
6769
- let h = [o, ...l ? Qu(l) : []], g = [], _ = /* @__PURE__ */ new Set();
6909
+ let h = [o, ...l ? nd(l) : []], g = [], _ = /* @__PURE__ */ new Set();
6770
6910
  for (let e of h) if (!_.has(e)) {
6771
6911
  if (_.add(e), e === o) {
6772
6912
  g.push({
@@ -6778,11 +6918,11 @@ replicas = 1
6778
6918
  try {
6779
6919
  g.push({
6780
6920
  path: e,
6781
- pkg: ad(e)
6921
+ pkg: ld(e)
6782
6922
  });
6783
6923
  } catch {}
6784
6924
  }
6785
- let v = ud(g, m, a.exact);
6925
+ let v = md(g, m, a.exact);
6786
6926
  console.log(`voltro update: ${f} → ${m}`);
6787
6927
  let y = v.length > 1;
6788
6928
  l && console.log(` workspace: ${l} (${v.length} package.json with @voltro/* deps)`);
@@ -6793,7 +6933,7 @@ replicas = 1
6793
6933
  for (let n of e.entries.filter((e) => e.skipped)) console.log(`${t}${n.name}: kept (${n.skipped})`);
6794
6934
  }
6795
6935
  if (console.log(` package manager: ${p}`), console.log(` install runs in: ${u}`), console.log(` codemods: applied from ${m} after install`), a.dryRun) return console.log("\n(dry-run — nothing written. Codemods are resolved from the target version and"), console.log(" only listed/applied after install; re-run without --dry-run to upgrade.)"), 0;
6796
- for (let e of dd(g, v)) Le(e.path, JSON.stringify(e.pkg, null, 2) + "\n");
6936
+ for (let e of hd(g, v)) Le(e.path, JSON.stringify(e.pkg, null, 2) + "\n");
6797
6937
  console.log(`\nInstalling with ${p} in ${u}…`);
6798
6938
  let ee = i(p, u);
6799
6939
  if (ee !== 0) return console.error(`\nvoltro update: ${p} install failed in ${u}.`), console.error(` • package.json was bumped to ${m} (${v.length} file${v.length === 1 ? "" : "s"}).`), console.error(" • CODEMODS WERE NOT APPLIED — they ship inside the target version, which the"), console.error(" failed install did not put on disk, so there was nothing to run."), console.error(" Fix the install, re-run it, then apply the codemods you are missing:"), console.error(` voltro update --codemods-only --from ${f} --to ${m}`), ee;
@@ -6813,7 +6953,7 @@ replicas = 1
6813
6953
  stdio: "inherit"
6814
6954
  });
6815
6955
  if (e.status !== 0) return e.status ?? 1;
6816
- } else console.log("\nApplying codemods (in-process — installed bin not found)…"), await _d([
6956
+ } else console.log("\nApplying codemods (in-process — installed bin not found)…"), await xd([
6817
6957
  "--root",
6818
6958
  a.root,
6819
6959
  "--from",
@@ -6822,7 +6962,7 @@ replicas = 1
6822
6962
  m
6823
6963
  ]);
6824
6964
  return console.log("\n✓ update complete. Next:"), console.log(" • voltro db apply # reconcile any changed framework tables (NOT db migrate)"), console.log(" • <your typecheck> # verify the codemods + your code compile against the new API"), 0;
6825
- }, gd = (e) => {
6965
+ }, bd = (e) => {
6826
6966
  let t = (t) => {
6827
6967
  let n = e.indexOf(t);
6828
6968
  return n >= 0 ? e[n + 1] : void 0;
@@ -6833,8 +6973,8 @@ replicas = 1
6833
6973
  to: t("--to") ?? "0.0.0",
6834
6974
  dryRun: e.includes("--dry-run")
6835
6975
  };
6836
- }, _d = async (e) => {
6837
- let t = gd(e), n = await Uu({
6976
+ }, xd = async (e) => {
6977
+ let t = bd(e), n = await qu({
6838
6978
  root: t.root,
6839
6979
  from: t.from,
6840
6980
  to: t.to,
@@ -6851,18 +6991,18 @@ replicas = 1
6851
6991
  }
6852
6992
  }
6853
6993
  return t.dryRun && console.log("\n(dry-run — no files written)"), 0;
6854
- }, vd = k({ scope: "voltro:test" }), yd = async (e) => {
6994
+ }, Sd = k({ scope: "voltro:test" }), Cd = async (e) => {
6855
6995
  let t = T(e.find((e) => !e.startsWith("-")) ?? process.cwd());
6856
- vd.info("running vitest", { root: t });
6996
+ Sd.info("running vitest", { root: t });
6857
6997
  let n = await (await import("vitest/node")).startVitest("test", [], {
6858
6998
  root: t,
6859
6999
  watch: !1,
6860
7000
  passWithNoTests: !0
6861
7001
  });
6862
7002
  return n ? (await n.close(), +!n.state.getFiles().every((e) => e.result?.state !== "fail")) : 1;
6863
- }, bd = class extends Me.TaggedError("BundleError") {}, xd = 0, Sd = (e) => C(at(), `voltro-sls-${e}-${process.pid}-${xd++}.mjs`), Cd = (e) => E.tryPromise({
7003
+ }, wd = class extends Me.TaggedError("BundleError") {}, Td = 0, Ed = (e) => C(at(), `voltro-sls-${e}-${process.pid}-${Td++}.mjs`), Dd = (e) => E.tryPromise({
6864
7004
  try: async () => {
6865
- let t = Sd(x(e).replace(/\W+/g, "-"));
7005
+ let t = Ed(x(e).replace(/\W+/g, "-"));
6866
7006
  await nn.build({
6867
7007
  entryPoints: [e],
6868
7008
  outfile: t,
@@ -6879,12 +7019,12 @@ replicas = 1
6879
7019
  await Je(t, { force: !0 });
6880
7020
  }
6881
7021
  },
6882
- catch: (t) => new bd({
7022
+ catch: (t) => new wd({
6883
7023
  file: e,
6884
7024
  message: t instanceof Error ? t.message : String(t)
6885
7025
  })
6886
- }), wd = (e) => e === "browser" ? "browser" : e === "neutral" ? "neutral" : "node", Td = (e) => E.gen(function* () {
6887
- let t = yield* Cd(e.sourceFile), n = C(e.outDir, t.name), r = C(n, e.platform.esbuild.entryFile ?? (e.platform.esbuild.format === "esm" ? "index.mjs" : "index.js"));
7026
+ }), Od = (e) => e === "browser" ? "browser" : e === "neutral" ? "neutral" : "node", kd = (e) => E.gen(function* () {
7027
+ let t = yield* Dd(e.sourceFile), n = C(e.outDir, t.name), r = C(n, e.platform.esbuild.entryFile ?? (e.platform.esbuild.format === "esm" ? "index.mjs" : "index.js"));
6888
7028
  return yield* E.tryPromise({
6889
7029
  try: async () => {
6890
7030
  await Ge(n, { recursive: !0 }), await nn.build({
@@ -6896,7 +7036,7 @@ replicas = 1
6896
7036
  },
6897
7037
  outfile: r,
6898
7038
  bundle: !0,
6899
- platform: wd(e.platform.esbuild.platform),
7039
+ platform: Od(e.platform.esbuild.platform),
6900
7040
  format: e.platform.esbuild.format,
6901
7041
  ...e.platform.esbuild.targetEs ? { target: e.platform.esbuild.targetEs } : {},
6902
7042
  ...e.platform.esbuild.conditions ? { conditions: [...e.platform.esbuild.conditions] } : {},
@@ -6905,7 +7045,7 @@ replicas = 1
6905
7045
  logLevel: "silent"
6906
7046
  });
6907
7047
  },
6908
- catch: (t) => new bd({
7048
+ catch: (t) => new wd({
6909
7049
  file: e.sourceFile,
6910
7050
  message: t instanceof Error ? t.message : String(t)
6911
7051
  })
@@ -6915,32 +7055,32 @@ replicas = 1
6915
7055
  dir: n,
6916
7056
  ...t.runtime ? { runtime: t.runtime } : {}
6917
7057
  };
6918
- }), Ed = (e, t = {}) => E.async((n) => {
7058
+ }), Ad = (e, t = {}) => E.async((n) => {
6919
7059
  let r;
6920
7060
  return rn(e, t).then((t) => {
6921
7061
  r = t, console.log(`Serving ${e.length} function(s) on http://${t.host}:${t.port}`);
6922
7062
  for (let e of t.routes) console.log(` ▸ ${e.name} ${e.method} http://${t.host}:${t.port}${e.path}`);
6923
7063
  console.log(" (Ctrl-C to stop)");
6924
7064
  }).catch((e) => n(E.fail(e instanceof Error ? e : Error(String(e))))), E.promise(() => r ? r.close() : Promise.resolve());
6925
- }), Dd = (e) => JSON.stringify(e), Od = (e, t) => {
7065
+ }), jd = (e) => JSON.stringify(e), Md = (e, t) => {
6926
7066
  let n = [
6927
- `name = ${Dd(e.name)}`,
6928
- `main = ${Dd(x(e.file))}`,
6929
- `compatibility_date = ${Dd(t.compatibilityDate ?? "2024-09-23")}`
7067
+ `name = ${jd(e.name)}`,
7068
+ `main = ${jd(x(e.file))}`,
7069
+ `compatibility_date = ${jd(t.compatibilityDate ?? "2024-09-23")}`
6930
7070
  ];
6931
7071
  t.nodeCompat && n.push("compatibility_flags = [\"nodejs_compat\"]"), n.push(`workers_dev = ${t.workersDev === !1 ? "false" : "true"}`);
6932
7072
  let r = t.vars ?? {}, i = Object.keys(r);
6933
7073
  if (i.length > 0) {
6934
7074
  n.push("", "[vars]");
6935
- for (let e of i) n.push(`${e} = ${Dd(r[e])}`);
7075
+ for (let e of i) n.push(`${e} = ${jd(r[e])}`);
6936
7076
  }
6937
7077
  return n.join("\n") + "\n";
6938
- }, kd = /https:\/\/[^\s'"()<>]+workers\.dev[^\s'"()<>]*/, Ad = (e) => ({
7078
+ }, Nd = /https:\/\/[^\s'"()<>]+workers\.dev[^\s'"()<>]*/, Pd = (e) => ({
6939
7079
  ...an,
6940
7080
  deploy: (t, n) => E.gen(function* () {
6941
7081
  let r = n.options ?? {}, i = C(t.dir, "wrangler.toml");
6942
7082
  yield* E.tryPromise({
6943
- try: () => Ye(i, Od(t, r), "utf8"),
7083
+ try: () => Ye(i, Md(t, r), "utf8"),
6944
7084
  catch: (e) => new L({
6945
7085
  target: "cloudflare",
6946
7086
  message: `failed to write wrangler.toml: ${String(e)}`
@@ -6979,17 +7119,17 @@ replicas = 1
6979
7119
  target: "cloudflare",
6980
7120
  message: `wrangler deploy failed (exit ${e.exitCode})`,
6981
7121
  detail: e.stderr
6982
- }))), l = kd.exec(c.stdout)?.[0];
7122
+ }))), l = Nd.exec(c.stdout)?.[0];
6983
7123
  return {
6984
7124
  target: "cloudflare",
6985
7125
  name: t.name,
6986
7126
  ...l ? { url: l } : {}
6987
7127
  };
6988
7128
  })
6989
- }), jd = /https:\/\/[^\s'"()<>]+\.fnc\.[a-z0-9-]+\.scw\.cloud[^\s'"()<>]*/, Md = "handler.handle", Nd = (e, t) => {
6990
- let n = [`handler=${Md}`, "http-option=enabled"], r = e.runtime;
7129
+ }), Fd = /https:\/\/[^\s'"()<>]+\.fnc\.[a-z0-9-]+\.scw\.cloud[^\s'"()<>]*/, Id = "handler.handle", Ld = (e, t) => {
7130
+ let n = [`handler=${Id}`, "http-option=enabled"], r = e.runtime;
6991
7131
  return r?.memoryMb !== void 0 && n.push(`memory-limit=${r.memoryMb}`), r?.timeoutSeconds !== void 0 && n.push(`timeout=${r.timeoutSeconds}s`), r?.minScale !== void 0 && n.push(`min-scale=${r.minScale}`), r?.maxScale !== void 0 && n.push(`max-scale=${r.maxScale}`), t && n.push(`privacy=${t}`), n;
6992
- }, Pd = (e, t) => E.try({
7132
+ }, Rd = (e, t) => E.try({
6993
7133
  try: () => {
6994
7134
  let n = e.trim();
6995
7135
  if (!n) return;
@@ -7001,14 +7141,14 @@ replicas = 1
7001
7141
  message: "could not parse `scw function list` output",
7002
7142
  detail: e instanceof Error ? e.message : String(e)
7003
7143
  })
7004
- }), Fd = (e) => ({
7144
+ }), zd = (e) => ({
7005
7145
  ...sn,
7006
7146
  deploy: (t, n) => E.gen(function* () {
7007
7147
  let r = n.options ?? {}, i = C(t.dir, "function.zip"), a = r.runtime ?? "node22", o = r.namespaceId;
7008
7148
  if (n.dryRun) return yield* E.logInfo("dry-run: would deploy Scaleway Function", {
7009
7149
  name: t.name,
7010
7150
  zip: `zip -r -q ${i} . (cwd ${t.dir})`,
7011
- ensure: `scw function function create|update name=${t.name} runtime=${a} ${Nd(t, r.privacy).join(" ")}`,
7151
+ ensure: `scw function function create|update name=${t.name} runtime=${a} ${Ld(t, r.privacy).join(" ")}`,
7012
7152
  deploy: `scw function deploy name=${t.name} runtime=${a} zip-file=${i}${o ? ` namespace-id=${o}` : ""}`
7013
7153
  }), {
7014
7154
  target: "scaleway",
@@ -7037,7 +7177,7 @@ replicas = 1
7037
7177
  i,
7038
7178
  "."
7039
7179
  ], { cwd: t.dir }).pipe(E.mapError(s("zip")));
7040
- let c = yield* Pd((yield* e.exec("scw", [
7180
+ let c = yield* Rd((yield* e.exec("scw", [
7041
7181
  "function",
7042
7182
  "function",
7043
7183
  "list",
@@ -7052,7 +7192,7 @@ replicas = 1
7052
7192
  "update",
7053
7193
  c,
7054
7194
  `runtime=${a}`,
7055
- ...Nd(t, r.privacy)
7195
+ ...Ld(t, r.privacy)
7056
7196
  ], { env: n.env }).pipe(E.mapError(s("scw function update"))) : yield* e.exec("scw", [
7057
7197
  "function",
7058
7198
  "function",
@@ -7060,7 +7200,7 @@ replicas = 1
7060
7200
  `namespace-id=${o}`,
7061
7201
  `name=${t.name}`,
7062
7202
  `runtime=${a}`,
7063
- ...Nd(t, r.privacy)
7203
+ ...Ld(t, r.privacy)
7064
7204
  ], { env: n.env }).pipe(E.mapError(s("scw function create")));
7065
7205
  let l = yield* e.exec("scw", [
7066
7206
  "function",
@@ -7069,7 +7209,7 @@ replicas = 1
7069
7209
  `namespace-id=${o}`,
7070
7210
  `runtime=${a}`,
7071
7211
  `zip-file=${i}`
7072
- ], { env: n.env }).pipe(E.mapError(s("scw function deploy"))), u = jd.exec(l.stdout)?.[0];
7212
+ ], { env: n.env }).pipe(E.mapError(s("scw function deploy"))), u = Fd.exec(l.stdout)?.[0];
7073
7213
  return {
7074
7214
  target: "scaleway",
7075
7215
  name: t.name,
@@ -7077,14 +7217,14 @@ replicas = 1
7077
7217
  ...u ? { url: u } : {}
7078
7218
  };
7079
7219
  })
7080
- }), Id = (e, t) => `# AUTO-GENERATED by @voltro/serverless — self-hosted Node function image.
7220
+ }), Bd = (e, t) => `# AUTO-GENERATED by @voltro/serverless — self-hosted Node function image.
7081
7221
  FROM node:22-slim
7082
7222
  WORKDIR /app
7083
7223
  COPY ${e} ./
7084
7224
  ENV PORT=${t}
7085
7225
  EXPOSE ${t}
7086
7226
  CMD ["node", "${e}"]
7087
- `, Ld = (e, t, n) => `# ${e} — self-hosted serverless function
7227
+ `, Vd = (e, t, n) => `# ${e} — self-hosted serverless function
7088
7228
 
7089
7229
  Built by \`voltro serverless\`. This folder is a complete, runnable artifact — the
7090
7230
  same handler that would run on Cloudflare/Scaleway, on your own infra.
@@ -7127,16 +7267,16 @@ ${e}.example.com {
7127
7267
 
7128
7268
  To run ALL of a project's functions in ONE process instead of a per-function
7129
7269
  image, use \`voltro serverless serve\` from the project root.
7130
- `, Rd = (e, t) => E.tryPromise({
7270
+ `, Hd = (e, t) => E.tryPromise({
7131
7271
  try: async () => {
7132
7272
  let n = x(e.file);
7133
- return await Ye(C(e.dir, "Dockerfile"), Id(n, t), "utf8"), await Ye(C(e.dir, "README.md"), Ld(e.name, n, t), "utf8"), ["Dockerfile", "README.md"];
7273
+ return await Ye(C(e.dir, "Dockerfile"), Bd(n, t), "utf8"), await Ye(C(e.dir, "README.md"), Vd(e.name, n, t), "utf8"), ["Dockerfile", "README.md"];
7134
7274
  },
7135
7275
  catch: (e) => new L({
7136
7276
  target: "node",
7137
7277
  message: `failed to write self-host kit: ${String(e)}`
7138
7278
  })
7139
- }), zd = () => ({
7279
+ }), Ud = () => ({
7140
7280
  ...on,
7141
7281
  deploy: (e, t) => E.gen(function* () {
7142
7282
  let n = (t.options ?? {}).port ?? 8910, r = x(e.file), i = {
@@ -7148,23 +7288,23 @@ image, use \`voltro serverless serve\` from the project root.
7148
7288
  return t.dryRun ? (yield* E.logInfo("dry-run: would emit self-hosted Node artifact + kit (Dockerfile, README)", i), {
7149
7289
  target: "node",
7150
7290
  name: e.name
7151
- }) : (yield* Rd(e, n), yield* E.logInfo("self-hosted Node artifact + kit ready", i), {
7291
+ }) : (yield* Hd(e, n), yield* E.logInfo("self-hosted Node artifact + kit ready", i), {
7152
7292
  target: "node",
7153
7293
  name: e.name,
7154
7294
  url: `http://localhost:${n}`
7155
7295
  });
7156
7296
  })
7157
- }), Bd = [
7297
+ }), Wd = [
7158
7298
  "node",
7159
7299
  "cloudflare",
7160
7300
  "scaleway"
7161
- ], Vd = (e) => Bd.includes(e), Hd = (e, t) => {
7301
+ ], Gd = (e) => Wd.includes(e), Kd = (e, t) => {
7162
7302
  switch (e) {
7163
- case "node": return zd();
7164
- case "cloudflare": return Ad(t);
7165
- case "scaleway": return Fd(t);
7303
+ case "node": return Ud();
7304
+ case "cloudflare": return Pd(t);
7305
+ case "scaleway": return zd(t);
7166
7306
  }
7167
- }, Ud = class extends Me.TaggedError("CommandError") {}, Wd = (e, t) => [e, ...t].join(" "), Gd = {
7307
+ }, qd = class extends Me.TaggedError("CommandError") {}, Jd = (e, t) => [e, ...t].join(" "), Yd = {
7168
7308
  exec: (e, t, n) => E.async((r) => {
7169
7309
  let i = rt(e, [...t], {
7170
7310
  cwd: n?.cwd,
@@ -7182,8 +7322,8 @@ image, use \`voltro serverless serve\` from the project root.
7182
7322
  a += String(e);
7183
7323
  }), i.stderr?.on("data", (e) => {
7184
7324
  o += String(e);
7185
- }), i.on("error", (n) => r(E.fail(new Ud({
7186
- command: Wd(e, t),
7325
+ }), i.on("error", (n) => r(E.fail(new qd({
7326
+ command: Jd(e, t),
7187
7327
  exitCode: -1,
7188
7328
  stderr: n.message
7189
7329
  })))), i.on("close", (n) => {
@@ -7192,8 +7332,8 @@ image, use \`voltro serverless serve\` from the project root.
7192
7332
  exitCode: i,
7193
7333
  stdout: a,
7194
7334
  stderr: o
7195
- }) : E.fail(new Ud({
7196
- command: Wd(e, t),
7335
+ }) : E.fail(new qd({
7336
+ command: Jd(e, t),
7197
7337
  exitCode: i,
7198
7338
  stderr: o || a
7199
7339
  })));
@@ -7205,7 +7345,7 @@ image, use \`voltro serverless serve\` from the project root.
7205
7345
  let n = rt(process.platform === "win32" ? "where" : "which", [e], { stdio: "ignore" });
7206
7346
  n.on("error", () => t(E.succeed(!1))), n.on("close", (e) => t(E.succeed(e === 0)));
7207
7347
  })
7208
- }, Q = (e) => E.sync(() => console.log(e)), Kd = (e, t) => {
7348
+ }, Q = (e) => E.sync(() => console.log(e)), Xd = (e, t) => {
7209
7349
  let n = "node", r = C(t, ".voltro", "serverless"), i = !1, a = {}, o = [], s;
7210
7350
  for (let c = 0; c < e.length; c++) {
7211
7351
  let l = e[c];
@@ -7240,7 +7380,7 @@ image, use \`voltro serverless serve\` from the project root.
7240
7380
  default: l.startsWith("-") ? s = `unknown flag '${l}'` : o.push(l);
7241
7381
  }
7242
7382
  }
7243
- return Vd(n) || (s = `unknown --target '${n}' (one of: ${Bd.join(", ")})`, n = "node"), {
7383
+ return Gd(n) || (s = `unknown --target '${n}' (one of: ${Wd.join(", ")})`, n = "node"), {
7244
7384
  target: n,
7245
7385
  out: r,
7246
7386
  dryRun: i,
@@ -7248,7 +7388,7 @@ image, use \`voltro serverless serve\` from the project root.
7248
7388
  positional: o,
7249
7389
  error: s
7250
7390
  };
7251
- }, qd = (e, t) => e.length > 0 ? E.succeed(e.map((e) => T(t, e))) : Hl(t), Jd = () => Q([
7391
+ }, Zd = (e, t) => e.length > 0 ? E.succeed(e.map((e) => T(t, e))) : Hl(t), Qd = () => Q([
7252
7392
  "voltro serverless <list|dev|serve|build|deploy> [options] [files…]",
7253
7393
  "",
7254
7394
  " list discover *.serverless.ts files",
@@ -7258,7 +7398,7 @@ image, use \`voltro serverless serve\` from the project root.
7258
7398
  " deploy [--target <id>] ship: node → self-host artifact, cloudflare/scaleway → vendor",
7259
7399
  "",
7260
7400
  "Options:",
7261
- ` --target <id> ${Bd.join(" | ")} (default node)`,
7401
+ ` --target <id> ${Wd.join(" | ")} (default node)`,
7262
7402
  " --out <dir> artifact dir (default .voltro/serverless)",
7263
7403
  " --dry-run bundle, then print the deploy plan",
7264
7404
  " --port <n> dev/serve: listen port (default 8910)",
@@ -7268,7 +7408,7 @@ image, use \`voltro serverless serve\` from the project root.
7268
7408
  " --node-compat Cloudflare: add nodejs_compat flag",
7269
7409
  " --compat-date <date> Cloudflare: compatibility_date",
7270
7410
  " --runtime <r> Scaleway: node20 | node22"
7271
- ].join("\n")).pipe(E.as(0)), Yd = (e) => {
7411
+ ].join("\n")).pipe(E.as(0)), $d = (e) => {
7272
7412
  let t = 8910, n, r = [], i;
7273
7413
  for (let a = 0; a < e.length; a++) {
7274
7414
  let o = e[a];
@@ -7280,13 +7420,13 @@ image, use \`voltro serverless serve\` from the project root.
7280
7420
  files: r,
7281
7421
  error: i
7282
7422
  };
7283
- }, Xd = (e, t, n) => E.gen(function* () {
7284
- let { port: r, host: i, files: a, error: o } = Yd(e);
7423
+ }, ef = (e, t, n) => E.gen(function* () {
7424
+ let { port: r, host: i, files: a, error: o } = $d(e);
7285
7425
  if (o) return yield* Q(`voltro serverless ${n.label}: ${o}`), 2;
7286
7426
  let s = a.length > 0 ? a.map((e) => T(t, e)) : yield* Hl(t);
7287
7427
  if (s.length === 0) return yield* Q("No *.serverless.ts function found."), 0;
7288
7428
  if (n.single && s.length > 1) return yield* Q(`voltro serverless dev: multiple functions found — name one (or use \`serve\`):\n${s.map((e) => ` ${w(t, e)}`).join("\n")}`), 2;
7289
- let c = yield* E.forEach(s, (e) => Cd(e).pipe(E.map((e) => ({
7429
+ let c = yield* E.forEach(s, (e) => Dd(e).pipe(E.map((e) => ({
7290
7430
  ok: !0,
7291
7431
  d: e
7292
7432
  })), E.catchAll((e) => E.succeed({
@@ -7297,36 +7437,36 @@ image, use \`voltro serverless serve\` from the project root.
7297
7437
  let u = c.flatMap((e) => e.ok ? [e.d] : []);
7298
7438
  if (u.length === 0) return 1;
7299
7439
  let d = i ?? n.defaultHost;
7300
- return yield* Ed(u, {
7440
+ return yield* Ad(u, {
7301
7441
  port: r,
7302
7442
  ...d ? { host: d } : {}
7303
7443
  }).pipe(E.catchAll((e) => E.as(Q(`server error: ${e.message}`), void 0))), 0;
7304
- }), Zd = (e) => E.gen(function* () {
7444
+ }), tf = (e) => E.gen(function* () {
7305
7445
  let [t, ...n] = e, r = process.cwd();
7306
- if (t === void 0 || t === "help" || t === "--help" || t === "-h") return yield* Jd();
7307
- if (t === "dev") return yield* Xd(n, r, {
7446
+ if (t === void 0 || t === "help" || t === "--help" || t === "-h") return yield* Qd();
7447
+ if (t === "dev") return yield* ef(n, r, {
7308
7448
  single: !0,
7309
7449
  defaultHost: "127.0.0.1",
7310
7450
  label: "dev"
7311
7451
  });
7312
- if (t === "serve") return yield* Xd(n, r, {
7452
+ if (t === "serve") return yield* ef(n, r, {
7313
7453
  single: !1,
7314
7454
  defaultHost: "0.0.0.0",
7315
7455
  label: "serve"
7316
7456
  });
7317
7457
  if (t !== "list" && t !== "build" && t !== "deploy") return yield* Q(`voltro serverless: unknown subcommand '${t}'`), 2;
7318
- let i = Kd(n, r);
7458
+ let i = Xd(n, r);
7319
7459
  if (i.error) return yield* Q(`voltro serverless: ${i.error}`), 2;
7320
- let a = yield* qd(i.positional, r);
7460
+ let a = yield* Zd(i.positional, r);
7321
7461
  if (a.length === 0) return yield* Q("No *.serverless.ts functions found."), 0;
7322
7462
  if (t === "list") {
7323
7463
  yield* Q(`Found ${a.length} serverless function(s):`);
7324
7464
  for (let e of a) yield* Q(` ${w(r, e)}`);
7325
7465
  return 0;
7326
7466
  }
7327
- let o = Gd, s = Hd(i.target, o), c = C(i.out, s.id), l = 0;
7467
+ let o = Yd, s = Kd(i.target, o), c = C(i.out, s.id), l = 0;
7328
7468
  for (let e of a) {
7329
- let n = yield* Td({
7469
+ let n = yield* kd({
7330
7470
  platform: s,
7331
7471
  sourceFile: e,
7332
7472
  outDir: c
@@ -7344,7 +7484,7 @@ image, use \`voltro serverless serve\` from the project root.
7344
7484
  if (t === "build") {
7345
7485
  if (s.id === "node") {
7346
7486
  let e = typeof i.options.port == "number" ? i.options.port : 8910;
7347
- yield* Rd(n.b, e).pipe(E.catchAll(() => E.void));
7487
+ yield* Hd(n.b, e).pipe(E.catchAll(() => E.void));
7348
7488
  }
7349
7489
  yield* Q(` ✓ bundled ${n.b.name} → ${w(r, n.b.file)} [${s.id}]`);
7350
7490
  continue;
@@ -7368,25 +7508,25 @@ image, use \`voltro serverless serve\` from the project root.
7368
7508
  yield* Q(` ✓ ${t === "deploy" ? "deployed" : "built"} ${n.b.name} [${s.id}]${u}`);
7369
7509
  }
7370
7510
  return +(l > 0);
7371
- }), Qd = (e) => r(Zd(e), { scope: "voltro:serverless" }), $d = async (e) => {
7511
+ }), nf = (e) => i(tf(e), { scope: "voltro:serverless" }), rf = async (e) => {
7372
7512
  try {
7373
7513
  return await We(e), !0;
7374
7514
  } catch {
7375
7515
  return !1;
7376
7516
  }
7377
- }, ef = "# AUTO-GENERATED by @voltro/cli — hashed assets are immutable for a year.\n/assets/*\n Cache-Control: public, max-age=31536000, immutable\n", tf = "# AUTO-GENERATED by @voltro/cli — single-page-app fallback.\n/* /index.html 200\n", nf = (e, t) => E.promise(async () => {
7517
+ }, af = "# AUTO-GENERATED by @voltro/cli — hashed assets are immutable for a year.\n/assets/*\n Cache-Control: public, max-age=31536000, immutable\n", of = "# AUTO-GENERATED by @voltro/cli — single-page-app fallback.\n/* /index.html 200\n", sf = (e, t) => E.promise(async () => {
7378
7518
  let n = [], r = C(e, "_headers");
7379
- if (await $d(r) || (await Ye(r, ef, "utf8"), n.push("_headers")), t) {
7519
+ if (await rf(r) || (await Ye(r, af, "utf8"), n.push("_headers")), t) {
7380
7520
  let t = C(e, "_redirects");
7381
- await $d(t) || (await Ye(t, tf, "utf8"), n.push("_redirects"));
7521
+ await rf(t) || (await Ye(t, of, "utf8"), n.push("_redirects"));
7382
7522
  }
7383
7523
  return n;
7384
- }), rf = /https:\/\/[^\s'"()<>]+\.pages\.dev[^\s'"()<>]*/, af = (e) => ({
7524
+ }), cf = /https:\/\/[^\s'"()<>]+\.pages\.dev[^\s'"()<>]*/, lf = (e) => ({
7385
7525
  id: "cloudflare-pages",
7386
7526
  label: "Cloudflare Pages",
7387
7527
  deploy: (t, n) => E.gen(function* () {
7388
7528
  let r = n.options ?? {};
7389
- yield* nf(t, n.spaFallback ?? !1);
7529
+ yield* sf(t, n.spaFallback ?? !1);
7390
7530
  let i = [
7391
7531
  "pages",
7392
7532
  "deploy",
@@ -7419,7 +7559,7 @@ image, use \`voltro serverless serve\` from the project root.
7419
7559
  host: "cloudflare-pages",
7420
7560
  message: `wrangler pages deploy failed (exit ${e.exitCode})`,
7421
7561
  detail: e.stderr
7422
- }))), c = rf.exec(s.stdout)?.[0];
7562
+ }))), c = cf.exec(s.stdout)?.[0];
7423
7563
  return {
7424
7564
  host: "cloudflare-pages",
7425
7565
  filesUploaded: 0,
@@ -7427,7 +7567,7 @@ image, use \`voltro serverless serve\` from the project root.
7427
7567
  ...c ? { url: c } : {}
7428
7568
  };
7429
7569
  })
7430
- }), of = (e) => (e.match(/^upload:/gm) ?? []).length, sf = (e) => ({
7570
+ }), uf = (e) => (e.match(/^upload:/gm) ?? []).length, df = (e) => ({
7431
7571
  id: "s3",
7432
7572
  label: "S3-compatible bucket (aws s3 sync)",
7433
7573
  deploy: (t, n) => E.gen(function* () {
@@ -7484,24 +7624,24 @@ image, use \`voltro serverless serve\` from the project root.
7484
7624
  }))), u = yield* l(s, "aws s3 sync (assets)"), d = yield* l(c, "aws s3 sync (html)");
7485
7625
  return {
7486
7626
  host: "s3",
7487
- filesUploaded: of(u.stdout) + of(d.stdout),
7627
+ filesUploaded: uf(u.stdout) + uf(d.stdout),
7488
7628
  filesSkipped: 0,
7489
7629
  ...r.websiteUrl ? { url: r.websiteUrl } : {}
7490
7630
  };
7491
7631
  })
7492
- }), cf = (e) => {
7632
+ }), ff = (e) => {
7493
7633
  try {
7494
7634
  let t = JSON.parse(e);
7495
7635
  return t.ssl_url ?? t.url ?? t.deploy_url;
7496
7636
  } catch {
7497
7637
  return;
7498
7638
  }
7499
- }, lf = (e) => ({
7639
+ }, pf = (e) => ({
7500
7640
  id: "netlify",
7501
7641
  label: "Netlify",
7502
7642
  deploy: (t, n) => E.gen(function* () {
7503
7643
  let r = n.options ?? {};
7504
- yield* nf(t, n.spaFallback ?? !1);
7644
+ yield* sf(t, n.spaFallback ?? !1);
7505
7645
  let i = [
7506
7646
  "deploy",
7507
7647
  "--dir",
@@ -7524,7 +7664,7 @@ image, use \`voltro serverless serve\` from the project root.
7524
7664
  host: "netlify",
7525
7665
  message: "`netlify` CLI not found on PATH (install: npm i -g netlify-cli)"
7526
7666
  }));
7527
- let a = cf((yield* e.exec("netlify", i, { env: n.env }).pipe(E.mapError((e) => new R({
7667
+ let a = ff((yield* e.exec("netlify", i, { env: n.env }).pipe(E.mapError((e) => new R({
7528
7668
  host: "netlify",
7529
7669
  message: `netlify deploy failed (exit ${e.exitCode})`,
7530
7670
  detail: e.stderr
@@ -7536,17 +7676,17 @@ image, use \`voltro serverless serve\` from the project root.
7536
7676
  ...a ? { url: a } : {}
7537
7677
  };
7538
7678
  })
7539
- }), uf = [
7679
+ }), mf = [
7540
7680
  "cloudflare-pages",
7541
7681
  "s3",
7542
7682
  "netlify"
7543
- ], df = (e) => uf.includes(e), ff = (e, t) => {
7683
+ ], hf = (e) => mf.includes(e), gf = (e, t) => {
7544
7684
  switch (e) {
7545
- case "cloudflare-pages": return af(t);
7546
- case "s3": return sf(t);
7547
- case "netlify": return lf(t);
7685
+ case "cloudflare-pages": return lf(t);
7686
+ case "s3": return df(t);
7687
+ case "netlify": return pf(t);
7548
7688
  }
7549
- }, $ = (e) => E.sync(() => console.log(e)), pf = (e) => E.promise(() => We(e).then(() => !0, () => !1)), mf = (e, t) => {
7689
+ }, $ = (e) => E.sync(() => console.log(e)), _f = (e) => E.promise(() => We(e).then(() => !0, () => !1)), vf = (e, t) => {
7550
7690
  let n = "", r = T(t, "dist"), i = t, a = !1, o = !1, s = !1, c = {}, l;
7551
7691
  for (let u = 0; u < e.length; u++) {
7552
7692
  let d = e[u];
@@ -7600,7 +7740,7 @@ image, use \`voltro serverless serve\` from the project root.
7600
7740
  options: c,
7601
7741
  error: l
7602
7742
  };
7603
- }, hf = (e, t) => E.gen(function* () {
7743
+ }, yf = (e, t) => E.gen(function* () {
7604
7744
  return (yield* E.promise(() => Ke(C(e, ".voltro-build.json"), "utf8").then((e) => {
7605
7745
  try {
7606
7746
  let t = JSON.parse(e).profile;
@@ -7609,14 +7749,14 @@ image, use \`voltro serverless serve\` from the project root.
7609
7749
  return;
7610
7750
  }
7611
7751
  }, () => void 0))) ?? (yield* Mn(t));
7612
- }), gf = () => $([
7752
+ }), bf = () => $([
7613
7753
  "voltro static <hosts|deploy> [options]",
7614
7754
  "",
7615
7755
  " hosts list supported static hosts",
7616
7756
  " deploy --host <id> deploy ./dist to a host",
7617
7757
  "",
7618
7758
  "Options:",
7619
- ` --host <id> ${uf.join(" | ")}`,
7759
+ ` --host <id> ${mf.join(" | ")}`,
7620
7760
  " --dir <dir> static dir to deploy (default dist)",
7621
7761
  " --app <dir> web app source root for the render-mode gate (default cwd)",
7622
7762
  " --allow-dynamic deploy even with ssr/isr pages (they become client-rendered)",
@@ -7628,25 +7768,25 @@ image, use \`voltro serverless serve\` from the project root.
7628
7768
  " --branch <name> cloudflare-pages: branch",
7629
7769
  " --site-id <id> netlify: site",
7630
7770
  " --website-url <url> s3: public URL to report"
7631
- ].join("\n")).pipe(E.as(0)), _f = (e) => E.gen(function* () {
7771
+ ].join("\n")).pipe(E.as(0)), xf = (e) => E.gen(function* () {
7632
7772
  let [t, ...n] = e, r = process.cwd();
7633
- if (t === void 0 || t === "help" || t === "--help" || t === "-h") return yield* gf();
7773
+ if (t === void 0 || t === "help" || t === "--help" || t === "-h") return yield* bf();
7634
7774
  if (t === "hosts") {
7635
7775
  yield* $("Supported static hosts:");
7636
- for (let e of uf) yield* $(` ${e}`);
7776
+ for (let e of mf) yield* $(` ${e}`);
7637
7777
  return 0;
7638
7778
  }
7639
7779
  if (t !== "deploy") return yield* $(`voltro static: unknown subcommand '${t}'`), 2;
7640
- let i = mf(n, r);
7780
+ let i = vf(n, r);
7641
7781
  if (i.error) return yield* $(`voltro static: ${i.error}`), 2;
7642
- if (!df(i.host)) return yield* $(`voltro static deploy: --host must be one of ${uf.join(", ")}`), 2;
7643
- if (!(yield* pf(i.dir))) return yield* $(`voltro static deploy: directory not found: ${i.dir} (run \`voltro build\` first?)`), 1;
7644
- let a = yield* hf(i.dir, i.app);
7782
+ if (!hf(i.host)) return yield* $(`voltro static deploy: --host must be one of ${mf.join(", ")}`), 2;
7783
+ if (!(yield* _f(i.dir))) return yield* $(`voltro static deploy: directory not found: ${i.dir} (run \`voltro build\` first?)`), 1;
7784
+ let a = yield* yf(i.dir, i.app);
7645
7785
  if (!a.staticSafe) {
7646
7786
  if (!i.allowDynamic) return yield* $(`voltro static deploy: "${w(process.cwd(), i.app) || "."}" is NOT pure-static —`), a.counts.ssr > 0 && (yield* $(` • ${a.counts.ssr} ssr page(s): need a runtime; a CDN can't render them`)), a.counts.isr > 0 && (yield* $(` • ${a.counts.isr} isr page(s): need a runtime for revalidation`)), a.dynamicMissing.length > 0 && (yield* $(` • ${a.dynamicMissing.length} dynamic route(s) without getStaticPaths → no artifact built (missing on a CDN): ${a.dynamicMissing.join(", ")}`)), yield* $(" → Serve those on a runtime (`voltro start` / a container), OR add getStaticPaths /"), yield* $(" set the pages to `static` / `spa`. To ship anyway (ssr/isr become client-rendered), pass --allow-dynamic."), 2;
7647
7787
  yield* $(" ⚠ --allow-dynamic: ssr/isr pages will be client-rendered only (no server first-paint).");
7648
7788
  }
7649
- let o = ff(i.host, Gd), s = {
7789
+ let o = gf(i.host, Yd), s = {
7650
7790
  env: process.env,
7651
7791
  options: i.options,
7652
7792
  spaFallback: i.spaFallback,
@@ -7661,7 +7801,7 @@ image, use \`voltro serverless serve\` from the project root.
7661
7801
  if (!c.ok) return yield* $(` ✗ ${o.label}: ${c.message}`), 1;
7662
7802
  let l = c.r.url ? ` → ${c.r.url}` : i.dryRun ? " (dry-run)" : "", u = i.dryRun ? "" : ` (${c.r.filesUploaded} uploaded)`;
7663
7803
  return yield* $(` ✓ deployed ${i.dir} to ${o.label}${u}${l}`), 0;
7664
- }), vf = [
7804
+ }), Sf = [
7665
7805
  {
7666
7806
  name: "list-templates",
7667
7807
  summary: "List available app templates (kind, summary, tags).",
@@ -7732,7 +7872,7 @@ image, use \`voltro serverless serve\` from the project root.
7732
7872
  name: "test",
7733
7873
  summary: "Run vitest against the app at the cwd (auto-discovers *.test.ts/tsx).",
7734
7874
  status: "wired",
7735
- run: yd
7875
+ run: Cd
7736
7876
  },
7737
7877
  {
7738
7878
  name: "cache",
@@ -7759,7 +7899,7 @@ image, use \`voltro serverless serve\` from the project root.
7759
7899
  run: async (e) => {
7760
7900
  let t = e.find((e) => !e.startsWith("-")) ?? process.cwd();
7761
7901
  if (e.includes("--preflight")) return Or(T(t), !0);
7762
- let n = await i(T(t));
7902
+ let n = await m(T(t));
7763
7903
  return n.ran ? n.exitCode : Ee(e);
7764
7904
  }
7765
7905
  },
@@ -7809,13 +7949,13 @@ image, use \`voltro serverless serve\` from the project root.
7809
7949
  name: "serverless",
7810
7950
  summary: "Bundle + deploy standalone *.serverless.ts functions to a function host. Subcommands: list, build, deploy [--target cloudflare|scaleway] [--dry-run].",
7811
7951
  status: "wired",
7812
- run: Qd
7952
+ run: nf
7813
7953
  },
7814
7954
  {
7815
7955
  name: "static",
7816
7956
  summary: "Deploy a pre-built static site (./dist) to a cheap external host. Subcommands: hosts, deploy --host cloudflare-pages|s3|netlify [--spa] [--dry-run].",
7817
7957
  status: "wired",
7818
- run: (e) => r(_f(e), { scope: "voltro:static" })
7958
+ run: (e) => i(xf(e), { scope: "voltro:static" })
7819
7959
  },
7820
7960
  {
7821
7961
  name: "db",
@@ -7857,14 +7997,14 @@ image, use \`voltro serverless serve\` from the project root.
7857
7997
  name: "update",
7858
7998
  summary: "Upgrade the app to the latest framework: bump every @voltro/* dependency (across ALL workspace members, install once at the workspace root), then run the codemods shipped with the target version. Flags: --to <version>, --from <version>, --codemods-only, --dry-run, --force, --exact, --help. After: run `voltro db apply`.",
7859
7999
  status: "wired",
7860
- run: hd
8000
+ run: yd
7861
8001
  },
7862
8002
  {
7863
8003
  name: "_apply-codemods",
7864
8004
  summary: "Internal: apply shipped codemods for a framework version jump (re-exec target of `voltro update`).",
7865
8005
  status: "wired",
7866
8006
  hidden: !0,
7867
- run: _d
8007
+ run: xd
7868
8008
  },
7869
8009
  {
7870
8010
  name: "embeddings",
@@ -7882,7 +8022,7 @@ image, use \`voltro serverless serve\` from the project root.
7882
8022
  name: "agents-md",
7883
8023
  summary: "Write the framework-tailored AGENTS.md into the project root (use --force to overwrite).",
7884
8024
  status: "wired",
7885
- run: ne
8025
+ run: y
7886
8026
  },
7887
8027
  {
7888
8028
  name: "schedule-manifest",
@@ -7936,7 +8076,7 @@ image, use \`voltro serverless serve\` from the project root.
7936
8076
  name: "help",
7937
8077
  summary: "Print this help.",
7938
8078
  status: "wired",
7939
- run: async (e) => (yf(), 0)
8079
+ run: async (e) => (Cf(), 0)
7940
8080
  },
7941
8081
  {
7942
8082
  name: "version",
@@ -7944,18 +8084,18 @@ image, use \`voltro serverless serve\` from the project root.
7944
8084
  status: "wired",
7945
8085
  run: async () => (console.log("framework cli v0.0.0 (scaffold)"), 0)
7946
8086
  }
7947
- ], yf = () => {
7948
- let e = vf.filter((e) => !e.hidden), t = e.reduce((e, t) => Math.max(e, t.name.length), 0);
8087
+ ], Cf = () => {
8088
+ let e = Sf.filter((e) => !e.hidden), t = e.reduce((e, t) => Math.max(e, t.name.length), 0);
7949
8089
  console.log("framework <command> [args]"), console.log(), console.log("Commands:");
7950
8090
  for (let n of e) {
7951
8091
  let e = " ".repeat(t - n.name.length + 2);
7952
8092
  console.log(` ${n.name}${e}${n.summary}`);
7953
8093
  }
7954
- }, bf = async (e) => {
8094
+ }, wf = async (e) => {
7955
8095
  let [t, ...n] = e;
7956
- if (!t) return yf(), 0;
7957
- let r = vf.find((e) => e.name === t);
8096
+ if (!t) return Cf(), 0;
8097
+ let r = Sf.find((e) => e.name === t);
7958
8098
  return r ? r.run(n) : (console.error(`framework: unknown command '${t}'`), console.error("run 'voltro help' for available commands"), 2);
7959
8099
  };
7960
8100
  //#endregion
7961
- export { Ka as i, bf as n, yf as r, vf as t };
8101
+ export { Ka as i, wf as n, Cf as r, Sf as t };