@earendil-works/chord 1.2.0 → 1.3.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 (86) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +14 -13
  3. package/dist/cell.d.ts +30 -9
  4. package/dist/cell.d.ts.map +1 -1
  5. package/dist/cell.js +52 -12
  6. package/dist/cell.js.map +1 -1
  7. package/dist/cluster.d.ts +4 -3
  8. package/dist/cluster.d.ts.map +1 -1
  9. package/dist/cluster.js +58 -42
  10. package/dist/cluster.js.map +1 -1
  11. package/dist/connection.d.ts +171 -58
  12. package/dist/connection.d.ts.map +1 -1
  13. package/dist/connection.js +676 -217
  14. package/dist/connection.js.map +1 -1
  15. package/dist/contract.d.ts +120 -45
  16. package/dist/contract.d.ts.map +1 -1
  17. package/dist/contract.js +104 -15
  18. package/dist/contract.js.map +1 -1
  19. package/dist/declare.d.ts +11 -48
  20. package/dist/declare.d.ts.map +1 -1
  21. package/dist/declare.js +1 -21
  22. package/dist/declare.js.map +1 -1
  23. package/dist/delta/index.d.ts +2 -1
  24. package/dist/delta/index.d.ts.map +1 -1
  25. package/dist/delta/index.js +4 -0
  26. package/dist/delta/index.js.map +1 -1
  27. package/dist/errors.d.ts +4 -3
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/errors.js +15 -0
  30. package/dist/errors.js.map +1 -1
  31. package/dist/host.d.ts +214 -120
  32. package/dist/host.d.ts.map +1 -1
  33. package/dist/host.js +446 -241
  34. package/dist/host.js.map +1 -1
  35. package/dist/index.d.ts +8 -8
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +4 -4
  38. package/dist/index.js.map +1 -1
  39. package/dist/link.d.ts +47 -12
  40. package/dist/link.d.ts.map +1 -1
  41. package/dist/link.js +200 -14
  42. package/dist/link.js.map +1 -1
  43. package/dist/loader.d.ts.map +1 -1
  44. package/dist/loader.js +7 -2
  45. package/dist/loader.js.map +1 -1
  46. package/dist/manager.d.ts.map +1 -1
  47. package/dist/manager.js +35 -9
  48. package/dist/manager.js.map +1 -1
  49. package/dist/object.d.ts +438 -0
  50. package/dist/object.d.ts.map +1 -0
  51. package/dist/{record.js → object.js} +614 -335
  52. package/dist/object.js.map +1 -0
  53. package/dist/output.d.ts +85 -0
  54. package/dist/output.d.ts.map +1 -0
  55. package/dist/output.js +350 -0
  56. package/dist/output.js.map +1 -0
  57. package/dist/registry.d.ts +3 -3
  58. package/dist/registry.d.ts.map +1 -1
  59. package/dist/registry.js +1 -1
  60. package/dist/registry.js.map +1 -1
  61. package/dist/reload.d.ts +35 -27
  62. package/dist/reload.d.ts.map +1 -1
  63. package/dist/reload.js +256 -192
  64. package/dist/reload.js.map +1 -1
  65. package/dist/remote.d.ts +130 -35
  66. package/dist/remote.d.ts.map +1 -1
  67. package/dist/remote.js +493 -189
  68. package/dist/remote.js.map +1 -1
  69. package/dist/types.d.ts +39 -28
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/dist/websocket.d.ts +4 -5
  73. package/dist/websocket.d.ts.map +1 -1
  74. package/dist/websocket.js +3 -2
  75. package/dist/websocket.js.map +1 -1
  76. package/dist/wire.d.ts +210 -129
  77. package/dist/wire.d.ts.map +1 -1
  78. package/dist/wire.js +108 -55
  79. package/dist/wire.js.map +1 -1
  80. package/docs/guide.md +267 -203
  81. package/docs/spec.md +141 -114
  82. package/docs/tutorial.md +257 -235
  83. package/package.json +6 -1
  84. package/dist/record.d.ts +0 -320
  85. package/dist/record.d.ts.map +0 -1
  86. package/dist/record.js.map +0 -1
package/dist/wire.d.ts CHANGED
@@ -1,135 +1,216 @@
1
- import type { Description } from "./contract.ts";
2
- import type { WireOp } from "./delta/index.ts";
3
- import { ChordError, type ChordErrorCode } from "./errors.ts";
4
- import type { Principal } from "./types.ts";
5
- export interface RootRef {
6
- readonly rootId: string;
7
- readonly epoch: number;
8
- }
9
- export interface WireError {
10
- readonly code: ChordErrorCode;
11
- readonly message: string;
12
- readonly reason?: string;
13
- }
14
- export interface WireReloadOptions {
15
- readonly remove?: readonly string[];
16
- readonly gateDeadline?: number;
17
- readonly retireDeadline?: number;
18
- }
1
+ import { type Static, type TSchema, Type } from "typebox";
2
+ import { ChordError } from "./errors.ts";
3
+ /**
4
+ * The link protocol version, announced in `hello`. Peers of different versions do not talk: the side that notices
5
+ * closes the link with reason `protocol-version <its version>`, so the other side learns it too
6
+ * (`LinkOptions.onProtocolMismatch`).
7
+ */
8
+ export declare const PROTOCOL_VERSION = 4;
9
+ declare const RootRefSchema: Type.TObject<{
10
+ rootId: Type.TString;
11
+ codeVersion: Type.TInteger;
12
+ }>;
13
+ export type RootRef = Static<typeof RootRefSchema>;
14
+ declare const WireErrorSchema: Type.TObject<{
15
+ code: Type.TEnum<["aborted", "contract_mismatch", "forbidden", "gone", "invalid", "not_in_variant", "not_ready", "released", "superseded", "unavailable", "upgrade_conflict", "upgrade_required"]>;
16
+ message: Type.TString;
17
+ reason: Type.TOptional<Type.TString>;
18
+ }>;
19
+ export type WireError = Static<typeof WireErrorSchema>;
20
+ declare const WireReloadOptionsSchema: Type.TObject<{
21
+ remove: Type.TOptional<Type.TImmutable<Type.TArray<Type.TString>>>;
22
+ gateDeadline: Type.TOptional<Type.TNumber>;
23
+ retireDeadline: Type.TOptional<Type.TNumber>;
24
+ }>;
25
+ export type WireReloadOptions = Static<typeof WireReloadOptionsSchema>;
19
26
  /** A child's prepared reload, sent with its vote (same fields as `ReloadReport`, without children). */
20
- export interface WireReloadReport {
21
- readonly epoch: number;
22
- readonly previous: number;
23
- readonly changed: readonly string[];
24
- readonly removed: readonly string[];
25
- readonly prepared: readonly string[];
26
- readonly gated: readonly string[];
27
- }
28
- export type Frame = {
29
- readonly t: "hello";
30
- readonly name: string;
31
- readonly kind: "host" | "client";
32
- readonly epoch: number;
33
- } | {
34
- readonly t: "hold";
35
- readonly id: number;
36
- /** Target host name. */
37
- readonly at: string;
38
- /** The consumer's contract token; the provider checks that it covers it. */
39
- readonly token: Description;
40
- readonly input: unknown;
27
+ declare const WireReloadReportSchema: Type.TObject<{
28
+ codeVersion: Type.TInteger;
29
+ previous: Type.TInteger;
30
+ changed: Type.TImmutable<Type.TArray<Type.TString>>;
31
+ removed: Type.TImmutable<Type.TArray<Type.TString>>;
32
+ prepared: Type.TImmutable<Type.TArray<Type.TString>>;
33
+ gated: Type.TImmutable<Type.TArray<Type.TString>>;
34
+ }>;
35
+ export type WireReloadReport = Static<typeof WireReloadReportSchema>;
36
+ declare const FrameSchema: Type.TUnion<[Type.TObject<{
37
+ t: Type.TLiteral<"hello">;
38
+ /** The link protocol version (`PROTOCOL_VERSION`; absent: 1). Peers of another version close the link. */
39
+ protocol: Type.TOptional<Type.TInteger>;
40
+ name: Type.TString;
41
+ kind: Type.TUnion<[Type.TLiteral<"host">, Type.TLiteral<"client">]>;
42
+ codeVersion: Type.TInteger;
43
+ /** The sender acknowledges what it receives (`ack`). Flow control applies only when both hellos say so. */
44
+ acks: Type.TOptional<Type.TBoolean>;
45
+ }>, Type.TObject<{
46
+ t: Type.TLiteral<"ack">;
47
+ n: Type.TInteger;
48
+ }>, Type.TObject<{
49
+ t: Type.TLiteral<"hold">;
50
+ id: Type.TInteger;
51
+ /**
52
+ * Target host name, resolved by the consumer from the token's `host` and `input`. Relays route on it after
53
+ * checking it against both; the target refuses an input its own contract places elsewhere.
54
+ */
55
+ at: Type.TString;
56
+ /** The consumer's contract token; the provider checks that it covers it. Remote tokens carry `host`. */
57
+ token: Type.TObject<{
58
+ name: Type.TString;
59
+ scope: Type.TUnion<[Type.TLiteral<"remote">, Type.TLiteral<"local">]>;
60
+ version: Type.TInteger;
61
+ input: Type.TUnsafe<TSchema>;
62
+ host: Type.TOptional<Type.TUnion<[Type.TObject<{
63
+ name: Type.TString;
64
+ }>, Type.TObject<{
65
+ prefix: Type.TString;
66
+ key: Type.TString;
67
+ }>]>>;
68
+ variants: Type.TRecord<"^.*$", Type.TRecord<"^.*$", Type.TUnion<[Type.TObject<{
69
+ kind: Type.TLiteral<"method">;
70
+ args: Type.TOptional<Type.TUnsafe<TSchema>>;
71
+ result: Type.TOptional<Type.TUnsafe<TSchema>>;
72
+ }>, Type.TObject<{
73
+ kind: Type.TLiteral<"state">;
74
+ schema: Type.TUnsafe<TSchema>;
75
+ }>, Type.TObject<{
76
+ kind: Type.TLiteral<"opaque">;
77
+ id: Type.TString;
78
+ }>]>>>;
79
+ }>;
80
+ /** Normalized with the consumer's contract. */
81
+ input: Type.TUnknown;
41
82
  /** A forwarded principal. Honored only on frames from the parent link. */
42
- readonly principal?: Principal;
43
- /** Set by a relay when the hold originates from a client (browser): not a reload participant. */
44
- readonly client?: boolean;
45
- } | {
46
- readonly t: "release";
47
- readonly id: number;
48
- } | {
49
- readonly t: "shape";
50
- readonly id: number;
51
- readonly kind?: string;
52
- readonly members: readonly string[];
53
- readonly contract: string;
54
- readonly epoch: number;
55
- } | {
56
- readonly t: "gone";
57
- readonly id: number;
58
- readonly error: WireError;
59
- } | {
60
- readonly t: "call";
61
- readonly id: number;
62
- readonly hold: number;
63
- readonly member: string;
64
- readonly args: unknown;
83
+ principal: Type.TOptional<Type.TUnknown>;
84
+ /**
85
+ * The client connection the hold comes from: an opaque id the server assigned to it, forwarded by relays. Honored
86
+ * only on frames from the parent link; present means the hold is a client's (not a reload participant).
87
+ */
88
+ origin: Type.TOptional<Type.TString>;
89
+ /** False: no state for this hold until `sub` (a method-only binding). */
90
+ state: Type.TOptional<Type.TLiteral<false>>;
91
+ /** The consumer already has values (a re-hold after a link loss): its unit is not urgent. */
92
+ again: Type.TOptional<Type.TLiteral<true>>;
93
+ }>, Type.TObject<{
94
+ t: Type.TLiteral<"release">;
95
+ id: Type.TInteger;
96
+ }>, Type.TObject<{
97
+ t: Type.TLiteral<"shape">;
98
+ id: Type.TInteger;
99
+ variant: Type.TOptional<Type.TString>;
100
+ members: Type.TImmutable<Type.TArray<Type.TString>>;
101
+ contract: Type.TString;
102
+ /** The state members whose snapshots follow this frame; the consumer applies the shape once they are here. */
103
+ state: Type.TImmutable<Type.TArray<Type.TString>>;
104
+ }>, Type.TObject<{
105
+ t: Type.TLiteral<"gone">;
106
+ id: Type.TInteger;
107
+ error: Type.TObject<{
108
+ code: Type.TEnum<["aborted", "contract_mismatch", "forbidden", "gone", "invalid", "not_in_variant", "not_ready", "released", "superseded", "unavailable", "upgrade_conflict", "upgrade_required"]>;
109
+ message: Type.TString;
110
+ reason: Type.TOptional<Type.TString>;
111
+ }>;
112
+ }>, Type.TObject<{
113
+ t: Type.TLiteral<"call">;
114
+ id: Type.TInteger;
115
+ hold: Type.TInteger;
116
+ member: Type.TString;
117
+ args: Type.TOptional<Type.TUnknown>;
65
118
  /** Absent: the receiving host starts a root (browsers never carry roots). */
66
- readonly root?: RootRef;
67
- } | {
68
- readonly t: "result";
69
- readonly id: number;
70
- readonly value?: unknown;
71
- readonly error?: WireError;
72
- } | {
73
- readonly t: "cancel";
74
- readonly id: number;
75
- } | {
76
- readonly t: "sub";
77
- readonly id: number;
78
- readonly hold: number;
79
- readonly member: string;
80
- } | {
81
- readonly t: "unsub";
82
- readonly id: number;
83
- } | {
84
- readonly t: "state";
85
- readonly id: number;
86
- readonly snap?: unknown;
87
- readonly reset?: unknown;
88
- /** Encoded with the subscription's delta encoder. */
89
- readonly ops?: readonly WireOp[];
90
- readonly error?: WireError;
91
- } | {
92
- readonly t: "ping";
93
- readonly n: number;
94
- } | {
95
- readonly t: "pong";
96
- readonly n: number;
97
- } | {
98
- readonly t: "bye";
99
- readonly reason?: string;
100
- }
101
- /** The root ended at its origin: its leases on records end (sent on every link the root's calls used). */
102
- | {
103
- readonly t: "root_end";
104
- readonly rootId: string;
105
- } | {
106
- readonly t: "invalidate";
107
- readonly pattern: Readonly<Record<string, unknown>>;
108
- } | {
109
- readonly t: "prepare";
110
- readonly reload: number;
111
- readonly epoch: number;
112
- readonly plugins: readonly string[];
113
- readonly options: WireReloadOptions;
114
- } | {
115
- readonly t: "vote";
116
- readonly reload: number;
117
- readonly error?: WireError;
118
- readonly report?: WireReloadReport;
119
- } | {
120
- readonly t: "commit";
121
- readonly reload: number;
122
- }
123
- /** The parent dropped `epoch`: roots of it may still come from there no longer. */
124
- | {
125
- readonly t: "retire";
126
- readonly epoch: number;
127
- } | {
128
- readonly t: "abort";
129
- readonly reload: number;
130
- };
119
+ root: Type.TOptional<Type.TObject<{
120
+ rootId: Type.TString;
121
+ codeVersion: Type.TInteger;
122
+ }>>;
123
+ }>, Type.TObject<{
124
+ t: Type.TLiteral<"result">;
125
+ id: Type.TInteger;
126
+ value: Type.TOptional<Type.TUnknown>;
127
+ error: Type.TOptional<Type.TObject<{
128
+ code: Type.TEnum<["aborted", "contract_mismatch", "forbidden", "gone", "invalid", "not_in_variant", "not_ready", "released", "superseded", "unavailable", "upgrade_conflict", "upgrade_required"]>;
129
+ message: Type.TString;
130
+ reason: Type.TOptional<Type.TString>;
131
+ }>>;
132
+ }>, Type.TObject<{
133
+ t: Type.TLiteral<"cancel">;
134
+ id: Type.TInteger;
135
+ }>, Type.TObject<{
136
+ t: Type.TLiteral<"sub">;
137
+ hold: Type.TInteger;
138
+ }>, Type.TObject<{
139
+ t: Type.TLiteral<"unsub">;
140
+ hold: Type.TInteger;
141
+ }>, Type.TObject<{
142
+ t: Type.TLiteral<"resync">;
143
+ hold: Type.TInteger;
144
+ m: Type.TString;
145
+ }>, Type.TObject<{
146
+ t: Type.TLiteral<"state">;
147
+ hold: Type.TInteger;
148
+ m: Type.TString;
149
+ /** `snap`/`reset` without `value`: the value is `undefined`. */
150
+ as: Type.TUnion<[Type.TLiteral<"snap">, Type.TLiteral<"reset">, Type.TLiteral<"ops">, Type.TLiteral<"error">]>;
151
+ value: Type.TOptional<Type.TUnknown>;
152
+ /** Encoded with the stream's delta encoder; the decoder validates each op. */
153
+ ops: Type.TOptional<Type.TImmutable<Type.TArray<Type.TImmutable<Type.TArray<Type.TUnknown>>>>>;
154
+ error: Type.TOptional<Type.TObject<{
155
+ code: Type.TEnum<["aborted", "contract_mismatch", "forbidden", "gone", "invalid", "not_in_variant", "not_ready", "released", "superseded", "unavailable", "upgrade_conflict", "upgrade_required"]>;
156
+ message: Type.TString;
157
+ reason: Type.TOptional<Type.TString>;
158
+ }>>;
159
+ }>, Type.TObject<{
160
+ t: Type.TLiteral<"ping">;
161
+ n: Type.TInteger;
162
+ }>, Type.TObject<{
163
+ t: Type.TLiteral<"pong">;
164
+ n: Type.TInteger;
165
+ }>, Type.TObject<{
166
+ t: Type.TLiteral<"bye">;
167
+ reason: Type.TOptional<Type.TString>;
168
+ }>, Type.TObject<{
169
+ t: Type.TLiteral<"root_end">;
170
+ rootId: Type.TString;
171
+ }>, Type.TObject<{
172
+ t: Type.TLiteral<"invalidate">;
173
+ pattern: Type.TRecord<"^.*$", Type.TUnknown>;
174
+ }>, Type.TObject<{
175
+ t: Type.TLiteral<"prepare">;
176
+ reload: Type.TInteger;
177
+ codeVersion: Type.TInteger;
178
+ plugins: Type.TImmutable<Type.TArray<Type.TString>>;
179
+ options: Type.TObject<{
180
+ remove: Type.TOptional<Type.TImmutable<Type.TArray<Type.TString>>>;
181
+ gateDeadline: Type.TOptional<Type.TNumber>;
182
+ retireDeadline: Type.TOptional<Type.TNumber>;
183
+ }>;
184
+ }>, Type.TObject<{
185
+ t: Type.TLiteral<"vote">;
186
+ reload: Type.TInteger;
187
+ error: Type.TOptional<Type.TObject<{
188
+ code: Type.TEnum<["aborted", "contract_mismatch", "forbidden", "gone", "invalid", "not_in_variant", "not_ready", "released", "superseded", "unavailable", "upgrade_conflict", "upgrade_required"]>;
189
+ message: Type.TString;
190
+ reason: Type.TOptional<Type.TString>;
191
+ }>>;
192
+ report: Type.TOptional<Type.TObject<{
193
+ codeVersion: Type.TInteger;
194
+ previous: Type.TInteger;
195
+ changed: Type.TImmutable<Type.TArray<Type.TString>>;
196
+ removed: Type.TImmutable<Type.TArray<Type.TString>>;
197
+ prepared: Type.TImmutable<Type.TArray<Type.TString>>;
198
+ gated: Type.TImmutable<Type.TArray<Type.TString>>;
199
+ }>>;
200
+ }>, Type.TObject<{
201
+ t: Type.TLiteral<"commit">;
202
+ reload: Type.TInteger;
203
+ }>, Type.TObject<{
204
+ t: Type.TLiteral<"retire">;
205
+ codeVersion: Type.TInteger;
206
+ }>, Type.TObject<{
207
+ t: Type.TLiteral<"abort">;
208
+ reload: Type.TInteger;
209
+ }>]>;
210
+ export type Frame = Static<typeof FrameSchema>;
211
+ /** A frame that passes validation; anything else is a protocol violation. */
212
+ export declare const isFrame: (value: unknown) => value is Frame;
131
213
  export declare function toWireError(error: unknown): WireError;
132
214
  export declare const fromWireError: (error: WireError) => ChordError;
133
- /** A frame that passes structural validation; anything else is a protocol violation. */
134
- export declare const isFrame: (value: unknown) => value is Frame;
215
+ export {};
135
216
  //# sourceMappingURL=wire.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,EAAqB,UAAU,EAAE,MAAM,aAAa,CAAC;AAe5D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAQlC,QAAA,MAAM,aAAa;;;EAA2E,CAAC;AAC/F,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAEnD,QAAA,MAAM,eAAe;;;;EAInB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAEvD,QAAA,MAAM,uBAAuB;;;;EAI3B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAEvE,uGAAuG;AACvG,QAAA,MAAM,sBAAsB;;;;;;;EAO1B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAErE,QAAA,MAAM,WAAW;;IAGf,0GAA0G;;;;;IAK1G,2GAA2G;;;;;;;;IAQ3G;;;OAGG;;IAEH,wGAAwG;;;;;;;;;;;;;;;;;;;;;;;;IAExG,+CAA+C;;IAE/C,0EAA0E;;IAE1E;;;OAGG;;IAEH,yEAAyE;;IAEzE,6FAA6F;;;;;;;;;;;IAU7F,8GAA8G;;;;;;;;;;;;;;;;IAU9G,6EAA6E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoB7E,gEAAgE;;;IAGhE,8EAA8E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA4B9E,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAE/C,6EAA6E;AAC7E,eAAO,MAAM,OAAO,UAAW,OAAO,KAAG,KAAK,IAAI,KAAwC,CAAC;AAE3F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAOrD;AAED,eAAO,MAAM,aAAa,UAAW,SAAS,KAAG,UACyD,CAAC"}
package/dist/wire.js CHANGED
@@ -1,112 +1,165 @@
1
1
  import { Type } from "typebox";
2
2
  import { Value } from "typebox/value";
3
- import { ChordError } from "./errors.js";
4
- export function toWireError(error) {
5
- if (error instanceof ChordError) {
6
- return error.reason === undefined
7
- ? { code: error.code, message: error.message }
8
- : { code: error.code, message: error.message, reason: error.reason };
9
- }
10
- return { code: "unavailable", message: error instanceof Error ? error.message : String(error) };
11
- }
12
- export const fromWireError = (error) => new ChordError(error.code, error.message.replace(/^[a-z_]+(\/[a-z_]+)?: /, ""), { reason: error.reason });
13
- // ─── validation ──────────────────────────────────────────────────────────────
3
+ import { DescriptionSchema } from "./contract.js";
4
+ import { CHORD_ERROR_CODES, ChordError } from "./errors.js";
5
+ // JSON frames between two hosts (or a host and a client). Ids are chosen by the side that creates the hold, call or
6
+ // subscription; every frame type has one direction, so ids never collide:
7
+ // consumer → provider: hold, release, call, cancel, sub, unsub, resync
8
+ // provider → consumer: shape, gone, result, state
9
+ // State is addressed by (hold id, member): a hold implies the state of its variant, sent as units (a shape followed by
10
+ // a snapshot of each member the consumer lacks; see Connection).
11
+ // both: hello, ping, pong, bye, root_end, ack; parent → child: invalidate, prepare, commit, abort, retire;
12
+ // child → parent: vote
13
+ //
14
+ // Each frame is declared once, as the schema every received frame is checked against (`isFrame`); the types are derived
15
+ // from it. Payloads (hold inputs, call args, results, snapshots) stay `unknown`: they are validated where they are
16
+ // used, against the contract. State ops are checked op by op by the delta decoder.
17
+ /**
18
+ * The link protocol version, announced in `hello`. Peers of different versions do not talk: the side that notices
19
+ * closes the link with reason `protocol-version <its version>`, so the other side learns it too
20
+ * (`LinkOptions.onProtocolMismatch`).
21
+ */
22
+ export const PROTOCOL_VERSION = 4;
14
23
  const Id = Type.Integer({ minimum: 1 });
15
24
  const Name = Type.String({ minLength: 1 });
16
- const Root = Type.Object({ rootId: Name, epoch: Type.Integer({ minimum: 1 }) });
17
- const ErrorShape = Type.Object({ code: Type.String(), message: Type.String(), reason: Type.Optional(Type.String()) });
18
25
  const Reload = Type.Integer({ minimum: 1 });
19
- /** Structural schema of every frame. Payloads (args, values, ops) are checked where they are used. */
26
+ /** Arrays a sender may pass as readonly. */
27
+ const List = (item) => Type.Immutable(Type.Array(item));
28
+ const RootRefSchema = Type.Object({ rootId: Name, codeVersion: Type.Integer({ minimum: 1 }) });
29
+ const WireErrorSchema = Type.Object({
30
+ code: Type.Enum(CHORD_ERROR_CODES),
31
+ message: Type.String(),
32
+ reason: Type.Optional(Type.String()),
33
+ });
34
+ const WireReloadOptionsSchema = Type.Object({
35
+ remove: Type.Optional(List(Type.String())),
36
+ gateDeadline: Type.Optional(Type.Number()),
37
+ retireDeadline: Type.Optional(Type.Number()),
38
+ });
39
+ /** A child's prepared reload, sent with its vote (same fields as `ReloadReport`, without children). */
40
+ const WireReloadReportSchema = Type.Object({
41
+ codeVersion: Type.Integer(),
42
+ previous: Type.Integer(),
43
+ changed: List(Type.String()),
44
+ removed: List(Type.String()),
45
+ prepared: List(Type.String()),
46
+ gated: List(Type.String()),
47
+ });
20
48
  const FrameSchema = Type.Union([
21
49
  Type.Object({
22
50
  t: Type.Literal("hello"),
51
+ /** The link protocol version (`PROTOCOL_VERSION`; absent: 1). Peers of another version close the link. */
52
+ protocol: Type.Optional(Type.Integer({ minimum: 1 })),
23
53
  name: Name,
24
54
  kind: Type.Union([Type.Literal("host"), Type.Literal("client")]),
25
- epoch: Type.Integer({ minimum: 0 }),
55
+ codeVersion: Type.Integer({ minimum: 0 }),
56
+ /** The sender acknowledges what it receives (`ack`). Flow control applies only when both hellos say so. */
57
+ acks: Type.Optional(Type.Boolean()),
26
58
  }),
59
+ /** Every frame received so far except `ack` frames, counted from the link's start, once processed. */
60
+ Type.Object({ t: Type.Literal("ack"), n: Type.Integer({ minimum: 1 }) }),
27
61
  Type.Object({
28
62
  t: Type.Literal("hold"),
29
63
  id: Id,
64
+ /**
65
+ * Target host name, resolved by the consumer from the token's `host` and `input`. Relays route on it after
66
+ * checking it against both; the target refuses an input its own contract places elsewhere.
67
+ */
30
68
  at: Name,
31
- token: Type.Object({
32
- name: Name,
33
- scope: Type.String(),
34
- version: Type.Integer(),
35
- input: Type.Unknown(),
36
- variants: Type.Record(Type.String(), Type.Unknown()),
37
- }),
69
+ /** The consumer's contract token; the provider checks that it covers it. Remote tokens carry `host`. */
70
+ token: DescriptionSchema,
71
+ /** Normalized with the consumer's contract. */
38
72
  input: Type.Unknown(),
73
+ /** A forwarded principal. Honored only on frames from the parent link. */
39
74
  principal: Type.Optional(Type.Unknown()),
40
- client: Type.Optional(Type.Boolean()),
75
+ /**
76
+ * The client connection the hold comes from: an opaque id the server assigned to it, forwarded by relays. Honored
77
+ * only on frames from the parent link; present means the hold is a client's (not a reload participant).
78
+ */
79
+ origin: Type.Optional(Name),
80
+ /** False: no state for this hold until `sub` (a method-only binding). */
81
+ state: Type.Optional(Type.Literal(false)),
82
+ /** The consumer already has values (a re-hold after a link loss): its unit is not urgent. */
83
+ again: Type.Optional(Type.Literal(true)),
41
84
  }),
42
85
  Type.Object({ t: Type.Literal("release"), id: Id }),
43
86
  Type.Object({
44
87
  t: Type.Literal("shape"),
45
88
  id: Id,
46
- kind: Type.Optional(Type.String()),
47
- members: Type.Array(Type.String()),
89
+ variant: Type.Optional(Type.String()),
90
+ members: List(Type.String()),
48
91
  contract: Type.String(),
49
- epoch: Type.Integer(),
92
+ /** The state members whose snapshots follow this frame; the consumer applies the shape once they are here. */
93
+ state: List(Name),
50
94
  }),
51
- Type.Object({ t: Type.Literal("gone"), id: Id, error: ErrorShape }),
95
+ Type.Object({ t: Type.Literal("gone"), id: Id, error: WireErrorSchema }),
52
96
  Type.Object({
53
97
  t: Type.Literal("call"),
54
98
  id: Id,
55
99
  hold: Id,
56
100
  member: Name,
57
- args: Type.Unknown(),
58
- root: Type.Optional(Root),
101
+ args: Type.Optional(Type.Unknown()),
102
+ /** Absent: the receiving host starts a root (browsers never carry roots). */
103
+ root: Type.Optional(RootRefSchema),
59
104
  }),
60
105
  Type.Object({
61
106
  t: Type.Literal("result"),
62
107
  id: Id,
63
108
  value: Type.Optional(Type.Unknown()),
64
- error: Type.Optional(ErrorShape),
109
+ error: Type.Optional(WireErrorSchema),
65
110
  }),
66
111
  Type.Object({ t: Type.Literal("cancel"), id: Id }),
67
- Type.Object({ t: Type.Literal("sub"), id: Id, hold: Id, member: Name }),
68
- Type.Object({ t: Type.Literal("unsub"), id: Id }),
112
+ /** Starts the state of a hold opened with `state: false` (answered with a unit). */
113
+ Type.Object({ t: Type.Literal("sub"), hold: Id }),
114
+ /** Stops the state of a hold. */
115
+ Type.Object({ t: Type.Literal("unsub"), hold: Id }),
116
+ /** Sends member `m` again as a reset (a relay that had to drop what waited for it). */
117
+ Type.Object({ t: Type.Literal("resync"), hold: Id, m: Name }),
69
118
  Type.Object({
70
119
  t: Type.Literal("state"),
71
- id: Id,
72
- snap: Type.Optional(Type.Unknown()),
73
- reset: Type.Optional(Type.Unknown()),
74
- ops: Type.Optional(Type.Array(Type.Array(Type.Unknown()))),
75
- error: Type.Optional(ErrorShape),
120
+ hold: Id,
121
+ m: Name,
122
+ /** `snap`/`reset` without `value`: the value is `undefined`. */
123
+ as: Type.Union([Type.Literal("snap"), Type.Literal("reset"), Type.Literal("ops"), Type.Literal("error")]),
124
+ value: Type.Optional(Type.Unknown()),
125
+ /** Encoded with the stream's delta encoder; the decoder validates each op. */
126
+ ops: Type.Optional(List(List(Type.Unknown()))),
127
+ error: Type.Optional(WireErrorSchema),
76
128
  }),
77
129
  Type.Object({ t: Type.Literal("ping"), n: Type.Integer() }),
78
130
  Type.Object({ t: Type.Literal("pong"), n: Type.Integer() }),
131
+ /** Ends the link (consumed by the link itself). `reason: "protocol-version <n>"`: the sender speaks version n. */
79
132
  Type.Object({ t: Type.Literal("bye"), reason: Type.Optional(Type.String()) }),
133
+ /** The root ended at its origin: its leases on objects end (sent on every link the root's calls used). */
80
134
  Type.Object({ t: Type.Literal("root_end"), rootId: Name }),
81
135
  Type.Object({ t: Type.Literal("invalidate"), pattern: Type.Record(Type.String(), Type.Unknown()) }),
82
136
  Type.Object({
83
137
  t: Type.Literal("prepare"),
84
138
  reload: Reload,
85
- epoch: Type.Integer({ minimum: 1 }),
86
- plugins: Type.Array(Type.String()),
87
- options: Type.Object({
88
- remove: Type.Optional(Type.Array(Type.String())),
89
- gateDeadline: Type.Optional(Type.Number()),
90
- retireDeadline: Type.Optional(Type.Number()),
91
- }),
139
+ codeVersion: Type.Integer({ minimum: 1 }),
140
+ plugins: List(Type.String()),
141
+ options: WireReloadOptionsSchema,
92
142
  }),
93
143
  Type.Object({
94
144
  t: Type.Literal("vote"),
95
145
  reload: Reload,
96
- error: Type.Optional(ErrorShape),
97
- report: Type.Optional(Type.Object({
98
- epoch: Type.Integer(),
99
- previous: Type.Integer(),
100
- changed: Type.Array(Type.String()),
101
- removed: Type.Array(Type.String()),
102
- prepared: Type.Array(Type.String()),
103
- gated: Type.Array(Type.String()),
104
- })),
146
+ error: Type.Optional(WireErrorSchema),
147
+ report: Type.Optional(WireReloadReportSchema),
105
148
  }),
106
149
  Type.Object({ t: Type.Literal("commit"), reload: Reload }),
107
- Type.Object({ t: Type.Literal("retire"), epoch: Type.Integer({ minimum: 1 }) }),
150
+ /** The parent dropped `codeVersion`: roots of it may still come from there no longer. */
151
+ Type.Object({ t: Type.Literal("retire"), codeVersion: Type.Integer({ minimum: 1 }) }),
108
152
  Type.Object({ t: Type.Literal("abort"), reload: Reload }),
109
153
  ]);
110
- /** A frame that passes structural validation; anything else is a protocol violation. */
154
+ /** A frame that passes validation; anything else is a protocol violation. */
111
155
  export const isFrame = (value) => Value.Check(FrameSchema, value);
156
+ export function toWireError(error) {
157
+ if (error instanceof ChordError) {
158
+ return error.reason === undefined
159
+ ? { code: error.code, message: error.message }
160
+ : { code: error.code, message: error.message, reason: error.reason };
161
+ }
162
+ return { code: "unavailable", message: error instanceof Error ? error.message : String(error) };
163
+ }
164
+ export const fromWireError = (error) => new ChordError(error.code, error.message.replace(/^[a-z_]+(\/[a-z_]+)?: /, ""), { reason: error.reason });
112
165
  //# sourceMappingURL=wire.js.map