statewire 0.0.0-0 → 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 (154) hide show
  1. package/README.md +84 -1
  2. package/dist/client/changes.d.ts +7 -0
  3. package/dist/client/changes.d.ts.map +1 -0
  4. package/dist/client/changes.js +25 -0
  5. package/dist/client/changes.js.map +1 -0
  6. package/dist/client/connection.d.ts +63 -0
  7. package/dist/client/connection.d.ts.map +1 -0
  8. package/dist/client/connection.js +211 -0
  9. package/dist/client/connection.js.map +1 -0
  10. package/dist/client/core.d.ts +14 -0
  11. package/dist/client/core.d.ts.map +1 -0
  12. package/dist/client/core.js +272 -0
  13. package/dist/client/core.js.map +1 -0
  14. package/dist/client/session.d.ts +71 -0
  15. package/dist/client/session.d.ts.map +1 -0
  16. package/dist/client/session.js +529 -0
  17. package/dist/client/session.js.map +1 -0
  18. package/dist/client/sessionStore.d.ts +16 -0
  19. package/dist/client/sessionStore.d.ts.map +1 -0
  20. package/dist/client/sessionStore.js +14 -0
  21. package/dist/client/sessionStore.js.map +1 -0
  22. package/dist/client/timing.d.ts +11 -0
  23. package/dist/client/timing.d.ts.map +1 -0
  24. package/dist/client/timing.js +15 -0
  25. package/dist/client/timing.js.map +1 -0
  26. package/dist/host/adapter.d.ts +8 -0
  27. package/dist/host/adapter.d.ts.map +1 -0
  28. package/dist/host/adapter.js +23 -0
  29. package/dist/host/adapter.js.map +1 -0
  30. package/dist/host/draft.d.ts +28 -0
  31. package/dist/host/draft.d.ts.map +1 -0
  32. package/dist/host/draft.js +211 -0
  33. package/dist/host/draft.js.map +1 -0
  34. package/dist/host/engine.d.ts +14 -0
  35. package/dist/host/engine.d.ts.map +1 -0
  36. package/dist/host/engine.js +17 -0
  37. package/dist/host/engine.js.map +1 -0
  38. package/dist/host/host.d.ts +62 -0
  39. package/dist/host/host.d.ts.map +1 -0
  40. package/dist/host/host.js +331 -0
  41. package/dist/host/host.js.map +1 -0
  42. package/dist/host/socket.d.ts +14 -0
  43. package/dist/host/socket.d.ts.map +1 -0
  44. package/dist/host/socket.js +459 -0
  45. package/dist/host/socket.js.map +1 -0
  46. package/dist/host-internal.d.ts +27 -0
  47. package/dist/host-internal.d.ts.map +1 -0
  48. package/dist/host-internal.js +3 -0
  49. package/dist/host.d.ts +44 -0
  50. package/dist/host.d.ts.map +1 -0
  51. package/dist/host.js +4 -0
  52. package/dist/index.d.ts +5 -0
  53. package/dist/index.js +5 -0
  54. package/dist/statewire.d.ts +104 -0
  55. package/dist/statewire.d.ts.map +1 -0
  56. package/dist/statewire.js +53 -0
  57. package/dist/statewire.js.map +1 -0
  58. package/dist/transports/fake-sse.d.ts +12 -0
  59. package/dist/transports/fake-sse.d.ts.map +1 -0
  60. package/dist/transports/fake-sse.js +39 -0
  61. package/dist/transports/fake-sse.js.map +1 -0
  62. package/dist/transports/fixtures.d.ts +33 -0
  63. package/dist/transports/fixtures.d.ts.map +1 -0
  64. package/dist/transports/fixtures.js +50 -0
  65. package/dist/transports/fixtures.js.map +1 -0
  66. package/dist/transports/remote.d.ts +12 -0
  67. package/dist/transports/remote.d.ts.map +1 -0
  68. package/dist/transports/remote.js +21 -0
  69. package/dist/transports/remote.js.map +1 -0
  70. package/dist/transports/sse-parser.d.ts +5 -0
  71. package/dist/transports/sse-parser.d.ts.map +1 -0
  72. package/dist/transports/sse-parser.js +26 -0
  73. package/dist/transports/sse-parser.js.map +1 -0
  74. package/dist/transports/sse.d.ts +64 -0
  75. package/dist/transports/sse.d.ts.map +1 -0
  76. package/dist/transports/sse.js +137 -0
  77. package/dist/transports/sse.js.map +1 -0
  78. package/dist/transports/testing.d.ts +32 -0
  79. package/dist/transports/testing.d.ts.map +1 -0
  80. package/dist/transports/testing.js +94 -0
  81. package/dist/transports/testing.js.map +1 -0
  82. package/dist/transports/ws.d.ts +9 -0
  83. package/dist/transports/ws.d.ts.map +1 -0
  84. package/dist/transports/ws.js +169 -0
  85. package/dist/transports/ws.js.map +1 -0
  86. package/dist/types.d.ts +60 -0
  87. package/dist/types.d.ts.map +1 -0
  88. package/dist/types.js +24 -0
  89. package/dist/types.js.map +1 -0
  90. package/dist/util/draftTraps.d.ts +6 -0
  91. package/dist/util/draftTraps.d.ts.map +1 -0
  92. package/dist/util/draftTraps.js +25 -0
  93. package/dist/util/draftTraps.js.map +1 -0
  94. package/dist/util/macrotask.d.ts +5 -0
  95. package/dist/util/macrotask.d.ts.map +1 -0
  96. package/dist/util/macrotask.js +25 -0
  97. package/dist/util/macrotask.js.map +1 -0
  98. package/dist/util/methodProxy.d.ts +5 -0
  99. package/dist/util/methodProxy.d.ts.map +1 -0
  100. package/dist/util/methodProxy.js +17 -0
  101. package/dist/util/methodProxy.js.map +1 -0
  102. package/dist/util/randomId.d.ts +5 -0
  103. package/dist/util/randomId.d.ts.map +1 -0
  104. package/dist/util/randomId.js +6 -0
  105. package/dist/util/randomId.js.map +1 -0
  106. package/dist/util/recordMutations.d.ts +10 -0
  107. package/dist/util/recordMutations.d.ts.map +1 -0
  108. package/dist/util/recordMutations.js +109 -0
  109. package/dist/util/recordMutations.js.map +1 -0
  110. package/dist/util/statePath.d.ts +14 -0
  111. package/dist/util/statePath.d.ts.map +1 -0
  112. package/dist/util/statePath.js +31 -0
  113. package/dist/util/statePath.js.map +1 -0
  114. package/dist/wire/meta.d.ts +27 -0
  115. package/dist/wire/meta.d.ts.map +1 -0
  116. package/dist/wire/meta.js +75 -0
  117. package/dist/wire/meta.js.map +1 -0
  118. package/dist/wire/ops.d.ts +19 -0
  119. package/dist/wire/ops.d.ts.map +1 -0
  120. package/dist/wire/ops.js +112 -0
  121. package/dist/wire/ops.js.map +1 -0
  122. package/package.json +52 -5
  123. package/src/client/changes.ts +38 -0
  124. package/src/client/connection.ts +301 -0
  125. package/src/client/core.ts +312 -0
  126. package/src/client/session.ts +696 -0
  127. package/src/client/sessionStore.ts +23 -0
  128. package/src/client/timing.ts +14 -0
  129. package/src/host/adapter.ts +33 -0
  130. package/src/host/draft.ts +323 -0
  131. package/src/host/engine.ts +13 -0
  132. package/src/host/host.ts +482 -0
  133. package/src/host/socket.ts +542 -0
  134. package/src/host-internal.ts +28 -0
  135. package/src/host.ts +61 -0
  136. package/src/index.ts +5 -0
  137. package/src/statewire.ts +200 -0
  138. package/src/transports/fake-sse.ts +51 -0
  139. package/src/transports/fixtures.ts +60 -0
  140. package/src/transports/remote.ts +30 -0
  141. package/src/transports/sse-parser.ts +24 -0
  142. package/src/transports/sse.ts +267 -0
  143. package/src/transports/testing.ts +123 -0
  144. package/src/transports/ws.ts +256 -0
  145. package/src/types.ts +88 -0
  146. package/src/util/draftTraps.ts +32 -0
  147. package/src/util/macrotask.ts +25 -0
  148. package/src/util/methodProxy.ts +16 -0
  149. package/src/util/randomId.ts +4 -0
  150. package/src/util/recordMutations.ts +0 -0
  151. package/src/util/statePath.ts +61 -0
  152. package/src/wire/meta.ts +101 -0
  153. package/src/wire/ops.ts +202 -0
  154. package/index.js +0 -1
@@ -0,0 +1,459 @@
1
+ import { StatewireSendError } from "../types.js";
2
+ import { getCommandsHost, getEmitHost, getStateHost } from "./host.js";
3
+ import { encodeValue, reviveValue } from "../wire/meta.js";
4
+ import { createMacrotaskPoster } from "../util/macrotask.js";
5
+ import { randomId } from "../util/randomId.js";
6
+ import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
7
+ import { useEffect, useState } from "@assistant-ui/tap/react-shim";
8
+ import { createTapRoot, flushTapSync, useResource } from "@assistant-ui/tap";
9
+ //#region src/host/socket.ts
10
+ const CLIENT_ID_RE = /^[A-Za-z0-9._~-]{1,256}$/;
11
+ const RESULT_TTL_MS = 3e4;
12
+ const RESULT_LIMIT = 256;
13
+ const parseFrame = (raw) => {
14
+ let frame;
15
+ try {
16
+ frame = JSON.parse(raw);
17
+ } catch {
18
+ return null;
19
+ }
20
+ if (typeof frame !== "object" || frame === null || Array.isArray(frame)) return null;
21
+ const { method, seq, params, meta } = frame;
22
+ if (typeof method !== "string") return null;
23
+ if (typeof seq !== "number" || !Number.isInteger(seq) || seq < 1) return null;
24
+ if (!Array.isArray(params)) return null;
25
+ if (meta !== void 0) try {
26
+ reviveValue(params, meta);
27
+ } catch {
28
+ return null;
29
+ }
30
+ return {
31
+ method,
32
+ seq,
33
+ params
34
+ };
35
+ };
36
+ const encodePayload = (payload) => {
37
+ if (payload === void 0) return {};
38
+ const { value, meta } = encodeValue(payload);
39
+ return {
40
+ payload: value,
41
+ ...meta !== void 0 && { meta }
42
+ };
43
+ };
44
+ const responseOf = (adm, outcome) => {
45
+ if (outcome.type === "accepted") return {
46
+ seq: adm.seq,
47
+ type: "accepted",
48
+ ...encodePayload(outcome.payload)
49
+ };
50
+ const failure = outcome.error instanceof StatewireSendError ? outcome.error.failure : null;
51
+ switch (failure?.fate) {
52
+ case "unknown-command": return {
53
+ seq: adm.seq,
54
+ type: "rejected",
55
+ code: "unknown-command",
56
+ message: `unknown method: '${adm.method}'`
57
+ };
58
+ case "invalid-params": return {
59
+ seq: adm.seq,
60
+ type: "rejected",
61
+ code: "invalid-params",
62
+ message: failure.message
63
+ };
64
+ case "crashed": return {
65
+ seq: adm.seq,
66
+ type: "crashed"
67
+ };
68
+ case "rejected": return {
69
+ seq: adm.seq,
70
+ type: "rejected",
71
+ message: failure.message,
72
+ ...encodePayload(failure.payload)
73
+ };
74
+ default: return {
75
+ seq: adm.seq,
76
+ type: "rejected",
77
+ message: outcome.error instanceof Error ? outcome.error.message : String(outcome.error)
78
+ };
79
+ }
80
+ };
81
+ const createSocketHost = (options) => {
82
+ const heartbeatMs = options?.heartbeatIntervalMs ?? 15e3;
83
+ const clients = /* @__PURE__ */ new Map();
84
+ const conns = /* @__PURE__ */ new Set();
85
+ let pendingOps = [];
86
+ let dirtyAcks = /* @__PURE__ */ new Set();
87
+ let pendingRes = /* @__PURE__ */ new Map();
88
+ let inbox = Promise.resolve();
89
+ let finished = null;
90
+ let disposed = false;
91
+ let flushScheduled = false;
92
+ let heartbeatTimer = null;
93
+ let stateHost = null;
94
+ let commandsHost = null;
95
+ let emitHost = null;
96
+ let current = null;
97
+ const post = createMacrotaskPoster(() => {
98
+ flushScheduled = false;
99
+ flush();
100
+ });
101
+ const scheduleFlush = () => {
102
+ if (flushScheduled || disposed) return;
103
+ flushScheduled = true;
104
+ post();
105
+ };
106
+ const recordOp = (op) => {
107
+ if (op.op === "replace" || op.op === "add" || op.op === "event") {
108
+ const { value, meta } = encodeValue(op.value);
109
+ pendingOps.push({
110
+ ...op,
111
+ value,
112
+ ...meta !== void 0 && { meta }
113
+ });
114
+ } else pendingOps.push(op);
115
+ scheduleFlush();
116
+ };
117
+ const attach = (next) => {
118
+ if (next === current) return;
119
+ current = next;
120
+ const nextState = typeof next.state === "object" && next.state !== null ? getStateHost(next.state) : void 0;
121
+ const nextCommands = getCommandsHost(next.commands);
122
+ const nextEmit = getEmitHost(next.emit);
123
+ if (!nextState || !nextCommands?.submit || !nextEmit) throw new Error("StatewireSocketHost requires a statewire/host transport (useStatewireState with object state, useStatewireCommands, useStatewireEmit)");
124
+ if (nextState !== stateHost) {
125
+ stateHost?.setOnOp(null);
126
+ stateHost = nextState;
127
+ stateHost.setOnOp(recordOp);
128
+ }
129
+ commandsHost = nextCommands;
130
+ if (nextEmit !== emitHost) {
131
+ emitHost?.setSink(null);
132
+ emitHost = nextEmit;
133
+ emitHost.setSink((event) => recordOp({
134
+ op: "event",
135
+ value: event
136
+ }));
137
+ }
138
+ };
139
+ const closeConn = (conn, code, reason) => {
140
+ if (!conn.open) return;
141
+ conn.open = false;
142
+ conns.delete(conn);
143
+ try {
144
+ conn.socket.close(code, reason);
145
+ } catch {}
146
+ if (conns.size === 0 && heartbeatTimer !== null) {
147
+ clearInterval(heartbeatTimer);
148
+ heartbeatTimer = null;
149
+ }
150
+ };
151
+ const sendTo = (conn, data) => {
152
+ if (!conn.open) return;
153
+ conn.lastSentAt = Date.now();
154
+ try {
155
+ conn.socket.send(data);
156
+ } catch {
157
+ closeConn(conn);
158
+ }
159
+ };
160
+ const ensureHeartbeat = () => {
161
+ if (heartbeatTimer !== null || heartbeatMs <= 0) return;
162
+ heartbeatTimer = setInterval(() => {
163
+ const now = Date.now();
164
+ for (const conn of [...conns]) if (now - conn.lastSentAt >= heartbeatMs) sendTo(conn, "{}");
165
+ }, heartbeatMs);
166
+ heartbeatTimer.unref?.();
167
+ };
168
+ const flush = (fin) => {
169
+ flushTapSync(() => {});
170
+ stateHost?.takeChanged();
171
+ for (const rec of clients.values()) for (const adm of rec.inflight.values()) if (adm.acked && !adm.settled && !adm.pendingSent) {
172
+ adm.pendingSent = true;
173
+ queueRes(rec.id, {
174
+ seq: adm.seq,
175
+ type: "pending"
176
+ });
177
+ }
178
+ if (pendingOps.length === 0 && dirtyAcks.size === 0 && pendingRes.size === 0 && fin === void 0) return;
179
+ const ops = pendingOps;
180
+ const acks = dirtyAcks;
181
+ const res = pendingRes;
182
+ pendingOps = [];
183
+ dirtyAcks = /* @__PURE__ */ new Set();
184
+ pendingRes = /* @__PURE__ */ new Map();
185
+ for (const conn of [...conns]) {
186
+ const envelope = {};
187
+ if (ops.length > 0) envelope.ops = ops;
188
+ const clientRes = res.get(conn.clientId);
189
+ if (clientRes !== void 0) envelope.res = clientRes;
190
+ if (acks.has(conn.clientId)) {
191
+ const rec = clients.get(conn.clientId);
192
+ if (rec !== void 0) envelope.ack = rec.lastSeq;
193
+ }
194
+ if (fin !== void 0) envelope.fin = fin;
195
+ if (Object.keys(envelope).length === 0) continue;
196
+ sendTo(conn, JSON.stringify(envelope));
197
+ if (fin !== void 0) closeConn(conn);
198
+ }
199
+ };
200
+ const queueRes = (clientId, response) => {
201
+ const list = pendingRes.get(clientId);
202
+ if (list) list.push(response);
203
+ else pendingRes.set(clientId, [response]);
204
+ };
205
+ const advance = (rec, seq) => {
206
+ rec.lastSeq = seq;
207
+ dirtyAcks.add(rec.id);
208
+ };
209
+ const retain = (rec, response) => {
210
+ const now = Date.now();
211
+ rec.results.set(response.seq, {
212
+ ts: now,
213
+ res: response
214
+ });
215
+ const newest = Math.max(...rec.results.keys());
216
+ for (const [seq, entry] of rec.results) if (seq !== newest && now - entry.ts > RESULT_TTL_MS) rec.results.delete(seq);
217
+ while (rec.results.size > RESULT_LIMIT) rec.results.delete(Math.min(...rec.results.keys()));
218
+ };
219
+ const duplicateAnswer = (rec, seq) => {
220
+ const cached = rec.results.get(seq);
221
+ if (cached) {
222
+ if (seq === Math.max(...rec.results.keys()) || Date.now() - cached.ts <= RESULT_TTL_MS) return cached.res;
223
+ rec.results.delete(seq);
224
+ }
225
+ if (rec.inflight.has(seq)) return {
226
+ seq,
227
+ type: "pending"
228
+ };
229
+ return {
230
+ seq,
231
+ type: "result-unavailable"
232
+ };
233
+ };
234
+ const settleWire = (rec, adm, outcome) => {
235
+ if (adm.settled) return;
236
+ adm.settled = true;
237
+ rec.inflight.delete(adm.seq);
238
+ const response = responseOf(adm, outcome);
239
+ retain(rec, response);
240
+ if (!adm.acked) {
241
+ adm.acked = true;
242
+ advance(rec, adm.seq);
243
+ }
244
+ if (adm.pendingSent || response.type !== "accepted" || response.payload !== void 0) queueRes(rec.id, response);
245
+ scheduleFlush();
246
+ adm.release();
247
+ };
248
+ const admit = (conn, frame) => {
249
+ if (!conn.open || disposed || finished !== null) return;
250
+ const rec = clients.get(conn.clientId);
251
+ if (frame.seq <= rec.lastSeq || rec.inflight.has(frame.seq)) {
252
+ queueRes(rec.id, duplicateAnswer(rec, frame.seq));
253
+ scheduleFlush();
254
+ return;
255
+ }
256
+ if (frame.seq > rec.lastSeq + 1) {
257
+ closeConn(conn, 1008, `seq gap: expected ${rec.lastSeq + 1}, got ${frame.seq}`);
258
+ return;
259
+ }
260
+ const adm = {
261
+ seq: frame.seq,
262
+ method: frame.method,
263
+ acked: false,
264
+ pendingSent: false,
265
+ settled: false,
266
+ release: () => {}
267
+ };
268
+ const released = new Promise((resolve) => {
269
+ adm.release = resolve;
270
+ });
271
+ rec.inflight.set(frame.seq, adm);
272
+ commandsHost.submit({
273
+ method: frame.method,
274
+ params: frame.params,
275
+ clientId: rec.id,
276
+ onAck: () => {
277
+ if (adm.settled || adm.acked) return;
278
+ adm.acked = true;
279
+ advance(rec, adm.seq);
280
+ scheduleFlush();
281
+ adm.release();
282
+ },
283
+ onSettle: (outcome) => settleWire(rec, adm, outcome)
284
+ }).catch((error) => {
285
+ if (!adm.settled) settleWire(rec, adm, {
286
+ type: "rejected",
287
+ error
288
+ });
289
+ });
290
+ return released;
291
+ };
292
+ const message = (conn, data) => {
293
+ if (!conn.open || disposed || finished !== null) return;
294
+ const frame = parseFrame(data);
295
+ if (frame === null) {
296
+ closeConn(conn, 1008, "invalid frame");
297
+ return;
298
+ }
299
+ inbox = inbox.then(() => admit(conn, frame));
300
+ };
301
+ const connect = (clientId, socket) => {
302
+ const dead = {
303
+ clientId,
304
+ message: () => {},
305
+ close: () => {}
306
+ };
307
+ const refuse = (code, reason) => {
308
+ try {
309
+ socket.close(code, reason);
310
+ } catch {}
311
+ return dead;
312
+ };
313
+ if (disposed) return refuse(1001, "disposed");
314
+ if (!CLIENT_ID_RE.test(clientId)) return refuse(1008, "invalid client id");
315
+ flush();
316
+ if (finished !== null) {
317
+ try {
318
+ socket.send(JSON.stringify({ fin: finished }));
319
+ } catch {}
320
+ return refuse(1e3, "finished");
321
+ }
322
+ const known = clients.has(clientId);
323
+ const rec = known ? clients.get(clientId) : (() => {
324
+ const created = {
325
+ id: clientId,
326
+ lastSeq: 0,
327
+ results: /* @__PURE__ */ new Map(),
328
+ inflight: /* @__PURE__ */ new Map()
329
+ };
330
+ clients.set(clientId, created);
331
+ return created;
332
+ })();
333
+ for (const other of [...conns]) {
334
+ if (other.clientId !== clientId) continue;
335
+ sendTo(other, JSON.stringify({ fin: { reason: "superseded" } }));
336
+ closeConn(other);
337
+ }
338
+ const conn = {
339
+ clientId,
340
+ socket,
341
+ open: true,
342
+ lastSentAt: 0
343
+ };
344
+ conns.add(conn);
345
+ ensureHeartbeat();
346
+ const { value, meta } = encodeValue(stateHost.snapshot());
347
+ const snapshot = {
348
+ ops: [{
349
+ op: "replace",
350
+ path: [],
351
+ value,
352
+ ...meta !== void 0 && { meta }
353
+ }],
354
+ syn: {
355
+ lastSeq: known ? rec.lastSeq : -1,
356
+ lease: randomId()
357
+ }
358
+ };
359
+ sendTo(conn, JSON.stringify(snapshot));
360
+ return {
361
+ clientId,
362
+ message: (data) => message(conn, data),
363
+ close: () => closeConn(conn)
364
+ };
365
+ };
366
+ const finish = (fin) => {
367
+ if (finished !== null || disposed) return;
368
+ finished = fin;
369
+ flush(fin);
370
+ };
371
+ return {
372
+ connect,
373
+ finish,
374
+ attach,
375
+ resume: () => {
376
+ disposed = false;
377
+ stateHost?.setOnOp(recordOp);
378
+ emitHost?.setSink((event) => recordOp({
379
+ op: "event",
380
+ value: event
381
+ }));
382
+ },
383
+ suspend: () => {
384
+ disposed = true;
385
+ if (heartbeatTimer !== null) {
386
+ clearInterval(heartbeatTimer);
387
+ heartbeatTimer = null;
388
+ }
389
+ stateHost?.setOnOp(null);
390
+ emitHost?.setSink(null);
391
+ }
392
+ };
393
+ };
394
+ const useSocketHost = (transport, options) => {
395
+ const $ = c(8);
396
+ let t0;
397
+ if ($[0] !== options) {
398
+ t0 = () => createSocketHost(options);
399
+ $[0] = options;
400
+ $[1] = t0;
401
+ } else t0 = $[1];
402
+ const [h] = useState(t0);
403
+ const value = useResource(transport);
404
+ h.attach(value);
405
+ let t1;
406
+ if ($[2] !== h || $[3] !== value) {
407
+ t1 = () => {
408
+ h.attach(value);
409
+ };
410
+ $[2] = h;
411
+ $[3] = value;
412
+ $[4] = t1;
413
+ } else t1 = $[4];
414
+ useEffect(t1);
415
+ let t2;
416
+ let t3;
417
+ if ($[5] !== h) {
418
+ t2 = () => {
419
+ h.resume();
420
+ return () => h.suspend();
421
+ };
422
+ t3 = [h];
423
+ $[5] = h;
424
+ $[6] = t2;
425
+ $[7] = t3;
426
+ } else {
427
+ t2 = $[6];
428
+ t3 = $[7];
429
+ }
430
+ useEffect(t2, t3);
431
+ return h;
432
+ };
433
+ /**
434
+ * Transport-agnostic WS binding over a statewire/host transport:
435
+ * feed it sockets (anything with `send`/`close`) and raw text frames, and it
436
+ * speaks the Statewire envelope protocol — snapshot-first attach with `syn`,
437
+ * delta/ack/res envelopes, per-client-id supersede, and `fin` on finish.
438
+ */
439
+ const StatewireSocketHost = (transport, options) => {
440
+ const SocketHostRoot = () => {
441
+ return useSocketHost(transport, options);
442
+ };
443
+ const root = createTapRoot(SocketHostRoot);
444
+ let disposed = false;
445
+ return {
446
+ connect: (clientId, socket) => flushTapSync(() => root.getValue().connect(clientId, socket)),
447
+ finish: (fin) => flushTapSync(() => root.getValue().finish(fin)),
448
+ dispose: () => {
449
+ if (disposed) return;
450
+ disposed = true;
451
+ flushTapSync(() => root.getValue().finish({ reason: "evicted" }));
452
+ root.unmount();
453
+ }
454
+ };
455
+ };
456
+ //#endregion
457
+ export { StatewireSocketHost };
458
+
459
+ //# sourceMappingURL=socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.js","names":["useEffect","useState","createTapRoot","flushTapSync","useResource","ResourceElement","getCommandsHost","getEmitHost","getStateHost","CommandsHost","CommandsOutcome","StatewireSendError","Statewire","StatewireHost","StatewireHostInternal","encodeValue","reviveValue","createMacrotaskPoster","randomId","StatewireMetaEntry","StatewirePath","StatewireEnvelope","StatewireResponse","CLIENT_ID_RE","RESULT_TTL_MS","RESULT_LIMIT","WireOp","op","path","value","meta","Admission","seq","method","acked","pendingSent","settled","release","ClientRec","id","lastSeq","results","Map","ts","res","inflight","Conn","clientId","socket","Socket","open","lastSentAt","Frame","params","parseFrame","raw","frame","JSON","parse","Array","isArray","Record","Number","isInteger","undefined","encodePayload","payload","responseOf","adm","outcome","type","failure","error","fate","code","message","Error","String","createSocketHost","options","SocketHostOptions","heartbeatMs","heartbeatIntervalMs","clients","conns","Set","pendingOps","dirtyAcks","pendingRes","inbox","Promise","resolve","finished","Fin","disposed","flushScheduled","heartbeatTimer","ReturnType","setInterval","stateHost","StateHost","commandsHost","emitHost","current","post","flush","scheduleFlush","recordOp","push","attach","next","nextState","state","nextCommands","commands","nextEmit","emit","submit","setOnOp","setSink","event","closeConn","conn","reason","delete","close","size","clearInterval","sendTo","data","Date","now","send","ensureHeartbeat","unref","fin","takeChanged","rec","values","queueRes","length","ops","acks","envelope","clientRes","get","has","ack","Object","keys","stringify","response","list","set","advance","add","retain","newest","Math","max","entry","min","duplicateAnswer","cached","settleWire","admit","released","onAck","onSettle","catch","then","connect","SocketConnection","dead","refuse","test","known","created","other","snapshot","syn","lease","finish","resume","suspend","useSocketHost","transport","$","_c","t0","h","t1","t2","t3","StatewireSocketHost","SocketHost","SocketHostRoot","root","getValue","dispose","unmount"],"sources":["../../src/host/socket.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createTapRoot, flushTapSync, useResource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"./host\";\nimport type { CommandsHost, CommandsOutcome } from \"./host\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireHost } from \"../host\";\nimport type { StatewireHostInternal } from \"../host-internal\";\nimport { encodeValue, reviveValue } from \"../wire/meta\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { randomId } from \"../util/randomId\";\nimport type { StatewireMetaEntry } from \"../wire/meta\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type {\n StatewireEnvelope,\n StatewireResponse,\n} from \"../client/connection\";\n\nconst CLIENT_ID_RE = /^[A-Za-z0-9._~-]{1,256}$/;\nconst RESULT_TTL_MS = 30_000;\nconst RESULT_LIMIT = 256;\n\ntype WireOp = {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n meta?: StatewireMetaEntry[];\n};\n\ntype Admission = {\n seq: number;\n method: string;\n acked: boolean;\n pendingSent: boolean;\n settled: boolean;\n release: () => void;\n};\n\ntype ClientRec = {\n id: string;\n lastSeq: number;\n results: Map<number, { ts: number; res: StatewireResponse }>;\n inflight: Map<number, Admission>;\n};\n\ntype Conn = {\n clientId: string;\n socket: StatewireHostInternal.Socket;\n open: boolean;\n lastSentAt: number;\n};\n\ntype Frame = { method: string; seq: number; params: unknown[] };\n\nconst parseFrame = (raw: string): Frame | null => {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof frame !== \"object\" || frame === null || Array.isArray(frame)) {\n return null;\n }\n const { method, seq, params, meta } = frame as Record<string, unknown>;\n if (typeof method !== \"string\") return null;\n if (typeof seq !== \"number\" || !Number.isInteger(seq) || seq < 1) return null;\n if (!Array.isArray(params)) return null;\n if (meta !== undefined) {\n try {\n reviveValue(params, meta);\n } catch {\n return null;\n }\n }\n return { method, seq, params };\n};\n\nconst encodePayload = (\n payload: unknown,\n): { payload?: unknown; meta?: StatewireMetaEntry[] } => {\n if (payload === undefined) return {};\n const { value, meta } = encodeValue(payload);\n return { payload: value, ...(meta !== undefined && { meta }) };\n};\n\nconst responseOf = (\n adm: Admission,\n outcome: CommandsOutcome,\n): StatewireResponse => {\n if (outcome.type === \"accepted\") {\n return {\n seq: adm.seq,\n type: \"accepted\",\n ...encodePayload(outcome.payload),\n };\n }\n const failure =\n outcome.error instanceof StatewireSendError ? outcome.error.failure : null;\n switch (failure?.fate) {\n case \"unknown-command\":\n return {\n seq: adm.seq,\n type: \"rejected\",\n code: \"unknown-command\",\n message: `unknown method: '${adm.method}'`,\n };\n case \"invalid-params\":\n return {\n seq: adm.seq,\n type: \"rejected\",\n code: \"invalid-params\",\n message: failure.message,\n };\n case \"crashed\":\n return { seq: adm.seq, type: \"crashed\" };\n case \"rejected\":\n return {\n seq: adm.seq,\n type: \"rejected\",\n message: failure.message,\n ...encodePayload(failure.payload),\n };\n default:\n return {\n seq: adm.seq,\n type: \"rejected\",\n message:\n outcome.error instanceof Error\n ? outcome.error.message\n : String(outcome.error),\n };\n }\n};\n\nconst createSocketHost = (\n options?: StatewireHostInternal.SocketHostOptions,\n) => {\n const heartbeatMs = options?.heartbeatIntervalMs ?? 15_000;\n\n const clients = new Map<string, ClientRec>();\n const conns = new Set<Conn>();\n let pendingOps: WireOp[] = [];\n let dirtyAcks = new Set<string>();\n let pendingRes = new Map<string, StatewireResponse[]>();\n let inbox: Promise<void> = Promise.resolve();\n let finished: StatewireHost.Fin | null = null;\n let disposed = false;\n let flushScheduled = false;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n\n let stateHost: StatewireHost.StateHost | null = null;\n let commandsHost: CommandsHost | null = null;\n let emitHost: ReturnType<typeof getEmitHost> | null = null;\n let current: Statewire<unknown> | null = null;\n\n const post = createMacrotaskPoster(() => {\n flushScheduled = false;\n flush();\n });\n const scheduleFlush = () => {\n if (flushScheduled || disposed) return;\n flushScheduled = true;\n post();\n };\n\n const recordOp = (op: {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n }) => {\n if (op.op === \"replace\" || op.op === \"add\" || op.op === \"event\") {\n const { value, meta } = encodeValue(op.value);\n pendingOps.push({ ...op, value, ...(meta !== undefined && { meta }) });\n } else {\n pendingOps.push(op);\n }\n scheduleFlush();\n };\n\n const attach = (next: Statewire<unknown>) => {\n if (next === current) return;\n current = next;\n const nextState =\n typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined;\n const nextCommands = getCommandsHost(next.commands);\n const nextEmit = getEmitHost(next.emit);\n if (!nextState || !nextCommands?.submit || !nextEmit) {\n throw new Error(\n \"StatewireSocketHost requires a statewire/host transport (useStatewireState with object state, useStatewireCommands, useStatewireEmit)\",\n );\n }\n if (nextState !== stateHost) {\n stateHost?.setOnOp(null);\n stateHost = nextState;\n stateHost.setOnOp(recordOp);\n }\n commandsHost = nextCommands;\n if (nextEmit !== emitHost) {\n emitHost?.setSink(null);\n emitHost = nextEmit;\n emitHost.setSink((event) => recordOp({ op: \"event\", value: event }));\n }\n };\n\n const closeConn = (conn: Conn, code?: number, reason?: string) => {\n if (!conn.open) return;\n conn.open = false;\n conns.delete(conn);\n try {\n conn.socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n if (conns.size === 0 && heartbeatTimer !== null) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = null;\n }\n };\n\n const sendTo = (conn: Conn, data: string) => {\n if (!conn.open) return;\n conn.lastSentAt = Date.now();\n try {\n conn.socket.send(data);\n } catch {\n closeConn(conn);\n }\n };\n\n const ensureHeartbeat = () => {\n if (heartbeatTimer !== null || heartbeatMs <= 0) return;\n heartbeatTimer = setInterval(() => {\n const now = Date.now();\n for (const conn of [...conns]) {\n if (now - conn.lastSentAt >= heartbeatMs) sendTo(conn, \"{}\");\n }\n }, heartbeatMs);\n (heartbeatTimer as unknown as { unref?: () => void }).unref?.();\n };\n\n const flush = (fin?: StatewireHost.Fin) => {\n flushTapSync(() => {});\n stateHost?.takeChanged();\n for (const rec of clients.values()) {\n for (const adm of rec.inflight.values()) {\n if (adm.acked && !adm.settled && !adm.pendingSent) {\n adm.pendingSent = true;\n queueRes(rec.id, { seq: adm.seq, type: \"pending\" });\n }\n }\n }\n if (\n pendingOps.length === 0 &&\n dirtyAcks.size === 0 &&\n pendingRes.size === 0 &&\n fin === undefined\n ) {\n return;\n }\n const ops = pendingOps;\n const acks = dirtyAcks;\n const res = pendingRes;\n pendingOps = [];\n dirtyAcks = new Set();\n pendingRes = new Map();\n for (const conn of [...conns]) {\n const envelope: StatewireEnvelope = {};\n if (ops.length > 0) envelope.ops = ops;\n const clientRes = res.get(conn.clientId);\n if (clientRes !== undefined) envelope.res = clientRes;\n if (acks.has(conn.clientId)) {\n const rec = clients.get(conn.clientId);\n if (rec !== undefined) envelope.ack = rec.lastSeq;\n }\n if (fin !== undefined) envelope.fin = fin;\n if (Object.keys(envelope).length === 0) continue;\n sendTo(conn, JSON.stringify(envelope));\n if (fin !== undefined) closeConn(conn);\n }\n };\n\n const queueRes = (clientId: string, response: StatewireResponse) => {\n const list = pendingRes.get(clientId);\n if (list) list.push(response);\n else pendingRes.set(clientId, [response]);\n };\n\n const advance = (rec: ClientRec, seq: number) => {\n rec.lastSeq = seq;\n dirtyAcks.add(rec.id);\n };\n\n const retain = (rec: ClientRec, response: StatewireResponse) => {\n const now = Date.now();\n rec.results.set(response.seq, { ts: now, res: response });\n const newest = Math.max(...rec.results.keys());\n for (const [seq, entry] of rec.results) {\n if (seq !== newest && now - entry.ts > RESULT_TTL_MS) {\n rec.results.delete(seq);\n }\n }\n while (rec.results.size > RESULT_LIMIT) {\n rec.results.delete(Math.min(...rec.results.keys()));\n }\n };\n\n const duplicateAnswer = (rec: ClientRec, seq: number): StatewireResponse => {\n const cached = rec.results.get(seq);\n if (cached) {\n const newest = Math.max(...rec.results.keys());\n if (seq === newest || Date.now() - cached.ts <= RESULT_TTL_MS) {\n return cached.res;\n }\n rec.results.delete(seq);\n }\n if (rec.inflight.has(seq)) return { seq, type: \"pending\" };\n return { seq, type: \"result-unavailable\" };\n };\n\n const settleWire = (\n rec: ClientRec,\n adm: Admission,\n outcome: CommandsOutcome,\n ) => {\n if (adm.settled) return;\n adm.settled = true;\n rec.inflight.delete(adm.seq);\n const response = responseOf(adm, outcome);\n retain(rec, response);\n if (!adm.acked) {\n adm.acked = true;\n advance(rec, adm.seq);\n }\n if (\n adm.pendingSent ||\n response.type !== \"accepted\" ||\n response.payload !== undefined\n ) {\n queueRes(rec.id, response);\n }\n scheduleFlush();\n adm.release();\n };\n\n const admit = (conn: Conn, frame: Frame): Promise<void> | void => {\n if (!conn.open || disposed || finished !== null) return;\n const rec = clients.get(conn.clientId)!;\n if (frame.seq <= rec.lastSeq || rec.inflight.has(frame.seq)) {\n queueRes(rec.id, duplicateAnswer(rec, frame.seq));\n scheduleFlush();\n return;\n }\n if (frame.seq > rec.lastSeq + 1) {\n closeConn(\n conn,\n 1008,\n `seq gap: expected ${rec.lastSeq + 1}, got ${frame.seq}`,\n );\n return;\n }\n const adm: Admission = {\n seq: frame.seq,\n method: frame.method,\n acked: false,\n pendingSent: false,\n settled: false,\n release: () => {},\n };\n const released = new Promise<void>((resolve) => {\n adm.release = resolve;\n });\n rec.inflight.set(frame.seq, adm);\n commandsHost!.submit!({\n method: frame.method,\n params: frame.params,\n clientId: rec.id,\n onAck: () => {\n if (adm.settled || adm.acked) return;\n adm.acked = true;\n advance(rec, adm.seq);\n scheduleFlush();\n adm.release();\n },\n onSettle: (outcome) => settleWire(rec, adm, outcome),\n }).catch((error: unknown) => {\n if (!adm.settled) settleWire(rec, adm, { type: \"rejected\", error });\n });\n return released;\n };\n\n const message = (conn: Conn, data: string) => {\n if (!conn.open || disposed || finished !== null) return;\n const frame = parseFrame(data);\n if (frame === null) {\n closeConn(conn, 1008, \"invalid frame\");\n return;\n }\n inbox = inbox.then(() => admit(conn, frame));\n };\n\n const connect = (\n clientId: string,\n socket: StatewireHostInternal.Socket,\n ): StatewireHostInternal.SocketConnection => {\n const dead: StatewireHostInternal.SocketConnection = {\n clientId,\n message: () => {},\n close: () => {},\n };\n const refuse = (code: number, reason: string) => {\n try {\n socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n return dead;\n };\n if (disposed) return refuse(1001, \"disposed\");\n if (!CLIENT_ID_RE.test(clientId)) return refuse(1008, \"invalid client id\");\n flush();\n if (finished !== null) {\n try {\n socket.send(JSON.stringify({ fin: finished }));\n } catch {\n /* peer already gone */\n }\n return refuse(1000, \"finished\");\n }\n const known = clients.has(clientId);\n const rec = known\n ? clients.get(clientId)!\n : (() => {\n const created: ClientRec = {\n id: clientId,\n lastSeq: 0,\n results: new Map(),\n inflight: new Map(),\n };\n clients.set(clientId, created);\n return created;\n })();\n for (const other of [...conns]) {\n if (other.clientId !== clientId) continue;\n sendTo(other, JSON.stringify({ fin: { reason: \"superseded\" } }));\n closeConn(other);\n }\n const conn: Conn = { clientId, socket, open: true, lastSentAt: 0 };\n conns.add(conn);\n ensureHeartbeat();\n const { value, meta } = encodeValue(stateHost!.snapshot());\n const snapshot: StatewireEnvelope = {\n ops: [\n {\n op: \"replace\",\n path: [],\n value,\n ...(meta !== undefined && { meta }),\n },\n ],\n syn: { lastSeq: known ? rec.lastSeq : -1, lease: randomId() },\n };\n sendTo(conn, JSON.stringify(snapshot));\n return {\n clientId,\n message: (data) => message(conn, data),\n close: () => closeConn(conn),\n };\n };\n\n const finish = (fin: StatewireHost.Fin) => {\n if (finished !== null || disposed) return;\n finished = fin;\n flush(fin);\n };\n\n return {\n connect,\n finish,\n attach,\n resume: () => {\n disposed = false;\n stateHost?.setOnOp(recordOp);\n emitHost?.setSink((event) => recordOp({ op: \"event\", value: event }));\n },\n suspend: () => {\n disposed = true;\n if (heartbeatTimer !== null) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = null;\n }\n stateHost?.setOnOp(null);\n emitHost?.setSink(null);\n },\n };\n};\n\nconst useSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): Pick<StatewireHostInternal.SocketHost, \"connect\" | \"finish\"> => {\n const [h] = useState(() => createSocketHost(options));\n const value = useResource(transport) as Statewire<unknown>;\n h.attach(value);\n useEffect(() => {\n h.attach(value);\n });\n useEffect(() => {\n h.resume();\n return () => h.suspend();\n }, [h]);\n return h;\n};\n\n/**\n * Transport-agnostic WS binding over a statewire/host transport:\n * feed it sockets (anything with `send`/`close`) and raw text frames, and it\n * speaks the Statewire envelope protocol — snapshot-first attach with `syn`,\n * delta/ack/res envelopes, per-client-id supersede, and `fin` on finish.\n */\nexport const StatewireSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): StatewireHostInternal.SocketHost => {\n const SocketHostRoot = () => useSocketHost(transport, options);\n const root = createTapRoot(SocketHostRoot);\n let disposed = false;\n return {\n connect: (clientId, socket) =>\n flushTapSync(() => root.getValue().connect(clientId, socket)),\n finish: (fin) => flushTapSync(() => root.getValue().finish(fin)),\n dispose: () => {\n if (disposed) return;\n disposed = true;\n flushTapSync(() => root.getValue().finish({ reason: \"evicted\" }));\n root.unmount();\n },\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { StatewireHost } from "./host.js";
2
+ import { StatewireSocketHost } from "./host/socket.js";
3
+ import { clientIdFromUrl, textFrameOrClose } from "./host/adapter.js";
4
+ //#region src/host-internal.d.ts
5
+ declare namespace StatewireHostInternal {
6
+ type Socket = {
7
+ send: (data: string) => void;
8
+ close: (code?: number, reason?: string) => void;
9
+ };
10
+ type SocketConnection = {
11
+ readonly clientId: string;
12
+ message: (data: string) => void;
13
+ close: () => void;
14
+ };
15
+ type SocketHost = {
16
+ connect: (clientId: string, socket: Socket) => SocketConnection;
17
+ finish: (fin: StatewireHost.Fin) => void;
18
+ dispose: () => void;
19
+ };
20
+ type SocketHostOptions = {
21
+ heartbeatIntervalMs?: number;
22
+ };
23
+ type Fin = StatewireHost.Fin;
24
+ }
25
+ //#endregion
26
+ export { StatewireHostInternal, StatewireSocketHost, clientIdFromUrl, textFrameOrClose };
27
+ //# sourceMappingURL=host-internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-internal.d.ts","names":[],"sources":["../src/host-internal.ts"],"mappings":";;;;kBAIiB;OACH;IACV,OAAO;IACP,QAAQ,eAAe;;OAGb;aACD;IACT,UAAU;IACV;;OAGU;IACV,UAAU,kBAAkB,QAAQ,WAAW;IAC/C,SAAS,KAAK,cAAc;IAC5B;;OAGU;IACV;;OAGU,MAAM,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { StatewireSocketHost } from "./host/socket.js";
2
+ import { clientIdFromUrl, textFrameOrClose } from "./host/adapter.js";
3
+ export { StatewireSocketHost, clientIdFromUrl, textFrameOrClose };
package/dist/host.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { ServerOp, append } from "./host/draft.js";
2
+ import { StatewireReject } from "./host/engine.js";
3
+ import { CommandExecution, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState } from "./host/host.js";
4
+ import { Statewire } from "./types.js";
5
+ //#region src/host.d.ts
6
+ declare const executionResult: unique symbol;
7
+ type UnwrapExecution<V> = V extends StatewireHost.Execution<infer T> ? T : V;
8
+ declare namespace StatewireHost {
9
+ type StateCtl<TState = unknown> = {
10
+ /** Apply server-vocabulary ops (root replace = whole-state swap). */
11
+ apply: (ops: readonly Statewire.Op[]) => void;
12
+ /** The committed plain state — immutably replaced per op, so a captured value is a stable snapshot. */
13
+ snapshot: () => TState;
14
+ };
15
+ type StateHost = Statewire.StateSource & {
16
+ /** Synchronous, per locally-recorded op; the host owns batching. */
17
+ setOnOp: (listener: ((op: ServerOp) => void) | null) => void;
18
+ };
19
+ type StateSource = Statewire.StateSource;
20
+ /**
21
+ * Handle a command handler returns to keep the command open past its own
22
+ * return: `ack()` releases the client's optimistic overlay / ack watermark,
23
+ * `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent
24
+ * and a no-op after settle.
25
+ */
26
+ type Execution<T = void> = {
27
+ readonly [executionResult]: (value: T) => void;
28
+ };
29
+ type ExecutionResolvers<T = void> = {
30
+ ack: () => void;
31
+ resolve: (value: T) => void;
32
+ reject: (error: unknown) => void;
33
+ };
34
+ type HandledCommands<THandlers extends Statewire.Commands> = { [K in keyof THandlers]: (...args: Parameters<THandlers[K]>) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>; };
35
+ type Fin = {
36
+ reason: "evicted" | "error" | "gone" | "superseded" | (string & {});
37
+ message?: string;
38
+ payload?: unknown;
39
+ meta?: unknown;
40
+ };
41
+ }
42
+ //#endregion
43
+ export { CommandExecution, StatewireHost, StatewireReject, append, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
44
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","names":[],"sources":["../src/host.ts"],"mappings":";;;;;cAac;KAET,gBAAgB,KAAK,UAAU,cAAc,gBAAgB,KAAK,IAAI;kBAE1D;OACH,SAAS;;IAEnB,QAAQ,cAAc,UAAU;;IAEhC,gBAAgB;;OAGN,YAAY,UAAU;;IAEhC,UAAU,YAAY,IAAI;;OAGhB,cAAc,UAAU;;;;;;;OAQxB,UAAU;cACV,mBAAmB,OAAO;;OAG1B,mBAAmB;IAC7B;IACA,UAAU,OAAO;IACjB,SAAS;;OAGC,gBAAgB,kBAAkB,UAAU,eACrD,WAAW,gBACP,MAAM,WAAW,UAAU,QAC3B,gBAAgB,QAAQ,WAAW,UAAU;OAGxC;IACV;IACA;IACA;IACA"}
package/dist/host.js ADDED
@@ -0,0 +1,4 @@
1
+ import { StatewireReject } from "./host/engine.js";
2
+ import { append } from "./host/draft.js";
3
+ import { CommandExecution, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState } from "./host/host.js";
4
+ export { CommandExecution, StatewireReject, append, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
@@ -0,0 +1,5 @@
1
+ import { StatewireSSE } from "./transports/sse.js";
2
+ import { StatewireClient, StatewireResource, useStatewire } from "./statewire.js";
3
+ import { Statewire, StatewireSendError } from "./types.js";
4
+ import { StatewireWS } from "./transports/ws.js";
5
+ export { type Statewire, StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, useStatewire };
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { StatewireSendError } from "./types.js";
2
+ import { StatewireClient, StatewireResource, useStatewire } from "./statewire.js";
3
+ import { StatewireSSE } from "./transports/sse.js";
4
+ import { StatewireWS } from "./transports/ws.js";
5
+ export { StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, useStatewire };