@voltro/web 0.28.0 → 0.30.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
@@ -5,6 +5,7 @@ import { MouseEvent as MouseEvent_2 } from 'react';
5
5
  import { ReactElement } from 'react';
6
6
  import { ReactNode } from 'react';
7
7
  import { Ref } from 'react';
8
+ import { ResolvableHeaders } from '@voltro/client';
8
9
  import { Rpc } from '@effect/rpc';
9
10
  import { RpcGroup } from '@effect/rpc';
10
11
 
@@ -135,6 +136,12 @@ export declare interface BrowserConsoleBridgeOptions {
135
136
  readonly flushAfterMs?: number;
136
137
  }
137
138
 
139
+ /** Absolute canonical URL for a page. Relative paths are also valid in a
140
+ * `<link rel="canonical">` (crawlers resolve them against the document base),
141
+ * but an absolute one is unambiguous across environments — pass the production
142
+ * `siteUrl` and the page's root-relative `path`. */
143
+ export declare const canonicalUrl: (siteUrl: string, path: string) => string;
144
+
138
145
  /**
139
146
  * Structured result of running a route's loaders: the page (leaf) loader's
140
147
  * data plus each chain segment's loader data, keyed by segment index.
@@ -425,6 +432,10 @@ export declare const isNotFound: (e: unknown) => e is NotFoundError;
425
432
  /** Brand guard — true for any `RedirectError`, even cross-bundle. */
426
433
  export declare const isRedirect: (e: unknown) => e is RedirectError;
427
434
 
435
+ /** Join an origin (`https://example.com`, optionally with a trailing slash) to a
436
+ * root-relative path, collapsing the slash seam so exactly one separates them. */
437
+ export declare const joinUrl: (siteUrl: string, path: string) => string;
438
+
428
439
  /** Lazy variant of `pageRoute`: takes a thunk that dynamically imports the page
429
440
  * instead of an already-imported module. The chunk loads only when the route
430
441
  * first matches — the initial bundle no longer pulls in every page. */
@@ -560,6 +571,11 @@ declare type LoaderEntry<T = unknown> = {
560
571
 
561
572
  export declare type LoaderFn<T = unknown> = (ctx: LoaderContext) => Promise<T> | T;
562
573
 
574
+ /** The URL path a logical page takes for a given locale under URL-prefix
575
+ * routing: the default locale keeps the bare path, every other locale gets a
576
+ * `/<locale>` prefix. `/about` @ `de` → `/de/about`; @ the default → `/about`. */
577
+ export declare const localizedPath: (path: string, locale: string, defaultLocale: string) => string;
578
+
563
579
  export declare const matchRoute: (compiled: ReadonlyArray<CompiledRoute>, pathname: string) => RouteMatch | null;
564
580
 
565
581
  /**
@@ -628,6 +644,12 @@ export declare interface NavigateOptions {
628
644
 
629
645
  export declare const NavigationIndicator: () => ReactNode;
630
646
 
647
+ /* Excluded from this release type: NO_LOADER_DATA */
648
+
649
+ /** Strip a trailing slash from a path, except the root, and guarantee a leading
650
+ * slash. `''` and `'/'` both normalise to `'/'`. */
651
+ export declare const normalizePath: (path: string) => string;
652
+
631
653
  /** Construct-and-throw sugar for `NotFoundError`. Typed `: never` so
632
654
  * control-flow narrows at the call site (`const x = row ?? notFound()`). */
633
655
  export declare const notFound: (detail?: string) => never;
@@ -737,6 +759,11 @@ export declare interface PageMeta {
737
759
  * JSON-serialisable is accepted; `@context` + `@type` are
738
760
  * authored, not synthesised. */
739
761
  readonly jsonLd?: ReadonlyArray<Record<string, unknown>>;
762
+ /** Keep this page out of the index. Emits
763
+ * `<meta name="robots" content="noindex, nofollow">`, and the build's
764
+ * `sitemap.xml` generator EXCLUDES the route. Use for utility pages
765
+ * (checkout steps, previews, thank-you pages) that should not rank. */
766
+ readonly noIndex?: boolean;
740
767
  }
741
768
 
742
769
  export declare const pageRoute: (pattern: string, mod: Record<string, unknown>, extras?: {
@@ -852,16 +879,6 @@ export declare const RENDER_MODES: readonly ["static", "spa", "ssr", "isr"];
852
879
  */
853
880
  export declare type RenderMode = (typeof RENDER_MODES)[number];
854
881
 
855
- /**
856
- * Auth headers for an api's WS rpc client. @effect/rpc sends these in the
857
- * per-request MESSAGE frame (NOT the WS upgrade — a browser can't set upgrade
858
- * headers), so the server's AuthMiddleware sees them on every call; this is
859
- * what lets a cross-origin api authenticate WS subscriptions. A thunk is
860
- * resolved fresh on every (re)connect, so a rotating token (e.g. a Supabase
861
- * access token) is pulled anew per connect instead of freezing at first mount.
862
- */
863
- declare type ResolvableHeaders = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
864
-
865
882
  /**
866
883
  * Decide whether a click on (or inside) an anchor should be handled by the
867
884
  * SPA router or left to the browser. Pure — no side effects, no
@@ -991,6 +1008,56 @@ export declare type SearchParamsInit = URLSearchParams | Readonly<Record<string,
991
1008
  */
992
1009
  export declare const segmentLoaderKey: (pattern: string, index: number, params: Readonly<Record<string, string>>) => string;
993
1010
 
1011
+ /**
1012
+ * Compute the canonical URL + the full set of `hreflang` alternates for a
1013
+ * localized page. Spread the result straight into a `PageMeta`:
1014
+ *
1015
+ * export const meta = ({ locale }) => ({
1016
+ * title: t('about.title'),
1017
+ * ...seoAlternates({
1018
+ * siteUrl: 'https://example.com', path: '/about',
1019
+ * locale, locales: ['en', 'de'], defaultLocale: 'en',
1020
+ * }),
1021
+ * })
1022
+ *
1023
+ * `canonical` is the current locale's absolute URL; `links` carries one
1024
+ * `rel="alternate"` per locale (each an ABSOLUTE URL — Google ignores relative
1025
+ * hreflang hrefs) plus the `x-default`. Every page in a locale set should point
1026
+ * its alternates at the SAME set of URLs, including a self-referential one, per
1027
+ * Google's reciprocity rule — which is exactly what iterating `locales` here
1028
+ * produces.
1029
+ */
1030
+ export declare const seoAlternates: (input: SeoAlternatesInput) => {
1031
+ readonly canonical: string;
1032
+ readonly links: ReadonlyArray<SeoLink>;
1033
+ };
1034
+
1035
+ export declare interface SeoAlternatesInput {
1036
+ /** Production origin, e.g. `'https://voltro.dev'`. Absolute URLs are required
1037
+ * for `hreflang` alternates to be valid, so this is not optional. */
1038
+ readonly siteUrl: string;
1039
+ /** The page's LOGICAL path WITHOUT any locale prefix — the path as the default
1040
+ * locale serves it (`/plugins/foo`, `/about`, `/`). The current locale's
1041
+ * prefix is applied by this function, not by the caller. */
1042
+ readonly path: string;
1043
+ /** The locale THIS render is for — drives which URL is `canonical`. */
1044
+ readonly locale: string;
1045
+ /** Every locale the site publishes this page under. */
1046
+ readonly locales: ReadonlyArray<string>;
1047
+ /** The locale served at the bare (un-prefixed) path. */
1048
+ readonly defaultLocale: string;
1049
+ /** Emit an `hreflang="x-default"` alternate pointing at the default-locale URL
1050
+ * (the recommended fallback for an unmatched language). Default `true`. */
1051
+ readonly xDefault?: boolean;
1052
+ }
1053
+
1054
+ /** A `<link>` descriptor, structurally the entries `PageMeta.links` accepts. */
1055
+ export declare interface SeoLink {
1056
+ readonly rel: string;
1057
+ readonly href: string;
1058
+ readonly hreflang?: string;
1059
+ }
1060
+
994
1061
  export declare interface ServerLogRelay {
995
1062
  readonly dispose: () => void;
996
1063
  }
@@ -1142,6 +1209,23 @@ export declare const useLocation: () => string;
1142
1209
 
1143
1210
  export declare const useNavigate: () => RouterContextValue["navigate"];
1144
1211
 
1212
+ /**
1213
+ * `useLoaderData()` for a component that may render at a level with no
1214
+ * `loader` — returns `undefined` there instead of throwing.
1215
+ *
1216
+ * This exists for ONE case, and it is worth stating so it is not reached for
1217
+ * casually: a component genuinely shared between routes that declare a loader
1218
+ * and routes that do not. On a page that declares its own `loader`, the router
1219
+ * never renders it without its data, so reaching for the optional read there
1220
+ * only hides a real mistake behind a `?.`.
1221
+ *
1222
+ * Note what it does NOT mean: an empty RESULT is a value, not an absence. A
1223
+ * loader returning `{ items: [] }` returns exactly that through both hooks —
1224
+ * `undefined` here says "there is no loader at this level", never "the query
1225
+ * found nothing".
1226
+ */
1227
+ export declare const useOptionalLoaderData: <T>() => LoaderData<T> | undefined;
1228
+
1145
1229
  export declare const useParams: <T extends Record<string, string> = Record<string, string>>() => T;
1146
1230
 
1147
1231
  /** Imperatively pre-warm a route's loader. No-op if the route has no loader. */
package/dist/index.js CHANGED
@@ -1,29 +1,29 @@
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-DHdoAwd9.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-BkBY6M2s.js";
4
4
  import { a as d, c as f, i as p, l as m, o as h, s as g } from "./defaultFallbacks-Fik_Gcjh.js";
5
- import { i as _, n as ee, r as te, t as ne } from "./mount-DfyG67bY.js";
6
- import { c as re, d as ie, h as ae, l as oe, p as se, u as ce } from "./routerState-DDDBYFrI.js";
7
- import { A as le, B as ue, C as de, D as fe, E as pe, F as me, H as v, I 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 L, h as R, i as z, j as he, k as ge, l as _e, m as ve, n as ye, o as be, p as xe, r as Se, s as Ce, t as we, u as Te, v as Ee, w as De, x as Oe, y as ke, z as Ae } from "./serverContext-BASpdhXG.js";
8
- import { Component as je, Suspense as Me, createElement as B, use as Ne, useCallback as Pe, useContext as Fe, useSyncExternalStore as Ie } from "react";
9
- import { Fragment as V, jsx as H, jsxs as U } from "react/jsx-runtime";
5
+ import { i as _, n as ee, r as te, t as ne } from "./mount-fDzFaIuf.js";
6
+ import { c as re, d as ie, h as ae, l as oe, p as se, u as ce } from "./routerState-DAT472IC.js";
7
+ import { A as le, B as ue, C as de, D as fe, E as pe, F as me, G as he, H as ge, I as _e, K as v, L as y, M as b, N as x, O as S, P as C, R as w, S as T, T as E, U as D, V as O, W as k, _ as A, a as j, b as M, c as N, d as P, f as F, g as I, h as ve, i as ye, j as be, k as xe, l as Se, m as Ce, n as we, o as Te, p as Ee, r as De, s as Oe, t as ke, u as L, v as Ae, w as je, x as Me, y as Ne, z as Pe } from "./serverContext-CV1ieZKF.js";
8
+ import { Component as Fe, Suspense as Ie, createElement as R, use as Le, useCallback as Re, useContext as ze, useSyncExternalStore as Be } from "react";
9
+ import { Fragment as z, jsx as B, jsxs as V } from "react/jsx-runtime";
10
10
  export * from "@voltro/client";
11
11
  export * from "@voltro/ui";
12
12
  //#region src/await.tsx
13
- var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PURE__ */ H(Me, {
13
+ var Ve = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PURE__ */ B(Ie, {
14
14
  fallback: t,
15
- children: /* @__PURE__ */ H(ze, {
15
+ children: /* @__PURE__ */ B(Ue, {
16
16
  errorFallback: r,
17
- children: /* @__PURE__ */ H(Re, {
17
+ children: /* @__PURE__ */ B(He, {
18
18
  value: e,
19
19
  errorFallback: r ?? null,
20
20
  children: n
21
21
  })
22
22
  })
23
- }), W = ({ body: e }) => /* @__PURE__ */ H("script", { dangerouslySetInnerHTML: { __html: e } }), Re = ({ value: e, errorFallback: t, children: n }) => {
24
- let r = Ne(e), i = ce(e), a = ae(r);
25
- return a === void 0 ? /* @__PURE__ */ U(V, { children: [i === void 0 ? null : /* @__PURE__ */ H(W, { body: ie(i, r) }), n(r)] }) : /* @__PURE__ */ U(V, { children: [i === void 0 ? null : /* @__PURE__ */ H(W, { body: oe(i, a) }), t] });
26
- }, ze = class extends je {
23
+ }), H = ({ body: e }) => /* @__PURE__ */ B("script", { dangerouslySetInnerHTML: { __html: e } }), He = ({ value: e, errorFallback: t, children: n }) => {
24
+ let r = Le(e), i = ce(e), a = ae(r);
25
+ return a === void 0 ? /* @__PURE__ */ V(z, { children: [i === void 0 ? null : /* @__PURE__ */ B(H, { body: ie(i, r) }), n(r)] }) : /* @__PURE__ */ V(z, { children: [i === void 0 ? null : /* @__PURE__ */ B(H, { body: oe(i, a) }), t] });
26
+ }, Ue = class extends Fe {
27
27
  state = { error: void 0 };
28
28
  static getDerivedStateFromError(e) {
29
29
  return { error: e };
@@ -31,7 +31,7 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
31
31
  render() {
32
32
  return this.state.error === void 0 ? this.props.children : this.props.errorFallback ?? null;
33
33
  }
34
- }, G = ({ src: e }) => e, K = e("imageConfig", { loader: G }), Be = ({ loader: e, children: t }) => B(K.Provider, { value: { loader: e } }, t), q = [
34
+ }, U = ({ src: e }) => e, W = e("imageConfig", { loader: U }), We = ({ loader: e, children: t }) => R(W.Provider, { value: { loader: e } }, t), G = [
35
35
  640,
36
36
  750,
37
37
  828,
@@ -40,8 +40,8 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
40
40
  1920,
41
41
  2048,
42
42
  3840
43
- ], Ve = (e, t, n, r) => {
44
- let i = n !== void 0 && !r ? Array.from(new Set(q.filter((e) => e <= n * 2).concat(n))).sort((e, t) => e - t) : [...q];
43
+ ], Ge = (e, t, n, r) => {
44
+ let i = n !== void 0 && !r ? Array.from(new Set(G.filter((e) => e <= n * 2).concat(n))).sort((e, t) => e - t) : [...G];
45
45
  return {
46
46
  srcSet: i.map((n) => `${e({
47
47
  src: t,
@@ -52,10 +52,10 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
52
52
  width: i[i.length - 1]
53
53
  })
54
54
  };
55
- }, He = ({ src: e, alt: t, width: n, height: r, fill: i = !1, sizes: a, priority: o = !1, loader: s, placeholder: c = "empty", blurDataURL: l, style: u, ...d }) => {
56
- let f = Fe(K).loader, p = s ?? f;
55
+ }, Ke = ({ src: e, alt: t, width: n, height: r, fill: i = !1, sizes: a, priority: o = !1, loader: s, placeholder: c = "empty", blurDataURL: l, style: u, ...d }) => {
56
+ let f = ze(W).loader, p = s ?? f;
57
57
  !i && (n === void 0 || r === void 0) && typeof console < "u" && console.warn(`<Image src="${e}"> needs both width and height (or fill) to reserve layout space and avoid CLS.`);
58
- let { srcSet: m, fallback: h } = Ve(p, e, n, i), g = c === "blur" && l ? {
58
+ let { srcSet: m, fallback: h } = Ge(p, e, n, i), g = c === "blur" && l ? {
59
59
  backgroundImage: `url(${l})`,
60
60
  backgroundSize: "cover",
61
61
  backgroundPosition: "center"
@@ -66,7 +66,7 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
66
66
  width: "100%",
67
67
  objectFit: "cover"
68
68
  } : {};
69
- return B("img", {
69
+ return R("img", {
70
70
  src: h,
71
71
  srcSet: m,
72
72
  sizes: a ?? (i ? "100vw" : void 0),
@@ -85,10 +85,10 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
85
85
  },
86
86
  ...d
87
87
  });
88
- }, Ue = ({ to: e, children: t, className: n }) => B("a", {
88
+ }, qe = ({ to: e, children: t, className: n }) => R("a", {
89
89
  href: e,
90
90
  className: n
91
- }, t), We = {
91
+ }, t), Je = {
92
92
  position: "sticky",
93
93
  top: 0,
94
94
  zIndex: 10,
@@ -99,13 +99,13 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
99
99
  padding: "1rem 1.5rem",
100
100
  borderBottom: "1px solid var(--voltro-blog-border, rgba(127,127,127,0.2))",
101
101
  backdropFilter: "blur(8px)"
102
- }, Ge = {
102
+ }, Ye = {
103
103
  maxWidth: "48rem",
104
104
  margin: "0 auto",
105
105
  padding: "2rem 1.5rem",
106
106
  width: "100%"
107
- }, Ke = (e) => {
108
- let { brand: t, topNav: n, children: r, pathname: i, footer: a, breadcrumbs: o, headerRight: s } = e, c = e.Link ?? Ue, l = B("nav", {
107
+ }, Xe = (e) => {
108
+ let { brand: t, topNav: n, children: r, pathname: i, footer: a, breadcrumbs: o, headerRight: s } = e, c = e.Link ?? qe, l = R("nav", {
109
109
  className: "voltro-blog-nav",
110
110
  style: {
111
111
  display: "flex",
@@ -114,20 +114,20 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
114
114
  }
115
115
  }, ...n.map((e) => {
116
116
  let t = i === e.href;
117
- return B(c, {
117
+ return R(c, {
118
118
  key: e.href,
119
119
  to: e.href,
120
120
  className: t ? "voltro-blog-nav-link is-active" : "voltro-blog-nav-link",
121
121
  children: e.label
122
122
  });
123
- })), u = B("header", {
123
+ })), u = R("header", {
124
124
  className: "voltro-blog-header",
125
- style: We
126
- }, B("div", { className: "voltro-blog-brand" }, t), s ? B("div", { style: {
125
+ style: Je
126
+ }, R("div", { className: "voltro-blog-brand" }, t), s ? R("div", { style: {
127
127
  display: "flex",
128
128
  gap: "1rem",
129
129
  alignItems: "center"
130
- } }, l, s) : l), d = o && o.length > 0 ? B("nav", {
130
+ } }, l, s) : l), d = o && o.length > 0 ? R("nav", {
131
131
  className: "voltro-blog-breadcrumbs",
132
132
  "aria-label": "Breadcrumb",
133
133
  style: {
@@ -138,20 +138,20 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
138
138
  gap: "0.5rem"
139
139
  }
140
140
  }, ...o.flatMap((e, t) => {
141
- let n = t === o.length - 1, r = e.href && !n ? B(c, {
141
+ let n = t === o.length - 1, r = e.href && !n ? R(c, {
142
142
  key: `c${t}`,
143
143
  to: e.href,
144
144
  children: e.label
145
- }) : B("span", {
145
+ }) : R("span", {
146
146
  key: `c${t}`,
147
147
  "aria-current": n ? "page" : void 0
148
148
  }, e.label);
149
- return n ? [r] : [r, B("span", { key: `s${t}` }, "/")];
149
+ return n ? [r] : [r, R("span", { key: `s${t}` }, "/")];
150
150
  })) : null;
151
- return B("div", { className: "voltro-blog-layout" }, u, B("main", {
151
+ return R("div", { className: "voltro-blog-layout" }, u, R("main", {
152
152
  className: "voltro-blog-main",
153
- style: Ge
154
- }, d, r), a ? B("footer", {
153
+ style: Ye
154
+ }, d, r), a ? R("footer", {
155
155
  className: "voltro-blog-footer",
156
156
  style: {
157
157
  maxWidth: "48rem",
@@ -160,7 +160,31 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
160
160
  borderTop: "1px solid var(--voltro-blog-border, rgba(127,127,127,0.2))"
161
161
  }
162
162
  }, a) : null);
163
- }, J = "voltro:theme", qe = 31536e3, Je = () => {
163
+ }, K = (e) => {
164
+ if (e === "" || e === "/") return "/";
165
+ let t = e.startsWith("/") ? e : `/${e}`;
166
+ return t.endsWith("/") ? t.slice(0, -1) : t;
167
+ }, q = (e, t) => {
168
+ let n = e.endsWith("/") ? e.slice(0, -1) : e, r = K(t);
169
+ return r === "/" ? `${n}/` : `${n}${r}`;
170
+ }, J = (e, t, n) => {
171
+ let r = K(e);
172
+ return t === n ? r : r === "/" ? `/${t}` : `/${t}${r}`;
173
+ }, Ze = (e, t) => q(e, t), Qe = (e) => {
174
+ let { siteUrl: t, path: n, locale: r, locales: i, defaultLocale: a, xDefault: o = !0 } = e, s = q(t, J(n, r, a)), c = i.map((e) => ({
175
+ rel: "alternate",
176
+ hreflang: e,
177
+ href: q(t, J(n, e, a))
178
+ }));
179
+ return o && c.push({
180
+ rel: "alternate",
181
+ hreflang: "x-default",
182
+ href: q(t, J(n, a, a))
183
+ }), {
184
+ canonical: s,
185
+ links: c
186
+ };
187
+ }, Y = "voltro:theme", $e = 31536e3, et = () => {
164
188
  if (typeof document > "u") return "system";
165
189
  try {
166
190
  let e = document.cookie.match(/(?:^|;\s*)voltro:theme=([^;]*)/), t = e?.[1] === void 0 ? "" : decodeURIComponent(e[1]);
@@ -168,17 +192,17 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
168
192
  } catch {
169
193
  return "system";
170
194
  }
171
- }, Y = () => {
195
+ }, tt = () => {
172
196
  if (typeof window > "u" || !window.matchMedia) return !1;
173
197
  try {
174
198
  return window.matchMedia("(prefers-color-scheme: dark)").matches;
175
199
  } catch {
176
200
  return !1;
177
201
  }
178
- }, X = (e) => e === "system" ? Y() ? "dark" : "light" : e, Z = 0, Q = /* @__PURE__ */ new Set(), $ = () => {
202
+ }, X = (e) => e === "system" ? tt() ? "dark" : "light" : e, Z = 0, Q = /* @__PURE__ */ new Set(), $ = () => {
179
203
  Z++;
180
204
  for (let e of Q) e();
181
- }, Ye = (e) => {
205
+ }, nt = (e) => {
182
206
  Q.add(e);
183
207
  let t, n = () => $();
184
208
  if (typeof window < "u" && window.matchMedia) try {
@@ -190,21 +214,21 @@ var Le = ({ value: e, fallback: t, children: n, errorFallback: r }) => /* @__PUR
190
214
  t?.removeEventListener("change", n);
191
215
  } catch {}
192
216
  };
193
- }, Xe = () => {
194
- let e = Je();
217
+ }, rt = () => {
218
+ let e = et();
195
219
  return `${Z}:${e}:${X(e)}`;
196
- }, Ze = () => "0:system:light", Qe = () => {
197
- let [, e, t] = Ie(Ye, Xe, Ze).split(":");
220
+ }, it = () => "0:system:light", at = () => {
221
+ let [, e, t] = Be(nt, rt, it).split(":");
198
222
  return {
199
223
  theme: e,
200
224
  resolvedTheme: t,
201
- setTheme: Pe((e) => {
225
+ setTheme: Re((e) => {
202
226
  if (typeof document < "u") try {
203
- document.cookie = e === "system" ? `${J}=; path=/; max-age=0; SameSite=Lax` : `${J}=${e}; path=/; max-age=${qe}; SameSite=Lax`, document.documentElement.classList.toggle("dark", X(e) === "dark");
227
+ document.cookie = e === "system" ? `${Y}=; path=/; max-age=0; SameSite=Lax` : `${Y}=${e}; path=/; max-age=${$e}; SameSite=Lax`, document.documentElement.classList.toggle("dark", X(e) === "dark");
204
228
  } catch {}
205
229
  $();
206
230
  }, [])
207
231
  };
208
- }, $e = "framework-web";
232
+ }, ot = "framework-web";
209
233
  //#endregion
210
- export { t as AppClientsContext, Le as Await, Ke as BlogLayout, p as FallbackStringsProvider, He as Image, Be as ImageConfigProvider, be as Link, Ce as LoaderCache, P as LoaderDataContext, i as NavigationIndicator, y as NotFoundError, _e as PAGE_SLOT_ATTR, Te as PageSlot, F as PlainLink, I as RENDER_MODES, o as ReconnectContext, b as RedirectError, xe as Router, ve as RouterContext, we as ServerRequestContext, ye as ServerRequestProvider, J as THEME_COOKIE, $e as WEB_NAME, R as compileRoute, d as defaultFallbackStrings, re as defer, L as externalUrl, j as findNotFound, c as formatPrintf, ee as getIslandComponent, v as getRouteSnapshot, g as getStatuses, te as hydrateIslandsOnPage, a as installBrowserConsoleBridge, r as installServerLogRelay, se as isDeferredLoaderResult, T as isNotFound, Ae as isRedirect, _ as island, Ee as lazyPageRoute, ke as loaderCacheKey, N as matchRoute, ne as mount, ue as notFound, Oe as pageRoute, Se as parseCookieHeader, G as passthroughImageLoader, E as preloadRouteModule, f as pushStatus, k as redirect, s as registerClientTrace, de as resolveAnchorNavigation, De as resolveMeta, D as segmentLoaderKey, O as setRouteSnapshot, pe as sortRoutesByPriority, A as subscribeRouteSnapshot, m as subscribeStatuses, l as subscribeTraceErrors, n as useAppClient, fe as useBlocker, h as useFallbackStrings, C as useLoaderData, ge as useLocation, le as useNavigate, he as useParams, x as usePrefetch, u as useReconnect, z as useSearchParams, M as useServerRequest, S as useSetSearchParams, Qe as useTheme, w as withHash, me as withQuery };
234
+ export { t as AppClientsContext, Ve as Await, Xe as BlogLayout, p as FallbackStringsProvider, Ke as Image, We as ImageConfigProvider, Te as Link, Oe as LoaderCache, N as LoaderDataContext, Se as NO_LOADER_DATA, i as NavigationIndicator, w as NotFoundError, L as PAGE_SLOT_ATTR, P as PageSlot, F as PlainLink, Ee as RENDER_MODES, o as ReconnectContext, Pe as RedirectError, Ce as Router, ve as RouterContext, ke as ServerRequestContext, we as ServerRequestProvider, Y as THEME_COOKIE, ot as WEB_NAME, Ze as canonicalUrl, I as compileRoute, d as defaultFallbackStrings, re as defer, A as externalUrl, Ae as findNotFound, c as formatPrintf, ee as getIslandComponent, k as getRouteSnapshot, g as getStatuses, te as hydrateIslandsOnPage, a as installBrowserConsoleBridge, r as installServerLogRelay, se as isDeferredLoaderResult, ue as isNotFound, O as isRedirect, _ as island, q as joinUrl, Ne as lazyPageRoute, M as loaderCacheKey, J as localizedPath, Me as matchRoute, ne as mount, K as normalizePath, ge as notFound, T as pageRoute, De as parseCookieHeader, U as passthroughImageLoader, de as preloadRouteModule, f as pushStatus, D as redirect, s as registerClientTrace, je as resolveAnchorNavigation, E as resolveMeta, pe as segmentLoaderKey, Qe as seoAlternates, he as setRouteSnapshot, fe as sortRoutesByPriority, v as subscribeRouteSnapshot, m as subscribeStatuses, l as subscribeTraceErrors, n as useAppClient, S as useBlocker, h as useFallbackStrings, xe as useLoaderData, le as useLocation, be as useNavigate, b as useOptionalLoaderData, x as useParams, C as usePrefetch, u as useReconnect, ye as useSearchParams, j as useServerRequest, me as useSetSearchParams, at as useTheme, _e as withHash, y as withQuery };
@@ -1,35 +1,35 @@
1
- import { t as e } from "./frameworkBoot-DHdoAwd9.js";
2
- import { o as t, r as n, t as r } from "./routerState-DDDBYFrI.js";
1
+ import { t as e } from "./frameworkBoot-BkBY6M2s.js";
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";
5
- import { applyStoreSeeds as c } from "@voltro/client";
6
- import { jsx as l } from "react/jsx-runtime";
5
+ import { applyPreloadSeeds as c, applyStoreSeeds as l } from "@voltro/client";
6
+ import { jsx as u } from "react/jsx-runtime";
7
7
  //#region src/islands.tsx
8
- var u = /* @__PURE__ */ new Map(), d = (e, t) => {
9
- u.set(t.name, e);
8
+ var d = /* @__PURE__ */ new Map(), f = (e, t) => {
9
+ d.set(t.name, e);
10
10
  let n = t.hydrate ?? "visible", r = (r) => a("div", {
11
11
  "data-voltro-island": "",
12
12
  "data-island-name": t.name,
13
13
  "data-island-hydrate": n,
14
- "data-island-props": p(r)
14
+ "data-island-props": m(r)
15
15
  }, a(e, r));
16
16
  return r.displayName = `Island(${t.name})`, r;
17
- }, f = (e) => u.get(e), p = (e) => JSON.stringify(e ?? {}).replace(/</g, "\\u003c"), m = (e) => {
17
+ }, p = (e) => d.get(e), m = (e) => JSON.stringify(e ?? {}).replace(/</g, "\\u003c"), h = (e) => {
18
18
  if (!e) return {};
19
19
  try {
20
20
  return JSON.parse(e);
21
21
  } catch {
22
22
  return {};
23
23
  }
24
- }, h = () => {
24
+ }, g = () => {
25
25
  let e = document.querySelectorAll("[data-voltro-island]:not([data-voltro-hydrated])");
26
26
  for (let t of e) {
27
- let e = t.dataset.islandName ?? "", n = u.get(e);
27
+ let e = t.dataset.islandName ?? "", n = d.get(e);
28
28
  if (!n) {
29
29
  console.warn(`[voltro] island "${e}" referenced in DOM but not registered. Did the island file get imported in this bundle?`);
30
30
  continue;
31
31
  }
32
- let r = m(t.dataset.islandProps ?? null), i = t.dataset.islandHydrate ?? "visible";
32
+ let r = h(t.dataset.islandProps ?? null), i = t.dataset.islandHydrate ?? "visible";
33
33
  t.setAttribute("data-voltro-hydrated", "pending");
34
34
  let o = () => {
35
35
  t.getAttribute("data-voltro-hydrated") !== "done" && (t.setAttribute("data-voltro-hydrated", "done"), s(t, a(n, r)));
@@ -50,28 +50,28 @@ var u = /* @__PURE__ */ new Map(), d = (e, t) => {
50
50
  t.addEventListener("pointerdown", e, { passive: !0 }), t.addEventListener("keydown", e);
51
51
  }
52
52
  }
53
- }, g = (e) => `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws/${e}`, _ = (a, u) => {
54
- let d = document.getElementById(u.rootId ?? "root");
55
- if (!d) throw Error(`@voltro/web: no #${u.rootId ?? "root"} element in the document`);
56
- let f = Object.entries(u.apis).map(([e, t]) => ({
53
+ }, _ = (e) => `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws/${e}`, v = (a, d) => {
54
+ let f = document.getElementById(d.rootId ?? "root");
55
+ if (!f) throw Error(`@voltro/web: no #${d.rootId ?? "root"} element in the document`);
56
+ let p = Object.entries(d.apis).map(([e, t]) => ({
57
57
  name: e,
58
58
  group: t.group,
59
59
  descriptors: t.descriptors ?? {},
60
- wsUrl: t.wsUrl ?? g(e),
60
+ wsUrl: t.wsUrl ?? _(e),
61
61
  headers: t.headers
62
- })), p = n(document.getElementById(r)?.textContent, window.location.pathname), m = p !== null && d.children.length > 0, _ = document.querySelector("meta[name=\"voltro-interactive\"]")?.getAttribute("content") ?? "full";
63
- if (_ === "none") return;
64
- if (_ === "islands") {
65
- h();
62
+ })), 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";
63
+ if (v === "none") return;
64
+ if (v === "islands") {
65
+ g();
66
66
  return;
67
67
  }
68
- m && t(p), c(p?.storeSeeds);
69
- let v = /* @__PURE__ */ l(i, { children: /* @__PURE__ */ l(e, {
68
+ h && t(m), l(m?.storeSeeds), c(m?.preloadSeeds);
69
+ let y = /* @__PURE__ */ u(i, { children: /* @__PURE__ */ u(e, {
70
70
  App: a,
71
- apis: f,
72
- ...u.Devtools ? { Devtools: u.Devtools } : {}
71
+ apis: p,
72
+ ...d.Devtools ? { Devtools: d.Devtools } : {}
73
73
  }) });
74
- m ? s(d, v) : o(d).render(v);
74
+ h ? s(f, y) : o(f).render(y);
75
75
  };
76
76
  //#endregion
77
- export { d as i, f as n, h as r, _ as t };
77
+ export { f as i, p as n, g as r, v as t };
package/dist/mount.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ComponentType } from 'react';
2
+ import { ResolvableHeaders } from '@voltro/client';
2
3
  import { Rpc } from '@effect/rpc';
3
4
  import { RpcGroup } from '@effect/rpc';
4
5
 
@@ -57,14 +58,4 @@ export declare interface MountOptions {
57
58
  readonly Devtools?: ComponentType;
58
59
  }
59
60
 
60
- /**
61
- * Auth headers for an api's WS rpc client. @effect/rpc sends these in the
62
- * per-request MESSAGE frame (NOT the WS upgrade — a browser can't set upgrade
63
- * headers), so the server's AuthMiddleware sees them on every call; this is
64
- * what lets a cross-origin api authenticate WS subscriptions. A thunk is
65
- * resolved fresh on every (re)connect, so a rotating token (e.g. a Supabase
66
- * access token) is pulled anew per connect instead of freezing at first mount.
67
- */
68
- declare type ResolvableHeaders = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
69
-
70
61
  export { }
package/dist/mount.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as e } from "./mount-DfyG67bY.js";
1
+ import { t as e } from "./mount-fDzFaIuf.js";
2
2
  export { e as mount };
@@ -69,6 +69,7 @@ var e = /* @__PURE__ */ RegExp("\\u2028", "g"), t = /* @__PURE__ */ RegExp("\\u2
69
69
  },
70
70
  ...e.pageClientOnly ? { pageClientOnly: !0 } : {},
71
71
  ...e.storeSeeds !== void 0 && e.storeSeeds.length > 0 ? { storeSeeds: e.storeSeeds } : {},
72
+ ...e.preloadSeeds !== void 0 && e.preloadSeeds.length > 0 ? { preloadSeeds: e.preloadSeeds } : {},
72
73
  ...n || r ? { deferred: {
73
74
  ...n ? { page: e.deferredPageIds } : {},
74
75
  ...r ? { segments: e.deferredSegmentIds } : {}
@@ -99,7 +100,8 @@ var e = /* @__PURE__ */ RegExp("\\u2028", "g"), t = /* @__PURE__ */ RegExp("\\u2
99
100
  segments: a.size > 0 ? a : T,
100
101
  pageClientOnly: r.pageClientOnly === !0,
101
102
  pathname: t,
102
- storeSeeds: Array.isArray(r.storeSeeds) ? r.storeSeeds : []
103
+ storeSeeds: Array.isArray(r.storeSeeds) ? r.storeSeeds : [],
104
+ preloadSeeds: Array.isArray(r.preloadSeeds) ? r.preloadSeeds : []
103
105
  };
104
106
  }, O = (e, t) => {
105
107
  if (!t) return e;