statewire 0.19.4 → 0.21.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.
Files changed (73) hide show
  1. package/README.md +15 -1
  2. package/dist/client/connection.d.ts.map +1 -1
  3. package/dist/client/connection.js +18 -4
  4. package/dist/client/connection.js.map +1 -1
  5. package/dist/client/session.d.ts +10 -1
  6. package/dist/client/session.d.ts.map +1 -1
  7. package/dist/client/session.js +92 -27
  8. package/dist/client/session.js.map +1 -1
  9. package/dist/client/sessionStore.d.ts +2 -0
  10. package/dist/client/sessionStore.d.ts.map +1 -1
  11. package/dist/client/sessionStore.js.map +1 -1
  12. package/dist/host/host.d.ts +2 -1
  13. package/dist/host/host.d.ts.map +1 -1
  14. package/dist/host/host.js +20 -12
  15. package/dist/host/host.js.map +1 -1
  16. package/dist/host/http.d.ts.map +1 -1
  17. package/dist/host/http.js +14 -9
  18. package/dist/host/http.js.map +1 -1
  19. package/dist/host/socket.d.ts +14 -3
  20. package/dist/host/socket.d.ts.map +1 -1
  21. package/dist/host/socket.js +162 -20
  22. package/dist/host/socket.js.map +1 -1
  23. package/dist/host-internal.d.ts +5 -2
  24. package/dist/host-internal.d.ts.map +1 -1
  25. package/dist/host.d.ts +6 -0
  26. package/dist/host.d.ts.map +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/statewire.d.ts +3 -0
  29. package/dist/statewire.d.ts.map +1 -1
  30. package/dist/statewire.js +23 -5
  31. package/dist/statewire.js.map +1 -1
  32. package/dist/transports/remote.d.ts.map +1 -1
  33. package/dist/transports/remote.js +3 -2
  34. package/dist/transports/remote.js.map +1 -1
  35. package/dist/transports/sse.d.ts +5 -0
  36. package/dist/transports/sse.d.ts.map +1 -1
  37. package/dist/transports/sse.js +12 -0
  38. package/dist/transports/sse.js.map +1 -1
  39. package/dist/transports/testing.d.ts +2 -0
  40. package/dist/transports/testing.d.ts.map +1 -1
  41. package/dist/transports/testing.js +11 -4
  42. package/dist/transports/testing.js.map +1 -1
  43. package/dist/types.d.ts +4 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js.map +1 -1
  46. package/dist/wire/context.d.ts +14 -0
  47. package/dist/wire/context.d.ts.map +1 -0
  48. package/dist/wire/context.js +31 -0
  49. package/dist/wire/context.js.map +1 -0
  50. package/dist/wire/limits.d.ts +2 -1
  51. package/dist/wire/limits.d.ts.map +1 -1
  52. package/dist/wire/limits.js +2 -1
  53. package/dist/wire/limits.js.map +1 -1
  54. package/dist/wire/protocol.d.ts +1 -1
  55. package/dist/wire/protocol.js +4 -4
  56. package/dist/wire/protocol.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/client/connection.ts +16 -4
  59. package/src/client/session.ts +128 -19
  60. package/src/client/sessionStore.ts +5 -1
  61. package/src/host/host.ts +18 -3
  62. package/src/host/http.ts +37 -5
  63. package/src/host/socket.ts +232 -13
  64. package/src/host-internal.ts +4 -0
  65. package/src/host.ts +5 -0
  66. package/src/statewire.ts +34 -3
  67. package/src/transports/remote.ts +3 -0
  68. package/src/transports/sse.ts +19 -0
  69. package/src/transports/testing.ts +14 -3
  70. package/src/types.ts +12 -0
  71. package/src/wire/context.ts +40 -0
  72. package/src/wire/limits.ts +2 -0
  73. package/src/wire/protocol.ts +1 -1
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["StatewireDocuments","StatewireHost","StatewireProtocol","StatewireClient","StatewirePath","Statewire","Commands","Record","args","Command","TCommands","method","K","params","Parameters","CommandsProxy","Promise","Awaited","ReturnType","CommandUpdate","protocol","seq","status","result","failure","SendFailure","StateSource","snapshot","takeChanged","setOnChange","listener","Op","op","path","value","Changes","isChangedAt","getChangedKeys","state","TState","documents","Source","selection","Selection","invoke","commands","connection","Connection","source","isBusy","work","isIdle","attaching","attach","Attach","detached","failures","list","FailedCommand","retry","entry","discard","describe","fate","code","message","cause","StatewireSendError","Error","constructor","name"],"sources":["../src/types.ts"],"sourcesContent":["import type { StatewireDocuments } from \"./wire/documents\";\nimport type { StatewireHost } from \"./host\";\nimport type { StatewireProtocol } from \"./wire/protocol\";\nimport type { StatewireClient } from \"./statewire\";\nimport type { StatewirePath } from \"./wire/ops\";\n\nexport namespace Statewire {\n export type Commands = Record<string, (...args: never[]) => unknown>;\n\n export type Command<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: { method: K; params: Parameters<TCommands[K]> };\n }[keyof TCommands];\n\n export type CommandsProxy<TCommands extends Commands> = {\n [K in keyof TCommands]: (\n ...args: Parameters<TCommands[K]>\n ) => Promise<Awaited<ReturnType<TCommands[K]>>>;\n };\n\n export type CommandUpdate<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: {\n readonly protocol?: string;\n method: K;\n params: Parameters<TCommands[K]>;\n seq: number;\n status: \"queued\" | \"posted\" | \"applied\" | \"settled\";\n result?: unknown;\n failure?: StatewireClient.SendFailure;\n };\n }[keyof TCommands];\n\n /** Push-based canonical state feed a statewire hands to the client core. */\n export type StateSource = {\n snapshot: () => unknown;\n /** Drain the paths changed since the last take (`[]` entry = whole state). */\n takeChanged: () => StatewirePath[];\n /** Synchronous, per change; the consumer owns coalescing. */\n setOnChange: (listener: (() => void) | null) => void;\n };\n\n export type Op =\n | { op: \"replace\"; path: StatewirePath; value: unknown }\n | { op: \"add\"; path: StatewirePath; value: unknown }\n | { op: \"remove\"; path: StatewirePath };\n\n export type Changes = {\n isChangedAt(path: readonly (string | number)[]): boolean;\n getChangedKeys(path: readonly (string | number)[]): string[];\n };\n}\n\nexport interface Statewire<\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n> {\n state: TState;\n documents?: StatewireDocuments.Source;\n selection?: StatewireProtocol.Selection | undefined;\n invoke?: (\n protocol: string,\n method: string,\n params: readonly unknown[],\n ) => Promise<unknown>;\n commands: Statewire.CommandsProxy<TCommands>;\n connection?: StatewireClient.Connection;\n source?: Statewire.StateSource;\n /** Negation of the server's broadcast idle level; absent means always busy. */\n isBusy?: boolean;\n work?: { readonly isIdle: boolean };\n /** Called by the serving host once an attach is registered, before its hello: documents mounted here ride the hello. */\n attaching?: (attach: StatewireHost.Attach) => void;\n /** Called by the serving host when an attach ends; its ephemeral documents and includes are gone. */\n detached?: (attach: StatewireHost.Attach) => void;\n /** The retained failed-send lane; present on transports with a durable session. */\n failures?: {\n list: () => readonly StatewireClient.FailedCommand[];\n retry: (entry: StatewireClient.FailedCommand) => Promise<unknown>;\n discard: (entry: StatewireClient.FailedCommand) => void;\n };\n}\n\nconst describe = (failure: StatewireClient.SendFailure): string => {\n switch (failure.fate) {\n case \"rejected\":\n return `statewire: command rejected${failure.code ? ` (${failure.code})` : \"\"}${failure.message ? `: ${failure.message}` : \"\"}`;\n case \"aborted\":\n return `statewire: command aborted (${failure.cause})`;\n case \"lost\":\n return `statewire: command lost (${failure.cause})`;\n case \"failed\":\n return failure.message\n ? `statewire: command failed: ${failure.message}`\n : \"statewire: command failed\";\n case \"crashed\":\n return \"statewire: command crashed\";\n case \"expired\":\n return `statewire: command result expired (${failure.cause})`;\n }\n};\n\nexport class StatewireSendError extends Error {\n readonly failure: StatewireClient.SendFailure;\n\n constructor(failure: StatewireClient.SendFailure) {\n super(describe(failure));\n this.name = \"StatewireSendError\";\n this.failure = failure;\n }\n}\n"],"mappings":";AAiFA,MAAM8D,YAAYtC,YAAiD;CACjE,QAAQA,QAAQuC,MAAhB;EACE,KAAK,YACH,OAAO,8BAA8BvC,QAAQwC,OAAO,KAAKxC,QAAQwC,KAAI,KAAM,KAAKxC,QAAQyC,UAAU,KAAKzC,QAAQyC,YAAY;EAC7H,KAAK,WACH,OAAO,+BAA+BzC,QAAQ0C,MAAK;EACrD,KAAK,QACH,OAAO,4BAA4B1C,QAAQ0C,MAAK;EAClD,KAAK,UACH,OAAO1C,QAAQyC,UACX,8BAA8BzC,QAAQyC,YACtC;EACN,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO,sCAAsCzC,QAAQ0C,MAAK;CAC9D;AACF;AAEA,IAAaC,qBAAb,cAAwCC,MAAM;CAC5C;CAEAC,YAAY7C,SAAsC;EAChD,MAAMsC,SAAStC,OAAO,CAAC;EACvB,KAAK8C,OAAO;EACZ,KAAK9C,UAAUA;CACjB;AACF"}
1
+ {"version":3,"file":"types.js","names":["StatewireDocuments","StatewireHost","StatewireProtocol","StatewireClient","StatewirePath","Statewire","Context","Readonly","Record","Commands","args","Command","TCommands","method","K","params","Parameters","CommandsProxy","Promise","Awaited","ReturnType","CommandUpdate","protocol","seq","status","result","failure","SendFailure","StateSource","snapshot","takeChanged","setOnChange","listener","Op","op","path","value","Changes","isChangedAt","getChangedKeys","state","TState","documents","Source","selection","Selection","setContext","validateContext","attach","Attach","contextChanged","invoke","commands","connection","Connection","source","isBusy","work","isIdle","attaching","detached","failures","list","FailedCommand","retry","entry","discard","describe","fate","code","message","cause","StatewireSendError","Error","constructor","name"],"sources":["../src/types.ts"],"sourcesContent":["import type { StatewireDocuments } from \"./wire/documents\";\nimport type { StatewireHost } from \"./host\";\nimport type { StatewireProtocol } from \"./wire/protocol\";\nimport type { StatewireClient } from \"./statewire\";\nimport type { StatewirePath } from \"./wire/ops\";\n\nexport namespace Statewire {\n export type Context = Readonly<Record<string, unknown>>;\n export type Commands = Record<string, (...args: never[]) => unknown>;\n\n export type Command<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: { method: K; params: Parameters<TCommands[K]> };\n }[keyof TCommands];\n\n export type CommandsProxy<TCommands extends Commands> = {\n [K in keyof TCommands]: (\n ...args: Parameters<TCommands[K]>\n ) => Promise<Awaited<ReturnType<TCommands[K]>>>;\n };\n\n export type CommandUpdate<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: {\n readonly protocol?: string;\n method: K;\n params: Parameters<TCommands[K]>;\n seq: number;\n status: \"queued\" | \"posted\" | \"applied\" | \"settled\";\n result?: unknown;\n failure?: StatewireClient.SendFailure;\n };\n }[keyof TCommands];\n\n /** Push-based canonical state feed a statewire hands to the client core. */\n export type StateSource = {\n snapshot: () => unknown;\n /** Drain the paths changed since the last take (`[]` entry = whole state). */\n takeChanged: () => StatewirePath[];\n /** Synchronous, per change; the consumer owns coalescing. */\n setOnChange: (listener: (() => void) | null) => void;\n };\n\n export type Op =\n | { op: \"replace\"; path: StatewirePath; value: unknown }\n | { op: \"add\"; path: StatewirePath; value: unknown }\n | { op: \"remove\"; path: StatewirePath };\n\n export type Changes = {\n isChangedAt(path: readonly (string | number)[]): boolean;\n getChangedKeys(path: readonly (string | number)[]): string[];\n };\n}\n\nexport interface Statewire<\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n> {\n state: TState;\n documents?: StatewireDocuments.Source;\n selection?: StatewireProtocol.Selection | undefined;\n setContext?: (protocol: string, value: Statewire.Context) => void;\n validateContext?: (\n protocol: string,\n value: Statewire.Context,\n attach: StatewireHost.Attach,\n ) => void;\n contextChanged?: (\n protocol: string,\n value: Statewire.Context,\n attach: StatewireHost.Attach,\n ) => void;\n invoke?: (\n protocol: string,\n method: string,\n params: readonly unknown[],\n ) => Promise<unknown>;\n commands: Statewire.CommandsProxy<TCommands>;\n connection?: StatewireClient.Connection;\n source?: Statewire.StateSource;\n /** Negation of the server's broadcast idle level; absent means always busy. */\n isBusy?: boolean;\n work?: { readonly isIdle: boolean };\n /** Called by the serving host once an attach is registered, before its hello: documents mounted here ride the hello. */\n attaching?: (attach: StatewireHost.Attach) => void;\n /** Called by the serving host when an attach ends; its ephemeral documents and includes are gone. */\n detached?: (attach: StatewireHost.Attach) => void;\n /** The retained failed-send lane; present on transports with a durable session. */\n failures?: {\n list: () => readonly StatewireClient.FailedCommand[];\n retry: (entry: StatewireClient.FailedCommand) => Promise<unknown>;\n discard: (entry: StatewireClient.FailedCommand) => void;\n };\n}\n\nconst describe = (failure: StatewireClient.SendFailure): string => {\n switch (failure.fate) {\n case \"rejected\":\n return `statewire: command rejected${failure.code ? ` (${failure.code})` : \"\"}${failure.message ? `: ${failure.message}` : \"\"}`;\n case \"aborted\":\n return `statewire: command aborted (${failure.cause})`;\n case \"lost\":\n return `statewire: command lost (${failure.cause})`;\n case \"failed\":\n return failure.message\n ? `statewire: command failed: ${failure.message}`\n : \"statewire: command failed\";\n case \"crashed\":\n return \"statewire: command crashed\";\n case \"expired\":\n return `statewire: command result expired (${failure.cause})`;\n }\n};\n\nexport class StatewireSendError extends Error {\n readonly failure: StatewireClient.SendFailure;\n\n constructor(failure: StatewireClient.SendFailure) {\n super(describe(failure));\n this.name = \"StatewireSendError\";\n this.failure = failure;\n }\n}\n"],"mappings":";AA6FA,MAAMmE,YAAYzC,YAAiD;CACjE,QAAQA,QAAQ0C,MAAhB;EACE,KAAK,YACH,OAAO,8BAA8B1C,QAAQ2C,OAAO,KAAK3C,QAAQ2C,KAAI,KAAM,KAAK3C,QAAQ4C,UAAU,KAAK5C,QAAQ4C,YAAY;EAC7H,KAAK,WACH,OAAO,+BAA+B5C,QAAQ6C,MAAK;EACrD,KAAK,QACH,OAAO,4BAA4B7C,QAAQ6C,MAAK;EAClD,KAAK,UACH,OAAO7C,QAAQ4C,UACX,8BAA8B5C,QAAQ4C,YACtC;EACN,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO,sCAAsC5C,QAAQ6C,MAAK;CAC9D;AACF;AAEA,IAAaC,qBAAb,cAAwCC,MAAM;CAC5C;CAEAC,YAAYhD,SAAsC;EAChD,MAAMyC,SAASzC,OAAO,CAAC;EACvB,KAAKiD,OAAO;EACZ,KAAKjD,UAAUA;CACjB;AACF"}
@@ -0,0 +1,14 @@
1
+ import { StatewireDocument } from "../document.js";
2
+ import { Statewire } from "../types.js";
3
+ import "../index.js";
4
+ //#region src/wire/context.d.ts
5
+ declare const contextValue: (encoded: unknown) => Statewire.Context;
6
+ declare const encodeContext: (protocol: string, value: Statewire.Context) => {
7
+ value: StatewireDocument.Value;
8
+ meta?: StatewireDocument.MetaEntry[];
9
+ protocol: string;
10
+ };
11
+ declare const captureContext: (value: Statewire.Context) => Statewire.Context;
12
+ //#endregion
13
+ export { captureContext, contextValue, encodeContext };
14
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","names":[],"sources":["../../src/wire/context.ts"],"mappings":";;;;cAWa,eAAgB,qBAAmB,UAAU;cAa7C,gBAAiB,kBAAkB,OAAO,UAAU;;;;;cAcpD,iBAAkB,OAAO,UAAU,YAAU,UAAU"}
@@ -0,0 +1,31 @@
1
+ import { decodeDocument, encodeDocument } from "../document.js";
2
+ import "./limits.js";
3
+ import { protocolToken } from "./protocol.js";
4
+ //#region src/wire/context.ts
5
+ const freeze = (value) => {
6
+ if (typeof value !== "object" || value === null) return;
7
+ for (const child of Object.values(value)) freeze(child);
8
+ Object.freeze(value);
9
+ };
10
+ const contextValue = (encoded) => {
11
+ const value = decodeDocument(encoded);
12
+ if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date) throw new TypeError("statewire: context root must be an object");
13
+ freeze(value);
14
+ return value;
15
+ };
16
+ const encodeContext = (protocol, value) => {
17
+ if (!protocolToken.test(protocol)) throw new TypeError("statewire: invalid context protocol");
18
+ const encoded = encodeDocument(value);
19
+ contextValue(encoded);
20
+ const entry = {
21
+ protocol,
22
+ ...encoded
23
+ };
24
+ if (new TextEncoder().encode(JSON.stringify(entry)).byteLength > 65536) throw new TypeError("statewire: context entry too large");
25
+ return entry;
26
+ };
27
+ const captureContext = (value) => contextValue(encodeDocument(value));
28
+ //#endregion
29
+ export { captureContext, contextValue, encodeContext };
30
+
31
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","names":["decodeDocument","encodeDocument","Statewire","MAX_CONTEXT_BYTES","protocolToken","freeze","value","child","Object","values","contextValue","encoded","Context","Array","isArray","Date","TypeError","encodeContext","protocol","test","entry","TextEncoder","encode","JSON","stringify","byteLength","captureContext"],"sources":["../../src/wire/context.ts"],"sourcesContent":["import { decodeDocument, encodeDocument } from \"../document\";\nimport type { Statewire } from \"../types\";\nimport { MAX_CONTEXT_BYTES } from \"./limits\";\nimport { protocolToken } from \"./protocol\";\n\nconst freeze = (value: unknown): void => {\n if (typeof value !== \"object\" || value === null) return;\n for (const child of Object.values(value)) freeze(child);\n Object.freeze(value);\n};\n\nexport const contextValue = (encoded: unknown): Statewire.Context => {\n const value = decodeDocument(encoded);\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value) ||\n value instanceof Date\n )\n throw new TypeError(\"statewire: context root must be an object\");\n freeze(value);\n return value as Statewire.Context;\n};\n\nexport const encodeContext = (protocol: string, value: Statewire.Context) => {\n if (!protocolToken.test(protocol))\n throw new TypeError(\"statewire: invalid context protocol\");\n const encoded = encodeDocument(value);\n contextValue(encoded);\n const entry = { protocol, ...encoded };\n if (\n new TextEncoder().encode(JSON.stringify(entry)).byteLength >\n MAX_CONTEXT_BYTES\n )\n throw new TypeError(\"statewire: context entry too large\");\n return entry;\n};\n\nexport const captureContext = (value: Statewire.Context): Statewire.Context =>\n contextValue(encodeDocument(value));\n"],"mappings":";;;;AAKA,MAAMK,UAAUC,UAAyB;CACvC,IAAI,OAAOA,UAAU,YAAYA,UAAU,MAAM;CACjD,KAAK,MAAMC,SAASC,OAAOC,OAAOH,KAAK,GAAGD,OAAOE,KAAK;CACtDC,OAAOH,OAAOC,KAAK;AACrB;AAEA,MAAaI,gBAAgBC,YAAwC;CACnE,MAAML,QAAQN,eAAeW,OAAO;CACpC,IACE,OAAOL,UAAU,YACjBA,UAAU,QACVO,MAAMC,QAAQR,KAAK,KACnBA,iBAAiBS,MAEjB,MAAM,IAAIC,UAAU,2CAA2C;CACjEX,OAAOC,KAAK;CACZ,OAAOA;AACT;AAEA,MAAaW,iBAAiBC,UAAkBZ,UAA6B;CAC3E,IAAI,CAACF,cAAce,KAAKD,QAAQ,GAC9B,MAAM,IAAIF,UAAU,qCAAqC;CAC3D,MAAML,UAAUV,eAAeK,KAAK;CACpCI,aAAaC,OAAO;CACpB,MAAMS,QAAQ;EAAEF;EAAU,GAAGP;CAAQ;CACrC,IACE,IAAIU,YAAY,CAAC,CAACC,OAAOC,KAAKC,UAAUJ,KAAK,CAAC,CAAC,CAACK,aAAAA,OAGhD,MAAM,IAAIT,UAAU,oCAAoC;CAC1D,OAAOI;AACT;AAEA,MAAaM,kBAAkBpB,UAC7BI,aAAaT,eAAeK,KAAK,CAAC"}
@@ -5,6 +5,7 @@ declare const MAX_FRAME_BODY_BYTES: number;
5
5
  declare const MAX_EPHEMERAL_DOCUMENT_BYTES: number;
6
6
  declare const MAX_INFLIGHT_COMMANDS_PER_HOST = 1024;
7
7
  declare const COMMAND_CAPACITY_RETRY_MS = 1000;
8
+ declare const MAX_CONTEXT_BYTES: number;
8
9
  //#endregion
9
- export { COMMAND_CAPACITY_RETRY_MS, MAX_COMMANDS_PER_FRAME, MAX_EPHEMERAL_DOCUMENT_BYTES, MAX_FRAME_BODY_BYTES, MAX_INFLIGHT_COMMANDS_PER_CLIENT, MAX_INFLIGHT_COMMANDS_PER_HOST };
10
+ export { COMMAND_CAPACITY_RETRY_MS, MAX_COMMANDS_PER_FRAME, MAX_CONTEXT_BYTES, MAX_EPHEMERAL_DOCUMENT_BYTES, MAX_FRAME_BODY_BYTES, MAX_INFLIGHT_COMMANDS_PER_CLIENT, MAX_INFLIGHT_COMMANDS_PER_HOST };
10
11
  //# sourceMappingURL=limits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","names":[],"sources":["../../src/wire/limits.ts"],"mappings":";cAAa;cACA;cACA;cACA;cACA;cACA"}
1
+ {"version":3,"file":"limits.d.ts","names":[],"sources":["../../src/wire/limits.ts"],"mappings":";cAAa;cACA;cACA;cACA;cACA;cACA;cAEA"}
@@ -5,7 +5,8 @@ const MAX_FRAME_BODY_BYTES = 8388608;
5
5
  const MAX_EPHEMERAL_DOCUMENT_BYTES = 65536;
6
6
  const MAX_INFLIGHT_COMMANDS_PER_HOST = 1024;
7
7
  const COMMAND_CAPACITY_RETRY_MS = 1e3;
8
+ const MAX_CONTEXT_BYTES = 65536;
8
9
  //#endregion
9
- export { COMMAND_CAPACITY_RETRY_MS, MAX_COMMANDS_PER_FRAME, MAX_EPHEMERAL_DOCUMENT_BYTES, MAX_FRAME_BODY_BYTES, MAX_INFLIGHT_COMMANDS_PER_CLIENT, MAX_INFLIGHT_COMMANDS_PER_HOST };
10
+ export { COMMAND_CAPACITY_RETRY_MS, MAX_COMMANDS_PER_FRAME, MAX_CONTEXT_BYTES, MAX_EPHEMERAL_DOCUMENT_BYTES, MAX_FRAME_BODY_BYTES, MAX_INFLIGHT_COMMANDS_PER_CLIENT, MAX_INFLIGHT_COMMANDS_PER_HOST };
10
11
 
11
12
  //# sourceMappingURL=limits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","names":["MAX_COMMANDS_PER_FRAME","MAX_INFLIGHT_COMMANDS_PER_CLIENT","MAX_FRAME_BODY_BYTES","MAX_EPHEMERAL_DOCUMENT_BYTES","MAX_INFLIGHT_COMMANDS_PER_HOST","COMMAND_CAPACITY_RETRY_MS"],"sources":["../../src/wire/limits.ts"],"sourcesContent":["export const MAX_COMMANDS_PER_FRAME = 256;\nexport const MAX_INFLIGHT_COMMANDS_PER_CLIENT = 256;\nexport const MAX_FRAME_BODY_BYTES = 8 * 1024 * 1024;\nexport const MAX_EPHEMERAL_DOCUMENT_BYTES = 64 * 1024;\nexport const MAX_INFLIGHT_COMMANDS_PER_HOST = 1024;\nexport const COMMAND_CAPACITY_RETRY_MS = 1_000;\n"],"mappings":";AAAA,MAAaA,yBAAyB;AACtC,MAAaC,mCAAmC;AAChD,MAAaC,uBAAuB;AACpC,MAAaC,+BAA+B;AAC5C,MAAaC,iCAAiC;AAC9C,MAAaC,4BAA4B"}
1
+ {"version":3,"file":"limits.js","names":["MAX_COMMANDS_PER_FRAME","MAX_INFLIGHT_COMMANDS_PER_CLIENT","MAX_FRAME_BODY_BYTES","MAX_EPHEMERAL_DOCUMENT_BYTES","MAX_INFLIGHT_COMMANDS_PER_HOST","COMMAND_CAPACITY_RETRY_MS","MAX_CONTEXT_BYTES"],"sources":["../../src/wire/limits.ts"],"sourcesContent":["export const MAX_COMMANDS_PER_FRAME = 256;\nexport const MAX_INFLIGHT_COMMANDS_PER_CLIENT = 256;\nexport const MAX_FRAME_BODY_BYTES = 8 * 1024 * 1024;\nexport const MAX_EPHEMERAL_DOCUMENT_BYTES = 64 * 1024;\nexport const MAX_INFLIGHT_COMMANDS_PER_HOST = 1024;\nexport const COMMAND_CAPACITY_RETRY_MS = 1_000;\n\nexport const MAX_CONTEXT_BYTES = 64 * 1024;\n"],"mappings":";AAAA,MAAaA,yBAAyB;AACtC,MAAaC,mCAAmC;AAChD,MAAaC,uBAAuB;AACpC,MAAaC,+BAA+B;AAC5C,MAAaC,iCAAiC;AAC9C,MAAaC,4BAA4B;AAEzC,MAAaC,oBAAoB"}
@@ -31,7 +31,7 @@ declare namespace StatewireProtocol {
31
31
  }[];
32
32
  };
33
33
  }
34
- declare const WIRE_VERSION = "2026-09-13";
34
+ declare const WIRE_VERSION = "2026-09-30";
35
35
  declare const DEFAULT_PROTOCOL = "default";
36
36
  declare const PROTOCOL_HEADER = "Statewire-Protocol";
37
37
  declare const VERSION_HEADER = "Statewire-Version";
@@ -1,5 +1,5 @@
1
1
  //#region src/wire/protocol.ts
2
- const WIRE_VERSION = "2026-09-13";
2
+ const WIRE_VERSION = "2026-09-30";
3
3
  const DEFAULT_PROTOCOL = "default";
4
4
  const PROTOCOL_HEADER = "Statewire-Protocol";
5
5
  const VERSION_HEADER = "Statewire-Version";
@@ -28,7 +28,7 @@ const inRange = (value, range) => {
28
28
  };
29
29
  const protocolOffers = (options) => options.applicationProtocols ?? [{
30
30
  name: options.protocol ?? "default",
31
- version: "2026-09-13"
31
+ version: "2026-09-30"
32
32
  }];
33
33
  const negotiationHeaders = (options) => {
34
34
  const suffix = (range) => {
@@ -39,7 +39,7 @@ const negotiationHeaders = (options) => {
39
39
  const seen = /* @__PURE__ */ new Set();
40
40
  if (offers !== "*" && !offers.length) invalid("protocol list must not be empty");
41
41
  return {
42
- [VERSION_HEADER]: suffix(options.wireVersion ?? { version: "2026-09-13" }),
42
+ [VERSION_HEADER]: suffix(options.wireVersion ?? { version: "2026-09-30" }),
43
43
  [PROTOCOL_HEADER]: offers === "*" ? "*" : offers.map((offer) => {
44
44
  if (!protocolToken.test(offer.name) || offer.name === "*" || seen.has(offer.name)) invalid("invalid or duplicate protocol name");
45
45
  if (offer.optional !== void 0 && typeof offer.optional !== "boolean") invalid("optional must be boolean");
@@ -123,7 +123,7 @@ const validateSelection = (value, options) => {
123
123
  if (typeof value !== "object" || value === null) return invalid("missing negotiation selection");
124
124
  const syn = value;
125
125
  const version = validateVersion(syn.version);
126
- if (!inRange(version, options.wireVersion ?? { version: "2026-09-13" })) invalid("selected wire version is outside offer");
126
+ if (!inRange(version, options.wireVersion ?? { version: "2026-09-30" })) invalid("selected wire version is outside offer");
127
127
  if (!Array.isArray(syn.protocols)) return invalid("syn.protocols must be an array");
128
128
  const offers = protocolOffers(options);
129
129
  const seen = /* @__PURE__ */ new Map();
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":["StatewireProtocol","Range","version","minVersion","Offer","name","optional","Selection","protocols","record","Options","wireVersion","applicationProtocols","protocol","keyPrefixes","Readonly","Record","Definition","requires","subprotocols","WIRE_VERSION","DEFAULT_PROTOCOL","PROTOCOL_HEADER","VERSION_HEADER","protocolToken","invalid","message","Error","validateVersion","value","test","parsed","Date","Number","isFinite","getTime","toISOString","slice","startsWith","validateRange","range","undefined","inRange","checked","protocolOffers","options","negotiationHeaders","suffix","offers","seen","Set","length","map","offer","has","add","join","parseItem","input","atom","String","raw","text","trim","match","RegExp","exec","quoted","decode","replace","params","Map","rest","parameter","key","set","parseNegotiation","headers","Headers","Required","Pick","wire","get","application","item","keys","some","k","names","split","includes","validateSelection","syn","Array","isArray","entry","parent","lastIndexOf","child","find","o","negotiateProtocols","definitions","fin","reason","code","status","payload","supported","d","failure","const","request","ReturnType","error","sort","registry","size","definition","children","offered","solve","active","dependency","Object","result","ranges","flatMap","lower","r","at","upper","selected","filter"],"sources":["../../src/wire/protocol.ts"],"sourcesContent":["export namespace StatewireProtocol {\n export type Range = { version: string; minVersion?: string };\n export type Offer = Range & { name: string; optional?: boolean };\n export type Selection = {\n version: string;\n protocols: { name: string; version: string; record?: \"ephemeral\" }[];\n };\n export type Options = {\n wireVersion?: Range;\n applicationProtocols?: readonly Offer[] | \"*\";\n protocol?: string;\n keyPrefixes?: Readonly<Record<string, readonly string[]>>;\n };\n export type Definition = Range & {\n name: string;\n requires?: Readonly<Record<string, Range>>;\n subprotocols?: readonly { name: string; record?: \"ephemeral\" }[];\n };\n}\n\nexport const WIRE_VERSION = \"2026-09-13\";\nexport const DEFAULT_PROTOCOL = \"default\";\nexport const PROTOCOL_HEADER = \"Statewire-Protocol\";\nexport const VERSION_HEADER = \"Statewire-Version\";\nexport const protocolToken = /^[A-Za-z*][A-Za-z0-9!#$%&'*+.^_`|~:/-]*$/;\n\nconst invalid = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nexport const validateVersion = (value: unknown): string => {\n if (typeof value !== \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value))\n return invalid(\"version must be a YYYY-MM-DD date\");\n const parsed = new Date(`${value}T00:00:00.000Z`);\n if (\n !Number.isFinite(parsed.getTime()) ||\n parsed.toISOString().slice(0, 10) !== value ||\n value.startsWith(\"0000\")\n )\n return invalid(\"invalid version date\");\n return value;\n};\n\nexport const validateRange = (range: StatewireProtocol.Range) => {\n const version = validateVersion(range.version);\n const minVersion =\n range.minVersion === undefined\n ? version\n : validateVersion(range.minVersion);\n if (minVersion > version) invalid(\"min-version exceeds version\");\n return { version, minVersion };\n};\n\nexport const inRange = (value: string, range: StatewireProtocol.Range) => {\n const checked = validateRange(range);\n return value >= checked.minVersion && value <= checked.version;\n};\n\nexport const protocolOffers = (options: StatewireProtocol.Options) =>\n options.applicationProtocols ?? [\n { name: options.protocol ?? DEFAULT_PROTOCOL, version: WIRE_VERSION },\n ];\n\nexport const negotiationHeaders = (\n options: StatewireProtocol.Options,\n): Record<string, string> => {\n const suffix = (range: StatewireProtocol.Range) => {\n const { version, minVersion } = validateRange(range);\n return `\"${version}\"${minVersion === version ? \"\" : `; min-version=\"${minVersion}\"`}`;\n };\n const offers = protocolOffers(options);\n const seen = new Set<string>();\n if (offers !== \"*\" && !offers.length)\n invalid(\"protocol list must not be empty\");\n return {\n [VERSION_HEADER]: suffix(options.wireVersion ?? { version: WIRE_VERSION }),\n [PROTOCOL_HEADER]:\n offers === \"*\"\n ? \"*\"\n : offers\n .map((offer) => {\n if (\n !protocolToken.test(offer.name) ||\n offer.name === \"*\" ||\n seen.has(offer.name)\n )\n invalid(\"invalid or duplicate protocol name\");\n if (\n offer.optional !== undefined &&\n typeof offer.optional !== \"boolean\"\n )\n invalid(\"optional must be boolean\");\n seen.add(offer.name);\n return `${offer.name}; version=${suffix(offer)}${offer.optional ? \"; optional\" : \"\"}`;\n })\n .join(\", \"),\n };\n};\n\nconst parseItem = (input: string) => {\n const atom = String.raw`\"(?:[\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e]|\\\\[\"\\\\])*\"|[A-Za-z*][A-Za-z0-9!#$%&'*+.^_\\x60|~:/-]*|\\?[01]`;\n const text = input.trim();\n const match = new RegExp(`^(?:${atom})`).exec(text);\n if (!match) return invalid(\"invalid structured item\");\n const quoted = match[0].startsWith('\"');\n const decode = (raw: string) => raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n const value = quoted ? decode(match[0]) : match[0];\n const params = new Map<string, string | boolean>();\n let rest = text.slice(match[0].length);\n while (rest) {\n const parameter = new RegExp(\n `^; *([a-z*][a-z0-9_.*-]*)(?:=(${atom}))?`,\n ).exec(rest);\n if (!parameter) return invalid(\"invalid structured parameters\");\n const key = parameter[1]!;\n const raw = parameter[2];\n if (params.has(key)) invalid(\"duplicate parameter\");\n if (raw === undefined || raw === \"?1\") params.set(key, true);\n else if (raw === \"?0\") params.set(key, false);\n else if (raw.startsWith('\"')) params.set(key, decode(raw));\n else invalid(\"parameter must be string or boolean\");\n rest = rest.slice(parameter[0].length);\n }\n return { value, quoted, params };\n};\n\nexport const parseNegotiation = (\n headers: Headers,\n): Required<\n Pick<StatewireProtocol.Options, \"wireVersion\" | \"applicationProtocols\">\n> => {\n const wire = headers.get(VERSION_HEADER);\n const application = headers.get(PROTOCOL_HEADER);\n if (wire === null || application === null)\n return invalid(\"negotiation fields are required\");\n const item = parseItem(wire);\n if (!item.quoted || [...item.params.keys()].some((k) => k !== \"min-version\"))\n invalid(\"invalid Statewire-Version\");\n const minVersion = item.params.get(\"min-version\");\n if (minVersion !== undefined && typeof minVersion !== \"string\")\n return invalid(\"min-version must be a string\");\n const wireVersion = validateRange({\n version: item.value,\n ...(minVersion === undefined ? {} : { minVersion }),\n });\n if (application.trim() === \"*\")\n return { wireVersion, applicationProtocols: \"*\" };\n const names = new Set<string>();\n const applicationProtocols = application\n .split(\",\")\n .map((text): StatewireProtocol.Offer => {\n const { value: name, quoted, params } = parseItem(text);\n if (\n quoted ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n names.has(name)\n )\n invalid(\"invalid or duplicate protocol name\");\n names.add(name);\n if (\n [...params.keys()].some(\n (k) => ![\"version\", \"min-version\", \"optional\"].includes(k),\n )\n )\n invalid(\"unsupported parameter\");\n const version = params.get(\"version\");\n const minVersion = params.get(\"min-version\");\n const optional = params.get(\"optional\") ?? false;\n if (\n typeof version !== \"string\" ||\n (minVersion !== undefined && typeof minVersion !== \"string\") ||\n typeof optional !== \"boolean\"\n )\n return invalid(\"invalid protocol parameters\");\n return {\n name,\n ...validateRange({\n version,\n ...(minVersion === undefined ? {} : { minVersion }),\n }),\n optional,\n };\n });\n return { wireVersion, applicationProtocols };\n};\n\nexport const validateSelection = (\n value: unknown,\n options: StatewireProtocol.Options,\n): StatewireProtocol.Selection => {\n if (typeof value !== \"object\" || value === null)\n return invalid(\"missing negotiation selection\");\n const syn = value as Record<string, unknown>;\n const version = validateVersion(syn.version);\n if (!inRange(version, options.wireVersion ?? { version: WIRE_VERSION }))\n invalid(\"selected wire version is outside offer\");\n if (!Array.isArray(syn.protocols))\n return invalid(\"syn.protocols must be an array\");\n const offers = protocolOffers(options);\n const seen = new Map<string, string>();\n const protocols = syn.protocols.map((entry: unknown) => {\n if (typeof entry !== \"object\" || entry === null)\n return invalid(\"invalid selected protocol\");\n const item = entry as Record<string, unknown>;\n const name = item.name;\n if (\n typeof name !== \"string\" ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n seen.has(name)\n )\n return invalid(\"invalid selected protocol name\");\n const version = validateVersion(item.version);\n if (item.record !== undefined && item.record !== \"ephemeral\")\n invalid(\"invalid selected protocol record\");\n const record = item.record as \"ephemeral\" | undefined;\n const parent = name.slice(0, name.lastIndexOf(\"/\"));\n const child = parent !== \"\" && seen.has(parent);\n if (child) {\n if (seen.get(parent) !== version)\n invalid(\"subprotocol version differs from its parent\");\n } else if (offers !== \"*\") {\n const offer = offers.find((o) => o.name === name);\n if (!offer || !inRange(version, offer))\n invalid(\"selected protocol is outside offer\");\n }\n seen.set(name, version);\n return { name, version, ...(record !== undefined && { record }) };\n });\n if (offers !== \"*\" && offers.some((o) => !o.optional && !seen.has(o.name)))\n invalid(\"required protocol missing from selection\");\n return { version, protocols };\n};\n\nexport const negotiateProtocols = (\n headers: Headers,\n wire: StatewireProtocol.Range,\n definitions: readonly StatewireProtocol.Definition[],\n):\n | StatewireProtocol.Selection\n | {\n fin: {\n reason: \"protocol-error\";\n code: string;\n status: number;\n message: string;\n payload?: unknown;\n };\n } => {\n const supported = {\n ...validateRange(wire),\n protocols: definitions.map((d) => ({ name: d.name, ...validateRange(d) })),\n };\n const failure = (code: string, message: string) => ({\n fin: {\n reason: \"protocol-error\" as const,\n code,\n message,\n status: code === \"invalid-negotiation\" ? 400 : 406,\n ...(code === \"invalid-negotiation\" ? {} : { payload: supported }),\n },\n });\n let request: ReturnType<typeof parseNegotiation>;\n try {\n request = parseNegotiation(headers);\n } catch (error) {\n return failure(\n \"invalid-negotiation\",\n error instanceof Error ? error.message : \"invalid negotiation\",\n );\n }\n const version = [wire.version, request.wireVersion.version].sort()[0]!;\n if (!inRange(version, wire) || !inRange(version, request.wireVersion))\n return failure(\"unsupported-version\", \"Statewire versions do not overlap\");\n const offers =\n request.applicationProtocols === \"*\"\n ? definitions.map((d) => ({\n name: d.name,\n version: d.version,\n optional: false,\n }))\n : request.applicationProtocols;\n const registry = new Map(definitions.map((d) => [d.name, d]));\n if (registry.size !== definitions.length)\n throw new Error(\"statewire: duplicate registered protocol\");\n for (const definition of definitions) {\n const children = new Set<string>();\n for (const child of definition.subprotocols ?? []) {\n if (\n !protocolToken.test(child.name) ||\n child.name.includes(\"/\") ||\n children.has(child.name) ||\n (child.record !== undefined && child.record !== \"ephemeral\")\n )\n throw new Error(\"statewire: invalid subprotocol registration\");\n children.add(child.name);\n }\n }\n const offered = new Map(offers.map((o) => [o.name, o]));\n const solve = (names: Set<string>) => {\n const active = new Set(names);\n for (const name of active) {\n const definition = registry.get(name);\n if (!definition || !offered.has(name)) return undefined;\n for (const dependency of Object.keys(definition.requires ?? {}))\n active.add(dependency);\n }\n const result = new Map<string, string>();\n for (const name of active) {\n const ranges = [\n registry.get(name)!,\n offered.get(name)!,\n ...[...active].flatMap((parent) => {\n const dependency = registry.get(parent)!.requires?.[name];\n return dependency ? [dependency] : [];\n }),\n ].map(validateRange);\n const lower = ranges\n .map((r) => r.minVersion)\n .sort()\n .at(-1)!;\n const upper = ranges.map((r) => r.version).sort()[0]!;\n if (lower > upper) return undefined;\n result.set(name, upper);\n }\n return result;\n };\n let selected = solve(\n new Set(offers.filter((o) => !o.optional).map((o) => o.name)),\n );\n if (!selected)\n return failure(\n \"unsupported-protocol\",\n \"required protocol combination is unsupported\",\n );\n for (const offer of offers) {\n if (offer.optional)\n selected = solve(new Set([...selected.keys(), offer.name])) ?? selected;\n }\n return {\n version,\n protocols: offers.flatMap((o) => {\n const version = selected!.get(o.name);\n if (version === undefined) return [];\n return [\n { name: o.name, version },\n ...(registry.get(o.name)!.subprotocols ?? []).map((child) => ({\n name: `${o.name}/${child.name}`,\n version,\n ...(child.record !== undefined && { record: child.record }),\n })),\n ];\n }),\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"protocol.js","names":["StatewireProtocol","Range","version","minVersion","Offer","name","optional","Selection","protocols","record","Options","wireVersion","applicationProtocols","protocol","keyPrefixes","Readonly","Record","Definition","requires","subprotocols","WIRE_VERSION","DEFAULT_PROTOCOL","PROTOCOL_HEADER","VERSION_HEADER","protocolToken","invalid","message","Error","validateVersion","value","test","parsed","Date","Number","isFinite","getTime","toISOString","slice","startsWith","validateRange","range","undefined","inRange","checked","protocolOffers","options","negotiationHeaders","suffix","offers","seen","Set","length","map","offer","has","add","join","parseItem","input","atom","String","raw","text","trim","match","RegExp","exec","quoted","decode","replace","params","Map","rest","parameter","key","set","parseNegotiation","headers","Headers","Required","Pick","wire","get","application","item","keys","some","k","names","split","includes","validateSelection","syn","Array","isArray","entry","parent","lastIndexOf","child","find","o","negotiateProtocols","definitions","fin","reason","code","status","payload","supported","d","failure","const","request","ReturnType","error","sort","registry","size","definition","children","offered","solve","active","dependency","Object","result","ranges","flatMap","lower","r","at","upper","selected","filter"],"sources":["../../src/wire/protocol.ts"],"sourcesContent":["export namespace StatewireProtocol {\n export type Range = { version: string; minVersion?: string };\n export type Offer = Range & { name: string; optional?: boolean };\n export type Selection = {\n version: string;\n protocols: { name: string; version: string; record?: \"ephemeral\" }[];\n };\n export type Options = {\n wireVersion?: Range;\n applicationProtocols?: readonly Offer[] | \"*\";\n protocol?: string;\n keyPrefixes?: Readonly<Record<string, readonly string[]>>;\n };\n export type Definition = Range & {\n name: string;\n requires?: Readonly<Record<string, Range>>;\n subprotocols?: readonly { name: string; record?: \"ephemeral\" }[];\n };\n}\n\nexport const WIRE_VERSION = \"2026-09-30\";\nexport const DEFAULT_PROTOCOL = \"default\";\nexport const PROTOCOL_HEADER = \"Statewire-Protocol\";\nexport const VERSION_HEADER = \"Statewire-Version\";\nexport const protocolToken = /^[A-Za-z*][A-Za-z0-9!#$%&'*+.^_`|~:/-]*$/;\n\nconst invalid = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nexport const validateVersion = (value: unknown): string => {\n if (typeof value !== \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value))\n return invalid(\"version must be a YYYY-MM-DD date\");\n const parsed = new Date(`${value}T00:00:00.000Z`);\n if (\n !Number.isFinite(parsed.getTime()) ||\n parsed.toISOString().slice(0, 10) !== value ||\n value.startsWith(\"0000\")\n )\n return invalid(\"invalid version date\");\n return value;\n};\n\nexport const validateRange = (range: StatewireProtocol.Range) => {\n const version = validateVersion(range.version);\n const minVersion =\n range.minVersion === undefined\n ? version\n : validateVersion(range.minVersion);\n if (minVersion > version) invalid(\"min-version exceeds version\");\n return { version, minVersion };\n};\n\nexport const inRange = (value: string, range: StatewireProtocol.Range) => {\n const checked = validateRange(range);\n return value >= checked.minVersion && value <= checked.version;\n};\n\nexport const protocolOffers = (options: StatewireProtocol.Options) =>\n options.applicationProtocols ?? [\n { name: options.protocol ?? DEFAULT_PROTOCOL, version: WIRE_VERSION },\n ];\n\nexport const negotiationHeaders = (\n options: StatewireProtocol.Options,\n): Record<string, string> => {\n const suffix = (range: StatewireProtocol.Range) => {\n const { version, minVersion } = validateRange(range);\n return `\"${version}\"${minVersion === version ? \"\" : `; min-version=\"${minVersion}\"`}`;\n };\n const offers = protocolOffers(options);\n const seen = new Set<string>();\n if (offers !== \"*\" && !offers.length)\n invalid(\"protocol list must not be empty\");\n return {\n [VERSION_HEADER]: suffix(options.wireVersion ?? { version: WIRE_VERSION }),\n [PROTOCOL_HEADER]:\n offers === \"*\"\n ? \"*\"\n : offers\n .map((offer) => {\n if (\n !protocolToken.test(offer.name) ||\n offer.name === \"*\" ||\n seen.has(offer.name)\n )\n invalid(\"invalid or duplicate protocol name\");\n if (\n offer.optional !== undefined &&\n typeof offer.optional !== \"boolean\"\n )\n invalid(\"optional must be boolean\");\n seen.add(offer.name);\n return `${offer.name}; version=${suffix(offer)}${offer.optional ? \"; optional\" : \"\"}`;\n })\n .join(\", \"),\n };\n};\n\nconst parseItem = (input: string) => {\n const atom = String.raw`\"(?:[\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e]|\\\\[\"\\\\])*\"|[A-Za-z*][A-Za-z0-9!#$%&'*+.^_\\x60|~:/-]*|\\?[01]`;\n const text = input.trim();\n const match = new RegExp(`^(?:${atom})`).exec(text);\n if (!match) return invalid(\"invalid structured item\");\n const quoted = match[0].startsWith('\"');\n const decode = (raw: string) => raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n const value = quoted ? decode(match[0]) : match[0];\n const params = new Map<string, string | boolean>();\n let rest = text.slice(match[0].length);\n while (rest) {\n const parameter = new RegExp(\n `^; *([a-z*][a-z0-9_.*-]*)(?:=(${atom}))?`,\n ).exec(rest);\n if (!parameter) return invalid(\"invalid structured parameters\");\n const key = parameter[1]!;\n const raw = parameter[2];\n if (params.has(key)) invalid(\"duplicate parameter\");\n if (raw === undefined || raw === \"?1\") params.set(key, true);\n else if (raw === \"?0\") params.set(key, false);\n else if (raw.startsWith('\"')) params.set(key, decode(raw));\n else invalid(\"parameter must be string or boolean\");\n rest = rest.slice(parameter[0].length);\n }\n return { value, quoted, params };\n};\n\nexport const parseNegotiation = (\n headers: Headers,\n): Required<\n Pick<StatewireProtocol.Options, \"wireVersion\" | \"applicationProtocols\">\n> => {\n const wire = headers.get(VERSION_HEADER);\n const application = headers.get(PROTOCOL_HEADER);\n if (wire === null || application === null)\n return invalid(\"negotiation fields are required\");\n const item = parseItem(wire);\n if (!item.quoted || [...item.params.keys()].some((k) => k !== \"min-version\"))\n invalid(\"invalid Statewire-Version\");\n const minVersion = item.params.get(\"min-version\");\n if (minVersion !== undefined && typeof minVersion !== \"string\")\n return invalid(\"min-version must be a string\");\n const wireVersion = validateRange({\n version: item.value,\n ...(minVersion === undefined ? {} : { minVersion }),\n });\n if (application.trim() === \"*\")\n return { wireVersion, applicationProtocols: \"*\" };\n const names = new Set<string>();\n const applicationProtocols = application\n .split(\",\")\n .map((text): StatewireProtocol.Offer => {\n const { value: name, quoted, params } = parseItem(text);\n if (\n quoted ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n names.has(name)\n )\n invalid(\"invalid or duplicate protocol name\");\n names.add(name);\n if (\n [...params.keys()].some(\n (k) => ![\"version\", \"min-version\", \"optional\"].includes(k),\n )\n )\n invalid(\"unsupported parameter\");\n const version = params.get(\"version\");\n const minVersion = params.get(\"min-version\");\n const optional = params.get(\"optional\") ?? false;\n if (\n typeof version !== \"string\" ||\n (minVersion !== undefined && typeof minVersion !== \"string\") ||\n typeof optional !== \"boolean\"\n )\n return invalid(\"invalid protocol parameters\");\n return {\n name,\n ...validateRange({\n version,\n ...(minVersion === undefined ? {} : { minVersion }),\n }),\n optional,\n };\n });\n return { wireVersion, applicationProtocols };\n};\n\nexport const validateSelection = (\n value: unknown,\n options: StatewireProtocol.Options,\n): StatewireProtocol.Selection => {\n if (typeof value !== \"object\" || value === null)\n return invalid(\"missing negotiation selection\");\n const syn = value as Record<string, unknown>;\n const version = validateVersion(syn.version);\n if (!inRange(version, options.wireVersion ?? { version: WIRE_VERSION }))\n invalid(\"selected wire version is outside offer\");\n if (!Array.isArray(syn.protocols))\n return invalid(\"syn.protocols must be an array\");\n const offers = protocolOffers(options);\n const seen = new Map<string, string>();\n const protocols = syn.protocols.map((entry: unknown) => {\n if (typeof entry !== \"object\" || entry === null)\n return invalid(\"invalid selected protocol\");\n const item = entry as Record<string, unknown>;\n const name = item.name;\n if (\n typeof name !== \"string\" ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n seen.has(name)\n )\n return invalid(\"invalid selected protocol name\");\n const version = validateVersion(item.version);\n if (item.record !== undefined && item.record !== \"ephemeral\")\n invalid(\"invalid selected protocol record\");\n const record = item.record as \"ephemeral\" | undefined;\n const parent = name.slice(0, name.lastIndexOf(\"/\"));\n const child = parent !== \"\" && seen.has(parent);\n if (child) {\n if (seen.get(parent) !== version)\n invalid(\"subprotocol version differs from its parent\");\n } else if (offers !== \"*\") {\n const offer = offers.find((o) => o.name === name);\n if (!offer || !inRange(version, offer))\n invalid(\"selected protocol is outside offer\");\n }\n seen.set(name, version);\n return { name, version, ...(record !== undefined && { record }) };\n });\n if (offers !== \"*\" && offers.some((o) => !o.optional && !seen.has(o.name)))\n invalid(\"required protocol missing from selection\");\n return { version, protocols };\n};\n\nexport const negotiateProtocols = (\n headers: Headers,\n wire: StatewireProtocol.Range,\n definitions: readonly StatewireProtocol.Definition[],\n):\n | StatewireProtocol.Selection\n | {\n fin: {\n reason: \"protocol-error\";\n code: string;\n status: number;\n message: string;\n payload?: unknown;\n };\n } => {\n const supported = {\n ...validateRange(wire),\n protocols: definitions.map((d) => ({ name: d.name, ...validateRange(d) })),\n };\n const failure = (code: string, message: string) => ({\n fin: {\n reason: \"protocol-error\" as const,\n code,\n message,\n status: code === \"invalid-negotiation\" ? 400 : 406,\n ...(code === \"invalid-negotiation\" ? {} : { payload: supported }),\n },\n });\n let request: ReturnType<typeof parseNegotiation>;\n try {\n request = parseNegotiation(headers);\n } catch (error) {\n return failure(\n \"invalid-negotiation\",\n error instanceof Error ? error.message : \"invalid negotiation\",\n );\n }\n const version = [wire.version, request.wireVersion.version].sort()[0]!;\n if (!inRange(version, wire) || !inRange(version, request.wireVersion))\n return failure(\"unsupported-version\", \"Statewire versions do not overlap\");\n const offers =\n request.applicationProtocols === \"*\"\n ? definitions.map((d) => ({\n name: d.name,\n version: d.version,\n optional: false,\n }))\n : request.applicationProtocols;\n const registry = new Map(definitions.map((d) => [d.name, d]));\n if (registry.size !== definitions.length)\n throw new Error(\"statewire: duplicate registered protocol\");\n for (const definition of definitions) {\n const children = new Set<string>();\n for (const child of definition.subprotocols ?? []) {\n if (\n !protocolToken.test(child.name) ||\n child.name.includes(\"/\") ||\n children.has(child.name) ||\n (child.record !== undefined && child.record !== \"ephemeral\")\n )\n throw new Error(\"statewire: invalid subprotocol registration\");\n children.add(child.name);\n }\n }\n const offered = new Map(offers.map((o) => [o.name, o]));\n const solve = (names: Set<string>) => {\n const active = new Set(names);\n for (const name of active) {\n const definition = registry.get(name);\n if (!definition || !offered.has(name)) return undefined;\n for (const dependency of Object.keys(definition.requires ?? {}))\n active.add(dependency);\n }\n const result = new Map<string, string>();\n for (const name of active) {\n const ranges = [\n registry.get(name)!,\n offered.get(name)!,\n ...[...active].flatMap((parent) => {\n const dependency = registry.get(parent)!.requires?.[name];\n return dependency ? [dependency] : [];\n }),\n ].map(validateRange);\n const lower = ranges\n .map((r) => r.minVersion)\n .sort()\n .at(-1)!;\n const upper = ranges.map((r) => r.version).sort()[0]!;\n if (lower > upper) return undefined;\n result.set(name, upper);\n }\n return result;\n };\n let selected = solve(\n new Set(offers.filter((o) => !o.optional).map((o) => o.name)),\n );\n if (!selected)\n return failure(\n \"unsupported-protocol\",\n \"required protocol combination is unsupported\",\n );\n for (const offer of offers) {\n if (offer.optional)\n selected = solve(new Set([...selected.keys(), offer.name])) ?? selected;\n }\n return {\n version,\n protocols: offers.flatMap((o) => {\n const version = selected!.get(o.name);\n if (version === undefined) return [];\n return [\n { name: o.name, version },\n ...(registry.get(o.name)!.subprotocols ?? []).map((child) => ({\n name: `${o.name}/${child.name}`,\n version,\n ...(child.record !== undefined && { record: child.record }),\n })),\n ];\n }),\n };\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statewire",
3
- "version": "0.19.4",
3
+ "version": "0.21.0",
4
4
  "description": "State over the wire: snapshot-first delta streaming and command submission for any state and any command",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -167,13 +167,21 @@ const setStatus = (c: Conn, next: ConnectionState) => {
167
167
  c.onChange(c.connection);
168
168
  };
169
169
 
170
- const storageFailed = (c: Conn, error: Error) => {
170
+ const stopForError = ({
171
+ connection: c,
172
+ error,
173
+ reason,
174
+ }: {
175
+ connection: Conn;
176
+ error: Error;
177
+ reason: "storage-error" | "protocol-error";
178
+ }) => {
171
179
  c.error = error;
172
180
  c.onError(error);
173
181
  flushTapSync(() =>
174
182
  setStatus(c, {
175
183
  status: "stopped",
176
- reason: "storage-error",
184
+ reason,
177
185
  degraded: true,
178
186
  message: error.message,
179
187
  }),
@@ -481,6 +489,7 @@ export const handleEnvelope = (
481
489
  return null;
482
490
  }
483
491
  deliverEnvelope(c, env);
492
+ if (c.connection.status === "stopped") return "stop";
484
493
  if (env.fin !== undefined) return settleFin(c, env.fin);
485
494
  if (c.connection.status !== "connected") {
486
495
  c.attempt = 0;
@@ -568,7 +577,7 @@ const run = async (c: Conn, lifetime: AbortController, gate: Gate) => {
568
577
  } catch (error) {
569
578
  if (!abort.signal.aborted) {
570
579
  if (isStorageError(error)) {
571
- storageFailed(c, error);
580
+ stopForError({ connection: c, error, reason: "storage-error" });
572
581
  outcome = "stop";
573
582
  } else if (
574
583
  error instanceof SyntaxError ||
@@ -718,7 +727,10 @@ export const useLink = (
718
727
  return c.parked;
719
728
  },
720
729
  send: (frame) => latest.current.send(c, frame),
721
- storageFailed: (error) => storageFailed(c, error),
730
+ storageFailed: (error) =>
731
+ stopForError({ connection: c, error, reason: "storage-error" }),
732
+ protocolFailed: (error) =>
733
+ stopForError({ connection: c, error, reason: "protocol-error" }),
722
734
  }),
723
735
  );
724
736