@mauroandre/velojs 0.0.25 → 0.0.27
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 +121 -1
- package/dist/client.js +43 -16
- package/dist/events.d.ts +58 -3
- package/dist/events.js +174 -135
- package/dist/hooks.d.ts +56 -0
- package/dist/hooks.js +128 -68
- package/dist/index.d.ts +3 -2
- package/dist/server.js +180 -122
- package/dist/sockets.d.ts +94 -0
- package/dist/sockets.js +182 -0
- package/dist/static.js +9 -8
- package/dist/testing/index.js +453 -290
- package/dist/testing/socketSession.d.ts +13 -0
- package/dist/testing/types.d.ts +36 -0
- package/dist/types.d.ts +10 -1
- package/dist/vite.d.ts +18 -0
- package/dist/vite.js +339 -231
- package/package.json +6 -1
package/dist/events.js
CHANGED
|
@@ -1,130 +1,165 @@
|
|
|
1
|
-
import { getAppContext as
|
|
2
|
-
const
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
for (const
|
|
1
|
+
import { getAppContext as R } from "./app-context.js";
|
|
2
|
+
const D = 2e4, N = 300 * 1e3, M = "";
|
|
3
|
+
function F(r) {
|
|
4
|
+
const o = R().pendingStreamRoutes.splice(0);
|
|
5
|
+
for (const a of o) a(r);
|
|
6
6
|
}
|
|
7
|
-
const
|
|
8
|
-
function
|
|
9
|
-
return
|
|
7
|
+
const q = /* @__PURE__ */ new Set();
|
|
8
|
+
function O() {
|
|
9
|
+
return q;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
if (
|
|
11
|
+
function x(r = {}) {
|
|
12
|
+
if (r.broadcast && r.channel && console.warn(
|
|
13
13
|
"[velojs] createEventStream: `broadcast: true` ignores `channel`. Remove one of them — broadcast streams have no channels."
|
|
14
|
-
),
|
|
14
|
+
), (r.source ? 1 : 0) + (r.perChannelSource ? 1 : 0) + (r.pipe ? 1 : 0) > 1)
|
|
15
15
|
throw new Error(
|
|
16
|
-
"[velojs] createEventStream: `source` and `
|
|
16
|
+
"[velojs] createEventStream: `source`, `perChannelSource`, and `pipe` are mutually exclusive — pick one. `source` is stream-wide, `perChannelSource` and `pipe` are per-channel (prefer `pipe` for new code)."
|
|
17
17
|
);
|
|
18
|
-
const
|
|
19
|
-
let
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const o = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Map();
|
|
19
|
+
let f = null, i = 0;
|
|
20
|
+
const h = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), d = () => {
|
|
21
|
+
r.source && (f || (f = new AbortController(), Promise.resolve(
|
|
22
|
+
r.source(v, { abortSignal: f.signal })
|
|
23
23
|
).catch((e) => {
|
|
24
24
|
console.error("[velojs] event stream source threw:", e);
|
|
25
25
|
})));
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
if (!
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
26
|
+
}, m = () => {
|
|
27
|
+
i > 0 || f && (f.abort(), f = null);
|
|
28
|
+
}, C = (e) => {
|
|
29
|
+
if (!r.perChannelSource || h.has(e)) return;
|
|
30
|
+
const t = new AbortController();
|
|
31
|
+
h.set(e, t);
|
|
32
|
+
const s = (n, u) => {
|
|
33
|
+
c.emit(e, n, u);
|
|
34
34
|
};
|
|
35
35
|
Promise.resolve(
|
|
36
|
-
|
|
37
|
-
).catch((
|
|
36
|
+
r.perChannelSource(e, s, { abortSignal: t.signal })
|
|
37
|
+
).catch((n) => {
|
|
38
38
|
console.error(
|
|
39
39
|
`[velojs] perChannelSource threw for channel "${e}":`,
|
|
40
|
-
|
|
40
|
+
n
|
|
41
41
|
);
|
|
42
42
|
});
|
|
43
|
-
},
|
|
44
|
-
const
|
|
45
|
-
|
|
43
|
+
}, P = (e) => {
|
|
44
|
+
const t = h.get(e);
|
|
45
|
+
t && (t.abort(), h.delete(e));
|
|
46
|
+
}, w = (e, t) => {
|
|
47
|
+
if (!r.pipe || S.has(e) || !t)
|
|
48
|
+
return;
|
|
49
|
+
const s = new AbortController();
|
|
50
|
+
S.set(e, s);
|
|
51
|
+
let n = !1;
|
|
52
|
+
const u = (g, _) => {
|
|
53
|
+
c.emit(e, g, _);
|
|
54
|
+
}, A = () => c.close(e), j = () => {
|
|
55
|
+
n = !0;
|
|
56
|
+
};
|
|
57
|
+
Promise.resolve(
|
|
58
|
+
r.pipe({
|
|
59
|
+
send: u,
|
|
60
|
+
keepOpen: j,
|
|
61
|
+
close: A,
|
|
62
|
+
abortSignal: s.signal,
|
|
63
|
+
c: t.c,
|
|
64
|
+
params: t.params,
|
|
65
|
+
query: t.query,
|
|
66
|
+
channel: e
|
|
67
|
+
})
|
|
68
|
+
).then(() => {
|
|
69
|
+
s.signal.aborted || n || c.close(e);
|
|
70
|
+
}).catch((g) => {
|
|
71
|
+
console.error(
|
|
72
|
+
`[velojs] stream pipe threw for channel "${e}":`,
|
|
73
|
+
g
|
|
74
|
+
), s.signal.aborted || c.close(e);
|
|
75
|
+
});
|
|
76
|
+
}, E = (e) => {
|
|
77
|
+
const t = S.get(e);
|
|
78
|
+
t && (t.abort(), S.delete(e));
|
|
46
79
|
};
|
|
47
|
-
function
|
|
48
|
-
let
|
|
49
|
-
if (!!
|
|
80
|
+
function v(e, t, s) {
|
|
81
|
+
let n, u, A;
|
|
82
|
+
if (!!r.broadcast ? (n = M, u = e, A = t) : (n = e, u = t, A = s), p.has(n)) {
|
|
50
83
|
console.warn(
|
|
51
|
-
`[velojs] emit() called on closed channel "${
|
|
84
|
+
`[velojs] emit() called on closed channel "${n}" — ignored. Reopen the channel by emitting to a fresh ID, or remove the close() call.`
|
|
52
85
|
);
|
|
53
86
|
return;
|
|
54
87
|
}
|
|
55
|
-
if (
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
88
|
+
if (A?.snapshot) {
|
|
89
|
+
const _ = a.get(n) ?? [];
|
|
90
|
+
_.push(u);
|
|
91
|
+
const I = r.bufferSize ?? 1 / 0;
|
|
92
|
+
_.length > I && _.splice(0, _.length - I), a.set(n, _);
|
|
60
93
|
}
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
for (const
|
|
94
|
+
const g = o.get(n);
|
|
95
|
+
if (g)
|
|
96
|
+
for (const _ of g) _(u);
|
|
64
97
|
}
|
|
65
|
-
function
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
for (const
|
|
71
|
-
const
|
|
72
|
-
|
|
98
|
+
function T(e = M) {
|
|
99
|
+
if (p.has(e)) return;
|
|
100
|
+
p.add(e);
|
|
101
|
+
const t = o.get(e);
|
|
102
|
+
if (t)
|
|
103
|
+
for (const u of t) u.close();
|
|
104
|
+
const s = r.retainMs ?? N, n = l.get(e);
|
|
105
|
+
n && clearTimeout(n), l.set(
|
|
73
106
|
e,
|
|
74
107
|
setTimeout(() => {
|
|
75
|
-
|
|
76
|
-
},
|
|
108
|
+
a.delete(e), l.delete(e), p.delete(e);
|
|
109
|
+
}, s)
|
|
77
110
|
);
|
|
78
111
|
}
|
|
79
|
-
const
|
|
80
|
-
emit:
|
|
81
|
-
close:
|
|
112
|
+
const c = {
|
|
113
|
+
emit: v,
|
|
114
|
+
close: T,
|
|
82
115
|
__isVeloEventStream: !0,
|
|
83
|
-
__config:
|
|
84
|
-
__listeners:
|
|
85
|
-
__buffers:
|
|
86
|
-
__path:
|
|
87
|
-
__onConnect(e,
|
|
88
|
-
let
|
|
89
|
-
const
|
|
90
|
-
|
|
116
|
+
__config: r,
|
|
117
|
+
__listeners: o,
|
|
118
|
+
__buffers: a,
|
|
119
|
+
__path: r.path,
|
|
120
|
+
__onConnect(e, t, s) {
|
|
121
|
+
let n = o.get(e);
|
|
122
|
+
const u = !n || n.size === 0;
|
|
123
|
+
n || (n = /* @__PURE__ */ new Set(), o.set(e, n)), n.add(t), i++, d(), u && (C(e), w(e, s));
|
|
91
124
|
},
|
|
92
|
-
__onDisconnect(e,
|
|
93
|
-
const
|
|
94
|
-
|
|
125
|
+
__onDisconnect(e, t) {
|
|
126
|
+
const s = o.get(e);
|
|
127
|
+
s && (s.delete(t), s.size === 0 && (o.delete(e), P(e), E(e)), i--, m());
|
|
95
128
|
},
|
|
96
129
|
__reset() {
|
|
97
|
-
for (const e of
|
|
98
|
-
for (const
|
|
130
|
+
for (const e of o.values())
|
|
131
|
+
for (const t of e)
|
|
99
132
|
try {
|
|
100
|
-
|
|
133
|
+
t.close();
|
|
101
134
|
} catch {
|
|
102
135
|
}
|
|
103
|
-
|
|
104
|
-
for (const e of
|
|
105
|
-
|
|
106
|
-
for (const e of
|
|
107
|
-
|
|
136
|
+
o.clear(), a.clear(), p.clear();
|
|
137
|
+
for (const e of l.values()) clearTimeout(e);
|
|
138
|
+
l.clear(), f && (f.abort(), f = null);
|
|
139
|
+
for (const e of h.values()) e.abort();
|
|
140
|
+
h.clear();
|
|
141
|
+
for (const e of S.values()) e.abort();
|
|
142
|
+
S.clear(), i = 0;
|
|
108
143
|
}
|
|
109
144
|
};
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
}),
|
|
145
|
+
return r.path && R().pendingStreamRoutes.push((e) => {
|
|
146
|
+
k(e, r.path, c, r.middlewares ?? []);
|
|
147
|
+
}), q.add(c), c;
|
|
113
148
|
}
|
|
114
|
-
function
|
|
115
|
-
return async (
|
|
116
|
-
for (; !
|
|
149
|
+
function z(r) {
|
|
150
|
+
return async (b, { abortSignal: o }) => {
|
|
151
|
+
for (; !o.aborted; ) {
|
|
117
152
|
try {
|
|
118
|
-
await
|
|
119
|
-
} catch (
|
|
120
|
-
console.error("[velojs] poll tick threw:",
|
|
153
|
+
await r.tick(b);
|
|
154
|
+
} catch (a) {
|
|
155
|
+
console.error("[velojs] poll tick threw:", a);
|
|
121
156
|
}
|
|
122
|
-
await new Promise((
|
|
123
|
-
const
|
|
124
|
-
|
|
157
|
+
await new Promise((a) => {
|
|
158
|
+
const p = setTimeout(a, r.intervalMs);
|
|
159
|
+
o.addEventListener(
|
|
125
160
|
"abort",
|
|
126
161
|
() => {
|
|
127
|
-
clearTimeout(
|
|
162
|
+
clearTimeout(p), a();
|
|
128
163
|
},
|
|
129
164
|
{ once: !0 }
|
|
130
165
|
);
|
|
@@ -132,70 +167,74 @@ function B(t) {
|
|
|
132
167
|
}
|
|
133
168
|
};
|
|
134
169
|
}
|
|
135
|
-
function
|
|
136
|
-
return
|
|
170
|
+
function L(r) {
|
|
171
|
+
return r.req.query("channel") ?? M;
|
|
137
172
|
}
|
|
138
|
-
function
|
|
139
|
-
const
|
|
140
|
-
const { streamSSE:
|
|
173
|
+
function k(r, b, o, a = []) {
|
|
174
|
+
const p = async (l) => {
|
|
175
|
+
const { streamSSE: f } = await import("hono/streaming");
|
|
141
176
|
let i;
|
|
142
|
-
if (
|
|
143
|
-
i =
|
|
177
|
+
if (o.__config.broadcast)
|
|
178
|
+
i = M;
|
|
144
179
|
else {
|
|
145
|
-
const d =
|
|
146
|
-
let
|
|
180
|
+
const d = o.__config.channel ?? L;
|
|
181
|
+
let m;
|
|
147
182
|
try {
|
|
148
|
-
|
|
149
|
-
} catch (
|
|
150
|
-
return console.error("[velojs] channel resolver threw:",
|
|
183
|
+
m = await d(l);
|
|
184
|
+
} catch (C) {
|
|
185
|
+
return console.error("[velojs] channel resolver threw:", C), l.json({ error: "internal" }, 500);
|
|
151
186
|
}
|
|
152
|
-
if (
|
|
153
|
-
return
|
|
154
|
-
i =
|
|
187
|
+
if (m == null)
|
|
188
|
+
return l.json({ error: "forbidden" }, 403);
|
|
189
|
+
i = m;
|
|
155
190
|
}
|
|
156
|
-
const
|
|
157
|
-
return
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
191
|
+
const h = o.__config.heartbeatMs, S = h === !1 ? 0 : h ?? D;
|
|
192
|
+
return f(l, async (d) => {
|
|
193
|
+
const m = o.__buffers.get(i);
|
|
194
|
+
if (m && m.length > 0 && await d.writeSSE({
|
|
160
195
|
event: "snapshot",
|
|
161
|
-
data: JSON.stringify(
|
|
162
|
-
}),
|
|
163
|
-
const
|
|
164
|
-
i ===
|
|
196
|
+
data: JSON.stringify(m)
|
|
197
|
+
}), o.__config.snapshot) {
|
|
198
|
+
const t = await o.__config.snapshot(
|
|
199
|
+
i === M ? void 0 : i
|
|
165
200
|
);
|
|
166
|
-
|
|
201
|
+
t !== void 0 && await d.writeSSE({
|
|
167
202
|
event: "snapshot",
|
|
168
|
-
data: JSON.stringify(
|
|
203
|
+
data: JSON.stringify(t)
|
|
169
204
|
});
|
|
170
205
|
}
|
|
171
|
-
let
|
|
172
|
-
const
|
|
173
|
-
|
|
206
|
+
let C;
|
|
207
|
+
const P = new Promise((t) => {
|
|
208
|
+
C = t;
|
|
174
209
|
});
|
|
175
|
-
let
|
|
176
|
-
const
|
|
177
|
-
}),
|
|
178
|
-
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
d.close(),
|
|
210
|
+
let w = !1, E = null, v = Promise.resolve();
|
|
211
|
+
const T = (t) => (v = v.then(() => d.writeSSE(t)).catch(() => {
|
|
212
|
+
}), v), c = () => {
|
|
213
|
+
w || (w = !0, E && clearInterval(E), o.__onDisconnect(i, e), C());
|
|
214
|
+
}, e = ((t) => {
|
|
215
|
+
w || (T({ data: JSON.stringify(t) }), o.__config.closeOn?.(t) && v.then(() => {
|
|
216
|
+
d.close(), c();
|
|
182
217
|
}));
|
|
183
218
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
d.close(),
|
|
219
|
+
e.close = () => {
|
|
220
|
+
w || (T({ event: "close", data: "" }), v.then(() => {
|
|
221
|
+
d.close(), c();
|
|
187
222
|
}));
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
|
|
223
|
+
}, o.__onConnect(i, e, {
|
|
224
|
+
c: l,
|
|
225
|
+
params: l.req.param(),
|
|
226
|
+
query: l.req.query()
|
|
227
|
+
}), E = S > 0 ? setInterval(() => {
|
|
228
|
+
w || T({ event: "heartbeat", data: "" });
|
|
229
|
+
}, S) : null, d.onAbort(c), await P;
|
|
191
230
|
});
|
|
192
231
|
};
|
|
193
|
-
|
|
232
|
+
a.length > 0 ? r.on(["GET"], [b], ...a, p) : r.on(["GET"], [b], p);
|
|
194
233
|
}
|
|
195
234
|
export {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
235
|
+
x as createEventStream,
|
|
236
|
+
F as flushPendingStreamRoutes,
|
|
237
|
+
O as getRegisteredStreams,
|
|
238
|
+
z as poll,
|
|
239
|
+
k as registerStreamHandler
|
|
201
240
|
};
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Signal } from "@preact/signals";
|
|
2
2
|
import type { EventStream } from "./events.js";
|
|
3
|
+
import type { SocketStub } from "./sockets.js";
|
|
3
4
|
export declare const __veloUpdatePending: Signal<boolean>;
|
|
4
5
|
/**
|
|
5
6
|
* Força o signal a notificar mudanças após mutação de propriedades aninhadas
|
|
@@ -149,3 +150,58 @@ export declare function useEventStream<TEvent, TSnapshot = TEvent>(stream: Event
|
|
|
149
150
|
closed: Signal<boolean>;
|
|
150
151
|
error: Signal<Error | null>;
|
|
151
152
|
};
|
|
153
|
+
type SocketStatus = "connecting" | "open" | "closed";
|
|
154
|
+
export interface UseSocketOptions {
|
|
155
|
+
/** Optional channel; appended as `?channel=...` to the socket URL. */
|
|
156
|
+
channel?: string;
|
|
157
|
+
/** Called for every incoming message (text frame = string, binary frame = `Uint8Array`). */
|
|
158
|
+
onMessage?: (msg: string | Uint8Array) => void;
|
|
159
|
+
/** Called on successful connection. */
|
|
160
|
+
onOpen?: () => void;
|
|
161
|
+
/** Called on close (client or server). */
|
|
162
|
+
onClose?: (evt: CloseEvent) => void;
|
|
163
|
+
/** Called on socket error. */
|
|
164
|
+
onError?: (err: Event) => void;
|
|
165
|
+
/** When false, the connection is not opened. Flip to true to connect later. Default: true. */
|
|
166
|
+
enabled?: boolean;
|
|
167
|
+
}
|
|
168
|
+
export interface UseSocketApi {
|
|
169
|
+
/**
|
|
170
|
+
* Send a message. Strings and `Uint8Array` are sent as-is;
|
|
171
|
+
* objects are auto-serialized with `JSON.stringify`.
|
|
172
|
+
*
|
|
173
|
+
* Buffered if called before the socket is open — dropped once closed.
|
|
174
|
+
*/
|
|
175
|
+
send: (msg: string | Uint8Array | object) => void;
|
|
176
|
+
/** Close the socket (client-initiated). */
|
|
177
|
+
close: (code?: number, reason?: string) => void;
|
|
178
|
+
/** `"connecting" | "open" | "closed"`. Reactive. */
|
|
179
|
+
status: Signal<SocketStatus>;
|
|
180
|
+
/** Last received message. Reactive. */
|
|
181
|
+
lastMessage: Signal<string | Uint8Array | null>;
|
|
182
|
+
/** Last socket error, if any. Reactive. */
|
|
183
|
+
error: Signal<Event | null>;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Subscribes to a WebSocket declared via the `socket_*` convention.
|
|
187
|
+
*
|
|
188
|
+
* The hook opens the connection on mount (when `enabled !== false`) and closes
|
|
189
|
+
* it on unmount or when `channel` / `stub` / `enabled` change.
|
|
190
|
+
*
|
|
191
|
+
* **No auto-reconnect.** If the server closes the socket, `status.value`
|
|
192
|
+
* becomes `"closed"` and stays that way — user flow decides whether to
|
|
193
|
+
* reconnect (typically by re-mounting or toggling `enabled`).
|
|
194
|
+
*
|
|
195
|
+
* ```tsx
|
|
196
|
+
* const { send, status, lastMessage } = useSocket(socket_terminal, {
|
|
197
|
+
* channel: workerId,
|
|
198
|
+
* onMessage: (msg) => { ... },
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* send({ type: "resize", cols: 80, rows: 24 });
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export declare function useSocket(stub: SocketStub | {
|
|
205
|
+
__path: string;
|
|
206
|
+
}, options?: UseSocketOptions): UseSocketApi;
|
|
207
|
+
export {};
|