statewire 0.13.1 → 0.14.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.
@@ -14,9 +14,20 @@ export namespace StatewireHostInternal {
14
14
  close: () => void;
15
15
  };
16
16
 
17
+ export type SocketConnectOptions = {
18
+ /** The adapter restored this socket from an already-authorized session. */
19
+ authorized?: boolean;
20
+ /** Persist authorization before the first snapshot is released. */
21
+ onAuthorized?: () => void;
22
+ };
23
+
17
24
  export type SocketHost = {
18
25
  /** Null means refused: the socket was told (fin and/or close) and is dead. */
19
- connect: (clientId: string, socket: Socket) => SocketConnection | null;
26
+ connect: (
27
+ clientId: string,
28
+ socket: Socket,
29
+ options?: SocketConnectOptions,
30
+ ) => SocketConnection | null;
20
31
  stream: (request: Request) => Promise<Response>;
21
32
  frames: (request: Request) => Promise<Response>;
22
33
  finish: (fin: StatewireHost.Fin) => void;
@@ -27,6 +38,8 @@ export namespace StatewireHostInternal {
27
38
  authorize?: (ctx: {
28
39
  headers: Record<string, string>;
29
40
  }) => void | Promise<void>;
41
+ /** Time allowed to complete WebSocket initialization and authorization. */
42
+ wsInitTimeoutMs?: number;
30
43
  heartbeatIntervalMs?: number;
31
44
  /** LRU cap on stored client records; live or in-flight records are exempt. */
32
45
  clientRecordLimit?: number;
package/src/statewire.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  withKey,
10
10
  } from "@assistant-ui/tap";
11
11
  import type { ResourceElement } from "@assistant-ui/tap";
12
- import { LIVE_CONNECTION, useStatewireCore } from "./client/core";
12
+ import { CONNECTED_CONNECTION, useStatewireCore } from "./client/core";
13
13
  import type { Statewire } from "./types";
14
14
  import type { TERMINAL } from "./client/connection";
15
15
  import { SessionStorageContext } from "./client/sessionStore";
@@ -64,7 +64,7 @@ export const useStatewire = <
64
64
  core.getFailed,
65
65
  core.getFailed,
66
66
  );
67
- const connection = value.connection ?? LIVE_CONNECTION;
67
+ const connection = value.connection ?? CONNECTED_CONNECTION;
68
68
 
69
69
  return useMemo(() => {
70
70
  const failures = (): NonNullable<typeof value.failures> => {
@@ -211,20 +211,29 @@ export namespace StatewireClient {
211
211
  discardFailed: (entry: FailedCommand) => void;
212
212
  };
213
213
 
214
+ /** `cause`/`nextRetryAt`/`lastError` are present only while waiting out the backoff timer; a dial in flight carries `attempt` alone. */
214
215
  export type ConnectionState =
215
- | { status: "connecting" }
216
- | { status: "live" }
217
- | { status: "idle" }
218
216
  | {
219
- status: "reconnecting";
220
- cause: "dropped" | "evicted" | "error" | "unauthorized";
217
+ status: "disconnected";
218
+ reason: "deferred" | "idle" | "gone";
219
+ message?: string;
220
+ payload?: unknown;
221
+ }
222
+ | {
223
+ status: "connecting";
224
+ /** 0 = the episode's first dial, >0 = retry. */
221
225
  attempt: number;
222
- nextRetryAt: number;
226
+ cause?: "dropped" | "evicted" | "error" | "unauthorized";
227
+ nextRetryAt?: number;
223
228
  lastError?: string;
224
229
  message?: string;
225
230
  }
226
- | { status: "gone"; message?: string; payload?: unknown };
227
- export type Connection = ConnectionState & { reconnect: () => void };
231
+ | { status: "connected" };
232
+ export type Connection = ConnectionState & {
233
+ reconnect: () => void;
234
+ /** True while retrying (`connecting` with `attempt > 0`) or parked `gone`. */
235
+ degraded: boolean;
236
+ };
228
237
  export type ConnectionStatus = Connection["status"];
229
238
 
230
239
  /**