@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/dist/events.js CHANGED
@@ -1,130 +1,165 @@
1
- import { getAppContext as A } from "./app-context.js";
2
- const M = 2e4, R = 300 * 1e3, m = "";
3
- function x(t) {
4
- const n = A().pendingStreamRoutes.splice(0);
5
- for (const l of n) l(t);
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 I = /* @__PURE__ */ new Set();
8
- function y() {
9
- return I;
7
+ const q = /* @__PURE__ */ new Set();
8
+ function O() {
9
+ return q;
10
10
  }
11
- function L(t = {}) {
12
- if (t.broadcast && t.channel && console.warn(
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
- ), t.source && t.perChannelSource)
14
+ ), (r.source ? 1 : 0) + (r.perChannelSource ? 1 : 0) + (r.pipe ? 1 : 0) > 1)
15
15
  throw new Error(
16
- "[velojs] createEventStream: `source` and `perChannelSource` are mutually exclusive. Use `source` for stream-wide producers or `perChannelSource` for per-channel resources, not both."
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 a = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Map();
19
- let c = null, w = 0;
20
- const i = /* @__PURE__ */ new Map(), b = () => {
21
- t.source && (c || (c = new AbortController(), Promise.resolve(
22
- t.source(_, { abortSignal: c.signal })
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
- }, g = () => {
27
- w > 0 || c && (c.abort(), c = null);
28
- }, d = (e) => {
29
- if (!t.perChannelSource || i.has(e)) return;
30
- const r = new AbortController();
31
- i.set(e, r);
32
- const o = (s, f) => {
33
- p.emit(e, s, f);
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
- t.perChannelSource(e, o, { abortSignal: r.signal })
37
- ).catch((s) => {
36
+ r.perChannelSource(e, s, { abortSignal: t.signal })
37
+ ).catch((n) => {
38
38
  console.error(
39
39
  `[velojs] perChannelSource threw for channel "${e}":`,
40
- s
40
+ n
41
41
  );
42
42
  });
43
- }, S = (e) => {
44
- const r = i.get(e);
45
- r && (r.abort(), i.delete(e));
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 _(e, r, o) {
48
- let s, f, u;
49
- if (!!t.broadcast ? (s = m, f = e, u = r) : (s = e, f = r, u = o), l.has(s)) {
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 "${s}" — ignored. Reopen the channel by emitting to a fresh ID, or remove the close() call.`
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 (u?.snapshot) {
56
- const v = n.get(s) ?? [];
57
- v.push(f);
58
- const T = t.bufferSize ?? 1 / 0;
59
- v.length > T && v.splice(0, v.length - T), n.set(s, v);
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 E = a.get(s);
62
- if (E)
63
- for (const v of E) v(f);
94
+ const g = o.get(n);
95
+ if (g)
96
+ for (const _ of g) _(u);
64
97
  }
65
- function C(e = m) {
66
- if (l.has(e)) return;
67
- l.add(e);
68
- const r = a.get(e);
69
- if (r)
70
- for (const f of r) f.close();
71
- const o = t.retainMs ?? R, s = h.get(e);
72
- s && clearTimeout(s), h.set(
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
- n.delete(e), h.delete(e), l.delete(e);
76
- }, o)
108
+ a.delete(e), l.delete(e), p.delete(e);
109
+ }, s)
77
110
  );
78
111
  }
79
- const p = {
80
- emit: _,
81
- close: C,
112
+ const c = {
113
+ emit: v,
114
+ close: T,
82
115
  __isVeloEventStream: !0,
83
- __config: t,
84
- __listeners: a,
85
- __buffers: n,
86
- __path: t.path,
87
- __onConnect(e, r) {
88
- let o = a.get(e);
89
- const s = !o || o.size === 0;
90
- o || (o = /* @__PURE__ */ new Set(), a.set(e, o)), o.add(r), w++, b(), s && d(e);
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, r) {
93
- const o = a.get(e);
94
- o && (o.delete(r), o.size === 0 && (a.delete(e), S(e)), w--, g());
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 a.values())
98
- for (const r of e)
130
+ for (const e of o.values())
131
+ for (const t of e)
99
132
  try {
100
- r.close();
133
+ t.close();
101
134
  } catch {
102
135
  }
103
- a.clear(), n.clear(), l.clear();
104
- for (const e of h.values()) clearTimeout(e);
105
- h.clear(), c && (c.abort(), c = null);
106
- for (const e of i.values()) e.abort();
107
- i.clear(), w = 0;
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 t.path && A().pendingStreamRoutes.push((e) => {
111
- P(e, t.path, p, t.middlewares ?? []);
112
- }), I.add(p), p;
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 B(t) {
115
- return async (a, { abortSignal: n }) => {
116
- for (; !n.aborted; ) {
149
+ function z(r) {
150
+ return async (b, { abortSignal: o }) => {
151
+ for (; !o.aborted; ) {
117
152
  try {
118
- await t.tick(a);
119
- } catch (l) {
120
- console.error("[velojs] poll tick threw:", l);
153
+ await r.tick(b);
154
+ } catch (a) {
155
+ console.error("[velojs] poll tick threw:", a);
121
156
  }
122
- await new Promise((l) => {
123
- const h = setTimeout(l, t.intervalMs);
124
- n.addEventListener(
157
+ await new Promise((a) => {
158
+ const p = setTimeout(a, r.intervalMs);
159
+ o.addEventListener(
125
160
  "abort",
126
161
  () => {
127
- clearTimeout(h), l();
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 j(t) {
136
- return t.req.query("channel") ?? m;
170
+ function L(r) {
171
+ return r.req.query("channel") ?? M;
137
172
  }
138
- function P(t, a, n, l = []) {
139
- const h = async (c) => {
140
- const { streamSSE: w } = await import("hono/streaming");
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 (n.__config.broadcast)
143
- i = m;
177
+ if (o.__config.broadcast)
178
+ i = M;
144
179
  else {
145
- const d = n.__config.channel ?? j;
146
- let S;
180
+ const d = o.__config.channel ?? L;
181
+ let m;
147
182
  try {
148
- S = await d(c);
149
- } catch (_) {
150
- return console.error("[velojs] channel resolver threw:", _), c.json({ error: "internal" }, 500);
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 (S == null)
153
- return c.json({ error: "forbidden" }, 403);
154
- i = S;
187
+ if (m == null)
188
+ return l.json({ error: "forbidden" }, 403);
189
+ i = m;
155
190
  }
156
- const b = n.__config.heartbeatMs, g = b === !1 ? 0 : b ?? M;
157
- return w(c, async (d) => {
158
- const S = n.__buffers.get(i);
159
- if (S && S.length > 0 && await d.writeSSE({
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(S)
162
- }), n.__config.snapshot) {
163
- const u = await n.__config.snapshot(
164
- i === m ? void 0 : 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
- u !== void 0 && await d.writeSSE({
201
+ t !== void 0 && await d.writeSSE({
167
202
  event: "snapshot",
168
- data: JSON.stringify(u)
203
+ data: JSON.stringify(t)
169
204
  });
170
205
  }
171
- let _;
172
- const C = new Promise((u) => {
173
- _ = u;
206
+ let C;
207
+ const P = new Promise((t) => {
208
+ C = t;
174
209
  });
175
- let p = !1, e = null, r = Promise.resolve();
176
- const o = (u) => (r = r.then(() => d.writeSSE(u)).catch(() => {
177
- }), r), s = () => {
178
- p || (p = !0, e && clearInterval(e), n.__onDisconnect(i, f), _());
179
- }, f = ((u) => {
180
- p || (o({ data: JSON.stringify(u) }), n.__config.closeOn?.(u) && r.then(() => {
181
- d.close(), s();
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
- f.close = () => {
185
- p || (o({ event: "close", data: "" }), r.then(() => {
186
- d.close(), s();
219
+ e.close = () => {
220
+ w || (T({ event: "close", data: "" }), v.then(() => {
221
+ d.close(), c();
187
222
  }));
188
- }, n.__onConnect(i, f), e = g > 0 ? setInterval(() => {
189
- p || o({ event: "heartbeat", data: "" });
190
- }, g) : null, d.onAbort(s), await C;
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
- l.length > 0 ? t.on(["GET"], [a], ...l, h) : t.on(["GET"], [a], h);
232
+ a.length > 0 ? r.on(["GET"], [b], ...a, p) : r.on(["GET"], [b], p);
194
233
  }
195
234
  export {
196
- L as createEventStream,
197
- x as flushPendingStreamRoutes,
198
- y as getRegisteredStreams,
199
- B as poll,
200
- P as registerStreamHandler
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 {};