statewire 0.0.4 → 0.4.0
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 +3 -1
- package/dist/client/session.d.ts +7 -2
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +65 -33
- package/dist/client/session.js.map +1 -1
- package/dist/host/host.d.ts +2 -14
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +44 -92
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +1 -0
- package/dist/host/socket.js.map +1 -1
- package/dist/host.d.ts +16 -20
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -2
- package/dist/statewire.d.ts +14 -1
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js.map +1 -1
- package/dist/transports/remote.d.ts +2 -2
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +4 -1
- package/dist/transports/remote.js.map +1 -1
- package/dist/util/statePath.js +8 -6
- package/dist/util/statePath.js.map +1 -1
- package/package.json +1 -1
- package/src/client/session.ts +63 -19
- package/src/host/host.ts +58 -113
- package/src/host/socket.ts +1 -0
- package/src/host.ts +14 -22
- package/src/statewire.ts +15 -6
- package/src/transports/remote.ts +9 -3
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ const onClick = async () => {
|
|
|
33
33
|
- `"crashed"` — acked, but the processing died before producing a result
|
|
34
34
|
- `"result-unavailable"` — acked, but the result can no longer be delivered
|
|
35
35
|
|
|
36
|
-
Every failure also carries `cause` — which channel settled it: `"server"` = the server's answer, `"stream-gone"` = terminal `finish(gone)`, `"session-lost"` = the server forgot the client id (eviction), `"superseded"` = another attach took the writer lease, `"unmount"`
|
|
36
|
+
Every failure also carries `cause` — which channel settled it: `"server"` = the server's answer, `"stream-gone"` = terminal `finish(gone)`, `"session-lost"` = the server forgot the client id (eviction), `"superseded"` = another attach took the writer lease, `"unmount"` is client-side. An `"aborted"` failure carries `predecessorLost: true` when an earlier in-flight command of the same batch has uncertain fate. The taxonomy's single exported type is `StatewireClient.SendFailure`; the `fate` and `cause` unions are reachable as `SendFailure["fate"]` / `SendFailure["cause"]`.
|
|
37
|
+
|
|
38
|
+
Session-lost failures are opt-out per command: the transport option `onCommandSessionLost({ method, params, seq })` is offered each pending command during an eviction, in seq order; return `"replay"` to re-enqueue it under the rotated client id (fresh seq, relative order preserved, the original promise settles with the replay's outcome) instead of settling it as a failure. Context submissions are never offered; replay may re-run a command the lost session already applied.
|
|
37
39
|
|
|
38
40
|
Reconnects are not errors: transport drops and server evictions self-heal with exponential backoff while `state` stays renderable. Commands carry a monotonic seq, so retries are deduplicated server-side and a reconnect splices already-applied commands instead of re-running them.
|
|
39
41
|
|
package/dist/client/session.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StatewireSessionRecord, StatewireSessionStore, StatewireSessionStoreOptions } from "./sessionStore.js";
|
|
2
|
+
import { StatewireClient } from "../statewire.js";
|
|
2
3
|
import { Statewire, StatewireSendError } from "../types.js";
|
|
3
4
|
import { StatewirePath } from "../wire/ops.js";
|
|
4
5
|
import { CommandsInvocation, CommandsListener } from "../host/host.js";
|
|
@@ -42,6 +43,9 @@ type Pending = {
|
|
|
42
43
|
reject: (error: StatewireSendError) => void;
|
|
43
44
|
};
|
|
44
45
|
type AttachState = "detached" | "attached" | "finished";
|
|
46
|
+
type StatewireSessionOptions = StatewireSessionStoreOptions & {
|
|
47
|
+
onCommandSessionLost?: ((command: StatewireClient.SessionLostCommand) => "replay" | void) | undefined;
|
|
48
|
+
};
|
|
45
49
|
type Session = {
|
|
46
50
|
record: StatewireSessionRecord;
|
|
47
51
|
ctx: StatewireRemoteProps;
|
|
@@ -61,6 +65,7 @@ type Session = {
|
|
|
61
65
|
transport: RemoteLink | null;
|
|
62
66
|
waiters: (() => void)[];
|
|
63
67
|
lifecycle: CommandsListener | null;
|
|
68
|
+
onCommandSessionLost: StatewireSessionOptions["onCommandSessionLost"];
|
|
64
69
|
notify: () => void;
|
|
65
70
|
emitEvent: (event: unknown) => void;
|
|
66
71
|
send: (command: StatewireWireCommand) => Promise<unknown>;
|
|
@@ -68,7 +73,7 @@ type Session = {
|
|
|
68
73
|
};
|
|
69
74
|
declare const setTransport: (s: Session, transport: RemoteLink) => void;
|
|
70
75
|
declare const setContext: (s: Session, value: Record<string, unknown>) => Promise<unknown>;
|
|
71
|
-
declare const useStatewireSession: (options:
|
|
76
|
+
declare const useStatewireSession: (options: StatewireSessionOptions, emit: (event: unknown) => void) => Session;
|
|
72
77
|
//#endregion
|
|
73
|
-
export { setContext, setTransport, useStatewireSession };
|
|
78
|
+
export { StatewireSessionOptions, setContext, setTransport, useStatewireSession };
|
|
74
79
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;;;;;;;;KA+BK;EACC;EAAc;;EACd;EAAc;EAAwB;;EACtC;EAAc;EAAwB;;EACtC;EAAuB;EAAiB;;EACxC;EAAoB;EAAiB;;EACrC;;KAED;WACM;WACA,SAAS;;WAET,KAAK;EACd,OAAO;EACP,UAAU;EACV,SAAS,OAAO;;KAqGb;KAEO,0BAA0B;EACpC,yBACM,SAAS,gBAAgB;;KAI5B;EACH,QAAQ;EACR,KAAK;EACL;EACA,OAAO;EACP;EACA,SAAS;EACT,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ,UAAU;EAClB,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX,sBAAsB;EACtB;EACA,YAAY;EACZ,OAAO,SAAS,yBAAyB;EACzC,UAAU,mBAAmB,sBAAsB;;cAmf/C,eAAgB,GAAG,SAAS,WAAW;cAsDvC,aACJ,GAAG,SACH,OAAO,4BACN;cAQU,sBACX,SAAS,yBACT,OAAO,4BACN"}
|
package/dist/client/session.js
CHANGED
|
@@ -48,18 +48,20 @@ const transition = (s, entry, next, outcome) => {
|
|
|
48
48
|
const covers = !isStateSettled(prev) && isStateSettled(next);
|
|
49
49
|
entry.state = next;
|
|
50
50
|
if (covers && entry.inv) s.lifecycle?.onCovered?.(entry.inv);
|
|
51
|
-
if (settles && outcome !== void 0)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
if (settles && outcome !== void 0) {
|
|
52
|
+
if (outcome.ok) {
|
|
53
|
+
entry.resolve(outcome.payload);
|
|
54
|
+
if (entry.inv) s.lifecycle?.onSettle(entry.inv, {
|
|
55
|
+
type: "accepted",
|
|
56
|
+
payload: outcome.payload
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
entry.reject(new StatewireSendError(outcome.failure));
|
|
60
|
+
if (entry.inv) s.lifecycle?.onSettle(entry.inv, {
|
|
61
|
+
type: "rejected",
|
|
62
|
+
error: new StatewireSendError(outcome.failure)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
if (next.at === "done") s.pending = s.pending.filter((p) => p !== entry);
|
|
65
67
|
};
|
|
@@ -121,6 +123,7 @@ const createSession = (options) => {
|
|
|
121
123
|
transport: null,
|
|
122
124
|
waiters: [],
|
|
123
125
|
lifecycle: null,
|
|
126
|
+
onCommandSessionLost: options.onCommandSessionLost,
|
|
124
127
|
notify: () => {},
|
|
125
128
|
emitEvent: () => {},
|
|
126
129
|
send: (command) => sendCommand(s, command),
|
|
@@ -170,10 +173,17 @@ const settleAll = (s, detach, verdict) => {
|
|
|
170
173
|
fate: "result-unavailable",
|
|
171
174
|
cause
|
|
172
175
|
};
|
|
173
|
-
if (state.at === "queued")
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
if (state.at === "queued") {
|
|
177
|
+
const predecessorLost = batch.some((p) => p.seq < entry.seq && p.state.at !== "queued");
|
|
178
|
+
return predecessorLost ? {
|
|
179
|
+
fate: "aborted",
|
|
180
|
+
cause,
|
|
181
|
+
predecessorLost
|
|
182
|
+
} : {
|
|
183
|
+
fate: "aborted",
|
|
184
|
+
cause
|
|
185
|
+
};
|
|
186
|
+
}
|
|
177
187
|
return {
|
|
178
188
|
fate: "lost",
|
|
179
189
|
cause
|
|
@@ -187,7 +197,24 @@ const settleAll = (s, detach, verdict) => {
|
|
|
187
197
|
});
|
|
188
198
|
}
|
|
189
199
|
};
|
|
200
|
+
const claimReplays = (s) => {
|
|
201
|
+
const handler = s.onCommandSessionLost;
|
|
202
|
+
if (!handler) return [];
|
|
203
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
204
|
+
for (const entry of s.pending) {
|
|
205
|
+
if (!entry.inv || isResolved(entry.state)) continue;
|
|
206
|
+
if (handler({
|
|
207
|
+
method: entry.inv.method,
|
|
208
|
+
params: [...entry.inv.params],
|
|
209
|
+
seq: entry.seq
|
|
210
|
+
}) === "replay") claimed.add(entry);
|
|
211
|
+
}
|
|
212
|
+
if (claimed.size === 0) return [];
|
|
213
|
+
s.pending = s.pending.filter((p) => !claimed.has(p));
|
|
214
|
+
return [...claimed];
|
|
215
|
+
};
|
|
190
216
|
const evict = (s, verdict) => {
|
|
217
|
+
const replays = claimReplays(s);
|
|
191
218
|
settleAll(s, "evicted", verdict);
|
|
192
219
|
s.record = {
|
|
193
220
|
clientId: newClientId(),
|
|
@@ -196,6 +223,10 @@ const evict = (s, verdict) => {
|
|
|
196
223
|
s.store.save(s.record);
|
|
197
224
|
s.attach = "detached";
|
|
198
225
|
if (s.context !== void 0) s.send({ context: s.context });
|
|
226
|
+
for (const old of replays) {
|
|
227
|
+
if (old.inv) s.lifecycle?.onCovered?.(old.inv);
|
|
228
|
+
enqueue(s, old.command, old.resolve, old.reject);
|
|
229
|
+
}
|
|
199
230
|
publish(s);
|
|
200
231
|
wake(s);
|
|
201
232
|
};
|
|
@@ -276,7 +307,6 @@ const applyResponse = (s, response) => {
|
|
|
276
307
|
cause: "server"
|
|
277
308
|
}
|
|
278
309
|
});
|
|
279
|
-
break;
|
|
280
310
|
}
|
|
281
311
|
};
|
|
282
312
|
const ingestOps = (s, ops) => {
|
|
@@ -469,15 +499,7 @@ const setTransport = (s, transport) => {
|
|
|
469
499
|
s.transport = transport;
|
|
470
500
|
wake(s);
|
|
471
501
|
};
|
|
472
|
-
const
|
|
473
|
-
if (!s.running) return rejectedSend({
|
|
474
|
-
fate: "aborted",
|
|
475
|
-
cause: "unmount"
|
|
476
|
-
});
|
|
477
|
-
if (isFinished(s)) return rejectedSend({
|
|
478
|
-
fate: "aborted",
|
|
479
|
-
cause: "stream-gone"
|
|
480
|
-
});
|
|
502
|
+
const enqueue = (s, command, resolve, reject) => {
|
|
481
503
|
const seq = s.record.nextSeq;
|
|
482
504
|
s.record = {
|
|
483
505
|
...s.record,
|
|
@@ -498,17 +520,26 @@ const sendCommand = (s, command) => {
|
|
|
498
520
|
at: "queued",
|
|
499
521
|
pendingMarked: false
|
|
500
522
|
},
|
|
501
|
-
resolve
|
|
502
|
-
reject
|
|
523
|
+
resolve,
|
|
524
|
+
reject
|
|
503
525
|
};
|
|
504
|
-
const result = new Promise((resolve, reject) => {
|
|
505
|
-
entry.resolve = resolve;
|
|
506
|
-
entry.reject = reject;
|
|
507
|
-
});
|
|
508
|
-
result.catch(() => {});
|
|
509
526
|
s.pending.push(entry);
|
|
510
527
|
if (entry.inv) s.lifecycle?.onStart(entry.inv);
|
|
511
528
|
pump(s);
|
|
529
|
+
};
|
|
530
|
+
const sendCommand = (s, command) => {
|
|
531
|
+
if (!s.running) return rejectedSend({
|
|
532
|
+
fate: "aborted",
|
|
533
|
+
cause: "unmount"
|
|
534
|
+
});
|
|
535
|
+
if (isFinished(s)) return rejectedSend({
|
|
536
|
+
fate: "aborted",
|
|
537
|
+
cause: "stream-gone"
|
|
538
|
+
});
|
|
539
|
+
const result = new Promise((resolve, reject) => {
|
|
540
|
+
enqueue(s, command, resolve, reject);
|
|
541
|
+
});
|
|
542
|
+
result.catch(() => {});
|
|
512
543
|
return result;
|
|
513
544
|
};
|
|
514
545
|
const setContext = (s, value) => {
|
|
@@ -518,6 +549,7 @@ const setContext = (s, value) => {
|
|
|
518
549
|
};
|
|
519
550
|
const useStatewireSession = (options, emit) => {
|
|
520
551
|
const [s] = useState(() => createSession(options));
|
|
552
|
+
s.onCommandSessionLost = options.onCommandSessionLost;
|
|
521
553
|
useEffect(() => {
|
|
522
554
|
s.emitEvent = emit;
|
|
523
555
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":["useEffect","useState","applyOps","StatewirePath","StatewireResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","retryDelay","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireRemoteProps","StatewireSubmitResult","StatewireWireCommand","PendingState","at","pendingMarked","failedAttempts","posted","Pending","seq","command","inv","state","resolve","payload","reject","error","Outcome","ok","failure","SendFailure","isResolved","isStateSettled","inFlight","Error","legalTransitions","Record","queued","trying","done","transition","s","Session","entry","next","outcome","prev","includes","settles","undefined","covers","lifecycle","onCovered","onSettle","type","pending","filter","p","carried","Exclude","newClientId","rejectedSend","Promise","result","catch","AttachState","record","ctx","recordPersisted","store","lastAck","context","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","notify","emitEvent","event","send","commands","params","createSession","options","sessionStore","stored","load","clientId","nextSeq","onSnapshot","args","handleSnapshot","onOps","handleOps","onFinish","handleFinish","snapshot","takeChanged","setOnChange","listener","sendCommand","method","setListener","publish","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","w","settleAll","detach","verdict","cause","evicted","unmount","gone","superseded","const","batch","detachFailure","fate","cascades","some","evict","save","noteAcked","onAck","spliceAcked","settleVoid","protocolError","message","applyResponse","response","find","code","ingestOps","ops","applied","changedPaths","events","publishIngested","lastSeq","connection","reconnect","pump","res","ack","horizon","Math","max","reason","isFinished","sendable","epoch","onPosted","console","start","stop","setTransport","ts","Date","now","onStart","setContext","value","Array","isArray","useStatewireSession","emit"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireResponse } from \"./connection\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { retryDelay } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireRemoteProps,\n StatewireSubmitResult,\n StatewireWireCommand,\n} from \"../transports/sse\";\n\n/**\n * queued/trying/posted: overlay live, result pending. awaiting-result: acked\n * (overlay gone) but the result rides a late res; `posted: false` re-sends.\n * settled-live: result settled but the overlay stays until the covering ack.\n * done: removed from the queue.\n */\ntype PendingState =\n | { at: \"queued\"; pendingMarked: boolean }\n | { at: \"trying\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"posted\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"awaiting-result\"; posted: boolean; failedAttempts: number }\n | { at: \"settled-live\"; posted: boolean; failedAttempts: number }\n | { at: \"done\" };\n\ntype Pending = {\n readonly seq: number;\n readonly command: StatewireWireCommand;\n /** null for context submissions — they carry no command lifecycle. */\n readonly inv: CommandsInvocation | null;\n state: PendingState;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype Outcome =\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure };\n\nconst isResolved = (state: PendingState) =>\n state.at === \"settled-live\" || state.at === \"done\";\n\nconst isStateSettled = (state: PendingState) =>\n state.at === \"awaiting-result\" || state.at === \"done\";\n\n// The pump admits an entry as trying before sending; only the pump itself\n// requeues, and a done entry is never still pending.\nconst inFlight = (state: PendingState) => {\n if (state.at === \"queued\" || state.at === \"done\") {\n throw new Error(`statewire: ${state.at} entry observed after a send`);\n }\n return state;\n};\n\nconst legalTransitions: Record<PendingState[\"at\"], PendingState[\"at\"][]> = {\n queued: [\"queued\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n // trying -> queued: an answered unknown-client proves the seq was never admitted.\n trying: [\n \"queued\",\n \"trying\",\n \"posted\",\n \"awaiting-result\",\n \"settled-live\",\n \"done\",\n ],\n posted: [\"posted\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n \"awaiting-result\": [\"awaiting-result\", \"done\"],\n \"settled-live\": [\"settled-live\", \"done\"],\n done: [],\n};\n\nconst transition = (\n s: Session,\n entry: Pending,\n next: PendingState,\n outcome?: Outcome,\n) => {\n const prev = entry.state;\n if (!legalTransitions[prev.at].includes(next.at)) {\n throw new Error(\n `statewire: illegal pending transition ${prev.at} -> ${next.at}`,\n );\n }\n const settles = !isResolved(prev) && isResolved(next);\n if (settles && outcome === undefined) {\n throw new Error(\n `statewire: settling transition ${prev.at} -> ${next.at} without an outcome`,\n );\n }\n const covers = !isStateSettled(prev) && isStateSettled(next);\n entry.state = next;\n if (covers && entry.inv) s.lifecycle?.onCovered?.(entry.inv);\n if (settles && outcome !== undefined) {\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n }\n if (next.at === \"done\") {\n s.pending = s.pending.filter((p) => p !== entry);\n }\n};\n\nconst carried = (\n state: Exclude<PendingState, { at: \"done\" }>,\n): { posted: boolean; failedAttempts: number } =>\n state.at === \"queued\"\n ? { posted: false, failedAttempts: 0 }\n : state.at === \"trying\" || state.at === \"posted\"\n ? { posted: state.at === \"posted\", failedAttempts: state.failedAttempts }\n : { posted: state.posted, failedAttempts: state.failedAttempts };\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n lastAck: number;\n context: Record<string, unknown> | undefined;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n emitEvent: (event: unknown) => void;\n send: (command: StatewireWireCommand) => Promise<unknown>;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst createSession = (options: StatewireSessionStoreOptions): Session => {\n const store = options.sessionStore ?? createMemorySessionStore();\n const stored = store.load();\n const record = stored ?? { clientId: newClientId(), nextSeq: 1 };\n const s: Session = {\n record,\n ctx: {\n get clientId() {\n return s.record.clientId;\n },\n onSnapshot: (args) => handleSnapshot(s, args),\n onOps: (args) => handleOps(s, args),\n onFinish: (args) => handleFinish(s, args),\n },\n recordPersisted: stored !== null,\n store,\n lastAck: -1,\n context: undefined,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => s.canonical,\n takeChanged: () => {\n const changed = s.changed;\n s.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n s.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n emitEvent: () => {},\n send: (command) => sendCommand(s, command),\n commands: createMethodProxy((method, params) => s.send({ method, params })),\n };\n bindCommandsHost(s.commands, {\n setListener: (listener) => {\n s.lifecycle = listener;\n },\n });\n return s;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst settleAll = (\n s: Session,\n detach: \"evicted\" | \"unmount\" | \"gone\" | \"superseded\",\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n const cause = (\n {\n evicted: \"session-lost\",\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n const state = entry.state;\n if (state.at === \"awaiting-result\")\n return { fate: \"result-unavailable\", cause };\n if (state.at === \"queued\") {\n const cascades = batch.some(\n (p) => p.seq < entry.seq && p.state.at !== \"queued\",\n );\n return { fate: \"aborted\", cause: cascades ? \"cascade\" : cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n const failure =\n entry === verdict?.entry ? verdict.failure : detachFailure(entry);\n transition(s, entry, { at: \"done\" }, { ok: false, failure });\n }\n};\n\nconst evict = (\n s: Session,\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n settleAll(s, \"evicted\", verdict);\n s.record = { clientId: newClientId(), nextSeq: 1 };\n s.store.save(s.record);\n s.attach = \"detached\";\n // The server dropped this client's record and its stored context with it:\n // the new session opens with a fresh full root replace.\n if (s.context !== undefined) void s.send({ context: s.context });\n publish(s);\n wake(s);\n};\n\nconst noteAcked = (s: Session, seq: number) => {\n for (const entry of s.pending) {\n if (entry.seq > seq || isStateSettled(entry.state)) continue;\n if (entry.inv) s.lifecycle?.onAck(entry.inv);\n }\n};\n\nconst spliceAcked = (s: Session, seq: number, settleVoid: boolean) => {\n for (const entry of [...s.pending]) {\n const state = entry.state;\n if (entry.seq > seq || isStateSettled(state)) continue;\n if (state.at === \"settled-live\") {\n transition(s, entry, { at: \"done\" });\n } else if (settleVoid && !state.pendingMarked) {\n transition(s, entry, { at: \"done\" }, { ok: true, payload: undefined });\n } else {\n transition(s, entry, { at: \"awaiting-result\", ...carried(state) });\n }\n }\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst applyResponse = (s: Session, response: StatewireResponse) => {\n const entry = s.pending.find((p) => p.seq === response.seq);\n if (!entry) return;\n const state = entry.state;\n if (state.at === \"done\") return;\n switch (response.type) {\n case \"pending\":\n if (\n state.at === \"queued\" ||\n state.at === \"trying\" ||\n state.at === \"posted\"\n ) {\n transition(s, entry, { ...state, pendingMarked: true });\n }\n break;\n case \"accepted\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: true, payload: response.payload },\n );\n break;\n case \"rejected\":\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure:\n response.code === \"unknown-command\"\n ? { fate: \"unknown-command\", cause: \"server\" }\n : response.code === \"invalid-params\"\n ? {\n fate: \"invalid-params\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n }\n : {\n fate: \"rejected\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n ...(response.payload !== undefined && {\n payload: response.payload,\n }),\n },\n },\n );\n break;\n case \"crashed\":\n case \"result-unavailable\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: false, failure: { fate: response.type, cause: \"server\" } },\n );\n break;\n }\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n return applied.events;\n};\n\nconst publishIngested = (s: Session, events: unknown[]) => {\n publish(s);\n for (const event of events) s.emitEvent(event);\n wake(s);\n};\n\nconst handleSnapshot = (\n s: Session,\n { ops, lastSeq }: { ops: unknown[]; lastSeq: number },\n) => {\n if (lastSeq === -1 && s.attach !== \"detached\") {\n evict(s);\n s.transport?.connection.reconnect();\n publishIngested(s, ingestOps(s, ops));\n return;\n }\n const events = ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n // The attach snapshot is not the true covering envelope: acked entries\n // stay awaiting their result and re-send under seq dedup.\n noteAcked(s, lastSeq);\n spliceAcked(s, lastSeq, false);\n s.lastAck = lastSeq;\n for (const entry of s.pending) {\n const state = entry.state;\n if (entry.seq <= lastSeq) {\n if (state.at === \"awaiting-result\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n }\n if (state.at === \"awaiting-result\") void pump(s);\n } else if (state.at === \"posted\") {\n // Above the ack watermark a prior receipt no longer counts.\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n void pump(s);\n } else if (state.at === \"settled-live\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n void pump(s);\n }\n }\n publishIngested(s, events);\n};\n\nconst handleOps = (\n s: Session,\n {\n ops,\n res,\n ack,\n }: { ops: unknown[]; res?: StatewireResponse[]; ack?: number },\n) => {\n const horizon = ack === undefined ? s.lastAck : Math.max(s.lastAck, ack);\n if (res !== undefined) {\n for (const response of res) {\n if (response.seq > horizon) {\n protocolError(\"response seq beyond the ack\");\n }\n }\n }\n const events = ingestOps(s, ops);\n if (ack !== undefined) noteAcked(s, ack);\n if (res !== undefined) for (const response of res) applyResponse(s, response);\n if (ack !== undefined) {\n spliceAcked(s, ack, true);\n s.lastAck = Math.max(s.lastAck, ack);\n }\n publishIngested(s, events);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\nconst sendable = (p: Pending) =>\n p.state.at === \"queued\" ||\n p.state.at === \"trying\" ||\n ((p.state.at === \"awaiting-result\" || p.state.at === \"settled-live\") &&\n !p.state.posted);\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const entry = s.pending.find(sendable);\n if (!entry) return;\n const transport = s.transport;\n if (s.attach !== \"attached\" || !transport) {\n await waitForWake(s);\n continue;\n }\n const clientId = s.record.clientId;\n const epoch = s.attachEpoch;\n if (entry.state.at === \"queued\") {\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: entry.state.pendingMarked,\n failedAttempts: 0,\n });\n }\n let result: StatewireSubmitResult;\n try {\n result = await transport.send({\n seq: entry.seq,\n command: entry.command,\n });\n } catch (error) {\n if (!s.running || isFinished(s)) return;\n if (!s.pending.includes(entry)) continue;\n const state = inFlight(entry.state);\n transition(s, entry, {\n ...state,\n failedAttempts: state.failedAttempts + 1,\n });\n await sleepUntilWake(s, retryDelay(error, state.failedAttempts + 1));\n continue;\n }\n if (!s.running) return;\n if (!s.pending.includes(entry)) continue;\n if (result.type === \"receipt\") {\n const response = result.response;\n const state = entry.state;\n if (state.at === \"trying\" || state.at === \"posted\") {\n transition(s, entry, {\n at: \"posted\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n } else if (\n state.at === \"awaiting-result\" ||\n state.at === \"settled-live\"\n ) {\n transition(s, entry, { ...state, posted: true });\n }\n if (entry.inv) s.lifecycle?.onPosted?.(entry.inv);\n if (response !== undefined) {\n applyResponse(s, response);\n publish(s);\n }\n } else if (result.type === \"rejected\") {\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure: {\n fate: \"rejected\",\n cause: \"server\",\n message: result.message,\n },\n },\n );\n publish(s);\n } else if (result.type === \"stale-lease\") {\n if (epoch === s.attachEpoch) await waitForWake(s);\n } else if (result.type === \"unknown-client\") {\n // Eviction is never decided on the POST leg: re-attach first and let\n // the snapshot's lastSeq deliver the verdict. An answered attempt\n // proves the server never admitted this seq, so a clean entry requeues.\n if (epoch === s.attachEpoch) {\n const state = entry.state;\n if (state.at === \"trying\" && state.failedAttempts === 0) {\n transition(s, entry, {\n at: \"queued\",\n pendingMarked: state.pendingMarked,\n });\n }\n s.transport?.connection.reconnect();\n while (\n s.running &&\n !isFinished(s) &&\n epoch === s.attachEpoch &&\n clientId === s.record.clientId\n ) {\n await waitForWake(s);\n }\n }\n } else if (result.type === \"seq-gap\") {\n if (clientId === s.record.clientId) {\n console.error(\n `statewire: server reports a seq gap for seq ${entry.seq}; ` +\n \"treating the session as corrupt and rotating the client id\",\n );\n // Every attempt answered means the server never saw it; a thrown\n // attempt may have left the machine unanswered, so the verdict is \"lost\".\n const state = inFlight(entry.state);\n const failure: StatewireClient.SendFailure =\n state.at === \"awaiting-result\"\n ? { fate: \"result-unavailable\", cause: \"session-lost\" }\n : {\n fate: state.failedAttempts > 0 ? \"lost\" : \"aborted\",\n cause: \"session-lost\",\n };\n evict(s, { entry, failure });\n s.transport?.connection.reconnect();\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst sendCommand = (\n s: Session,\n command: StatewireWireCommand,\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const seq = s.record.nextSeq;\n s.record = { ...s.record, nextSeq: seq + 1 };\n s.store.save(s.record);\n const entry: Pending = {\n seq,\n command,\n inv:\n command.method === undefined\n ? null\n : {\n method: command.method,\n params: command.params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n state: { at: \"queued\", pendingMarked: false },\n resolve: () => {},\n reject: () => {},\n };\n const result = new Promise<unknown>((resolve, reject) => {\n entry.resolve = resolve;\n entry.reject = reject;\n });\n result.catch(() => {});\n s.pending.push(entry);\n if (entry.inv) s.lifecycle?.onStart(entry.inv);\n void pump(s);\n return result;\n};\n\nconst setContext = (\n s: Session,\n value: Record<string, unknown>,\n): Promise<unknown> => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n s.context = value;\n return s.send({ context: value });\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionStoreOptions,\n emit: (event: unknown) => void,\n): Session => {\n const [s] = useState(() => createSession(options));\n useEffect(() => {\n s.emitEvent = emit;\n return () => {\n s.emitEvent = () => {};\n };\n }, [s, emit]);\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session.js","names":["useEffect","useState","applyOps","StatewirePath","StatewireResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","retryDelay","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireRemoteProps","StatewireSubmitResult","StatewireWireCommand","PendingState","at","pendingMarked","failedAttempts","posted","Pending","seq","command","inv","state","resolve","payload","reject","error","Outcome","ok","failure","SendFailure","isResolved","isStateSettled","inFlight","Error","legalTransitions","Record","queued","trying","done","transition","s","Session","entry","next","outcome","prev","includes","settles","undefined","covers","lifecycle","onCovered","onSettle","type","pending","filter","p","carried","Exclude","newClientId","rejectedSend","Promise","result","catch","AttachState","StatewireSessionOptions","onCommandSessionLost","SessionLostCommand","record","ctx","recordPersisted","store","lastAck","context","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","notify","emitEvent","event","send","commands","params","createSession","options","sessionStore","stored","load","clientId","nextSeq","onSnapshot","args","handleSnapshot","onOps","handleOps","onFinish","handleFinish","snapshot","takeChanged","setOnChange","listener","sendCommand","method","setListener","publish","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","w","settleAll","detach","verdict","cause","evicted","unmount","gone","superseded","const","batch","detachFailure","fate","predecessorLost","some","claimReplays","handler","claimed","Set","decision","add","size","has","evict","replays","save","old","enqueue","noteAcked","onAck","spliceAcked","settleVoid","protocolError","message","applyResponse","response","find","code","ingestOps","ops","applied","changedPaths","events","publishIngested","lastSeq","connection","reconnect","pump","res","ack","horizon","Math","max","reason","isFinished","sendable","epoch","onPosted","console","start","stop","setTransport","ts","Date","now","onStart","setContext","value","Array","isArray","useStatewireSession","emit"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireResponse } from \"./connection\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { retryDelay } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireRemoteProps,\n StatewireSubmitResult,\n StatewireWireCommand,\n} from \"../transports/sse\";\n\n/**\n * queued/trying/posted: overlay live, result pending. awaiting-result: acked\n * (overlay gone) but the result rides a late res; `posted: false` re-sends.\n * settled-live: result settled but the overlay stays until the covering ack.\n * done: removed from the queue.\n */\ntype PendingState =\n | { at: \"queued\"; pendingMarked: boolean }\n | { at: \"trying\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"posted\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"awaiting-result\"; posted: boolean; failedAttempts: number }\n | { at: \"settled-live\"; posted: boolean; failedAttempts: number }\n | { at: \"done\" };\n\ntype Pending = {\n readonly seq: number;\n readonly command: StatewireWireCommand;\n /** null for context submissions — they carry no command lifecycle. */\n readonly inv: CommandsInvocation | null;\n state: PendingState;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype Outcome =\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure };\n\nconst isResolved = (state: PendingState) =>\n state.at === \"settled-live\" || state.at === \"done\";\n\nconst isStateSettled = (state: PendingState) =>\n state.at === \"awaiting-result\" || state.at === \"done\";\n\n// The pump admits an entry as trying before sending; only the pump itself\n// requeues, and a done entry is never still pending.\nconst inFlight = (state: PendingState) => {\n if (state.at === \"queued\" || state.at === \"done\") {\n throw new Error(`statewire: ${state.at} entry observed after a send`);\n }\n return state;\n};\n\nconst legalTransitions: Record<PendingState[\"at\"], PendingState[\"at\"][]> = {\n queued: [\"queued\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n // trying -> queued: an answered unknown-client proves the seq was never admitted.\n trying: [\n \"queued\",\n \"trying\",\n \"posted\",\n \"awaiting-result\",\n \"settled-live\",\n \"done\",\n ],\n posted: [\"posted\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n \"awaiting-result\": [\"awaiting-result\", \"done\"],\n \"settled-live\": [\"settled-live\", \"done\"],\n done: [],\n};\n\nconst transition = (\n s: Session,\n entry: Pending,\n next: PendingState,\n outcome?: Outcome,\n) => {\n const prev = entry.state;\n if (!legalTransitions[prev.at].includes(next.at)) {\n throw new Error(\n `statewire: illegal pending transition ${prev.at} -> ${next.at}`,\n );\n }\n const settles = !isResolved(prev) && isResolved(next);\n if (settles && outcome === undefined) {\n throw new Error(\n `statewire: settling transition ${prev.at} -> ${next.at} without an outcome`,\n );\n }\n const covers = !isStateSettled(prev) && isStateSettled(next);\n entry.state = next;\n if (covers && entry.inv) s.lifecycle?.onCovered?.(entry.inv);\n if (settles && outcome !== undefined) {\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n }\n if (next.at === \"done\") {\n s.pending = s.pending.filter((p) => p !== entry);\n }\n};\n\nconst carried = (\n state: Exclude<PendingState, { at: \"done\" }>,\n): { posted: boolean; failedAttempts: number } =>\n state.at === \"queued\"\n ? { posted: false, failedAttempts: 0 }\n : state.at === \"trying\" || state.at === \"posted\"\n ? { posted: state.at === \"posted\", failedAttempts: state.failedAttempts }\n : { posted: state.posted, failedAttempts: state.failedAttempts };\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = StatewireSessionStoreOptions & {\n onCommandSessionLost?:\n | ((command: StatewireClient.SessionLostCommand) => \"replay\" | void)\n | undefined;\n};\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n lastAck: number;\n context: Record<string, unknown> | undefined;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n onCommandSessionLost: StatewireSessionOptions[\"onCommandSessionLost\"];\n notify: () => void;\n emitEvent: (event: unknown) => void;\n send: (command: StatewireWireCommand) => Promise<unknown>;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst createSession = (options: StatewireSessionOptions): Session => {\n const store = options.sessionStore ?? createMemorySessionStore();\n const stored = store.load();\n const record = stored ?? { clientId: newClientId(), nextSeq: 1 };\n const s: Session = {\n record,\n ctx: {\n get clientId() {\n return s.record.clientId;\n },\n onSnapshot: (args) => handleSnapshot(s, args),\n onOps: (args) => handleOps(s, args),\n onFinish: (args) => handleFinish(s, args),\n },\n recordPersisted: stored !== null,\n store,\n lastAck: -1,\n context: undefined,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => s.canonical,\n takeChanged: () => {\n const changed = s.changed;\n s.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n s.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n onCommandSessionLost: options.onCommandSessionLost,\n notify: () => {},\n emitEvent: () => {},\n send: (command) => sendCommand(s, command),\n commands: createMethodProxy((method, params) => s.send({ method, params })),\n };\n bindCommandsHost(s.commands, {\n setListener: (listener) => {\n s.lifecycle = listener;\n },\n });\n return s;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst settleAll = (\n s: Session,\n detach: \"evicted\" | \"unmount\" | \"gone\" | \"superseded\",\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n const cause = (\n {\n evicted: \"session-lost\",\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n const state = entry.state;\n if (state.at === \"awaiting-result\")\n return { fate: \"result-unavailable\", cause };\n if (state.at === \"queued\") {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.state.at !== \"queued\",\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n const failure =\n entry === verdict?.entry ? verdict.failure : detachFailure(entry);\n transition(s, entry, { at: \"done\" }, { ok: false, failure });\n }\n};\n\n// Offers every unsettled real command for replay, in seq order. Not caught:\n// a throwing callback aborts the eviction before any entry settles; the\n// attach retry evicts again and re-offers.\nconst claimReplays = (s: Session): Pending[] => {\n const handler = s.onCommandSessionLost;\n if (!handler) return [];\n const claimed = new Set<Pending>();\n for (const entry of s.pending) {\n if (!entry.inv || isResolved(entry.state)) continue;\n const decision = handler({\n method: entry.inv.method,\n params: [...entry.inv.params],\n seq: entry.seq,\n });\n if (decision === \"replay\") claimed.add(entry);\n }\n if (claimed.size === 0) return [];\n s.pending = s.pending.filter((p) => !claimed.has(p));\n return [...claimed];\n};\n\nconst evict = (\n s: Session,\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n const replays = claimReplays(s);\n settleAll(s, \"evicted\", verdict);\n s.record = { clientId: newClientId(), nextSeq: 1 };\n s.store.save(s.record);\n s.attach = \"detached\";\n // The server dropped this client's record and its stored context with it:\n // the new session opens with a fresh full root replace.\n if (s.context !== undefined) void s.send({ context: s.context });\n for (const old of replays) {\n if (old.inv) s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.resolve, old.reject);\n }\n publish(s);\n wake(s);\n};\n\nconst noteAcked = (s: Session, seq: number) => {\n for (const entry of s.pending) {\n if (entry.seq > seq || isStateSettled(entry.state)) continue;\n if (entry.inv) s.lifecycle?.onAck(entry.inv);\n }\n};\n\nconst spliceAcked = (s: Session, seq: number, settleVoid: boolean) => {\n for (const entry of [...s.pending]) {\n const state = entry.state;\n if (entry.seq > seq || isStateSettled(state)) continue;\n if (state.at === \"settled-live\") {\n transition(s, entry, { at: \"done\" });\n } else if (settleVoid && !state.pendingMarked) {\n transition(s, entry, { at: \"done\" }, { ok: true, payload: undefined });\n } else {\n transition(s, entry, { at: \"awaiting-result\", ...carried(state) });\n }\n }\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst applyResponse = (s: Session, response: StatewireResponse) => {\n const entry = s.pending.find((p) => p.seq === response.seq);\n if (!entry) return;\n const state = entry.state;\n if (state.at === \"done\") return;\n switch (response.type) {\n case \"pending\":\n if (\n state.at === \"queued\" ||\n state.at === \"trying\" ||\n state.at === \"posted\"\n ) {\n transition(s, entry, { ...state, pendingMarked: true });\n }\n break;\n case \"accepted\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: true, payload: response.payload },\n );\n break;\n case \"rejected\":\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure:\n response.code === \"unknown-command\"\n ? { fate: \"unknown-command\", cause: \"server\" }\n : response.code === \"invalid-params\"\n ? {\n fate: \"invalid-params\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n }\n : {\n fate: \"rejected\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n ...(response.payload !== undefined && {\n payload: response.payload,\n }),\n },\n },\n );\n break;\n case \"crashed\":\n case \"result-unavailable\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: false, failure: { fate: response.type, cause: \"server\" } },\n );\n break;\n }\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n return applied.events;\n};\n\nconst publishIngested = (s: Session, events: unknown[]) => {\n publish(s);\n for (const event of events) s.emitEvent(event);\n wake(s);\n};\n\nconst handleSnapshot = (\n s: Session,\n { ops, lastSeq }: { ops: unknown[]; lastSeq: number },\n) => {\n if (lastSeq === -1 && s.attach !== \"detached\") {\n evict(s);\n s.transport?.connection.reconnect();\n publishIngested(s, ingestOps(s, ops));\n return;\n }\n const events = ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n // The attach snapshot is not the true covering envelope: acked entries\n // stay awaiting their result and re-send under seq dedup.\n noteAcked(s, lastSeq);\n spliceAcked(s, lastSeq, false);\n s.lastAck = lastSeq;\n for (const entry of s.pending) {\n const state = entry.state;\n if (entry.seq <= lastSeq) {\n if (state.at === \"awaiting-result\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n }\n if (state.at === \"awaiting-result\") void pump(s);\n } else if (state.at === \"posted\") {\n // Above the ack watermark a prior receipt no longer counts.\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n void pump(s);\n } else if (state.at === \"settled-live\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n void pump(s);\n }\n }\n publishIngested(s, events);\n};\n\nconst handleOps = (\n s: Session,\n {\n ops,\n res,\n ack,\n }: { ops: unknown[]; res?: StatewireResponse[]; ack?: number },\n) => {\n const horizon = ack === undefined ? s.lastAck : Math.max(s.lastAck, ack);\n if (res !== undefined) {\n for (const response of res) {\n if (response.seq > horizon) {\n protocolError(\"response seq beyond the ack\");\n }\n }\n }\n const events = ingestOps(s, ops);\n if (ack !== undefined) noteAcked(s, ack);\n if (res !== undefined) for (const response of res) applyResponse(s, response);\n if (ack !== undefined) {\n spliceAcked(s, ack, true);\n s.lastAck = Math.max(s.lastAck, ack);\n }\n publishIngested(s, events);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\nconst sendable = (p: Pending) =>\n p.state.at === \"queued\" ||\n p.state.at === \"trying\" ||\n ((p.state.at === \"awaiting-result\" || p.state.at === \"settled-live\") &&\n !p.state.posted);\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const entry = s.pending.find(sendable);\n if (!entry) return;\n const transport = s.transport;\n if (s.attach !== \"attached\" || !transport) {\n await waitForWake(s);\n continue;\n }\n const clientId = s.record.clientId;\n const epoch = s.attachEpoch;\n if (entry.state.at === \"queued\") {\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: entry.state.pendingMarked,\n failedAttempts: 0,\n });\n }\n let result: StatewireSubmitResult;\n try {\n result = await transport.send({\n seq: entry.seq,\n command: entry.command,\n });\n } catch (error) {\n if (!s.running || isFinished(s)) return;\n if (!s.pending.includes(entry)) continue;\n const state = inFlight(entry.state);\n transition(s, entry, {\n ...state,\n failedAttempts: state.failedAttempts + 1,\n });\n await sleepUntilWake(s, retryDelay(error, state.failedAttempts + 1));\n continue;\n }\n if (!s.running) return;\n if (!s.pending.includes(entry)) continue;\n if (result.type === \"receipt\") {\n const response = result.response;\n const state = entry.state;\n if (state.at === \"trying\" || state.at === \"posted\") {\n transition(s, entry, {\n at: \"posted\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n } else if (\n state.at === \"awaiting-result\" ||\n state.at === \"settled-live\"\n ) {\n transition(s, entry, { ...state, posted: true });\n }\n if (entry.inv) s.lifecycle?.onPosted?.(entry.inv);\n if (response !== undefined) {\n applyResponse(s, response);\n publish(s);\n }\n } else if (result.type === \"rejected\") {\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure: {\n fate: \"rejected\",\n cause: \"server\",\n message: result.message,\n },\n },\n );\n publish(s);\n } else if (result.type === \"stale-lease\") {\n if (epoch === s.attachEpoch) await waitForWake(s);\n } else if (result.type === \"unknown-client\") {\n // Eviction is never decided on the POST leg: re-attach first and let\n // the snapshot's lastSeq deliver the verdict. An answered attempt\n // proves the server never admitted this seq, so a clean entry requeues.\n if (epoch === s.attachEpoch) {\n const state = entry.state;\n if (state.at === \"trying\" && state.failedAttempts === 0) {\n transition(s, entry, {\n at: \"queued\",\n pendingMarked: state.pendingMarked,\n });\n }\n s.transport?.connection.reconnect();\n while (\n s.running &&\n !isFinished(s) &&\n epoch === s.attachEpoch &&\n clientId === s.record.clientId\n ) {\n await waitForWake(s);\n }\n }\n } else if (result.type === \"seq-gap\") {\n if (clientId === s.record.clientId) {\n console.error(\n `statewire: server reports a seq gap for seq ${entry.seq}; ` +\n \"treating the session as corrupt and rotating the client id\",\n );\n // Every attempt answered means the server never saw it; a thrown\n // attempt may have left the machine unanswered, so the verdict is \"lost\".\n const state = inFlight(entry.state);\n const failure: StatewireClient.SendFailure =\n state.at === \"awaiting-result\"\n ? { fate: \"result-unavailable\", cause: \"session-lost\" }\n : {\n fate: state.failedAttempts > 0 ? \"lost\" : \"aborted\",\n cause: \"session-lost\",\n };\n evict(s, { entry, failure });\n s.transport?.connection.reconnect();\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: StatewireWireCommand,\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n s.record = { ...s.record, nextSeq: seq + 1 };\n s.store.save(s.record);\n const entry: Pending = {\n seq,\n command,\n inv:\n command.method === undefined\n ? null\n : {\n method: command.method,\n params: command.params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n state: { at: \"queued\", pendingMarked: false },\n resolve,\n reject,\n };\n s.pending.push(entry);\n if (entry.inv) s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\nconst sendCommand = (\n s: Session,\n command: StatewireWireCommand,\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n enqueue(s, command, resolve, reject);\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (\n s: Session,\n value: Record<string, unknown>,\n): Promise<unknown> => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n s.context = value;\n return s.send({ context: value });\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n emit: (event: unknown) => void,\n): Session => {\n const [s] = useState(() => createSession(options));\n s.onCommandSessionLost = options.onCommandSessionLost;\n useEffect(() => {\n s.emitEvent = emit;\n return () => {\n s.emitEvent = () => {};\n };\n }, [s, emit]);\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
|
package/dist/host/host.d.ts
CHANGED
|
@@ -4,18 +4,6 @@ import { StatewireHost } from "../host.js";
|
|
|
4
4
|
/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
|
|
5
5
|
declare const getStateHost: (state: object) => StatewireHost.StateHost | undefined;
|
|
6
6
|
declare const useStatewireState: <TState>(init: () => TState) => readonly [TState, StatewireHost.StateCtl<TState>];
|
|
7
|
-
/**
|
|
8
|
-
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
9
|
-
* immediately: its return value resolves the execution, a throw/rejection
|
|
10
|
-
* rejects it; `ack()` releases the client's ack watermark before the executor
|
|
11
|
-
* finishes. `CommandExecution.withResolvers()` returns `[execution,
|
|
12
|
-
* { ack, resolve, reject }]` instead — settle them from your own spawned work.
|
|
13
|
-
*/
|
|
14
|
-
declare const CommandExecution: (<T = void>(executor: (resolvers: {
|
|
15
|
-
ack: () => void;
|
|
16
|
-
}) => T | void | PromiseLike<T | void>) => StatewireHost.Execution<T>) & {
|
|
17
|
-
withResolvers: <T = void>() => [StatewireHost.Execution<T>, StatewireHost.ExecutionResolvers<T>];
|
|
18
|
-
};
|
|
19
7
|
type CommandsInvocation = {
|
|
20
8
|
readonly method: string;
|
|
21
9
|
readonly params: readonly unknown[];
|
|
@@ -52,12 +40,12 @@ type CommandsHost = {
|
|
|
52
40
|
};
|
|
53
41
|
declare const getCommandsHost: (commands: object) => CommandsHost | undefined;
|
|
54
42
|
declare const bindCommandsHost: (commands: object, host: CommandsHost) => void;
|
|
55
|
-
declare const useStatewireCommands: <THandlers extends Statewire.Commands>(commands: (THandlers & ThisType<StatewireHost.
|
|
43
|
+
declare const useStatewireCommands: <THandlers extends Statewire.Commands>(commands: (THandlers & ThisType<StatewireHost.Ctx>) | undefined | null | false) => Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>;
|
|
56
44
|
type EmitHost = {
|
|
57
45
|
setSink: (sink: ((event: unknown) => void) | null) => void;
|
|
58
46
|
};
|
|
59
47
|
declare const getEmitHost: (emit: object | undefined) => EmitHost | undefined;
|
|
60
48
|
declare const useStatewireEmit: <TEvent>() => ((event: TEvent) => void);
|
|
61
49
|
//#endregion
|
|
62
|
-
export {
|
|
50
|
+
export { CommandsHost, CommandsInvocation, CommandsListener, CommandsOutcome, CommandsSubmission, EmitHost, bindCommandsHost, getCommandsHost, getEmitHost, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
|
|
63
51
|
//# sourceMappingURL=host.d.ts.map
|
package/dist/host/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;cAmBa,eACX,kBACC,cAAc;cAaJ,oBAAqB,QAChC,YAAY,qBACD,QAAQ,cAAc,SAAS
|
|
1
|
+
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;cAmBa,eACX,kBACC,cAAc;cAaJ,oBAAqB,QAChC,YAAY,qBACD,QAAQ,cAAc,SAAS;KAwEhC;WACD;WACA;WACA;WACA;WACA;;KAGC;EACN;EAAkB;;EAClB;EAAkB;;KAEZ;EACV,UAAU,KAAK;EACf,YAAY,KAAK;EACjB,QAAQ,KAAK;;EAEb,aAAa,KAAK;EAClB,WAAW,KAAK,oBAAoB,SAAS;;KAGnC;EACV;EACA;EACA;EACA,SAAS,cAAc;EACvB;EACA,YAAY,SAAS;;KAGX;EACV,cAAc,UAAU;EACxB,UAAU,MAAM,uBAAuB;;cAK5B,kBAAmB,qBAAmB;cAGtC,mBAAoB,kBAAkB,MAAM;cA4K5C,uBAAwB,kBAAkB,UAAU,UAC/D,WACK,YAAY,SAAS,cAAc,qCAIvC,UAAU,cAAc,cAAc,gBAAgB;KAqF7C;EACV,UAAU,QAAQ;;cAKP,cAAe,6BAA2B;cAG1C,mBAAoB,eAAa,OAAO"}
|
package/dist/host/host.js
CHANGED
|
@@ -69,64 +69,6 @@ const useStatewireState = (init) => {
|
|
|
69
69
|
}
|
|
70
70
|
return [value, cell.ctl];
|
|
71
71
|
};
|
|
72
|
-
const executionStates = /* @__PURE__ */ new WeakMap();
|
|
73
|
-
/**
|
|
74
|
-
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
75
|
-
* immediately: its return value resolves the execution, a throw/rejection
|
|
76
|
-
* rejects it; `ack()` releases the client's ack watermark before the executor
|
|
77
|
-
* finishes. `CommandExecution.withResolvers()` returns `[execution,
|
|
78
|
-
* { ack, resolve, reject }]` instead — settle them from your own spawned work.
|
|
79
|
-
*/
|
|
80
|
-
const CommandExecution = Object.assign((executor) => {
|
|
81
|
-
const state = {
|
|
82
|
-
acked: false,
|
|
83
|
-
settled: null,
|
|
84
|
-
bind: null
|
|
85
|
-
};
|
|
86
|
-
const execution = Object.freeze({});
|
|
87
|
-
executionStates.set(execution, state);
|
|
88
|
-
const settle = (settled) => {
|
|
89
|
-
if (state.settled) return;
|
|
90
|
-
state.settled = settled;
|
|
91
|
-
state.acked = true;
|
|
92
|
-
state.bind?.onSettle(settled);
|
|
93
|
-
};
|
|
94
|
-
const ack = () => {
|
|
95
|
-
if (state.acked) return;
|
|
96
|
-
state.acked = true;
|
|
97
|
-
state.bind?.onAck();
|
|
98
|
-
};
|
|
99
|
-
(async () => {
|
|
100
|
-
try {
|
|
101
|
-
settle({
|
|
102
|
-
type: "resolved",
|
|
103
|
-
value: await executor({ ack })
|
|
104
|
-
});
|
|
105
|
-
} catch (error) {
|
|
106
|
-
settle({
|
|
107
|
-
type: "rejected",
|
|
108
|
-
error
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
})();
|
|
112
|
-
return execution;
|
|
113
|
-
}, { withResolvers: () => {
|
|
114
|
-
let resolve;
|
|
115
|
-
let reject;
|
|
116
|
-
const promise = new Promise((res, rej) => {
|
|
117
|
-
resolve = res;
|
|
118
|
-
reject = rej;
|
|
119
|
-
});
|
|
120
|
-
let ack;
|
|
121
|
-
return [CommandExecution(({ ack: a }) => {
|
|
122
|
-
ack = a;
|
|
123
|
-
return promise;
|
|
124
|
-
}), {
|
|
125
|
-
ack: () => ack(),
|
|
126
|
-
resolve,
|
|
127
|
-
reject
|
|
128
|
-
}];
|
|
129
|
-
} });
|
|
130
72
|
const commandsHosts = /* @__PURE__ */ new WeakMap();
|
|
131
73
|
const getCommandsHost = (commands) => commandsHosts.get(commands);
|
|
132
74
|
const bindCommandsHost = (commands, host) => {
|
|
@@ -134,6 +76,9 @@ const bindCommandsHost = (commands, host) => {
|
|
|
134
76
|
};
|
|
135
77
|
const STALL_WARN_MS = 5e3;
|
|
136
78
|
const LOCAL_CALLER = Object.freeze({
|
|
79
|
+
get clientId() {
|
|
80
|
+
throw new Error("statewire: in-process dispatch has no client id");
|
|
81
|
+
},
|
|
137
82
|
context: null,
|
|
138
83
|
isConnected: true
|
|
139
84
|
});
|
|
@@ -170,13 +115,8 @@ const settleInvocation = (cell, inv, call, settled) => {
|
|
|
170
115
|
call.reject(error);
|
|
171
116
|
}
|
|
172
117
|
};
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
watchStall(cell);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
const call = cell.queue.shift();
|
|
179
|
-
if (!call) return;
|
|
118
|
+
const isThenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
119
|
+
const dispatch = (cell, call) => {
|
|
180
120
|
const handler = cell.handlers[call.method];
|
|
181
121
|
if (handler === void 0) {
|
|
182
122
|
const error = new StatewireSendError({
|
|
@@ -188,7 +128,6 @@ const pump = (cell) => {
|
|
|
188
128
|
error
|
|
189
129
|
});
|
|
190
130
|
call.reject(error);
|
|
191
|
-
pump(cell);
|
|
192
131
|
return;
|
|
193
132
|
}
|
|
194
133
|
const inv = {
|
|
@@ -200,35 +139,48 @@ const pump = (cell) => {
|
|
|
200
139
|
...call.origin !== void 0 && { origin: call.origin },
|
|
201
140
|
...call.origin?.clientId !== void 0 && { clientId: call.origin.clientId }
|
|
202
141
|
};
|
|
203
|
-
cell.running = true;
|
|
204
142
|
cell.listener?.onStart(inv);
|
|
205
143
|
cell.listener?.onPosted?.(inv);
|
|
206
144
|
cell.listener?.onCovered?.(inv);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
145
|
+
const ctx = {
|
|
146
|
+
caller: call.origin?.caller ?? LOCAL_CALLER,
|
|
147
|
+
ack: () => ackInvocation(cell, inv)
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
const value = handler.call(ctx, ...call.params);
|
|
151
|
+
if (isThenable(value)) value.then((resolved) => settleInvocation(cell, inv, call, {
|
|
152
|
+
type: "resolved",
|
|
153
|
+
value: resolved
|
|
154
|
+
}), (error) => settleInvocation(cell, inv, call, {
|
|
155
|
+
type: "rejected",
|
|
156
|
+
error
|
|
157
|
+
}));
|
|
158
|
+
else settleInvocation(cell, inv, call, {
|
|
159
|
+
type: "resolved",
|
|
160
|
+
value
|
|
161
|
+
});
|
|
162
|
+
} catch (error) {
|
|
163
|
+
settleInvocation(cell, inv, call, {
|
|
164
|
+
type: "rejected",
|
|
165
|
+
error
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const pump = (cell) => {
|
|
170
|
+
if (cell.running || !cell.handlers) {
|
|
171
|
+
watchStall(cell);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
cell.running = true;
|
|
175
|
+
try {
|
|
176
|
+
while (cell.handlers) {
|
|
177
|
+
const call = cell.queue.shift();
|
|
178
|
+
if (!call) break;
|
|
179
|
+
dispatch(cell, call);
|
|
230
180
|
}
|
|
231
|
-
}
|
|
181
|
+
} finally {
|
|
182
|
+
cell.running = false;
|
|
183
|
+
}
|
|
232
184
|
};
|
|
233
185
|
const watchStall = (cell) => {
|
|
234
186
|
if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;
|
|
@@ -330,6 +282,6 @@ function _temp() {
|
|
|
330
282
|
return emit;
|
|
331
283
|
}
|
|
332
284
|
//#endregion
|
|
333
|
-
export {
|
|
285
|
+
export { bindCommandsHost, getCommandsHost, getEmitHost, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
|
|
334
286
|
|
|
335
287
|
//# sourceMappingURL=host.js.map
|