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
@@ -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 };
package/dist/index.d.ts CHANGED
@@ -1,28 +1,5 @@
1
- import { StatewireOp, applyOps } from "./wire/ops.js";
2
- import { StatewireConnection, StatewireSSE, StatewireSSEOptions } from "./transports/sse.js";
3
- import { SendFailure, StatewireChanges, StatewireCommand, StatewireCommandUpdate, StatewireCommands, StatewireCommandsProxy, StatewireHelpers, StatewireOptimistic, StatewireOptimisticContext, StatewireOptions, StatewireSendError, StatewireStateSource, StatewireTransport } from "./types.js";
4
- import { StatewireWS, StatewireWSOptions } from "./transports/ws.js";
5
- import { Statewire, StatewireResource, useStatewire } from "./statewire.js";
6
- //#region src/index.d.ts
7
- declare namespace Statewire {
8
- type Connection = StatewireConnection;
9
- type ConnectionStatus = StatewireConnection["status"];
10
- type Transport<TState = any, TCommands extends StatewireCommands = StatewireCommands, TEvent = any> = StatewireTransport<TState, TCommands, TEvent>;
11
- type StateSource = StatewireStateSource;
12
- type SSEOptions = StatewireSSEOptions;
13
- type WSOptions = StatewireWSOptions;
14
- type Options<TState, TCommands extends StatewireCommands = StatewireCommands, TEvent = any> = StatewireOptions<TState, TCommands, TEvent>;
15
- type Helpers<TState, TCommands extends StatewireCommands = StatewireCommands> = StatewireHelpers<TState, TCommands>;
16
- type Commands = StatewireCommands;
17
- type Command<TCommands extends StatewireCommands = StatewireCommands> = StatewireCommand<TCommands>;
18
- type CommandsProxy<TCommands extends StatewireCommands> = StatewireCommandsProxy<TCommands>;
19
- type CommandUpdate<TCommands extends StatewireCommands = StatewireCommands> = StatewireCommandUpdate<TCommands>;
20
- type Optimistic<TState, TCommands extends StatewireCommands = StatewireCommands> = StatewireOptimistic<TState, TCommands>;
21
- type OptimisticContext = StatewireOptimisticContext;
22
- type SendFailure = SendFailure;
23
- type Changes = StatewireChanges;
24
- type Op = StatewireOp;
25
- }
26
- //#endregion
27
- export { Statewire, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, applyOps, useStatewire };
28
- //# sourceMappingURL=index.d.ts.map
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 CHANGED
@@ -1,6 +1,5 @@
1
- import { applyOps } from "./wire/ops.js";
2
1
  import { StatewireSendError } from "./types.js";
3
- import { Statewire, StatewireResource, useStatewire } from "./statewire.js";
2
+ import { StatewireClient, StatewireResource, useStatewire } from "./statewire.js";
4
3
  import { StatewireSSE } from "./transports/sse.js";
5
4
  import { StatewireWS } from "./transports/ws.js";
6
- export { Statewire, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, applyOps, useStatewire };
5
+ export { StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, useStatewire };