@voltro/web 0.1.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.js ADDED
@@ -0,0 +1,141 @@
1
+ import { AppClientsContext as e, useAppClient as t } from "./hooks.js";
2
+ import { a as n, c as r, i, l as a, o, r as s, s as c, u as l } from "./frameworkBoot-DBOFujAI.js";
3
+ import { a as u, c as d, i as f, l as p, o as m, s as h } from "./defaultFallbacks-Bg_X-9Wv.js";
4
+ import { i as g, n as _, r as ee, t as te } from "./mount-BuFzlw1e.js";
5
+ import { A as ne, B as re, C as ie, D as v, E as y, F as b, I as x, L as S, M as C, N as w, O as T, P as E, R as D, S as O, T as k, V 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 B, k as V, l as ae, m as H, n as U, o as W, p as G, r as K, s as q, t as J, u as oe, v as se, w as ce, x as le, y as ue, z as de } from "./serverContext-D5-Dmh8d.js";
6
+ import { createContext as fe, createElement as Y, useContext as pe } from "react";
7
+ export * from "@voltro/client";
8
+ export * from "@voltro/ui";
9
+ //#region src/image.tsx
10
+ var X = ({ src: e }) => e, Z = fe({ loader: X }), me = ({ loader: e, children: t }) => Y(Z.Provider, { value: { loader: e } }, t), Q = [
11
+ 640,
12
+ 750,
13
+ 828,
14
+ 1080,
15
+ 1200,
16
+ 1920,
17
+ 2048,
18
+ 3840
19
+ ], he = (e, t, n, r) => {
20
+ let i = n !== void 0 && !r ? Array.from(new Set(Q.filter((e) => e <= n * 2).concat(n))).sort((e, t) => e - t) : [...Q];
21
+ return {
22
+ srcSet: i.map((n) => `${e({
23
+ src: t,
24
+ width: n
25
+ })} ${n}w`).join(", "),
26
+ fallback: e({
27
+ src: t,
28
+ width: i[i.length - 1]
29
+ })
30
+ };
31
+ }, ge = ({ 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 }) => {
32
+ let f = pe(Z).loader, p = s ?? f;
33
+ !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.`);
34
+ let { srcSet: m, fallback: h } = he(p, e, n, i), g = c === "blur" && l ? {
35
+ backgroundImage: `url(${l})`,
36
+ backgroundSize: "cover",
37
+ backgroundPosition: "center"
38
+ } : {}, _ = i ? {
39
+ position: "absolute",
40
+ inset: 0,
41
+ height: "100%",
42
+ width: "100%",
43
+ objectFit: "cover"
44
+ } : {};
45
+ return Y("img", {
46
+ src: h,
47
+ srcSet: m,
48
+ sizes: a ?? (i ? "100vw" : void 0),
49
+ alt: t,
50
+ ...i ? {} : {
51
+ width: n,
52
+ height: r
53
+ },
54
+ loading: o ? "eager" : "lazy",
55
+ decoding: "async",
56
+ ...o ? { fetchPriority: "high" } : {},
57
+ style: {
58
+ ...g,
59
+ ..._,
60
+ ...u
61
+ },
62
+ ...d
63
+ });
64
+ }, $ = ({ to: e, children: t, className: n }) => Y("a", {
65
+ href: e,
66
+ className: n
67
+ }, t), _e = {
68
+ position: "sticky",
69
+ top: 0,
70
+ zIndex: 10,
71
+ display: "flex",
72
+ alignItems: "center",
73
+ justifyContent: "space-between",
74
+ gap: "1.5rem",
75
+ padding: "1rem 1.5rem",
76
+ borderBottom: "1px solid var(--voltro-blog-border, rgba(127,127,127,0.2))",
77
+ backdropFilter: "blur(8px)"
78
+ }, ve = {
79
+ maxWidth: "48rem",
80
+ margin: "0 auto",
81
+ padding: "2rem 1.5rem",
82
+ width: "100%"
83
+ }, ye = (e) => {
84
+ let { brand: t, topNav: n, children: r, pathname: i, footer: a, breadcrumbs: o, headerRight: s } = e, c = e.Link ?? $, l = Y("nav", {
85
+ className: "voltro-blog-nav",
86
+ style: {
87
+ display: "flex",
88
+ gap: "1rem",
89
+ alignItems: "center"
90
+ }
91
+ }, ...n.map((e) => {
92
+ let t = i === e.href;
93
+ return Y(c, {
94
+ key: e.href,
95
+ to: e.href,
96
+ className: t ? "voltro-blog-nav-link is-active" : "voltro-blog-nav-link",
97
+ children: e.label
98
+ });
99
+ })), u = Y("header", {
100
+ className: "voltro-blog-header",
101
+ style: _e
102
+ }, Y("div", { className: "voltro-blog-brand" }, t), s ? Y("div", { style: {
103
+ display: "flex",
104
+ gap: "1rem",
105
+ alignItems: "center"
106
+ } }, l, s) : l), d = o && o.length > 0 ? Y("nav", {
107
+ className: "voltro-blog-breadcrumbs",
108
+ "aria-label": "Breadcrumb",
109
+ style: {
110
+ fontSize: "0.85rem",
111
+ opacity: .75,
112
+ marginBottom: "1rem",
113
+ display: "flex",
114
+ gap: "0.5rem"
115
+ }
116
+ }, ...o.flatMap((e, t) => {
117
+ let n = t === o.length - 1, r = e.href && !n ? Y(c, {
118
+ key: `c${t}`,
119
+ to: e.href,
120
+ children: e.label
121
+ }) : Y("span", {
122
+ key: `c${t}`,
123
+ "aria-current": n ? "page" : void 0
124
+ }, e.label);
125
+ return n ? [r] : [r, Y("span", { key: `s${t}` }, "/")];
126
+ })) : null;
127
+ return Y("div", { className: "voltro-blog-layout" }, u, Y("main", {
128
+ className: "voltro-blog-main",
129
+ style: ve
130
+ }, d, r), a ? Y("footer", {
131
+ className: "voltro-blog-footer",
132
+ style: {
133
+ maxWidth: "48rem",
134
+ margin: "0 auto",
135
+ padding: "2rem 1.5rem",
136
+ borderTop: "1px solid var(--voltro-blog-border, rgba(127,127,127,0.2))"
137
+ }
138
+ }, a) : null);
139
+ }, be = "framework-web";
140
+ //#endregion
141
+ export { e as AppClientsContext, ye as BlogLayout, f as FallbackStringsProvider, ge as Image, me as ImageConfigProvider, W as Link, q as LoaderCache, P as LoaderDataContext, r as NavigationIndicator, w as NotFoundError, ae as PlainLink, a as ReconnectContext, E as RedirectError, oe as Router, F as RouterContext, J as ServerRequestContext, U as ServerRequestProvider, be as WEB_NAME, I as compileRoute, u as defaultFallbackStrings, G as externalUrl, H as findNotFound, s as formatPrintf, _ as getIslandComponent, de as getRouteSnapshot, h as getStatuses, ee as hydrateIslandsOnPage, i as installBrowserConsoleBridge, n as installServerLogRelay, b as isNotFound, x as isRedirect, g as island, R as lazyPageRoute, L as loaderCacheKey, j as matchRoute, te as mount, S as notFound, se as pageRoute, K as parseCookieHeader, X as passthroughImageLoader, ue as preloadRouteModule, d as pushStatus, D as redirect, o as registerClientTrace, N as resolveAnchorNavigation, le as resolveMeta, O as segmentLoaderKey, re as setRouteSnapshot, ie as sortRoutesByPriority, A as subscribeRouteSnapshot, p as subscribeStatuses, c as subscribeTraceErrors, t as useAppClient, ce as useBlocker, m as useFallbackStrings, k as useLoaderData, y as useLocation, v as useNavigate, T as useParams, V as usePrefetch, l as useReconnect, z as useSearchParams, M as useServerRequest, ne as useSetSearchParams, B as withHash, C as withQuery };
@@ -0,0 +1,74 @@
1
+ import { t as e } from "./frameworkBoot-DBOFujAI.js";
2
+ import { StrictMode as t, createElement as n } from "react";
3
+ import { createRoot as r, hydrateRoot as i } from "react-dom/client";
4
+ import { jsx as a } from "react/jsx-runtime";
5
+ //#region src/islands.tsx
6
+ var o = /* @__PURE__ */ new Map(), s = (e, t) => {
7
+ o.set(t.name, e);
8
+ let r = t.hydrate ?? "visible", i = (i) => n("div", {
9
+ "data-voltro-island": "",
10
+ "data-island-name": t.name,
11
+ "data-island-hydrate": r,
12
+ "data-island-props": l(i)
13
+ }, n(e, i));
14
+ return i.displayName = `Island(${t.name})`, i;
15
+ }, c = (e) => o.get(e), l = (e) => JSON.stringify(e ?? {}).replace(/</g, "\\u003c"), u = (e) => {
16
+ if (!e) return {};
17
+ try {
18
+ return JSON.parse(e);
19
+ } catch {
20
+ return {};
21
+ }
22
+ }, d = () => {
23
+ let e = document.querySelectorAll("[data-voltro-island]:not([data-voltro-hydrated])");
24
+ for (let t of e) {
25
+ let e = t.dataset.islandName ?? "", r = o.get(e);
26
+ if (!r) {
27
+ console.warn(`[voltro] island "${e}" referenced in DOM but not registered. Did the island file get imported in this bundle?`);
28
+ continue;
29
+ }
30
+ let a = u(t.dataset.islandProps ?? null), s = t.dataset.islandHydrate ?? "visible";
31
+ t.setAttribute("data-voltro-hydrated", "pending");
32
+ let c = () => {
33
+ t.getAttribute("data-voltro-hydrated") !== "done" && (t.setAttribute("data-voltro-hydrated", "done"), i(t, n(r, a)));
34
+ };
35
+ if (s === "load") c();
36
+ else if (s === "idle") {
37
+ let e = globalThis.requestIdleCallback;
38
+ e ? e(c) : setTimeout(c, 0);
39
+ } else if (s === "visible") {
40
+ let e = new IntersectionObserver((t) => {
41
+ t.some((e) => e.isIntersecting) && (e.disconnect(), c());
42
+ }, { rootMargin: "64px" });
43
+ e.observe(t);
44
+ } else if (s === "interaction") {
45
+ let e = () => {
46
+ t.removeEventListener("pointerdown", e), t.removeEventListener("keydown", e), c();
47
+ };
48
+ t.addEventListener("pointerdown", e, { passive: !0 }), t.addEventListener("keydown", e);
49
+ }
50
+ }
51
+ }, f = (e) => `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws/${e}`, p = (n, o) => {
52
+ let s = document.getElementById(o.rootId ?? "root");
53
+ if (!s) throw Error(`@voltro/web: no #${o.rootId ?? "root"} element in the document`);
54
+ let c = Object.entries(o.apis).map(([e, t]) => ({
55
+ name: e,
56
+ group: t.group,
57
+ descriptors: t.descriptors ?? {},
58
+ wsUrl: t.wsUrl ?? f(e),
59
+ headers: t.headers
60
+ })), l = document.getElementById("__voltro_state__") !== null && s.children.length > 0, u = document.querySelector("meta[name=\"voltro-interactive\"]")?.getAttribute("content") ?? "full";
61
+ if (u === "none") return;
62
+ if (u === "islands") {
63
+ d();
64
+ return;
65
+ }
66
+ let p = /* @__PURE__ */ a(t, { children: /* @__PURE__ */ a(e, {
67
+ App: n,
68
+ apis: c,
69
+ ...o.Devtools ? { Devtools: o.Devtools } : {}
70
+ }) });
71
+ l ? i(s, p) : r(s).render(p);
72
+ };
73
+ //#endregion
74
+ export { s as i, c as n, d as r, p as t };
@@ -0,0 +1,70 @@
1
+ import { ComponentType } from 'react';
2
+ import { Rpc } from '@effect/rpc';
3
+ import { RpcGroup } from '@effect/rpc';
4
+
5
+ /**
6
+ * Boot the framework's React stack against the given app. Resolves the
7
+ * typed rpc client for each api in parallel, renders the user's component
8
+ * tree inside the framework runtime + clients providers, and in dev mode
9
+ * auto-recovers each client in-place when its dev server restarts.
10
+ */
11
+ export declare const mount: (App: ComponentType, options: MountOptions) => void;
12
+
13
+ export declare interface MountApiSpec<Rpcs extends Rpc.Any = Rpc.Any> {
14
+ /** The RpcGroup this api speaks. */
15
+ readonly group: RpcGroup.RpcGroup<Rpcs>;
16
+ /** Per-rpc-tag descriptor metadata: `{ kind, source?, targets? }`. The
17
+ * framework's auto-optimistic flow reads this to know which mutation
18
+ * affects which cached query. The CLI's codegen emits it as
19
+ * `appDescriptors` next to `appGroup`. */
20
+ readonly descriptors?: Readonly<Record<string, {
21
+ readonly kind: 'query' | 'mutation' | 'action' | 'stream' | 'workflow';
22
+ readonly source?: string;
23
+ readonly targets?: ReadonlyArray<{
24
+ readonly table: string;
25
+ readonly op: 'insert' | 'update' | 'delete';
26
+ readonly order?: 'prepend' | 'append';
27
+ }>;
28
+ }>>;
29
+ /** Explicit WebSocket URL for this api. Required for external apis
30
+ * (where there's no Vite proxy). Defaults to same-origin `/ws/<name>`
31
+ * for workspace apis served via the framework's dev proxy. */
32
+ readonly wsUrl?: string;
33
+ /** Auth headers sent with EVERY rpc request over the socket — this is how
34
+ * a cross-origin api authenticates WS subscriptions. They ride in the
35
+ * @effect/rpc message frame, NOT the WS handshake (a browser can't set
36
+ * upgrade headers). Pass a thunk to fetch a rotating token fresh on every
37
+ * (re)connect, e.g. `headers: async () => ({ authorization: 'Bearer ' +
38
+ * (await supabase.auth.getSession()).data.session?.access_token })`; a
39
+ * static object is also accepted. Do NOT hardcode a secret literal — it
40
+ * ships to the client and is visible in source; resolve it at runtime. */
41
+ readonly headers?: ResolvableHeaders;
42
+ }
43
+
44
+ export declare interface MountOptions {
45
+ /** Map of api name → spec. The name is the lookup key for hooks
46
+ * (`useAppClient<T>(name)`, `useSubscription(name, ...)`, etc.). */
47
+ readonly apis: Record<string, MountApiSpec>;
48
+ /** Override the root element id. Defaults to 'root'. */
49
+ readonly rootId?: string;
50
+ /** Optional in-page devtools overlay component. Wired up by the
51
+ * CLI's generated `main.tsx` when `import.meta.env.DEV` is truthy
52
+ * and the app hasn't set `disableDevtools` in app.config.ts.
53
+ * Renders INSIDE the FrameworkBoot tree so its hooks
54
+ * (useFrameworkRuntimes, useLoaderData, …) resolve normally.
55
+ * PascalCase because it is a React component reference rendered
56
+ * as <Devtools/>. */
57
+ readonly Devtools?: ComponentType;
58
+ }
59
+
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
+ export { }
package/dist/mount.js ADDED
@@ -0,0 +1,2 @@
1
+ import { t as e } from "./mount-BuFzlw1e.js";
2
+ export { e as mount };