@voltro/web 0.37.0 → 0.39.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.
package/dist/index.d.ts CHANGED
@@ -503,9 +503,47 @@ export declare class LoaderCache {
503
503
 
504
504
  export declare const loaderCacheKey: (pattern: string, params: Readonly<Record<string, string>>) => string;
505
505
 
506
+ /**
507
+ * What a `loader` receives.
508
+ *
509
+ * **It runs on the server AND again in the browser.** A loader is not a
510
+ * server-only hook: it runs during SSR for the first paint, and then runs
511
+ * AGAIN, in the browser, on every in-app navigation to the route. Same
512
+ * function, different environment.
513
+ *
514
+ * That precondition was never written down, and a consumer read "runs once per
515
+ * request" into the gap — which is the reading the wording invites. It cost
516
+ * them two days, and the reason it survived that long is the nastiest part: a
517
+ * client-only failure is invisible to every probe that does not NAVIGATE. A
518
+ * fresh page load, a `curl`, any SSR check goes through the server path and
519
+ * passes. Only clicking a link inside the running app reaches the other one.
520
+ *
521
+ * So anything server-only in a loader must be guarded by {@link isServer}.
522
+ */
506
523
  export declare interface LoaderContext<Procedures = unknown> {
507
524
  readonly params: Readonly<Record<string, string>>;
508
525
  readonly pathname: string;
526
+ /**
527
+ * `true` when this invocation is the SERVER one (SSR under `voltro dev` or
528
+ * `voltro start`), `false` in the browser.
529
+ *
530
+ * Exists because the alternatives are traps. `query` is absent in the browser
531
+ * and so reads as a discriminator, but branching on the ABSENCE OF A FUNCTION
532
+ * says nothing about why it is absent and breaks the moment anything else
533
+ * becomes conditional. `headers` is worse: it is `{}` in the browser, not
534
+ * `undefined`, so `ctx.headers ? …` is TRUE on both paths — a consumer
535
+ * checked exactly that and it silently did nothing.
536
+ *
537
+ * export const loader = async (ctx: LoaderContext) => {
538
+ * if (ctx.isServer) seedStore(prefs, await ctx.query!('prefs.get'))
539
+ * return null
540
+ * }
541
+ *
542
+ * REQUIRED, not optional: an optional boolean is omissible, and a server path
543
+ * that forgot it would read as `undefined` — falsy — and claim to be the
544
+ * browser. Required means the compiler names every construction site.
545
+ */
546
+ readonly isServer: boolean;
509
547
  /**
510
548
  * The raw query string, INCLUDING the leading `?`, or `''` when there is
511
549
  * none. Filled on every path — client navigation, `voltro dev` SSR and
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { t as e } from "./globalContext-d4A-ugDg.js";
2
2
  import { AppClientsContext as t, useAppClient as n } from "./hooks.js";
3
- import { a as r, c as i, i as a, l as o, o as s, r as c, s as l, u } from "./frameworkBoot--fOA34nd.js";
3
+ import { a as r, c as i, i as a, l as o, o as s, r as c, s as l, u } from "./frameworkBoot-Tso-IjA7.js";
4
4
  import { a as d, c as f, i as p, l as m, o as h, u as g } from "./defaultFallbacks-B4Ak-R6R.js";
5
- import { a as _, i as ee, n as te, r as ne, t as re } from "./mount-BKP99m_W.js";
5
+ import { a as _, i as ee, n as te, r as ne, t as re } from "./mount-BTAQ-8Vl.js";
6
6
  import { c as ie, d as ae, h as oe, l as se, p as ce, u as le } from "./routerState-DAT472IC.js";
7
- import { A as ue, B as de, C as fe, D as pe, E as me, F as he, G as ge, H as _e, I as v, K as y, L as b, M as x, N as S, O as C, P as w, R as T, S as E, T as D, U as O, V as k, W as A, _ as j, a as M, b as N, c as P, d as F, f as I, g as ve, h as ye, i as be, j as xe, k as Se, l as Ce, m as we, n as Te, o as Ee, p as De, q as Oe, r as ke, s as Ae, t as je, u as Me, v as Ne, w as Pe, x as Fe, y as Ie, z as Le } from "./serverContext-7toyzR70.js";
7
+ import { A as ue, B as de, C as fe, D as pe, E as me, F as he, G as ge, H as _e, I as v, K as y, L as b, M as x, N as S, O as C, P as w, R as T, S as E, T as D, U as O, V as k, W as A, _ as j, a as M, b as N, c as P, d as F, f as I, g as ve, h as ye, i as be, j as xe, k as Se, l as Ce, m as we, n as Te, o as Ee, p as De, q as Oe, r as ke, s as Ae, t as je, u as Me, v as Ne, w as Pe, x as Fe, y as Ie, z as Le } from "./serverContext-38JTbYPa.js";
8
8
  import { Component as Re, Suspense as ze, createElement as L, use as Be, useCallback as Ve, useContext as He, useSyncExternalStore as Ue } from "react";
9
9
  import { Fragment as R, jsx as z, jsxs as B } from "react/jsx-runtime";
10
10
  export * from "@voltro/client";
@@ -147,11 +147,10 @@ var We = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
147
147
  "aria-current": n ? "page" : void 0
148
148
  }, e.label);
149
149
  return n ? [r] : [r, L("span", { key: `s${t}` }, "/")];
150
- })) : null;
151
- return L("div", { className: "voltro-blog-layout" }, u, L("main", {
150
+ })) : null, f = L("main", {
152
151
  className: "voltro-blog-main",
153
152
  style: Qe
154
- }, d, r), a ? L("footer", {
153
+ }, d, r), p = a ? L("footer", {
155
154
  className: "voltro-blog-footer",
156
155
  style: {
157
156
  maxWidth: "48rem",
@@ -159,7 +158,8 @@ var We = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
159
158
  padding: "2rem 1.5rem",
160
159
  borderTop: "1px solid var(--voltro-blog-border, rgba(127,127,127,0.2))"
161
160
  }
162
- }, a) : null);
161
+ }, a) : null;
162
+ return L("div", { className: "voltro-blog-layout" }, u, f, p);
163
163
  }, G = (e) => {
164
164
  if (e === "" || e === "/") return "/";
165
165
  let t = e.startsWith("/") ? e : `/${e}`;
@@ -1,4 +1,4 @@
1
- import { t as e } from "./frameworkBoot--fOA34nd.js";
1
+ import { t as e } from "./frameworkBoot-Tso-IjA7.js";
2
2
  import { o as t, r as n, t as r } from "./routerState-DAT472IC.js";
3
3
  import { StrictMode as i, createElement as a } from "react";
4
4
  import { createRoot as o, hydrateRoot as s } from "react-dom/client";
@@ -63,19 +63,19 @@ var d = /* @__PURE__ */ new Map(), f = (e, t) => {
63
63
  descriptors: t.descriptors ?? {},
64
64
  wsUrl: t.wsUrl ?? b(e),
65
65
  headers: t.headers
66
- })), m = n(document.getElementById(r)?.textContent, window.location.pathname), h = m !== null && f.children.length > 0, v = document.querySelector("meta[name=\"voltro-interactive\"]")?.getAttribute("content") ?? "full";
67
- if (v === "none") return;
68
- if (v === "islands") {
66
+ })), m = document.getElementById(r), h = n(m?.textContent, window.location.pathname), v = h !== null && f.children.length > 0, x = document.querySelector("meta[name=\"voltro-interactive\"]")?.getAttribute("content") ?? "full";
67
+ if (x === "none") return;
68
+ if (x === "islands") {
69
69
  y(_()), g();
70
70
  return;
71
71
  }
72
- h && t(m), l(m?.storeSeeds), c(m?.preloadSeeds);
73
- let x = /* @__PURE__ */ u(i, { children: /* @__PURE__ */ u(e, {
72
+ v && t(h), l(h?.storeSeeds), c(h?.preloadSeeds);
73
+ let S = /* @__PURE__ */ u(i, { children: /* @__PURE__ */ u(e, {
74
74
  App: a,
75
75
  apis: p,
76
76
  ...d.Devtools ? { Devtools: d.Devtools } : {}
77
77
  }) });
78
- h ? s(f, x) : o(f).render(x);
78
+ v ? s(f, S) : o(f).render(S);
79
79
  };
80
80
  //#endregion
81
81
  export { f as a, g as i, y as n, p as r, x as t };
package/dist/mount.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./mount-BKP99m_W.js";
1
+ import { n as e, t } from "./mount-BTAQ-8Vl.js";
2
2
  export { t as mount, e as noticeIslandsShipTheFullBundle };
@@ -291,7 +291,7 @@ var h = {
291
291
  }
292
292
  return this.props.children;
293
293
  }
294
- }, R = "data-voltro-page-slot", Se = () => /* @__PURE__ */ p("div", { [R]: "" }), Ce = ({ routes: e, notFounds: i = [], notFound: o = n, errorFallback: c = r }) => {
294
+ }, Se = "data-voltro-page-slot", Ce = () => /* @__PURE__ */ p("div", { [Se]: "" }), R = ({ routes: e, notFounds: i = [], notFound: o = n, errorFallback: c = r }) => {
295
295
  let [m, h] = f(() => window.location.pathname), [g, _] = f(() => window.location.pathname), [v, y] = f(() => window.location.search + window.location.hash), b = v.split("#")[0] ?? "", x = d(new de()), S = d(/* @__PURE__ */ new Map()), [ne, w] = f(null), re = s((e, t) => (S.current.set(e, t), () => {
296
296
  S.current.delete(e);
297
297
  }), []), T = d(/* @__PURE__ */ new Map()), E = d(null), D = u(() => ae(e.map(ie)), [e]);
@@ -330,7 +330,7 @@ var h = {
330
330
  n?.replace ? window.history.replaceState(r, "", e) : window.history.pushState(r, "", e), h(t.pathname), y(t.search + t.hash), n?.scroll !== !1 && me(t);
331
331
  }, []), M = s((e, t) => {
332
332
  let n = new URL(window.location.href), r = new URL(e, n);
333
- if (!(r.pathname === n.pathname && r.search === n.search && r.hash === n.hash)) {
333
+ if (r.pathname !== n.pathname || r.search !== n.search || r.hash !== n.hash) {
334
334
  for (let n of S.current.values()) if (n({
335
335
  to: e,
336
336
  ...t ? { opts: t } : {}
@@ -356,7 +356,7 @@ var h = {
356
356
  };
357
357
  return document.addEventListener("click", e), () => document.removeEventListener("click", e);
358
358
  }, [M]);
359
- let N = u(() => O(D, m), [D, m]), P = d(/* @__PURE__ */ new Map()), [F, be] = f(0);
359
+ let N = u(() => O(D, m), [D, m]), P = d(/* @__PURE__ */ new Map()), [F, be] = f(0), Se = N?.route.load ? N.route.pattern : null;
360
360
  l(() => {
361
361
  let e = N?.route;
362
362
  if (!e?.load || P.current.has(e.pattern)) return;
@@ -366,8 +366,8 @@ var h = {
366
366
  }).catch(() => {}), () => {
367
367
  t = !0;
368
368
  };
369
- }, [N?.route.load ? N.route.pattern : null]);
370
- let R = d(/* @__PURE__ */ new Set()), Ce = s((e) => {
369
+ }, [Se]);
370
+ let R = d(/* @__PURE__ */ new Set()), Te = s((e) => {
371
371
  let t = e.load;
372
372
  t && (P.current.has(e.pattern) || R.current.has(e.pattern) || (R.current.add(e.pattern), t().then((t) => {
373
373
  P.current.set(e.pattern, t);
@@ -381,7 +381,7 @@ var h = {
381
381
  let t = P.current.get(e.pattern);
382
382
  return t ? {
383
383
  ...e,
384
- ...Ne(t),
384
+ ...Pe(t),
385
385
  load: void 0
386
386
  } : null;
387
387
  }, [N, F]), B = u(() => O(D, g), [D, g]), V = u(() => {
@@ -391,10 +391,10 @@ var h = {
391
391
  let t = P.current.get(e.pattern);
392
392
  return t ? {
393
393
  ...e,
394
- ...Ne(t),
394
+ ...Pe(t),
395
395
  load: void 0
396
396
  } : null;
397
- }, [B, F]), H = s((e) => {
397
+ }, [B, F]), Oe = s((e) => {
398
398
  let t = new URL(e, window.location.origin), n = O(D, t.pathname);
399
399
  if (!n) return;
400
400
  let r = (e, r) => {
@@ -402,37 +402,38 @@ var h = {
402
402
  params: n.params,
403
403
  pathname: t.pathname,
404
404
  search: t.search,
405
- signal: e
405
+ signal: e,
406
+ isServer: !1
406
407
  }));
407
408
  };
408
- Ce(n.route), n.route.loader && r(k(n.route.pattern, n.params), n.route.loader), n.route.chain?.forEach((e, t) => {
409
+ Te(n.route), n.route.loader && r(k(n.route.pattern, n.params), n.route.loader), n.route.chain?.forEach((e, t) => {
409
410
  e.loader && r(oe(n.route.pattern, t, n.params), e.loader);
410
411
  });
411
- }, [D, Ce]), [U, W] = f({ status: "idle" }), G = d(void 0);
412
- G.current === void 0 && (G.current = a());
413
- let K = d(!1), [De, Oe] = f(() => G.current?.pageClientOnly === !0 && G.current.pathname === m);
412
+ }, [D, Te]), [H, U] = f({ status: "idle" }), W = d(void 0);
413
+ W.current === void 0 && (W.current = a());
414
+ let ke = d(!1), [Ae, je] = f(() => W.current?.pageClientOnly === !0 && W.current.pathname === m);
414
415
  l(() => {
415
- Oe(!1);
416
+ je(!1);
416
417
  }, []);
417
- let [q, ke] = f(() => ({
418
+ let [G, Me] = f(() => ({
418
419
  pathname: m,
419
- data: ge(G.current ?? null, m),
420
+ data: ge(W.current ?? null, m),
420
421
  chain: N?.route.chain
421
422
  }));
422
423
  l(() => {
423
424
  let e = (e) => {
424
- _(m), ke((t) => t.pathname === m && _e(t.data, e) ? t : {
425
+ _(m), Me((t) => t.pathname === m && _e(t.data, e) ? t : {
425
426
  pathname: m,
426
427
  data: e,
427
428
  chain: N?.route.chain
428
429
  });
429
430
  };
430
431
  if (!N) {
431
- W({ status: "idle" }), e(void 0);
432
+ U({ status: "idle" }), e(void 0);
432
433
  return;
433
434
  }
434
435
  if (!z) {
435
- W({
436
+ U({
436
437
  status: "pending",
437
438
  key: k(N.route.pattern, N.params)
438
439
  });
@@ -448,7 +449,8 @@ var h = {
448
449
  params: n,
449
450
  pathname: m,
450
451
  search: b,
451
- signal: t
452
+ signal: t,
453
+ isServer: !1
452
454
  })
453
455
  });
454
456
  }
@@ -462,17 +464,18 @@ var h = {
462
464
  params: n,
463
465
  pathname: m,
464
466
  search: b,
465
- signal: e
467
+ signal: e,
468
+ isServer: !1
466
469
  })
467
470
  });
468
471
  }
469
472
  }), a.length === 0) {
470
- W({ status: "idle" }), e(void 0);
473
+ U({ status: "idle" }), e(void 0);
471
474
  return;
472
475
  }
473
- let o = k(t.pattern, n), s = G.current;
474
- if (s && !K.current && m === s.pathname) {
475
- K.current = !0;
476
+ let o = k(t.pattern, n), s = W.current;
477
+ if (s && !ke.current && m === s.pathname) {
478
+ ke.current = !0;
476
479
  for (let e of a) e.target === "page" ? s.pageRan && x.current.seed(e.cacheKey, s.page) : s.segments.has(e.target) && x.current.seed(e.cacheKey, s.segments.get(e.target));
477
480
  }
478
481
  let c = a.map((e) => ({
@@ -498,18 +501,18 @@ var h = {
498
501
  };
499
502
  }, u = l();
500
503
  if (u) {
501
- u.ok ? (W({
504
+ u.ok ? (U({
502
505
  status: "success",
503
506
  key: o,
504
507
  data: u.data
505
- }), e(u.data)) : (W({
508
+ }), e(u.data)) : (U({
506
509
  status: "error",
507
510
  key: o,
508
511
  error: u.error
509
512
  }), C(u.error) || e(void 0));
510
513
  return;
511
514
  }
512
- W({
515
+ U({
513
516
  status: "pending",
514
517
  key: o
515
518
  }), i && _(m);
@@ -517,11 +520,11 @@ var h = {
517
520
  return Promise.all(f).then(() => {
518
521
  if (d) return;
519
522
  let t = l();
520
- t && (t.ok ? (W({
523
+ t && (t.ok ? (U({
521
524
  status: "success",
522
525
  key: o,
523
526
  data: t.data
524
- }), e(t.data)) : (W({
527
+ }), e(t.data)) : (U({
525
528
  status: "error",
526
529
  key: o,
527
530
  error: t.error
@@ -534,36 +537,36 @@ var h = {
534
537
  z,
535
538
  m
536
539
  ]);
537
- let J = q.data?.page, Y = g === m, Ae = q.pathname === g, je = Ae ? q.data?.page : void 0, Me = V?.loader === void 0 ? I : je, Pe = ve(), X = u(() => {
540
+ let K = G.data?.page, q = g === m, J = G.pathname === g, Ne = J ? G.data?.page : void 0, Fe = V?.loader === void 0 ? I : Ne, Ie = ve(), Y = u(() => {
538
541
  if (!B) return null;
539
- let e = B.params.locale ?? Pe;
540
- return ce(V?.meta, B.params, J, e);
542
+ let e = B.params.locale ?? Ie;
543
+ return ce(V?.meta, B.params, K, e);
541
544
  }, [
542
545
  B,
543
546
  V,
544
- J,
545
- Pe
547
+ K,
548
+ Ie
546
549
  ]);
547
- l(() => ye(X), [X]);
548
- let [Fe, Z] = f(""), Q = d(!1), [Ie, Le] = f(!1);
550
+ l(() => ye(Y), [Y]);
551
+ let [X, Z] = f(""), Q = d(!1), [Le, Re] = f(!1);
549
552
  l(() => {
550
553
  if (!Q.current) {
551
- Q.current = !0, Le(!0);
554
+ Q.current = !0, Re(!0);
552
555
  return;
553
556
  }
554
- let e = X?.title ?? (typeof document < "u" ? document.title : "");
557
+ let e = Y?.title ?? (typeof document < "u" ? document.title : "");
555
558
  Z(e && e.length > 0 ? e : g);
556
- }, [g, X]), l(() => {
559
+ }, [g, Y]), l(() => {
557
560
  let e = E.current;
558
561
  e && (E.current = null, pe(() => window.scrollTo(e.x, e.y)));
559
562
  }, [g]);
560
- let Re = u(() => ({
563
+ let ze = u(() => ({
561
564
  pathname: g,
562
565
  search: v,
563
566
  navigate: M,
564
567
  params: B?.params ?? {},
565
- prefetch: H,
566
- loaderData: J,
568
+ prefetch: Oe,
569
+ loaderData: K,
567
570
  registerBlocker: re,
568
571
  blocked: ne
569
572
  }), [
@@ -571,8 +574,8 @@ var h = {
571
574
  v,
572
575
  M,
573
576
  B,
574
- H,
575
- J,
577
+ Oe,
578
+ K,
576
579
  re,
577
580
  ne
578
581
  ]);
@@ -591,47 +594,47 @@ var h = {
591
594
  ee({
592
595
  pathname: g,
593
596
  params: B?.params ?? {},
594
- loaderData: J
597
+ loaderData: K
595
598
  });
596
599
  }, [
597
600
  g,
598
601
  B,
599
- J
602
+ K
600
603
  ]), l(() => {
601
- if (U.status === "pending") return t({
604
+ if (H.status === "pending") return t({
602
605
  id: "voltro:router:loader",
603
606
  kind: "loading",
604
607
  label: "Loading…"
605
608
  });
606
- }, [U.status]), l(() => {
607
- if (U.status === "error") return t({
609
+ }, [H.status]), l(() => {
610
+ if (H.status === "error") return t({
608
611
  id: "voltro:router:loader-error",
609
612
  kind: "error",
610
613
  label: "Loader failed"
611
614
  });
612
- }, [U.status]), l(() => {
613
- U.status === "error" && C(U.error) && M(U.error.location, { replace: !0 });
614
- }, [U, M]);
615
- let ze = () => {
615
+ }, [H.status]), l(() => {
616
+ H.status === "error" && C(H.error) && M(H.error.location, { replace: !0 });
617
+ }, [H, M]);
618
+ let Be = () => {
616
619
  let e = le(i, g);
617
620
  if (e) {
618
621
  let { Component: t, chain: n } = e;
619
- return Ee(n, /* @__PURE__ */ p(t, {}), g, c);
622
+ return De(n, /* @__PURE__ */ p(t, {}), g, c);
620
623
  }
621
624
  return /* @__PURE__ */ p(o, {});
622
- }, Be = Y && B && U.status === "error" ? U.error : void 0, $;
623
- if (!B) $ = ze();
624
- else if (te(Be)) $ = ze();
625
+ }, Ve = q && B && H.status === "error" ? H.error : void 0, $;
626
+ if (!B) $ = Be();
627
+ else if (te(Ve)) $ = Be();
625
628
  else {
626
629
  let e = B.route.chain, t = e ? e.length - 1 : -1, n = V?.ErrorBoundary ?? ue(e, "Error", t) ?? c, r;
627
- if (De) r = /* @__PURE__ */ p(Se, {});
628
- else if (Y && U.status === "error" && !C(U.error)) r = /* @__PURE__ */ p(n, {
629
- error: U.error,
630
+ if (Ae) r = /* @__PURE__ */ p(Ce, {});
631
+ else if (q && H.status === "error" && !C(H.error)) r = /* @__PURE__ */ p(n, {
632
+ error: H.error,
630
633
  reset: () => {
631
- x.current.invalidate(B.route.pattern), h((e) => e), W({ status: "idle" });
634
+ x.current.invalidate(B.route.pattern), h((e) => e), U({ status: "idle" });
632
635
  }
633
636
  });
634
- else if (Y && U.status === "pending") {
637
+ else if (q && H.status === "pending") {
635
638
  let i = V?.Pending ?? ue(e, "Pending", t);
636
639
  if (i) r = /* @__PURE__ */ p(i, {});
637
640
  else if (V?.Component) {
@@ -640,7 +643,7 @@ var h = {
640
643
  resetKey: g,
641
644
  Fallback: n,
642
645
  children: /* @__PURE__ */ p(L.Provider, {
643
- value: Me,
646
+ value: Fe,
644
647
  children: /* @__PURE__ */ p(e, {})
645
648
  })
646
649
  });
@@ -651,30 +654,30 @@ var h = {
651
654
  resetKey: g,
652
655
  Fallback: n,
653
656
  children: /* @__PURE__ */ p(L.Provider, {
654
- value: Me,
657
+ value: Fe,
655
658
  children: /* @__PURE__ */ p(e, {})
656
659
  })
657
660
  }) : null;
658
661
  }
659
- $ = Ee(e, r, g, c, (t) => {
660
- if (Ae) return q.data?.segments[t];
662
+ $ = De(e, r, g, c, (t) => {
663
+ if (J) return G.data?.segments[t];
661
664
  let n = e?.[t]?.Layout;
662
- return n !== void 0 && n === q.chain?.[t]?.Layout ? q.data?.segments[t] : void 0;
665
+ return n !== void 0 && n === G.chain?.[t]?.Layout ? G.data?.segments[t] : void 0;
663
666
  });
664
667
  }
665
668
  return /* @__PURE__ */ p(we, {
666
- value: Re,
667
- announcer: Ie ? /* @__PURE__ */ p(Te, { message: Fe }) : null,
669
+ value: ze,
670
+ announcer: Le ? /* @__PURE__ */ p(Ee, { message: X }) : null,
668
671
  children: $
669
672
  });
670
673
  }, we = ({ value: e, announcer: t, children: n }) => /* @__PURE__ */ m(P.Provider, {
671
674
  value: e,
672
675
  children: [n, t]
673
- }), z = (e, t) => /* @__PURE__ */ p(we, {
676
+ }), Te = (e, t) => /* @__PURE__ */ p(we, {
674
677
  value: e,
675
678
  announcer: null,
676
679
  children: t
677
- }), B = {
680
+ }), z = {
678
681
  position: "absolute",
679
682
  width: 1,
680
683
  height: 1,
@@ -684,14 +687,14 @@ var h = {
684
687
  clip: "rect(0, 0, 0, 0)",
685
688
  whiteSpace: "nowrap",
686
689
  border: 0
687
- }, Te = ({ message: e }) => /* @__PURE__ */ p("div", {
690
+ }, Ee = ({ message: e }) => /* @__PURE__ */ p("div", {
688
691
  "data-vweb-route-announcer": "",
689
692
  role: "status",
690
693
  "aria-live": "assertive",
691
694
  "aria-atomic": "true",
692
- style: B,
695
+ style: z,
693
696
  children: e
694
- }), Ee = (e, t, n, r, i) => {
697
+ }), De = (e, t, n, r, i) => {
695
698
  if (!e || e.length === 0) return t;
696
699
  let a = t;
697
700
  for (let t = e.length - 1; t >= 0; t--) {
@@ -713,14 +716,14 @@ var h = {
713
716
  }
714
717
  }
715
718
  return a;
716
- }, V = () => F().pathname, H = () => F().navigate, U = () => F().params, W = () => {
719
+ }, B = () => F().pathname, V = () => F().navigate, Oe = () => F().params, H = () => {
717
720
  let e = c(L);
718
721
  if (e === I) throw Error("useLoaderData() was called at a level that declares no `loader`. Export `loader` from this page/layout, or — if this component is shared between routes that have one and routes that do not — read it with `useOptionalLoaderData()`, which returns `undefined` here instead of throwing. The React component stack below names the component.");
719
722
  return e;
720
- }, G = () => {
723
+ }, U = () => {
721
724
  let e = c(L);
722
725
  return e === I ? void 0 : e;
723
- }, K = () => F().prefetch, De = (e) => {
726
+ }, W = () => F().prefetch, ke = (e) => {
724
727
  let { registerBlocker: t, blocked: n } = F(), r = d(Symbol("voltro:blocker")), i = d(e);
725
728
  return i.current = e, l(() => {
726
729
  let e = r.current;
@@ -734,22 +737,22 @@ var h = {
734
737
  retry: n.retry,
735
738
  reset: n.reset
736
739
  } : { blocked: !1 };
737
- }, Oe = (e) => e instanceof URLSearchParams ? e : new URLSearchParams(e), q = () => {
740
+ }, Ae = (e) => e instanceof URLSearchParams ? e : new URLSearchParams(e), je = () => {
738
741
  let { navigate: e } = F();
739
742
  return s((t, n) => {
740
- let r = new URLSearchParams(typeof window < "u" ? window.location.search : ""), i = Oe(typeof t == "function" ? t(r) : t).toString(), a = typeof window < "u" ? window.location.pathname : "/", o = i ? `${a}?${i}` : a;
743
+ let r = new URLSearchParams(typeof window < "u" ? window.location.search : ""), i = Ae(typeof t == "function" ? t(r) : t).toString(), a = typeof window < "u" ? window.location.pathname : "/", o = i ? `${a}?${i}` : a;
741
744
  e(o, {
742
745
  replace: !n?.push,
743
746
  ...n?.scroll === void 0 ? {} : { scroll: n.scroll }
744
747
  });
745
748
  }, [e]);
746
- }, ke = (e) => e, J = (e, t) => {
749
+ }, G = (e) => e, Me = (e, t) => {
747
750
  let n = new URLSearchParams();
748
751
  for (let [e, r] of Object.entries(t)) r !== void 0 && n.append(e, String(r));
749
752
  let r = n.toString();
750
753
  return r ? `${e}?${r}` : e;
751
- }, Y = (e, t) => `${e}#${t.replace(/^#/, "")}`, Ae = (e) => /^([a-z][a-z0-9+.-]*:|\/\/|#)/i.test(e), je = ({ to: e, children: t, onClick: n, prefetch: r = !1, replace: i = !1, onMouseEnter: a, onFocus: o, ref: c, ...u }) => {
752
- let f = H(), m = K(), h = Ae(e), g = d(null), ee = s((e) => {
754
+ }, K = (e, t) => `${e}#${t.replace(/^#/, "")}`, q = (e) => /^([a-z][a-z0-9+.-]*:|\/\/|#)/i.test(e), J = ({ to: e, children: t, onClick: n, prefetch: r = !1, replace: i = !1, onMouseEnter: a, onFocus: o, ref: c, ...u }) => {
755
+ let f = V(), m = W(), h = q(e), g = d(null), ee = s((e) => {
753
756
  g.current = e, typeof c == "function" ? c(e) : c && (c.current = e);
754
757
  }, [c]);
755
758
  l(() => {
@@ -782,10 +785,10 @@ var h = {
782
785
  onFocus: y,
783
786
  children: t
784
787
  });
785
- }, Me = (e) => je({
788
+ }, Ne = (e) => J({
786
789
  ...e,
787
790
  to: e.to
788
- }), Ne = (e) => ({
791
+ }), Pe = (e) => ({
789
792
  Component: e.default,
790
793
  meta: e.meta,
791
794
  loader: e.loader,
@@ -793,27 +796,27 @@ var h = {
793
796
  Pending: e.Pending,
794
797
  renderMode: e.renderMode,
795
798
  interactive: e.interactive
796
- }), Pe = (e, t, n = {}) => ({
799
+ }), Fe = (e, t, n = {}) => ({
797
800
  pattern: e,
798
- ...Ne(t),
801
+ ...Pe(t),
799
802
  chain: n.chain
800
- }), X = (e, t, n = {}) => ({
803
+ }), Ie = (e, t, n = {}) => ({
801
804
  pattern: e,
802
805
  load: t,
803
806
  chain: n.chain
804
- }), Fe = async (e, t) => {
807
+ }), Y = async (e, t) => {
805
808
  let n = O(e.map(ie), t)?.route.load;
806
809
  if (n) try {
807
810
  await n();
808
811
  } catch {}
809
- }, Z = e("serverRequest", null), Q = () => c(Z), Ie = () => {
810
- let e = Q();
812
+ }, X = e("serverRequest", null), Z = () => c(X), Q = () => {
813
+ let e = Z();
811
814
  if (e !== null) {
812
815
  let t = e.url.indexOf("?");
813
816
  return new URLSearchParams(t >= 0 ? e.url.slice(t + 1) : "");
814
817
  }
815
818
  return typeof window < "u" ? new URLSearchParams(window.location.search) : new URLSearchParams();
816
- }, Le = ({ value: e, children: t }) => /* @__PURE__ */ p(Z.Provider, {
819
+ }, Le = ({ value: e, children: t }) => /* @__PURE__ */ p(X.Provider, {
817
820
  value: e,
818
821
  children: t
819
822
  }), Re = (e) => {
@@ -834,4 +837,4 @@ var h = {
834
837
  return t;
835
838
  };
836
839
  //#endregion
837
- export { W as A, b as B, Fe as C, oe as D, ce as E, K as F, _ as G, C as H, q as I, ee as K, Y as L, H as M, G as N, ae as O, U as P, J as R, Pe as S, fe as T, x as U, te as V, S as W, ke as _, Q as a, k as b, L as c, Se as d, Me as f, ie as g, P as h, Ie as i, V as j, De as k, I as l, Ce as m, Le as n, je as o, ne as p, v as q, Re as r, de as s, Z as t, R as u, le as v, z as w, O as x, X as y, y as z };
840
+ export { H as A, b as B, Y as C, oe as D, ce as E, W as F, _ as G, C as H, je as I, ee as K, K as L, V as M, U as N, ae as O, Oe as P, Me as R, Fe as S, fe as T, x as U, te as V, S as W, G as _, Z as a, k as b, L as c, Ce as d, Ne as f, ie as g, P as h, Q as i, B as j, ke as k, I as l, R as m, Le as n, J as o, ne as p, v as q, Re as r, de as s, X as t, Se as u, le as v, Te as w, O as x, Ie as y, y as z };
package/dist/ssr.d.ts CHANGED
@@ -6,6 +6,7 @@ import { ProcedureOutput } from '@voltro/client';
6
6
  import { ProcedureTypeMap } from '@voltro/client';
7
7
  import { ReactNode } from 'react';
8
8
  import { seedPreloadedSubscription } from '@voltro/client';
9
+ import { seedPreloadedSubscriptionFailure } from '@voltro/client';
9
10
  import { StoreSeed } from '@voltro/client';
10
11
  import { StoreSeedBag } from '@voltro/client';
11
12
 
@@ -210,9 +211,47 @@ declare type InteractiveMode = 'full' | 'islands' | 'none';
210
211
  /** True for exactly the object {@link defer} returns. */
211
212
  export declare const isDeferredLoaderResult: (value: unknown) => value is DeferredLoaderResult;
212
213
 
214
+ /**
215
+ * What a `loader` receives.
216
+ *
217
+ * **It runs on the server AND again in the browser.** A loader is not a
218
+ * server-only hook: it runs during SSR for the first paint, and then runs
219
+ * AGAIN, in the browser, on every in-app navigation to the route. Same
220
+ * function, different environment.
221
+ *
222
+ * That precondition was never written down, and a consumer read "runs once per
223
+ * request" into the gap — which is the reading the wording invites. It cost
224
+ * them two days, and the reason it survived that long is the nastiest part: a
225
+ * client-only failure is invisible to every probe that does not NAVIGATE. A
226
+ * fresh page load, a `curl`, any SSR check goes through the server path and
227
+ * passes. Only clicking a link inside the running app reaches the other one.
228
+ *
229
+ * So anything server-only in a loader must be guarded by {@link isServer}.
230
+ */
213
231
  declare interface LoaderContext<Procedures = unknown> {
214
232
  readonly params: Readonly<Record<string, string>>;
215
233
  readonly pathname: string;
234
+ /**
235
+ * `true` when this invocation is the SERVER one (SSR under `voltro dev` or
236
+ * `voltro start`), `false` in the browser.
237
+ *
238
+ * Exists because the alternatives are traps. `query` is absent in the browser
239
+ * and so reads as a discriminator, but branching on the ABSENCE OF A FUNCTION
240
+ * says nothing about why it is absent and breaks the moment anything else
241
+ * becomes conditional. `headers` is worse: it is `{}` in the browser, not
242
+ * `undefined`, so `ctx.headers ? …` is TRUE on both paths — a consumer
243
+ * checked exactly that and it silently did nothing.
244
+ *
245
+ * export const loader = async (ctx: LoaderContext) => {
246
+ * if (ctx.isServer) seedStore(prefs, await ctx.query!('prefs.get'))
247
+ * return null
248
+ * }
249
+ *
250
+ * REQUIRED, not optional: an optional boolean is omissible, and a server path
251
+ * that forgot it would read as `undefined` — falsy — and claim to be the
252
+ * browser. Required means the compiler names every construction site.
253
+ */
254
+ readonly isServer: boolean;
216
255
  /**
217
256
  * The raw query string, INCLUDING the leading `?`, or `''` when there is
218
257
  * none. Filled on every path — client navigation, `voltro dev` SSR and
@@ -676,6 +715,8 @@ declare interface RouteSegment {
676
715
 
677
716
  export { seedPreloadedSubscription }
678
717
 
718
+ export { seedPreloadedSubscriptionFailure }
719
+
679
720
  /**
680
721
  * Serialise arbitrary data for inlining into an HTML `<script>` body.
681
722
  *