statewire 0.24.1 → 0.24.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/dist/client/connection.d.ts +4 -0
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +9 -2
- package/dist/client/connection.js.map +1 -1
- package/dist/client/session.d.ts +2 -0
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +52 -11
- package/dist/client/session.js.map +1 -1
- package/dist/host/host.d.ts +1 -0
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +8 -0
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +3 -2
- package/dist/host/socket.js.map +1 -1
- package/dist/host.d.ts +13 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +2 -1
- package/dist/transports/sse.js.map +1 -1
- package/dist/wire/protocol.d.ts +6 -1
- package/dist/wire/protocol.d.ts.map +1 -1
- package/dist/wire/protocol.js +12 -4
- package/dist/wire/protocol.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +11 -0
- package/src/client/session.ts +85 -11
- package/src/host/host.ts +13 -0
- package/src/host/socket.ts +5 -2
- package/src/host.ts +14 -6
- package/src/transports/sse.ts +2 -0
- package/src/wire/protocol.ts +11 -3
package/src/host/host.ts
CHANGED
|
@@ -182,6 +182,7 @@ export type CommandsSubmission = {
|
|
|
182
182
|
clientId?: string;
|
|
183
183
|
caller?: StatewireHost.Caller;
|
|
184
184
|
context?: Statewire.Context;
|
|
185
|
+
version?: string;
|
|
185
186
|
attach?: StatewireHost.Attach;
|
|
186
187
|
onApplied?: () => void;
|
|
187
188
|
onSettle?: (outcome: CommandsOutcome) => void;
|
|
@@ -206,6 +207,7 @@ export const bindCommandsHost = (commands: object, host: CommandsHost) => {
|
|
|
206
207
|
|
|
207
208
|
type QueuedCall = {
|
|
208
209
|
context?: Statewire.Context;
|
|
210
|
+
version?: string | undefined;
|
|
209
211
|
method: string;
|
|
210
212
|
params: readonly unknown[];
|
|
211
213
|
ts: number;
|
|
@@ -223,6 +225,7 @@ type ActiveInvocation = CommandsInvocation & {
|
|
|
223
225
|
|
|
224
226
|
type CommandsCell = {
|
|
225
227
|
context: (() => Statewire.Context) | undefined;
|
|
228
|
+
version: string | undefined;
|
|
226
229
|
handlers: Record<string, (...args: never[]) => unknown> | null;
|
|
227
230
|
queue: QueuedCall[];
|
|
228
231
|
active: Map<ActiveInvocation, QueuedCall>;
|
|
@@ -364,9 +367,16 @@ const dispatch = (cell: CommandsCell, call: QueuedCall) => {
|
|
|
364
367
|
cell.listener?.onStart(inv);
|
|
365
368
|
cell.listener?.onPosted?.(inv);
|
|
366
369
|
const attach = call.origin?.attach;
|
|
370
|
+
const version =
|
|
371
|
+
call.origin !== undefined ? call.origin.version : call.version;
|
|
367
372
|
const ctx: StatewireHost.Ctx = {
|
|
368
373
|
caller: call.origin?.caller ?? LOCAL_CALLER,
|
|
369
374
|
context: call.context ?? call.origin?.context ?? Object.freeze({}),
|
|
375
|
+
get version() {
|
|
376
|
+
if (version === undefined)
|
|
377
|
+
throw new Error("statewire: in-process dispatch has no version");
|
|
378
|
+
return version;
|
|
379
|
+
},
|
|
370
380
|
get attach() {
|
|
371
381
|
if (attach === undefined)
|
|
372
382
|
throw new Error("statewire: in-process dispatch has no attach");
|
|
@@ -445,6 +455,7 @@ export const useStatewireCommands = <THandlers extends Statewire.Commands>(
|
|
|
445
455
|
const [built] = useState(() => {
|
|
446
456
|
const cell: CommandsCell = {
|
|
447
457
|
context: undefined,
|
|
458
|
+
version: undefined,
|
|
448
459
|
handlers: null,
|
|
449
460
|
queue: [],
|
|
450
461
|
active: new Map(),
|
|
@@ -465,6 +476,7 @@ export const useStatewireCommands = <THandlers extends Statewire.Commands>(
|
|
|
465
476
|
}
|
|
466
477
|
cell.queue.push({
|
|
467
478
|
context: captureContext(cell.context?.() ?? {}),
|
|
479
|
+
version: cell.version,
|
|
468
480
|
method,
|
|
469
481
|
params,
|
|
470
482
|
ts: Date.now(),
|
|
@@ -507,6 +519,7 @@ export const useStatewireCommands = <THandlers extends Statewire.Commands>(
|
|
|
507
519
|
});
|
|
508
520
|
|
|
509
521
|
built.cell.context = options?.context;
|
|
522
|
+
built.cell.version = options?.version;
|
|
510
523
|
useEffect(() => {
|
|
511
524
|
const { cell } = built;
|
|
512
525
|
cell.unmounted = false;
|
package/src/host/socket.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { MAX_CONTEXT_BYTES } from "../wire/limits";
|
|
|
3
3
|
import { encodeDocument } from "../document";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_PROTOCOL,
|
|
6
|
-
WIRE_VERSION,
|
|
7
6
|
DEFAULT_PROTOCOL_VERSION,
|
|
8
7
|
GAP_FILL_VERSION,
|
|
9
8
|
MIN_SERVED_WIRE_VERSION,
|
|
@@ -183,8 +182,9 @@ export type ParsedFrame = {
|
|
|
183
182
|
res?: { protocol: string; seq: number }[];
|
|
184
183
|
};
|
|
185
184
|
|
|
185
|
+
// This host serves through the gap-fill wire: it names no attach to retire.
|
|
186
186
|
const SERVED_WIRE: StatewireProtocol.Range = {
|
|
187
|
-
version:
|
|
187
|
+
version: GAP_FILL_VERSION,
|
|
188
188
|
minVersion: MIN_SERVED_WIRE_VERSION,
|
|
189
189
|
};
|
|
190
190
|
|
|
@@ -1147,6 +1147,9 @@ const admitCmd = (
|
|
|
1147
1147
|
caller: callerOf(h, rec, conn),
|
|
1148
1148
|
attach: conn.attach,
|
|
1149
1149
|
context: captureContext(conn.contexts.get(statement.protocol) ?? {}),
|
|
1150
|
+
version: conn.selection.protocols.find(
|
|
1151
|
+
(p) => p.name === statement.protocol,
|
|
1152
|
+
)!.version,
|
|
1150
1153
|
onApplied: () => {
|
|
1151
1154
|
if (adm.settled || adm.applied) return;
|
|
1152
1155
|
adm.applied = true;
|
package/src/host.ts
CHANGED
|
@@ -45,19 +45,27 @@ export namespace StatewireHost {
|
|
|
45
45
|
setOnDocuments: (listener: (() => void) | null) => void;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
/** What `this.context` and `this.version` read for a call made on the proxy (in-process dispatch). */
|
|
49
|
+
export type CommandsOptions = {
|
|
50
|
+
context?: () => Statewire.Context;
|
|
51
|
+
version?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
48
54
|
/**
|
|
49
55
|
* Per-invocation handle bound as `this` of a command handler: `caller` is
|
|
50
56
|
* the submitting client, `attach` the admitting attach's document view
|
|
51
|
-
* (in-process dispatch has none — reading it throws), `
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
57
|
+
* (in-process dispatch has none — reading it throws), `version` the
|
|
58
|
+
* command protocol's version that attach negotiated (in-process dispatch
|
|
59
|
+
* reads `CommandsOptions.version`; without one, reading it throws),
|
|
60
|
+
* `applied()` states the broadcast state now contains the command's effects
|
|
61
|
+
* — the client drops its optimistic overlay — before the handler settles
|
|
62
|
+
* (idempotent, a no-op after settle). The command settles when the
|
|
63
|
+
* handler's returned promise does.
|
|
55
64
|
*/
|
|
56
|
-
export type CommandsOptions = { context?: () => Statewire.Context };
|
|
57
|
-
|
|
58
65
|
export type Ctx = {
|
|
59
66
|
readonly caller: Caller;
|
|
60
67
|
readonly context: Statewire.Context;
|
|
68
|
+
readonly version: string;
|
|
61
69
|
readonly attach: Attach;
|
|
62
70
|
readonly signal: AbortSignal;
|
|
63
71
|
readonly applied: () => void;
|
package/src/transports/sse.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ResourceElement } from "@assistant-ui/tap";
|
|
|
4
4
|
import { createSseParser } from "./sse-parser";
|
|
5
5
|
import { timing } from "../client/timing";
|
|
6
6
|
import {
|
|
7
|
+
replacing,
|
|
7
8
|
settleFin,
|
|
8
9
|
classify,
|
|
9
10
|
handleEnvelope,
|
|
@@ -149,6 +150,7 @@ const attemptStream = async (
|
|
|
149
150
|
if (!headers.has(name)) headers.set(name, value);
|
|
150
151
|
}
|
|
151
152
|
c.negotiation = parseNegotiation(headers);
|
|
153
|
+
replacing(c, headers);
|
|
152
154
|
c.authStale = false;
|
|
153
155
|
const lanes = flushTapSync(() => c.ctx.takeAttachLanes());
|
|
154
156
|
const body = lanes === null ? undefined : JSON.stringify(lanes);
|
package/src/wire/protocol.ts
CHANGED
|
@@ -18,9 +18,17 @@ export namespace StatewireProtocol {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export const WIRE_VERSION = "2026-11-
|
|
21
|
+
export const WIRE_VERSION = "2026-11-02";
|
|
22
22
|
/** Attach carries a results cursor and gets its verdicts gap-filled; a re-sent seq at or below the watermark is a protocol error. */
|
|
23
23
|
export const GAP_FILL_VERSION = "2026-11-01";
|
|
24
|
+
/** Every attach of a connection names its attacher; a host retires the attacher's earlier attaches before the hello. */
|
|
25
|
+
export const FENCE_VERSION = "2026-11-02";
|
|
26
|
+
/** The wire a client offers by default: the gap-fill contract and newer. */
|
|
27
|
+
export const CLIENT_WIRE: StatewireProtocol.Range = {
|
|
28
|
+
version: WIRE_VERSION,
|
|
29
|
+
minVersion: GAP_FILL_VERSION,
|
|
30
|
+
};
|
|
31
|
+
export const ATTACHER_HEADER = "Statewire-Attacher";
|
|
24
32
|
/** The oldest wire contract a host serves by default (re-sent seqs re-emit their verdicts). */
|
|
25
33
|
export const MIN_SERVED_WIRE_VERSION = "2026-09-30";
|
|
26
34
|
export const DEFAULT_PROTOCOL_VERSION = "2026-09-30";
|
|
@@ -81,7 +89,7 @@ export const negotiationHeaders = (
|
|
|
81
89
|
if (offers !== "*" && !offers.length)
|
|
82
90
|
invalid("protocol list must not be empty");
|
|
83
91
|
return {
|
|
84
|
-
[VERSION_HEADER]: suffix(options.wireVersion ??
|
|
92
|
+
[VERSION_HEADER]: suffix(options.wireVersion ?? CLIENT_WIRE),
|
|
85
93
|
[PROTOCOL_HEADER]:
|
|
86
94
|
offers === "*"
|
|
87
95
|
? "*"
|
|
@@ -201,7 +209,7 @@ export const validateSelection = (
|
|
|
201
209
|
return invalid("missing negotiation selection");
|
|
202
210
|
const syn = value as Record<string, unknown>;
|
|
203
211
|
const version = validateVersion(syn.version);
|
|
204
|
-
if (!inRange(version, options.wireVersion ??
|
|
212
|
+
if (!inRange(version, options.wireVersion ?? CLIENT_WIRE))
|
|
205
213
|
invalid("selected wire version is outside offer");
|
|
206
214
|
if (!Array.isArray(syn.protocols))
|
|
207
215
|
return invalid("syn.protocols must be an array");
|