@mauroandre/velojs 0.0.22 → 0.0.23
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 +55 -0
- package/dist/app-context.d.ts +38 -0
- package/dist/app-context.js +43 -0
- package/dist/events.d.ts +4 -0
- package/dist/events.js +124 -105
- package/dist/server.js +96 -95
- package/dist/testing/conventions.d.ts +25 -0
- package/dist/testing/createTestApp.d.ts +23 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.js +488 -0
- package/dist/testing/internal.d.ts +30 -0
- package/dist/testing/mockContext.d.ts +10 -0
- package/dist/testing/subscription.d.ts +11 -0
- package/dist/testing/types.d.ts +125 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1188,6 +1188,60 @@ Hooks (`useParams`, `useQuery`, `usePathname`, `Loader`, `useLoader`) access thi
|
|
|
1188
1188
|
|
|
1189
1189
|
---
|
|
1190
1190
|
|
|
1191
|
+
## Testing
|
|
1192
|
+
|
|
1193
|
+
VeloJS ships a backend testing toolkit at `@mauroandre/velojs/testing`. Spin up the app in memory, fire HTTP requests against the registered handlers, subscribe to event streams. No socket, no browser, no fragile mocks of framework internals.
|
|
1194
|
+
|
|
1195
|
+
```typescript
|
|
1196
|
+
import { createTestApp } from "@mauroandre/velojs/testing";
|
|
1197
|
+
import { routes } from "../app/routes.js";
|
|
1198
|
+
import { stream_progress } from "../app/Deploy.js";
|
|
1199
|
+
import { action_startDeploy } from "../app/Deploy.js";
|
|
1200
|
+
|
|
1201
|
+
const app = await createTestApp({
|
|
1202
|
+
routes,
|
|
1203
|
+
bootstrap: async () => { await connect(process.env.MONGO_URI!); },
|
|
1204
|
+
getSessionCookie: async ({ user }) => ({ session: await sign(user) }),
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
// HTTP
|
|
1208
|
+
const res = await app.get("/api/health");
|
|
1209
|
+
|
|
1210
|
+
// Convention helpers — pass the function, framework resolves the URL
|
|
1211
|
+
const data = await app.loader(homeLoader, { params: { id: "abc" } });
|
|
1212
|
+
await app.action(action_startDeploy, { body: { appId } });
|
|
1213
|
+
|
|
1214
|
+
// Streams — TestSubscription with snapshot, next(), close, etc
|
|
1215
|
+
const sub = await app.subscribe(stream_progress, { channel: appId });
|
|
1216
|
+
expect(sub.status).toBe(200);
|
|
1217
|
+
const event = await sub.next({ timeoutMs: 2000 });
|
|
1218
|
+
|
|
1219
|
+
// Auth — sub-client with cookies bound automatically
|
|
1220
|
+
const asAlice = app.as({ user: alice });
|
|
1221
|
+
await asAlice.subscribe(stream_progress, { channel: appId });
|
|
1222
|
+
|
|
1223
|
+
await app.close();
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
**Prerequisite:** `vitest.config.ts` must include `veloPlugin()` so action/loader/stream metadata is injected.
|
|
1227
|
+
|
|
1228
|
+
| API | Purpose |
|
|
1229
|
+
|-----|---------|
|
|
1230
|
+
| `createTestApp(options)` | Build isolated app with bootstrap + auth callback |
|
|
1231
|
+
| `app.get/post/put/patch/delete` | HTTP requests (cookies, headers, query, JSON/FormData body) |
|
|
1232
|
+
| `app.action(fn, opts)` | Invoke `action_*` by function reference |
|
|
1233
|
+
| `app.loader(fn, opts)` | Invoke `loader` and unwrap response data |
|
|
1234
|
+
| `app.subscribe(stream, opts)` | Subscribe to a stream; returns `TestSubscription` with `next/nextN/snapshot/close/closed` |
|
|
1235
|
+
| `app.as({ user })` | Sub-client with cookies bound to a user |
|
|
1236
|
+
| `app.sessionCookies({ user })` | Build cookies via `getSessionCookie` |
|
|
1237
|
+
| `app.mockContext(opts)` | Escape hatch — partial Hono Context for direct invocation |
|
|
1238
|
+
| `app.reset()` | Clear stream buffers/listeners between tests |
|
|
1239
|
+
| `app.close()` | Tear down everything (zero open handles guaranteed) |
|
|
1240
|
+
|
|
1241
|
+
See [Testing docs](https://github.com/mauro-andre/velojs/blob/dev/site/docs/16-testing.md) for the full guide with patterns, isolation, and FAQ.
|
|
1242
|
+
|
|
1243
|
+
---
|
|
1244
|
+
|
|
1191
1245
|
## Subpath Exports
|
|
1192
1246
|
|
|
1193
1247
|
| Import | Contents |
|
|
@@ -1197,6 +1251,7 @@ Hooks (`useParams`, `useQuery`, `usePathname`, `Loader`, `useLoader`) access thi
|
|
|
1197
1251
|
| `@mauroandre/velojs/client` | `startClient` |
|
|
1198
1252
|
| `@mauroandre/velojs/hooks` | `Loader`, `useLoader`, `useEventStream`, `useParams`, `useQuery`, `useNavigate`, `usePathname`, `touch` |
|
|
1199
1253
|
| `@mauroandre/velojs/events` | `createEventStream`, `poll`, `EventStream`, `EventStreamConfig`, `EmitFn`, `EmitOptions`, `SourceFn`, `PerChannelSourceFn`, `ChannelResolver` (also re-exported from root) |
|
|
1254
|
+
| `@mauroandre/velojs/testing` | `createTestApp`, `TestApp`, `TestResponse`, `TestSubscription`, `CreateTestAppOptions`, `MockContextOptions` |
|
|
1200
1255
|
| `@mauroandre/velojs/cookie` | `getCookie`, `setCookie`, `deleteCookie`, `getSignedCookie`, `setSignedCookie` |
|
|
1201
1256
|
| `@mauroandre/velojs/factory` | `createMiddleware`, `createFactory` |
|
|
1202
1257
|
| `@mauroandre/velojs/vite` | `veloPlugin` |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App context — scoped via a swappable global slot so multiple test apps
|
|
3
|
+
* can coexist without polluting each other's pending route registrations.
|
|
4
|
+
*
|
|
5
|
+
* Production runtime uses a single default context implicitly. Tests create
|
|
6
|
+
* isolated contexts via `withAppContext()` which swaps the slot for the
|
|
7
|
+
* duration of the call.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: this module is client-safe (no `node:async_hooks` import). Vitest runs
|
|
10
|
+
* each test file in its own worker, so a simple swappable slot is sufficient
|
|
11
|
+
* for parallelism between files. Within a single file, tests run sequentially
|
|
12
|
+
* unless `describe.concurrent`/`it.concurrent` is used.
|
|
13
|
+
*/
|
|
14
|
+
import type { Hono } from "hono";
|
|
15
|
+
type RouteRegisterFn = (app: Hono) => void | Promise<void>;
|
|
16
|
+
type StreamRouteRegisterFn = (app: Hono) => void;
|
|
17
|
+
type ServerCallbackFn = (server: any) => void;
|
|
18
|
+
export interface AppContext {
|
|
19
|
+
pendingRoutes: RouteRegisterFn[];
|
|
20
|
+
pendingStreamRoutes: StreamRouteRegisterFn[];
|
|
21
|
+
serverCallbacks: ServerCallbackFn[];
|
|
22
|
+
disposers: Set<() => void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function getAppContext(): AppContext;
|
|
25
|
+
/**
|
|
26
|
+
* Run a function within an isolated app context (for tests).
|
|
27
|
+
* Restores the previous context after the function resolves/rejects.
|
|
28
|
+
*/
|
|
29
|
+
export declare function withAppContext<T>(ctx: AppContext, fn: () => T | Promise<T>): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Create a new isolated context. Inherits a SNAPSHOT of the default context
|
|
32
|
+
* so any addRoutes/onServer/createEventStream calls that happened at top-level
|
|
33
|
+
* import time are captured.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createIsolatedContext(): AppContext;
|
|
36
|
+
export declare function registerDisposer(dispose: () => void): void;
|
|
37
|
+
export declare function unregisterDisposer(dispose: () => void): void;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function o() {
|
|
2
|
+
return {
|
|
3
|
+
pendingRoutes: [],
|
|
4
|
+
pendingStreamRoutes: [],
|
|
5
|
+
serverCallbacks: [],
|
|
6
|
+
disposers: /* @__PURE__ */ new Set()
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
const n = o();
|
|
10
|
+
let e = n;
|
|
11
|
+
function i() {
|
|
12
|
+
return e;
|
|
13
|
+
}
|
|
14
|
+
async function p(t, s) {
|
|
15
|
+
const r = e;
|
|
16
|
+
e = t;
|
|
17
|
+
try {
|
|
18
|
+
return await s();
|
|
19
|
+
} finally {
|
|
20
|
+
e = r;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function u() {
|
|
24
|
+
return {
|
|
25
|
+
pendingRoutes: [...n.pendingRoutes],
|
|
26
|
+
pendingStreamRoutes: [...n.pendingStreamRoutes],
|
|
27
|
+
serverCallbacks: [...n.serverCallbacks],
|
|
28
|
+
disposers: /* @__PURE__ */ new Set()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function a(t) {
|
|
32
|
+
e.disposers.add(t);
|
|
33
|
+
}
|
|
34
|
+
function d(t) {
|
|
35
|
+
e.disposers.delete(t);
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
u as createIsolatedContext,
|
|
39
|
+
i as getAppContext,
|
|
40
|
+
a as registerDisposer,
|
|
41
|
+
d as unregisterDisposer,
|
|
42
|
+
p as withAppContext
|
|
43
|
+
};
|
package/dist/events.d.ts
CHANGED
|
@@ -221,6 +221,8 @@ export interface EventStream<TEvent, TSnapshot = TEvent> {
|
|
|
221
221
|
__onConnect(channelKey: string, listener: StreamListener<TEvent>): void;
|
|
222
222
|
/** Internal: source lifecycle hook */
|
|
223
223
|
__onDisconnect(channelKey: string, listener: StreamListener<TEvent>): void;
|
|
224
|
+
/** @internal Test-only: reset all transient state (buffers, listeners, controllers, timers). */
|
|
225
|
+
__reset(): void;
|
|
224
226
|
}
|
|
225
227
|
/** Internal listener — receives events and a close signal. */
|
|
226
228
|
interface StreamListener<TEvent> {
|
|
@@ -228,6 +230,8 @@ interface StreamListener<TEvent> {
|
|
|
228
230
|
close(): void;
|
|
229
231
|
}
|
|
230
232
|
export declare function flushPendingStreamRoutes(app: Hono): void;
|
|
233
|
+
/** @internal Used by `@mauroandre/velojs/testing` to reset all streams. */
|
|
234
|
+
export declare function getRegisteredStreams(): ReadonlySet<EventStream<unknown, unknown>>;
|
|
231
235
|
export declare function createEventStream<TEvent, TSnapshot = TEvent>(config?: EventStreamConfig<TEvent, TSnapshot>): EventStream<TEvent, TSnapshot>;
|
|
232
236
|
/**
|
|
233
237
|
* Creates a `source` function that runs `tick` every `intervalMs` while subscribed.
|
package/dist/events.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getAppContext as A } from "./app-context.js";
|
|
2
|
+
const M = 2e4, R = 300 * 1e3, v = "";
|
|
3
|
+
function x(t) {
|
|
4
|
+
const o = A().pendingStreamRoutes.splice(0);
|
|
5
|
+
for (const c of o) c(t);
|
|
6
|
+
}
|
|
7
|
+
const I = /* @__PURE__ */ new Set();
|
|
8
|
+
function y() {
|
|
9
|
+
return I;
|
|
5
10
|
}
|
|
6
11
|
function L(t = {}) {
|
|
7
12
|
if (t.broadcast && t.channel && console.warn(
|
|
@@ -10,103 +15,116 @@ function L(t = {}) {
|
|
|
10
15
|
throw new Error(
|
|
11
16
|
"[velojs] createEventStream: `source` and `perChannelSource` are mutually exclusive. Use `source` for stream-wide producers or `perChannelSource` for per-channel resources, not both."
|
|
12
17
|
);
|
|
13
|
-
const a = /* @__PURE__ */ new Map(),
|
|
14
|
-
let
|
|
15
|
-
const
|
|
16
|
-
t.source && (
|
|
17
|
-
t.source(
|
|
18
|
+
const a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Map();
|
|
19
|
+
let l = null, w = 0;
|
|
20
|
+
const i = /* @__PURE__ */ new Map(), b = () => {
|
|
21
|
+
t.source && (l || (l = new AbortController(), Promise.resolve(
|
|
22
|
+
t.source(p, { abortSignal: l.signal })
|
|
18
23
|
).catch((e) => {
|
|
19
24
|
console.error("[velojs] event stream source threw:", e);
|
|
20
25
|
})));
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
if (!t.perChannelSource ||
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
26
|
+
}, m = () => {
|
|
27
|
+
w > 0 || l && (l.abort(), l = null);
|
|
28
|
+
}, u = (e) => {
|
|
29
|
+
if (!t.perChannelSource || i.has(e)) return;
|
|
30
|
+
const n = new AbortController();
|
|
31
|
+
i.set(e, n);
|
|
32
|
+
const s = (r, S) => {
|
|
28
33
|
h.emit(e, r, S);
|
|
29
34
|
};
|
|
30
35
|
Promise.resolve(
|
|
31
|
-
t.perChannelSource(e,
|
|
36
|
+
t.perChannelSource(e, s, { abortSignal: n.signal })
|
|
32
37
|
).catch((r) => {
|
|
33
38
|
console.error(
|
|
34
39
|
`[velojs] perChannelSource threw for channel "${e}":`,
|
|
35
40
|
r
|
|
36
41
|
);
|
|
37
42
|
});
|
|
38
|
-
},
|
|
39
|
-
const
|
|
40
|
-
|
|
43
|
+
}, d = (e) => {
|
|
44
|
+
const n = i.get(e);
|
|
45
|
+
n && (n.abort(), i.delete(e));
|
|
41
46
|
};
|
|
42
|
-
function
|
|
43
|
-
let r, S,
|
|
44
|
-
if (!!t.broadcast ? (r =
|
|
47
|
+
function p(e, n, s) {
|
|
48
|
+
let r, S, E;
|
|
49
|
+
if (!!t.broadcast ? (r = v, S = e, E = n) : (r = e, S = n, E = s), c.has(r)) {
|
|
45
50
|
console.warn(
|
|
46
51
|
`[velojs] emit() called on closed channel "${r}" — ignored. Reopen the channel by emitting to a fresh ID, or remove the close() call.`
|
|
47
52
|
);
|
|
48
53
|
return;
|
|
49
54
|
}
|
|
50
|
-
if (
|
|
51
|
-
const
|
|
52
|
-
|
|
55
|
+
if (E?.snapshot) {
|
|
56
|
+
const _ = o.get(r) ?? [];
|
|
57
|
+
_.push(S);
|
|
53
58
|
const T = t.bufferSize ?? 1 / 0;
|
|
54
|
-
|
|
59
|
+
_.length > T && _.splice(0, _.length - T), o.set(r, _);
|
|
55
60
|
}
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
for (const
|
|
61
|
+
const C = a.get(r);
|
|
62
|
+
if (C)
|
|
63
|
+
for (const _ of C) _(S);
|
|
59
64
|
}
|
|
60
|
-
function
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
for (const S of
|
|
66
|
-
const
|
|
67
|
-
r && clearTimeout(r),
|
|
65
|
+
function g(e = v) {
|
|
66
|
+
if (c.has(e)) return;
|
|
67
|
+
c.add(e);
|
|
68
|
+
const n = a.get(e);
|
|
69
|
+
if (n)
|
|
70
|
+
for (const S of n) S.close();
|
|
71
|
+
const s = t.retainMs ?? R, r = f.get(e);
|
|
72
|
+
r && clearTimeout(r), f.set(
|
|
68
73
|
e,
|
|
69
74
|
setTimeout(() => {
|
|
70
|
-
|
|
71
|
-
},
|
|
75
|
+
o.delete(e), f.delete(e), c.delete(e);
|
|
76
|
+
}, s)
|
|
72
77
|
);
|
|
73
78
|
}
|
|
74
79
|
const h = {
|
|
75
|
-
emit:
|
|
76
|
-
close:
|
|
80
|
+
emit: p,
|
|
81
|
+
close: g,
|
|
77
82
|
__isVeloEventStream: !0,
|
|
78
83
|
__config: t,
|
|
79
84
|
__listeners: a,
|
|
80
|
-
__buffers:
|
|
85
|
+
__buffers: o,
|
|
81
86
|
__path: t.path,
|
|
82
|
-
__onConnect(e,
|
|
83
|
-
let
|
|
84
|
-
const r = !
|
|
85
|
-
|
|
87
|
+
__onConnect(e, n) {
|
|
88
|
+
let s = a.get(e);
|
|
89
|
+
const r = !s || s.size === 0;
|
|
90
|
+
s || (s = /* @__PURE__ */ new Set(), a.set(e, s)), s.add(n), w++, b(), r && u(e);
|
|
91
|
+
},
|
|
92
|
+
__onDisconnect(e, n) {
|
|
93
|
+
const s = a.get(e);
|
|
94
|
+
s && (s.delete(n), s.size === 0 && (a.delete(e), d(e)), w--, m());
|
|
86
95
|
},
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
96
|
+
__reset() {
|
|
97
|
+
for (const e of a.values())
|
|
98
|
+
for (const n of e)
|
|
99
|
+
try {
|
|
100
|
+
n.close();
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
a.clear(), o.clear(), c.clear();
|
|
104
|
+
for (const e of f.values()) clearTimeout(e);
|
|
105
|
+
f.clear(), l && (l.abort(), l = null);
|
|
106
|
+
for (const e of i.values()) e.abort();
|
|
107
|
+
i.clear(), w = 0;
|
|
90
108
|
}
|
|
91
109
|
};
|
|
92
|
-
return t.path &&
|
|
93
|
-
|
|
94
|
-
}), h;
|
|
110
|
+
return t.path && A().pendingStreamRoutes.push((e) => {
|
|
111
|
+
D(e, t.path, h, t.middlewares ?? []);
|
|
112
|
+
}), I.add(h), h;
|
|
95
113
|
}
|
|
96
|
-
function
|
|
97
|
-
return async (a, { abortSignal:
|
|
98
|
-
for (; !
|
|
114
|
+
function B(t) {
|
|
115
|
+
return async (a, { abortSignal: o }) => {
|
|
116
|
+
for (; !o.aborted; ) {
|
|
99
117
|
try {
|
|
100
118
|
await t.tick(a);
|
|
101
|
-
} catch (
|
|
102
|
-
console.error("[velojs] poll tick threw:",
|
|
119
|
+
} catch (c) {
|
|
120
|
+
console.error("[velojs] poll tick threw:", c);
|
|
103
121
|
}
|
|
104
|
-
await new Promise((
|
|
105
|
-
const
|
|
106
|
-
|
|
122
|
+
await new Promise((c) => {
|
|
123
|
+
const f = setTimeout(c, t.intervalMs);
|
|
124
|
+
o.addEventListener(
|
|
107
125
|
"abort",
|
|
108
126
|
() => {
|
|
109
|
-
clearTimeout(
|
|
127
|
+
clearTimeout(f), c();
|
|
110
128
|
},
|
|
111
129
|
{ once: !0 }
|
|
112
130
|
);
|
|
@@ -114,67 +132,68 @@ function M(t) {
|
|
|
114
132
|
}
|
|
115
133
|
};
|
|
116
134
|
}
|
|
117
|
-
function
|
|
118
|
-
return t.req.query("channel") ??
|
|
135
|
+
function j(t) {
|
|
136
|
+
return t.req.query("channel") ?? v;
|
|
119
137
|
}
|
|
120
|
-
function
|
|
121
|
-
const
|
|
122
|
-
const { streamSSE:
|
|
123
|
-
let
|
|
124
|
-
if (
|
|
125
|
-
|
|
138
|
+
function D(t, a, o, c = []) {
|
|
139
|
+
const f = async (l) => {
|
|
140
|
+
const { streamSSE: w } = await import("hono/streaming");
|
|
141
|
+
let i;
|
|
142
|
+
if (o.__config.broadcast)
|
|
143
|
+
i = v;
|
|
126
144
|
else {
|
|
127
|
-
const
|
|
128
|
-
let
|
|
145
|
+
const u = o.__config.channel ?? j;
|
|
146
|
+
let d;
|
|
129
147
|
try {
|
|
130
|
-
|
|
131
|
-
} catch (
|
|
132
|
-
return console.error("[velojs] channel resolver threw:",
|
|
148
|
+
d = await u(l);
|
|
149
|
+
} catch (p) {
|
|
150
|
+
return console.error("[velojs] channel resolver threw:", p), l.json({ error: "internal" }, 500);
|
|
133
151
|
}
|
|
134
|
-
if (
|
|
135
|
-
return
|
|
136
|
-
|
|
152
|
+
if (d == null)
|
|
153
|
+
return l.json({ error: "forbidden" }, 403);
|
|
154
|
+
i = d;
|
|
137
155
|
}
|
|
138
|
-
const
|
|
139
|
-
return
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
156
|
+
const b = o.__config.heartbeatMs, m = b === !1 ? 0 : b ?? M;
|
|
157
|
+
return w(l, async (u) => {
|
|
158
|
+
const d = o.__buffers.get(i);
|
|
159
|
+
if (d && d.length > 0 && await u.writeSSE({
|
|
142
160
|
event: "snapshot",
|
|
143
|
-
data: JSON.stringify(
|
|
144
|
-
}),
|
|
145
|
-
const r = await
|
|
146
|
-
|
|
161
|
+
data: JSON.stringify(d)
|
|
162
|
+
}), o.__config.snapshot) {
|
|
163
|
+
const r = await o.__config.snapshot(
|
|
164
|
+
i === v ? void 0 : i
|
|
147
165
|
);
|
|
148
|
-
r !== void 0 && await
|
|
166
|
+
r !== void 0 && await u.writeSSE({
|
|
149
167
|
event: "snapshot",
|
|
150
168
|
data: JSON.stringify(r)
|
|
151
169
|
});
|
|
152
170
|
}
|
|
153
|
-
let
|
|
154
|
-
const
|
|
155
|
-
|
|
171
|
+
let p;
|
|
172
|
+
const g = new Promise((r) => {
|
|
173
|
+
p = r;
|
|
156
174
|
});
|
|
157
175
|
let h = !1, e = null;
|
|
158
|
-
const
|
|
159
|
-
h || (h = !0, e && clearInterval(e),
|
|
160
|
-
},
|
|
161
|
-
h || (
|
|
162
|
-
}),
|
|
176
|
+
const n = () => {
|
|
177
|
+
h || (h = !0, e && clearInterval(e), o.__onDisconnect(i, s), p());
|
|
178
|
+
}, s = ((r) => {
|
|
179
|
+
h || (u.writeSSE({ data: JSON.stringify(r) }).catch(() => {
|
|
180
|
+
}), o.__config.closeOn?.(r) && (u.close(), n()));
|
|
163
181
|
});
|
|
164
|
-
|
|
165
|
-
h || (
|
|
166
|
-
}),
|
|
167
|
-
},
|
|
168
|
-
h ||
|
|
182
|
+
s.close = () => {
|
|
183
|
+
h || (u.writeSSE({ event: "close", data: "" }).catch(() => {
|
|
184
|
+
}), u.close(), n());
|
|
185
|
+
}, o.__onConnect(i, s), e = m > 0 ? setInterval(() => {
|
|
186
|
+
h || u.writeSSE({ event: "heartbeat", data: "" }).catch(() => {
|
|
169
187
|
});
|
|
170
|
-
},
|
|
188
|
+
}, m) : null, u.onAbort(n), await g;
|
|
171
189
|
});
|
|
172
190
|
};
|
|
173
|
-
|
|
191
|
+
c.length > 0 ? t.on(["GET"], [a], ...c, f) : t.on(["GET"], [a], f);
|
|
174
192
|
}
|
|
175
193
|
export {
|
|
176
194
|
L as createEventStream,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
195
|
+
x as flushPendingStreamRoutes,
|
|
196
|
+
y as getRegisteredStreams,
|
|
197
|
+
B as poll,
|
|
198
|
+
D as registerStreamHandler
|
|
180
199
|
};
|