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
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
  };
@@ -134,22 +143,26 @@ const ackInvocation = (cell, inv) => {
134
143
  if (inv.acked) return;
135
144
  inv.acked = true;
136
145
  cell.listener?.onAck(inv);
146
+ inv.origin?.onAck?.();
137
147
  };
138
148
  const settleInvocation = (cell, inv, call, settled) => {
139
149
  ackInvocation(cell, inv);
140
150
  if (settled.type === "resolved") {
141
151
  const outcome = settled.value === void 0 ? { type: "accepted" } : {
142
152
  type: "accepted",
143
- payload: frozen(settled.value)
153
+ payload: sanitizeValue(settled.value)
144
154
  };
145
155
  cell.listener?.onSettle(inv, outcome);
156
+ inv.origin?.onSettle?.(outcome);
146
157
  call.resolve(outcome.payload);
147
158
  } else {
148
159
  const error = toSendError(settled.error);
149
- cell.listener?.onSettle(inv, {
160
+ const outcome = {
150
161
  type: "rejected",
151
162
  error
152
- });
163
+ };
164
+ cell.listener?.onSettle(inv, outcome);
165
+ inv.origin?.onSettle?.(outcome);
153
166
  call.reject(error);
154
167
  }
155
168
  };
@@ -162,10 +175,15 @@ const pump = (cell) => {
162
175
  if (!call) return;
163
176
  const handler = cell.handlers[call.method];
164
177
  if (handler === void 0) {
165
- call.reject(new StatewireSendError({
178
+ const error = new StatewireSendError({
166
179
  fate: "unknown-command",
167
180
  cause: "server"
168
- }));
181
+ });
182
+ call.origin?.onSettle?.({
183
+ type: "rejected",
184
+ error
185
+ });
186
+ call.reject(error);
169
187
  pump(cell);
170
188
  return;
171
189
  }
@@ -174,7 +192,9 @@ const pump = (cell) => {
174
192
  params: call.params,
175
193
  ts: call.ts,
176
194
  seq: ++cell.seq,
177
- acked: false
195
+ acked: false,
196
+ ...call.origin !== void 0 && { origin: call.origin },
197
+ ...call.origin?.clientId !== void 0 && { clientId: call.origin.clientId }
178
198
  };
179
199
  cell.running = true;
180
200
  cell.listener?.onStart(inv);
@@ -225,32 +245,40 @@ const useStatewireCommands = (commands) => {
225
245
  unmounted: false,
226
246
  stallTimer: null
227
247
  };
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);
248
+ const proxy = createMethodProxy((method, params) => new Promise((resolve, reject) => {
249
+ if (cell.unmounted) {
250
+ reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
251
+ return;
248
252
  }
249
- return fn;
250
- } });
251
- commandsHosts.set(proxy, { setListener: (listener) => {
252
- cell.listener = listener;
253
- } });
253
+ cell.queue.push({
254
+ method,
255
+ params,
256
+ ts: Date.now(),
257
+ resolve,
258
+ reject
259
+ });
260
+ pump(cell);
261
+ }));
262
+ commandsHosts.set(proxy, {
263
+ setListener: (listener) => {
264
+ cell.listener = listener;
265
+ },
266
+ submit: (origin) => new Promise((resolve_0, reject_0) => {
267
+ if (cell.unmounted) {
268
+ reject_0(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
269
+ return;
270
+ }
271
+ cell.queue.push({
272
+ method: origin.method,
273
+ params: origin.params,
274
+ ts: Date.now(),
275
+ resolve: resolve_0,
276
+ reject: reject_0,
277
+ origin
278
+ });
279
+ pump(cell);
280
+ })
281
+ });
254
282
  return {
255
283
  cell,
256
284
  proxy
@@ -281,7 +309,7 @@ const useStatewireCommands = (commands) => {
281
309
  return built.proxy;
282
310
  };
283
311
  const emitHosts = /* @__PURE__ */ new WeakMap();
284
- const getEmitHost = (emit) => emitHosts.get(emit);
312
+ const getEmitHost = (emit) => emit && emitHosts.get(emit);
285
313
  const useStatewireEmit = () => {
286
314
  const [built] = useState(_temp);
287
315
  return built;
@@ -290,7 +318,7 @@ function _temp() {
290
318
  const cell = { sink: null };
291
319
  const emit = (event) => {
292
320
  if (!cell.sink) throw new Error("statewire: emit before a host bound the event sink");
293
- cell.sink(frozen(event));
321
+ cell.sink(sanitizeValue(event));
294
322
  };
295
323
  emitHosts.set(emit, { setSink: (sink) => {
296
324
  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","CommandsHost","setListener","submit","call","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","origin","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","pump","watchStall","shift","handler","console","warn","useStatewireCommands","Commands","THandlers","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 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\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 (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\nexport const useStatewireCommands = <THandlers extends Statewire.Commands>(\n commands: THandlers | undefined | null | 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":";;;;;;;;;;cA2gBa,sBACX,WAAW,gBAAgB,YAC3B,UAAU,sBAAsB,sBAC/B,sBAAsB"}