statewire 0.0.1 → 0.0.3

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 +12 -26
  15. package/dist/client/session.d.ts.map +1 -1
  16. package/dist/client/session.js +70 -43
  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 +19 -35
  33. package/dist/host/host.d.ts.map +1 -1
  34. package/dist/host/host.js +86 -54
  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 +511 -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 +54 -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 +97 -10
  49. package/dist/statewire.d.ts.map +1 -1
  50. package/dist/statewire.js +32 -36
  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 +7 -25
  63. package/dist/transports/remote.js.map +1 -1
  64. package/dist/transports/sse.d.ts +32 -20
  65. package/dist/transports/sse.d.ts.map +1 -1
  66. package/dist/transports/sse.js +57 -46
  67. package/dist/transports/sse.js.map +1 -1
  68. package/dist/transports/testing.d.ts +8 -6
  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 +59 -63
  75. package/dist/transports/ws.js.map +1 -1
  76. package/dist/types.d.ts +55 -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 +129 -90
  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 +122 -110
  121. package/src/host/socket.ts +618 -0
  122. package/src/host-internal.ts +28 -0
  123. package/src/host.ts +72 -0
  124. package/src/index.ts +2 -62
  125. package/src/statewire.ts +168 -61
  126. package/src/transports/fake-sse.ts +51 -0
  127. package/src/transports/fixtures.ts +60 -0
  128. package/src/transports/remote.ts +14 -51
  129. package/src/transports/sse.ts +87 -89
  130. package/src/transports/testing.ts +33 -63
  131. package/src/transports/ws.ts +78 -100
  132. package/src/types.ts +52 -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
package/dist/host/host.js CHANGED
@@ -1,32 +1,43 @@
1
- import { applyOp, applyOps } from "../wire/ops.js";
1
+ import { applyOps, sanitizeValue } from "../wire/ops.js";
2
2
  import { StatewireReject } from "./engine.js";
3
- import { createStateDraft, frozen } from "./draft.js";
3
+ import { createStateDraft } from "./draft.js";
4
+ import { createMethodProxy } from "../util/methodProxy.js";
4
5
  import { StatewireSendError } from "../types.js";
5
6
  import { useEffect, useState } from "@assistant-ui/tap/react-shim";
6
7
  //#region src/host/host.ts
7
8
  const isDev = () => typeof process === "undefined" || process.env["NODE_ENV"] !== "production";
8
9
  const stateHosts = /* @__PURE__ */ new WeakMap();
10
+ /** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
9
11
  const getStateHost = (state) => stateHosts.get(state);
10
12
  const useStatewireState = (init) => {
11
13
  const [, setVersion] = useState(0);
12
14
  const [cell] = useState(() => {
15
+ const commit = () => {
16
+ if (c.draft) c.current = c.draft.flush();
17
+ return c.current;
18
+ };
13
19
  const c = {
14
- current: frozen(init()),
20
+ current: sanitizeValue(init()),
15
21
  draft: null,
16
22
  changed: [[]],
17
23
  onOp: null,
18
24
  onChange: null,
19
25
  bump: () => setVersion((v) => v + 1),
20
- ctl: { apply: (ops) => {
21
- const result = applyOps(c.current, ops);
22
- if (result.events.length > 0) throw new Error("statewire: event ops do not apply to state — route them to emit");
23
- c.current = result.state;
24
- c.changed.push(...result.changedPaths);
25
- c.bump();
26
- c.onChange?.();
27
- } },
26
+ ctl: {
27
+ apply: (ops) => {
28
+ const result = applyOps(commit(), ops);
29
+ if (result.events.length > 0) throw new Error("statewire: event ops do not apply to state — route them to emit");
30
+ c.current = result.state;
31
+ c.draft?.invalidate();
32
+ c.changed.push(...result.changedPaths);
33
+ if (c.onOp) for (const op of ops) c.onOp(op);
34
+ c.bump();
35
+ c.onChange?.();
36
+ },
37
+ snapshot: commit
38
+ },
28
39
  host: {
29
- root: () => c.current,
40
+ snapshot: commit,
30
41
  setOnOp: (listener) => {
31
42
  c.onOp = listener;
32
43
  },
@@ -42,21 +53,19 @@ const useStatewireState = (init) => {
42
53
  };
43
54
  return c;
44
55
  });
45
- let value = cell.current;
56
+ let value = cell.ctl.snapshot();
46
57
  if (typeof value === "object" && value !== null) {
47
58
  cell.draft ??= createStateDraft({
48
- root: () => cell.current,
49
- record: (op) => {
50
- const applied = applyOp(cell.current, op);
51
- cell.current = applied.state;
52
- cell.changed.push(...applied.changedPaths);
59
+ snapshot: () => cell.current,
60
+ record: (op, changedPath) => {
61
+ cell.changed.push(changedPath);
53
62
  cell.onOp?.(op);
54
63
  cell.bump();
55
64
  cell.onChange?.();
56
65
  }
57
66
  });
58
- stateHosts.set(cell.draft, cell.host);
59
- value = cell.draft;
67
+ stateHosts.set(cell.draft.proxy, cell.host);
68
+ value = cell.draft.proxy;
60
69
  }
61
70
  return [value, cell.ctl];
62
71
  };
@@ -124,6 +133,10 @@ const bindCommandsHost = (commands, host) => {
124
133
  commandsHosts.set(commands, host);
125
134
  };
126
135
  const STALL_WARN_MS = 5e3;
136
+ const LOCAL_CALLER = Object.freeze({
137
+ context: null,
138
+ isConnected: true
139
+ });
127
140
  const toSendError = (error) => error instanceof StatewireSendError ? error : new StatewireSendError({
128
141
  fate: "rejected",
129
142
  cause: "server",
@@ -134,22 +147,26 @@ const ackInvocation = (cell, inv) => {
134
147
  if (inv.acked) return;
135
148
  inv.acked = true;
136
149
  cell.listener?.onAck(inv);
150
+ inv.origin?.onAck?.();
137
151
  };
138
152
  const settleInvocation = (cell, inv, call, settled) => {
139
153
  ackInvocation(cell, inv);
140
154
  if (settled.type === "resolved") {
141
155
  const outcome = settled.value === void 0 ? { type: "accepted" } : {
142
156
  type: "accepted",
143
- payload: frozen(settled.value)
157
+ payload: sanitizeValue(settled.value)
144
158
  };
145
159
  cell.listener?.onSettle(inv, outcome);
160
+ inv.origin?.onSettle?.(outcome);
146
161
  call.resolve(outcome.payload);
147
162
  } else {
148
163
  const error = toSendError(settled.error);
149
- cell.listener?.onSettle(inv, {
164
+ const outcome = {
150
165
  type: "rejected",
151
166
  error
152
- });
167
+ };
168
+ cell.listener?.onSettle(inv, outcome);
169
+ inv.origin?.onSettle?.(outcome);
153
170
  call.reject(error);
154
171
  }
155
172
  };
@@ -162,10 +179,15 @@ const pump = (cell) => {
162
179
  if (!call) return;
163
180
  const handler = cell.handlers[call.method];
164
181
  if (handler === void 0) {
165
- call.reject(new StatewireSendError({
182
+ const error = new StatewireSendError({
166
183
  fate: "unknown-command",
167
184
  cause: "server"
168
- }));
185
+ });
186
+ call.origin?.onSettle?.({
187
+ type: "rejected",
188
+ error
189
+ });
190
+ call.reject(error);
169
191
  pump(cell);
170
192
  return;
171
193
  }
@@ -174,7 +196,9 @@ const pump = (cell) => {
174
196
  params: call.params,
175
197
  ts: call.ts,
176
198
  seq: ++cell.seq,
177
- acked: false
199
+ acked: false,
200
+ ...call.origin !== void 0 && { origin: call.origin },
201
+ ...call.origin?.clientId !== void 0 && { clientId: call.origin.clientId }
178
202
  };
179
203
  cell.running = true;
180
204
  cell.listener?.onStart(inv);
@@ -182,7 +206,7 @@ const pump = (cell) => {
182
206
  cell.listener?.onCovered?.(inv);
183
207
  (async () => {
184
208
  try {
185
- const value = await handler(...call.params);
209
+ const value = await handler.call(call.origin?.caller ?? LOCAL_CALLER, ...call.params);
186
210
  const execution = typeof value === "object" && value !== null ? executionStates.get(value) : void 0;
187
211
  if (execution) {
188
212
  if (execution.acked) ackInvocation(cell, inv);
@@ -225,32 +249,40 @@ const useStatewireCommands = (commands) => {
225
249
  unmounted: false,
226
250
  stallTimer: null
227
251
  };
228
- const memo = /* @__PURE__ */ new Map();
229
- const proxy = new Proxy({}, { get: (_target, method) => {
230
- if (typeof method !== "string") return void 0;
231
- let fn = memo.get(method);
232
- if (fn === void 0) {
233
- fn = (...params) => new Promise((resolve, reject) => {
234
- if (cell.unmounted) {
235
- reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
236
- return;
237
- }
238
- cell.queue.push({
239
- method,
240
- params,
241
- ts: Date.now(),
242
- resolve,
243
- reject
244
- });
245
- pump(cell);
246
- });
247
- memo.set(method, fn);
252
+ const proxy = createMethodProxy((method, params) => new Promise((resolve, reject) => {
253
+ if (cell.unmounted) {
254
+ reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
255
+ return;
248
256
  }
249
- return fn;
250
- } });
251
- commandsHosts.set(proxy, { setListener: (listener) => {
252
- cell.listener = listener;
253
- } });
257
+ cell.queue.push({
258
+ method,
259
+ params,
260
+ ts: Date.now(),
261
+ resolve,
262
+ reject
263
+ });
264
+ pump(cell);
265
+ }));
266
+ commandsHosts.set(proxy, {
267
+ setListener: (listener) => {
268
+ cell.listener = listener;
269
+ },
270
+ submit: (origin) => new Promise((resolve_0, reject_0) => {
271
+ if (cell.unmounted) {
272
+ reject_0(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
273
+ return;
274
+ }
275
+ cell.queue.push({
276
+ method: origin.method,
277
+ params: origin.params,
278
+ ts: Date.now(),
279
+ resolve: resolve_0,
280
+ reject: reject_0,
281
+ origin
282
+ });
283
+ pump(cell);
284
+ })
285
+ });
254
286
  return {
255
287
  cell,
256
288
  proxy
@@ -281,7 +313,7 @@ const useStatewireCommands = (commands) => {
281
313
  return built.proxy;
282
314
  };
283
315
  const emitHosts = /* @__PURE__ */ new WeakMap();
284
- const getEmitHost = (emit) => emitHosts.get(emit);
316
+ const getEmitHost = (emit) => emit && emitHosts.get(emit);
285
317
  const useStatewireEmit = () => {
286
318
  const [built] = useState(_temp);
287
319
  return built;
@@ -290,7 +322,7 @@ function _temp() {
290
322
  const cell = { sink: null };
291
323
  const emit = (event) => {
292
324
  if (!cell.sink) throw new Error("statewire: emit before a host bound the event sink");
293
- cell.sink(frozen(event));
325
+ cell.sink(sanitizeValue(event));
294
326
  };
295
327
  emitHosts.set(emit, { setSink: (sink) => {
296
328
  cell.sink = sink;
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","names":["useEffect","useState","applyOp","applyOps","StatewireOp","StatewirePath","StatewireReject","createStateDraft","frozen","ServerOp","StatewireSendError","StatewireCommands","StatewireCommandsProxy","StatewireStateSource","isDev","process","env","StatewireStateCtl","apply","ops","StateHost","setOnOp","listener","op","stateHosts","WeakMap","getStateHost","state","get","StateCell","current","draft","changed","onOp","onChange","ctl","host","bump","useStatewireState","init","TState","setVersion","cell","c","v","result","events","length","Error","push","changedPaths","root","setOnChange","takeChanged","value","record","applied","set","const","ExecutionSettled","type","error","ExecutionBind","onAck","onSettle","settled","ExecutionState","acked","bind","executionStates","executionResult","CommandExecution","T","CommandExecutionResolvers","ack","resolve","reject","Object","assign","executor","resolvers","PromiseLike","execution","freeze","settle","withResolvers","promise","Promise","res","rej","a","CommandsInvocation","method","params","ts","seq","clientId","CommandsOutcome","payload","CommandsListener","onStart","inv","onPosted","onCovered","outcome","CommandsHost","setListener","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","call","pump","watchStall","shift","handler","console","warn","UnwrapExecution","V","StatewireHandledCommands","THandlers","Parameters","K","Awaited","useStatewireCommands","built","memo","Map","proxy","Proxy","_target","fn","Date","now","clearTimeout","queueMicrotask","queued","EmitHost","setSink","sink","event","emitHosts","getEmitHost","emit","useStatewireEmit","_temp"],"sources":["../../src/host/host.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOp, applyOps } from \"../wire/ops\";\nimport type { StatewireOp, StatewirePath } from \"../wire/ops\";\nimport { StatewireReject } from \"./engine\";\nimport { createStateDraft, frozen } from \"./draft\";\nimport type { ServerOp } from \"./draft\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n StatewireCommands,\n StatewireCommandsProxy,\n StatewireStateSource,\n} from \"../types\";\n\nconst isDev = () =>\n typeof process === \"undefined\" || process.env[\"NODE_ENV\"] !== \"production\";\n\n// ─── state ──────────────────────────────────────────────\n\nexport type StatewireStateCtl = {\n /** Apply server-vocabulary ops (root replace = whole-state swap). */\n apply: (ops: readonly StatewireOp[]) => void;\n};\n\nexport type StateHost = StatewireStateSource & {\n /** Synchronous, per locally-recorded op; the host owns batching. */\n setOnOp: (listener: ((op: ServerOp) => void) | null) => void;\n};\n\nconst stateHosts = new WeakMap<object, StateHost>();\n\nexport const getStateHost = (state: object): StateHost | undefined =>\n stateHosts.get(state);\n\ntype StateCell = {\n current: unknown;\n draft: object | null;\n changed: StatewirePath[];\n onOp: ((op: ServerOp) => void) | null;\n onChange: (() => void) | null;\n ctl: StatewireStateCtl;\n host: StateHost;\n bump: () => void;\n};\n\nexport const useStatewireState = <TState>(\n init: () => TState,\n): readonly [TState, StatewireStateCtl] => {\n const [, setVersion] = useState(0);\n const [cell] = useState((): StateCell => {\n const c: StateCell = {\n current: frozen(init()),\n draft: null,\n changed: [[]],\n onOp: null,\n onChange: null,\n bump: () => setVersion((v) => v + 1),\n ctl: {\n apply: (ops) => {\n const result = applyOps(c.current, ops);\n if (result.events.length > 0) {\n throw new Error(\n \"statewire: event ops do not apply to state — route them to emit\",\n );\n }\n c.current = result.state;\n c.changed.push(...result.changedPaths);\n c.bump();\n c.onChange?.();\n },\n },\n host: {\n root: () => c.current,\n setOnOp: (listener) => {\n c.onOp = listener;\n },\n setOnChange: (listener) => {\n c.onChange = listener;\n },\n takeChanged: () => {\n const changed = c.changed;\n c.changed = [];\n return changed;\n },\n },\n };\n return c;\n });\n\n let value = cell.current;\n if (typeof value === \"object\" && value !== null) {\n cell.draft ??= createStateDraft<object>({\n root: () => cell.current,\n record: (op) => {\n const applied = applyOp(cell.current, op);\n cell.current = applied.state;\n cell.changed.push(...applied.changedPaths);\n cell.onOp?.(op);\n cell.bump();\n cell.onChange?.();\n },\n });\n stateHosts.set(cell.draft, cell.host);\n value = cell.draft;\n }\n return [value as TState, cell.ctl] as const;\n};\n\n// ─── commands ───────────────────────────────────────────\n\ntype ExecutionSettled =\n | { type: \"resolved\"; value: unknown }\n | { type: \"rejected\"; error: unknown };\n\ntype ExecutionBind = {\n onAck: () => void;\n onSettle: (settled: ExecutionSettled) => void;\n};\n\ntype ExecutionState = {\n acked: boolean;\n settled: ExecutionSettled | null;\n bind: ExecutionBind | null;\n};\n\nconst executionStates = new WeakMap<object, ExecutionState>();\n\ndeclare const executionResult: unique symbol;\n\n/**\n * Handle a command handler returns to keep the command open past its own\n * return: `ack()` releases the client's optimistic overlay / ack watermark,\n * `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent\n * and a no-op after settle.\n */\nexport type CommandExecution<T = void> = {\n readonly [executionResult]: (value: T) => void;\n};\n\nexport type CommandExecutionResolvers<T = void> = {\n ack: () => void;\n resolve: (value: T) => void;\n reject: (error: unknown) => void;\n};\n\n/**\n * `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`\n * immediately: its return value resolves the execution, a throw/rejection\n * rejects it; `ack()` releases the client's ack watermark before the executor\n * finishes. `CommandExecution.withResolvers()` returns `[execution,\n * { ack, resolve, reject }]` instead — settle them from your own spawned work.\n */\nexport const CommandExecution = Object.assign(\n <T = void>(\n executor: (resolvers: {\n ack: () => void;\n }) => T | void | PromiseLike<T | void>,\n ): CommandExecution<T> => {\n const state: ExecutionState = { acked: false, settled: null, bind: null };\n const execution = Object.freeze({}) as unknown as CommandExecution<T>;\n executionStates.set(execution, state);\n const settle = (settled: ExecutionSettled) => {\n if (state.settled) return;\n state.settled = settled;\n state.acked = true;\n state.bind?.onSettle(settled);\n };\n const ack = () => {\n if (state.acked) return;\n state.acked = true;\n state.bind?.onAck();\n };\n void (async () => {\n try {\n settle({ type: \"resolved\", value: await executor({ ack }) });\n } catch (error) {\n settle({ type: \"rejected\", error });\n }\n })();\n return execution;\n },\n {\n withResolvers: <T = void>(): [\n CommandExecution<T>,\n CommandExecutionResolvers<T>,\n ] => {\n let resolve!: (value: T) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n let ack!: () => void;\n const execution = CommandExecution<T>(({ ack: a }) => {\n ack = a;\n return promise;\n });\n return [execution, { ack: () => ack(), resolve, reject }];\n },\n },\n);\n\nexport type CommandsInvocation = {\n readonly method: string;\n readonly params: readonly unknown[];\n readonly ts: number;\n readonly seq: number;\n readonly clientId?: string;\n};\n\nexport type CommandsOutcome =\n | { type: \"accepted\"; payload?: unknown }\n | { type: \"rejected\"; error: unknown };\n\nexport type CommandsListener = {\n onStart: (inv: CommandsInvocation) => void;\n onPosted?: (inv: CommandsInvocation) => void;\n onAck: (inv: CommandsInvocation) => void;\n /** Canonical state now covers the invocation's effect — drop any overlay. */\n onCovered?: (inv: CommandsInvocation) => void;\n onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsHost = {\n setListener: (listener: CommandsListener | null) => void;\n};\n\nconst commandsHosts = new WeakMap<object, CommandsHost>();\n\nexport const getCommandsHost = (commands: object): CommandsHost | undefined =>\n commandsHosts.get(commands);\n\nexport const bindCommandsHost = (commands: object, host: CommandsHost) => {\n commandsHosts.set(commands, host);\n};\n\ntype QueuedCall = {\n method: string;\n params: readonly unknown[];\n ts: number;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n};\n\ntype ActiveInvocation = CommandsInvocation & { acked: boolean };\n\ntype CommandsCell = {\n handlers: Record<string, (...args: never[]) => unknown> | null;\n queue: QueuedCall[];\n running: boolean;\n listener: CommandsListener | null;\n seq: number;\n unmounted: boolean;\n stallTimer: ReturnType<typeof setTimeout> | null;\n};\n\nconst STALL_WARN_MS = 5000;\n\nconst toSendError = (error: unknown): StatewireSendError =>\n error instanceof StatewireSendError\n ? error\n : new StatewireSendError({\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof StatewireReject &&\n error.payload !== undefined && { payload: error.payload }),\n });\n\nconst ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {\n if (inv.acked) return;\n inv.acked = true;\n cell.listener?.onAck(inv);\n};\n\nconst settleInvocation = (\n cell: CommandsCell,\n inv: ActiveInvocation,\n call: QueuedCall,\n settled: ExecutionSettled,\n) => {\n ackInvocation(cell, inv);\n if (settled.type === \"resolved\") {\n const outcome: CommandsOutcome =\n settled.value === undefined\n ? { type: \"accepted\" }\n : { type: \"accepted\", payload: frozen(settled.value) };\n cell.listener?.onSettle(inv, outcome);\n call.resolve(outcome.payload);\n } else {\n const error = toSendError(settled.error);\n cell.listener?.onSettle(inv, { type: \"rejected\", error });\n call.reject(error);\n }\n};\n\nconst pump = (cell: CommandsCell) => {\n if (cell.running || !cell.handlers) {\n watchStall(cell);\n return;\n }\n const call = cell.queue.shift();\n if (!call) return;\n const handler = cell.handlers[call.method];\n if (handler === undefined) {\n call.reject(\n new StatewireSendError({ fate: \"unknown-command\", cause: \"server\" }),\n );\n pump(cell);\n return;\n }\n const inv: ActiveInvocation = {\n method: call.method,\n params: call.params,\n ts: call.ts,\n seq: ++cell.seq,\n acked: false,\n };\n cell.running = true;\n cell.listener?.onStart(inv);\n cell.listener?.onPosted?.(inv);\n cell.listener?.onCovered?.(inv);\n void (async () => {\n try {\n const value = await (handler as (...args: readonly unknown[]) => unknown)(\n ...call.params,\n );\n const execution =\n typeof value === \"object\" && value !== null\n ? executionStates.get(value)\n : undefined;\n if (execution) {\n if (execution.acked) ackInvocation(cell, inv);\n if (execution.settled) {\n settleInvocation(cell, inv, call, execution.settled);\n } else {\n execution.bind = {\n onAck: () => ackInvocation(cell, inv),\n onSettle: (settled) => settleInvocation(cell, inv, call, settled),\n };\n }\n } else {\n settleInvocation(cell, inv, call, { type: \"resolved\", value });\n }\n } catch (error) {\n settleInvocation(cell, inv, call, { type: \"rejected\", error });\n } finally {\n cell.running = false;\n pump(cell);\n }\n })();\n};\n\nconst watchStall = (cell: CommandsCell) => {\n if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;\n if (cell.handlers) return;\n cell.stallTimer = setTimeout(() => {\n cell.stallTimer = null;\n if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) {\n console.warn(\n `statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`,\n );\n }\n }, STALL_WARN_MS);\n};\n\ntype UnwrapExecution<V> = V extends CommandExecution<infer T> ? T : V;\n\nexport type StatewireHandledCommands<THandlers extends StatewireCommands> = {\n [K in keyof THandlers]: (\n ...args: Parameters<THandlers[K]>\n ) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>;\n};\n\nexport const useStatewireCommands = <THandlers extends StatewireCommands>(\n commands: THandlers | undefined | null | false,\n): StatewireCommandsProxy<StatewireHandledCommands<THandlers>> => {\n const [built] = useState(() => {\n const cell: CommandsCell = {\n handlers: null,\n queue: [],\n running: false,\n listener: null,\n seq: 0,\n unmounted: false,\n stallTimer: null,\n };\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n const proxy = new Proxy(\n {} as StatewireCommandsProxy<StatewireHandledCommands<THandlers>>,\n {\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 new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method,\n params,\n ts: Date.now(),\n resolve,\n reject,\n });\n pump(cell);\n });\n memo.set(method, fn);\n }\n return fn;\n },\n },\n );\n commandsHosts.set(proxy, {\n setListener: (listener) => {\n cell.listener = listener;\n },\n });\n return { cell, proxy };\n });\n\n useEffect(() => {\n const { cell } = built;\n cell.unmounted = false;\n return () => {\n cell.unmounted = true;\n cell.handlers = null;\n if (cell.stallTimer !== null) {\n clearTimeout(cell.stallTimer);\n cell.stallTimer = null;\n }\n queueMicrotask(() => {\n if (!cell.unmounted) return;\n const queued = cell.queue;\n cell.queue = [];\n for (const call of queued) {\n call.reject(new Error(\"statewire: unmounted\"));\n }\n });\n };\n }, [built]);\n\n useEffect(() => {\n built.cell.handlers = commands || null;\n pump(built.cell);\n });\n\n return built.proxy;\n};\n\n// ─── emit ───────────────────────────────────────────────\n\nexport type EmitHost = {\n setSink: (sink: ((event: unknown) => void) | null) => void;\n};\n\nconst emitHosts = new WeakMap<object, EmitHost>();\n\nexport const getEmitHost = (emit: object): EmitHost | undefined =>\n emitHosts.get(emit);\n\nexport const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {\n const [built] = useState(() => {\n const cell = { sink: null as ((event: unknown) => void) | null };\n const emit = (event: TEvent) => {\n if (!cell.sink) {\n throw new Error(\"statewire: emit before a host bound the event sink\");\n }\n cell.sink(frozen(event));\n };\n emitHosts.set(emit, {\n setSink: (sink) => {\n cell.sink = sink;\n },\n });\n return emit;\n });\n return built;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"host.js","names":["useEffect","useState","applyOps","sanitizeValue","StatewirePath","StatewireReject","createStateDraft","createMethodProxy","ServerOp","StateDraft","StatewireSendError","Statewire","StatewireHost","isDev","process","env","stateHosts","WeakMap","StateHost","getStateHost","state","get","StateCell","current","draft","changed","onOp","op","onChange","ctl","StateCtl","host","bump","useStatewireState","init","TState","setVersion","cell","commit","c","flush","v","apply","ops","result","events","length","Error","invalidate","push","changedPaths","snapshot","setOnOp","listener","setOnChange","takeChanged","value","record","changedPath","set","proxy","const","ExecutionSettled","type","error","ExecutionBind","onAck","onSettle","settled","ExecutionState","acked","bind","executionStates","CommandExecution","Object","assign","executor","resolvers","ack","T","PromiseLike","Execution","execution","freeze","settle","withResolvers","ExecutionResolvers","resolve","reject","promise","Promise","res","rej","a","CommandsInvocation","method","params","ts","seq","clientId","CommandsOutcome","payload","CommandsListener","onStart","inv","onPosted","onCovered","outcome","CommandsSubmission","caller","Caller","CommandsHost","setListener","submit","call","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","origin","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","LOCAL_CALLER","context","isConnected","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","pump","watchStall","shift","handler","console","warn","useStatewireCommands","Commands","THandlers","ThisType","CommandsProxy","HandledCommands","built","Date","now","clearTimeout","queueMicrotask","queued","EmitHost","setSink","sink","event","emitHosts","getEmitHost","emit","useStatewireEmit","_temp"],"sources":["../../src/host/host.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps, sanitizeValue } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireReject } from \"./engine\";\nimport { createStateDraft } from \"./draft\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { ServerOp, StateDraft } from \"./draft\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireHost } from \"../host\";\n\nconst isDev = () =>\n typeof process === \"undefined\" || process.env[\"NODE_ENV\"] !== \"production\";\n\n// ─── state ──────────────────────────────────────────────\n\nconst stateHosts = new WeakMap<object, StatewireHost.StateHost>();\n\n/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */\nexport const getStateHost = (\n state: object,\n): StatewireHost.StateHost | undefined => stateHosts.get(state);\n\ntype StateCell = {\n current: unknown;\n draft: StateDraft<object> | null;\n changed: StatewirePath[];\n onOp: ((op: ServerOp) => void) | null;\n onChange: (() => void) | null;\n ctl: StatewireHost.StateCtl;\n host: StatewireHost.StateHost;\n bump: () => void;\n};\n\nexport const useStatewireState = <TState>(\n init: () => TState,\n): readonly [TState, StatewireHost.StateCtl<TState>] => {\n const [, setVersion] = useState(0);\n const [cell] = useState((): StateCell => {\n const commit = () => {\n if (c.draft) c.current = c.draft.flush();\n return c.current;\n };\n const c: StateCell = {\n current: sanitizeValue(init()),\n draft: null,\n changed: [[]],\n onOp: null,\n onChange: null,\n bump: () => setVersion((v) => v + 1),\n ctl: {\n apply: (ops) => {\n const result = applyOps(commit(), ops);\n if (result.events.length > 0) {\n throw new Error(\n \"statewire: event ops do not apply to state — route them to emit\",\n );\n }\n c.current = result.state;\n c.draft?.invalidate();\n c.changed.push(...result.changedPaths);\n if (c.onOp) for (const op of ops) c.onOp(op as ServerOp);\n c.bump();\n c.onChange?.();\n },\n snapshot: commit,\n },\n host: {\n snapshot: commit,\n setOnOp: (listener) => {\n c.onOp = listener;\n },\n setOnChange: (listener) => {\n c.onChange = listener;\n },\n takeChanged: () => {\n const changed = c.changed;\n c.changed = [];\n return changed;\n },\n },\n };\n return c;\n });\n\n let value = cell.ctl.snapshot();\n if (typeof value === \"object\" && value !== null) {\n cell.draft ??= createStateDraft<object>({\n snapshot: () => cell.current,\n record: (op, changedPath) => {\n cell.changed.push(changedPath);\n cell.onOp?.(op);\n cell.bump();\n cell.onChange?.();\n },\n });\n stateHosts.set(cell.draft.proxy, cell.host);\n value = cell.draft.proxy;\n }\n return [value as TState, cell.ctl as StatewireHost.StateCtl<TState>] as const;\n};\n\n// ─── commands ───────────────────────────────────────────\n\ntype ExecutionSettled =\n | { type: \"resolved\"; value: unknown }\n | { type: \"rejected\"; error: unknown };\n\ntype ExecutionBind = {\n onAck: () => void;\n onSettle: (settled: ExecutionSettled) => void;\n};\n\ntype ExecutionState = {\n acked: boolean;\n settled: ExecutionSettled | null;\n bind: ExecutionBind | null;\n};\n\nconst executionStates = new WeakMap<object, ExecutionState>();\n\n/**\n * `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`\n * immediately: its return value resolves the execution, a throw/rejection\n * rejects it; `ack()` releases the client's ack watermark before the executor\n * finishes. `CommandExecution.withResolvers()` returns `[execution,\n * { ack, resolve, reject }]` instead — settle them from your own spawned work.\n */\nexport const CommandExecution = Object.assign(\n <T = void>(\n executor: (resolvers: {\n ack: () => void;\n }) => T | void | PromiseLike<T | void>,\n ): StatewireHost.Execution<T> => {\n const state: ExecutionState = { acked: false, settled: null, bind: null };\n const execution = Object.freeze(\n {},\n ) as unknown as StatewireHost.Execution<T>;\n executionStates.set(execution, state);\n const settle = (settled: ExecutionSettled) => {\n if (state.settled) return;\n state.settled = settled;\n state.acked = true;\n state.bind?.onSettle(settled);\n };\n const ack = () => {\n if (state.acked) return;\n state.acked = true;\n state.bind?.onAck();\n };\n void (async () => {\n try {\n settle({ type: \"resolved\", value: await executor({ ack }) });\n } catch (error) {\n settle({ type: \"rejected\", error });\n }\n })();\n return execution;\n },\n {\n withResolvers: <T = void>(): [\n StatewireHost.Execution<T>,\n StatewireHost.ExecutionResolvers<T>,\n ] => {\n let resolve!: (value: T) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n let ack!: () => void;\n const execution = CommandExecution<T>(({ ack: a }) => {\n ack = a;\n return promise;\n });\n return [execution, { ack: () => ack(), resolve, reject }];\n },\n },\n);\n\nexport type CommandsInvocation = {\n readonly method: string;\n readonly params: readonly unknown[];\n readonly ts: number;\n readonly seq: number;\n readonly clientId?: string;\n};\n\nexport type CommandsOutcome =\n | { type: \"accepted\"; payload?: unknown }\n | { type: \"rejected\"; error: unknown };\n\nexport type CommandsListener = {\n onStart: (inv: CommandsInvocation) => void;\n onPosted?: (inv: CommandsInvocation) => void;\n onAck: (inv: CommandsInvocation) => void;\n /** Canonical state now covers the invocation's effect — drop any overlay. */\n onCovered?: (inv: CommandsInvocation) => void;\n onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsSubmission = {\n method: string;\n params: readonly unknown[];\n clientId?: string;\n caller?: StatewireHost.Caller;\n onAck?: () => void;\n onSettle?: (outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsHost = {\n setListener: (listener: CommandsListener | null) => void;\n submit?: (call: CommandsSubmission) => Promise<unknown>;\n};\n\nconst commandsHosts = new WeakMap<object, CommandsHost>();\n\nexport const getCommandsHost = (commands: object): CommandsHost | undefined =>\n commandsHosts.get(commands);\n\nexport const bindCommandsHost = (commands: object, host: CommandsHost) => {\n commandsHosts.set(commands, host);\n};\n\ntype QueuedCall = {\n method: string;\n params: readonly unknown[];\n ts: number;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n origin?: CommandsSubmission;\n};\n\ntype ActiveInvocation = CommandsInvocation & {\n acked: boolean;\n origin?: CommandsSubmission;\n};\n\ntype CommandsCell = {\n handlers: Record<string, (...args: never[]) => unknown> | null;\n queue: QueuedCall[];\n running: boolean;\n listener: CommandsListener | null;\n seq: number;\n unmounted: boolean;\n stallTimer: ReturnType<typeof setTimeout> | null;\n};\n\nconst STALL_WARN_MS = 5000;\n\n// In-process dispatch has no wire client: never any ambient context, trivially attached.\nconst LOCAL_CALLER: StatewireHost.Caller = Object.freeze({\n context: null,\n isConnected: true,\n});\n\nconst toSendError = (error: unknown): StatewireSendError =>\n error instanceof StatewireSendError\n ? error\n : new StatewireSendError({\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof StatewireReject &&\n error.payload !== undefined && { payload: error.payload }),\n });\n\nconst ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {\n if (inv.acked) return;\n inv.acked = true;\n cell.listener?.onAck(inv);\n inv.origin?.onAck?.();\n};\n\nconst settleInvocation = (\n cell: CommandsCell,\n inv: ActiveInvocation,\n call: QueuedCall,\n settled: ExecutionSettled,\n) => {\n ackInvocation(cell, inv);\n if (settled.type === \"resolved\") {\n const outcome: CommandsOutcome =\n settled.value === undefined\n ? { type: \"accepted\" }\n : { type: \"accepted\", payload: sanitizeValue(settled.value) };\n cell.listener?.onSettle(inv, outcome);\n inv.origin?.onSettle?.(outcome);\n call.resolve(outcome.payload);\n } else {\n const error = toSendError(settled.error);\n const outcome: CommandsOutcome = { type: \"rejected\", error };\n cell.listener?.onSettle(inv, outcome);\n inv.origin?.onSettle?.(outcome);\n call.reject(error);\n }\n};\n\nconst pump = (cell: CommandsCell) => {\n if (cell.running || !cell.handlers) {\n watchStall(cell);\n return;\n }\n const call = cell.queue.shift();\n if (!call) return;\n const handler = cell.handlers[call.method];\n if (handler === undefined) {\n const error = new StatewireSendError({\n fate: \"unknown-command\",\n cause: \"server\",\n });\n call.origin?.onSettle?.({ type: \"rejected\", error });\n call.reject(error);\n pump(cell);\n return;\n }\n const inv: ActiveInvocation = {\n method: call.method,\n params: call.params,\n ts: call.ts,\n seq: ++cell.seq,\n acked: false,\n ...(call.origin !== undefined && { origin: call.origin }),\n ...(call.origin?.clientId !== undefined && {\n clientId: call.origin.clientId,\n }),\n };\n cell.running = true;\n cell.listener?.onStart(inv);\n cell.listener?.onPosted?.(inv);\n cell.listener?.onCovered?.(inv);\n void (async () => {\n try {\n const value = await (\n handler as (...args: readonly unknown[]) => unknown\n ).call(call.origin?.caller ?? LOCAL_CALLER, ...call.params);\n const execution =\n typeof value === \"object\" && value !== null\n ? executionStates.get(value)\n : undefined;\n if (execution) {\n if (execution.acked) ackInvocation(cell, inv);\n if (execution.settled) {\n settleInvocation(cell, inv, call, execution.settled);\n } else {\n execution.bind = {\n onAck: () => ackInvocation(cell, inv),\n onSettle: (settled) => settleInvocation(cell, inv, call, settled),\n };\n }\n } else {\n settleInvocation(cell, inv, call, { type: \"resolved\", value });\n }\n } catch (error) {\n settleInvocation(cell, inv, call, { type: \"rejected\", error });\n } finally {\n cell.running = false;\n pump(cell);\n }\n })();\n};\n\nconst watchStall = (cell: CommandsCell) => {\n if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;\n if (cell.handlers) return;\n cell.stallTimer = setTimeout(() => {\n cell.stallTimer = null;\n if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) {\n console.warn(\n `statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`,\n );\n }\n }, STALL_WARN_MS);\n};\n\nexport const useStatewireCommands = <THandlers extends Statewire.Commands>(\n commands:\n | (THandlers & ThisType<StatewireHost.Caller>)\n | undefined\n | null\n | false,\n): Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>> => {\n const [built] = useState(() => {\n const cell: CommandsCell = {\n handlers: null,\n queue: [],\n running: false,\n listener: null,\n seq: 0,\n unmounted: false,\n stallTimer: null,\n };\n const proxy = createMethodProxy<\n Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>\n >(\n (method, params) =>\n new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method,\n params,\n ts: Date.now(),\n resolve,\n reject,\n });\n pump(cell);\n }),\n );\n commandsHosts.set(proxy, {\n setListener: (listener) => {\n cell.listener = listener;\n },\n submit: (origin) =>\n new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method: origin.method,\n params: origin.params,\n ts: Date.now(),\n resolve,\n reject,\n origin,\n });\n pump(cell);\n }),\n });\n return { cell, proxy };\n });\n\n useEffect(() => {\n const { cell } = built;\n cell.unmounted = false;\n return () => {\n cell.unmounted = true;\n cell.handlers = null;\n if (cell.stallTimer !== null) {\n clearTimeout(cell.stallTimer);\n cell.stallTimer = null;\n }\n queueMicrotask(() => {\n if (!cell.unmounted) return;\n const queued = cell.queue;\n cell.queue = [];\n for (const call of queued) {\n call.reject(new Error(\"statewire: unmounted\"));\n }\n });\n };\n }, [built]);\n\n useEffect(() => {\n built.cell.handlers = commands || null;\n pump(built.cell);\n });\n\n return built.proxy;\n};\n\n// ─── emit ───────────────────────────────────────────────\n\nexport type EmitHost = {\n setSink: (sink: ((event: unknown) => void) | null) => void;\n};\n\nconst emitHosts = new WeakMap<object, EmitHost>();\n\nexport const getEmitHost = (emit: object | undefined): EmitHost | undefined =>\n emit && emitHosts.get(emit);\n\nexport const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {\n const [built] = useState(() => {\n const cell = { sink: null as ((event: unknown) => void) | null };\n const emit = (event: TEvent) => {\n if (!cell.sink) {\n throw new Error(\"statewire: emit before a host bound the event sink\");\n }\n cell.sink(sanitizeValue(event));\n };\n emitHosts.set(emit, {\n setSink: (sink) => {\n cell.sink = sink;\n },\n });\n return emit;\n });\n return built;\n};\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Statewire } from "../types.js";
2
+ import { StatewireHostInternal } from "../host-internal.js";
3
+ import { ResourceElement } from "@assistant-ui/tap";
4
+ //#region src/host/socket.d.ts
5
+ /**
6
+ * Transport-agnostic WS binding over a statewire/host transport:
7
+ * feed it sockets (anything with `send`/`close`) and raw text frames, and it
8
+ * speaks the Statewire envelope protocol — snapshot-first attach with `syn`,
9
+ * delta/ack/res envelopes, per-client-id supersede, and `fin` on finish.
10
+ */
11
+ declare const StatewireSocketHost: (transport: ResourceElement<Statewire>, options?: StatewireHostInternal.SocketHostOptions) => StatewireHostInternal.SocketHost;
12
+ //#endregion
13
+ export { StatewireSocketHost };
14
+ //# sourceMappingURL=socket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":";;;;;;;;;;cAulBa,sBACX,WAAW,gBAAgB,YAC3B,UAAU,sBAAsB,sBAC/B,sBAAsB"}