statewire 0.0.1 → 0.0.2

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 (148) hide show
  1. package/README.md +14 -13
  2. package/dist/client/changes.d.ts +2 -2
  3. package/dist/client/changes.d.ts.map +1 -1
  4. package/dist/client/changes.js +3 -10
  5. package/dist/client/changes.js.map +1 -1
  6. package/dist/client/connection.d.ts +18 -39
  7. package/dist/client/connection.d.ts.map +1 -1
  8. package/dist/client/connection.js +61 -54
  9. package/dist/client/connection.js.map +1 -1
  10. package/dist/client/core.d.ts +7 -11
  11. package/dist/client/core.d.ts.map +1 -1
  12. package/dist/client/core.js +58 -55
  13. package/dist/client/core.js.map +1 -1
  14. package/dist/client/session.d.ts +8 -25
  15. package/dist/client/session.d.ts.map +1 -1
  16. package/dist/client/session.js +56 -36
  17. package/dist/client/session.js.map +1 -1
  18. package/dist/client/sessionStore.d.ts +1 -1
  19. package/dist/client/sessionStore.js.map +1 -1
  20. package/dist/client/timing.d.ts +2 -1
  21. package/dist/client/timing.d.ts.map +1 -1
  22. package/dist/client/timing.js +5 -1
  23. package/dist/client/timing.js.map +1 -1
  24. package/dist/host/adapter.d.ts +8 -0
  25. package/dist/host/adapter.d.ts.map +1 -0
  26. package/dist/host/adapter.js +23 -0
  27. package/dist/host/adapter.js.map +1 -0
  28. package/dist/host/draft.d.ts +21 -7
  29. package/dist/host/draft.d.ts.map +1 -1
  30. package/dist/host/draft.js +190 -103
  31. package/dist/host/draft.js.map +1 -1
  32. package/dist/host/host.d.ts +18 -35
  33. package/dist/host/host.d.ts.map +1 -1
  34. package/dist/host/host.js +81 -53
  35. package/dist/host/host.js.map +1 -1
  36. package/dist/host/socket.d.ts +14 -0
  37. package/dist/host/socket.d.ts.map +1 -0
  38. package/dist/host/socket.js +459 -0
  39. package/dist/host/socket.js.map +1 -0
  40. package/dist/host-internal.d.ts +27 -0
  41. package/dist/host-internal.d.ts.map +1 -0
  42. package/dist/host-internal.js +3 -0
  43. package/dist/host.d.ts +44 -0
  44. package/dist/host.d.ts.map +1 -0
  45. package/dist/host.js +4 -0
  46. package/dist/index.d.ts +5 -28
  47. package/dist/index.js +2 -3
  48. package/dist/statewire.d.ts +94 -10
  49. package/dist/statewire.d.ts.map +1 -1
  50. package/dist/statewire.js +23 -35
  51. package/dist/statewire.js.map +1 -1
  52. package/dist/transports/fake-sse.d.ts +12 -0
  53. package/dist/transports/fake-sse.d.ts.map +1 -0
  54. package/dist/transports/fake-sse.js +39 -0
  55. package/dist/transports/fake-sse.js.map +1 -0
  56. package/dist/transports/fixtures.d.ts +33 -0
  57. package/dist/transports/fixtures.d.ts.map +1 -0
  58. package/dist/transports/fixtures.js +50 -0
  59. package/dist/transports/fixtures.js.map +1 -0
  60. package/dist/transports/remote.d.ts +6 -13
  61. package/dist/transports/remote.d.ts.map +1 -1
  62. package/dist/transports/remote.js +5 -24
  63. package/dist/transports/remote.js.map +1 -1
  64. package/dist/transports/sse.d.ts +10 -20
  65. package/dist/transports/sse.d.ts.map +1 -1
  66. package/dist/transports/sse.js +42 -41
  67. package/dist/transports/sse.js.map +1 -1
  68. package/dist/transports/testing.d.ts +4 -3
  69. package/dist/transports/testing.d.ts.map +1 -1
  70. package/dist/transports/testing.js +19 -46
  71. package/dist/transports/testing.js.map +1 -1
  72. package/dist/transports/ws.d.ts +4 -13
  73. package/dist/transports/ws.d.ts.map +1 -1
  74. package/dist/transports/ws.js +57 -60
  75. package/dist/transports/ws.js.map +1 -1
  76. package/dist/types.d.ts +53 -100
  77. package/dist/types.d.ts.map +1 -1
  78. package/dist/types.js.map +1 -1
  79. package/dist/util/draftTraps.d.ts +6 -0
  80. package/dist/util/draftTraps.d.ts.map +1 -0
  81. package/dist/util/draftTraps.js +25 -0
  82. package/dist/util/draftTraps.js.map +1 -0
  83. package/dist/util/macrotask.d.ts +5 -0
  84. package/dist/util/macrotask.d.ts.map +1 -0
  85. package/dist/util/macrotask.js +25 -0
  86. package/dist/util/macrotask.js.map +1 -0
  87. package/dist/util/methodProxy.d.ts +5 -0
  88. package/dist/util/methodProxy.d.ts.map +1 -0
  89. package/dist/util/methodProxy.js +17 -0
  90. package/dist/util/methodProxy.js.map +1 -0
  91. package/dist/util/randomId.d.ts +5 -0
  92. package/dist/util/randomId.d.ts.map +1 -0
  93. package/dist/util/randomId.js +6 -0
  94. package/dist/util/randomId.js.map +1 -0
  95. package/dist/{client/optimistic.d.ts → util/recordMutations.d.ts} +2 -2
  96. package/dist/util/recordMutations.d.ts.map +1 -0
  97. package/dist/{client/optimistic.js → util/recordMutations.js} +5 -24
  98. package/dist/util/recordMutations.js.map +1 -0
  99. package/dist/util/statePath.d.ts +14 -0
  100. package/dist/util/statePath.d.ts.map +1 -0
  101. package/dist/util/statePath.js +31 -0
  102. package/dist/util/statePath.js.map +1 -0
  103. package/dist/wire/meta.d.ts +6 -1
  104. package/dist/wire/meta.d.ts.map +1 -1
  105. package/dist/wire/meta.js +30 -28
  106. package/dist/wire/meta.js.map +1 -1
  107. package/dist/wire/ops.d.ts +5 -26
  108. package/dist/wire/ops.d.ts.map +1 -1
  109. package/dist/wire/ops.js +67 -80
  110. package/dist/wire/ops.js.map +1 -1
  111. package/package.json +15 -11
  112. package/src/client/changes.ts +5 -15
  113. package/src/client/connection.ts +87 -113
  114. package/src/client/core.ts +57 -91
  115. package/src/client/session.ts +87 -70
  116. package/src/client/sessionStore.ts +1 -1
  117. package/src/client/timing.ts +6 -0
  118. package/src/host/adapter.ts +33 -0
  119. package/src/host/draft.ts +293 -125
  120. package/src/host/host.ts +107 -106
  121. package/src/host/socket.ts +542 -0
  122. package/src/host-internal.ts +28 -0
  123. package/src/host.ts +61 -0
  124. package/src/index.ts +2 -62
  125. package/src/statewire.ts +154 -61
  126. package/src/transports/fake-sse.ts +51 -0
  127. package/src/transports/fixtures.ts +60 -0
  128. package/src/transports/remote.ts +12 -54
  129. package/src/transports/sse.ts +68 -81
  130. package/src/transports/testing.ts +27 -59
  131. package/src/transports/ws.ts +76 -92
  132. package/src/types.ts +50 -131
  133. package/src/util/draftTraps.ts +32 -0
  134. package/src/util/macrotask.ts +25 -0
  135. package/src/util/methodProxy.ts +16 -0
  136. package/src/util/randomId.ts +4 -0
  137. package/src/{client/optimistic.ts → util/recordMutations.ts} +0 -0
  138. package/src/util/statePath.ts +61 -0
  139. package/src/wire/meta.ts +42 -47
  140. package/src/wire/ops.ts +107 -106
  141. package/LICENSE +0 -21
  142. package/dist/client/optimistic.d.ts.map +0 -1
  143. package/dist/client/optimistic.js.map +0 -1
  144. package/dist/index.d.ts.map +0 -1
  145. package/dist/server.d.ts +0 -15
  146. package/dist/server.d.ts.map +0 -1
  147. package/dist/server.js +0 -3
  148. package/src/server.ts +0 -32
@@ -1,7 +1,11 @@
1
- import { createChanges } from "./changes.js";
2
- import { recordMutations } from "./optimistic.js";
1
+ import { recordMutations } from "../util/recordMutations.js";
2
+ import { createMethodProxy } from "../util/methodProxy.js";
3
3
  import { StatewireSendError } from "../types.js";
4
4
  import { getCommandsHost, getEmitHost, getStateHost } from "../host/host.js";
5
+ import { createMacrotaskPoster } from "../util/macrotask.js";
6
+ import { createChanges } from "./changes.js";
7
+ import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
8
+ import { useEffect, useState } from "@assistant-ui/tap/react-shim";
5
9
  //#region src/client/core.ts
6
10
  const LIVE_CONNECTION = {
7
11
  status: "live",
@@ -12,27 +16,7 @@ const failureOf = (error) => error instanceof StatewireSendError ? error.failure
12
16
  cause: "server",
13
17
  message: error instanceof Error ? error.message : String(error)
14
18
  };
15
- const createMacrotaskPoster = (callback) => {
16
- if (typeof MessageChannel !== "undefined") {
17
- let port1 = null;
18
- let port2;
19
- return () => {
20
- if (!port1) {
21
- const channel = new MessageChannel();
22
- channel.port1.onmessage = () => {
23
- port1?.unref?.();
24
- callback();
25
- };
26
- port1 = channel.port1;
27
- port2 = channel.port2;
28
- }
29
- port1.ref?.();
30
- port2.postMessage(null);
31
- };
32
- }
33
- return () => setTimeout(callback, 0);
34
- };
35
- const createStatewireCore = (getOptions) => {
19
+ const createCore = (getOptions) => {
36
20
  let value = null;
37
21
  let source = null;
38
22
  let overlays = [];
@@ -48,7 +32,6 @@ const createStatewireCore = (getOptions) => {
48
32
  let stale = false;
49
33
  let overlaysDirty = false;
50
34
  let flushing = false;
51
- let disposed = false;
52
35
  const listeners = /* @__PURE__ */ new Set();
53
36
  const post = createMacrotaskPoster(() => {
54
37
  scheduled = false;
@@ -56,7 +39,7 @@ const createStatewireCore = (getOptions) => {
56
39
  });
57
40
  let scheduled = false;
58
41
  const schedule = () => {
59
- if (scheduled || disposed) return;
42
+ if (scheduled) return;
60
43
  scheduled = true;
61
44
  post();
62
45
  };
@@ -67,7 +50,7 @@ const createStatewireCore = (getOptions) => {
67
50
  const drainCanonical = () => {
68
51
  if (source) {
69
52
  pendingChanged.push(...source.takeChanged());
70
- canonical = source.root();
53
+ canonical = source.snapshot();
71
54
  } else if (value && value.state !== canonical) {
72
55
  pendingChanged.push([]);
73
56
  canonical = value.state;
@@ -108,7 +91,7 @@ const createStatewireCore = (getOptions) => {
108
91
  };
109
92
  const getConnection = () => value?.connection ?? LIVE_CONNECTION;
110
93
  const flush = () => {
111
- if (disposed || !value) return;
94
+ if (!value) return;
112
95
  if (flushing) {
113
96
  schedule();
114
97
  return;
@@ -135,7 +118,7 @@ const createStatewireCore = (getOptions) => {
135
118
  updates = [];
136
119
  for (const update of batch) options.onCommandChange?.(update, { state: published });
137
120
  }
138
- if (events.length > 0) {
121
+ if (events.length > 0 && published !== void 0) {
139
122
  const batch = events;
140
123
  events = [];
141
124
  for (const event of batch) options.onEvent?.(event, { state: published });
@@ -188,7 +171,7 @@ const createStatewireCore = (getOptions) => {
188
171
  schedule();
189
172
  };
190
173
  const attach = (next) => {
191
- if (disposed || next === value) return;
174
+ if (next === value) return;
192
175
  const prev = value;
193
176
  value = next;
194
177
  const nextSource = next.source ?? (typeof next.state === "object" && next.state !== null ? getStateHost(next.state) : void 0) ?? null;
@@ -223,38 +206,23 @@ const createStatewireCore = (getOptions) => {
223
206
  value = null;
224
207
  }
225
208
  };
226
- const memo = /* @__PURE__ */ new Map();
227
209
  return {
228
210
  attach,
229
211
  detach,
230
- dispose: () => {
231
- detach();
232
- disposed = true;
233
- },
234
212
  getState: () => {
235
213
  recompute();
236
214
  return published;
237
215
  },
238
- getConnection,
239
- commands: new Proxy({}, { get: (_target, method) => {
240
- if (typeof method !== "string") return void 0;
241
- let fn = memo.get(method);
242
- if (fn === void 0) {
243
- fn = (...params) => {
244
- if (!value) return Promise.reject(new StatewireSendError({
245
- fate: "aborted",
246
- cause: "unmount"
247
- }));
248
- const send = value.commands[method];
249
- const promise = send(...params);
250
- flush();
251
- return promise;
252
- };
253
- memo.set(method, fn);
254
- }
255
- return fn;
256
- } }),
257
- flush,
216
+ commands: createMethodProxy((method, params) => {
217
+ if (!value) return Promise.reject(new StatewireSendError({
218
+ fate: "aborted",
219
+ cause: "unmount"
220
+ }));
221
+ const send = value.commands[method];
222
+ const promise = send(...params);
223
+ flush();
224
+ return promise;
225
+ }),
258
226
  subscribe: (listener) => {
259
227
  listeners.add(listener);
260
228
  return () => {
@@ -263,7 +231,42 @@ const createStatewireCore = (getOptions) => {
263
231
  }
264
232
  };
265
233
  };
234
+ const useStatewireCore = (getOptions, value) => {
235
+ const $ = c(8);
236
+ let t0;
237
+ if ($[0] !== getOptions) {
238
+ t0 = () => createCore(getOptions);
239
+ $[0] = getOptions;
240
+ $[1] = t0;
241
+ } else t0 = $[1];
242
+ const [core] = useState(t0);
243
+ core.attach(value);
244
+ let t1;
245
+ if ($[2] !== core || $[3] !== value) {
246
+ t1 = () => {
247
+ core.attach(value);
248
+ };
249
+ $[2] = core;
250
+ $[3] = value;
251
+ $[4] = t1;
252
+ } else t1 = $[4];
253
+ useEffect(t1);
254
+ let t2;
255
+ let t3;
256
+ if ($[5] !== core) {
257
+ t2 = () => () => core.detach();
258
+ t3 = [core];
259
+ $[5] = core;
260
+ $[6] = t2;
261
+ $[7] = t3;
262
+ } else {
263
+ t2 = $[6];
264
+ t3 = $[7];
265
+ }
266
+ useEffect(t2, t3);
267
+ return core;
268
+ };
266
269
  //#endregion
267
- export { LIVE_CONNECTION, createStatewireCore };
270
+ export { LIVE_CONNECTION, useStatewireCore };
268
271
 
269
272
  //# sourceMappingURL=core.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","names":["createChanges","getCommandsHost","getEmitHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","StatewirePath","StatewireSendError","SendFailure","StatewireSendFailure","StatewireStateSource","StatewireTransport","StatewireCommand","StatewireCommandsProxy","StatewireCommandUpdate","StatewireOptimisticContext","StatewireOptions","StatewireConnection","LIVE_CONNECTION","status","reconnect","failureOf","error","failure","fate","cause","message","Error","String","createMacrotaskPoster","callback","MessageChannel","port1","MessagePort","ref","unref","port2","channel","onmessage","postMessage","setTimeout","Overlay","inv","dropped","StatewireCore","ReturnType","createStatewireCore","getOptions","value","source","overlays","updates","events","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","stale","overlaysDirty","flushing","disposed","listeners","Set","post","scheduled","flush","schedule","markStale","drainCanonical","push","takeChanged","root","state","recompute","prevCanonical","optimistic","next","paths","undefined","overlay","ctx","ts","seq","clientId","recorded","draft","method","params","changedPaths","console","getConnection","connection","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","event","onEvent","connectionChanged","listener","note","extra","result","commandsListener","onStart","onPosted","onAck","onCovered","some","o","filter","onSettle","outcome","type","payload","eventSink","attach","nextSource","setOnChange","commands","setListener","emit","setSink","detach","memo","Map","Promise","Proxy","get","_target","fn","reject","send","Record","args","promise","set","dispose","getState","subscribe","add","delete"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { createChanges } from \"./changes\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"./optimistic\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n SendFailure as StatewireSendFailure,\n StatewireStateSource,\n StatewireTransport,\n StatewireCommand,\n StatewireCommandsProxy,\n StatewireCommandUpdate,\n StatewireOptimisticContext,\n StatewireOptions,\n} from \"../types\";\nimport type { StatewireConnection } from \"../transports/sse\";\n\nexport const LIVE_CONNECTION: StatewireConnection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireSendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\n// MessageChannel schedules the flush as a macrotask (tap's pattern): it runs\n// after the whole microtask drain, so every producer in the current turn\n// coalesces. The port is ref'd only while a flush is pending — an active\n// MessagePort holds the Node event loop open. ref/unref are Node-only.\nconst createMacrotaskPoster = (callback: () => void): (() => void) => {\n if (typeof MessageChannel !== \"undefined\") {\n let port1: (MessagePort & { ref?: () => void; unref?: () => void }) | null =\n null;\n let port2: MessagePort;\n return () => {\n if (!port1) {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n port1?.unref?.();\n callback();\n };\n port1 = channel.port1;\n port2 = channel.port2;\n }\n port1.ref?.();\n port2!.postMessage(null);\n };\n }\n return () => setTimeout(callback, 0);\n};\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\nexport type StatewireCore = ReturnType<typeof createStatewireCore>;\n\nexport const createStatewireCore = (\n getOptions: () => StatewireOptions<any, any, any>,\n) => {\n let value: StatewireTransport<unknown> | null = null;\n let source: StatewireStateSource | null = null;\n let overlays: Overlay[] = [];\n let updates: StatewireCommandUpdate[] = [];\n let events: unknown[] = [];\n let pendingChanged: StatewirePath[] = [];\n let canonical: unknown;\n let published: unknown;\n let overlayPaths: readonly StatewirePath[] = [];\n let notifiedPublished: unknown;\n let notifiedOverlayPaths: readonly StatewirePath[] = [];\n let notifiedConnection: StatewireConnection | null = null;\n let stale = false;\n let overlaysDirty = false;\n let flushing = false;\n let disposed = false;\n const listeners = new Set<() => void>();\n\n const post = createMacrotaskPoster(() => {\n scheduled = false;\n flush();\n });\n let scheduled = false;\n const schedule = () => {\n if (scheduled || disposed) return;\n scheduled = true;\n post();\n };\n\n const markStale = () => {\n stale = true;\n schedule();\n };\n\n const drainCanonical = () => {\n if (source) {\n pendingChanged.push(...source.takeChanged());\n canonical = source.root();\n } else if (value && value.state !== canonical) {\n pendingChanged.push([]);\n canonical = value.state;\n }\n };\n\n const recompute = () => {\n if (!stale || !value) return;\n stale = false;\n const prevCanonical = canonical;\n drainCanonical();\n if (canonical === prevCanonical && !overlaysDirty) return;\n overlaysDirty = false;\n const { optimistic } = getOptions();\n let next = canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && canonical !== undefined) {\n for (const overlay of overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireOptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as StatewireCommand,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n published = next;\n overlayPaths = paths;\n };\n\n const getConnection = (): StatewireConnection =>\n value?.connection ?? LIVE_CONNECTION;\n\n const flush = () => {\n if (disposed || !value) return;\n if (flushing) {\n schedule();\n return;\n }\n flushing = true;\n try {\n recompute();\n const options = getOptions();\n const stateChanged = published !== notifiedPublished;\n if (stateChanged) {\n const prev = notifiedPublished;\n const changed = [\n ...pendingChanged,\n ...notifiedOverlayPaths,\n ...overlayPaths,\n ];\n pendingChanged = [];\n notifiedPublished = published;\n notifiedOverlayPaths = overlayPaths;\n options.onStateChange?.(published as never, {\n changes: createChanges(prev, published, changed),\n });\n }\n if (updates.length > 0 && published !== undefined) {\n const batch = updates;\n updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: published as never });\n }\n }\n if (events.length > 0) {\n const batch = events;\n events = [];\n for (const event of batch) {\n options.onEvent?.(event, { state: published as never });\n }\n }\n const connection = getConnection();\n const connectionChanged = connection !== notifiedConnection;\n notifiedConnection = connection;\n if (stateChanged || connectionChanged) {\n for (const listener of [...listeners]) listener();\n }\n } finally {\n flushing = false;\n }\n };\n\n const note = (\n inv: CommandsInvocation,\n status: StatewireCommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireSendFailure },\n ) => {\n if (!getOptions().onCommandChange) return;\n updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule();\n };\n\n const commandsListener: CommandsListener = {\n onStart: (inv) => {\n overlays.push({ inv, dropped: false });\n overlaysDirty = true;\n markStale();\n note(inv, \"queued\");\n },\n onPosted: (inv) => note(inv, \"posted\"),\n onAck: (inv) => note(inv, \"acked\"),\n onCovered: (inv) => {\n if (!overlays.some((o) => o.inv === inv)) return;\n overlays = overlays.filter((o) => o.inv !== inv);\n overlaysDirty = true;\n markStale();\n },\n onSettle: (inv, outcome) => {\n note(\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush();\n },\n };\n\n const eventSink = (event: unknown) => {\n if (!getOptions().onEvent) return;\n events.push(event);\n schedule();\n };\n\n const attach = (next: StatewireTransport<unknown>) => {\n if (disposed || next === value) return;\n const prev = value;\n value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== source) {\n source?.setOnChange(null);\n source = nextSource;\n source?.setOnChange(markStale);\n markStale();\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(commandsListener);\n if (overlays.length > 0) {\n overlays = [];\n overlaysDirty = true;\n }\n markStale();\n }\n if (next.emit !== prev?.emit) {\n if (prev) getEmitHost(prev.emit)?.setSink(null);\n getEmitHost(next.emit)?.setSink(eventSink);\n }\n if (!source) markStale();\n schedule();\n };\n\n const detach = () => {\n source?.setOnChange(null);\n source = null;\n if (value) {\n getCommandsHost(value.commands)?.setListener(null);\n getEmitHost(value.emit)?.setSink(null);\n value = null;\n }\n };\n\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n const commands = new Proxy({} as StatewireCommandsProxy<any>, {\n get: (_target, method) => {\n if (typeof method !== \"string\") return undefined;\n let fn = memo.get(method);\n if (fn === undefined) {\n fn = (...params: unknown[]) => {\n if (!value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush();\n return promise;\n };\n memo.set(method, fn);\n }\n return fn;\n },\n });\n\n return {\n attach,\n detach,\n dispose: () => {\n detach();\n disposed = true;\n },\n getState: () => {\n recompute();\n return published;\n },\n getConnection,\n commands,\n flush,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"core.js","names":["useEffect","useState","createChanges","getCommandsHost","getEmitHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","createMacrotaskPoster","createMethodProxy","StatewirePath","StatewireSendError","Statewire","StatewireClient","LIVE_CONNECTION","Connection","status","reconnect","failureOf","error","SendFailure","failure","fate","cause","message","Error","String","Overlay","inv","dropped","createCore","getOptions","Options","value","source","StateSource","overlays","updates","CommandUpdate","events","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","stale","overlaysDirty","flushing","listeners","Set","post","scheduled","flush","schedule","markStale","drainCanonical","push","takeChanged","snapshot","state","recompute","prevCanonical","optimistic","next","paths","undefined","overlay","ctx","OptimisticContext","ts","seq","clientId","recorded","draft","method","params","Command","changedPaths","console","getConnection","connection","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","event","onEvent","connectionChanged","listener","note","extra","result","commandsListener","onStart","onPosted","onAck","onCovered","some","o","filter","onSettle","outcome","type","payload","eventSink","attach","nextSource","setOnChange","commands","setListener","emit","setSink","detach","CommandsProxy","Promise","reject","send","Record","args","promise","getState","subscribe","add","delete","useStatewireCore","$","_c","t0","core","t1","t2","t3"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createChanges } from \"./changes\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"../util/recordMutations\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\n\nexport const LIVE_CONNECTION: StatewireClient.Connection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireClient.SendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\nconst createCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n) => {\n let value: Statewire<unknown> | null = null;\n let source: Statewire.StateSource | null = null;\n let overlays: Overlay[] = [];\n let updates: Statewire.CommandUpdate[] = [];\n let events: unknown[] = [];\n let pendingChanged: StatewirePath[] = [];\n let canonical: unknown;\n let published: unknown;\n let overlayPaths: readonly StatewirePath[] = [];\n let notifiedPublished: unknown;\n let notifiedOverlayPaths: readonly StatewirePath[] = [];\n let notifiedConnection: StatewireClient.Connection | null = null;\n let stale = false;\n let overlaysDirty = false;\n let flushing = false;\n const listeners = new Set<() => void>();\n\n const post = createMacrotaskPoster(() => {\n scheduled = false;\n flush();\n });\n let scheduled = false;\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n post();\n };\n\n const markStale = () => {\n stale = true;\n schedule();\n };\n\n const drainCanonical = () => {\n if (source) {\n pendingChanged.push(...source.takeChanged());\n canonical = source.snapshot();\n } else if (value && value.state !== canonical) {\n pendingChanged.push([]);\n canonical = value.state;\n }\n };\n\n const recompute = () => {\n if (!stale || !value) return;\n stale = false;\n const prevCanonical = canonical;\n drainCanonical();\n if (canonical === prevCanonical && !overlaysDirty) return;\n overlaysDirty = false;\n const { optimistic } = getOptions();\n let next = canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && canonical !== undefined) {\n for (const overlay of overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireClient.OptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as Statewire.Command,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n published = next;\n overlayPaths = paths;\n };\n\n const getConnection = (): StatewireClient.Connection =>\n value?.connection ?? LIVE_CONNECTION;\n\n const flush = () => {\n if (!value) return;\n if (flushing) {\n schedule();\n return;\n }\n flushing = true;\n try {\n recompute();\n const options = getOptions();\n const stateChanged = published !== notifiedPublished;\n if (stateChanged) {\n const prev = notifiedPublished;\n const changed = [\n ...pendingChanged,\n ...notifiedOverlayPaths,\n ...overlayPaths,\n ];\n pendingChanged = [];\n notifiedPublished = published;\n notifiedOverlayPaths = overlayPaths;\n options.onStateChange?.(published as never, {\n changes: createChanges(prev, published, changed),\n });\n }\n if (updates.length > 0 && published !== undefined) {\n const batch = updates;\n updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: published as never });\n }\n }\n if (events.length > 0 && published !== undefined) {\n const batch = events;\n events = [];\n for (const event of batch) {\n options.onEvent?.(event, { state: published as never });\n }\n }\n const connection = getConnection();\n const connectionChanged = connection !== notifiedConnection;\n notifiedConnection = connection;\n if (stateChanged || connectionChanged) {\n for (const listener of [...listeners]) listener();\n }\n } finally {\n flushing = false;\n }\n };\n\n const note = (\n inv: CommandsInvocation,\n status: Statewire.CommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireClient.SendFailure },\n ) => {\n if (!getOptions().onCommandChange) return;\n updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule();\n };\n\n const commandsListener: CommandsListener = {\n onStart: (inv) => {\n overlays.push({ inv, dropped: false });\n overlaysDirty = true;\n markStale();\n note(inv, \"queued\");\n },\n onPosted: (inv) => note(inv, \"posted\"),\n onAck: (inv) => note(inv, \"acked\"),\n onCovered: (inv) => {\n if (!overlays.some((o) => o.inv === inv)) return;\n overlays = overlays.filter((o) => o.inv !== inv);\n overlaysDirty = true;\n markStale();\n },\n onSettle: (inv, outcome) => {\n note(\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush();\n },\n };\n\n const eventSink = (event: unknown) => {\n if (!getOptions().onEvent) return;\n events.push(event);\n schedule();\n };\n\n const attach = (next: Statewire<unknown>) => {\n if (next === value) return;\n const prev = value;\n value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== source) {\n source?.setOnChange(null);\n source = nextSource;\n source?.setOnChange(markStale);\n markStale();\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(commandsListener);\n if (overlays.length > 0) {\n overlays = [];\n overlaysDirty = true;\n }\n markStale();\n }\n if (next.emit !== prev?.emit) {\n if (prev) getEmitHost(prev.emit)?.setSink(null);\n getEmitHost(next.emit)?.setSink(eventSink);\n }\n if (!source) markStale();\n schedule();\n };\n\n const detach = () => {\n source?.setOnChange(null);\n source = null;\n if (value) {\n getCommandsHost(value.commands)?.setListener(null);\n getEmitHost(value.emit)?.setSink(null);\n value = null;\n }\n };\n\n const commands = createMethodProxy<Statewire.CommandsProxy<any>>(\n (method, params) => {\n if (!value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush();\n return promise;\n },\n );\n\n return {\n attach,\n detach,\n getState: () => {\n recompute();\n return published;\n },\n commands,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nexport const useStatewireCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n value: Statewire<unknown>,\n) => {\n const [core] = useState(() => createCore(getOptions));\n // Attach during render so the first snapshot reads the transport's state;\n // re-attaching the same transport is a no-op and the single-slot host\n // listeners make a swap land on the last writer.\n core.attach(value);\n useEffect(() => {\n core.attach(value);\n });\n useEffect(() => () => core.detach(), [core]);\n return core;\n};\n"],"mappings":"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"}
@@ -1,13 +1,9 @@
1
- import { StatewirePath } from "../wire/ops.js";
2
- import { StatewireResponse } from "./connection.js";
3
- import { StatewireSessionRecord, StatewireSessionStore } from "./sessionStore.js";
4
- import { RemoteLink, StatewireWireCommand } from "../transports/sse.js";
5
- import { StatewireSendError, StatewireStateSource } from "../types.js";
6
1
  import { CommandsInvocation, CommandsListener } from "../host/host.js";
2
+ import { RemoteLink, StatewireRemoteProps, StatewireWireCommand } from "../transports/sse.js";
3
+ import { StatewireSessionRecord, StatewireSessionStore, StatewireSessionStoreOptions } from "./sessionStore.js";
4
+ import { Statewire, StatewireSendError } from "../types.js";
5
+ import { StatewirePath } from "../wire/ops.js";
7
6
  //#region src/client/session.d.ts
8
- type SessionOptions = {
9
- sessionStore?: StatewireSessionStore | undefined;
10
- };
11
7
  /**
12
8
  * queued/trying/posted: overlay live, result pending. awaiting-result: acked
13
9
  * (overlay gone) but the result rides a late res; `posted: false` re-sends.
@@ -47,6 +43,7 @@ type Pending = {
47
43
  type AttachState = "detached" | "attached" | "finished";
48
44
  type Session = {
49
45
  record: StatewireSessionRecord;
46
+ ctx: StatewireRemoteProps;
50
47
  recordPersisted: boolean;
51
48
  store: StatewireSessionStore;
52
49
  lastAck: number;
@@ -54,7 +51,7 @@ type Session = {
54
51
  canonical: unknown;
55
52
  lastPublished: unknown;
56
53
  changed: StatewirePath[];
57
- source: StatewireStateSource;
54
+ source: Statewire.StateSource;
58
55
  attach: AttachState;
59
56
  attachEpoch: number;
60
57
  running: boolean;
@@ -67,22 +64,8 @@ type Session = {
67
64
  send: (command: StatewireWireCommand) => Promise<unknown>;
68
65
  commands: Record<string, (...params: unknown[]) => Promise<unknown>>;
69
66
  };
70
- declare const createSession: (options: SessionOptions) => Session;
71
- declare const handleSnapshot: (s: Session, { ops, lastSeq }: {
72
- ops: unknown[];
73
- lastSeq: number;
74
- }) => void;
75
- declare const handleOps: (s: Session, { ops, res, ack }: {
76
- ops: unknown[];
77
- res?: StatewireResponse[];
78
- ack?: number;
79
- }) => void;
80
- declare const handleFinish: (s: Session, { reason }: {
81
- reason: string;
82
- }) => void;
83
- declare const start: (s: Session) => void;
84
- declare const stop: (s: Session) => void;
85
67
  declare const setTransport: (s: Session, transport: RemoteLink) => void;
68
+ declare const useStatewireSession: (options: StatewireSessionStoreOptions, emit: (event: unknown) => void) => Session;
86
69
  //#endregion
87
- export { SessionOptions, createSession, handleFinish, handleOps, handleSnapshot, setTransport, start, stop };
70
+ export { setTransport, useStatewireSession };
88
71
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;;KAoBY;EACV,eAAe;;;;;;;;KASZ;EACC;EAAc;;EACd;EAAc;EAAwB;;EACtC;EAAc;EAAwB;;EACtC;EAAuB;EAAiB;;EACxC;EAAoB;EAAiB;;EACrC;;KAED;WACM;WACA,SAAS;WACT,KAAK;EACd,OAAO;EACP,UAAU;EACV,SAAS,OAAO;;KAyFb;KAEA;EACH,QAAQ;EACR;EACA,OAAO;EACP;EACA,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ;EACR,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX;EACA,YAAY;EACZ,OAAO,SAAS,yBAAyB;EACzC,UAAU,mBAAmB,sBAAsB;;cAoB/C,gBAAiB,SAAS,mBAAiB;cA0N3C,iBACJ,GAAG,WACH,KAAA;EAAoB;EAAgB;;cAuChC,YACJ,GAAG,WACH,KAAA,KAAA;EAIK;EAAgB,MAAM;EAAqB;;cAoB5C,eAAgB,GAAG,WAAS;EAAc;;cAuJ1C,QAAS,GAAG;cASZ,OAAQ,GAAG;cAMX,eAAgB,GAAG,SAAS,WAAW"}
1
+ {"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;;;;;;;KA+BK;EACC;EAAc;;EACd;EAAc;EAAwB;;EACtC;EAAc;EAAwB;;EACtC;EAAuB;EAAiB;;EACxC;EAAoB;EAAiB;;EACrC;;KAED;WACM;WACA,SAAS;WACT,KAAK;EACd,OAAO;EACP,UAAU;EACV,SAAS,OAAO;;KAmGb;KAEA;EACH,QAAQ;EACR,KAAK;EACL;EACA,OAAO;EACP;EACA,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ,UAAU;EAClB,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX;EACA,YAAY;EACZ,OAAO,SAAS,yBAAyB;EACzC,UAAU,mBAAmB,sBAAsB;;cAkd/C,eAAgB,GAAG,SAAS,WAAW;cA4ChC,sBACX,SAAS,8BACT,OAAO,4BACN"}
@@ -1,11 +1,18 @@
1
1
  import { applyOps } from "../wire/ops.js";
2
+ import { createMethodProxy } from "../util/methodProxy.js";
2
3
  import { StatewireSendError } from "../types.js";
3
4
  import { bindCommandsHost } from "../host/host.js";
4
- import { backoff } from "./timing.js";
5
+ import { randomId } from "../util/randomId.js";
6
+ import { retryDelay } from "./timing.js";
5
7
  import { createMemorySessionStore } from "./sessionStore.js";
8
+ import { useEffect, useState } from "@assistant-ui/tap/react-shim";
6
9
  //#region src/client/session.ts
7
10
  const isResolved = (state) => state.at === "settled-live" || state.at === "done";
8
11
  const isStateSettled = (state) => state.at === "awaiting-result" || state.at === "done";
12
+ const inFlight = (state) => {
13
+ if (state.at === "queued" || state.at === "done") throw new Error(`statewire: ${state.at} entry observed after a send`);
14
+ return state;
15
+ };
9
16
  const legalTransitions = {
10
17
  queued: [
11
18
  "queued",
@@ -66,27 +73,12 @@ const carried = (state) => state.at === "queued" ? {
66
73
  posted: state.posted,
67
74
  failedAttempts: state.failedAttempts
68
75
  };
69
- const newClientId = () => `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
76
+ const newClientId = () => `c-${randomId()}`;
70
77
  const rejectedSend = (failure) => {
71
78
  const result = Promise.reject(new StatewireSendError(failure));
72
79
  result.catch(() => {});
73
80
  return result;
74
81
  };
75
- const createCommandsProxy = (send) => {
76
- const memo = /* @__PURE__ */ new Map();
77
- return new Proxy({}, { get: (_target, method) => {
78
- if (typeof method !== "string") return void 0;
79
- let fn = memo.get(method);
80
- if (fn === void 0) {
81
- fn = (...params) => send({
82
- method,
83
- params
84
- });
85
- memo.set(method, fn);
86
- }
87
- return fn;
88
- } });
89
- };
90
82
  const createSession = (options) => {
91
83
  const store = options.sessionStore ?? createMemorySessionStore();
92
84
  const stored = store.load();
@@ -95,6 +87,14 @@ const createSession = (options) => {
95
87
  clientId: newClientId(),
96
88
  nextSeq: 1
97
89
  },
90
+ ctx: {
91
+ get clientId() {
92
+ return s.record.clientId;
93
+ },
94
+ onSnapshot: (args) => handleSnapshot(s, args),
95
+ onOps: (args) => handleOps(s, args),
96
+ onFinish: (args) => handleFinish(s, args)
97
+ },
98
98
  recordPersisted: stored !== null,
99
99
  store,
100
100
  lastAck: -1,
@@ -103,7 +103,7 @@ const createSession = (options) => {
103
103
  lastPublished: void 0,
104
104
  changed: [],
105
105
  source: {
106
- root: () => s.canonical,
106
+ snapshot: () => s.canonical,
107
107
  takeChanged: () => {
108
108
  const changed = s.changed;
109
109
  s.changed = [];
@@ -123,7 +123,10 @@ const createSession = (options) => {
123
123
  notify: () => {},
124
124
  emitEvent: () => {},
125
125
  send: (command) => sendCommand(s, command),
126
- commands: createCommandsProxy((command) => s.send(command))
126
+ commands: createMethodProxy((method, params) => s.send({
127
+ method,
128
+ params
129
+ }))
127
130
  };
128
131
  bindCommandsHost(s.commands, { setListener: (listener) => {
129
132
  s.lifecycle = listener;
@@ -141,6 +144,17 @@ const wake = (s) => {
141
144
  for (const waiter of waiters) waiter();
142
145
  };
143
146
  const waitForWake = (s) => new Promise((r) => s.waiters.push(r));
147
+ const sleepUntilWake = (s, delayMs) => new Promise((resolve) => {
148
+ const waiter = () => {
149
+ clearTimeout(timer);
150
+ resolve();
151
+ };
152
+ const timer = setTimeout(() => {
153
+ s.waiters = s.waiters.filter((w) => w !== waiter);
154
+ resolve();
155
+ }, delayMs);
156
+ s.waiters.push(waiter);
157
+ });
144
158
  const settleAll = (s, detach, verdict) => {
145
159
  const cause = {
146
160
  evicted: "session-lost",
@@ -238,11 +252,11 @@ const applyResponse = (s, response) => {
238
252
  } : response.code === "invalid-params" ? {
239
253
  fate: "invalid-params",
240
254
  cause: "server",
241
- message: response.message ?? ""
255
+ message: response.message ?? protocolError("rejected response missing message")
242
256
  } : {
243
257
  fate: "rejected",
244
258
  cause: "server",
245
- message: response.message ?? "",
259
+ message: response.message ?? protocolError("rejected response missing message"),
246
260
  ...response.payload !== void 0 && { payload: response.payload }
247
261
  }
248
262
  });
@@ -271,7 +285,7 @@ const ingestOps = (s, ops) => {
271
285
  };
272
286
  const publishIngested = (s, events) => {
273
287
  publish(s);
274
- for (const { event } of events) s.emitEvent(event);
288
+ for (const event of events) s.emitEvent(event);
275
289
  wake(s);
276
290
  };
277
291
  const handleSnapshot = (s, { ops, lastSeq }) => {
@@ -327,7 +341,7 @@ const handleOps = (s, { ops, res, ack }) => {
327
341
  publishIngested(s, events);
328
342
  };
329
343
  const handleFinish = (s, { reason }) => {
330
- if (reason === "ended" || reason === "error") return;
344
+ if (reason === "evicted" || reason === "error") return;
331
345
  s.attach = "finished";
332
346
  settleAll(s, reason === "superseded" ? "superseded" : "gone");
333
347
  publish(s);
@@ -363,15 +377,12 @@ const pump = async (s) => {
363
377
  } catch (error) {
364
378
  if (!s.running || isFinished(s)) return;
365
379
  if (!s.pending.includes(entry)) continue;
366
- const state = entry.state;
367
- if (state.at === "queued" || state.at === "done") continue;
380
+ const state = inFlight(entry.state);
368
381
  transition(s, entry, {
369
382
  ...state,
370
383
  failedAttempts: state.failedAttempts + 1
371
384
  });
372
- const { retryAfterMs } = error;
373
- const delay = typeof retryAfterMs === "number" ? retryAfterMs : backoff(state.failedAttempts + 1);
374
- await new Promise((r) => setTimeout(r, delay));
385
+ await sleepUntilWake(s, retryDelay(error, state.failedAttempts + 1));
375
386
  continue;
376
387
  }
377
388
  if (!s.running) return;
@@ -394,14 +405,12 @@ const pump = async (s) => {
394
405
  publish(s);
395
406
  }
396
407
  } else if (result.type === "rejected") {
397
- const { message, payload } = result;
398
408
  transition(s, entry, { at: "done" }, {
399
409
  ok: false,
400
410
  failure: {
401
411
  fate: "rejected",
402
412
  cause: "server",
403
- message,
404
- ...payload !== void 0 && { payload }
413
+ message: result.message
405
414
  }
406
415
  });
407
416
  publish(s);
@@ -420,13 +429,10 @@ const pump = async (s) => {
420
429
  } else if (result.type === "seq-gap") {
421
430
  if (clientId === s.record.clientId) {
422
431
  console.error(`statewire: server reports a seq gap for seq ${entry.seq}; treating the session as corrupt and rotating the client id`);
423
- const state = entry.state;
432
+ const state = inFlight(entry.state);
424
433
  const failure = state.at === "awaiting-result" ? {
425
434
  fate: "result-unavailable",
426
435
  cause: "session-lost"
427
- } : state.at === "queued" || state.at === "done" ? {
428
- fate: "aborted",
429
- cause: "session-lost"
430
436
  } : {
431
437
  fate: state.failedAttempts > 0 ? "lost" : "aborted",
432
438
  cause: "session-lost"
@@ -503,7 +509,21 @@ const sendCommand = (s, command) => {
503
509
  pump(s);
504
510
  return result;
505
511
  };
512
+ const useStatewireSession = (options, emit) => {
513
+ const [s] = useState(() => createSession(options));
514
+ useEffect(() => {
515
+ s.emitEvent = emit;
516
+ return () => {
517
+ s.emitEvent = () => {};
518
+ };
519
+ }, [s, emit]);
520
+ useEffect(() => {
521
+ start(s);
522
+ return () => stop(s);
523
+ }, [s]);
524
+ return s;
525
+ };
506
526
  //#endregion
507
- export { createSession, handleFinish, handleOps, handleSnapshot, setTransport, start, stop };
527
+ export { setTransport, useStatewireSession };
508
528
 
509
529
  //# sourceMappingURL=session.js.map