statewire 0.8.1 → 0.9.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.
Files changed (95) hide show
  1. package/README.md +2 -2
  2. package/dist/client/connection.d.ts +23 -19
  3. package/dist/client/connection.d.ts.map +1 -1
  4. package/dist/client/connection.js +143 -103
  5. package/dist/client/connection.js.map +1 -1
  6. package/dist/client/core.d.ts +30 -3
  7. package/dist/client/core.d.ts.map +1 -1
  8. package/dist/client/core.js +222 -260
  9. package/dist/client/core.js.map +1 -1
  10. package/dist/client/session.d.ts +34 -39
  11. package/dist/client/session.d.ts.map +1 -1
  12. package/dist/client/session.js +380 -373
  13. package/dist/client/session.js.map +1 -1
  14. package/dist/client/sessionStore.d.ts +1 -0
  15. package/dist/client/sessionStore.d.ts.map +1 -1
  16. package/dist/client/sessionStore.js.map +1 -1
  17. package/dist/client/timing.d.ts +1 -2
  18. package/dist/client/timing.d.ts.map +1 -1
  19. package/dist/client/timing.js +1 -5
  20. package/dist/client/timing.js.map +1 -1
  21. package/dist/host/draft.d.ts +2 -5
  22. package/dist/host/draft.d.ts.map +1 -1
  23. package/dist/host/draft.js.map +1 -1
  24. package/dist/host/host.d.ts +5 -8
  25. package/dist/host/host.d.ts.map +1 -1
  26. package/dist/host/host.js +23 -32
  27. package/dist/host/host.js.map +1 -1
  28. package/dist/host/socket.d.ts +3 -2
  29. package/dist/host/socket.d.ts.map +1 -1
  30. package/dist/host/socket.js +484 -424
  31. package/dist/host/socket.js.map +1 -1
  32. package/dist/host-internal.d.ts +4 -0
  33. package/dist/host-internal.d.ts.map +1 -1
  34. package/dist/host.d.ts +10 -10
  35. package/dist/host.d.ts.map +1 -1
  36. package/dist/host.js +2 -2
  37. package/dist/index.d.ts +1 -2
  38. package/dist/index.js +1 -2
  39. package/dist/statewire.d.ts +29 -24
  40. package/dist/statewire.d.ts.map +1 -1
  41. package/dist/statewire.js +8 -2
  42. package/dist/statewire.js.map +1 -1
  43. package/dist/transports/fake-sse.d.ts +1 -1
  44. package/dist/transports/fake-sse.d.ts.map +1 -1
  45. package/dist/transports/fake-sse.js +5 -2
  46. package/dist/transports/fake-sse.js.map +1 -1
  47. package/dist/transports/fixtures.d.ts +1 -2
  48. package/dist/transports/fixtures.d.ts.map +1 -1
  49. package/dist/transports/fixtures.js +5 -7
  50. package/dist/transports/fixtures.js.map +1 -1
  51. package/dist/transports/remote.d.ts +2 -1
  52. package/dist/transports/remote.d.ts.map +1 -1
  53. package/dist/transports/remote.js +10 -4
  54. package/dist/transports/remote.js.map +1 -1
  55. package/dist/transports/sse.d.ts +32 -46
  56. package/dist/transports/sse.d.ts.map +1 -1
  57. package/dist/transports/sse.js +56 -59
  58. package/dist/transports/sse.js.map +1 -1
  59. package/dist/transports/testing.d.ts +7 -5
  60. package/dist/transports/testing.d.ts.map +1 -1
  61. package/dist/transports/testing.js +41 -14
  62. package/dist/transports/testing.js.map +1 -1
  63. package/dist/transports/ws.d.ts +2 -3
  64. package/dist/transports/ws.d.ts.map +1 -1
  65. package/dist/transports/ws.js +25 -80
  66. package/dist/transports/ws.js.map +1 -1
  67. package/dist/types.d.ts +8 -8
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/types.js +3 -4
  70. package/dist/types.js.map +1 -1
  71. package/dist/wire/ops.d.ts +1 -5
  72. package/dist/wire/ops.d.ts.map +1 -1
  73. package/dist/wire/ops.js +1 -4
  74. package/dist/wire/ops.js.map +1 -1
  75. package/package.json +1 -1
  76. package/src/client/connection.ts +175 -128
  77. package/src/client/core.ts +287 -278
  78. package/src/client/session.ts +474 -480
  79. package/src/client/sessionStore.ts +1 -0
  80. package/src/client/timing.ts +0 -6
  81. package/src/host/draft.ts +1 -3
  82. package/src/host/host.ts +41 -58
  83. package/src/host/socket.ts +653 -448
  84. package/src/host-internal.ts +4 -0
  85. package/src/host.ts +13 -10
  86. package/src/index.ts +0 -1
  87. package/src/statewire.ts +38 -32
  88. package/src/transports/fake-sse.ts +2 -1
  89. package/src/transports/fixtures.ts +4 -9
  90. package/src/transports/remote.ts +12 -5
  91. package/src/transports/sse.ts +99 -115
  92. package/src/transports/testing.ts +47 -19
  93. package/src/transports/ws.ts +44 -115
  94. package/src/types.ts +15 -14
  95. package/src/wire/ops.ts +9 -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 over SSE (`GET /stream`, snapshot first); clients change it by POSTing method calls (`POST /commands`, body `{"method", "params"}`). 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.
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, Event>({
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 StatewireResponse = {
9
+ type StatewireCmd = {
10
10
  seq: number;
11
- type: "accepted" | "rejected" | "pending" | "crashed" | "result-unavailable";
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
- lastSeq: number;
19
- lease?: string;
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
- res?: StatewireResponse[];
24
- ack?: number;
29
+ cmd?: StatewireCmd[];
30
+ next?: true;
31
+ idle?: boolean;
25
32
  syn?: StatewireSyn;
26
33
  fin?: StatewireHost.Fin;
27
34
  };
28
- declare const createGate: () => {
29
- take: () => boolean;
30
- wait: (timeoutMs?: number) => Promise<void>;
31
- open: () => void;
35
+ type Gate = {
36
+ latched: boolean;
37
+ wake: (() => void) | undefined;
32
38
  };
33
- type Gate = ReturnType<typeof createGate>;
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<"stop" | "reattach" | "ended">;
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) => "stop" | "reattach" | null;
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<"stop" | "reattach" | "ended">;
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, StatewireEnvelope, StatewireResponse, StatewireSyn, classify, handleEnvelope, overloadedError, parseRetryAfter, useStatewireConn };
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;;cAGhB;;EASiB,OAAA,uBAAM;;;KA2BjB,OAAO,kBAAkB;KAEzB;EACV,YAAY,gBAAgB;EAC5B;EACA;EACA;EACA,SAAS;EACT,MAAM;EACN,OAAO;EACP,UAAU;EACV,KAAK;EACL,WAAW,YAAY,gBAAgB;EACvC,cACE,OAAO,oBACJ;EACL,SAAS,YAAY,gBAAgB;EACrC;;cAuEW,iBACX,GAAG,MACH,KAAK;cAyEM,mBAAoB;EAC/B;EACA,aAAa,YAAY,gBAAgB;EACzC,KAAK;EACL,cACE,GAAG,MACH,OAAO,oBACJ;MACH;cA8CS,WAAY,SAAS,gBAAW;cAGhC,kBAAmB;cAQnB,kBAAmB,0BAAqB"}
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, reviveResponse, reviveValue } from "../wire/meta.js";
2
- import { retryDelay } from "./timing.js";
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 createGate = () => {
7
- let latched = false;
8
- let wake;
9
- return {
10
- take: () => {
11
- const was = latched;
12
- latched = false;
13
- return was;
14
- },
15
- wait: (timeoutMs) => {
16
- if (latched) {
17
- latched = false;
18
- return Promise.resolve();
19
- }
20
- return new Promise((resolve) => {
21
- const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
22
- wake = void 0;
23
- resolve();
24
- }, timeoutMs);
25
- wake = () => {
26
- clearTimeout(timer);
27
- wake = void 0;
28
- resolve();
29
- };
30
- });
31
- },
32
- open: () => {
33
- if (wake) wake();
34
- else latched = true;
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
- ctx.onSnapshot({
54
- ops,
55
- lastSeq: env.syn.lastSeq
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
- c.hasSnapshot = true;
58
- } else if (env.ops !== void 0 || env.res !== void 0 || env.ack !== void 0) ctx.onOps({
59
- ops,
60
- ...res !== void 0 && { res },
61
- ...env.ack !== void 0 && { ack: env.ack }
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 settleEnvelope = (c, env) => {
66
- let finish = env.fin;
67
- if (finish) {
68
- if (finish.meta !== void 0) {
69
- const { meta, ...rest } = finish;
70
- finish = {
71
- ...rest,
72
- payload: reviveValue(rest.payload, meta)
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
- if (c.connection.status !== "live") {
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: "live" });
108
+ setStatus(c, { status: "idle" });
109
+ return "idle";
94
110
  }
95
- return null;
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 settleEnvelope(c, env);
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, cause) => {
142
+ const scheduleRetry = (c, failure, immediate) => {
102
143
  c.attempt += 1;
103
144
  c.episode ??= { cause: "dropped" };
104
- const delay = immediate ? 0 : retryDelay(cause, c.attempt);
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.open();
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
- gone = await c.attemptOnce(abort) === "stop";
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
- if (gone) {
146
- await gate.wait();
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.take();
150
- const delay = flushTapSync(() => scheduleRetry(c, failure, immediate, cause));
193
+ const immediate = takeGate(gate);
194
+ const delay = flushTapSync(() => scheduleRetry(c, failure, immediate));
151
195
  if (delay === 0) continue;
152
- await gate.wait(delay);
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: createGate(),
212
+ gate: newGate(),
167
213
  lifetime: null,
168
- ctx: props.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 = createGate();
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.open();
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, overloadedError, parseRetryAfter, useStatewireConn };
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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"}
@@ -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
- declare const useStatewireCore: (getOptions: () => StatewireClient.Options<any, any, any>, value: Statewire<unknown>) => {
6
- attach: (next: Statewire<unknown>) => void;
7
- detach: () => void;
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":";;;cAYa,iBAAiB,gBAAgB;cA6TjC,mBACX,kBAAkB,gBAAgB,wBAClC,OAAO;EAnFe,SAAA,MAAA;;;;EAwEE,YAAA"}
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"}