@mauroandre/velojs 0.0.55 → 0.0.57
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/README.md +423 -3
- package/agent/AGENTS.md +216 -0
- package/agent/skills/velojs-live-loader/SKILL.md +410 -0
- package/dist/channels.d.ts +370 -0
- package/dist/channels.js +376 -0
- package/dist/client.js +56 -45
- package/dist/components.js +7 -6
- package/dist/events.d.ts +6 -0
- package/dist/events.js +1 -0
- package/dist/hooks.d.ts +26 -21
- package/dist/hooks.js +117 -98
- package/dist/live-loader.d.ts +120 -0
- package/dist/live-loader.js +141 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +144 -118
- package/dist/testing/index.d.ts +5 -1
- package/dist/testing/index.js +495 -412
- package/dist/testing/subscription.d.ts +18 -2
- package/dist/testing/types.d.ts +85 -0
- package/dist/types.d.ts +9 -0
- package/dist/vite.d.ts +15 -0
- package/dist/vite.js +431 -340
- package/package.json +1 -1
package/dist/hooks.js
CHANGED
|
@@ -1,125 +1,144 @@
|
|
|
1
|
-
import { signal as
|
|
2
|
-
import { useEffect as
|
|
3
|
-
import { useLocation as b, useParams as
|
|
4
|
-
import { refetchModule as
|
|
5
|
-
import { __veloUpdatePending as
|
|
6
|
-
|
|
1
|
+
import { signal as S, useSignal as s } from "@preact/signals";
|
|
2
|
+
import { useEffect as m, useRef as E } from "preact/hooks";
|
|
3
|
+
import { useLocation as b, useParams as $, useSearch as B } from "wouter-preact";
|
|
4
|
+
import { refetchModule as C, loaderLoading as D, loaderEntry as N } from "./loader-store.js";
|
|
5
|
+
import { __veloUpdatePending as X } from "./loader-store.js";
|
|
6
|
+
import { loaderFreshness as w, moduleChannelFreshness as O } from "./live-loader.js";
|
|
7
|
+
function j() {
|
|
7
8
|
return typeof window > "u" ? {} : window.__VELO_BOOT__ ?? {};
|
|
8
9
|
}
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
function F(e) {
|
|
11
|
+
const t = e.value;
|
|
12
|
+
t === null || typeof t != "object" || (e.value = Array.isArray(t) ? [...t] : { ...t });
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
+
function J(e) {
|
|
14
15
|
return typeof window > "u" ? {
|
|
15
16
|
data: {
|
|
16
17
|
get value() {
|
|
17
18
|
return e ? globalThis.__veloServerData?.getStore?.()?.[e] ?? null : null;
|
|
18
19
|
}
|
|
19
20
|
},
|
|
20
|
-
loading:
|
|
21
|
+
loading: S(!1),
|
|
21
22
|
refetch: () => {
|
|
22
|
-
}
|
|
23
|
+
},
|
|
24
|
+
freshness: w(),
|
|
25
|
+
freshnessByChannel: S({})
|
|
23
26
|
} : e ? {
|
|
24
27
|
data: N(e),
|
|
25
|
-
loading:
|
|
26
|
-
refetch: () =>
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
loading: D(e),
|
|
29
|
+
refetch: () => C(e),
|
|
30
|
+
freshness: w(),
|
|
31
|
+
freshnessByChannel: O(e)
|
|
32
|
+
} : {
|
|
33
|
+
data: S(null),
|
|
34
|
+
loading: S(!1),
|
|
35
|
+
refetch: () => {
|
|
36
|
+
},
|
|
37
|
+
freshness: w(),
|
|
38
|
+
freshnessByChannel: S({})
|
|
39
|
+
};
|
|
29
40
|
}
|
|
30
|
-
function
|
|
31
|
-
let
|
|
32
|
-
if (Array.isArray(e) ? r = e : (
|
|
33
|
-
let
|
|
34
|
-
if (
|
|
35
|
-
const
|
|
36
|
-
|
|
41
|
+
function M(e, t) {
|
|
42
|
+
let n, r;
|
|
43
|
+
if (Array.isArray(e) ? r = e : (n = e, r = t), typeof window > "u") {
|
|
44
|
+
let a = null;
|
|
45
|
+
if (n) {
|
|
46
|
+
const v = globalThis.__veloServerData?.getStore?.();
|
|
47
|
+
v?.[n] && (a = v[n]);
|
|
37
48
|
}
|
|
38
49
|
return {
|
|
39
|
-
data:
|
|
40
|
-
loading:
|
|
50
|
+
data: s(a),
|
|
51
|
+
loading: s(!1),
|
|
41
52
|
refetch: () => {
|
|
42
|
-
}
|
|
53
|
+
},
|
|
54
|
+
freshness: w(),
|
|
55
|
+
freshnessByChannel: s({})
|
|
43
56
|
};
|
|
44
57
|
}
|
|
45
|
-
if (!
|
|
46
|
-
return {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
if (!n)
|
|
59
|
+
return {
|
|
60
|
+
data: s(null),
|
|
61
|
+
loading: s(!1),
|
|
62
|
+
refetch: () => {
|
|
63
|
+
},
|
|
64
|
+
freshness: w(),
|
|
65
|
+
freshnessByChannel: s({})
|
|
66
|
+
};
|
|
67
|
+
const l = N(n), o = D(n), u = () => C(n), h = w(), y = E(!0);
|
|
68
|
+
return m(() => {
|
|
69
|
+
y.current && (y.current = !1, l.value !== null) || u();
|
|
70
|
+
}, r ?? []), { data: l, loading: o, refetch: u, freshness: h, freshnessByChannel: O(n) };
|
|
52
71
|
}
|
|
53
|
-
function
|
|
72
|
+
function q() {
|
|
54
73
|
const [, e] = b();
|
|
55
74
|
return e;
|
|
56
75
|
}
|
|
57
|
-
function
|
|
58
|
-
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} :
|
|
76
|
+
function x() {
|
|
77
|
+
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} : $();
|
|
59
78
|
}
|
|
60
|
-
function
|
|
79
|
+
function G() {
|
|
61
80
|
if (typeof window > "u")
|
|
62
81
|
return globalThis.__veloServerData?.getStore?.()?.__query ?? {};
|
|
63
|
-
const e =
|
|
64
|
-
return new URLSearchParams(e).forEach((
|
|
65
|
-
|
|
66
|
-
}),
|
|
82
|
+
const e = B(), t = {};
|
|
83
|
+
return new URLSearchParams(e).forEach((n, r) => {
|
|
84
|
+
t[r] = n;
|
|
85
|
+
}), t;
|
|
67
86
|
}
|
|
68
|
-
function
|
|
87
|
+
function Q() {
|
|
69
88
|
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (b(), window.location.pathname);
|
|
70
89
|
}
|
|
71
|
-
function
|
|
72
|
-
const
|
|
73
|
-
return
|
|
74
|
-
if (typeof window > "u" || !
|
|
90
|
+
function V(e, t = {}) {
|
|
91
|
+
const n = s(null), r = s(null), l = s(!1), o = s(null), { channel: u, enabled: h = !0 } = t;
|
|
92
|
+
return m(() => {
|
|
93
|
+
if (typeof window > "u" || !h) return;
|
|
75
94
|
if (!e.__path) {
|
|
76
95
|
console.warn("[velojs] useEventStream: stream has no __path");
|
|
77
96
|
return;
|
|
78
97
|
}
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
return
|
|
98
|
+
l.value = !1, o.value = null;
|
|
99
|
+
const y = u ? `${e.__path}?channel=${encodeURIComponent(u)}` : e.__path, a = new EventSource(y);
|
|
100
|
+
return a.addEventListener("snapshot", (g) => {
|
|
82
101
|
try {
|
|
83
|
-
r.value = JSON.parse(
|
|
84
|
-
} catch (
|
|
85
|
-
|
|
102
|
+
r.value = JSON.parse(g.data);
|
|
103
|
+
} catch (v) {
|
|
104
|
+
o.value = v;
|
|
86
105
|
}
|
|
87
|
-
}),
|
|
106
|
+
}), a.addEventListener("message", (g) => {
|
|
88
107
|
try {
|
|
89
|
-
|
|
90
|
-
} catch (
|
|
91
|
-
|
|
108
|
+
n.value = JSON.parse(g.data);
|
|
109
|
+
} catch (v) {
|
|
110
|
+
o.value = v;
|
|
92
111
|
}
|
|
93
|
-
}),
|
|
94
|
-
}),
|
|
95
|
-
|
|
96
|
-
}),
|
|
97
|
-
|
|
112
|
+
}), a.addEventListener("heartbeat", () => {
|
|
113
|
+
}), a.addEventListener("close", () => {
|
|
114
|
+
l.value = !0, a.close();
|
|
115
|
+
}), a.addEventListener("error", () => {
|
|
116
|
+
a.readyState === EventSource.CLOSED && (l.value = !0);
|
|
98
117
|
}), () => {
|
|
99
|
-
|
|
118
|
+
a.close();
|
|
100
119
|
};
|
|
101
|
-
}, [e,
|
|
120
|
+
}, [e, u, h]), { data: n, snapshot: r, closed: l, error: o };
|
|
102
121
|
}
|
|
103
|
-
function
|
|
104
|
-
const
|
|
105
|
-
return
|
|
122
|
+
function z(e, t = {}) {
|
|
123
|
+
const n = s("connecting"), r = s(null), l = s(null), o = E(null), u = E([]), { channel: h, onMessage: y, onOpen: a, onClose: g, onError: v, enabled: L = !0 } = t;
|
|
124
|
+
return m(() => {
|
|
106
125
|
if (typeof window > "u" || !L) return;
|
|
107
126
|
if (!e.__path) {
|
|
108
127
|
console.warn("[velojs] useSocket: stub has no __path");
|
|
109
128
|
return;
|
|
110
129
|
}
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
return f.binaryType = "arraybuffer",
|
|
114
|
-
|
|
115
|
-
const c =
|
|
116
|
-
|
|
130
|
+
n.value = "connecting", l.value = null;
|
|
131
|
+
const d = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}`, i = h ? `${d}${e.__path}?channel=${encodeURIComponent(h)}` : `${d}${e.__path}`, f = new WebSocket(i);
|
|
132
|
+
return f.binaryType = "arraybuffer", o.current = f, f.addEventListener("open", () => {
|
|
133
|
+
n.value = "open";
|
|
134
|
+
const c = u.current;
|
|
135
|
+
u.current = [];
|
|
117
136
|
for (const _ of c)
|
|
118
137
|
try {
|
|
119
138
|
f.send(_);
|
|
120
139
|
} catch {
|
|
121
140
|
}
|
|
122
|
-
|
|
141
|
+
a?.();
|
|
123
142
|
}), f.addEventListener("message", (c) => {
|
|
124
143
|
let _;
|
|
125
144
|
if (typeof c.data == "string")
|
|
@@ -128,47 +147,47 @@ function F(e, n = {}) {
|
|
|
128
147
|
_ = new Uint8Array(c.data);
|
|
129
148
|
else
|
|
130
149
|
return;
|
|
131
|
-
r.value = _,
|
|
150
|
+
r.value = _, y?.(_);
|
|
132
151
|
}), f.addEventListener("close", (c) => {
|
|
133
|
-
|
|
152
|
+
n.value = "closed", o.current = null, g?.(c);
|
|
134
153
|
}), f.addEventListener("error", (c) => {
|
|
135
|
-
|
|
154
|
+
l.value = c, v?.(c);
|
|
136
155
|
}), () => {
|
|
137
|
-
|
|
156
|
+
o.current = null;
|
|
138
157
|
try {
|
|
139
158
|
(f.readyState === WebSocket.OPEN || f.readyState === WebSocket.CONNECTING) && f.close();
|
|
140
159
|
} catch {
|
|
141
160
|
}
|
|
142
161
|
};
|
|
143
|
-
}, [e.__path,
|
|
144
|
-
let
|
|
145
|
-
typeof p == "string" || p instanceof Uint8Array ?
|
|
146
|
-
const i =
|
|
162
|
+
}, [e.__path, h, L]), { send: (p) => {
|
|
163
|
+
let d;
|
|
164
|
+
typeof p == "string" || p instanceof Uint8Array ? d = p : d = JSON.stringify(p);
|
|
165
|
+
const i = o.current;
|
|
147
166
|
if (i && i.readyState === WebSocket.OPEN)
|
|
148
167
|
try {
|
|
149
|
-
i.send(
|
|
168
|
+
i.send(d);
|
|
150
169
|
} catch {
|
|
151
170
|
}
|
|
152
|
-
else i && i.readyState === WebSocket.CONNECTING &&
|
|
153
|
-
}, close: (p,
|
|
154
|
-
const i =
|
|
171
|
+
else i && i.readyState === WebSocket.CONNECTING && u.current.push(d);
|
|
172
|
+
}, close: (p, d) => {
|
|
173
|
+
const i = o.current;
|
|
155
174
|
if (i)
|
|
156
175
|
try {
|
|
157
|
-
i.close(p,
|
|
176
|
+
i.close(p, d);
|
|
158
177
|
} catch {
|
|
159
178
|
}
|
|
160
|
-
}, status:
|
|
179
|
+
}, status: n, lastMessage: r, error: l };
|
|
161
180
|
}
|
|
162
181
|
export {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
182
|
+
J as Loader,
|
|
183
|
+
X as __veloUpdatePending,
|
|
184
|
+
F as touch,
|
|
185
|
+
j as useBoot,
|
|
186
|
+
V as useEventStream,
|
|
187
|
+
M as useLoader,
|
|
188
|
+
q as useNavigate,
|
|
189
|
+
x as useParams,
|
|
190
|
+
Q as usePathname,
|
|
191
|
+
G as useQuery,
|
|
192
|
+
z as useSocket
|
|
174
193
|
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live loader — client runtime.
|
|
3
|
+
*
|
|
4
|
+
* After hydration, every rendered module that declares `channels` opens one SSE
|
|
5
|
+
* connection per (module, channel) at `/_channel/{moduleId}/{channel}`. The
|
|
6
|
+
* connection declares the route params the module's path declares, extracted
|
|
7
|
+
* from the current URL and serialized in `?_route=` — the client declares the
|
|
8
|
+
* address it is already seeing, the server validates it against the declared
|
|
9
|
+
* path and decides the partition. The snapshot the connection receives — the
|
|
10
|
+
* connect snapshot and each emit's — replaces the value of the module's entry
|
|
11
|
+
* in the loader store, so the page stays faithful to server state without a
|
|
12
|
+
* line of code from the developer: `useLoader()`/`Loader()` handles keep the
|
|
13
|
+
* same signal instance and the reading component re-renders on its own.
|
|
14
|
+
*
|
|
15
|
+
* The connection is keyed by those params, not by the pathname: navigating
|
|
16
|
+
* between children of the same params keeps the connection; a move to another
|
|
17
|
+
* resource closes it and opens a new one.
|
|
18
|
+
*
|
|
19
|
+
* A `slice` event is the other delivery mode: the producer already holds the
|
|
20
|
+
* value, so the payload carries only the changed keys. A slice is merged
|
|
21
|
+
* shallowly by key onto the module's current value — the key sent is replaced
|
|
22
|
+
* by the whole value sent, keys not sent stay intact, and an absent key never
|
|
23
|
+
* means "removed" (removal is re-sending the key with the new whole value).
|
|
24
|
+
* The merge happens on arrival, never at render: slices that land in the same
|
|
25
|
+
* frame are accumulated and applied together before the next render, so none is
|
|
26
|
+
* lost, and for the same key the last one in arrival order wins. A malformed
|
|
27
|
+
* payload keeps the previous value, exactly like a malformed snapshot.
|
|
28
|
+
*
|
|
29
|
+
* A channel declared by a layout AND by a page produces one connection per
|
|
30
|
+
* module (each feeding its own module's signal); a single `emit` by channel
|
|
31
|
+
* name reaches both, because the server groups connections by channel and
|
|
32
|
+
* partition, not by module.
|
|
33
|
+
*
|
|
34
|
+
* `freshness` is aggregated per page: every connection closed → `"stale"`; a
|
|
35
|
+
* failed loader re-execution → `"error"`; otherwise `"live"`. A page with no
|
|
36
|
+
* channels stays `"live"` for its whole life, and a static build never opens a
|
|
37
|
+
* connection at all, so its freshness is constant `"live"` too.
|
|
38
|
+
*
|
|
39
|
+
* The per-channel refinement: `freshnessByChannel` is a signal record keyed by
|
|
40
|
+
* the channel names the module declares, so a page with two channels — one
|
|
41
|
+
* fallen, one following — shows both states at once. The aggregate folds it by
|
|
42
|
+
* the same rule: a silenced or closed channel counts as not-open.
|
|
43
|
+
*
|
|
44
|
+
* The silence detector is the client half of the idle timeout: a connection
|
|
45
|
+
* open but without deliveries for `CHANNEL_SILENCE_MS` (60s — a constant, not
|
|
46
|
+
* a configuration) is marked `"stale"` even though `EventSource` still reads
|
|
47
|
+
* `OPEN` — the tab in the background, the sleeping notebook, the proxy that
|
|
48
|
+
* stopped flushing without closing. Any delivery (or a reconnect's snapshot)
|
|
49
|
+
* brings the channel back to `"live"`. The server's heartbeat comment never
|
|
50
|
+
* generates a client event, so it cannot mask the silence.
|
|
51
|
+
*/
|
|
52
|
+
import { type Signal } from "@preact/signals";
|
|
53
|
+
import type { ComponentChildren } from "preact";
|
|
54
|
+
/** Freshness of the page's live data, as data for CSS to react to. */
|
|
55
|
+
export type Freshness = "live" | "stale" | "error";
|
|
56
|
+
/**
|
|
57
|
+
* How long an open connection may stay without a delivery (snapshot or slice)
|
|
58
|
+
* before the client calls it stale — the detector of the connection that a
|
|
59
|
+
* proxy silenced without closing. A runtime constant, documented, not a
|
|
60
|
+
* configuration: a channel goes stale on the wire's silence, not on the
|
|
61
|
+
* heartbeat (a comment generates no client event).
|
|
62
|
+
*/
|
|
63
|
+
export declare const CHANNEL_SILENCE_MS = 60000;
|
|
64
|
+
/**
|
|
65
|
+
* The per-channel freshness signal of one module — what
|
|
66
|
+
* `useLoader()`/`Loader()` return as `freshnessByChannel`. Created empty: the
|
|
67
|
+
* keys appear as the module's connections open, the values follow them.
|
|
68
|
+
*/
|
|
69
|
+
export declare function moduleChannelFreshness(moduleId: string): Signal<Record<string, Freshness>>;
|
|
70
|
+
/** The page-level freshness signal shared by every loader handle. */
|
|
71
|
+
export declare function loaderFreshness(): Signal<Freshness>;
|
|
72
|
+
/**
|
|
73
|
+
* Opens the SSE connection of one (module, channel) pair and feeds the module's
|
|
74
|
+
* loader-store entry with every snapshot and slice it receives. Returns the
|
|
75
|
+
* closer — the channel lives exactly as long as the module that declares it is
|
|
76
|
+
* mounted.
|
|
77
|
+
*
|
|
78
|
+
* `routeParams` are the params the module's route declares, extracted from the
|
|
79
|
+
* current URL: the client **declares** the address it is already seeing (the
|
|
80
|
+
* same information `?_data=1` carries on every SPA navigation), serialized in
|
|
81
|
+
* the `?_route=` query param of the connection. It is not free input — the
|
|
82
|
+
* server validates the keys against the module's path and rejects what the path
|
|
83
|
+
* does not declare.
|
|
84
|
+
*
|
|
85
|
+
* Inert (no connection, no freshness change) in a static build: there is no
|
|
86
|
+
* server to speak SSE.
|
|
87
|
+
*/
|
|
88
|
+
export declare function connectChannel(moduleId: string, channel: string, routeParams?: Record<string, string> | undefined): () => void;
|
|
89
|
+
/**
|
|
90
|
+
* The params the module's route declares, extracted from a pathname. The
|
|
91
|
+
* module's `fullPath` matches as a **prefix** of the pathname: a layout
|
|
92
|
+
* declared at `/projeto/:id` covers `/projeto/7/sala` and extracts `id=7` —
|
|
93
|
+
* the child's suffix takes no part in the extraction. A path with a catch-all
|
|
94
|
+
* (`/docs/*`) declares no `:` key and yields the empty object, so the channel
|
|
95
|
+
* travels by principal.
|
|
96
|
+
*/
|
|
97
|
+
export declare function channelRouteParams(fullPath: string | undefined, pathname: string): Record<string, string>;
|
|
98
|
+
/**
|
|
99
|
+
* Mounts the live channels of one route module. It renders its children
|
|
100
|
+
* unchanged (no DOM node, so SSR output and hydration are untouched) and only
|
|
101
|
+
* exists to tie the connection lifecycle to the module's own mount/unmount —
|
|
102
|
+
* SPA navigation and page close close the connections.
|
|
103
|
+
*
|
|
104
|
+
* The connection is **keyed by the params the module's route declares**, not
|
|
105
|
+
* by the pathname: navigating between children of the same params
|
|
106
|
+
* (`/projeto/7` → `/projeto/7/sala`) does not reconnect (the child's suffix is
|
|
107
|
+
* not part of the connection's identity), while a move to another resource
|
|
108
|
+
* (`/projeto/7` → `/projeto/9`) closes the old connection and opens a new one —
|
|
109
|
+
* with a fresh snapshot on connect, mirroring the loader store, which refetches
|
|
110
|
+
* when its own route's params move.
|
|
111
|
+
*/
|
|
112
|
+
export declare function ChannelBoundary({ moduleId, channels, fullPath, children, }: {
|
|
113
|
+
moduleId?: string | undefined;
|
|
114
|
+
channels?: readonly string[] | undefined;
|
|
115
|
+
/** The module's declared route path (`metadata.fullPath`) — its `:params`. */
|
|
116
|
+
fullPath?: string | undefined;
|
|
117
|
+
children?: ComponentChildren;
|
|
118
|
+
}): ComponentChildren;
|
|
119
|
+
/** Test-only. Drops every connection and resets every freshness signal. */
|
|
120
|
+
export declare function __resetLiveLoader(): void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { signal as v } from "@preact/signals";
|
|
2
|
+
import { useEffect as m } from "preact/hooks";
|
|
3
|
+
import { useLocation as g } from "wouter-preact";
|
|
4
|
+
import { extractParams as _, loaderEntry as S } from "./loader-store.js";
|
|
5
|
+
const L = 6e4, C = "snapshot", w = "slice", f = /* @__PURE__ */ new Set(), p = v("live"), y = /* @__PURE__ */ new Map();
|
|
6
|
+
function R(e) {
|
|
7
|
+
let n = y.get(e);
|
|
8
|
+
return n || (n = v({}), y.set(e, n)), n;
|
|
9
|
+
}
|
|
10
|
+
function V() {
|
|
11
|
+
return p;
|
|
12
|
+
}
|
|
13
|
+
const u = /* @__PURE__ */ new Map();
|
|
14
|
+
let h = !1;
|
|
15
|
+
function N(e, n) {
|
|
16
|
+
let o = u.get(e);
|
|
17
|
+
o || (o = [], u.set(e, o)), o.push(n), !h && (h = !0, queueMicrotask(E));
|
|
18
|
+
}
|
|
19
|
+
function E() {
|
|
20
|
+
if (h = !1, u.size === 0) return;
|
|
21
|
+
const e = [...u];
|
|
22
|
+
u.clear();
|
|
23
|
+
for (const [n, o] of e) {
|
|
24
|
+
const s = S(n);
|
|
25
|
+
let t = s.value;
|
|
26
|
+
for (const l of o)
|
|
27
|
+
t = { ...t !== null && typeof t == "object" && !Array.isArray(t) ? t : {}, ...l };
|
|
28
|
+
s.value = t;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function T(e) {
|
|
32
|
+
if (typeof e != "string" || e.length === 0) return null;
|
|
33
|
+
try {
|
|
34
|
+
const n = JSON.parse(e);
|
|
35
|
+
return n === null || typeof n != "object" || Array.isArray(n) ? null : n;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function O(e) {
|
|
41
|
+
return e.some((n) => n.failed) ? "error" : e.some((n) => n.state === "open" && !n.silent) ? "live" : "stale";
|
|
42
|
+
}
|
|
43
|
+
function a() {
|
|
44
|
+
let e = !1, n = !1;
|
|
45
|
+
for (const o of f)
|
|
46
|
+
o.failed && (e = !0), o.state === "open" && !o.silent && (n = !0);
|
|
47
|
+
e ? p.value = "error" : f.size > 0 && !n ? p.value = "stale" : p.value = "live";
|
|
48
|
+
for (const [o, s] of y) {
|
|
49
|
+
const t = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const i of f) {
|
|
51
|
+
if (i.moduleId !== o) continue;
|
|
52
|
+
const r = t.get(i.channel);
|
|
53
|
+
r ? r.push(i) : t.set(i.channel, [i]);
|
|
54
|
+
}
|
|
55
|
+
const l = {};
|
|
56
|
+
for (const [i, r] of t)
|
|
57
|
+
l[i] = O(r);
|
|
58
|
+
s.value = l;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function A(e, n, o) {
|
|
62
|
+
if (typeof __VELO_STATIC__ < "u" && __VELO_STATIC__) return () => {
|
|
63
|
+
};
|
|
64
|
+
if (typeof window > "u" || typeof EventSource > "u")
|
|
65
|
+
return () => {
|
|
66
|
+
};
|
|
67
|
+
const s = {
|
|
68
|
+
state: "open",
|
|
69
|
+
failed: !1,
|
|
70
|
+
silent: !1,
|
|
71
|
+
moduleId: e,
|
|
72
|
+
channel: n
|
|
73
|
+
};
|
|
74
|
+
f.add(s), a();
|
|
75
|
+
let t = null;
|
|
76
|
+
const l = () => {
|
|
77
|
+
t !== null && clearTimeout(t), t = setTimeout(() => {
|
|
78
|
+
t = null, s.silent = !0, a();
|
|
79
|
+
}, L);
|
|
80
|
+
};
|
|
81
|
+
l();
|
|
82
|
+
const i = o && Object.keys(o).length > 0 ? `?_route=${encodeURIComponent(JSON.stringify(o))}` : "", r = new EventSource(
|
|
83
|
+
`/_channel/${encodeURI(e)}/${encodeURIComponent(n)}${i}`
|
|
84
|
+
);
|
|
85
|
+
return r.addEventListener(C, (c) => {
|
|
86
|
+
try {
|
|
87
|
+
E(), S(e).value = JSON.parse(c.data), s.state = "open", s.failed = !1, s.silent = !1, l(), a();
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}), r.addEventListener(w, (c) => {
|
|
91
|
+
const d = T(c.data);
|
|
92
|
+
d && (s.state = "open", s.failed = !1, s.silent = !1, l(), a(), N(e, d));
|
|
93
|
+
}), r.addEventListener("error", (c) => {
|
|
94
|
+
const d = c.data;
|
|
95
|
+
typeof d == "string" && d.length > 0 ? s.failed = !0 : s.state = r.readyState === EventSource.CLOSED ? "closed" : "reconnecting", a();
|
|
96
|
+
}), r.addEventListener("close", () => {
|
|
97
|
+
s.state = "closed", a();
|
|
98
|
+
}), () => {
|
|
99
|
+
f.delete(s), t !== null && clearTimeout(t);
|
|
100
|
+
try {
|
|
101
|
+
r.close();
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
a();
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function b(e, n) {
|
|
108
|
+
return e ? _(e, n) ?? {} : {};
|
|
109
|
+
}
|
|
110
|
+
function $({
|
|
111
|
+
moduleId: e,
|
|
112
|
+
channels: n,
|
|
113
|
+
fullPath: o,
|
|
114
|
+
children: s
|
|
115
|
+
}) {
|
|
116
|
+
const t = n && n.length > 0 ? n.join(",") : "";
|
|
117
|
+
g();
|
|
118
|
+
const l = t === "" || typeof window > "u" ? "" : JSON.stringify(b(o, window.location.pathname));
|
|
119
|
+
return m(() => {
|
|
120
|
+
if (!e || t === "") return;
|
|
121
|
+
const i = l === "" ? void 0 : JSON.parse(l), r = t.split(",").map(
|
|
122
|
+
(c) => A(e, c, i)
|
|
123
|
+
);
|
|
124
|
+
return () => {
|
|
125
|
+
for (const c of r) c();
|
|
126
|
+
};
|
|
127
|
+
}, [e, t, l]), s;
|
|
128
|
+
}
|
|
129
|
+
function k() {
|
|
130
|
+
f.clear(), u.clear(), h = !1, p.value = "live";
|
|
131
|
+
for (const e of y.values()) e.value = {};
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
L as CHANNEL_SILENCE_MS,
|
|
135
|
+
$ as ChannelBoundary,
|
|
136
|
+
k as __resetLiveLoader,
|
|
137
|
+
b as channelRouteParams,
|
|
138
|
+
A as connectChannel,
|
|
139
|
+
V as loaderFreshness,
|
|
140
|
+
R as moduleChannelFreshness
|
|
141
|
+
};
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import type { AppRoutes } from "./types.js";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
export { emit, inspectChannels, registerChannels } from "./channels.js";
|
|
5
|
+
export type { ChannelDefinition, ChannelMap, ChannelScopeContext, ChannelGroupOpenContext, ChannelGroupCloseContext, ChannelScopeResult, ChannelRegistryOptions, ChannelEmitModule, ChannelEmitKind, ChannelSlice, ChannelInspectGroup, ChannelInspectChannel, ChannelInspectWindow, ChannelInspectReport, } from "./channels.js";
|
|
4
6
|
export declare const serverDataStorage: AsyncLocalStorage<Record<string, unknown>>;
|
|
5
7
|
type ServerCallback = (server: import("http").Server) => void;
|
|
6
8
|
export declare function onServer(fn: ServerCallback): void;
|