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.
- package/README.md +84 -1
- package/dist/client/changes.d.ts +7 -0
- package/dist/client/changes.d.ts.map +1 -0
- package/dist/client/changes.js +25 -0
- package/dist/client/changes.js.map +1 -0
- package/dist/client/connection.d.ts +63 -0
- package/dist/client/connection.d.ts.map +1 -0
- package/dist/client/connection.js +211 -0
- package/dist/client/connection.js.map +1 -0
- package/dist/client/core.d.ts +14 -0
- package/dist/client/core.d.ts.map +1 -0
- package/dist/client/core.js +272 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/session.d.ts +71 -0
- package/dist/client/session.d.ts.map +1 -0
- package/dist/client/session.js +529 -0
- package/dist/client/session.js.map +1 -0
- package/dist/client/sessionStore.d.ts +16 -0
- package/dist/client/sessionStore.d.ts.map +1 -0
- package/dist/client/sessionStore.js +14 -0
- package/dist/client/sessionStore.js.map +1 -0
- package/dist/client/timing.d.ts +11 -0
- package/dist/client/timing.d.ts.map +1 -0
- package/dist/client/timing.js +15 -0
- package/dist/client/timing.js.map +1 -0
- package/dist/host/adapter.d.ts +8 -0
- package/dist/host/adapter.d.ts.map +1 -0
- package/dist/host/adapter.js +23 -0
- package/dist/host/adapter.js.map +1 -0
- package/dist/host/draft.d.ts +28 -0
- package/dist/host/draft.d.ts.map +1 -0
- package/dist/host/draft.js +211 -0
- package/dist/host/draft.js.map +1 -0
- package/dist/host/engine.d.ts +14 -0
- package/dist/host/engine.d.ts.map +1 -0
- package/dist/host/engine.js +17 -0
- package/dist/host/engine.js.map +1 -0
- package/dist/host/host.d.ts +62 -0
- package/dist/host/host.d.ts.map +1 -0
- package/dist/host/host.js +331 -0
- package/dist/host/host.js.map +1 -0
- package/dist/host/socket.d.ts +14 -0
- package/dist/host/socket.d.ts.map +1 -0
- package/dist/host/socket.js +459 -0
- package/dist/host/socket.js.map +1 -0
- package/dist/host-internal.d.ts +27 -0
- package/dist/host-internal.d.ts.map +1 -0
- package/dist/host-internal.js +3 -0
- package/dist/host.d.ts +44 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/statewire.d.ts +104 -0
- package/dist/statewire.d.ts.map +1 -0
- package/dist/statewire.js +53 -0
- package/dist/statewire.js.map +1 -0
- package/dist/transports/fake-sse.d.ts +12 -0
- package/dist/transports/fake-sse.d.ts.map +1 -0
- package/dist/transports/fake-sse.js +39 -0
- package/dist/transports/fake-sse.js.map +1 -0
- package/dist/transports/fixtures.d.ts +33 -0
- package/dist/transports/fixtures.d.ts.map +1 -0
- package/dist/transports/fixtures.js +50 -0
- package/dist/transports/fixtures.js.map +1 -0
- package/dist/transports/remote.d.ts +12 -0
- package/dist/transports/remote.d.ts.map +1 -0
- package/dist/transports/remote.js +21 -0
- package/dist/transports/remote.js.map +1 -0
- package/dist/transports/sse-parser.d.ts +5 -0
- package/dist/transports/sse-parser.d.ts.map +1 -0
- package/dist/transports/sse-parser.js +26 -0
- package/dist/transports/sse-parser.js.map +1 -0
- package/dist/transports/sse.d.ts +64 -0
- package/dist/transports/sse.d.ts.map +1 -0
- package/dist/transports/sse.js +137 -0
- package/dist/transports/sse.js.map +1 -0
- package/dist/transports/testing.d.ts +32 -0
- package/dist/transports/testing.d.ts.map +1 -0
- package/dist/transports/testing.js +94 -0
- package/dist/transports/testing.js.map +1 -0
- package/dist/transports/ws.d.ts +9 -0
- package/dist/transports/ws.d.ts.map +1 -0
- package/dist/transports/ws.js +169 -0
- package/dist/transports/ws.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/dist/util/draftTraps.d.ts +6 -0
- package/dist/util/draftTraps.d.ts.map +1 -0
- package/dist/util/draftTraps.js +25 -0
- package/dist/util/draftTraps.js.map +1 -0
- package/dist/util/macrotask.d.ts +5 -0
- package/dist/util/macrotask.d.ts.map +1 -0
- package/dist/util/macrotask.js +25 -0
- package/dist/util/macrotask.js.map +1 -0
- package/dist/util/methodProxy.d.ts +5 -0
- package/dist/util/methodProxy.d.ts.map +1 -0
- package/dist/util/methodProxy.js +17 -0
- package/dist/util/methodProxy.js.map +1 -0
- package/dist/util/randomId.d.ts +5 -0
- package/dist/util/randomId.d.ts.map +1 -0
- package/dist/util/randomId.js +6 -0
- package/dist/util/randomId.js.map +1 -0
- package/dist/util/recordMutations.d.ts +10 -0
- package/dist/util/recordMutations.d.ts.map +1 -0
- package/dist/util/recordMutations.js +109 -0
- package/dist/util/recordMutations.js.map +1 -0
- package/dist/util/statePath.d.ts +14 -0
- package/dist/util/statePath.d.ts.map +1 -0
- package/dist/util/statePath.js +31 -0
- package/dist/util/statePath.js.map +1 -0
- package/dist/wire/meta.d.ts +27 -0
- package/dist/wire/meta.d.ts.map +1 -0
- package/dist/wire/meta.js +75 -0
- package/dist/wire/meta.js.map +1 -0
- package/dist/wire/ops.d.ts +19 -0
- package/dist/wire/ops.d.ts.map +1 -0
- package/dist/wire/ops.js +112 -0
- package/dist/wire/ops.js.map +1 -0
- package/package.json +52 -5
- package/src/client/changes.ts +38 -0
- package/src/client/connection.ts +301 -0
- package/src/client/core.ts +312 -0
- package/src/client/session.ts +696 -0
- package/src/client/sessionStore.ts +23 -0
- package/src/client/timing.ts +14 -0
- package/src/host/adapter.ts +33 -0
- package/src/host/draft.ts +323 -0
- package/src/host/engine.ts +13 -0
- package/src/host/host.ts +482 -0
- package/src/host/socket.ts +542 -0
- package/src/host-internal.ts +28 -0
- package/src/host.ts +61 -0
- package/src/index.ts +5 -0
- package/src/statewire.ts +200 -0
- package/src/transports/fake-sse.ts +51 -0
- package/src/transports/fixtures.ts +60 -0
- package/src/transports/remote.ts +30 -0
- package/src/transports/sse-parser.ts +24 -0
- package/src/transports/sse.ts +267 -0
- package/src/transports/testing.ts +123 -0
- package/src/transports/ws.ts +256 -0
- package/src/types.ts +88 -0
- package/src/util/draftTraps.ts +32 -0
- package/src/util/macrotask.ts +25 -0
- package/src/util/methodProxy.ts +16 -0
- package/src/util/randomId.ts +4 -0
- package/src/util/recordMutations.ts +0 -0
- package/src/util/statePath.ts +61 -0
- package/src/wire/meta.ts +101 -0
- package/src/wire/ops.ts +202 -0
- 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"}
|
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
ADDED
|
@@ -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 };
|