@mauroandre/velojs 0.0.59 → 0.0.61
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 +182 -1
- package/agent/AGENTS.md +84 -0
- package/agent/skills/velojs-actions/SKILL.md +32 -0
- package/agent/skills/velojs-hooks/SKILL.md +26 -0
- package/agent/skills/velojs-live-actions/SKILL.md +211 -0
- package/agent/skills/velojs-telemetry/SKILL.md +6 -1
- package/agent/skills/velojs-type-reference/SKILL.md +1 -0
- package/agent/skills/velojs-vite-plugin/SKILL.md +51 -2
- package/dist/channels.d.ts +117 -11
- package/dist/channels.js +459 -284
- package/dist/config.d.ts +8 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +34 -32
- package/dist/leak-diagnostic.d.ts +77 -0
- package/dist/leak-diagnostic.js +14 -0
- package/dist/live-action.d.ts +86 -0
- package/dist/live-action.js +52 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +165 -143
- package/dist/telemetry-cli.d.ts +1 -0
- package/dist/telemetry-cli.js +116 -113
- package/dist/telemetry.d.ts +3 -3
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +500 -453
- package/dist/testing/subscription.d.ts +7 -0
- package/dist/testing/types.d.ts +43 -2
- package/dist/types.d.ts +12 -0
- package/dist/vite.d.ts +47 -1
- package/dist/vite.js +869 -401
- package/package.json +1 -1
package/dist/channels.js
CHANGED
|
@@ -1,431 +1,606 @@
|
|
|
1
|
-
import { DEFAULT_HEARTBEAT_MS as
|
|
2
|
-
import { trace as
|
|
3
|
-
const
|
|
4
|
-
let
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
function
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
function
|
|
12
|
-
return Object.keys(
|
|
13
|
-
}
|
|
14
|
-
const
|
|
15
|
-
function
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
1
|
+
import { DEFAULT_HEARTBEAT_MS as se } from "./events.js";
|
|
2
|
+
import { trace as F, byteLength as H, spanRecorder as ie } from "./telemetry.js";
|
|
3
|
+
const X = "snapshot", ae = "slice", Se = "error", le = "close", ce = 50, ue = 3e5;
|
|
4
|
+
let g = {}, Z = ce, L = ue, N = se, fe = !0;
|
|
5
|
+
function Pe(e, t) {
|
|
6
|
+
g = e ? { ...e } : {}, Z = t?.coalesceMs ?? ce, L = t?.idleMs ?? ue, N = t?.heartbeatMs ?? se, fe = t?.logEmits ?? !0, de(), O.clear(), G.clear();
|
|
7
|
+
}
|
|
8
|
+
function qe() {
|
|
9
|
+
return g;
|
|
10
|
+
}
|
|
11
|
+
function ze() {
|
|
12
|
+
return Object.keys(g);
|
|
13
|
+
}
|
|
14
|
+
const S = /* @__PURE__ */ new Map(), O = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Map();
|
|
15
|
+
function Ee(e, t, r) {
|
|
16
|
+
const n = ee(e, t), o = I.get(n);
|
|
17
|
+
if (o) {
|
|
18
|
+
r && o.gestures.push(r);
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
const s = {
|
|
22
22
|
timer: void 0,
|
|
23
23
|
channel: e,
|
|
24
24
|
partition: t,
|
|
25
|
-
gestures:
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
typeof
|
|
30
|
-
}
|
|
31
|
-
async function
|
|
32
|
-
const t =
|
|
33
|
-
for (const
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
function
|
|
37
|
-
for (const e of
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
function
|
|
25
|
+
gestures: r ? [r] : []
|
|
26
|
+
}, a = setTimeout(() => {
|
|
27
|
+
I.delete(n), Ae(s);
|
|
28
|
+
}, Z), u = a.unref;
|
|
29
|
+
typeof u == "function" && u.call(a), s.timer = a, I.set(n, s);
|
|
30
|
+
}
|
|
31
|
+
async function Ae(e) {
|
|
32
|
+
const t = ie(), r = await ve(e.channel, e.partition, t);
|
|
33
|
+
for (const n of e.gestures)
|
|
34
|
+
n.addSpans(t.spans), n.end({ status: "ok", connections: r });
|
|
35
|
+
}
|
|
36
|
+
function de() {
|
|
37
|
+
for (const e of I.values()) clearTimeout(e.timer);
|
|
38
|
+
I.clear();
|
|
39
|
+
}
|
|
40
|
+
function Le() {
|
|
41
41
|
const e = [];
|
|
42
42
|
let t = 0;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
const r = /* @__PURE__ */ new Map();
|
|
44
|
+
for (const [o, s] of S) {
|
|
45
|
+
const a = [];
|
|
46
|
+
for (const [u, v] of s) {
|
|
47
|
+
const l = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const i of v) {
|
|
49
|
+
const f = l.get(i.moduleId) ?? { count: 0, last: void 0 };
|
|
50
|
+
if (f.count += 1, i.lastDeliveryAt !== void 0 && (f.last === void 0 || i.lastDeliveryAt > f.last) && (f.last = i.lastDeliveryAt), l.set(i.moduleId, f), i.kind === "live" && i.liveId) {
|
|
51
|
+
let h = r.get(i.liveId);
|
|
52
|
+
if (h || (h = {
|
|
53
|
+
moduleId: i.moduleId,
|
|
54
|
+
action: i.actionName ?? "(anonymous)",
|
|
55
|
+
channels: [],
|
|
56
|
+
args: i.args ? { ...i.args } : null,
|
|
57
|
+
lastDeliveryAt: null
|
|
58
|
+
}, r.set(i.liveId, h)), h.channels.push({ channel: o, partition: u }), i.lastDeliveryAt !== void 0) {
|
|
59
|
+
const w = new Date(i.lastDeliveryAt).toISOString();
|
|
60
|
+
(h.lastDeliveryAt === null || w > h.lastDeliveryAt) && (h.lastDeliveryAt = w);
|
|
61
|
+
}
|
|
62
|
+
} else i.kind !== "live" && (t += 1);
|
|
50
63
|
}
|
|
51
|
-
for (const [
|
|
52
|
-
|
|
53
|
-
moduleId:
|
|
54
|
-
partition:
|
|
55
|
-
connections:
|
|
56
|
-
lastDeliveryAt:
|
|
64
|
+
for (const [i, f] of l)
|
|
65
|
+
a.push({
|
|
66
|
+
moduleId: i,
|
|
67
|
+
partition: u,
|
|
68
|
+
connections: f.count,
|
|
69
|
+
lastDeliveryAt: f.last === void 0 ? null : new Date(f.last).toISOString()
|
|
57
70
|
});
|
|
58
71
|
}
|
|
59
72
|
e.push({
|
|
60
73
|
channel: o,
|
|
61
|
-
groups:
|
|
62
|
-
connections:
|
|
63
|
-
})
|
|
74
|
+
groups: a,
|
|
75
|
+
connections: be(s)
|
|
76
|
+
});
|
|
64
77
|
}
|
|
78
|
+
t += r.size;
|
|
65
79
|
const n = [];
|
|
66
|
-
for (const o of
|
|
80
|
+
for (const o of I.values())
|
|
67
81
|
n.push({ channel: o.channel, partition: o.partition });
|
|
68
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
channels: e,
|
|
84
|
+
openCoalesceWindows: n,
|
|
85
|
+
liveActions: [...r.values()],
|
|
86
|
+
totalConnections: t
|
|
87
|
+
};
|
|
69
88
|
}
|
|
70
|
-
function
|
|
89
|
+
function be(e) {
|
|
71
90
|
let t = 0;
|
|
72
|
-
for (const
|
|
91
|
+
for (const r of e.values()) t += r.size;
|
|
73
92
|
return t;
|
|
74
93
|
}
|
|
75
|
-
function
|
|
94
|
+
function ee(e, t) {
|
|
76
95
|
return `${e}\0${t}`;
|
|
77
96
|
}
|
|
78
97
|
function pe(e) {
|
|
79
|
-
let t =
|
|
80
|
-
t || (t = /* @__PURE__ */ new Map(),
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
function
|
|
87
|
-
const t =
|
|
88
|
-
if (!t || !
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
let t = S.get(e.channel);
|
|
99
|
+
t || (t = /* @__PURE__ */ new Map(), S.set(e.channel, t));
|
|
100
|
+
let r = t.get(e.partition);
|
|
101
|
+
r || (r = /* @__PURE__ */ new Set(), t.set(e.partition, r));
|
|
102
|
+
const n = r.size === 0;
|
|
103
|
+
return r.add(e), n;
|
|
104
|
+
}
|
|
105
|
+
function ne(e) {
|
|
106
|
+
const t = S.get(e.channel), r = t?.get(e.partition);
|
|
107
|
+
if (!t || !r) return !1;
|
|
108
|
+
r.delete(e);
|
|
109
|
+
const n = r.size === 0;
|
|
110
|
+
return n && t.delete(e.partition), t.size === 0 && S.delete(e.channel), n;
|
|
111
|
+
}
|
|
112
|
+
const G = /* @__PURE__ */ new Map();
|
|
113
|
+
function te(e, t, r) {
|
|
114
|
+
const n = ee(e, t), o = g[e]?.onGroupOpen;
|
|
115
|
+
G.set(n, r);
|
|
116
|
+
const s = (async () => {
|
|
117
|
+
if (typeof o == "function")
|
|
96
118
|
try {
|
|
97
|
-
await
|
|
98
|
-
} catch (
|
|
119
|
+
await o({ partition: t, params: r.params, args: r.args });
|
|
120
|
+
} catch (a) {
|
|
99
121
|
console.error(
|
|
100
122
|
`[velojs] channel "${e}" onGroupOpen failed for partition "${t}":`,
|
|
101
|
-
|
|
123
|
+
a
|
|
102
124
|
);
|
|
103
125
|
}
|
|
104
126
|
})();
|
|
105
|
-
return
|
|
106
|
-
|
|
127
|
+
return O.set(n, s), s.then(() => {
|
|
128
|
+
O.get(n) === s && O.delete(n);
|
|
107
129
|
}), s;
|
|
108
130
|
}
|
|
109
|
-
async function
|
|
110
|
-
const t =
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
if (!
|
|
114
|
-
|
|
131
|
+
async function he(e) {
|
|
132
|
+
const t = ee(e.channel, e.partition), r = O.get(t);
|
|
133
|
+
r && await r;
|
|
134
|
+
const n = S.get(e.channel)?.get(e.partition);
|
|
135
|
+
if (!n || !n.has(e) || n.size > 1) {
|
|
136
|
+
ne(e);
|
|
115
137
|
return;
|
|
116
138
|
}
|
|
117
|
-
const
|
|
118
|
-
if (typeof
|
|
139
|
+
const o = g[e.channel]?.onGroupClose, s = G.get(t) ?? { params: e.params };
|
|
140
|
+
if (typeof o == "function")
|
|
119
141
|
try {
|
|
120
|
-
await
|
|
121
|
-
} catch (
|
|
142
|
+
await o({ partition: e.partition, args: s.args });
|
|
143
|
+
} catch (v) {
|
|
122
144
|
console.error(
|
|
123
145
|
`[velojs] channel "${e.channel}" onGroupClose failed for partition "${e.partition}":`,
|
|
124
|
-
|
|
146
|
+
v
|
|
125
147
|
);
|
|
126
148
|
}
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
149
|
+
ne(e);
|
|
150
|
+
const a = S.get(e.channel)?.get(e.partition), u = a ? [...a][0] : void 0;
|
|
151
|
+
u ? await te(e.channel, e.partition, {
|
|
152
|
+
params: u.params,
|
|
153
|
+
args: u.args
|
|
154
|
+
}) : G.delete(t);
|
|
130
155
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
156
|
+
function Ne() {
|
|
157
|
+
S.clear(), O.clear(), G.clear(), de();
|
|
133
158
|
}
|
|
134
|
-
async function
|
|
135
|
-
const
|
|
136
|
-
return typeof
|
|
159
|
+
async function x(e, t) {
|
|
160
|
+
const r = e.scope;
|
|
161
|
+
return typeof r != "function" ? "all" : await r(t);
|
|
137
162
|
}
|
|
138
|
-
function
|
|
163
|
+
function ye(e, t) {
|
|
139
164
|
return e.get(t);
|
|
140
165
|
}
|
|
141
|
-
async function
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
144
|
-
const
|
|
145
|
-
for (const s of [...
|
|
146
|
-
let
|
|
166
|
+
async function me(e, t) {
|
|
167
|
+
const r = S.get(e)?.get(t);
|
|
168
|
+
if (!r || r.size === 0) return null;
|
|
169
|
+
const n = g[e], o = [];
|
|
170
|
+
for (const s of [...r]) {
|
|
171
|
+
let a;
|
|
147
172
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
} catch (c) {
|
|
153
|
-
console.error(`[velojs] channel "${e}" scope threw on emit:`, c), i = null;
|
|
173
|
+
a = await x(n, je(s));
|
|
174
|
+
} catch (u) {
|
|
175
|
+
console.error(`[velojs] channel "${e}" scope threw on emit:`, u), a = null;
|
|
154
176
|
}
|
|
155
|
-
if (
|
|
177
|
+
if (a == null || a !== t) {
|
|
156
178
|
await s.terminate();
|
|
157
179
|
continue;
|
|
158
180
|
}
|
|
159
|
-
|
|
181
|
+
o.push(s);
|
|
160
182
|
}
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
return o.length > 0 ? o : null;
|
|
184
|
+
}
|
|
185
|
+
function je(e) {
|
|
186
|
+
return e.kind === "live" ? { user: e.principal, args: e.args } : { user: e.principal, params: e.params };
|
|
187
|
+
}
|
|
188
|
+
async function ve(e, t, r) {
|
|
189
|
+
const n = await me(e, t);
|
|
190
|
+
return n ? (await Promise.all(
|
|
191
|
+
n.map(async (o) => {
|
|
192
|
+
const s = r.span(ke(o)), a = await re(o);
|
|
193
|
+
s.end(a);
|
|
169
194
|
})
|
|
170
|
-
),
|
|
195
|
+
), n.length) : 0;
|
|
171
196
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
197
|
+
function ke(e) {
|
|
198
|
+
return e.kind === "live" ? `action:${e.moduleId}/${e.actionName ?? "anonymous"}` : `loader:${e.moduleId}`;
|
|
199
|
+
}
|
|
200
|
+
async function Te(e) {
|
|
201
|
+
if (e.kind === "live") {
|
|
202
|
+
if (typeof e.action != "function")
|
|
203
|
+
throw new Error("[velojs] live action connection has no action");
|
|
204
|
+
return e.action({
|
|
205
|
+
body: e.args ?? {},
|
|
176
206
|
params: e.params,
|
|
177
207
|
query: e.query,
|
|
178
208
|
c: e.c
|
|
179
209
|
});
|
|
180
|
-
}
|
|
210
|
+
}
|
|
211
|
+
if (typeof e.loader != "function")
|
|
212
|
+
throw new Error("[velojs] channel connection has no loader");
|
|
213
|
+
return e.loader({ params: e.params, query: e.query, c: e.c });
|
|
214
|
+
}
|
|
215
|
+
async function re(e) {
|
|
216
|
+
let t;
|
|
217
|
+
try {
|
|
218
|
+
t = await Te(e);
|
|
219
|
+
} catch (r) {
|
|
181
220
|
return await e.send(
|
|
182
|
-
|
|
221
|
+
Se,
|
|
183
222
|
JSON.stringify({
|
|
184
|
-
message:
|
|
223
|
+
message: r instanceof Error ? r.message : String(r)
|
|
185
224
|
})
|
|
186
225
|
), "error";
|
|
187
226
|
}
|
|
188
|
-
return t instanceof Response || await e.send(
|
|
227
|
+
return t instanceof Response || await e.send(X, JSON.stringify(t ?? null)), "ok";
|
|
189
228
|
}
|
|
190
|
-
function
|
|
191
|
-
return typeof e == "string" ?
|
|
229
|
+
function Ge(e, t, r, n) {
|
|
230
|
+
return typeof e == "string" ? De(e, t ?? {}) : Ce(e, t, r, n);
|
|
192
231
|
}
|
|
193
|
-
async function
|
|
194
|
-
const
|
|
195
|
-
if (!
|
|
232
|
+
async function De(e, t) {
|
|
233
|
+
const r = g[e];
|
|
234
|
+
if (!r)
|
|
196
235
|
throw new Error(
|
|
197
236
|
`[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.`
|
|
198
237
|
);
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const s =
|
|
238
|
+
const n = await x(r, t), o = n == null ? 0 : S.get(e)?.get(n)?.size ?? 0;
|
|
239
|
+
ge(e, n, "invalidate", o);
|
|
240
|
+
const s = F("emit", {
|
|
202
241
|
channel: e,
|
|
203
|
-
partition:
|
|
242
|
+
partition: n ?? null,
|
|
204
243
|
mode: "invalidate",
|
|
205
|
-
connections:
|
|
244
|
+
connections: o
|
|
206
245
|
});
|
|
207
|
-
if (
|
|
246
|
+
if (n == null) {
|
|
208
247
|
s.end({ status: "ok", connections: 0 });
|
|
209
248
|
return;
|
|
210
249
|
}
|
|
211
|
-
if (
|
|
250
|
+
if (o === 0) {
|
|
212
251
|
s.end({ status: "ok", connections: 0 });
|
|
213
252
|
return;
|
|
214
253
|
}
|
|
215
|
-
if (
|
|
216
|
-
const
|
|
217
|
-
s.addSpans(
|
|
254
|
+
if (Z <= 0) {
|
|
255
|
+
const a = ie(), u = await ve(e, n, a);
|
|
256
|
+
s.addSpans(a.spans), s.end({ status: "ok", connections: u });
|
|
218
257
|
return;
|
|
219
258
|
}
|
|
220
|
-
|
|
259
|
+
Ee(e, n, s);
|
|
221
260
|
}
|
|
222
|
-
async function
|
|
223
|
-
const
|
|
224
|
-
if (!
|
|
261
|
+
async function Ce(e, t, r, n) {
|
|
262
|
+
const o = e?.metadata?.moduleId;
|
|
263
|
+
if (!o)
|
|
225
264
|
throw new Error(
|
|
226
265
|
`[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.`
|
|
227
266
|
);
|
|
228
267
|
const s = e.channels;
|
|
229
268
|
if (!Array.isArray(s) || !s.includes(t))
|
|
230
269
|
throw new Error(
|
|
231
|
-
`[velojs] emit(module, "${t}", …): module "${
|
|
270
|
+
`[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.`
|
|
232
271
|
);
|
|
233
272
|
if (typeof e.loader != "function")
|
|
234
273
|
throw new Error(
|
|
235
|
-
`[velojs] emit(module, "${t}", …): module "${
|
|
274
|
+
`[velojs] emit(module, "${t}", …): module "${o}" has no \`loader\` — there is no data shape to slice.`
|
|
236
275
|
);
|
|
237
|
-
const
|
|
238
|
-
if (!
|
|
276
|
+
const a = g[t];
|
|
277
|
+
if (!a)
|
|
239
278
|
throw new Error(
|
|
240
279
|
`[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.`
|
|
241
280
|
);
|
|
242
|
-
if (
|
|
281
|
+
if (n === null || typeof n != "object" || Array.isArray(n))
|
|
243
282
|
throw new Error(
|
|
244
|
-
`[velojs] emit(module, "${t}", …): the slice must be the object of changed keys — got ${
|
|
283
|
+
`[velojs] emit(module, "${t}", …): the slice must be the object of changed keys — got ${n === null ? "null" : Array.isArray(n) ? "an array" : typeof n}.`
|
|
245
284
|
);
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
const
|
|
285
|
+
const u = await x(a, r), v = u == null ? void 0 : S.get(t)?.get(u), l = v?.size ?? 0;
|
|
286
|
+
ge(t, u, "slice", l);
|
|
287
|
+
const i = F("emit", {
|
|
249
288
|
channel: t,
|
|
250
|
-
partition:
|
|
289
|
+
partition: u ?? null,
|
|
251
290
|
mode: "slice",
|
|
252
|
-
connections:
|
|
291
|
+
connections: l
|
|
253
292
|
});
|
|
254
|
-
if (
|
|
255
|
-
|
|
293
|
+
if (u == null) {
|
|
294
|
+
i.end({ status: "ok", connections: 0 });
|
|
256
295
|
return;
|
|
257
296
|
}
|
|
258
|
-
if (!
|
|
259
|
-
|
|
297
|
+
if (!v || l === 0) {
|
|
298
|
+
i.end({ status: "ok", connections: 0 });
|
|
260
299
|
return;
|
|
261
300
|
}
|
|
262
|
-
const
|
|
263
|
-
if (!
|
|
264
|
-
|
|
301
|
+
const f = await me(t, u);
|
|
302
|
+
if (!f) {
|
|
303
|
+
i.end({ status: "ok", connections: 0 });
|
|
265
304
|
return;
|
|
266
305
|
}
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
306
|
+
const h = f.filter(
|
|
307
|
+
(E) => E.moduleId === o && E.kind !== "live"
|
|
308
|
+
);
|
|
309
|
+
if (h.length === 0) {
|
|
310
|
+
i.end({ status: "ok", connections: 0 });
|
|
270
311
|
return;
|
|
271
312
|
}
|
|
272
|
-
const
|
|
273
|
-
await Promise.all(
|
|
313
|
+
const w = JSON.stringify(n);
|
|
314
|
+
await Promise.all(h.map((E) => E.send(ae, w))), i.end({ status: "ok", connections: h.length });
|
|
274
315
|
}
|
|
275
|
-
function
|
|
276
|
-
if (!
|
|
277
|
-
const
|
|
316
|
+
function ge(e, t, r, n) {
|
|
317
|
+
if (!fe) return;
|
|
318
|
+
const o = typeof t == "string" ? `"${t}"` : "null";
|
|
278
319
|
console.log(
|
|
279
|
-
`[velojs] emit kind=${
|
|
320
|
+
`[velojs] emit kind=${r} channel="${e}" partition=${o} connections=${n} at=${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
280
321
|
);
|
|
281
322
|
}
|
|
282
|
-
function
|
|
283
|
-
let
|
|
284
|
-
return (
|
|
285
|
-
}).catch((
|
|
323
|
+
function we(e, t) {
|
|
324
|
+
let r = Promise.resolve();
|
|
325
|
+
return (n) => (r = r.then(() => t(n)).then(() => {
|
|
326
|
+
}).catch((o) => {
|
|
286
327
|
console.warn(
|
|
287
328
|
`[velojs] channel "${e}": SSE write failed — the snapshot did not reach the client:`,
|
|
288
|
-
|
|
329
|
+
o
|
|
289
330
|
);
|
|
290
|
-
}),
|
|
331
|
+
}), r);
|
|
291
332
|
}
|
|
292
|
-
const
|
|
293
|
-
function
|
|
333
|
+
const W = "_route";
|
|
334
|
+
function _e(e) {
|
|
294
335
|
return e ? e.split("/").filter((t) => t.startsWith(":")).map((t) => t.slice(1)) : [];
|
|
295
336
|
}
|
|
296
|
-
function
|
|
337
|
+
function Oe(e, t) {
|
|
297
338
|
if (e == null || e === "")
|
|
298
339
|
return { params: {}, error: null };
|
|
299
|
-
let
|
|
340
|
+
let r;
|
|
300
341
|
try {
|
|
301
|
-
|
|
342
|
+
r = JSON.parse(e);
|
|
302
343
|
} catch {
|
|
303
|
-
return { params: {}, error: `the ?${
|
|
344
|
+
return { params: {}, error: `the ?${W}= payload is not valid JSON` };
|
|
304
345
|
}
|
|
305
|
-
if (
|
|
346
|
+
if (r === null || typeof r != "object" || Array.isArray(r))
|
|
306
347
|
return {
|
|
307
348
|
params: {},
|
|
308
|
-
error: `the ?${
|
|
349
|
+
error: `the ?${W}= payload must be an object of route params`
|
|
309
350
|
};
|
|
310
|
-
const
|
|
311
|
-
for (const [
|
|
312
|
-
if (!t.includes(
|
|
313
|
-
const
|
|
351
|
+
const n = {};
|
|
352
|
+
for (const [o, s] of Object.entries(r)) {
|
|
353
|
+
if (!t.includes(o)) {
|
|
354
|
+
const a = t.length > 0 ? t.join(", ") : "none";
|
|
314
355
|
return {
|
|
315
356
|
params: {},
|
|
316
|
-
error: `route param "${
|
|
357
|
+
error: `route param "${o}" is not declared by the module's path (declared: ${a})`
|
|
317
358
|
};
|
|
318
359
|
}
|
|
319
360
|
if (typeof s != "string")
|
|
320
|
-
return { params: {}, error: `route param "${
|
|
321
|
-
o
|
|
361
|
+
return { params: {}, error: `route param "${o}" must be a URL string` };
|
|
362
|
+
n[o] = s;
|
|
322
363
|
}
|
|
323
|
-
return { params:
|
|
364
|
+
return { params: n, error: null };
|
|
324
365
|
}
|
|
325
|
-
function
|
|
326
|
-
if (typeof
|
|
366
|
+
function xe(e, t, r, n, o, s) {
|
|
367
|
+
if (typeof n.loader != "function")
|
|
327
368
|
throw new Error(
|
|
328
|
-
`[velojs] module "${
|
|
369
|
+
`[velojs] module "${o}" declares \`channels\` without a \`loader\` — there is nothing to synchronize. Add a loader or remove the export.`
|
|
329
370
|
);
|
|
330
|
-
if (!
|
|
371
|
+
if (!g[r])
|
|
331
372
|
throw new Error(
|
|
332
|
-
`[velojs] module "${
|
|
373
|
+
`[velojs] module "${o}" declares channel "${r}" with no entry in app/channels.ts — add the partition resolver for "${r}".`
|
|
333
374
|
);
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
375
|
+
const a = n.loader, u = _e(n.metadata?.fullPath), v = async (l) => {
|
|
376
|
+
const i = g[r], f = F("channel-connect", { route: t, module: o, channel: r }), h = Oe(
|
|
377
|
+
l.req.query(W),
|
|
378
|
+
u
|
|
338
379
|
);
|
|
339
|
-
if (
|
|
340
|
-
return console.error(`[velojs] channel "${
|
|
341
|
-
const
|
|
342
|
-
let
|
|
380
|
+
if (h.error)
|
|
381
|
+
return console.error(`[velojs] channel "${r}": rejecting subscription — ${h.error}`), f.end({ status: "error" }), l.json({ error: "invalid-route-params", message: h.error }, 400);
|
|
382
|
+
const w = h.params, E = ye(l, "user");
|
|
383
|
+
let T;
|
|
343
384
|
try {
|
|
344
|
-
|
|
345
|
-
} catch (
|
|
346
|
-
return console.error(`[velojs] channel "${
|
|
385
|
+
T = await x(i, { user: E, params: w });
|
|
386
|
+
} catch (p) {
|
|
387
|
+
return console.error(`[velojs] channel "${r}" scope threw:`, p), f.end({ status: "error" }), l.json({ error: "forbidden" }, 403);
|
|
347
388
|
}
|
|
348
|
-
if (
|
|
349
|
-
return
|
|
350
|
-
const { streamSSE:
|
|
351
|
-
for (const [
|
|
352
|
-
|
|
353
|
-
const
|
|
354
|
-
let
|
|
389
|
+
if (T == null)
|
|
390
|
+
return f.end({ status: "error" }), l.json({ error: "forbidden" }, 403);
|
|
391
|
+
const { streamSSE: Y } = await import("hono/streaming"), R = {};
|
|
392
|
+
for (const [p, y] of Object.entries(l.req.query()))
|
|
393
|
+
p !== W && (R[p] = y);
|
|
394
|
+
const b = await Y(l, async (p) => {
|
|
395
|
+
let y = !1, j = () => {
|
|
355
396
|
};
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
}),
|
|
359
|
-
|
|
360
|
-
(
|
|
361
|
-
),
|
|
362
|
-
channel:
|
|
363
|
-
partition:
|
|
364
|
-
moduleId:
|
|
397
|
+
const P = new Promise((d) => {
|
|
398
|
+
j = d;
|
|
399
|
+
}), D = we(
|
|
400
|
+
r,
|
|
401
|
+
(d) => typeof d == "string" ? p.write(d) : p.writeSSE(d)
|
|
402
|
+
), A = {
|
|
403
|
+
channel: r,
|
|
404
|
+
partition: T,
|
|
405
|
+
moduleId: o,
|
|
365
406
|
principal: E,
|
|
366
|
-
loader:
|
|
367
|
-
c:
|
|
368
|
-
params:
|
|
369
|
-
query:
|
|
370
|
-
telemetry:
|
|
371
|
-
send: (
|
|
372
|
-
|
|
373
|
-
})) :
|
|
374
|
-
terminate: (
|
|
375
|
-
},
|
|
376
|
-
|
|
377
|
-
}, q = async (
|
|
378
|
-
|
|
379
|
-
};
|
|
380
|
-
let v = null;
|
|
381
|
-
const x = () => {
|
|
382
|
-
if (N <= 0 || h) return;
|
|
383
|
-
v !== null && clearTimeout(v), v = setTimeout(() => {
|
|
384
|
-
v = null, q("idle");
|
|
385
|
-
}, N);
|
|
386
|
-
const l = v.unref;
|
|
387
|
-
typeof l == "function" && l.call(v);
|
|
407
|
+
loader: a,
|
|
408
|
+
c: l,
|
|
409
|
+
params: w,
|
|
410
|
+
query: R,
|
|
411
|
+
telemetry: f,
|
|
412
|
+
send: (d, c) => y ? Promise.resolve() : d === X || d === ae ? (A.telemetry?.enabled && A.telemetry.delivery(H(c)), D({ event: d, data: c }).then(() => {
|
|
413
|
+
y || (A.lastDeliveryAt = Date.now(), z());
|
|
414
|
+
})) : D({ event: d, data: c }),
|
|
415
|
+
terminate: (d) => q(d)
|
|
416
|
+
}, C = () => {
|
|
417
|
+
y || (y = !0, $ !== null && clearTimeout($), m !== null && clearInterval(m), j(), A.telemetry?.close(), he(A));
|
|
418
|
+
}, q = async (d = "partition") => {
|
|
419
|
+
y || (await A.send(le, JSON.stringify({ reason: d })), C());
|
|
388
420
|
};
|
|
389
421
|
let $ = null;
|
|
390
|
-
|
|
391
|
-
|
|
422
|
+
const z = () => {
|
|
423
|
+
if (L <= 0 || y) return;
|
|
424
|
+
$ !== null && clearTimeout($), $ = setTimeout(() => {
|
|
425
|
+
$ = null, q("idle");
|
|
426
|
+
}, L);
|
|
427
|
+
const d = $.unref;
|
|
428
|
+
typeof d == "function" && d.call($);
|
|
429
|
+
};
|
|
430
|
+
let m = null;
|
|
431
|
+
N > 0 && (m = setInterval(() => {
|
|
432
|
+
y || D(`: ping
|
|
392
433
|
|
|
393
434
|
`);
|
|
394
|
-
},
|
|
395
|
-
const
|
|
396
|
-
if (
|
|
397
|
-
const
|
|
398
|
-
typeof
|
|
435
|
+
}, N));
|
|
436
|
+
const J = pe(A);
|
|
437
|
+
if (p.onAbort(C), z(), m !== null) {
|
|
438
|
+
const d = m.unref;
|
|
439
|
+
typeof d == "function" && d.call(m);
|
|
399
440
|
}
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
}),
|
|
404
|
-
return
|
|
405
|
-
status:
|
|
406
|
-
statusText:
|
|
407
|
-
headers:
|
|
441
|
+
J && await te(r, T, { params: w });
|
|
442
|
+
const k = f.span("snapshot"), V = await re(A);
|
|
443
|
+
k.end(V), f.end({ status: V }), await P;
|
|
444
|
+
}), M = new Headers(b.headers);
|
|
445
|
+
return M.set("Cache-Control", "no-store"), new Response(b.body, {
|
|
446
|
+
status: b.status,
|
|
447
|
+
statusText: b.statusText,
|
|
448
|
+
headers: M
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
s.length > 0 ? e.on(["GET"], [t], ...s, v) : e.on(["GET"], [t], v);
|
|
452
|
+
}
|
|
453
|
+
const Q = "_args", K = 4096;
|
|
454
|
+
function Ie(e) {
|
|
455
|
+
if (e == null || e === "")
|
|
456
|
+
return { args: {}, error: null };
|
|
457
|
+
let t;
|
|
458
|
+
try {
|
|
459
|
+
t = JSON.parse(e);
|
|
460
|
+
} catch {
|
|
461
|
+
return { args: {}, error: `the ?${Q}= payload is not valid JSON` };
|
|
462
|
+
}
|
|
463
|
+
return t === null || typeof t != "object" || Array.isArray(t) ? {
|
|
464
|
+
args: {},
|
|
465
|
+
error: `the ?${Q}= payload must be an object of the action's arguments`
|
|
466
|
+
} : { args: t, error: null };
|
|
467
|
+
}
|
|
468
|
+
let oe = 0;
|
|
469
|
+
function Je(e, t, r, n, o, s, a) {
|
|
470
|
+
const u = o[`action_${r}`];
|
|
471
|
+
if (typeof u != "function")
|
|
472
|
+
throw new Error(
|
|
473
|
+
`[velojs] module "${s}" declares liveActions.${r} but has no \`export const action_${r}\` — there is nothing to synchronize. Add the action or remove the entry.`
|
|
474
|
+
);
|
|
475
|
+
for (const l of n)
|
|
476
|
+
if (!g[l])
|
|
477
|
+
throw new Error(
|
|
478
|
+
`[velojs] module "${s}" liveActions.${r} declares channel "${l}" with no entry in app/channels.ts — add the partition resolver for "${l}".`
|
|
479
|
+
);
|
|
480
|
+
const v = async (l) => {
|
|
481
|
+
const i = F("live-action-connect", { route: t, module: s, name: r }), f = Ie(l.req.query(Q));
|
|
482
|
+
if (f.error)
|
|
483
|
+
return console.error(
|
|
484
|
+
`[velojs] live action "${s}/${r}": rejecting subscription — ` + f.error
|
|
485
|
+
), i.end({ status: "error" }), l.json({ error: "invalid-args", message: f.error }, 400);
|
|
486
|
+
const h = f.args, w = JSON.stringify(h);
|
|
487
|
+
if (H(w) > K) {
|
|
488
|
+
const p = `[velojs] live action "${s}/${r}": the serialized args are ${H(w)} bytes — the limit is ${K}. Pass less data (an id, not the document) — the args are the tab's address.`;
|
|
489
|
+
return console.error(p), i.end({ status: "error" }), l.json(
|
|
490
|
+
{ error: "args-too-large", message: p, limit: K },
|
|
491
|
+
400
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const E = ye(l, "user"), T = [];
|
|
495
|
+
for (const p of n) {
|
|
496
|
+
const y = g[p];
|
|
497
|
+
let j;
|
|
498
|
+
try {
|
|
499
|
+
j = await x(y, { user: E, args: h });
|
|
500
|
+
} catch (P) {
|
|
501
|
+
return console.error(`[velojs] channel "${p}" scope threw:`, P), i.end({ status: "error" }), l.json({ error: "forbidden" }, 403);
|
|
502
|
+
}
|
|
503
|
+
if (j == null)
|
|
504
|
+
return i.end({ status: "error" }), l.json({ error: "forbidden" }, 403);
|
|
505
|
+
T.push({ channel: p, partition: j });
|
|
506
|
+
}
|
|
507
|
+
const { streamSSE: Y } = await import("hono/streaming"), R = {};
|
|
508
|
+
for (const [p, y] of Object.entries(l.req.query()))
|
|
509
|
+
p !== Q && (R[p] = y);
|
|
510
|
+
const b = await Y(l, async (p) => {
|
|
511
|
+
let y = !1, j = () => {
|
|
512
|
+
};
|
|
513
|
+
const P = new Promise((c) => {
|
|
514
|
+
j = c;
|
|
515
|
+
}), D = we(
|
|
516
|
+
`${s}/${r}`,
|
|
517
|
+
(c) => typeof c == "string" ? p.write(c) : p.writeSSE(c)
|
|
518
|
+
);
|
|
519
|
+
oe += 1;
|
|
520
|
+
const A = `live-${oe}`, C = T.map(({ channel: c, partition: _ }) => ({
|
|
521
|
+
channel: c,
|
|
522
|
+
partition: _,
|
|
523
|
+
moduleId: s,
|
|
524
|
+
kind: "live",
|
|
525
|
+
principal: E,
|
|
526
|
+
action: u,
|
|
527
|
+
actionName: r,
|
|
528
|
+
args: h,
|
|
529
|
+
liveId: A,
|
|
530
|
+
c: l,
|
|
531
|
+
params: {},
|
|
532
|
+
query: R,
|
|
533
|
+
telemetry: i,
|
|
534
|
+
send: (U, B) => q(U, B),
|
|
535
|
+
terminate: (U) => z(U)
|
|
536
|
+
})), q = (c, _) => y ? Promise.resolve() : c === X ? (i.enabled && i.delivery(H(_)), D({ event: c, data: _ }).then(() => {
|
|
537
|
+
if (y) return;
|
|
538
|
+
const B = Date.now();
|
|
539
|
+
for (const $e of C) $e.lastDeliveryAt = B;
|
|
540
|
+
J();
|
|
541
|
+
})) : D({ event: c, data: _ }), $ = () => {
|
|
542
|
+
if (!y) {
|
|
543
|
+
y = !0, m !== null && clearTimeout(m), k !== null && clearInterval(k), j(), i.close();
|
|
544
|
+
for (const c of C) he(c);
|
|
545
|
+
}
|
|
546
|
+
}, z = async (c = "partition") => {
|
|
547
|
+
y || (await q(le, JSON.stringify({ reason: c })), $());
|
|
548
|
+
};
|
|
549
|
+
let m = null;
|
|
550
|
+
const J = () => {
|
|
551
|
+
if (L <= 0 || y) return;
|
|
552
|
+
m !== null && clearTimeout(m), m = setTimeout(() => {
|
|
553
|
+
m = null, z("idle");
|
|
554
|
+
}, L);
|
|
555
|
+
const c = m.unref;
|
|
556
|
+
typeof c == "function" && c.call(m);
|
|
557
|
+
};
|
|
558
|
+
let k = null;
|
|
559
|
+
if (N > 0 && (k = setInterval(() => {
|
|
560
|
+
y || D(`: ping
|
|
561
|
+
|
|
562
|
+
`);
|
|
563
|
+
}, N)), p.onAbort($), J(), k !== null) {
|
|
564
|
+
const c = k.unref;
|
|
565
|
+
typeof c == "function" && c.call(k);
|
|
566
|
+
}
|
|
567
|
+
for (const c of C)
|
|
568
|
+
pe(c) && await te(c.channel, c.partition, {
|
|
569
|
+
params: {},
|
|
570
|
+
args: h
|
|
571
|
+
});
|
|
572
|
+
const V = i.span("snapshot"), d = await re(C[0]);
|
|
573
|
+
V.end(d), i.end({ status: d }), await P;
|
|
574
|
+
}), M = new Headers(b.headers);
|
|
575
|
+
return M.set("Cache-Control", "no-store"), new Response(b.body, {
|
|
576
|
+
status: b.status,
|
|
577
|
+
statusText: b.statusText,
|
|
578
|
+
headers: M
|
|
408
579
|
});
|
|
409
580
|
};
|
|
410
|
-
|
|
581
|
+
a.length > 0 ? e.on(["GET"], [t], ...a, v) : e.on(["GET"], [t], v);
|
|
411
582
|
}
|
|
412
583
|
export {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
Q as
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
584
|
+
le as CHANNEL_CLOSE_EVENT,
|
|
585
|
+
Se as CHANNEL_ERROR_EVENT,
|
|
586
|
+
ce as DEFAULT_COALESCE_MS,
|
|
587
|
+
ue as DEFAULT_IDLE_MS,
|
|
588
|
+
K as LIVE_ARGS_MAX_BYTES,
|
|
589
|
+
Q as LIVE_ARGS_QUERY,
|
|
590
|
+
W as ROUTE_PARAMS_QUERY,
|
|
591
|
+
ae as SLICE_EVENT,
|
|
592
|
+
X as SNAPSHOT_EVENT,
|
|
593
|
+
Ne as __resetChannels,
|
|
594
|
+
ze as channelNames,
|
|
595
|
+
we as createChannelWriteChain,
|
|
596
|
+
_e as declaredRouteParams,
|
|
597
|
+
Ge as emit,
|
|
598
|
+
qe as getChannelMap,
|
|
599
|
+
Le as inspectChannels,
|
|
600
|
+
Ie as parseLiveArgsQuery,
|
|
601
|
+
Oe as parseRouteParamsQuery,
|
|
602
|
+
re as pushSnapshot,
|
|
603
|
+
xe as registerChannelRoute,
|
|
604
|
+
Pe as registerChannels,
|
|
605
|
+
Je as registerLiveActionRoute
|
|
431
606
|
};
|