@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/channels.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { DEFAULT_HEARTBEAT_MS as J } from "./events.js";
|
|
2
|
+
const U = "snapshot", V = "slice", ne = "error", oe = "close", H = 50, W = 3e5;
|
|
3
|
+
let d = {}, O = H, M = W, N = J, F = !0;
|
|
4
|
+
function ye(e, t) {
|
|
5
|
+
d = e ? { ...e } : {}, O = t?.coalesceMs ?? H, M = t?.idleMs ?? W, N = t?.heartbeatMs ?? J, F = t?.logEmits ?? !0, Q(), E.clear();
|
|
6
|
+
}
|
|
7
|
+
function we() {
|
|
8
|
+
return d;
|
|
9
|
+
}
|
|
10
|
+
function ge() {
|
|
11
|
+
return Object.keys(d);
|
|
12
|
+
}
|
|
13
|
+
const f = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new Map(), $ = /* @__PURE__ */ new Map();
|
|
14
|
+
function se(e, t) {
|
|
15
|
+
const r = P(e, t);
|
|
16
|
+
if ($.has(r)) return;
|
|
17
|
+
const n = setTimeout(() => {
|
|
18
|
+
$.delete(r), Y(e, t);
|
|
19
|
+
}, O), o = n.unref;
|
|
20
|
+
typeof o == "function" && o.call(n), $.set(r, { timer: n, channel: e, partition: t });
|
|
21
|
+
}
|
|
22
|
+
function Q() {
|
|
23
|
+
for (const e of $.values()) clearTimeout(e.timer);
|
|
24
|
+
$.clear();
|
|
25
|
+
}
|
|
26
|
+
function ve() {
|
|
27
|
+
const e = [];
|
|
28
|
+
let t = 0;
|
|
29
|
+
for (const [n, o] of f) {
|
|
30
|
+
const s = [];
|
|
31
|
+
for (const [l, c] of o) {
|
|
32
|
+
const p = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const a of c) {
|
|
34
|
+
const u = p.get(a.moduleId) ?? { count: 0, last: void 0 };
|
|
35
|
+
u.count += 1, a.lastDeliveryAt !== void 0 && (u.last === void 0 || a.lastDeliveryAt > u.last) && (u.last = a.lastDeliveryAt), p.set(a.moduleId, u);
|
|
36
|
+
}
|
|
37
|
+
for (const [a, u] of p)
|
|
38
|
+
s.push({
|
|
39
|
+
moduleId: a,
|
|
40
|
+
partition: l,
|
|
41
|
+
connections: u.count,
|
|
42
|
+
lastDeliveryAt: u.last === void 0 ? null : new Date(u.last).toISOString()
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
e.push({
|
|
46
|
+
channel: n,
|
|
47
|
+
groups: s,
|
|
48
|
+
connections: q(o)
|
|
49
|
+
}), t += q(o);
|
|
50
|
+
}
|
|
51
|
+
const r = [];
|
|
52
|
+
for (const n of $.values())
|
|
53
|
+
r.push({ channel: n.channel, partition: n.partition });
|
|
54
|
+
return { channels: e, openCoalesceWindows: r, totalConnections: t };
|
|
55
|
+
}
|
|
56
|
+
function q(e) {
|
|
57
|
+
let t = 0;
|
|
58
|
+
for (const r of e.values()) t += r.size;
|
|
59
|
+
return t;
|
|
60
|
+
}
|
|
61
|
+
function P(e, t) {
|
|
62
|
+
return `${e}\0${t}`;
|
|
63
|
+
}
|
|
64
|
+
function ie(e) {
|
|
65
|
+
let t = f.get(e.channel);
|
|
66
|
+
t || (t = /* @__PURE__ */ new Map(), f.set(e.channel, t));
|
|
67
|
+
let r = t.get(e.partition);
|
|
68
|
+
r || (r = /* @__PURE__ */ new Set(), t.set(e.partition, r));
|
|
69
|
+
const n = r.size === 0;
|
|
70
|
+
return r.add(e), n;
|
|
71
|
+
}
|
|
72
|
+
function x(e) {
|
|
73
|
+
const t = f.get(e.channel), r = t?.get(e.partition);
|
|
74
|
+
if (!t || !r) return !1;
|
|
75
|
+
r.delete(e);
|
|
76
|
+
const n = r.size === 0;
|
|
77
|
+
return n && t.delete(e.partition), t.size === 0 && f.delete(e.channel), n;
|
|
78
|
+
}
|
|
79
|
+
function B(e, t, r) {
|
|
80
|
+
const n = P(e, t), o = d[e]?.onGroupOpen, s = (async () => {
|
|
81
|
+
if (typeof o == "function")
|
|
82
|
+
try {
|
|
83
|
+
await o({ partition: t, params: r });
|
|
84
|
+
} catch (l) {
|
|
85
|
+
console.error(
|
|
86
|
+
`[velojs] channel "${e}" onGroupOpen failed for partition "${t}":`,
|
|
87
|
+
l
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
return E.set(n, s), s.then(() => {
|
|
92
|
+
E.get(n) === s && E.delete(n);
|
|
93
|
+
}), s;
|
|
94
|
+
}
|
|
95
|
+
async function ae(e) {
|
|
96
|
+
const t = P(e.channel, e.partition), r = E.get(t);
|
|
97
|
+
r && await r;
|
|
98
|
+
const n = f.get(e.channel)?.get(e.partition);
|
|
99
|
+
if (!n || !n.has(e) || n.size > 1) {
|
|
100
|
+
x(e);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const o = d[e.channel]?.onGroupClose;
|
|
104
|
+
if (typeof o == "function")
|
|
105
|
+
try {
|
|
106
|
+
await o({ partition: e.partition });
|
|
107
|
+
} catch (c) {
|
|
108
|
+
console.error(
|
|
109
|
+
`[velojs] channel "${e.channel}" onGroupClose failed for partition "${e.partition}":`,
|
|
110
|
+
c
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
x(e);
|
|
114
|
+
const s = f.get(e.channel)?.get(e.partition), l = s ? [...s][0] : void 0;
|
|
115
|
+
l && await B(e.channel, e.partition, l.params);
|
|
116
|
+
}
|
|
117
|
+
function Ee() {
|
|
118
|
+
f.clear(), E.clear(), Q();
|
|
119
|
+
}
|
|
120
|
+
async function T(e, t) {
|
|
121
|
+
const r = e.scope;
|
|
122
|
+
return typeof r != "function" ? "all" : await r(t);
|
|
123
|
+
}
|
|
124
|
+
function le(e, t) {
|
|
125
|
+
return e.get(t);
|
|
126
|
+
}
|
|
127
|
+
async function K(e, t) {
|
|
128
|
+
const r = f.get(e)?.get(t);
|
|
129
|
+
if (!r || r.size === 0) return null;
|
|
130
|
+
const n = d[e], o = [];
|
|
131
|
+
for (const s of [...r]) {
|
|
132
|
+
let l;
|
|
133
|
+
try {
|
|
134
|
+
l = await T(n, {
|
|
135
|
+
user: s.principal,
|
|
136
|
+
params: s.params
|
|
137
|
+
});
|
|
138
|
+
} catch (c) {
|
|
139
|
+
console.error(`[velojs] channel "${e}" scope threw on emit:`, c), l = null;
|
|
140
|
+
}
|
|
141
|
+
if (l == null || l !== t) {
|
|
142
|
+
await s.terminate();
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
o.push(s);
|
|
146
|
+
}
|
|
147
|
+
return o.length > 0 ? o : null;
|
|
148
|
+
}
|
|
149
|
+
async function Y(e, t) {
|
|
150
|
+
const r = await K(e, t);
|
|
151
|
+
r && await Promise.all(r.map((n) => X(n)));
|
|
152
|
+
}
|
|
153
|
+
async function X(e) {
|
|
154
|
+
let t;
|
|
155
|
+
try {
|
|
156
|
+
t = await e.loader({
|
|
157
|
+
params: e.params,
|
|
158
|
+
query: e.query,
|
|
159
|
+
c: e.c
|
|
160
|
+
});
|
|
161
|
+
} catch (r) {
|
|
162
|
+
await e.send(
|
|
163
|
+
ne,
|
|
164
|
+
JSON.stringify({
|
|
165
|
+
message: r instanceof Error ? r.message : String(r)
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
t instanceof Response || await e.send(U, JSON.stringify(t ?? null));
|
|
171
|
+
}
|
|
172
|
+
function $e(e, t, r, n) {
|
|
173
|
+
return typeof e == "string" ? ue(e, t ?? {}) : ce(e, t, r, n);
|
|
174
|
+
}
|
|
175
|
+
async function ue(e, t) {
|
|
176
|
+
const r = d[e];
|
|
177
|
+
if (!r)
|
|
178
|
+
throw new Error(
|
|
179
|
+
`[velojs] emit("${e}"): this channel has no entry in app/channels.ts. Add one — a typo or a renamed channel would otherwise be a silent no-op.`
|
|
180
|
+
);
|
|
181
|
+
const n = await T(r, t), o = n == null ? 0 : f.get(e)?.get(n)?.size ?? 0;
|
|
182
|
+
if (Z(e, n, "invalidate", o), n != null && o !== 0) {
|
|
183
|
+
if (O <= 0) {
|
|
184
|
+
await Y(e, n);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
se(e, n);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function ce(e, t, r, n) {
|
|
191
|
+
const o = e?.metadata?.moduleId;
|
|
192
|
+
if (!o)
|
|
193
|
+
throw new Error(
|
|
194
|
+
`[velojs] emit(module, "${t}", …): the module has no metadata.moduleId — pass the imported route module (the Vite plugin injects its metadata), the same object the route tree uses.`
|
|
195
|
+
);
|
|
196
|
+
const s = e.channels;
|
|
197
|
+
if (!Array.isArray(s) || !s.includes(t))
|
|
198
|
+
throw new Error(
|
|
199
|
+
`[velojs] emit(module, "${t}", …): module "${o}" does not declare channel "${t}" — add it to the module's \`channels\` export, or use emit("${t}", ctx) for the invalidation mode.`
|
|
200
|
+
);
|
|
201
|
+
if (typeof e.loader != "function")
|
|
202
|
+
throw new Error(
|
|
203
|
+
`[velojs] emit(module, "${t}", …): module "${o}" has no \`loader\` — there is no data shape to slice.`
|
|
204
|
+
);
|
|
205
|
+
const l = d[t];
|
|
206
|
+
if (!l)
|
|
207
|
+
throw new Error(
|
|
208
|
+
`[velojs] emit(module, "${t}", …): this channel has no entry in app/channels.ts. Add one — a typo or a renamed channel would otherwise be a silent no-op.`
|
|
209
|
+
);
|
|
210
|
+
if (n === null || typeof n != "object" || Array.isArray(n))
|
|
211
|
+
throw new Error(
|
|
212
|
+
`[velojs] emit(module, "${t}", …): the slice must be the object of changed keys — got ${n === null ? "null" : Array.isArray(n) ? "an array" : typeof n}.`
|
|
213
|
+
);
|
|
214
|
+
const c = await T(l, r), p = c == null ? void 0 : f.get(t)?.get(c), a = p?.size ?? 0;
|
|
215
|
+
if (Z(t, c, "slice", a), c == null || !p || a === 0) return;
|
|
216
|
+
const u = await K(t, c);
|
|
217
|
+
if (!u) return;
|
|
218
|
+
const w = u.filter((g) => g.moduleId === o);
|
|
219
|
+
if (w.length === 0) return;
|
|
220
|
+
const S = JSON.stringify(n);
|
|
221
|
+
await Promise.all(w.map((g) => g.send(V, S)));
|
|
222
|
+
}
|
|
223
|
+
function Z(e, t, r, n) {
|
|
224
|
+
if (!F) return;
|
|
225
|
+
const o = typeof t == "string" ? `"${t}"` : "null";
|
|
226
|
+
console.log(
|
|
227
|
+
`[velojs] emit kind=${r} channel="${e}" partition=${o} connections=${n} at=${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
function fe(e, t) {
|
|
231
|
+
let r = Promise.resolve();
|
|
232
|
+
return (n) => (r = r.then(() => t(n)).then(() => {
|
|
233
|
+
}).catch((o) => {
|
|
234
|
+
console.warn(
|
|
235
|
+
`[velojs] channel "${e}": SSE write failed — the snapshot did not reach the client:`,
|
|
236
|
+
o
|
|
237
|
+
);
|
|
238
|
+
}), r);
|
|
239
|
+
}
|
|
240
|
+
const C = "_route";
|
|
241
|
+
function de(e) {
|
|
242
|
+
return e ? e.split("/").filter((t) => t.startsWith(":")).map((t) => t.slice(1)) : [];
|
|
243
|
+
}
|
|
244
|
+
function pe(e, t) {
|
|
245
|
+
if (e == null || e === "")
|
|
246
|
+
return { params: {}, error: null };
|
|
247
|
+
let r;
|
|
248
|
+
try {
|
|
249
|
+
r = JSON.parse(e);
|
|
250
|
+
} catch {
|
|
251
|
+
return { params: {}, error: `the ?${C}= payload is not valid JSON` };
|
|
252
|
+
}
|
|
253
|
+
if (r === null || typeof r != "object" || Array.isArray(r))
|
|
254
|
+
return {
|
|
255
|
+
params: {},
|
|
256
|
+
error: `the ?${C}= payload must be an object of route params`
|
|
257
|
+
};
|
|
258
|
+
const n = {};
|
|
259
|
+
for (const [o, s] of Object.entries(r)) {
|
|
260
|
+
if (!t.includes(o)) {
|
|
261
|
+
const l = t.length > 0 ? t.join(", ") : "none";
|
|
262
|
+
return {
|
|
263
|
+
params: {},
|
|
264
|
+
error: `route param "${o}" is not declared by the module's path (declared: ${l})`
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
if (typeof s != "string")
|
|
268
|
+
return { params: {}, error: `route param "${o}" must be a URL string` };
|
|
269
|
+
n[o] = s;
|
|
270
|
+
}
|
|
271
|
+
return { params: n, error: null };
|
|
272
|
+
}
|
|
273
|
+
function Se(e, t, r, n, o, s) {
|
|
274
|
+
if (typeof n.loader != "function")
|
|
275
|
+
throw new Error(
|
|
276
|
+
`[velojs] module "${o}" declares \`channels\` without a \`loader\` — there is nothing to synchronize. Add a loader or remove the export.`
|
|
277
|
+
);
|
|
278
|
+
if (!d[r])
|
|
279
|
+
throw new Error(
|
|
280
|
+
`[velojs] module "${o}" declares channel "${r}" with no entry in app/channels.ts — add the partition resolver for "${r}".`
|
|
281
|
+
);
|
|
282
|
+
const l = n.loader, c = de(n.metadata?.fullPath), p = async (a) => {
|
|
283
|
+
const u = d[r], w = pe(
|
|
284
|
+
a.req.query(C),
|
|
285
|
+
c
|
|
286
|
+
);
|
|
287
|
+
if (w.error)
|
|
288
|
+
return console.error(`[velojs] channel "${r}": rejecting subscription — ${w.error}`), a.json({ error: "invalid-route-params", message: w.error }, 400);
|
|
289
|
+
const S = w.params, g = le(a, "user");
|
|
290
|
+
let b;
|
|
291
|
+
try {
|
|
292
|
+
b = await T(u, { user: g, params: S });
|
|
293
|
+
} catch (h) {
|
|
294
|
+
return console.error(`[velojs] channel "${r}" scope threw:`, h), a.json({ error: "forbidden" }, 403);
|
|
295
|
+
}
|
|
296
|
+
if (b == null)
|
|
297
|
+
return a.json({ error: "forbidden" }, 403);
|
|
298
|
+
const { streamSSE: ee } = await import("hono/streaming"), D = {};
|
|
299
|
+
for (const [h, m] of Object.entries(a.req.query()))
|
|
300
|
+
h !== C && (D[h] = m);
|
|
301
|
+
const A = await ee(a, async (h) => {
|
|
302
|
+
let m = !1, R = () => {
|
|
303
|
+
};
|
|
304
|
+
const te = new Promise((i) => {
|
|
305
|
+
R = i;
|
|
306
|
+
}), _ = fe(
|
|
307
|
+
r,
|
|
308
|
+
(i) => typeof i == "string" ? h.write(i) : h.writeSSE(i)
|
|
309
|
+
), j = {
|
|
310
|
+
channel: r,
|
|
311
|
+
partition: b,
|
|
312
|
+
moduleId: o,
|
|
313
|
+
principal: g,
|
|
314
|
+
loader: l,
|
|
315
|
+
c: a,
|
|
316
|
+
params: S,
|
|
317
|
+
query: D,
|
|
318
|
+
send: (i, G) => m ? Promise.resolve() : i === U || i === V ? _({ event: i, data: G }).then(() => {
|
|
319
|
+
m || (j.lastDeliveryAt = Date.now(), L());
|
|
320
|
+
}) : _({ event: i, data: G }),
|
|
321
|
+
terminate: (i) => z(i)
|
|
322
|
+
}, k = () => {
|
|
323
|
+
m || (m = !0, y !== null && clearTimeout(y), v !== null && clearInterval(v), R(), ae(j));
|
|
324
|
+
}, z = async (i = "partition") => {
|
|
325
|
+
m || (await j.send(oe, JSON.stringify({ reason: i })), k());
|
|
326
|
+
};
|
|
327
|
+
let y = null;
|
|
328
|
+
const L = () => {
|
|
329
|
+
if (M <= 0 || m) return;
|
|
330
|
+
y !== null && clearTimeout(y), y = setTimeout(() => {
|
|
331
|
+
y = null, z("idle");
|
|
332
|
+
}, M);
|
|
333
|
+
const i = y.unref;
|
|
334
|
+
typeof i == "function" && i.call(y);
|
|
335
|
+
};
|
|
336
|
+
let v = null;
|
|
337
|
+
N > 0 && (v = setInterval(() => {
|
|
338
|
+
m || _(`: ping
|
|
339
|
+
|
|
340
|
+
`);
|
|
341
|
+
}, N));
|
|
342
|
+
const re = ie(j);
|
|
343
|
+
if (h.onAbort(k), L(), v !== null) {
|
|
344
|
+
const i = v.unref;
|
|
345
|
+
typeof i == "function" && i.call(v);
|
|
346
|
+
}
|
|
347
|
+
re && await B(r, b, S), await X(j), await te;
|
|
348
|
+
}), I = new Headers(A.headers);
|
|
349
|
+
return I.set("Cache-Control", "no-store"), new Response(A.body, {
|
|
350
|
+
status: A.status,
|
|
351
|
+
statusText: A.statusText,
|
|
352
|
+
headers: I
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
s.length > 0 ? e.on(["GET"], [t], ...s, p) : e.on(["GET"], [t], p);
|
|
356
|
+
}
|
|
357
|
+
export {
|
|
358
|
+
oe as CHANNEL_CLOSE_EVENT,
|
|
359
|
+
ne as CHANNEL_ERROR_EVENT,
|
|
360
|
+
H as DEFAULT_COALESCE_MS,
|
|
361
|
+
W as DEFAULT_IDLE_MS,
|
|
362
|
+
C as ROUTE_PARAMS_QUERY,
|
|
363
|
+
V as SLICE_EVENT,
|
|
364
|
+
U as SNAPSHOT_EVENT,
|
|
365
|
+
Ee as __resetChannels,
|
|
366
|
+
ge as channelNames,
|
|
367
|
+
fe as createChannelWriteChain,
|
|
368
|
+
de as declaredRouteParams,
|
|
369
|
+
$e as emit,
|
|
370
|
+
we as getChannelMap,
|
|
371
|
+
ve as inspectChannels,
|
|
372
|
+
pe as parseRouteParamsQuery,
|
|
373
|
+
X as pushSnapshot,
|
|
374
|
+
Se as registerChannelRoute,
|
|
375
|
+
ye as registerChannels
|
|
376
|
+
};
|
package/dist/client.js
CHANGED
|
@@ -1,92 +1,103 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { hydrate as
|
|
3
|
-
import { Router as
|
|
4
|
-
import { useEffect as
|
|
1
|
+
import { jsxs as w, jsx as r } from "preact/jsx-runtime";
|
|
2
|
+
import { hydrate as x } from "preact";
|
|
3
|
+
import { Router as S, Route as f, Switch as y, useLocation as b } from "wouter-preact";
|
|
4
|
+
import { useEffect as B } from "preact/hooks";
|
|
5
5
|
import { registerRoutePatterns as L, syncLocation as W } from "./loader-store.js";
|
|
6
|
-
|
|
6
|
+
import { ChannelBoundary as v } from "./live-loader.js";
|
|
7
|
+
const l = (t, n, e) => n ? e && n === "/" ? t || "/" : n.startsWith("/") ? t + n : t + "/" + n : t, E = (t) => t === "" ? "*" : `${t}/*?`, I = (t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), M = (t) => {
|
|
7
8
|
let n = !1;
|
|
8
9
|
t.endsWith("/*?") && (t = t.slice(0, -3), n = !0);
|
|
9
|
-
const e = t.split("/").map((c) => c.startsWith(":") ? "[^/]+" :
|
|
10
|
+
const e = t.split("/").map((c) => c.startsWith(":") ? "[^/]+" : I(c)).join("/");
|
|
10
11
|
return n ? `${e}(?:/.*)?` : e;
|
|
11
|
-
},
|
|
12
|
+
}, d = (t, n) => {
|
|
12
13
|
if (t.path === "*") return [];
|
|
13
14
|
if (!t.module) {
|
|
14
|
-
const c =
|
|
15
|
-
return (t.children ?? []).flatMap((i) =>
|
|
15
|
+
const c = l(n, t.path, !1);
|
|
16
|
+
return (t.children ?? []).flatMap((i) => d(i, c));
|
|
16
17
|
}
|
|
17
18
|
if (!t.children)
|
|
18
|
-
return [
|
|
19
|
+
return [l(n, t.path, !0) || "/"];
|
|
19
20
|
if (t.path)
|
|
20
|
-
return [`${
|
|
21
|
-
const e =
|
|
22
|
-
return (t.children ?? []).flatMap((c) =>
|
|
21
|
+
return [`${l(n, t.path, !1)}/*?`];
|
|
22
|
+
const e = l(n, t.path, !1);
|
|
23
|
+
return (t.children ?? []).flatMap((c) => d(c, e));
|
|
23
24
|
}, _ = (t, n) => {
|
|
24
|
-
const e =
|
|
25
|
+
const e = d(t, n);
|
|
25
26
|
return new RegExp(`^(?:${e.map(M).join("|")})$`);
|
|
26
|
-
},
|
|
27
|
+
}, C = (t) => {
|
|
27
28
|
for (const n of t) {
|
|
28
29
|
if (n.path === "*" && n.module) return n.module.Component;
|
|
29
30
|
if (n.children) {
|
|
30
|
-
const e =
|
|
31
|
+
const e = C(n.children);
|
|
31
32
|
if (e) return e;
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
return null;
|
|
35
|
-
},
|
|
36
|
+
}, R = (t, n) => n ? [...t, /* @__PURE__ */ r(f, { children: /* @__PURE__ */ r(n, {}) }, "__notfound")] : t, h = (t, n, e, c) => {
|
|
36
37
|
const i = [];
|
|
37
|
-
for (let
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
if (!
|
|
41
|
-
if (
|
|
42
|
-
const
|
|
43
|
-
i.push(...h(
|
|
38
|
+
for (let a = 0; a < t.length; a++) {
|
|
39
|
+
const o = t[a], s = `${c}${a}`;
|
|
40
|
+
if (o.path === "*") continue;
|
|
41
|
+
if (!o.module) {
|
|
42
|
+
if (o.children) {
|
|
43
|
+
const u = l(n, o.path, !1);
|
|
44
|
+
i.push(...h(o.children, u, e, `${s}-`));
|
|
44
45
|
}
|
|
45
46
|
continue;
|
|
46
47
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const p = o.module.Component, m = (u) => /* @__PURE__ */ r(
|
|
49
|
+
v,
|
|
50
|
+
{
|
|
51
|
+
moduleId: o.module?.metadata?.moduleId,
|
|
52
|
+
channels: o.module?.channels,
|
|
53
|
+
fullPath: o.module?.metadata?.fullPath,
|
|
54
|
+
children: u
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
if (!o.children) {
|
|
58
|
+
const u = l(n, o.path, !0) || "/";
|
|
50
59
|
i.push(
|
|
51
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ r(f, { path: u, children: m(/* @__PURE__ */ r(p, {})) }, s)
|
|
52
61
|
);
|
|
53
62
|
continue;
|
|
54
63
|
}
|
|
55
|
-
if (
|
|
64
|
+
if (o.isRoot) {
|
|
56
65
|
i.push(
|
|
57
|
-
/* @__PURE__ */
|
|
58
|
-
h(
|
|
66
|
+
/* @__PURE__ */ r(y, { children: R(
|
|
67
|
+
h(o.children, "", e, `${s}-`),
|
|
59
68
|
e
|
|
60
|
-
) },
|
|
69
|
+
) }, s)
|
|
61
70
|
);
|
|
62
71
|
continue;
|
|
63
72
|
}
|
|
64
|
-
const
|
|
73
|
+
const $ = l(n, o.path, !1), j = o.path ? E($) : _(o, n);
|
|
65
74
|
i.push(
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
/* @__PURE__ */ r(f, { path: j, children: m(
|
|
76
|
+
/* @__PURE__ */ r(p, { children: /* @__PURE__ */ r(y, { children: R(
|
|
77
|
+
h(o.children, $, e, `${s}-`),
|
|
78
|
+
e
|
|
79
|
+
) }) })
|
|
80
|
+
) }, s)
|
|
70
81
|
);
|
|
71
82
|
}
|
|
72
83
|
return i;
|
|
73
84
|
}, g = () => {
|
|
74
|
-
const [t] =
|
|
75
|
-
return
|
|
85
|
+
const [t] = b();
|
|
86
|
+
return B(() => {
|
|
76
87
|
W(window.location.pathname);
|
|
77
88
|
}, [t]), null;
|
|
78
89
|
}, k = ({ routes: t }) => {
|
|
79
90
|
L(t);
|
|
80
|
-
const n =
|
|
81
|
-
return /* @__PURE__ */
|
|
82
|
-
/* @__PURE__ */
|
|
91
|
+
const n = C(t), e = h(t, "", n, "");
|
|
92
|
+
return /* @__PURE__ */ w(S, { children: [
|
|
93
|
+
/* @__PURE__ */ r(g, {}),
|
|
83
94
|
e
|
|
84
95
|
] });
|
|
85
|
-
},
|
|
96
|
+
}, H = (t) => {
|
|
86
97
|
const { routes: n } = t, e = document.querySelector("body");
|
|
87
|
-
e &&
|
|
98
|
+
e && x(/* @__PURE__ */ r(k, { routes: n }), e);
|
|
88
99
|
};
|
|
89
100
|
export {
|
|
90
101
|
k as ClientRoutes,
|
|
91
|
-
|
|
102
|
+
H as startClient
|
|
92
103
|
};
|
package/dist/components.js
CHANGED
|
@@ -3,7 +3,8 @@ import { useRouter as L, Link as $ } from "wouter-preact";
|
|
|
3
3
|
import "@preact/signals";
|
|
4
4
|
import "preact/hooks";
|
|
5
5
|
import { __veloUpdatePending as C } from "./loader-store.js";
|
|
6
|
-
|
|
6
|
+
import "./live-loader.js";
|
|
7
|
+
function y({ basePath: t, favicon: e = "/favicon.ico" } = {}) {
|
|
7
8
|
const n = process.env.NODE_ENV !== "production";
|
|
8
9
|
t = t || process.env.STATIC_BASE_URL || (process.env.VELO_STATIC ? "/client" : "");
|
|
9
10
|
const s = e === !1 ? void 0 : e.endsWith(".svg") ? "image/svg+xml" : e.endsWith(".png") ? "image/png" : "image/x-icon", i = e !== !1 && /* @__PURE__ */ r("link", { rel: "icon", href: `${t}${e}`, type: s });
|
|
@@ -25,7 +26,7 @@ function S({ localStorage: t, cookies: e } = {}) {
|
|
|
25
26
|
const n = (c) => JSON.stringify(c).replace(/</g, "\\u003c"), s = t && t.length > 0 ? `var L=${n(t)};for(var i=0;i<L.length;i++){var v=localStorage.getItem(L[i]);if(v!==null)b[L[i]]=v;}` : "", i = e && e.length > 0 ? `var C=${n(e)};var s=document.cookie?document.cookie.split("; "):[];var m={};for(var j=0;j<s.length;j++){var p=s[j].indexOf("=");if(p>0){m[s[j].slice(0,p)]=decodeURIComponent(s[j].slice(p+1));}}for(var k=0;k<C.length;k++){if(C[k]in m)b[C[k]]=m[C[k]];}` : "";
|
|
26
27
|
return `(function(){var b={};try{${s}}catch(e){}try{${i}}catch(e){}if(Object.keys(b).length){window.__VELO_BOOT__=Object.assign(window.__VELO_BOOT__||{},b);var d=document.documentElement;for(var q in b){d.setAttribute("data-"+String(q).replace(/[^a-zA-Z0-9-]/g,"-"),b[q]);}}})();`;
|
|
27
28
|
}
|
|
28
|
-
function
|
|
29
|
+
function I(t) {
|
|
29
30
|
const e = S(t);
|
|
30
31
|
return e ? /* @__PURE__ */ r("script", { dangerouslySetInnerHTML: { __html: e } }) : null;
|
|
31
32
|
}
|
|
@@ -35,7 +36,7 @@ function m(t, e) {
|
|
|
35
36
|
n = n.replace(`:${s}`, i);
|
|
36
37
|
return n;
|
|
37
38
|
}
|
|
38
|
-
function
|
|
39
|
+
function V({ to: t, params: e, search: n, absolute: s, ...i }) {
|
|
39
40
|
const c = typeof t != "string", l = L(), _ = c ? t.metadata?.fullPath ?? "/" : t, f = e ? m(_, e) : _, u = n ? `?${new URLSearchParams(n).toString()}` : "", v = `${f}${u}`;
|
|
40
41
|
if (typeof window < "u" && C.value) {
|
|
41
42
|
let a;
|
|
@@ -61,9 +62,9 @@ function I({ to: t, params: e, search: n, absolute: s, ...i }) {
|
|
|
61
62
|
return /* @__PURE__ */ r($, { to: v, ...i });
|
|
62
63
|
}
|
|
63
64
|
export {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
I as Boot,
|
|
66
|
+
V as Link,
|
|
67
|
+
y as Scripts,
|
|
67
68
|
S as bootScript,
|
|
68
69
|
m as substituteParams
|
|
69
70
|
};
|
package/dist/events.d.ts
CHANGED
|
@@ -284,6 +284,12 @@ interface StreamListener<TEvent> {
|
|
|
284
284
|
(event: TEvent): void;
|
|
285
285
|
close(): void;
|
|
286
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* The house's single SSE transport heartbeat, in milliseconds — the same
|
|
289
|
+
* default the live-loader channels use. Exported so the two surfaces never
|
|
290
|
+
* diverge in operational expectation.
|
|
291
|
+
*/
|
|
292
|
+
export declare const DEFAULT_HEARTBEAT_MS = 20000;
|
|
287
293
|
export declare function flushPendingStreamRoutes(app: Hono): void;
|
|
288
294
|
/** @internal Used by `@mauroandre/velojs/testing` to reset all streams. */
|
|
289
295
|
export declare function getRegisteredStreams(): ReadonlySet<EventStream<unknown, unknown>>;
|
package/dist/events.js
CHANGED
package/dist/hooks.d.ts
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { type Signal } from "@preact/signals";
|
|
1
|
+
import { type ReadonlySignal, type Signal } from "@preact/signals";
|
|
2
2
|
import type { EventStream } from "./events.js";
|
|
3
3
|
import type { SocketHandler, SocketStub } from "./sockets.js";
|
|
4
4
|
import { __veloUpdatePending } from "./loader-store.js";
|
|
5
|
+
import { type Freshness } from "./live-loader.js";
|
|
6
|
+
export type { Freshness } from "./live-loader.js";
|
|
7
|
+
/**
|
|
8
|
+
* What `Loader()`/`useLoader()` return. `freshness` is the page-level state of
|
|
9
|
+
* the live loader: `"live"` (connected), `"stale"` (connection closed,
|
|
10
|
+
* reconnecting or silent) and `"error"` (the last loader re-execution failed).
|
|
11
|
+
* On a page with no `channels` it exists and stays `"live"`. The framework
|
|
12
|
+
* exposes it as data for CSS to react to — it renders no JSX of its own.
|
|
13
|
+
*
|
|
14
|
+
* `freshnessByChannel` is the same state with per-channel granularity, keyed
|
|
15
|
+
* by the channel names the module declares — a page with two channels, one
|
|
16
|
+
* fallen and one following, shows both states at once. Empty record when the
|
|
17
|
+
* module declares no channels.
|
|
18
|
+
*/
|
|
19
|
+
export interface LoaderHandle<T> {
|
|
20
|
+
data: Signal<T | null>;
|
|
21
|
+
loading: Signal<boolean>;
|
|
22
|
+
refetch: () => void;
|
|
23
|
+
freshness: Signal<Freshness>;
|
|
24
|
+
freshnessByChannel: ReadonlySignal<Record<string, Freshness>>;
|
|
25
|
+
}
|
|
5
26
|
export { __veloUpdatePending };
|
|
6
27
|
declare global {
|
|
7
28
|
interface Window {
|
|
@@ -58,11 +79,7 @@ export declare function touch<T>(sig: Signal<T | null>): void;
|
|
|
58
79
|
*
|
|
59
80
|
* @param moduleId - Injetado automaticamente pelo veloPlugin
|
|
60
81
|
*/
|
|
61
|
-
export declare function Loader<T>(moduleId?: string):
|
|
62
|
-
data: Signal<T | null>;
|
|
63
|
-
loading: Signal<boolean>;
|
|
64
|
-
refetch: () => void;
|
|
65
|
-
};
|
|
82
|
+
export declare function Loader<T>(moduleId?: string): LoaderHandle<T>;
|
|
66
83
|
/**
|
|
67
84
|
* Hook para acessar dados do loader dentro de componentes
|
|
68
85
|
* Suporta SSR hydration e navegação SPA (faz fetch se necessário)
|
|
@@ -82,21 +99,9 @@ export declare function Loader<T>(moduleId?: string): {
|
|
|
82
99
|
* @param moduleId - Injetado automaticamente pelo veloPlugin
|
|
83
100
|
* @param deps - Array de dependências (triggers re-fetch quando mudam)
|
|
84
101
|
*/
|
|
85
|
-
export declare function useLoader<T>():
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
refetch: () => void;
|
|
89
|
-
};
|
|
90
|
-
export declare function useLoader<T>(deps: any[]): {
|
|
91
|
-
data: Signal<T | null>;
|
|
92
|
-
loading: Signal<boolean>;
|
|
93
|
-
refetch: () => void;
|
|
94
|
-
};
|
|
95
|
-
export declare function useLoader<T>(moduleId: string, deps?: any[]): {
|
|
96
|
-
data: Signal<T | null>;
|
|
97
|
-
loading: Signal<boolean>;
|
|
98
|
-
refetch: () => void;
|
|
99
|
-
};
|
|
102
|
+
export declare function useLoader<T>(): LoaderHandle<T>;
|
|
103
|
+
export declare function useLoader<T>(deps: any[]): LoaderHandle<T>;
|
|
104
|
+
export declare function useLoader<T>(moduleId: string, deps?: any[]): LoaderHandle<T>;
|
|
100
105
|
/**
|
|
101
106
|
* Hook para navegação programática
|
|
102
107
|
*
|