statewire 0.14.0 → 0.16.0
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 +29 -80
- package/dist/client/connection.d.ts +19 -6
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +79 -40
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +1 -0
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +5 -0
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +2 -0
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +98 -19
- package/dist/client/session.js.map +1 -1
- package/dist/host/adapter.d.ts +1 -1
- package/dist/host/adapter.d.ts.map +1 -1
- package/dist/host/adapter.js +7 -1
- package/dist/host/adapter.js.map +1 -1
- package/dist/host/host.d.ts +1 -0
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +45 -5
- package/dist/host/host.js.map +1 -1
- package/dist/host/http.d.ts +1 -1
- package/dist/host/http.d.ts.map +1 -1
- package/dist/host/http.js +75 -47
- package/dist/host/http.js.map +1 -1
- package/dist/host/socket.d.ts +23 -7
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +275 -74
- package/dist/host/socket.js.map +1 -1
- package/dist/host/work.d.ts +7 -0
- package/dist/host/work.d.ts.map +1 -0
- package/dist/host/work.js +53 -0
- package/dist/host/work.js.map +1 -0
- package/dist/host-internal.d.ts +35 -5
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host-internal.js +2 -1
- package/dist/host.d.ts +16 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/statewire.d.ts +13 -4
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js.map +1 -1
- package/dist/transports/sse.d.ts +2 -5
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +50 -59
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.js +4 -4
- package/dist/transports/testing.js.map +1 -1
- package/dist/transports/ws.d.ts +3 -3
- package/dist/transports/ws.d.ts.map +1 -1
- package/dist/transports/ws.js +57 -44
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util/randomId.js +1 -1
- package/dist/util/randomId.js.map +1 -1
- package/dist/wire/documents.d.ts +20 -0
- package/dist/wire/documents.d.ts.map +1 -0
- package/dist/wire/documents.js +74 -0
- package/dist/wire/documents.js.map +1 -0
- package/dist/wire/limits.d.ts +10 -0
- package/dist/wire/limits.d.ts.map +1 -0
- package/dist/wire/limits.js +11 -0
- package/dist/wire/limits.js.map +1 -0
- package/dist/wire/meta.d.ts.map +1 -1
- package/dist/wire/meta.js +1 -1
- package/dist/wire/meta.js.map +1 -1
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +1 -2
- package/dist/wire/ops.js.map +1 -1
- package/dist/wire/version.d.ts +7 -0
- package/dist/wire/version.d.ts.map +1 -0
- package/dist/wire/version.js +8 -0
- package/dist/wire/version.js.map +1 -0
- package/package.json +1 -1
- package/src/client/connection.ts +159 -52
- package/src/client/core.ts +6 -0
- package/src/client/session.ts +105 -32
- package/src/host/adapter.ts +10 -2
- package/src/host/host.ts +54 -4
- package/src/host/http.ts +132 -66
- package/src/host/socket.ts +410 -81
- package/src/host/work.ts +50 -0
- package/src/host-internal.ts +32 -4
- package/src/host.ts +18 -2
- package/src/index.ts +2 -2
- package/src/statewire.ts +18 -4
- package/src/transports/sse.ts +70 -78
- package/src/transports/testing.ts +4 -4
- package/src/transports/ws.ts +36 -55
- package/src/types.ts +1 -0
- package/src/util/randomId.ts +3 -3
- package/src/wire/documents.ts +109 -0
- package/src/wire/limits.ts +6 -0
- package/src/wire/meta.ts +6 -1
- package/src/wire/ops.ts +1 -2
- package/src/wire/version.ts +3 -0
package/README.md
CHANGED
|
@@ -1,90 +1,39 @@
|
|
|
1
1
|
# statewire
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
One JSON object replicated from a server to any number of clients, with typed commands back: the server streams snapshot-first ops, the client keeps optimistic updates rebased on top and settles every command it sent. Part of [harness-sdk](https://github.com/assistant-ui/harness-sdk), with `harness-sdk` and `pinned`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import { useStatewire, StatewireSSE } from "statewire";
|
|
7
|
-
|
|
8
|
-
type Commands = {
|
|
9
|
-
increment(): void;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const { state, connection, commands } = useStatewire<State, Commands>({
|
|
13
|
-
transport: StatewireSSE({ url: "/api/threads/demo" }),
|
|
14
|
-
optimistic: (draft, command) => {
|
|
15
|
-
if (command.method === "increment") draft.count += 1;
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const onClick = async () => {
|
|
20
|
-
await commands.increment(); // resolves when the server has provably applied it
|
|
21
|
-
};
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## What you get
|
|
25
|
-
|
|
26
|
-
- **`state`** — `undefined` until the first snapshot, then canonical server state with your pending commands' optimistic effects rebased on top. When the server's ack arrives, the optimistic overlay drops in the same tick: no flicker.
|
|
27
|
-
- **`connection`** — connection health as a tagged union: `connection.status` is `"connecting" | "live" | "reconnecting" | "gone"`, and each variant carries exactly the data that exists in that situation: `reconnecting` has `cause` (`"dropped"`, `"evicted"`, or `"error"`), `attempt`, `nextRetryAt`, `lastError?`, `message?`; `gone` carries the server's `message?` and `payload?`. `connection.reconnect()` re-attaches manually from `gone`, or "retry now" during backoff.
|
|
28
|
-
- **`commands`** — a typed proxy over the `Commands` method map: `commands.increment()` submits `{ method: "increment", params: [] }` and resolves with the handler's result only once the stream ack proves the effect is in canonical state. Rejects with `StatewireSendError`, whose `failure.fate` carries the verdict; the boundary is definitiveness:
|
|
29
|
-
- `"aborted"` — definitely not applied (never POSTed, or every attempt accounted for); safe to resend
|
|
30
|
-
- `"lost"` — a POST left the machine and never got a definitive answer; whether the server applied it is unknown — check the snapshot for your payload ids
|
|
31
|
-
- `"rejected"` — the server said no; not applied (`message`, optional `payload` from the backend's `StatewireReject`)
|
|
32
|
-
- `"unknown-command"` / `"invalid-params"` — the server does not know the method, or the args failed its schema
|
|
33
|
-
- `"crashed"` — acked, but the processing died before producing a result
|
|
34
|
-
- `"result-unavailable"` — acked, but the result can no longer be delivered
|
|
35
|
-
|
|
36
|
-
Every failure also carries `cause` — which channel settled it: `"server"` = the server's answer, `"stream-gone"` = terminal `finish(gone)`, `"session-lost"` = the server forgot the client id (eviction), `"unmount"` is client-side. An `"aborted"` failure carries `predecessorLost: true` when an earlier in-flight command of the same batch has uncertain fate. The taxonomy's single exported type is `StatewireClient.SendFailure`; the `fate` and `cause` unions are reachable as `SendFailure["fate"]` / `SendFailure["cause"]`.
|
|
37
|
-
|
|
38
|
-
An eviction replays every unsettled command under the rotated client id, in seq order — fresh seqs, relative order preserved, the original promise settles with the replay's outcome. Context submissions settle as session-lost failures and the stored context re-sends whole; replay may re-run a command the lost session already applied.
|
|
39
|
-
|
|
40
|
-
Reconnects are not errors: transport drops and server evictions self-heal with exponential backoff while `state` stays renderable. Commands carry a monotonic seq, so retries are deduplicated server-side and a reconnect splices already-applied commands instead of re-running them.
|
|
41
|
-
|
|
42
|
-
A client id may hold any number of concurrent attaches: each gets its own hello and lease, and cmd answers fan out to every live attach of the id. A lease lives and dies with its attach — a `423` means that attach is gone: re-attach and resend.
|
|
5
|
+
## Install
|
|
43
6
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Three ways to mount
|
|
47
|
-
|
|
48
|
-
One implementation, three entry points:
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
useStatewire(options) // React hook
|
|
52
|
-
StatewireResource(options) // @assistant-ui/tap resource, for composition
|
|
53
|
-
new StatewireClient(options) // framework-free: .state/.connection/.commands,
|
|
54
|
-
// .subscribe(cb), .dispose()
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add statewire react
|
|
55
9
|
```
|
|
56
10
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Every public type merges onto the class as a namespace — `StatewireClient.Connection`, `StatewireClient.ConnectionStatus`, `StatewireClient.Options`, `StatewireClient.SendFailure`, … — so one import covers the whole surface.
|
|
11
|
+
## Use
|
|
60
12
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
13
|
+
```tsx
|
|
14
|
+
import { StatewireHttp, useStatewire } from "statewire";
|
|
15
|
+
|
|
16
|
+
type CounterState = { count: number };
|
|
17
|
+
type CounterCommands = { increment(): void; reset(): void };
|
|
18
|
+
|
|
19
|
+
export const App = () => {
|
|
20
|
+
const { state, connection, commands } = useStatewire<
|
|
21
|
+
CounterState | undefined,
|
|
22
|
+
CounterCommands
|
|
23
|
+
>({ transport: StatewireHttp({ url: "/threads/main" }) });
|
|
24
|
+
return (
|
|
25
|
+
<main>
|
|
26
|
+
<p>{connection.status}</p>
|
|
27
|
+
<h1>{state?.count ?? 0}</h1>
|
|
28
|
+
<button onClick={() => void commands.increment()}>+1</button>
|
|
29
|
+
</main>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
71
32
|
```
|
|
72
33
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## Backend
|
|
76
|
-
|
|
77
|
-
Any server speaking the Statewire convention works. The Python implementation lives in this repo (`python/statewire/statewire`):
|
|
78
|
-
|
|
79
|
-
```python
|
|
80
|
-
from statewire import Statewire, StatewireReject, command
|
|
81
|
-
|
|
82
|
-
class Counter(Statewire):
|
|
83
|
-
@command
|
|
84
|
-
async def increment(self):
|
|
85
|
-
if self.state["count"] >= 10:
|
|
86
|
-
raise StatewireReject("counter is full", payload={"max": 10})
|
|
87
|
-
self.state["count"] += 1
|
|
88
|
-
```
|
|
34
|
+
## Docs
|
|
89
35
|
|
|
90
|
-
|
|
36
|
+
- Get started: https://github.com/assistant-ui/harness-sdk/blob/main/apps/docs/app/docs/get-started/statewire/page.mdx
|
|
37
|
+
- Guide: https://github.com/assistant-ui/harness-sdk/blob/main/apps/docs/app/docs/guides/statewire/consume/page.mdx
|
|
38
|
+
- Reference: https://github.com/assistant-ui/harness-sdk/blob/main/apps/docs/app/docs/reference/statewire/page.mdx
|
|
39
|
+
- Spec: https://github.com/assistant-ui/harness-sdk/blob/main/apps/docs/app/docs/spec/statewire-protocol/page.mdx
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { StatewireHost } from "../host.js";
|
|
2
|
-
import { ConnectionState, StatewireRemoteProps } from "../transports/sse.js";
|
|
2
|
+
import { ConnectionState, RemoteLink, StatewireFrame, StatewireRemoteProps, StatewireSubmitResult } from "../transports/sse.js";
|
|
3
3
|
import { StatewireClient } from "../statewire.js";
|
|
4
4
|
//#region src/client/connection.d.ts
|
|
5
5
|
type Episode = {
|
|
6
|
-
cause: "dropped" | "
|
|
6
|
+
cause: "dropped" | "reconnect" | "error" | "unauthorized";
|
|
7
7
|
message?: string;
|
|
8
|
+
code?: string;
|
|
8
9
|
};
|
|
9
10
|
/** The terminal cmd verdict types; "applied" is the sole non-terminal type. */
|
|
10
11
|
declare const TERMINAL: readonly ["result", "rejected", "failed", "crashed", "expired"];
|
|
@@ -42,11 +43,13 @@ type AttemptOutcome = "stop" | "reattach" | "ended" | "idle";
|
|
|
42
43
|
type Conn = {
|
|
43
44
|
connection: StatewireClient.Connection;
|
|
44
45
|
parked: boolean;
|
|
45
|
-
hasSnapshot: boolean;
|
|
46
46
|
lease: string | null;
|
|
47
47
|
attempt: number;
|
|
48
48
|
authDenials: number;
|
|
49
|
+
authStale: boolean;
|
|
49
50
|
liveAt: number | null;
|
|
51
|
+
retryAt: number;
|
|
52
|
+
error: StatewireClient.ConnectionError | undefined;
|
|
50
53
|
episode: Episode | null;
|
|
51
54
|
awaitingHello: boolean;
|
|
52
55
|
held: StatewireCmd[];
|
|
@@ -54,6 +57,7 @@ type Conn = {
|
|
|
54
57
|
abort: AbortController | null;
|
|
55
58
|
lifetime: AbortController | null;
|
|
56
59
|
ctx: StatewireRemoteProps;
|
|
60
|
+
onError: (error: StatewireClient.ConnectionError) => void;
|
|
57
61
|
onChange: (connection: StatewireClient.Connection) => void;
|
|
58
62
|
attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;
|
|
59
63
|
notify: (connection: StatewireClient.Connection) => void;
|
|
@@ -61,15 +65,24 @@ type Conn = {
|
|
|
61
65
|
};
|
|
62
66
|
declare const degradedOf: (state: ConnectionState) => boolean;
|
|
63
67
|
declare const validateRefusalSyn: (value: unknown) => StatewireSyn;
|
|
68
|
+
declare const settleFin: (c: Conn, fin: StatewireHost.Fin) => AttemptOutcome;
|
|
64
69
|
declare const handleEnvelope: (c: Conn, value: unknown) => AttemptOutcome | null;
|
|
65
|
-
declare const
|
|
70
|
+
declare const unsupportedVersion: (c: Conn, version: string) => AttemptOutcome;
|
|
71
|
+
declare const resolveHeaders: (headers: StatewireClient.HeadersOption | undefined, staleAuth: boolean) => Promise<Headers>;
|
|
72
|
+
declare const attachFrame: (c: Conn, headers: StatewireClient.HeadersOption | undefined) => Promise<{
|
|
73
|
+
headers: Headers;
|
|
74
|
+
body: string;
|
|
75
|
+
}>;
|
|
76
|
+
declare const useLink: (props: {
|
|
66
77
|
url: string;
|
|
67
78
|
isNew?: boolean | undefined;
|
|
68
79
|
onChange?: ((connection: StatewireClient.Connection) => void) | undefined;
|
|
80
|
+
onError?: ((error: StatewireClient.ConnectionError) => void) | undefined;
|
|
69
81
|
ctx: StatewireRemoteProps;
|
|
70
82
|
attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;
|
|
71
|
-
|
|
83
|
+
send: (c: Conn, frame: StatewireFrame) => Promise<StatewireSubmitResult>;
|
|
84
|
+
}) => RemoteLink;
|
|
72
85
|
declare const classify: (timeout: AbortSignal) => Error;
|
|
73
86
|
//#endregion
|
|
74
|
-
export { AttemptOutcome, Conn, Episode, Gate, StatewireCmd, StatewireEnvelope, StatewireSyn, TERMINAL, classify, degradedOf, handleEnvelope,
|
|
87
|
+
export { AttemptOutcome, Conn, Episode, Gate, StatewireCmd, StatewireEnvelope, StatewireSyn, TERMINAL, attachFrame, classify, degradedOf, handleEnvelope, resolveHeaders, settleFin, unsupportedVersion, useLink, validateRefusalSyn };
|
|
75
88
|
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAcY;EACV;EACA;EACA;;;cAIW;KAQD;EACV;EACA,2BAA2B;EAC3B;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;EACA;EACA;EACA;EACA,OAAO,gBAAgB;EACvB,SAAS;EACT;EACA,MAAM;EACN,MAAM;EACN,OAAO;EACP,UAAU;EACV,KAAK;EACL,UAAU,OAAO,gBAAgB;EACjC,WAAW,YAAY,gBAAgB;EACvC,cAAc,OAAO,oBAAoB,QAAQ;EACjD,SAAS,YAAY,gBAAgB;EACrC;;cAGW,aAAc,OAAO;cAuErB,qBAAsB,mBAAiB;cAyKvC,YAAa,GAAG,MAAM,KAAK,cAAc,QAAM;cA+D/C,iBACX,GAAG,MACH,mBACC;cAkIU,qBAAsB,GAAG,MAAM,oBAAkB;cAUjD,iBACX,SAAS,gBAAgB,2BACzB,uBACC,QAAQ;cAME,cACX,GAAG,MACH,SAAS,gBAAgB,8BAAyB;;;;cAcvC,UAAW;EACtB;EACA;EACA,aAAa,YAAY,gBAAgB;EACzC,YAAY,OAAO,gBAAgB;EACnC,KAAK;EACL,cAAc,GAAG,MAAM,OAAO,oBAAoB,QAAQ;EAC1D,OAAO,GAAG,MAAM,OAAO,mBAAmB,QAAQ;MAChD;cA+ES,WAAY,SAAS,gBAAW"}
|
|
@@ -41,17 +41,18 @@ const openGate = (g) => {
|
|
|
41
41
|
if (g.wake) g.wake();
|
|
42
42
|
else g.latched = true;
|
|
43
43
|
};
|
|
44
|
-
const degradedOf = (state) => state.status === "connecting" && state.attempt > 0 || state.status === "disconnected" && state.reason
|
|
44
|
+
const degradedOf = (state) => state.status === "connecting" && state.attempt > 0 || state.status === "disconnected" && state.reason !== "idle" && state.reason !== "deferred";
|
|
45
45
|
const sameState = (a, b) => {
|
|
46
46
|
if (a.status !== b.status) return false;
|
|
47
|
-
if (a.status === "connecting" && b.status === "connecting") return a.attempt === b.attempt && a.cause === b.cause && a.nextRetryAt === b.nextRetryAt && a.lastError === b.lastError;
|
|
47
|
+
if (a.status === "connecting" && b.status === "connecting") return a.attempt === b.attempt && a.cause === b.cause && a.code === b.code && a.nextRetryAt === b.nextRetryAt && a.lastError === b.lastError;
|
|
48
48
|
if (a.status === "disconnected" && b.status === "disconnected") return a.reason === b.reason;
|
|
49
49
|
return true;
|
|
50
50
|
};
|
|
51
51
|
const setStatus = (c, next) => {
|
|
52
|
-
if (sameState(next, c.connection)) return;
|
|
52
|
+
if (sameState(next, c.connection) && c.connection.error === c.error) return;
|
|
53
53
|
c.connection = {
|
|
54
54
|
...next,
|
|
55
|
+
...c.error === void 0 ? {} : { error: c.error },
|
|
55
56
|
reconnect: c.reconnect,
|
|
56
57
|
degraded: degradedOf(next)
|
|
57
58
|
};
|
|
@@ -95,6 +96,9 @@ const validateFin = (value) => {
|
|
|
95
96
|
if (!isObject(value)) return protocolError("fin must be an object");
|
|
96
97
|
if (typeof value["reason"] !== "string") protocolError("fin.reason must be a string");
|
|
97
98
|
validateOptionalString(value["message"], "fin.message");
|
|
99
|
+
validateOptionalString(value["code"], "fin.code");
|
|
100
|
+
if (value["status"] !== void 0 && !isNonnegativeSafeInteger(value["status"])) protocolError("fin.status must be a nonnegative safe integer");
|
|
101
|
+
if (value["retry"] !== void 0 && !isNonnegativeSafeInteger(value["retry"])) protocolError("fin.retry must be a nonnegative safe integer");
|
|
98
102
|
return true;
|
|
99
103
|
};
|
|
100
104
|
const validateEnvelope = (value) => {
|
|
@@ -126,9 +130,7 @@ const cmdsOf = (env) => {
|
|
|
126
130
|
const snapshotOf = (env) => {
|
|
127
131
|
const ops = env.ops;
|
|
128
132
|
if (ops === void 0) return protocolError("hello missing snapshot");
|
|
129
|
-
if (ops.length
|
|
130
|
-
const op = ops[0];
|
|
131
|
-
if (op["op"] !== "replace" || !Array.isArray(op["path"]) || op["path"].length !== 0 || !Object.hasOwn(op, "value")) return protocolError("hello snapshot must be one root replace op");
|
|
133
|
+
if (ops.length === 0) return protocolError("hello missing snapshot");
|
|
132
134
|
return reviveOps(ops);
|
|
133
135
|
};
|
|
134
136
|
const helloSynOf = (env) => {
|
|
@@ -146,7 +148,7 @@ const handleHello = (c, env) => {
|
|
|
146
148
|
});
|
|
147
149
|
c.ctx.onIdle(syn.idle ?? false);
|
|
148
150
|
c.awaitingHello = false;
|
|
149
|
-
c.
|
|
151
|
+
c.error = void 0;
|
|
150
152
|
c.liveAt = Date.now();
|
|
151
153
|
c.authDenials = 0;
|
|
152
154
|
c.episode = null;
|
|
@@ -186,28 +188,18 @@ const deliverEnvelope = (c, env) => {
|
|
|
186
188
|
}
|
|
187
189
|
if (typeof env.idle === "boolean") c.ctx.onIdle(env.idle);
|
|
188
190
|
};
|
|
189
|
-
const transientCauseOf = (reason) => {
|
|
190
|
-
switch (reason) {
|
|
191
|
-
case "evicted": return "evicted";
|
|
192
|
-
case "error": return "error";
|
|
193
|
-
case "unauthorized": return "unauthorized";
|
|
194
|
-
default: return "error";
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
191
|
const settleFin = (c, fin) => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
...rest,
|
|
203
|
-
payload: reviveValue(rest.payload, meta)
|
|
204
|
-
};
|
|
205
|
-
}
|
|
192
|
+
const finish = fin.meta === void 0 ? fin : {
|
|
193
|
+
...fin,
|
|
194
|
+
payload: reviveValue(fin.payload, fin.meta)
|
|
195
|
+
};
|
|
206
196
|
releaseHeld(c);
|
|
197
|
+
c.retryAt = Date.now() + (finish.retry ?? 0);
|
|
207
198
|
if (finish.reason === "idle") {
|
|
208
199
|
c.ctx.onIdle(true);
|
|
209
200
|
c.attempt = 0;
|
|
210
201
|
c.episode = null;
|
|
202
|
+
c.error = void 0;
|
|
211
203
|
setStatus(c, {
|
|
212
204
|
status: "disconnected",
|
|
213
205
|
reason: "idle"
|
|
@@ -215,23 +207,26 @@ const settleFin = (c, fin) => {
|
|
|
215
207
|
return "idle";
|
|
216
208
|
}
|
|
217
209
|
const denial = finish.reason === "unauthorized";
|
|
218
|
-
const terminal = finish.reason === "gone" || denial && c.authDenials > 0;
|
|
219
|
-
if (denial)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
});
|
|
210
|
+
const terminal = finish.reason === "gone" || finish.reason === "forbidden" || finish.reason === "protocol-error" || denial && c.authDenials > 0;
|
|
211
|
+
if (denial) {
|
|
212
|
+
c.authDenials += 1;
|
|
213
|
+
c.authStale = true;
|
|
214
|
+
}
|
|
215
|
+
c.error = finish.reason !== "reconnect" && finish.reason !== "gone" && (!denial || terminal) ? Object.assign(new Error(finish.message ?? finish.reason), { fin: finish }) : void 0;
|
|
216
|
+
if (c.error) c.onError(c.error);
|
|
224
217
|
if (!terminal) {
|
|
225
218
|
c.episode = {
|
|
226
|
-
cause:
|
|
227
|
-
...finish.message !== void 0 && { message: finish.message }
|
|
219
|
+
cause: finish.reason === "reconnect" ? "reconnect" : denial ? "unauthorized" : "error",
|
|
220
|
+
...finish.message !== void 0 && { message: finish.message },
|
|
221
|
+
...finish.code !== void 0 && { code: finish.code }
|
|
228
222
|
};
|
|
229
223
|
if (denial) openGate(c.gate);
|
|
230
224
|
return "reattach";
|
|
231
225
|
}
|
|
226
|
+
c.ctx.onFinish(finish);
|
|
232
227
|
setStatus(c, {
|
|
233
228
|
status: "disconnected",
|
|
234
|
-
reason: "gone",
|
|
229
|
+
reason: denial ? "unauthorized" : finish.reason === "forbidden" ? "forbidden" : finish.reason === "protocol-error" ? "protocol-error" : "gone",
|
|
235
230
|
...finish.message !== void 0 && { message: finish.message },
|
|
236
231
|
...finish.payload !== void 0 && { payload: finish.payload }
|
|
237
232
|
});
|
|
@@ -262,8 +257,7 @@ const scheduleRetry = (c, failure, immediate) => {
|
|
|
262
257
|
c.liveAt = null;
|
|
263
258
|
c.attempt += 1;
|
|
264
259
|
c.episode ??= { cause: "dropped" };
|
|
265
|
-
|
|
266
|
-
const delay = backoff(c.attempt);
|
|
260
|
+
const delay = Math.max(immediate ? 0 : backoff(c.attempt), c.retryAt - Date.now());
|
|
267
261
|
setStatus(c, {
|
|
268
262
|
status: "connecting",
|
|
269
263
|
attempt: c.attempt,
|
|
@@ -292,6 +286,10 @@ const run = async (c, lifetime, gate) => {
|
|
|
292
286
|
c.parked = false;
|
|
293
287
|
}
|
|
294
288
|
while (alive()) {
|
|
289
|
+
if (c.retryAt > Date.now()) {
|
|
290
|
+
await waitGate(gate, c.retryAt - Date.now());
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
295
293
|
flushTapSync(() => dialing(c));
|
|
296
294
|
const abort = new AbortController();
|
|
297
295
|
c.abort = abort;
|
|
@@ -302,7 +300,17 @@ const run = async (c, lifetime, gate) => {
|
|
|
302
300
|
try {
|
|
303
301
|
outcome = await c.attemptOnce(abort);
|
|
304
302
|
} catch (error) {
|
|
305
|
-
if (!abort.signal.aborted)
|
|
303
|
+
if (!abort.signal.aborted) {
|
|
304
|
+
if (error instanceof SyntaxError || error instanceof Error && /^(statewire: protocol error:|Statewire cannot apply)/.test(error.message)) outcome = flushTapSync(() => settleFin(c, {
|
|
305
|
+
reason: "protocol-error",
|
|
306
|
+
message: error.message
|
|
307
|
+
}));
|
|
308
|
+
else if (c.authStale) outcome = flushTapSync(() => settleFin(c, {
|
|
309
|
+
reason: "unauthorized",
|
|
310
|
+
message: error instanceof Error ? error.message : "credential refresh failed"
|
|
311
|
+
}));
|
|
312
|
+
failure = error instanceof Error && error.message ? error.message : "network";
|
|
313
|
+
}
|
|
306
314
|
}
|
|
307
315
|
abort.abort();
|
|
308
316
|
if (!alive()) return;
|
|
@@ -311,6 +319,7 @@ const run = async (c, lifetime, gate) => {
|
|
|
311
319
|
c.ctx.onDetach();
|
|
312
320
|
});
|
|
313
321
|
if (!alive()) return;
|
|
322
|
+
if (c.connection.status === "disconnected" && !["deferred", "idle"].includes(c.connection.reason)) outcome = "stop";
|
|
314
323
|
if (outcome === "stop" || outcome === "idle") {
|
|
315
324
|
await waitGate(gate);
|
|
316
325
|
continue;
|
|
@@ -321,18 +330,37 @@ const run = async (c, lifetime, gate) => {
|
|
|
321
330
|
await waitGate(gate, delay);
|
|
322
331
|
}
|
|
323
332
|
};
|
|
324
|
-
const
|
|
333
|
+
const unsupportedVersion = (c, version) => flushTapSync(() => handleEnvelope(c, { fin: {
|
|
334
|
+
reason: "protocol-error",
|
|
335
|
+
message: `unsupported protocol version: ${version || "\"\""}`
|
|
336
|
+
} })) ?? "stop";
|
|
337
|
+
const resolveHeaders = async (headers, staleAuth) => new Headers(typeof headers === "function" ? await headers({ staleAuth }) : headers);
|
|
338
|
+
const attachFrame = async (c, headers) => {
|
|
339
|
+
const resolved = await resolveHeaders(headers, c.authStale);
|
|
340
|
+
c.authStale = false;
|
|
341
|
+
const lanes = flushTapSync(() => c.ctx.takeAttachLanes());
|
|
342
|
+
return {
|
|
343
|
+
headers: resolved,
|
|
344
|
+
body: JSON.stringify({
|
|
345
|
+
headers: Object.fromEntries(resolved),
|
|
346
|
+
...lanes ?? {}
|
|
347
|
+
})
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
const useLink = (props) => {
|
|
325
351
|
const latest = useRef(props);
|
|
326
352
|
latest.current = props;
|
|
327
353
|
const [, setConnection] = useState(null);
|
|
328
354
|
const [c] = useState(() => {
|
|
329
355
|
const conn = {
|
|
330
356
|
parked: props.isNew ?? false,
|
|
331
|
-
hasSnapshot: false,
|
|
332
357
|
lease: null,
|
|
333
358
|
attempt: 0,
|
|
334
359
|
authDenials: 0,
|
|
360
|
+
authStale: false,
|
|
335
361
|
liveAt: null,
|
|
362
|
+
retryAt: 0,
|
|
363
|
+
error: void 0,
|
|
336
364
|
episode: null,
|
|
337
365
|
awaitingHello: true,
|
|
338
366
|
held: [],
|
|
@@ -342,6 +370,7 @@ const useStatewireConn = (props) => {
|
|
|
342
370
|
get ctx() {
|
|
343
371
|
return latest.current.ctx;
|
|
344
372
|
},
|
|
373
|
+
onError: (error) => latest.current.onError?.(error),
|
|
345
374
|
onChange: (connection) => latest.current.onChange?.(connection),
|
|
346
375
|
attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),
|
|
347
376
|
notify: setConnection,
|
|
@@ -360,12 +389,22 @@ const useStatewireConn = (props) => {
|
|
|
360
389
|
};
|
|
361
390
|
return conn;
|
|
362
391
|
});
|
|
392
|
+
const [link] = useState(() => ({
|
|
393
|
+
get connection() {
|
|
394
|
+
return c.connection;
|
|
395
|
+
},
|
|
396
|
+
get parked() {
|
|
397
|
+
return c.parked;
|
|
398
|
+
},
|
|
399
|
+
send: (frame) => latest.current.send(c, frame)
|
|
400
|
+
}));
|
|
363
401
|
useEffect(() => {
|
|
364
|
-
c.hasSnapshot = false;
|
|
365
402
|
c.lease = null;
|
|
366
403
|
c.attempt = 0;
|
|
367
404
|
c.authDenials = 0;
|
|
405
|
+
c.authStale = false;
|
|
368
406
|
c.liveAt = null;
|
|
407
|
+
c.retryAt = 0;
|
|
369
408
|
c.episode = null;
|
|
370
409
|
setStatus(c, c.parked ? {
|
|
371
410
|
status: "disconnected",
|
|
@@ -385,10 +424,10 @@ const useStatewireConn = (props) => {
|
|
|
385
424
|
openGate(c.gate);
|
|
386
425
|
};
|
|
387
426
|
}, [c, props.url]);
|
|
388
|
-
return
|
|
427
|
+
return link;
|
|
389
428
|
};
|
|
390
429
|
const classify = (timeout) => /* @__PURE__ */ new Error(timeout.aborted ? "timeout" : "network");
|
|
391
430
|
//#endregion
|
|
392
|
-
export { TERMINAL, classify, degradedOf, handleEnvelope,
|
|
431
|
+
export { TERMINAL, attachFrame, classify, degradedOf, handleEnvelope, resolveHeaders, settleFin, unsupportedVersion, useLink, validateRefusalSyn };
|
|
393
432
|
|
|
394
433
|
//# sourceMappingURL=connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","TERMINAL","const","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","parked","hasSnapshot","lease","attempt","authDenials","liveAt","episode","awaitingHello","held","gate","abort","AbortController","lifetime","ctx","onChange","attemptOnce","notify","reconnect","degradedOf","state","status","reason","sameState","a","b","nextRetryAt","lastError","setStatus","c","degraded","protocolError","Error","isObject","value","Record","Array","isArray","isNonnegativeSafeInteger","Number","isSafeInteger","isPositiveSafeInteger","validateOptionalString","field","validateSynScalars","validateRefusalSyn","validateCmd","validateFin","validateEnvelope","entry","releaseHeld","length","onCmds","cmdsOf","env","snapshotOf","op","Object","hasOwn","helloSynOf","handleHello","onHello","onIdle","Date","now","warnedUnknownSynFact","warnUnknownSynFact","console","warn","deliverSyn","onRefusal","keys","deliverEnvelope","onOps","cmds","push","transientCauseOf","settleFin","finish","rest","denial","terminal","onFinish","handleEnvelope","dialing","scheduleRetry","failure","immediate","backoffResetMs","delay","reconnectConn","run","alive","outcome","error","signal","aborted","onDetach","useStatewireConn","props","url","isNew","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, timing } 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\n/** The terminal cmd verdict types; \"applied\" is the sole non-terminal type. */\nexport const TERMINAL = [\n \"result\",\n \"rejected\",\n \"failed\",\n \"crashed\",\n \"expired\",\n] as const;\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | (typeof TERMINAL)[number];\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?: \"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 parked: boolean;\n hasSnapshot: boolean;\n lease: string | null;\n attempt: number;\n authDenials: number;\n liveAt: number | null;\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\nexport const degradedOf = (state: ConnectionState): boolean =>\n (state.status === \"connecting\" && state.attempt > 0) ||\n (state.status === \"disconnected\" && state.reason === \"gone\");\n\nconst sameState = (a: ConnectionState, b: ConnectionState): boolean => {\n if (a.status !== b.status) return false;\n if (a.status === \"connecting\" && b.status === \"connecting\") {\n return (\n a.attempt === b.attempt &&\n a.cause === b.cause &&\n a.nextRetryAt === b.nextRetryAt &&\n a.lastError === b.lastError\n );\n }\n if (a.status === \"disconnected\" && b.status === \"disconnected\") {\n return a.reason === b.reason;\n }\n return true;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n if (sameState(next, c.connection)) return;\n c.connection = {\n ...next,\n reconnect: c.reconnect,\n degraded: degradedOf(next),\n };\n c.notify(c.connection);\n c.onChange(c.connection);\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isNonnegativeSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) >= 0;\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) > 0;\n\nconst validateOptionalString = (value: unknown, field: string): void => {\n if (value !== undefined && typeof value !== \"string\") {\n protocolError(`${field} must be a string`);\n }\n};\n\nconst validateSynScalars = (syn: Record<string, unknown>) => {\n if (syn[\"seq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"seq\"])) {\n protocolError(\"syn.seq must be a nonnegative safe integer\");\n }\n if (syn[\"ctxSeq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"ctxSeq\"])) {\n protocolError(\"syn.ctxSeq must be a nonnegative safe integer\");\n }\n if (syn[\"retry\"] !== undefined && !isNonnegativeSafeInteger(syn[\"retry\"])) {\n protocolError(\"syn.retry must be a nonnegative safe integer\");\n }\n validateOptionalString(syn[\"id\"], \"syn.id\");\n if (syn[\"idle\"] !== undefined && typeof syn[\"idle\"] !== \"boolean\") {\n protocolError(\"syn.idle must be a boolean\");\n }\n validateOptionalString(syn[\"record\"], \"syn.record\");\n};\n\nexport const validateRefusalSyn = (value: unknown): StatewireSyn => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (\n value[\"id\"] !== undefined ||\n value[\"idle\"] !== undefined ||\n value[\"record\"] !== undefined\n ) {\n protocolError(\"invalid refusal syn\");\n }\n if (\n value[\"seq\"] === undefined &&\n value[\"ctxSeq\"] === undefined &&\n value[\"retry\"] === undefined\n ) {\n protocolError(\"refusal syn has no watermarks or retry\");\n }\n return value as StatewireSyn;\n};\n\nconst validateCmd = (value: unknown): value is StatewireCmd => {\n if (!isObject(value)) return protocolError(\"cmd entry must be an object\");\n if (!isPositiveSafeInteger(value[\"seq\"])) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n validateOptionalString(value[\"type\"], \"cmd.type\");\n validateOptionalString(value[\"code\"], \"cmd.code\");\n validateOptionalString(value[\"message\"], \"cmd.message\");\n if (value[\"dur\"] !== undefined && value[\"dur\"] !== true) {\n protocolError(\"cmd.dur must be true\");\n }\n return true;\n};\n\nconst validateFin = (value: unknown): value is StatewireHost.Fin => {\n if (!isObject(value)) return protocolError(\"fin must be an object\");\n if (typeof value[\"reason\"] !== \"string\") {\n protocolError(\"fin.reason must be a string\");\n }\n validateOptionalString(value[\"message\"], \"fin.message\");\n return true;\n};\n\nconst validateEnvelope = (value: unknown): StatewireEnvelope => {\n if (!isObject(value)) return protocolError(\"packet must be an object\");\n if (value[\"ops\"] !== undefined && !Array.isArray(value[\"ops\"])) {\n protocolError(\"ops must be an array\");\n }\n if (value[\"cmd\"] !== undefined) {\n const cmd = value[\"cmd\"];\n if (!Array.isArray(cmd)) return protocolError(\"cmd must be an array\");\n for (const entry of cmd) validateCmd(entry);\n }\n if (value[\"idle\"] !== undefined && typeof value[\"idle\"] !== \"boolean\") {\n protocolError(\"idle must be a boolean\");\n }\n if (value[\"next\"] !== undefined) {\n if (value[\"next\"] !== true) protocolError(\"next must be true\");\n if (\n value[\"cmd\"] === undefined ||\n value[\"ops\"] !== undefined ||\n value[\"fin\"] !== undefined\n ) {\n protocolError(\"next must ride a cmd-only packet\");\n }\n }\n if (value[\"fin\"] !== undefined) validateFin(value[\"fin\"]);\n return value as StatewireEnvelope;\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 return env.cmd;\n};\n\nconst snapshotOf = (env: StatewireEnvelope): unknown[] => {\n const ops = env.ops;\n if (ops === undefined) return protocolError(\"hello missing snapshot\");\n if (ops.length !== 1 || !isObject(ops[0])) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n const op = ops[0];\n if (\n op[\"op\"] !== \"replace\" ||\n !Array.isArray(op[\"path\"]) ||\n op[\"path\"].length !== 0 ||\n !Object.hasOwn(op, \"value\")\n ) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n return reviveOps(ops);\n};\n\nconst helloSynOf = (env: StatewireEnvelope): StatewireSyn => {\n if (!isObject(env.syn)) return protocolError(\"expected hello\");\n validateSynScalars(env.syn);\n if (env.syn[\"retry\"] !== undefined) {\n protocolError(\"hello syn cannot carry retry\");\n }\n return env.syn as StatewireSyn;\n};\n\nconst handleHello = (c: Conn, env: StatewireEnvelope) => {\n const syn = helloSynOf(env);\n const ops = snapshotOf(env);\n c.ctx.onHello({\n ops,\n syn,\n });\n c.ctx.onIdle(syn.idle ?? false);\n c.awaitingHello = false;\n c.hasSnapshot = true;\n c.liveAt = Date.now();\n c.authDenials = 0;\n c.episode = null;\n setStatus(c, { status: \"connected\" });\n};\n\nlet warnedUnknownSynFact = false;\n\nconst warnUnknownSynFact = () => {\n if (warnedUnknownSynFact) return;\n warnedUnknownSynFact = true;\n console.warn(\"statewire: ignoring unknown syn fact\");\n};\n\nconst deliverSyn = (c: Conn, value: unknown) => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (value[\"record\"] !== undefined || value[\"idle\"] !== undefined) {\n protocolError(\"invalid mid-stream syn\");\n }\n if (\n value[\"seq\"] !== undefined ||\n value[\"ctxSeq\"] !== undefined ||\n value[\"retry\"] !== undefined\n ) {\n c.ctx.onRefusal(validateRefusalSyn(value));\n return;\n }\n if (value[\"id\"] !== undefined) return;\n if (Object.keys(value).length === 0) {\n protocolError(\"invalid mid-stream syn\");\n }\n warnUnknownSynFact();\n};\n\nconst deliverEnvelope = (c: Conn, env: StatewireEnvelope) => {\n if (env.syn !== undefined) deliverSyn(c, env.syn);\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 transientCauseOf = (\n reason: StatewireHost.Fin[\"reason\"],\n): Episode[\"cause\"] => {\n switch (reason) {\n case \"evicted\":\n return \"evicted\";\n case \"error\":\n return \"error\";\n case \"unauthorized\":\n return \"unauthorized\";\n default:\n return \"error\";\n }\n};\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: \"disconnected\", reason: \"idle\" });\n return \"idle\";\n }\n // An auth denial gets one immediate redial; a consecutive repeat parks as gone.\n const denial = finish.reason === \"unauthorized\";\n const terminal = finish.reason === \"gone\" || (denial && c.authDenials > 0);\n if (denial) c.authDenials += 1;\n c.ctx.onFinish(terminal ? finish : { ...finish, reason: \"error\" });\n if (!terminal) {\n c.episode = {\n cause: transientCauseOf(finish.reason),\n ...(finish.message !== undefined && { message: finish.message }),\n };\n if (denial) openGate(c.gate);\n return \"reattach\";\n }\n setStatus(c, {\n status: \"disconnected\",\n reason: \"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 value: unknown,\n): AttemptOutcome | null => {\n const env = validateEnvelope(value);\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 !== \"connected\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"connected\" });\n }\n return null;\n};\n\nconst dialing = (c: Conn) =>\n setStatus(c, { status: \"connecting\", attempt: c.attempt });\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n) => {\n if (c.liveAt !== null && Date.now() - c.liveAt >= timing.backoffResetMs) {\n c.attempt = 0;\n }\n c.liveAt = null;\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n if (immediate) return 0;\n const delay = backoff(c.attempt);\n setStatus(c, {\n status: \"connecting\",\n attempt: c.attempt,\n ...c.episode,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"disconnected\") {\n // A reconnect out of a park starts a fresh episode: first dial, attempt 0.\n c.attempt = 0;\n c.authDenials = 0;\n c.episode = null;\n flushTapSync(() => dialing(c));\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 if (c.parked) {\n await waitGate(gate);\n if (!alive()) return;\n c.parked = false;\n }\n while (alive()) {\n // The dial is in flight: attempt alone, no backoff fields.\n flushTapSync(() => dialing(c));\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 isNew?: boolean | undefined;\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 parked: props.isNew ?? false,\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n authDenials: 0,\n liveAt: null,\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: {\n ...((props.isNew ?? false)\n ? { status: \"disconnected\", reason: \"deferred\" }\n : { status: \"connecting\", attempt: 0 }),\n degraded: false,\n reconnect: () => conn.reconnect(),\n },\n };\n return conn;\n });\n\n useEffect(() => {\n c.hasSnapshot = false;\n c.lease = null;\n c.attempt = 0;\n c.authDenials = 0;\n c.liveAt = null;\n c.episode = null;\n setStatus(\n c,\n c.parked\n ? { status: \"disconnected\", reason: \"deferred\" }\n : { status: \"connecting\", attempt: 0 },\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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"}
|
|
1
|
+
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","RemoteLink","StatewireFrame","StatewireRemoteProps","StatewireSubmitResult","StatewireClient","StatewireHost","Episode","cause","message","code","TERMINAL","const","StatewireCmd","seq","type","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","parked","lease","attempt","authDenials","authStale","liveAt","retryAt","error","ConnectionError","episode","awaitingHello","held","gate","abort","AbortController","lifetime","ctx","onError","onChange","attemptOnce","notify","reconnect","degradedOf","state","status","reason","sameState","a","b","nextRetryAt","lastError","setStatus","c","degraded","protocolError","Error","isObject","value","Record","Array","isArray","isNonnegativeSafeInteger","Number","isSafeInteger","isPositiveSafeInteger","validateOptionalString","field","validateSynScalars","validateRefusalSyn","validateCmd","validateFin","validateEnvelope","entry","releaseHeld","length","onCmds","cmdsOf","env","snapshotOf","helloSynOf","handleHello","onHello","onIdle","Date","now","warnedUnknownSynFact","warnUnknownSynFact","console","warn","deliverSyn","onRefusal","Object","keys","deliverEnvelope","onOps","cmds","push","settleFin","finish","denial","terminal","report","assign","onFinish","handleEnvelope","dialing","scheduleRetry","failure","immediate","backoffResetMs","delay","Math","max","reconnectConn","run","alive","outcome","signal","aborted","SyntaxError","test","onDetach","includes","unsupportedVersion","version","resolveHeaders","headers","HeadersOption","staleAuth","Headers","attachFrame","resolved","lanes","takeAttachLanes","body","JSON","stringify","fromEntries","useLink","props","url","isNew","send","frame","latest","current","setConnection","conn","link","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, timing } from \"./timing\";\nimport type {\n ConnectionState,\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n StatewireSubmitResult,\n} from \"../transports/sse\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\n\nexport type Episode = {\n cause: \"dropped\" | \"reconnect\" | \"error\" | \"unauthorized\";\n message?: string;\n code?: string;\n};\n\n/** The terminal cmd verdict types; \"applied\" is the sole non-terminal type. */\nexport const TERMINAL = [\n \"result\",\n \"rejected\",\n \"failed\",\n \"crashed\",\n \"expired\",\n] as const;\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | (typeof TERMINAL)[number];\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?: \"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 parked: boolean;\n lease: string | null;\n attempt: number;\n authDenials: number;\n authStale: boolean;\n liveAt: number | null;\n retryAt: number;\n error: StatewireClient.ConnectionError | undefined;\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 onError: (error: StatewireClient.ConnectionError) => void;\n onChange: (connection: StatewireClient.Connection) => void;\n attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;\n notify: (connection: StatewireClient.Connection) => void;\n reconnect: () => void;\n};\n\nexport const degradedOf = (state: ConnectionState): boolean =>\n (state.status === \"connecting\" && state.attempt > 0) ||\n (state.status === \"disconnected\" &&\n state.reason !== \"idle\" &&\n state.reason !== \"deferred\");\n\nconst sameState = (a: ConnectionState, b: ConnectionState): boolean => {\n if (a.status !== b.status) return false;\n if (a.status === \"connecting\" && b.status === \"connecting\") {\n return (\n a.attempt === b.attempt &&\n a.cause === b.cause &&\n a.code === b.code &&\n a.nextRetryAt === b.nextRetryAt &&\n a.lastError === b.lastError\n );\n }\n if (a.status === \"disconnected\" && b.status === \"disconnected\") {\n return a.reason === b.reason;\n }\n return true;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n if (sameState(next, c.connection) && c.connection.error === c.error) return;\n c.connection = {\n ...next,\n ...(c.error === undefined ? {} : { error: c.error }),\n reconnect: c.reconnect,\n degraded: degradedOf(next),\n };\n c.notify(c.connection);\n c.onChange(c.connection);\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isNonnegativeSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) >= 0;\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) > 0;\n\nconst validateOptionalString = (value: unknown, field: string): void => {\n if (value !== undefined && typeof value !== \"string\") {\n protocolError(`${field} must be a string`);\n }\n};\n\nconst validateSynScalars = (syn: Record<string, unknown>) => {\n if (syn[\"seq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"seq\"])) {\n protocolError(\"syn.seq must be a nonnegative safe integer\");\n }\n if (syn[\"ctxSeq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"ctxSeq\"])) {\n protocolError(\"syn.ctxSeq must be a nonnegative safe integer\");\n }\n if (syn[\"retry\"] !== undefined && !isNonnegativeSafeInteger(syn[\"retry\"])) {\n protocolError(\"syn.retry must be a nonnegative safe integer\");\n }\n validateOptionalString(syn[\"id\"], \"syn.id\");\n if (syn[\"idle\"] !== undefined && typeof syn[\"idle\"] !== \"boolean\") {\n protocolError(\"syn.idle must be a boolean\");\n }\n validateOptionalString(syn[\"record\"], \"syn.record\");\n};\n\nexport const validateRefusalSyn = (value: unknown): StatewireSyn => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (\n value[\"id\"] !== undefined ||\n value[\"idle\"] !== undefined ||\n value[\"record\"] !== undefined\n ) {\n protocolError(\"invalid refusal syn\");\n }\n if (\n value[\"seq\"] === undefined &&\n value[\"ctxSeq\"] === undefined &&\n value[\"retry\"] === undefined\n ) {\n protocolError(\"refusal syn has no watermarks or retry\");\n }\n return value as StatewireSyn;\n};\n\nconst validateCmd = (value: unknown): value is StatewireCmd => {\n if (!isObject(value)) return protocolError(\"cmd entry must be an object\");\n if (!isPositiveSafeInteger(value[\"seq\"])) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n validateOptionalString(value[\"type\"], \"cmd.type\");\n validateOptionalString(value[\"code\"], \"cmd.code\");\n validateOptionalString(value[\"message\"], \"cmd.message\");\n if (value[\"dur\"] !== undefined && value[\"dur\"] !== true) {\n protocolError(\"cmd.dur must be true\");\n }\n return true;\n};\n\nconst validateFin = (value: unknown): value is StatewireHost.Fin => {\n if (!isObject(value)) return protocolError(\"fin must be an object\");\n if (typeof value[\"reason\"] !== \"string\") {\n protocolError(\"fin.reason must be a string\");\n }\n validateOptionalString(value[\"message\"], \"fin.message\");\n validateOptionalString(value[\"code\"], \"fin.code\");\n if (\n value[\"status\"] !== undefined &&\n !isNonnegativeSafeInteger(value[\"status\"])\n )\n protocolError(\"fin.status must be a nonnegative safe integer\");\n if (value[\"retry\"] !== undefined && !isNonnegativeSafeInteger(value[\"retry\"]))\n protocolError(\"fin.retry must be a nonnegative safe integer\");\n return true;\n};\n\nconst validateEnvelope = (value: unknown): StatewireEnvelope => {\n if (!isObject(value)) return protocolError(\"packet must be an object\");\n if (value[\"ops\"] !== undefined && !Array.isArray(value[\"ops\"])) {\n protocolError(\"ops must be an array\");\n }\n if (value[\"cmd\"] !== undefined) {\n const cmd = value[\"cmd\"];\n if (!Array.isArray(cmd)) return protocolError(\"cmd must be an array\");\n for (const entry of cmd) validateCmd(entry);\n }\n if (value[\"idle\"] !== undefined && typeof value[\"idle\"] !== \"boolean\") {\n protocolError(\"idle must be a boolean\");\n }\n if (value[\"next\"] !== undefined) {\n if (value[\"next\"] !== true) protocolError(\"next must be true\");\n if (\n value[\"cmd\"] === undefined ||\n value[\"ops\"] !== undefined ||\n value[\"fin\"] !== undefined\n ) {\n protocolError(\"next must ride a cmd-only packet\");\n }\n }\n if (value[\"fin\"] !== undefined) validateFin(value[\"fin\"]);\n return value as StatewireEnvelope;\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 return env.cmd;\n};\n\nconst snapshotOf = (env: StatewireEnvelope): unknown[] => {\n const ops = env.ops;\n if (ops === undefined) return protocolError(\"hello missing snapshot\");\n if (ops.length === 0) return protocolError(\"hello missing snapshot\");\n return reviveOps(ops);\n};\n\nconst helloSynOf = (env: StatewireEnvelope): StatewireSyn => {\n if (!isObject(env.syn)) return protocolError(\"expected hello\");\n validateSynScalars(env.syn);\n if (env.syn[\"retry\"] !== undefined) {\n protocolError(\"hello syn cannot carry retry\");\n }\n return env.syn as StatewireSyn;\n};\n\nconst handleHello = (c: Conn, env: StatewireEnvelope) => {\n const syn = helloSynOf(env);\n const ops = snapshotOf(env);\n c.ctx.onHello({\n ops,\n syn,\n });\n c.ctx.onIdle(syn.idle ?? false);\n c.awaitingHello = false;\n c.error = undefined;\n c.liveAt = Date.now();\n c.authDenials = 0;\n c.episode = null;\n setStatus(c, { status: \"connected\" });\n};\n\nlet warnedUnknownSynFact = false;\n\nconst warnUnknownSynFact = () => {\n if (warnedUnknownSynFact) return;\n warnedUnknownSynFact = true;\n console.warn(\"statewire: ignoring unknown syn fact\");\n};\n\nconst deliverSyn = (c: Conn, value: unknown) => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (value[\"record\"] !== undefined || value[\"idle\"] !== undefined) {\n protocolError(\"invalid mid-stream syn\");\n }\n if (\n value[\"seq\"] !== undefined ||\n value[\"ctxSeq\"] !== undefined ||\n value[\"retry\"] !== undefined\n ) {\n c.ctx.onRefusal(validateRefusalSyn(value));\n return;\n }\n if (value[\"id\"] !== undefined) return;\n if (Object.keys(value).length === 0) {\n protocolError(\"invalid mid-stream syn\");\n }\n warnUnknownSynFact();\n};\n\nconst deliverEnvelope = (c: Conn, env: StatewireEnvelope) => {\n if (env.syn !== undefined) deliverSyn(c, env.syn);\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\nexport const settleFin = (c: Conn, fin: StatewireHost.Fin): AttemptOutcome => {\n const finish =\n fin.meta === undefined\n ? fin\n : { ...fin, payload: reviveValue(fin.payload, fin.meta) };\n releaseHeld(c);\n c.retryAt = Date.now() + (finish.retry ?? 0);\n if (finish.reason === \"idle\") {\n c.ctx.onIdle(true);\n c.attempt = 0;\n c.episode = null;\n c.error = undefined;\n setStatus(c, { status: \"disconnected\", reason: \"idle\" });\n return \"idle\";\n }\n const denial = finish.reason === \"unauthorized\";\n const terminal =\n finish.reason === \"gone\" ||\n finish.reason === \"forbidden\" ||\n finish.reason === \"protocol-error\" ||\n (denial && c.authDenials > 0);\n if (denial) {\n c.authDenials += 1;\n c.authStale = true;\n }\n const report =\n finish.reason !== \"reconnect\" &&\n finish.reason !== \"gone\" &&\n (!denial || terminal);\n c.error = report\n ? Object.assign(new Error(finish.message ?? finish.reason), { fin: finish })\n : undefined;\n if (c.error) c.onError(c.error);\n if (!terminal) {\n c.episode = {\n cause:\n finish.reason === \"reconnect\"\n ? \"reconnect\"\n : denial\n ? \"unauthorized\"\n : \"error\",\n ...(finish.message !== undefined && { message: finish.message }),\n ...(finish.code !== undefined && { code: finish.code }),\n };\n if (denial) openGate(c.gate);\n return \"reattach\";\n }\n c.ctx.onFinish(finish);\n setStatus(c, {\n status: \"disconnected\",\n reason: denial\n ? \"unauthorized\"\n : finish.reason === \"forbidden\"\n ? \"forbidden\"\n : finish.reason === \"protocol-error\"\n ? \"protocol-error\"\n : \"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 value: unknown,\n): AttemptOutcome | null => {\n const env = validateEnvelope(value);\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 !== \"connected\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"connected\" });\n }\n return null;\n};\n\nconst dialing = (c: Conn) =>\n setStatus(c, { status: \"connecting\", attempt: c.attempt });\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n) => {\n if (c.liveAt !== null && Date.now() - c.liveAt >= timing.backoffResetMs) {\n c.attempt = 0;\n }\n c.liveAt = null;\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n const delay = Math.max(\n immediate ? 0 : backoff(c.attempt),\n c.retryAt - Date.now(),\n );\n setStatus(c, {\n status: \"connecting\",\n attempt: c.attempt,\n ...c.episode,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"disconnected\") {\n // A reconnect out of a park starts a fresh episode: first dial, attempt 0.\n c.attempt = 0;\n c.authDenials = 0;\n c.episode = null;\n flushTapSync(() => dialing(c));\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 if (c.parked) {\n await waitGate(gate);\n if (!alive()) return;\n c.parked = false;\n }\n while (alive()) {\n if (c.retryAt > Date.now()) {\n await waitGate(gate, c.retryAt - Date.now());\n continue;\n }\n // The dial is in flight: attempt alone, no backoff fields.\n flushTapSync(() => dialing(c));\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 if (\n error instanceof SyntaxError ||\n (error instanceof Error &&\n /^(statewire: protocol error:|Statewire cannot apply)/.test(\n error.message,\n ))\n ) {\n outcome = flushTapSync(() =>\n settleFin(c, { reason: \"protocol-error\", message: error.message }),\n );\n } else if (c.authStale) {\n outcome = flushTapSync(() =>\n settleFin(c, {\n reason: \"unauthorized\",\n message:\n error instanceof Error\n ? error.message\n : \"credential refresh failed\",\n }),\n );\n }\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 (\n c.connection.status === \"disconnected\" &&\n ![\"deferred\", \"idle\"].includes(c.connection.reason)\n )\n outcome = \"stop\";\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 unsupportedVersion = (c: Conn, version: string): AttemptOutcome =>\n flushTapSync(() =>\n handleEnvelope(c, {\n fin: {\n reason: \"protocol-error\",\n message: `unsupported protocol version: ${version || '\"\"'}`,\n },\n }),\n ) ?? \"stop\";\n\nexport const resolveHeaders = async (\n headers: StatewireClient.HeadersOption | undefined,\n staleAuth: boolean,\n): Promise<Headers> =>\n new Headers(\n typeof headers === \"function\" ? await headers({ staleAuth }) : headers,\n );\n\n// The stale flag clears only once the resolver returns: a throwing refresh keeps it for the next attempt.\nexport const attachFrame = async (\n c: Conn,\n headers: StatewireClient.HeadersOption | undefined,\n) => {\n const resolved = await resolveHeaders(headers, c.authStale);\n c.authStale = false;\n const lanes = flushTapSync(() => c.ctx.takeAttachLanes());\n return {\n headers: resolved,\n body: JSON.stringify({\n headers: Object.fromEntries(resolved),\n ...(lanes ?? {}),\n }),\n };\n};\n\nexport const useLink = (props: {\n url: string;\n isNew?: boolean | undefined;\n onChange?: ((connection: StatewireClient.Connection) => void) | undefined;\n onError?: ((error: StatewireClient.ConnectionError) => void) | undefined;\n ctx: StatewireRemoteProps;\n attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;\n send: (c: Conn, frame: StatewireFrame) => Promise<StatewireSubmitResult>;\n}): RemoteLink => {\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 parked: props.isNew ?? false,\n lease: null,\n attempt: 0,\n authDenials: 0,\n authStale: false,\n liveAt: null,\n retryAt: 0,\n error: undefined,\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 onError: (error) => latest.current.onError?.(error),\n onChange: (connection) => latest.current.onChange?.(connection),\n attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),\n notify: setConnection,\n reconnect: () => reconnectConn(conn),\n connection: {\n ...((props.isNew ?? false)\n ? { status: \"disconnected\", reason: \"deferred\" }\n : { status: \"connecting\", attempt: 0 }),\n degraded: false,\n reconnect: () => conn.reconnect(),\n },\n };\n return conn;\n });\n const [link] = useState(\n (): RemoteLink => ({\n get connection() {\n return c.connection;\n },\n get parked() {\n return c.parked;\n },\n send: (frame) => latest.current.send(c, frame),\n }),\n );\n\n useEffect(() => {\n c.lease = null;\n c.attempt = 0;\n c.authDenials = 0;\n c.authStale = false;\n c.liveAt = null;\n c.retryAt = 0;\n c.episode = null;\n setStatus(\n c,\n c.parked\n ? { status: \"disconnected\", reason: \"deferred\" }\n : { status: \"connecting\", attempt: 0 },\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 link;\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
|
@@ -21,6 +21,7 @@ type Core = {
|
|
|
21
21
|
notifiedPublished: unknown;
|
|
22
22
|
notifiedOverlayPaths: readonly StatewirePath[];
|
|
23
23
|
notifiedConnection: StatewireClient.Connection | null;
|
|
24
|
+
notifiedError: StatewireClient.ConnectionError | undefined;
|
|
24
25
|
notifiedBusy: boolean;
|
|
25
26
|
notifiedFailed: readonly StatewireClient.FailedCommand[];
|
|
26
27
|
stale: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":";;;;;cAYa,sBAAsB,gBAAgB;KAiB9C;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,yBAAyB,gBAAgB;EACzC;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,kBAAkB;EAClB;EACA;EACA,0BAA0B,gBAAgB;EAC1C,UAAU,UAAU;EACpB,YAAY;;
|
|
1
|
+
{"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":";;;;;cAYa,sBAAsB,gBAAgB;KAiB9C;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,eAAe,gBAAgB;EAC/B;EACA,yBAAyB,gBAAgB;EACzC;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,kBAAkB;EAClB;EACA;EACA,0BAA0B,gBAAgB;EAC1C,UAAU,UAAU;EACpB,YAAY;;cAmOD,mBACX,kBAAkB,gBAAgB,mBAClC,OAAO,uBAAkB"}
|