statewire 0.8.1 → 0.9.1
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 +2 -2
- package/dist/client/connection.d.ts +23 -19
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +143 -103
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +30 -3
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +222 -260
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +34 -39
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +380 -373
- package/dist/client/session.js.map +1 -1
- package/dist/client/sessionStore.d.ts +1 -0
- package/dist/client/sessionStore.d.ts.map +1 -1
- package/dist/client/sessionStore.js.map +1 -1
- package/dist/client/timing.d.ts +1 -2
- package/dist/client/timing.d.ts.map +1 -1
- package/dist/client/timing.js +1 -5
- package/dist/client/timing.js.map +1 -1
- package/dist/host/draft.d.ts +2 -5
- package/dist/host/draft.d.ts.map +1 -1
- package/dist/host/draft.js.map +1 -1
- package/dist/host/host.d.ts +5 -8
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +23 -32
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts +3 -2
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +484 -424
- package/dist/host/socket.js.map +1 -1
- package/dist/host-internal.d.ts +4 -0
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host.d.ts +10 -10
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/statewire.d.ts +29 -24
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +8 -2
- package/dist/statewire.js.map +1 -1
- package/dist/transports/fake-sse.d.ts +1 -1
- package/dist/transports/fake-sse.d.ts.map +1 -1
- package/dist/transports/fake-sse.js +5 -2
- package/dist/transports/fake-sse.js.map +1 -1
- package/dist/transports/fixtures.d.ts +1 -2
- package/dist/transports/fixtures.d.ts.map +1 -1
- package/dist/transports/fixtures.js +5 -7
- package/dist/transports/fixtures.js.map +1 -1
- package/dist/transports/remote.d.ts +2 -1
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +10 -4
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +32 -46
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +56 -59
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.d.ts +7 -5
- package/dist/transports/testing.d.ts.map +1 -1
- package/dist/transports/testing.js +41 -14
- package/dist/transports/testing.js.map +1 -1
- package/dist/transports/ws.d.ts +2 -3
- package/dist/transports/ws.d.ts.map +1 -1
- package/dist/transports/ws.js +25 -80
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +8 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -4
- package/dist/types.js.map +1 -1
- package/dist/wire/ops.d.ts +1 -5
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +6 -5
- package/dist/wire/ops.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +175 -128
- package/src/client/core.ts +287 -278
- package/src/client/session.ts +474 -480
- package/src/client/sessionStore.ts +1 -0
- package/src/client/timing.ts +0 -6
- package/src/host/draft.ts +1 -3
- package/src/host/host.ts +41 -58
- package/src/host/socket.ts +653 -448
- package/src/host-internal.ts +4 -0
- package/src/host.ts +13 -10
- package/src/index.ts +0 -1
- package/src/statewire.ts +38 -32
- package/src/transports/fake-sse.ts +2 -1
- package/src/transports/fixtures.ts +4 -9
- package/src/transports/remote.ts +12 -5
- package/src/transports/sse.ts +99 -115
- package/src/transports/testing.ts +47 -19
- package/src/transports/ws.ts +44 -115
- package/src/types.ts +15 -14
- package/src/wire/ops.ts +13 -16
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# statewire
|
|
2
2
|
|
|
3
|
-
React/TypeScript client for the **Statewire** protocol: a server owns a JSON state object and streams it to clients
|
|
3
|
+
React/TypeScript client for the **Statewire** protocol: a server owns a JSON state object and streams it to clients as packets (`POST /stream` attach, hello-snapshot first); clients change it by submitting stamped statement frames (`POST /frames`, `{"cmd": [{"method", "params", "seq"}]}`). The client keeps your UI on the canonical state with optimistic updates rebased on top, survives disconnects, evictions, and reloads of the server, and tells you exactly what happened to every command you sent.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { useStatewire, StatewireSSE } from "statewire";
|
|
@@ -9,7 +9,7 @@ type Commands = {
|
|
|
9
9
|
increment(): void;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const { state, connection, commands } = useStatewire<State, Commands
|
|
12
|
+
const { state, connection, commands } = useStatewire<State, Commands>({
|
|
13
13
|
transport: StatewireSSE({ url: "/api/threads/demo" }),
|
|
14
14
|
optimistic: (draft, command) => {
|
|
15
15
|
if (command.method === "increment") draft.count += 1;
|
|
@@ -6,58 +6,62 @@ type Episode = {
|
|
|
6
6
|
cause: "dropped" | "evicted" | "error" | "unauthorized";
|
|
7
7
|
message?: string;
|
|
8
8
|
};
|
|
9
|
-
type
|
|
9
|
+
type StatewireCmd = {
|
|
10
10
|
seq: number;
|
|
11
|
-
type
|
|
11
|
+
type?: "applied" | "result" | "rejected" | "failed" | "crashed" | "expired";
|
|
12
12
|
code?: string;
|
|
13
13
|
message?: string;
|
|
14
14
|
payload?: unknown;
|
|
15
15
|
meta?: unknown;
|
|
16
|
+
/** Cumulative: resending through this seq will never be needed. */
|
|
17
|
+
dur?: boolean;
|
|
16
18
|
};
|
|
17
19
|
type StatewireSyn = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
seq?: number;
|
|
21
|
+
ctxSeq?: number;
|
|
22
|
+
record?: "fresh" | "resumed" | "ephemeral";
|
|
23
|
+
id?: string;
|
|
24
|
+
idle?: boolean;
|
|
25
|
+
retry?: number;
|
|
20
26
|
};
|
|
21
27
|
type StatewireEnvelope = {
|
|
22
28
|
ops?: unknown[];
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
cmd?: StatewireCmd[];
|
|
30
|
+
next?: true;
|
|
31
|
+
idle?: boolean;
|
|
25
32
|
syn?: StatewireSyn;
|
|
26
33
|
fin?: StatewireHost.Fin;
|
|
27
34
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
open: () => void;
|
|
35
|
+
type Gate = {
|
|
36
|
+
latched: boolean;
|
|
37
|
+
wake: (() => void) | undefined;
|
|
32
38
|
};
|
|
33
|
-
type
|
|
39
|
+
type AttemptOutcome = "stop" | "reattach" | "ended" | "idle";
|
|
34
40
|
type Conn = {
|
|
35
41
|
connection: StatewireClient.Connection;
|
|
36
42
|
hasSnapshot: boolean;
|
|
37
43
|
lease: string | null;
|
|
38
44
|
attempt: number;
|
|
39
45
|
episode: Episode | null;
|
|
46
|
+
awaitingHello: boolean;
|
|
47
|
+
held: StatewireCmd[];
|
|
40
48
|
gate: Gate;
|
|
41
49
|
abort: AbortController | null;
|
|
42
50
|
lifetime: AbortController | null;
|
|
43
51
|
ctx: StatewireRemoteProps;
|
|
44
52
|
onChange: (connection: StatewireClient.Connection) => void;
|
|
45
|
-
attemptOnce: (abort: AbortController) => Promise<
|
|
53
|
+
attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;
|
|
46
54
|
notify: (connection: StatewireClient.Connection) => void;
|
|
47
55
|
reconnect: () => void;
|
|
48
56
|
};
|
|
49
|
-
declare const handleEnvelope: (c: Conn, env: StatewireEnvelope) =>
|
|
57
|
+
declare const handleEnvelope: (c: Conn, env: StatewireEnvelope) => AttemptOutcome | null;
|
|
50
58
|
declare const useStatewireConn: (props: {
|
|
51
59
|
url: string;
|
|
52
60
|
onChange?: ((connection: StatewireClient.Connection) => void) | undefined;
|
|
53
61
|
ctx: StatewireRemoteProps;
|
|
54
|
-
attemptOnce: (c: Conn, abort: AbortController) => Promise<
|
|
62
|
+
attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;
|
|
55
63
|
}) => Conn;
|
|
56
64
|
declare const classify: (timeout: AbortSignal) => Error;
|
|
57
|
-
declare const parseRetryAfter: (header: string | null) => number | undefined;
|
|
58
|
-
declare const overloadedError: (retryAfterMs?: number) => Error & {
|
|
59
|
-
retryAfterMs: number | undefined;
|
|
60
|
-
};
|
|
61
65
|
//#endregion
|
|
62
|
-
export { Conn, Episode, Gate,
|
|
66
|
+
export { AttemptOutcome, Conn, Episode, Gate, StatewireCmd, StatewireEnvelope, StatewireSyn, classify, handleEnvelope, useStatewireConn };
|
|
63
67
|
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;;KAGU;EACV;EACA,MAAM;EACN;EACA,MAAM;EACN,MAAM,cAAc;;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA,MAAM;EACN;EACA;EACA,MAAM;EACN,MAAM,cAAc;;KAGV;EAAS;EAAkB;;KAoC3B;KAEA;EACV,YAAY,gBAAgB;EAC5B;EACA;EACA;EACA,SAAS;EACT;EACA,MAAM;EACN,MAAM;EACN,OAAO;EACP,UAAU;EACV,KAAK;EACL,WAAW,YAAY,gBAAgB;EACvC,cAAc,OAAO,oBAAoB,QAAQ;EACjD,SAAS,YAAY,gBAAgB;EACrC;;cAyGW,iBACX,GAAG,MACH,KAAK,sBACJ;cAwFU,mBAAoB;EAC/B;EACA,aAAa,YAAY,gBAAgB;EACzC,KAAK;EACL,cAAc,GAAG,MAAM,OAAO,oBAAoB,QAAQ;MACxD;cAkDS,WAAY,SAAS,gBAAW"}
|
|
@@ -1,39 +1,37 @@
|
|
|
1
|
-
import { reviveOps,
|
|
2
|
-
import {
|
|
1
|
+
import { reviveOps, reviveValue } from "../wire/meta.js";
|
|
2
|
+
import { backoff } from "./timing.js";
|
|
3
3
|
import { useEffect, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
4
4
|
import { flushTapSync } from "@assistant-ui/tap";
|
|
5
5
|
//#region src/client/connection.ts
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
};
|
|
6
|
+
const newGate = () => ({
|
|
7
|
+
latched: false,
|
|
8
|
+
wake: void 0
|
|
9
|
+
});
|
|
10
|
+
const takeGate = (g) => {
|
|
11
|
+
const was = g.latched;
|
|
12
|
+
g.latched = false;
|
|
13
|
+
return was;
|
|
14
|
+
};
|
|
15
|
+
const waitGate = (g, timeoutMs) => {
|
|
16
|
+
if (g.latched) {
|
|
17
|
+
g.latched = false;
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
22
|
+
g.wake = void 0;
|
|
23
|
+
resolve();
|
|
24
|
+
}, timeoutMs);
|
|
25
|
+
g.wake = () => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
g.wake = void 0;
|
|
28
|
+
resolve();
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const openGate = (g) => {
|
|
33
|
+
if (g.wake) g.wake();
|
|
34
|
+
else g.latched = true;
|
|
37
35
|
};
|
|
38
36
|
const setStatus = (c, next) => {
|
|
39
37
|
const changed = next.status !== c.connection.status;
|
|
@@ -44,64 +42,107 @@ const setStatus = (c, next) => {
|
|
|
44
42
|
c.notify(c.connection);
|
|
45
43
|
if (changed) c.onChange(c.connection);
|
|
46
44
|
};
|
|
45
|
+
const protocolError = (message) => {
|
|
46
|
+
throw new Error(`statewire: protocol error: ${message}`);
|
|
47
|
+
};
|
|
48
|
+
const releaseHeld = (c) => {
|
|
49
|
+
if (c.held.length === 0) return;
|
|
50
|
+
const held = c.held;
|
|
51
|
+
c.held = [];
|
|
52
|
+
c.ctx.onCmds(held);
|
|
53
|
+
};
|
|
54
|
+
const cmdsOf = (env) => {
|
|
55
|
+
if (env.cmd === void 0) return [];
|
|
56
|
+
if (!Array.isArray(env.cmd)) return protocolError("cmd must be an array");
|
|
57
|
+
return env.cmd;
|
|
58
|
+
};
|
|
59
|
+
const handleHello = (c, env) => {
|
|
60
|
+
const syn = env.syn;
|
|
61
|
+
if (syn === void 0) return protocolError("expected hello");
|
|
62
|
+
c.awaitingHello = false;
|
|
63
|
+
c.hasSnapshot = true;
|
|
64
|
+
c.ctx.onHello({
|
|
65
|
+
ops: env.ops === void 0 ? [] : reviveOps(env.ops),
|
|
66
|
+
syn
|
|
67
|
+
});
|
|
68
|
+
c.ctx.onIdle(syn.idle ?? false);
|
|
69
|
+
c.attempt = 0;
|
|
70
|
+
c.episode = null;
|
|
71
|
+
setStatus(c, { status: "live" });
|
|
72
|
+
};
|
|
47
73
|
const deliverEnvelope = (c, env) => {
|
|
48
|
-
if (env.syn?.lease !== void 0) c.lease = env.syn.lease;
|
|
49
|
-
const ctx = c.ctx;
|
|
50
|
-
const ops = env.ops === void 0 ? [] : reviveOps(env.ops);
|
|
51
|
-
const res = env.res?.map(reviveResponse);
|
|
52
74
|
if (env.syn !== void 0) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
75
|
+
const syn = env.syn;
|
|
76
|
+
if (syn.seq !== void 0 || syn.ctxSeq !== void 0 || syn.retry !== void 0) c.ctx.onRefusal(syn);
|
|
77
|
+
else if (syn.id === void 0) protocolError("invalid mid-stream syn");
|
|
78
|
+
}
|
|
79
|
+
if (env.ops !== void 0) {
|
|
80
|
+
const held = c.held;
|
|
81
|
+
c.held = [];
|
|
82
|
+
c.ctx.onOps({
|
|
83
|
+
ops: reviveOps(env.ops),
|
|
84
|
+
cmds: [...held, ...cmdsOf(env)]
|
|
56
85
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
} else if (env.cmd !== void 0) {
|
|
87
|
+
const cmds = cmdsOf(env);
|
|
88
|
+
if (env.next === true) c.held.push(...cmds);
|
|
89
|
+
else c.ctx.onCmds(cmds);
|
|
90
|
+
}
|
|
91
|
+
if (typeof env.idle === "boolean") c.ctx.onIdle(env.idle);
|
|
63
92
|
};
|
|
64
93
|
const isTransientFinish = (reason) => reason === "evicted" || reason === "error";
|
|
65
|
-
const
|
|
66
|
-
let finish =
|
|
67
|
-
if (finish) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
c.ctx.onFinish(finish);
|
|
76
|
-
if (isTransientFinish(finish.reason)) {
|
|
77
|
-
c.episode = {
|
|
78
|
-
cause: finish.reason,
|
|
79
|
-
...finish.message !== void 0 && { message: finish.message }
|
|
80
|
-
};
|
|
81
|
-
return "reattach";
|
|
82
|
-
}
|
|
83
|
-
setStatus(c, {
|
|
84
|
-
status: "gone",
|
|
85
|
-
...finish.message !== void 0 && { message: finish.message },
|
|
86
|
-
...finish.payload !== void 0 && { payload: finish.payload }
|
|
87
|
-
});
|
|
88
|
-
return "stop";
|
|
94
|
+
const settleFin = (c, fin) => {
|
|
95
|
+
let finish = fin;
|
|
96
|
+
if (finish.meta !== void 0) {
|
|
97
|
+
const { meta, ...rest } = finish;
|
|
98
|
+
finish = {
|
|
99
|
+
...rest,
|
|
100
|
+
payload: reviveValue(rest.payload, meta)
|
|
101
|
+
};
|
|
89
102
|
}
|
|
90
|
-
|
|
103
|
+
releaseHeld(c);
|
|
104
|
+
if (finish.reason === "idle") {
|
|
105
|
+
c.ctx.onIdle(true);
|
|
91
106
|
c.attempt = 0;
|
|
92
107
|
c.episode = null;
|
|
93
|
-
setStatus(c, { status: "
|
|
108
|
+
setStatus(c, { status: "idle" });
|
|
109
|
+
return "idle";
|
|
94
110
|
}
|
|
95
|
-
|
|
111
|
+
c.ctx.onFinish(finish);
|
|
112
|
+
if (isTransientFinish(finish.reason)) {
|
|
113
|
+
c.episode = {
|
|
114
|
+
cause: finish.reason,
|
|
115
|
+
...finish.message !== void 0 && { message: finish.message }
|
|
116
|
+
};
|
|
117
|
+
return "reattach";
|
|
118
|
+
}
|
|
119
|
+
setStatus(c, {
|
|
120
|
+
status: "gone",
|
|
121
|
+
...finish.message !== void 0 && { message: finish.message },
|
|
122
|
+
...finish.payload !== void 0 && { payload: finish.payload }
|
|
123
|
+
});
|
|
124
|
+
return "stop";
|
|
96
125
|
};
|
|
97
126
|
const handleEnvelope = (c, env) => {
|
|
127
|
+
if (typeof env !== "object" || env === null) return protocolError("packet must be an object");
|
|
128
|
+
if (c.awaitingHello) {
|
|
129
|
+
if (env.fin !== void 0) return settleFin(c, env.fin);
|
|
130
|
+
handleHello(c, env);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
98
133
|
deliverEnvelope(c, env);
|
|
99
|
-
return
|
|
134
|
+
if (env.fin !== void 0) return settleFin(c, env.fin);
|
|
135
|
+
if (c.connection.status !== "live") {
|
|
136
|
+
c.attempt = 0;
|
|
137
|
+
c.episode = null;
|
|
138
|
+
setStatus(c, { status: "live" });
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
100
141
|
};
|
|
101
|
-
const scheduleRetry = (c, failure, immediate
|
|
142
|
+
const scheduleRetry = (c, failure, immediate) => {
|
|
102
143
|
c.attempt += 1;
|
|
103
144
|
c.episode ??= { cause: "dropped" };
|
|
104
|
-
const delay = immediate ? 0 :
|
|
145
|
+
const delay = immediate ? 0 : backoff(c.attempt);
|
|
105
146
|
if (c.hasSnapshot) setStatus(c, {
|
|
106
147
|
status: "reconnecting",
|
|
107
148
|
...c.episode,
|
|
@@ -113,7 +154,7 @@ const scheduleRetry = (c, failure, immediate, cause) => {
|
|
|
113
154
|
};
|
|
114
155
|
const reconnectConn = (c) => {
|
|
115
156
|
if (!c.lifetime) return;
|
|
116
|
-
if (c.connection.status === "gone") {
|
|
157
|
+
if (c.connection.status === "gone" || c.connection.status === "idle") {
|
|
117
158
|
c.attempt = 0;
|
|
118
159
|
c.episode = { cause: "dropped" };
|
|
119
160
|
flushTapSync(() => {
|
|
@@ -121,7 +162,7 @@ const reconnectConn = (c) => {
|
|
|
121
162
|
else setStatus(c, { status: "connecting" });
|
|
122
163
|
});
|
|
123
164
|
}
|
|
124
|
-
c.gate
|
|
165
|
+
openGate(c.gate);
|
|
125
166
|
c.abort?.abort();
|
|
126
167
|
};
|
|
127
168
|
const run = async (c, lifetime, gate) => {
|
|
@@ -129,27 +170,30 @@ const run = async (c, lifetime, gate) => {
|
|
|
129
170
|
while (alive()) {
|
|
130
171
|
const abort = new AbortController();
|
|
131
172
|
c.abort = abort;
|
|
173
|
+
c.awaitingHello = true;
|
|
174
|
+
c.held = [];
|
|
175
|
+
let outcome;
|
|
132
176
|
let failure;
|
|
133
|
-
let cause;
|
|
134
|
-
let gone = false;
|
|
135
177
|
try {
|
|
136
|
-
|
|
178
|
+
outcome = await c.attemptOnce(abort);
|
|
137
179
|
} catch (error) {
|
|
138
|
-
if (!abort.signal.aborted)
|
|
139
|
-
failure = error instanceof Error && error.message ? error.message : "network";
|
|
140
|
-
cause = error;
|
|
141
|
-
}
|
|
180
|
+
if (!abort.signal.aborted) failure = error instanceof Error && error.message ? error.message : "network";
|
|
142
181
|
}
|
|
143
182
|
abort.abort();
|
|
144
183
|
if (!alive()) return;
|
|
145
|
-
|
|
146
|
-
|
|
184
|
+
flushTapSync(() => {
|
|
185
|
+
releaseHeld(c);
|
|
186
|
+
c.ctx.onDetach();
|
|
187
|
+
});
|
|
188
|
+
if (!alive()) return;
|
|
189
|
+
if (outcome === "stop" || outcome === "idle") {
|
|
190
|
+
await waitGate(gate);
|
|
147
191
|
continue;
|
|
148
192
|
}
|
|
149
|
-
const immediate = gate
|
|
150
|
-
const delay = flushTapSync(() => scheduleRetry(c, failure, immediate
|
|
193
|
+
const immediate = takeGate(gate);
|
|
194
|
+
const delay = flushTapSync(() => scheduleRetry(c, failure, immediate));
|
|
151
195
|
if (delay === 0) continue;
|
|
152
|
-
await gate
|
|
196
|
+
await waitGate(gate, delay);
|
|
153
197
|
}
|
|
154
198
|
};
|
|
155
199
|
const useStatewireConn = (props) => {
|
|
@@ -162,10 +206,14 @@ const useStatewireConn = (props) => {
|
|
|
162
206
|
lease: null,
|
|
163
207
|
attempt: 0,
|
|
164
208
|
episode: null,
|
|
209
|
+
awaitingHello: true,
|
|
210
|
+
held: [],
|
|
165
211
|
abort: null,
|
|
166
|
-
gate:
|
|
212
|
+
gate: newGate(),
|
|
167
213
|
lifetime: null,
|
|
168
|
-
ctx
|
|
214
|
+
get ctx() {
|
|
215
|
+
return latest.current.ctx;
|
|
216
|
+
},
|
|
169
217
|
onChange: (connection) => latest.current.onChange?.(connection),
|
|
170
218
|
attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),
|
|
171
219
|
notify: setConnection,
|
|
@@ -185,27 +233,19 @@ const useStatewireConn = (props) => {
|
|
|
185
233
|
if (c.connection.status !== "connecting") setStatus(c, { status: "connecting" });
|
|
186
234
|
const lifetime = new AbortController();
|
|
187
235
|
c.lifetime = lifetime;
|
|
188
|
-
c.gate =
|
|
236
|
+
c.gate = newGate();
|
|
189
237
|
run(c, lifetime, c.gate);
|
|
190
238
|
return () => {
|
|
191
239
|
c.abort?.abort();
|
|
192
240
|
lifetime.abort();
|
|
193
241
|
if (c.lifetime === lifetime) c.lifetime = null;
|
|
194
|
-
c.gate
|
|
242
|
+
openGate(c.gate);
|
|
195
243
|
};
|
|
196
244
|
}, [c, props.url]);
|
|
197
245
|
return c;
|
|
198
246
|
};
|
|
199
247
|
const classify = (timeout) => /* @__PURE__ */ new Error(timeout.aborted ? "timeout" : "network");
|
|
200
|
-
const parseRetryAfter = (header) => {
|
|
201
|
-
if (!header) return void 0;
|
|
202
|
-
const value = header.trim();
|
|
203
|
-
if (/^\d+$/.test(value)) return Number(value) * 1e3;
|
|
204
|
-
const date = Date.parse(value);
|
|
205
|
-
return Number.isNaN(date) ? void 0 : Math.max(0, date - Date.now());
|
|
206
|
-
};
|
|
207
|
-
const overloadedError = (retryAfterMs) => Object.assign(/* @__PURE__ */ new Error("overloaded"), { retryAfterMs });
|
|
208
248
|
//#endregion
|
|
209
|
-
export { classify, handleEnvelope,
|
|
249
|
+
export { classify, handleEnvelope, useStatewireConn };
|
|
210
250
|
|
|
211
251
|
//# sourceMappingURL=connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveResponse","reviveValue","retryDelay","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","StatewireResponse","seq","type","code","payload","meta","StatewireSyn","lastSeq","lease","StatewireEnvelope","ops","res","ack","syn","fin","Fin","createGate","latched","wake","take","was","wait","timeoutMs","Promise","resolve","timer","undefined","setTimeout","clearTimeout","open","Gate","ReturnType","Conn","connection","Connection","hasSnapshot","attempt","episode","gate","abort","AbortController","lifetime","ctx","onChange","attemptOnce","notify","reconnect","setStatus","c","next","changed","status","deliverEnvelope","env","Pick","map","onSnapshot","onOps","isTransientFinish","reason","settleEnvelope","finish","rest","onFinish","handleEnvelope","scheduleRetry","failure","immediate","delay","nextRetryAt","Date","now","lastError","reconnectConn","run","alive","gone","error","signal","aborted","Error","useStatewireConn","props","url","latest","current","setConnection","conn","classify","timeout","AbortSignal","parseRetryAfter","header","value","trim","test","Number","date","parse","isNaN","Math","max","overloadedError","retryAfterMs","Object","assign"],"sources":["../../src/client/connection.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { reviveOps, reviveResponse, reviveValue } from \"../wire/meta\";\nimport { retryDelay } from \"./timing\";\nimport type { ConnectionState, StatewireRemoteProps } from \"../transports/sse\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\n\nexport type Episode = {\n cause: \"dropped\" | \"evicted\" | \"error\" | \"unauthorized\";\n message?: string;\n};\n\nexport type StatewireResponse = {\n seq: number;\n type: \"accepted\" | \"rejected\" | \"pending\" | \"crashed\" | \"result-unavailable\";\n code?: string;\n message?: string;\n payload?: unknown;\n meta?: unknown;\n};\n\nexport type StatewireSyn = {\n lastSeq: number;\n lease?: string;\n};\n\nexport type StatewireEnvelope = {\n ops?: unknown[];\n res?: StatewireResponse[];\n ack?: number;\n syn?: StatewireSyn;\n fin?: StatewireHost.Fin;\n};\n\nconst createGate = () => {\n let latched = false;\n let wake: (() => void) | undefined;\n return {\n take: () => {\n const was = latched;\n latched = false;\n return was;\n },\n wait: (timeoutMs?: number) => {\n if (latched) {\n latched = false;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n wake = undefined;\n resolve();\n }, timeoutMs);\n wake = () => {\n clearTimeout(timer);\n wake = undefined;\n resolve();\n };\n });\n },\n open: () => {\n if (wake) wake();\n else latched = true;\n },\n };\n};\n\nexport type Gate = ReturnType<typeof createGate>;\n\nexport type Conn = {\n connection: StatewireClient.Connection;\n hasSnapshot: boolean;\n lease: string | null;\n attempt: number;\n episode: Episode | null;\n gate: Gate;\n abort: AbortController | null;\n lifetime: AbortController | null;\n ctx: StatewireRemoteProps;\n onChange: (connection: StatewireClient.Connection) => void;\n attemptOnce: (\n abort: AbortController,\n ) => Promise<\"stop\" | \"reattach\" | \"ended\">;\n notify: (connection: StatewireClient.Connection) => void;\n reconnect: () => void;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n const changed = next.status !== c.connection.status;\n c.connection = { ...next, reconnect: c.reconnect };\n c.notify(c.connection);\n if (changed) c.onChange(c.connection);\n};\n\nconst deliverEnvelope = (\n c: Conn,\n env: Pick<StatewireEnvelope, \"ops\" | \"res\" | \"ack\" | \"syn\">,\n) => {\n if (env.syn?.lease !== undefined) c.lease = env.syn.lease;\n const ctx = c.ctx;\n const ops = env.ops === undefined ? [] : reviveOps(env.ops);\n const res = env.res?.map(reviveResponse);\n if (env.syn !== undefined) {\n ctx.onSnapshot({ ops, lastSeq: env.syn.lastSeq });\n c.hasSnapshot = true;\n } else if (\n env.ops !== undefined ||\n env.res !== undefined ||\n env.ack !== undefined\n ) {\n ctx.onOps({\n ops,\n ...(res !== undefined && { res }),\n ...(env.ack !== undefined && { ack: env.ack }),\n });\n }\n};\n\nconst isTransientFinish = (\n reason: StatewireHost.Fin[\"reason\"],\n): reason is \"evicted\" | \"error\" => reason === \"evicted\" || reason === \"error\";\n\nconst settleEnvelope = (\n c: Conn,\n env: StatewireEnvelope,\n): \"stop\" | \"reattach\" | null => {\n let finish = env.fin;\n if (finish) {\n if (finish.meta !== undefined) {\n const { meta, ...rest } = finish;\n finish = { ...rest, payload: reviveValue(rest.payload, meta) };\n }\n c.ctx.onFinish(finish);\n if (isTransientFinish(finish.reason)) {\n c.episode = {\n cause: finish.reason,\n ...(finish.message !== undefined && { message: finish.message }),\n };\n return \"reattach\";\n }\n setStatus(c, {\n status: \"gone\",\n ...(finish.message !== undefined && { message: finish.message }),\n ...(finish.payload !== undefined && { payload: finish.payload }),\n });\n return \"stop\";\n }\n if (c.connection.status !== \"live\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n }\n return null;\n};\n\nexport const handleEnvelope = (\n c: Conn,\n env: StatewireEnvelope,\n): \"stop\" | \"reattach\" | null => {\n deliverEnvelope(c, env);\n return settleEnvelope(c, env);\n};\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n cause?: unknown,\n) => {\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n const delay = immediate ? 0 : retryDelay(cause, c.attempt);\n if (c.hasSnapshot) {\n setStatus(c, {\n status: \"reconnecting\",\n ...c.episode,\n attempt: c.attempt,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n }\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"gone\") {\n c.attempt = 0;\n c.episode = { cause: \"dropped\" };\n flushTapSync(() => {\n if (c.hasSnapshot) scheduleRetry(c, undefined, true);\n else setStatus(c, { status: \"connecting\" });\n });\n }\n c.gate.open();\n c.abort?.abort();\n};\n\nconst run = async (c: Conn, lifetime: AbortController, gate: Gate) => {\n const alive = () => c.lifetime === lifetime;\n while (alive()) {\n const abort = new AbortController();\n c.abort = abort;\n let failure: string | undefined;\n let cause: unknown;\n let gone = false;\n try {\n gone = (await c.attemptOnce(abort)) === \"stop\";\n } catch (error) {\n if (!abort.signal.aborted) {\n failure =\n error instanceof Error && error.message ? error.message : \"network\";\n cause = error;\n }\n }\n abort.abort();\n if (!alive()) return;\n if (gone) {\n await gate.wait();\n continue;\n }\n const immediate = gate.take();\n const delay = flushTapSync(() =>\n scheduleRetry(c, failure, immediate, cause),\n );\n if (delay === 0) continue;\n await gate.wait(delay);\n }\n};\n\nexport const useStatewireConn = (props: {\n url: string;\n onChange?: ((connection: StatewireClient.Connection) => void) | undefined;\n ctx: StatewireRemoteProps;\n attemptOnce: (\n c: Conn,\n abort: AbortController,\n ) => Promise<\"stop\" | \"reattach\" | \"ended\">;\n}): Conn => {\n const latest = useRef(props);\n latest.current = props;\n const [, setConnection] = useState<StatewireClient.Connection | null>(null);\n const [c] = useState((): Conn => {\n const conn: Conn = {\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n episode: null,\n abort: null,\n gate: createGate(),\n lifetime: null,\n ctx: props.ctx,\n onChange: (connection) => latest.current.onChange?.(connection),\n attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),\n notify: setConnection,\n reconnect: () => reconnectConn(conn),\n connection: { status: \"connecting\", reconnect: () => conn.reconnect() },\n };\n return conn;\n });\n\n useEffect(() => {\n c.hasSnapshot = false;\n c.lease = null;\n c.attempt = 0;\n c.episode = null;\n if (c.connection.status !== \"connecting\") {\n setStatus(c, { status: \"connecting\" });\n }\n const lifetime = new AbortController();\n c.lifetime = lifetime;\n c.gate = createGate();\n void run(c, lifetime, c.gate);\n return () => {\n c.abort?.abort();\n lifetime.abort();\n if (c.lifetime === lifetime) c.lifetime = null;\n c.gate.open();\n };\n }, [c, props.url]);\n\n return c;\n};\n\nexport const classify = (timeout: AbortSignal) =>\n new Error(timeout.aborted ? \"timeout\" : \"network\");\n\nexport const parseRetryAfter = (header: string | null): number | undefined => {\n if (!header) return undefined;\n const value = header.trim();\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n};\n\nexport const overloadedError = (retryAfterMs?: number) =>\n Object.assign(new Error(\"overloaded\"), { retryAfterMs });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","StatewireCmd","seq","type","code","payload","meta","dur","StatewireSyn","ctxSeq","record","id","idle","retry","StatewireEnvelope","ops","cmd","next","syn","fin","Fin","Gate","latched","wake","newGate","undefined","takeGate","g","was","waitGate","timeoutMs","Promise","resolve","timer","setTimeout","clearTimeout","openGate","AttemptOutcome","Conn","connection","Connection","hasSnapshot","lease","attempt","episode","awaitingHello","held","gate","abort","AbortController","lifetime","ctx","onChange","attemptOnce","notify","reconnect","setStatus","c","changed","status","protocolError","Error","releaseHeld","length","onCmds","cmdsOf","env","Array","isArray","handleHello","onHello","onIdle","deliverEnvelope","onRefusal","onOps","cmds","push","isTransientFinish","reason","settleFin","finish","rest","onFinish","handleEnvelope","scheduleRetry","failure","immediate","delay","nextRetryAt","Date","now","lastError","reconnectConn","run","alive","outcome","error","signal","aborted","onDetach","useStatewireConn","props","url","latest","current","setConnection","conn","classify","timeout","AbortSignal"],"sources":["../../src/client/connection.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { reviveOps, reviveValue } from \"../wire/meta\";\nimport { backoff } from \"./timing\";\nimport type { ConnectionState, StatewireRemoteProps } from \"../transports/sse\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\n\nexport type Episode = {\n cause: \"dropped\" | \"evicted\" | \"error\" | \"unauthorized\";\n message?: string;\n};\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | \"result\" | \"rejected\" | \"failed\" | \"crashed\" | \"expired\";\n code?: string;\n message?: string;\n payload?: unknown;\n meta?: unknown;\n /** Cumulative: resending through this seq will never be needed. */\n dur?: boolean;\n};\n\nexport type StatewireSyn = {\n seq?: number;\n ctxSeq?: number;\n record?: \"fresh\" | \"resumed\" | \"ephemeral\";\n id?: string;\n idle?: boolean;\n retry?: number;\n};\n\nexport type StatewireEnvelope = {\n ops?: unknown[];\n cmd?: StatewireCmd[];\n next?: true;\n idle?: boolean;\n syn?: StatewireSyn;\n fin?: StatewireHost.Fin;\n};\n\nexport type Gate = { latched: boolean; wake: (() => void) | undefined };\n\nconst newGate = (): Gate => ({ latched: false, wake: undefined });\n\nconst takeGate = (g: Gate) => {\n const was = g.latched;\n g.latched = false;\n return was;\n};\n\nconst waitGate = (g: Gate, timeoutMs?: number) => {\n if (g.latched) {\n g.latched = false;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n g.wake = undefined;\n resolve();\n }, timeoutMs);\n g.wake = () => {\n clearTimeout(timer);\n g.wake = undefined;\n resolve();\n };\n });\n};\n\nconst openGate = (g: Gate) => {\n if (g.wake) g.wake();\n else g.latched = true;\n};\n\nexport type AttemptOutcome = \"stop\" | \"reattach\" | \"ended\" | \"idle\";\n\nexport type Conn = {\n connection: StatewireClient.Connection;\n hasSnapshot: boolean;\n lease: string | null;\n attempt: number;\n episode: Episode | null;\n awaitingHello: boolean;\n held: StatewireCmd[];\n gate: Gate;\n abort: AbortController | null;\n lifetime: AbortController | null;\n ctx: StatewireRemoteProps;\n onChange: (connection: StatewireClient.Connection) => void;\n attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;\n notify: (connection: StatewireClient.Connection) => void;\n reconnect: () => void;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n const changed = next.status !== c.connection.status;\n c.connection = { ...next, reconnect: c.reconnect };\n c.notify(c.connection);\n if (changed) c.onChange(c.connection);\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\n// A hold's release delivers, never settles: fin and bare close both drain it.\nconst releaseHeld = (c: Conn) => {\n if (c.held.length === 0) return;\n const held = c.held;\n c.held = [];\n c.ctx.onCmds(held);\n};\n\nconst cmdsOf = (env: StatewireEnvelope): StatewireCmd[] => {\n if (env.cmd === undefined) return [];\n if (!Array.isArray(env.cmd)) return protocolError(\"cmd must be an array\");\n return env.cmd;\n};\n\nconst handleHello = (c: Conn, env: StatewireEnvelope) => {\n const syn = env.syn;\n if (syn === undefined) return protocolError(\"expected hello\");\n c.awaitingHello = false;\n c.hasSnapshot = true;\n c.ctx.onHello({\n ops: env.ops === undefined ? [] : reviveOps(env.ops),\n syn,\n });\n c.ctx.onIdle(syn.idle ?? false);\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n};\n\nconst deliverEnvelope = (c: Conn, env: StatewireEnvelope) => {\n if (env.syn !== undefined) {\n const syn = env.syn;\n if (\n syn.seq !== undefined ||\n syn.ctxSeq !== undefined ||\n syn.retry !== undefined\n ) {\n c.ctx.onRefusal(syn);\n } else if (syn.id === undefined) {\n protocolError(\"invalid mid-stream syn\");\n }\n }\n if (env.ops !== undefined) {\n const held = c.held;\n c.held = [];\n c.ctx.onOps({\n ops: reviveOps(env.ops),\n cmds: [...held, ...cmdsOf(env)],\n });\n } else if (env.cmd !== undefined) {\n const cmds = cmdsOf(env);\n if (env.next === true) c.held.push(...cmds);\n else c.ctx.onCmds(cmds);\n }\n if (typeof env.idle === \"boolean\") c.ctx.onIdle(env.idle);\n};\n\nconst isTransientFinish = (\n reason: StatewireHost.Fin[\"reason\"],\n): reason is \"evicted\" | \"error\" => reason === \"evicted\" || reason === \"error\";\n\nconst settleFin = (c: Conn, fin: StatewireHost.Fin): AttemptOutcome => {\n let finish = fin;\n if (finish.meta !== undefined) {\n const { meta, ...rest } = finish;\n finish = { ...rest, payload: reviveValue(rest.payload, meta) };\n }\n releaseHeld(c);\n if (finish.reason === \"idle\") {\n c.ctx.onIdle(true);\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"idle\" });\n return \"idle\";\n }\n c.ctx.onFinish(finish);\n if (isTransientFinish(finish.reason)) {\n c.episode = {\n cause: finish.reason,\n ...(finish.message !== undefined && { message: finish.message }),\n };\n return \"reattach\";\n }\n setStatus(c, {\n status: \"gone\",\n ...(finish.message !== undefined && { message: finish.message }),\n ...(finish.payload !== undefined && { payload: finish.payload }),\n });\n return \"stop\";\n};\n\nexport const handleEnvelope = (\n c: Conn,\n env: StatewireEnvelope,\n): AttemptOutcome | null => {\n if (typeof env !== \"object\" || env === null) {\n return protocolError(\"packet must be an object\");\n }\n if (c.awaitingHello) {\n if (env.fin !== undefined) return settleFin(c, env.fin);\n handleHello(c, env);\n return null;\n }\n deliverEnvelope(c, env);\n if (env.fin !== undefined) return settleFin(c, env.fin);\n if (c.connection.status !== \"live\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n }\n return null;\n};\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n) => {\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n const delay = immediate ? 0 : backoff(c.attempt);\n if (c.hasSnapshot) {\n setStatus(c, {\n status: \"reconnecting\",\n ...c.episode,\n attempt: c.attempt,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n }\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"gone\" || c.connection.status === \"idle\") {\n c.attempt = 0;\n c.episode = { cause: \"dropped\" };\n flushTapSync(() => {\n if (c.hasSnapshot) scheduleRetry(c, undefined, true);\n else setStatus(c, { status: \"connecting\" });\n });\n }\n openGate(c.gate);\n c.abort?.abort();\n};\n\nconst run = async (c: Conn, lifetime: AbortController, gate: Gate) => {\n const alive = () => c.lifetime === lifetime;\n while (alive()) {\n const abort = new AbortController();\n c.abort = abort;\n c.awaitingHello = true;\n c.held = [];\n let outcome: AttemptOutcome | undefined;\n let failure: string | undefined;\n try {\n outcome = await c.attemptOnce(abort);\n } catch (error) {\n if (!abort.signal.aborted) {\n failure =\n error instanceof Error && error.message ? error.message : \"network\";\n }\n }\n abort.abort();\n if (!alive()) return;\n flushTapSync(() => {\n releaseHeld(c);\n c.ctx.onDetach();\n });\n if (!alive()) return;\n if (outcome === \"stop\" || outcome === \"idle\") {\n await waitGate(gate);\n continue;\n }\n const immediate = takeGate(gate);\n const delay = flushTapSync(() => scheduleRetry(c, failure, immediate));\n if (delay === 0) continue;\n await waitGate(gate, delay);\n }\n};\n\nexport const useStatewireConn = (props: {\n url: string;\n onChange?: ((connection: StatewireClient.Connection) => void) | undefined;\n ctx: StatewireRemoteProps;\n attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;\n}): Conn => {\n const latest = useRef(props);\n latest.current = props;\n const [, setConnection] = useState<StatewireClient.Connection | null>(null);\n const [c] = useState((): Conn => {\n const conn: Conn = {\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n episode: null,\n awaitingHello: true,\n held: [],\n abort: null,\n gate: newGate(),\n lifetime: null,\n get ctx() {\n return latest.current.ctx;\n },\n onChange: (connection) => latest.current.onChange?.(connection),\n attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),\n notify: setConnection,\n reconnect: () => reconnectConn(conn),\n connection: { status: \"connecting\", reconnect: () => conn.reconnect() },\n };\n return conn;\n });\n\n useEffect(() => {\n c.hasSnapshot = false;\n c.lease = null;\n c.attempt = 0;\n c.episode = null;\n if (c.connection.status !== \"connecting\") {\n setStatus(c, { status: \"connecting\" });\n }\n const lifetime = new AbortController();\n c.lifetime = lifetime;\n c.gate = newGate();\n void run(c, lifetime, c.gate);\n return () => {\n c.abort?.abort();\n lifetime.abort();\n if (c.lifetime === lifetime) c.lifetime = null;\n openGate(c.gate);\n };\n }, [c, props.url]);\n\n return c;\n};\n\nexport const classify = (timeout: AbortSignal) =>\n new Error(timeout.aborted ? \"timeout\" : \"network\");\n"],"mappings":"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"}
|
package/dist/client/core.d.ts
CHANGED
|
@@ -1,14 +1,41 @@
|
|
|
1
1
|
import { StatewireClient } from "../statewire.js";
|
|
2
2
|
import { Statewire } from "../types.js";
|
|
3
|
+
import { StatewirePath } from "../wire/ops.js";
|
|
4
|
+
import { CommandsInvocation, CommandsListener } from "../host/host.js";
|
|
3
5
|
//#region src/client/core.d.ts
|
|
4
6
|
declare const LIVE_CONNECTION: StatewireClient.Connection;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
type Overlay = {
|
|
8
|
+
inv: CommandsInvocation;
|
|
9
|
+
dropped: boolean;
|
|
10
|
+
};
|
|
11
|
+
type Core = {
|
|
12
|
+
getOptions: () => StatewireClient.Options<any, any>;
|
|
13
|
+
value: Statewire<unknown> | null;
|
|
14
|
+
source: Statewire.StateSource | null;
|
|
15
|
+
overlays: Overlay[];
|
|
16
|
+
updates: Statewire.CommandUpdate[];
|
|
17
|
+
pendingChanged: StatewirePath[];
|
|
18
|
+
canonical: unknown;
|
|
19
|
+
published: unknown;
|
|
20
|
+
overlayPaths: readonly StatewirePath[];
|
|
21
|
+
notifiedPublished: unknown;
|
|
22
|
+
notifiedOverlayPaths: readonly StatewirePath[];
|
|
23
|
+
notifiedConnection: StatewireClient.Connection | null;
|
|
24
|
+
notifiedBusy: boolean;
|
|
25
|
+
stale: boolean;
|
|
26
|
+
everAttached: boolean;
|
|
27
|
+
overlaysDirty: boolean;
|
|
28
|
+
flushing: boolean;
|
|
29
|
+
scheduled: boolean;
|
|
30
|
+
listeners: Set<() => void>;
|
|
31
|
+
post: () => void;
|
|
32
|
+
commandsListener: CommandsListener;
|
|
8
33
|
getState: () => unknown;
|
|
34
|
+
getBusy: () => boolean;
|
|
9
35
|
commands: Statewire.CommandsProxy<any>;
|
|
10
36
|
subscribe: (listener: () => void) => () => void;
|
|
11
37
|
};
|
|
38
|
+
declare const useStatewireCore: (getOptions: () => StatewireClient.Options<any, any>, value: Statewire<unknown>) => Core;
|
|
12
39
|
//#endregion
|
|
13
40
|
export { LIVE_CONNECTION, useStatewireCore };
|
|
14
41
|
//# sourceMappingURL=core.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":";;;;;cAYa,iBAAiB,gBAAgB;KAczC;EAAY,KAAK;EAAoB;;KAErC;EACH,kBAAkB,gBAAgB;EAClC,OAAO;EACP,QAAQ,UAAU;EAClB,UAAU;EACV,SAAS,UAAU;EACnB,gBAAgB;EAChB;EACA;EACA,uBAAuB;EACvB;EACA,+BAA+B;EAC/B,oBAAoB,gBAAgB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,kBAAkB;EAClB;EACA;EACA,UAAU,UAAU;EACpB,YAAY;;cAyMD,mBACX,kBAAkB,gBAAgB,mBAClC,OAAO,uBAAkB"}
|