miakapi 3.0.3-DEV → 4.0.0-alpha.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 (124) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +358 -0
  3. package/contracts/miakapp-v3.json +6 -0
  4. package/dist/access-token-provider.d.ts +8 -0
  5. package/dist/access-token-provider.d.ts.map +1 -0
  6. package/dist/access-token-provider.js +167 -0
  7. package/dist/access-token-provider.js.map +1 -0
  8. package/dist/api.d.ts +224 -0
  9. package/dist/api.d.ts.map +1 -0
  10. package/dist/api.js +47 -0
  11. package/dist/api.js.map +1 -0
  12. package/dist/browser-api.d.ts +113 -0
  13. package/dist/browser-api.d.ts.map +1 -0
  14. package/dist/browser-api.js +2 -0
  15. package/dist/browser-api.js.map +1 -0
  16. package/dist/browser-client.d.ts +3 -0
  17. package/dist/browser-client.d.ts.map +1 -0
  18. package/dist/browser-client.js +678 -0
  19. package/dist/browser-client.js.map +1 -0
  20. package/dist/browser-relay-credential-provider.d.ts +3 -0
  21. package/dist/browser-relay-credential-provider.d.ts.map +1 -0
  22. package/dist/browser-relay-credential-provider.js +243 -0
  23. package/dist/browser-relay-credential-provider.js.map +1 -0
  24. package/dist/browser.d.ts +4 -0
  25. package/dist/browser.d.ts.map +1 -0
  26. package/dist/browser.js +4 -0
  27. package/dist/browser.js.map +1 -0
  28. package/dist/coordinator.d.ts +3 -0
  29. package/dist/coordinator.d.ts.map +1 -0
  30. package/dist/coordinator.js +544 -0
  31. package/dist/coordinator.js.map +1 -0
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +4 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/internal/browser-errors.d.ts +31 -0
  37. package/dist/internal/browser-errors.d.ts.map +1 -0
  38. package/dist/internal/browser-errors.js +69 -0
  39. package/dist/internal/browser-errors.js.map +1 -0
  40. package/dist/internal/browser-socket.d.ts +8 -0
  41. package/dist/internal/browser-socket.d.ts.map +1 -0
  42. package/dist/internal/browser-socket.js +204 -0
  43. package/dist/internal/browser-socket.js.map +1 -0
  44. package/dist/internal/calls.d.ts +33 -0
  45. package/dist/internal/calls.d.ts.map +1 -0
  46. package/dist/internal/calls.js +525 -0
  47. package/dist/internal/calls.js.map +1 -0
  48. package/dist/internal/control-plane-response.d.ts +10 -0
  49. package/dist/internal/control-plane-response.d.ts.map +1 -0
  50. package/dist/internal/control-plane-response.js +283 -0
  51. package/dist/internal/control-plane-response.js.map +1 -0
  52. package/dist/internal/declarations.d.ts +62 -0
  53. package/dist/internal/declarations.d.ts.map +1 -0
  54. package/dist/internal/declarations.js +427 -0
  55. package/dist/internal/declarations.js.map +1 -0
  56. package/dist/internal/errors.d.ts +31 -0
  57. package/dist/internal/errors.d.ts.map +1 -0
  58. package/dist/internal/errors.js +72 -0
  59. package/dist/internal/errors.js.map +1 -0
  60. package/dist/internal/events.d.ts +27 -0
  61. package/dist/internal/events.d.ts.map +1 -0
  62. package/dist/internal/events.js +207 -0
  63. package/dist/internal/events.js.map +1 -0
  64. package/dist/internal/presence.d.ts +15 -0
  65. package/dist/internal/presence.d.ts.map +1 -0
  66. package/dist/internal/presence.js +99 -0
  67. package/dist/internal/presence.js.map +1 -0
  68. package/dist/internal/resources.d.ts +33 -0
  69. package/dist/internal/resources.d.ts.map +1 -0
  70. package/dist/internal/resources.js +136 -0
  71. package/dist/internal/resources.js.map +1 -0
  72. package/dist/internal/runtime.d.ts +34 -0
  73. package/dist/internal/runtime.d.ts.map +1 -0
  74. package/dist/internal/runtime.js +17 -0
  75. package/dist/internal/runtime.js.map +1 -0
  76. package/dist/internal/session.d.ts +33 -0
  77. package/dist/internal/session.d.ts.map +1 -0
  78. package/dist/internal/session.js +199 -0
  79. package/dist/internal/session.js.map +1 -0
  80. package/dist/internal/socket.d.ts +6 -0
  81. package/dist/internal/socket.d.ts.map +1 -0
  82. package/dist/internal/socket.js +176 -0
  83. package/dist/internal/socket.js.map +1 -0
  84. package/dist/internal/state.d.ts +21 -0
  85. package/dist/internal/state.d.ts.map +1 -0
  86. package/dist/internal/state.js +133 -0
  87. package/dist/internal/state.js.map +1 -0
  88. package/dist/internal/user-calls.d.ts +27 -0
  89. package/dist/internal/user-calls.d.ts.map +1 -0
  90. package/dist/internal/user-calls.js +325 -0
  91. package/dist/internal/user-calls.js.map +1 -0
  92. package/dist/internal/user-session.d.ts +36 -0
  93. package/dist/internal/user-session.d.ts.map +1 -0
  94. package/dist/internal/user-session.js +250 -0
  95. package/dist/internal/user-session.js.map +1 -0
  96. package/dist/internal/user-state.d.ts +21 -0
  97. package/dist/internal/user-state.d.ts.map +1 -0
  98. package/dist/internal/user-state.js +240 -0
  99. package/dist/internal/user-state.js.map +1 -0
  100. package/dist/internal/validation.d.ts +32 -0
  101. package/dist/internal/validation.d.ts.map +1 -0
  102. package/dist/internal/validation.js +553 -0
  103. package/dist/internal/validation.js.map +1 -0
  104. package/dist/protocol/codec.d.ts +75 -0
  105. package/dist/protocol/codec.d.ts.map +1 -0
  106. package/dist/protocol/codec.js +1091 -0
  107. package/dist/protocol/codec.js.map +1 -0
  108. package/dist/protocol/session.d.ts +14 -0
  109. package/dist/protocol/session.d.ts.map +1 -0
  110. package/dist/protocol/session.js +125 -0
  111. package/dist/protocol/session.js.map +1 -0
  112. package/dist/protocol/user-session.d.ts +14 -0
  113. package/dist/protocol/user-session.d.ts.map +1 -0
  114. package/dist/protocol/user-session.js +116 -0
  115. package/dist/protocol/user-session.js.map +1 -0
  116. package/package.json +58 -10
  117. package/.eslintrc.js +0 -16
  118. package/.gitattributes +0 -2
  119. package/main.js +0 -305
  120. package/miakode.js +0 -56
  121. package/screenshots/bigGrid.jpg +0 -0
  122. package/screenshots/formExample.jpg +0 -0
  123. package/screenshots/properties&table.jpg +0 -0
  124. package/test.js +0 -45
@@ -0,0 +1,1091 @@
1
+ import { decode } from '@msgpack/msgpack';
2
+ export const LIMITS = {
3
+ frameBytes: 262_144,
4
+ depth: 16,
5
+ values: 16_384,
6
+ stringBytes: 65_536,
7
+ binaryBytes: 131_072,
8
+ arrayItems: 4_096,
9
+ mapEntries: 4_096,
10
+ mapKeyBytes: 256,
11
+ statePathsPerCoordinator: 4_096,
12
+ statePathsPerHome: 16_384,
13
+ declarationsPerCoordinator: 1_024,
14
+ subscriptions: 256,
15
+ inflightCalls: 128,
16
+ streamCredit: 32,
17
+ callTimeoutMs: 300_000,
18
+ };
19
+ export const Opcode = {
20
+ Hello: 0x00,
21
+ Welcome: 0x01,
22
+ Error: 0x02,
23
+ Fatal: 0x03,
24
+ Reauth: 0x04,
25
+ ReauthOk: 0x05,
26
+ HomeStatus: 0x06,
27
+ Goaway: 0x07,
28
+ StateSync: 0x10,
29
+ StateSyncOk: 0x11,
30
+ StateDict: 0x12,
31
+ StateSnapshot: 0x13,
32
+ StatePatch: 0x14,
33
+ StateSet: 0x15,
34
+ StateSetOk: 0x16,
35
+ StateAclSync: 0x17,
36
+ StateAclOk: 0x18,
37
+ StateResync: 0x19,
38
+ EventSync: 0x20,
39
+ EventSyncOk: 0x21,
40
+ TopicDict: 0x22,
41
+ EventAclSync: 0x23,
42
+ EventAclOk: 0x24,
43
+ Subscribe: 0x25,
44
+ SubscribeOk: 0x26,
45
+ Unsubscribe: 0x27,
46
+ UnsubscribeOk: 0x28,
47
+ Event: 0x29,
48
+ FunctionSync: 0x30,
49
+ FunctionSyncOk: 0x31,
50
+ FunctionDict: 0x32,
51
+ Call: 0x33,
52
+ CallDispatch: 0x34,
53
+ CallAccepted: 0x35,
54
+ CallResult: 0x36,
55
+ CallError: 0x37,
56
+ CallCancel: 0x38,
57
+ CallCredit: 0x39,
58
+ PresenceSnapshot: 0x40,
59
+ PresenceChange: 0x41,
60
+ };
61
+ const KNOWN_CORE_OPCODES = new Set(Object.values(Opcode));
62
+ const ERROR_CORRELATION_SOURCE_OPCODES = new Set([
63
+ Opcode.Reauth,
64
+ Opcode.StateSync,
65
+ Opcode.StateSet,
66
+ Opcode.StateAclSync,
67
+ Opcode.StateResync,
68
+ Opcode.EventSync,
69
+ Opcode.EventAclSync,
70
+ Opcode.Subscribe,
71
+ Opcode.Unsubscribe,
72
+ Opcode.Event,
73
+ Opcode.FunctionSync,
74
+ Opcode.Call,
75
+ Opcode.CallResult,
76
+ Opcode.CallError,
77
+ Opcode.CallCancel,
78
+ Opcode.CallCredit,
79
+ ]);
80
+ const KNOWN_CORE_ERROR_CODES = new Set([
81
+ 1000, 1001, 1002, 1003, 1004, 1005,
82
+ 1100, 1101, 1102,
83
+ 1200, 1201, 1202, 1203,
84
+ 1300, 1301, 1302, 1303, 1304, 1305,
85
+ 1400, 1401, 1402, 1403, 1404, 1405,
86
+ 1500,
87
+ ]);
88
+ const APPLICATION_ERROR_CODE_MINIMUM = 2000;
89
+ const APPLICATION_ERROR_CODE_MAXIMUM = 2999;
90
+ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
91
+ const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER;
92
+ const textEncoder = new TextEncoder();
93
+ const textDecoder = new TextDecoder('utf-8', { fatal: true });
94
+ export class ProtocolError extends Error {
95
+ kind;
96
+ constructor(kind, message) {
97
+ super(message);
98
+ this.name = 'ProtocolError';
99
+ this.kind = kind;
100
+ }
101
+ }
102
+ function fail(kind, message) {
103
+ throw new ProtocolError(kind, message);
104
+ }
105
+ function accountEncodedBytes(state, length) {
106
+ state.bytes += length;
107
+ if (state.bytes > LIMITS.frameBytes - 1) {
108
+ fail('frame_too_large', 'encoded frame exceeds byte limit');
109
+ }
110
+ }
111
+ function encodedAtom(state, value) {
112
+ accountEncodedBytes(state, value.length);
113
+ return value;
114
+ }
115
+ function ensureAvailable(input, offset, length) {
116
+ if (length < 0 || offset < 0 || offset + length > input.length) {
117
+ fail('malformed', 'truncated MessagePack value');
118
+ }
119
+ }
120
+ function view(input, offset, length) {
121
+ ensureAvailable(input, offset, length);
122
+ return new DataView(input.buffer, input.byteOffset + offset, length);
123
+ }
124
+ function compareBytes(left, right) {
125
+ const common = Math.min(left.length, right.length);
126
+ for (let index = 0; index < common; index += 1) {
127
+ const difference = left[index] - right[index];
128
+ if (difference !== 0)
129
+ return difference;
130
+ }
131
+ return left.length - right.length;
132
+ }
133
+ function scanString(input, start, length, next) {
134
+ if (length > LIMITS.stringBytes)
135
+ fail('limit', 'string length exceeds limit');
136
+ ensureAvailable(input, start, length);
137
+ const bytes = input.subarray(start, start + length);
138
+ let value;
139
+ try {
140
+ value = textDecoder.decode(bytes);
141
+ }
142
+ catch {
143
+ fail('invalid_value', 'string is not valid UTF-8');
144
+ }
145
+ return {
146
+ end: next + length,
147
+ kind: 'string',
148
+ stringBytes: bytes,
149
+ stringValue: value,
150
+ };
151
+ }
152
+ function scanBinary(input, start, length, next) {
153
+ if (length > LIMITS.binaryBytes)
154
+ fail('limit', 'binary length exceeds limit');
155
+ ensureAvailable(input, start, length);
156
+ return { end: next + length, kind: 'binary' };
157
+ }
158
+ function scanArray(input, offset, length, depth, state) {
159
+ if (length > LIMITS.arrayItems)
160
+ fail('limit', 'array length exceeds limit');
161
+ let cursor = offset;
162
+ for (let index = 0; index < length; index += 1) {
163
+ cursor = scanValue(input, cursor, depth + 1, state).end;
164
+ }
165
+ return { end: cursor, kind: 'array' };
166
+ }
167
+ function scanMap(input, offset, length, depth, state) {
168
+ if (length > LIMITS.mapEntries)
169
+ fail('limit', 'map length exceeds limit');
170
+ let cursor = offset;
171
+ let previous;
172
+ for (let index = 0; index < length; index += 1) {
173
+ const key = scanValue(input, cursor, depth + 1, state);
174
+ if (key.kind !== 'string' || !key.stringBytes || key.stringValue === undefined) {
175
+ fail('invalid_value', 'map keys must be strings');
176
+ }
177
+ if (key.stringBytes.length > LIMITS.mapKeyBytes)
178
+ fail('limit', 'map key exceeds limit');
179
+ if (key.stringValue === '__proto__')
180
+ fail('invalid_value', 'reserved map key');
181
+ if (previous) {
182
+ const order = compareBytes(previous, key.stringBytes);
183
+ if (order === 0)
184
+ fail('invalid_value', 'duplicate map key');
185
+ if (order > 0)
186
+ fail('non_canonical', 'map keys are not in UTF-8 byte order');
187
+ }
188
+ previous = key.stringBytes;
189
+ cursor = scanValue(input, key.end, depth + 1, state).end;
190
+ }
191
+ return { end: cursor, kind: 'map' };
192
+ }
193
+ function scanValue(input, offset, depth, state) {
194
+ if (depth > LIMITS.depth)
195
+ fail('limit', 'value nesting exceeds limit');
196
+ state.nodes += 1;
197
+ if (state.nodes > LIMITS.values)
198
+ fail('limit', 'payload value count exceeds limit');
199
+ ensureAvailable(input, offset, 1);
200
+ const marker = input[offset];
201
+ const next = offset + 1;
202
+ if (marker <= 0x7f || marker >= 0xe0)
203
+ return { end: next, kind: 'integer' };
204
+ if (marker >= 0x80 && marker <= 0x8f) {
205
+ return scanMap(input, next, marker & 0x0f, depth, state);
206
+ }
207
+ if (marker >= 0x90 && marker <= 0x9f) {
208
+ return scanArray(input, next, marker & 0x0f, depth, state);
209
+ }
210
+ if (marker >= 0xa0 && marker <= 0xbf) {
211
+ return scanString(input, next, marker & 0x1f, next);
212
+ }
213
+ switch (marker) {
214
+ case 0xc0:
215
+ return { end: next, kind: 'null' };
216
+ case 0xc1:
217
+ return fail('malformed', 'reserved MessagePack marker');
218
+ case 0xc2:
219
+ case 0xc3:
220
+ return { end: next, kind: 'boolean' };
221
+ case 0xc4: {
222
+ ensureAvailable(input, next, 1);
223
+ const length = input[next];
224
+ return scanBinary(input, next + 1, length, next + 1);
225
+ }
226
+ case 0xc5: {
227
+ const length = view(input, next, 2).getUint16(0);
228
+ if (length <= 0xff)
229
+ fail('non_canonical', 'non-shortest binary length');
230
+ return scanBinary(input, next + 2, length, next + 2);
231
+ }
232
+ case 0xc6: {
233
+ const length = view(input, next, 4).getUint32(0);
234
+ if (length <= 0xffff)
235
+ fail('non_canonical', 'non-shortest binary length');
236
+ return scanBinary(input, next + 4, length, next + 4);
237
+ }
238
+ case 0xca:
239
+ ensureAvailable(input, next, 4);
240
+ return fail('non_canonical', 'float32 is forbidden');
241
+ case 0xcb: {
242
+ const value = view(input, next, 8).getFloat64(0);
243
+ if (!Number.isFinite(value) || Object.is(value, -0)) {
244
+ fail('invalid_value', 'non-finite and negative-zero floats are forbidden');
245
+ }
246
+ if (Number.isInteger(value)) {
247
+ if (Number.isSafeInteger(value))
248
+ fail('non_canonical', 'integral float must be an integer');
249
+ fail('invalid_value', 'integral float exceeds the safe-integer range');
250
+ }
251
+ return { end: next + 8, kind: 'float' };
252
+ }
253
+ case 0xcc: {
254
+ ensureAvailable(input, next, 1);
255
+ const value = input[next];
256
+ if (value <= 0x7f)
257
+ fail('non_canonical', 'non-shortest unsigned integer');
258
+ return { end: next + 1, kind: 'integer' };
259
+ }
260
+ case 0xcd: {
261
+ const value = view(input, next, 2).getUint16(0);
262
+ if (value <= 0xff)
263
+ fail('non_canonical', 'non-shortest unsigned integer');
264
+ return { end: next + 2, kind: 'integer' };
265
+ }
266
+ case 0xce: {
267
+ const value = view(input, next, 4).getUint32(0);
268
+ if (value <= 0xffff)
269
+ fail('non_canonical', 'non-shortest unsigned integer');
270
+ return { end: next + 4, kind: 'integer' };
271
+ }
272
+ case 0xcf: {
273
+ const value = view(input, next, 8).getBigUint64(0);
274
+ if (value <= 0xffffffffn)
275
+ fail('non_canonical', 'non-shortest unsigned integer');
276
+ if (value > BigInt(MAX_SAFE_INTEGER))
277
+ fail('invalid_value', 'integer exceeds safe range');
278
+ return { end: next + 8, kind: 'integer' };
279
+ }
280
+ case 0xd0: {
281
+ const value = view(input, next, 1).getInt8(0);
282
+ if (value >= -32)
283
+ fail('non_canonical', 'non-shortest signed integer');
284
+ return { end: next + 1, kind: 'integer' };
285
+ }
286
+ case 0xd1: {
287
+ const value = view(input, next, 2).getInt16(0);
288
+ if (value >= -128)
289
+ fail('non_canonical', 'non-shortest signed integer');
290
+ return { end: next + 2, kind: 'integer' };
291
+ }
292
+ case 0xd2: {
293
+ const value = view(input, next, 4).getInt32(0);
294
+ if (value >= -32_768)
295
+ fail('non_canonical', 'non-shortest signed integer');
296
+ return { end: next + 4, kind: 'integer' };
297
+ }
298
+ case 0xd3: {
299
+ const value = view(input, next, 8).getBigInt64(0);
300
+ if (value >= -2147483648n)
301
+ fail('non_canonical', 'non-shortest signed integer');
302
+ if (value < BigInt(MIN_SAFE_INTEGER))
303
+ fail('invalid_value', 'integer exceeds safe range');
304
+ return { end: next + 8, kind: 'integer' };
305
+ }
306
+ case 0xd9: {
307
+ ensureAvailable(input, next, 1);
308
+ const length = input[next];
309
+ if (length <= 31)
310
+ fail('non_canonical', 'non-shortest string length');
311
+ return scanString(input, next + 1, length, next + 1);
312
+ }
313
+ case 0xda: {
314
+ const length = view(input, next, 2).getUint16(0);
315
+ if (length <= 0xff)
316
+ fail('non_canonical', 'non-shortest string length');
317
+ return scanString(input, next + 2, length, next + 2);
318
+ }
319
+ case 0xdb: {
320
+ const length = view(input, next, 4).getUint32(0);
321
+ if (length <= 0xffff)
322
+ fail('non_canonical', 'non-shortest string length');
323
+ return scanString(input, next + 4, length, next + 4);
324
+ }
325
+ case 0xdc: {
326
+ const length = view(input, next, 2).getUint16(0);
327
+ if (length <= 15)
328
+ fail('non_canonical', 'non-shortest array length');
329
+ return scanArray(input, next + 2, length, depth, state);
330
+ }
331
+ case 0xdd: {
332
+ const length = view(input, next, 4).getUint32(0);
333
+ if (length > LIMITS.arrayItems)
334
+ fail('limit', 'array length exceeds limit');
335
+ if (length <= 0xffff)
336
+ fail('non_canonical', 'non-shortest array length');
337
+ return scanArray(input, next + 4, length, depth, state);
338
+ }
339
+ case 0xde: {
340
+ const length = view(input, next, 2).getUint16(0);
341
+ if (length <= 15)
342
+ fail('non_canonical', 'non-shortest map length');
343
+ return scanMap(input, next + 2, length, depth, state);
344
+ }
345
+ case 0xdf: {
346
+ const length = view(input, next, 4).getUint32(0);
347
+ if (length > LIMITS.mapEntries)
348
+ fail('limit', 'map length exceeds limit');
349
+ if (length <= 0xffff)
350
+ fail('non_canonical', 'non-shortest map length');
351
+ return scanMap(input, next + 4, length, depth, state);
352
+ }
353
+ default:
354
+ if ((marker >= 0xc7 && marker <= 0xc9) || (marker >= 0xd4 && marker <= 0xd8)) {
355
+ return fail('invalid_value', 'MessagePack extensions are forbidden');
356
+ }
357
+ return fail('malformed', `unsupported MessagePack marker 0x${marker.toString(16)}`);
358
+ }
359
+ }
360
+ function byte(value) {
361
+ return Uint8Array.of(value);
362
+ }
363
+ function concat(parts) {
364
+ const length = parts.reduce((total, part) => total + part.length, 0);
365
+ const output = new Uint8Array(length);
366
+ let offset = 0;
367
+ for (const part of parts) {
368
+ output.set(part, offset);
369
+ offset += part.length;
370
+ }
371
+ return output;
372
+ }
373
+ function numberBytes(marker, length, setter) {
374
+ const output = new Uint8Array(1 + length);
375
+ output[0] = marker;
376
+ setter(new DataView(output.buffer, 1, length));
377
+ return output;
378
+ }
379
+ function encodeInteger(value) {
380
+ if (value >= 0) {
381
+ if (value <= 0x7f)
382
+ return byte(value);
383
+ if (value <= 0xff)
384
+ return Uint8Array.of(0xcc, value);
385
+ if (value <= 0xffff)
386
+ return numberBytes(0xcd, 2, (target) => target.setUint16(0, value));
387
+ if (value <= 0xffff_ffff)
388
+ return numberBytes(0xce, 4, (target) => target.setUint32(0, value));
389
+ return numberBytes(0xcf, 8, (target) => target.setBigUint64(0, BigInt(value)));
390
+ }
391
+ if (value >= -32)
392
+ return byte(0x100 + value);
393
+ if (value >= -128)
394
+ return numberBytes(0xd0, 1, (target) => target.setInt8(0, value));
395
+ if (value >= -32_768)
396
+ return numberBytes(0xd1, 2, (target) => target.setInt16(0, value));
397
+ if (value >= -2_147_483_648)
398
+ return numberBytes(0xd2, 4, (target) => target.setInt32(0, value));
399
+ return numberBytes(0xd3, 8, (target) => target.setBigInt64(0, BigInt(value)));
400
+ }
401
+ function encodeLength(length, fixedBase, fixedMaximum, marker8, marker16, marker32) {
402
+ if (length <= fixedMaximum)
403
+ return byte(fixedBase + length);
404
+ if (marker8 !== null && length <= 0xff)
405
+ return Uint8Array.of(marker8, length);
406
+ if (length <= 0xffff)
407
+ return numberBytes(marker16, 2, (target) => target.setUint16(0, length));
408
+ return numberBytes(marker32, 4, (target) => target.setUint32(0, length));
409
+ }
410
+ function hasUnpairedSurrogate(value) {
411
+ for (let index = 0; index < value.length; index += 1) {
412
+ const unit = value.charCodeAt(index);
413
+ if (unit >= 0xd800 && unit <= 0xdbff) {
414
+ const following = value.charCodeAt(index + 1);
415
+ if (index + 1 >= value.length || following < 0xdc00 || following > 0xdfff)
416
+ return true;
417
+ index += 1;
418
+ }
419
+ else if (unit >= 0xdc00 && unit <= 0xdfff)
420
+ return true;
421
+ }
422
+ return false;
423
+ }
424
+ function validUtf8String(value, maximum, label) {
425
+ if (hasUnpairedSurrogate(value))
426
+ fail('invalid_value', `${label} contains an unpaired surrogate`);
427
+ if (value.length > maximum)
428
+ fail('limit', `${label} exceeds byte limit`);
429
+ const encoded = textEncoder.encode(value);
430
+ if (encoded.length > maximum)
431
+ fail('limit', `${label} exceeds byte limit`);
432
+ return encoded;
433
+ }
434
+ function encodeValue(value, depth, state) {
435
+ if (depth > LIMITS.depth)
436
+ fail('limit', 'value nesting exceeds limit');
437
+ state.nodes += 1;
438
+ if (state.nodes > LIMITS.values)
439
+ fail('limit', 'payload value count exceeds limit');
440
+ if (value === null)
441
+ return encodedAtom(state, byte(0xc0));
442
+ if (value === false)
443
+ return encodedAtom(state, byte(0xc2));
444
+ if (value === true)
445
+ return encodedAtom(state, byte(0xc3));
446
+ if (typeof value === 'number') {
447
+ if (!Number.isFinite(value) || Object.is(value, -0)) {
448
+ fail('invalid_value', 'non-finite and negative-zero numbers are forbidden');
449
+ }
450
+ if (Number.isInteger(value)) {
451
+ if (!Number.isSafeInteger(value))
452
+ fail('invalid_value', 'integer exceeds safe range');
453
+ return encodedAtom(state, encodeInteger(value));
454
+ }
455
+ return encodedAtom(state, numberBytes(0xcb, 8, (target) => target.setFloat64(0, value)));
456
+ }
457
+ if (typeof value === 'string') {
458
+ const encoded = validUtf8String(value, LIMITS.stringBytes, 'string');
459
+ const prefix = encodeLength(encoded.length, 0xa0, 31, 0xd9, 0xda, 0xdb);
460
+ accountEncodedBytes(state, prefix.length + encoded.length);
461
+ return concat([prefix, encoded]);
462
+ }
463
+ if (value instanceof Uint8Array) {
464
+ if (value.length > LIMITS.binaryBytes)
465
+ fail('limit', 'binary length exceeds limit');
466
+ const prefix = encodeLength(value.length, 0, -1, 0xc4, 0xc5, 0xc6);
467
+ accountEncodedBytes(state, prefix.length + value.length);
468
+ return concat([prefix, value]);
469
+ }
470
+ if (Array.isArray(value)) {
471
+ if (value.length > LIMITS.arrayItems)
472
+ fail('limit', 'array length exceeds limit');
473
+ const prefix = encodeLength(value.length, 0x90, 15, null, 0xdc, 0xdd);
474
+ accountEncodedBytes(state, prefix.length);
475
+ const encoded = value.map((item) => encodeValue(item, depth + 1, state));
476
+ return concat([prefix, ...encoded]);
477
+ }
478
+ if (typeof value !== 'object')
479
+ fail('invalid_value', 'unsupported protocol value');
480
+ const prototype = Object.getPrototypeOf(value);
481
+ if (prototype !== Object.prototype && prototype !== null) {
482
+ fail('invalid_value', 'only plain objects are protocol maps');
483
+ }
484
+ const keys = Object.keys(value);
485
+ if (Reflect.ownKeys(value).length !== keys.length) {
486
+ fail('invalid_value', 'symbolic or non-enumerable map keys are forbidden');
487
+ }
488
+ if (keys.length > LIMITS.mapEntries)
489
+ fail('limit', 'map length exceeds limit');
490
+ const sorted = keys.map((key) => ({
491
+ key,
492
+ bytes: validUtf8String(key, LIMITS.mapKeyBytes, 'map key'),
493
+ })).sort((left, right) => compareBytes(left.bytes, right.bytes));
494
+ const prefix = encodeLength(sorted.length, 0x80, 15, null, 0xde, 0xdf);
495
+ accountEncodedBytes(state, prefix.length);
496
+ const parts = [prefix];
497
+ for (const { key } of sorted) {
498
+ if (key === '__proto__')
499
+ fail('invalid_value', 'reserved map key');
500
+ parts.push(encodeValue(key, depth + 1, state));
501
+ const child = value[key];
502
+ if (child === undefined)
503
+ fail('invalid_value', 'undefined map values are forbidden');
504
+ parts.push(encodeValue(child, depth + 1, state));
505
+ }
506
+ return concat(parts);
507
+ }
508
+ function asArray(value, label, maximum = LIMITS.arrayItems) {
509
+ if (!Array.isArray(value) || value.length > maximum)
510
+ fail('invalid_frame', `${label} must be an array`);
511
+ return value;
512
+ }
513
+ function exactTuple(value, length, label) {
514
+ const tuple = asArray(value, label);
515
+ if (tuple.length !== length)
516
+ fail('invalid_frame', `${label} must contain ${length} fields`);
517
+ return tuple;
518
+ }
519
+ function integer(value, minimum, maximum, label) {
520
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
521
+ fail('invalid_frame', `${label} must be an integer in range`);
522
+ }
523
+ return value;
524
+ }
525
+ function coreErrorCode(value, label) {
526
+ const code = integer(value, 1, 0xffff, label);
527
+ if (!KNOWN_CORE_ERROR_CODES.has(code))
528
+ fail('invalid_frame', `${label} is not a known core error code`);
529
+ return code;
530
+ }
531
+ function callErrorCode(value, label) {
532
+ const code = integer(value, 1, 0xffff, label);
533
+ const isApplicationCode = code >= APPLICATION_ERROR_CODE_MINIMUM
534
+ && code <= APPLICATION_ERROR_CODE_MAXIMUM;
535
+ if (!KNOWN_CORE_ERROR_CODES.has(code) && !isApplicationCode) {
536
+ fail('invalid_frame', `${label} is not a permitted call error code`);
537
+ }
538
+ return code;
539
+ }
540
+ function positiveId(value, label) {
541
+ return integer(value, 1, MAX_SAFE_INTEGER, label);
542
+ }
543
+ function correlationIdOrZero(value, label) {
544
+ return integer(value, 0, MAX_SAFE_INTEGER, label);
545
+ }
546
+ function errorCorrelation(correlationValue, sourceOpcodeValue) {
547
+ const correlationId = correlationIdOrZero(correlationValue, 'ERROR.correlationId');
548
+ const sourceOpcode = integer(sourceOpcodeValue, 0, 0xff, 'ERROR.sourceOpcode');
549
+ if (correlationId === 0) {
550
+ if (sourceOpcode !== 0) {
551
+ fail('invalid_frame', 'ERROR.sourceOpcode must be zero without an origin');
552
+ }
553
+ return;
554
+ }
555
+ if (!ERROR_CORRELATION_SOURCE_OPCODES.has(sourceOpcode)) {
556
+ fail('invalid_frame', 'ERROR.sourceOpcode is not an originating frame');
557
+ }
558
+ }
559
+ function booleanValue(value, label) {
560
+ if (typeof value !== 'boolean')
561
+ fail('invalid_frame', `${label} must be a boolean`);
562
+ return value;
563
+ }
564
+ function stringValue(value, minimum, maximum, label) {
565
+ if (typeof value !== 'string')
566
+ fail('invalid_frame', `${label} must be a string`);
567
+ if (hasUnpairedSurrogate(value))
568
+ fail('invalid_frame', `${label} is not valid UTF-8`);
569
+ if (value.length > maximum)
570
+ fail('invalid_frame', `${label} exceeds byte limit`);
571
+ const bytes = textEncoder.encode(value);
572
+ if (bytes.length < minimum || bytes.length > maximum) {
573
+ fail('invalid_frame', `${label} must be a string in range`);
574
+ }
575
+ return value;
576
+ }
577
+ function binaryValue(value, length, label) {
578
+ if (!(value instanceof Uint8Array) || value.length !== length) {
579
+ fail('invalid_frame', `${label} must be ${length} binary bytes`);
580
+ }
581
+ return value;
582
+ }
583
+ function nullableString(value, maximum, label) {
584
+ if (value === null)
585
+ return null;
586
+ return stringValue(value, 1, maximum, label);
587
+ }
588
+ function opaqueId(value, label) {
589
+ const result = stringValue(value, 1, 128, label);
590
+ if (/\p{Cc}/u.test(result))
591
+ fail('invalid_frame', `${label} contains a control character`);
592
+ return result;
593
+ }
594
+ function coordinatorName(value, label) {
595
+ const result = stringValue(value, 1, 64, label);
596
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(result)) {
597
+ fail('invalid_frame', `${label} is not a coordinator name`);
598
+ }
599
+ return result;
600
+ }
601
+ function structuredName(value, label) {
602
+ const result = stringValue(value, 1, 256, label);
603
+ if (/\p{Cc}/u.test(result) || result.includes('*') || result.startsWith('.') || result.endsWith('.') || result.includes('..')) {
604
+ fail('invalid_frame', `${label} is not a valid dotted name`);
605
+ }
606
+ return result;
607
+ }
608
+ function pattern(value, label) {
609
+ const result = stringValue(value, 1, 256, label);
610
+ structuredName(result.endsWith('.*') ? result.slice(0, -2) : result, label);
611
+ return result;
612
+ }
613
+ function uniqueStrings(values, validator, label) {
614
+ const seen = new Set();
615
+ values.forEach((value, index) => {
616
+ const item = validator(value, `${label}[${index}]`);
617
+ if (seen.has(item))
618
+ fail('invalid_frame', `${label} contains a duplicate`);
619
+ seen.add(item);
620
+ });
621
+ }
622
+ function uniqueIds(values, label) {
623
+ const seen = new Set();
624
+ values.forEach((value, index) => {
625
+ const item = positiveId(value, `${label}[${index}]`);
626
+ if (seen.has(item))
627
+ fail('invalid_frame', `${label} contains a duplicate`);
628
+ seen.add(item);
629
+ });
630
+ }
631
+ function epoch(value, label) {
632
+ binaryValue(value, 16, label);
633
+ }
634
+ function coordinators(value, label) {
635
+ const entries = asArray(value, label, 64);
636
+ const names = new Set();
637
+ entries.forEach((entry, index) => {
638
+ const fields = exactTuple(entry, 3, `${label}[${index}]`);
639
+ const name = coordinatorName(fields[0], `${label}[${index}].name`);
640
+ if (names.has(name))
641
+ fail('invalid_frame', `${label} contains a duplicate coordinator`);
642
+ names.add(name);
643
+ positiveId(fields[1], `${label}[${index}].generation`);
644
+ integer(fields[2], 1, 2, `${label}[${index}].status`);
645
+ });
646
+ }
647
+ function dictionary(value, maximum, nameLabel) {
648
+ const entries = asArray(value, 'dictionary', maximum);
649
+ const ids = new Set();
650
+ const names = new Set();
651
+ entries.forEach((entry, index) => {
652
+ const fields = exactTuple(entry, 2, `dictionary[${index}]`);
653
+ const id = positiveId(fields[0], `dictionary[${index}].id`);
654
+ const name = structuredName(fields[1], `dictionary[${index}].${nameLabel}`);
655
+ if (ids.has(id) || names.has(name))
656
+ fail('invalid_frame', 'dictionary entries must be unique');
657
+ ids.add(id);
658
+ names.add(name);
659
+ });
660
+ }
661
+ function mutations(value, label) {
662
+ const entries = asArray(value, label, LIMITS.statePathsPerCoordinator);
663
+ const ids = new Set();
664
+ entries.forEach((entry, index) => {
665
+ const fields = asArray(entry, `${label}[${index}]`);
666
+ if (fields.length !== 2 && fields.length !== 3)
667
+ fail('invalid_frame', 'mutation has wrong arity');
668
+ const id = positiveId(fields[0], `${label}[${index}].pathId`);
669
+ if (ids.has(id))
670
+ fail('invalid_frame', `${label} contains a duplicate path ID`);
671
+ ids.add(id);
672
+ const operation = integer(fields[1], 0, 1, `${label}[${index}].operation`);
673
+ if ((operation === 0 && fields.length !== 3) || (operation === 1 && fields.length !== 2)) {
674
+ fail('invalid_frame', 'mutation arity does not match operation');
675
+ }
676
+ });
677
+ }
678
+ function aclPatterns(value, label) {
679
+ const values = asArray(value, label);
680
+ uniqueStrings(values, pattern, label);
681
+ }
682
+ function target(kindValue, targetValue, label) {
683
+ const kind = integer(kindValue, 0, 2, `${label}.kind`);
684
+ if (kind === 0 && targetValue !== null)
685
+ fail('invalid_frame', `${label} default target must be null`);
686
+ if (kind === 1)
687
+ positiveId(targetValue, `${label}.session`);
688
+ if (kind === 2)
689
+ coordinatorName(targetValue, `${label}.coordinator`);
690
+ }
691
+ function principal(value, label) {
692
+ const fields = exactTuple(value, 5, label);
693
+ const kind = integer(fields[0], 1, 3, `${label}.kind`);
694
+ opaqueId(fields[1], `${label}.id`);
695
+ positiveId(fields[2], `${label}.sessionId`);
696
+ if (kind === 2)
697
+ coordinatorName(fields[3], `${label}.coordinatorName`);
698
+ else if (fields[3] !== null)
699
+ fail('invalid_frame', `${label}.coordinatorName must be null`);
700
+ nullableString(fields[4], 320, `${label}.email`);
701
+ }
702
+ function subscriptionPayload(payload, label) {
703
+ const fields = exactTuple(payload, 2, label);
704
+ positiveId(fields[0], `${label}.requestId`);
705
+ const ids = asArray(fields[1], `${label}.topicIds`, LIMITS.subscriptions);
706
+ uniqueIds(ids, `${label}.topicIds`);
707
+ }
708
+ function validateFrame(frame) {
709
+ const { opcode, payload } = frame;
710
+ if (opcode >= 0x80)
711
+ return;
712
+ if (!KNOWN_CORE_OPCODES.has(opcode))
713
+ fail('unknown_opcode', 'unknown core opcode');
714
+ switch (opcode) {
715
+ case Opcode.Hello: {
716
+ const fields = exactTuple(payload, 6, 'HELLO');
717
+ integer(fields[0], 0, 0xff, 'HELLO.major');
718
+ const minimum = integer(fields[1], 0, 0xff, 'HELLO.minMinor');
719
+ const maximum = integer(fields[2], 0, 0xff, 'HELLO.maxMinor');
720
+ if (minimum > maximum)
721
+ fail('invalid_frame', 'HELLO minor range is inverted');
722
+ const role = integer(fields[3], 1, 3, 'HELLO.role');
723
+ stringValue(fields[4], 1, 16_384, 'HELLO.token');
724
+ const context = asArray(fields[5], 'HELLO.context');
725
+ if (role === 1) {
726
+ const roleFields = exactTuple(context, 1, 'HELLO.userContext');
727
+ opaqueId(roleFields[0], 'HELLO.homeId');
728
+ }
729
+ else if (role === 2) {
730
+ const roleFields = exactTuple(context, 1, 'HELLO.coordinatorContext');
731
+ coordinatorName(roleFields[0], 'HELLO.coordinatorName');
732
+ }
733
+ else if (context.length !== 0)
734
+ fail('invalid_frame', 'HELLO CLI context must be empty');
735
+ return;
736
+ }
737
+ case Opcode.Welcome: {
738
+ const fields = exactTuple(payload, 8, 'WELCOME');
739
+ integer(fields[0], 0, 0xff, 'WELCOME.major');
740
+ integer(fields[1], 0, 0xff, 'WELCOME.minor');
741
+ positiveId(fields[2], 'WELCOME.sessionId');
742
+ epoch(fields[3], 'WELCOME.epoch');
743
+ booleanValue(fields[4], 'WELCOME.enrolled');
744
+ coordinators(fields[5], 'WELCOME.coordinators');
745
+ const limits = exactTuple(fields[6], 4, 'WELCOME.limits');
746
+ integer(limits[0], 1, LIMITS.frameBytes, 'WELCOME.maxFrameBytes');
747
+ integer(limits[1], 1, LIMITS.inflightCalls, 'WELCOME.maxInflightCalls');
748
+ integer(limits[2], 1, LIMITS.subscriptions, 'WELCOME.maxSubscriptions');
749
+ integer(limits[3], 1, 1_048_576, 'WELCOME.maxQueuedBytes');
750
+ positiveId(fields[7], 'WELCOME.expiresAtMs');
751
+ return;
752
+ }
753
+ case Opcode.Error: {
754
+ const fields = exactTuple(payload, 5, 'ERROR');
755
+ errorCorrelation(fields[0], fields[1]);
756
+ coreErrorCode(fields[2], 'ERROR.code');
757
+ booleanValue(fields[3], 'ERROR.retryable');
758
+ stringValue(fields[4], 1, 256, 'ERROR.message');
759
+ return;
760
+ }
761
+ case Opcode.Fatal: {
762
+ const fields = exactTuple(payload, 4, 'FATAL');
763
+ integer(fields[0], 0, 0xff, 'FATAL.sourceOpcode');
764
+ coreErrorCode(fields[1], 'FATAL.code');
765
+ booleanValue(fields[2], 'FATAL.retryable');
766
+ stringValue(fields[3], 1, 256, 'FATAL.message');
767
+ return;
768
+ }
769
+ case Opcode.Reauth: {
770
+ const fields = exactTuple(payload, 2, 'REAUTH');
771
+ positiveId(fields[0], 'REAUTH.requestId');
772
+ stringValue(fields[1], 1, 16_384, 'REAUTH.token');
773
+ return;
774
+ }
775
+ case Opcode.ReauthOk: {
776
+ const fields = exactTuple(payload, 2, 'REAUTH_OK');
777
+ positiveId(fields[0], 'REAUTH_OK.requestId');
778
+ positiveId(fields[1], 'REAUTH_OK.expiresAtMs');
779
+ return;
780
+ }
781
+ case Opcode.HomeStatus: {
782
+ const fields = exactTuple(payload, 2, 'HOME_STATUS');
783
+ booleanValue(fields[0], 'HOME_STATUS.enrolled');
784
+ coordinators(fields[1], 'HOME_STATUS.coordinators');
785
+ return;
786
+ }
787
+ case Opcode.Goaway: {
788
+ const fields = exactTuple(payload, 2, 'GOAWAY');
789
+ integer(fields[0], 0, LIMITS.callTimeoutMs, 'GOAWAY.retryAfterMs');
790
+ integer(fields[1], 0, 0xffff, 'GOAWAY.reasonCode');
791
+ return;
792
+ }
793
+ case Opcode.StateSync: {
794
+ const fields = exactTuple(payload, 2, 'STATE_SYNC');
795
+ positiveId(fields[0], 'STATE_SYNC.requestId');
796
+ const entries = asArray(fields[1], 'STATE_SYNC.entries', LIMITS.statePathsPerCoordinator);
797
+ const paths = new Set();
798
+ entries.forEach((entry, index) => {
799
+ const item = exactTuple(entry, 2, `STATE_SYNC.entries[${index}]`);
800
+ const path = structuredName(item[0], `STATE_SYNC.entries[${index}].path`);
801
+ if (paths.has(path))
802
+ fail('invalid_frame', 'STATE_SYNC contains a duplicate path');
803
+ paths.add(path);
804
+ });
805
+ return;
806
+ }
807
+ case Opcode.StateSyncOk: {
808
+ const fields = exactTuple(payload, 4, 'STATE_SYNC_OK');
809
+ positiveId(fields[0], 'STATE_SYNC_OK.requestId');
810
+ epoch(fields[1], 'STATE_SYNC_OK.epoch');
811
+ positiveId(fields[2], 'STATE_SYNC_OK.revision');
812
+ dictionary(fields[3], LIMITS.statePathsPerCoordinator, 'path');
813
+ return;
814
+ }
815
+ case Opcode.StateDict: {
816
+ const fields = exactTuple(payload, 3, 'STATE_DICT');
817
+ epoch(fields[0], 'STATE_DICT.epoch');
818
+ booleanValue(fields[1], 'STATE_DICT.replace');
819
+ dictionary(fields[2], LIMITS.statePathsPerHome, 'path');
820
+ return;
821
+ }
822
+ case Opcode.StateSnapshot: {
823
+ const fields = exactTuple(payload, 3, 'STATE_SNAPSHOT');
824
+ epoch(fields[0], 'STATE_SNAPSHOT.epoch');
825
+ positiveId(fields[1], 'STATE_SNAPSHOT.revision');
826
+ const entries = asArray(fields[2], 'STATE_SNAPSHOT.entries', LIMITS.statePathsPerHome);
827
+ const ids = entries.map((entry, index) => exactTuple(entry, 2, `STATE_SNAPSHOT.entries[${index}]`)[0]);
828
+ uniqueIds(ids, 'STATE_SNAPSHOT.pathIds');
829
+ return;
830
+ }
831
+ case Opcode.StatePatch: {
832
+ const fields = exactTuple(payload, 4, 'STATE_PATCH');
833
+ epoch(fields[0], 'STATE_PATCH.epoch');
834
+ const base = positiveId(fields[1], 'STATE_PATCH.baseRevision');
835
+ const revision = positiveId(fields[2], 'STATE_PATCH.revision');
836
+ if (revision <= base)
837
+ fail('invalid_frame', 'STATE_PATCH revision must advance');
838
+ mutations(fields[3], 'STATE_PATCH.mutations');
839
+ return;
840
+ }
841
+ case Opcode.StateSet: {
842
+ const fields = exactTuple(payload, 3, 'STATE_SET');
843
+ positiveId(fields[0], 'STATE_SET.requestId');
844
+ epoch(fields[1], 'STATE_SET.epoch');
845
+ mutations(fields[2], 'STATE_SET.mutations');
846
+ return;
847
+ }
848
+ case Opcode.StateSetOk: {
849
+ const fields = exactTuple(payload, 3, 'STATE_SET_OK');
850
+ positiveId(fields[0], 'STATE_SET_OK.requestId');
851
+ epoch(fields[1], 'STATE_SET_OK.epoch');
852
+ positiveId(fields[2], 'STATE_SET_OK.revision');
853
+ return;
854
+ }
855
+ case Opcode.StateAclSync: {
856
+ const fields = exactTuple(payload, 2, 'STATE_ACL_SYNC');
857
+ positiveId(fields[0], 'STATE_ACL_SYNC.requestId');
858
+ const entries = asArray(fields[1], 'STATE_ACL_SYNC.declarations', LIMITS.declarationsPerCoordinator);
859
+ const users = new Set();
860
+ entries.forEach((entry, index) => {
861
+ const item = exactTuple(entry, 2, `STATE_ACL_SYNC.declarations[${index}]`);
862
+ const user = opaqueId(item[0], `STATE_ACL_SYNC.declarations[${index}].userId`);
863
+ if (users.has(user))
864
+ fail('invalid_frame', 'STATE_ACL_SYNC contains a duplicate user');
865
+ users.add(user);
866
+ aclPatterns(item[1], `STATE_ACL_SYNC.declarations[${index}].patterns`);
867
+ });
868
+ return;
869
+ }
870
+ case Opcode.StateAclOk: {
871
+ const fields = exactTuple(payload, 2, 'STATE_ACL_OK');
872
+ positiveId(fields[0], 'STATE_ACL_OK.requestId');
873
+ positiveId(fields[1], 'STATE_ACL_OK.policyRevision');
874
+ return;
875
+ }
876
+ case Opcode.StateResync: {
877
+ const fields = exactTuple(payload, 1, 'STATE_RESYNC');
878
+ positiveId(fields[0], 'STATE_RESYNC.requestId');
879
+ return;
880
+ }
881
+ case Opcode.EventSync: {
882
+ const fields = exactTuple(payload, 2, 'EVENT_SYNC');
883
+ positiveId(fields[0], 'EVENT_SYNC.requestId');
884
+ const entries = asArray(fields[1], 'EVENT_SYNC.declarations', LIMITS.declarationsPerCoordinator);
885
+ const topics = new Set();
886
+ entries.forEach((entry, index) => {
887
+ const item = exactTuple(entry, 2, `EVENT_SYNC.declarations[${index}]`);
888
+ const topic = structuredName(item[0], `EVENT_SYNC.declarations[${index}].topic`);
889
+ if (topics.has(topic))
890
+ fail('invalid_frame', 'EVENT_SYNC contains a duplicate topic');
891
+ topics.add(topic);
892
+ integer(item[1], 1, 0x0f, `EVENT_SYNC.declarations[${index}].flags`);
893
+ });
894
+ return;
895
+ }
896
+ case Opcode.EventSyncOk: {
897
+ const fields = exactTuple(payload, 2, 'EVENT_SYNC_OK');
898
+ positiveId(fields[0], 'EVENT_SYNC_OK.requestId');
899
+ dictionary(fields[1], LIMITS.declarationsPerCoordinator, 'topic');
900
+ return;
901
+ }
902
+ case Opcode.TopicDict: {
903
+ const fields = exactTuple(payload, 3, 'TOPIC_DICT');
904
+ epoch(fields[0], 'TOPIC_DICT.epoch');
905
+ booleanValue(fields[1], 'TOPIC_DICT.replace');
906
+ dictionary(fields[2], LIMITS.statePathsPerHome, 'topic');
907
+ return;
908
+ }
909
+ case Opcode.EventAclSync: {
910
+ const fields = exactTuple(payload, 2, 'EVENT_ACL_SYNC');
911
+ positiveId(fields[0], 'EVENT_ACL_SYNC.requestId');
912
+ const entries = asArray(fields[1], 'EVENT_ACL_SYNC.declarations', LIMITS.declarationsPerCoordinator);
913
+ const users = new Set();
914
+ entries.forEach((entry, index) => {
915
+ const item = exactTuple(entry, 3, `EVENT_ACL_SYNC.declarations[${index}]`);
916
+ const user = opaqueId(item[0], `EVENT_ACL_SYNC.declarations[${index}].userId`);
917
+ if (users.has(user))
918
+ fail('invalid_frame', 'EVENT_ACL_SYNC contains a duplicate user');
919
+ users.add(user);
920
+ aclPatterns(item[1], `EVENT_ACL_SYNC.declarations[${index}].publishPatterns`);
921
+ aclPatterns(item[2], `EVENT_ACL_SYNC.declarations[${index}].subscribePatterns`);
922
+ });
923
+ return;
924
+ }
925
+ case Opcode.EventAclOk: {
926
+ const fields = exactTuple(payload, 2, 'EVENT_ACL_OK');
927
+ positiveId(fields[0], 'EVENT_ACL_OK.requestId');
928
+ positiveId(fields[1], 'EVENT_ACL_OK.policyRevision');
929
+ return;
930
+ }
931
+ case Opcode.Subscribe:
932
+ return subscriptionPayload(payload, 'SUBSCRIBE');
933
+ case Opcode.SubscribeOk:
934
+ return subscriptionPayload(payload, 'SUBSCRIBE_OK');
935
+ case Opcode.Unsubscribe:
936
+ return subscriptionPayload(payload, 'UNSUBSCRIBE');
937
+ case Opcode.UnsubscribeOk:
938
+ return subscriptionPayload(payload, 'UNSUBSCRIBE_OK');
939
+ case Opcode.Event: {
940
+ if (payload.length !== 5 && payload.length !== 6)
941
+ fail('invalid_frame', 'EVENT has wrong arity');
942
+ positiveId(payload[0], 'EVENT.eventId');
943
+ positiveId(payload[1], 'EVENT.topicId');
944
+ target(payload[2], payload[3], 'EVENT.target');
945
+ if (payload.length === 6)
946
+ principal(payload[4], 'EVENT.source');
947
+ return;
948
+ }
949
+ case Opcode.FunctionSync: {
950
+ const fields = exactTuple(payload, 2, 'FUNCTION_SYNC');
951
+ positiveId(fields[0], 'FUNCTION_SYNC.requestId');
952
+ const names = asArray(fields[1], 'FUNCTION_SYNC.names', LIMITS.declarationsPerCoordinator);
953
+ uniqueStrings(names, structuredName, 'FUNCTION_SYNC.names');
954
+ return;
955
+ }
956
+ case Opcode.FunctionSyncOk: {
957
+ const fields = exactTuple(payload, 2, 'FUNCTION_SYNC_OK');
958
+ positiveId(fields[0], 'FUNCTION_SYNC_OK.requestId');
959
+ dictionary(fields[1], LIMITS.declarationsPerCoordinator, 'function');
960
+ return;
961
+ }
962
+ case Opcode.FunctionDict: {
963
+ const fields = exactTuple(payload, 3, 'FUNCTION_DICT');
964
+ epoch(fields[0], 'FUNCTION_DICT.epoch');
965
+ booleanValue(fields[1], 'FUNCTION_DICT.replace');
966
+ dictionary(fields[2], LIMITS.statePathsPerHome, 'function');
967
+ return;
968
+ }
969
+ case Opcode.Call: {
970
+ const fields = exactTuple(payload, 8, 'CALL');
971
+ positiveId(fields[0], 'CALL.callId');
972
+ target(fields[1], fields[2], 'CALL.target');
973
+ positiveId(fields[3], 'CALL.functionId');
974
+ integer(fields[4], 1, LIMITS.callTimeoutMs, 'CALL.timeoutMs');
975
+ nullableString(fields[5], 128, 'CALL.idempotencyKey');
976
+ integer(fields[6], 0, LIMITS.streamCredit, 'CALL.initialCredit');
977
+ return;
978
+ }
979
+ case Opcode.CallDispatch: {
980
+ const fields = exactTuple(payload, 9, 'CALL_DISPATCH');
981
+ positiveId(fields[0], 'CALL_DISPATCH.callId');
982
+ principal(fields[1], 'CALL_DISPATCH.source');
983
+ target(fields[2], fields[3], 'CALL_DISPATCH.target');
984
+ positiveId(fields[4], 'CALL_DISPATCH.functionId');
985
+ integer(fields[5], 1, LIMITS.callTimeoutMs, 'CALL_DISPATCH.timeoutMs');
986
+ nullableString(fields[6], 128, 'CALL_DISPATCH.idempotencyKey');
987
+ integer(fields[7], 0, LIMITS.streamCredit, 'CALL_DISPATCH.initialCredit');
988
+ return;
989
+ }
990
+ case Opcode.CallAccepted: {
991
+ const fields = exactTuple(payload, 1, 'CALL_ACCEPTED');
992
+ positiveId(fields[0], 'CALL_ACCEPTED.callId');
993
+ return;
994
+ }
995
+ case Opcode.CallResult: {
996
+ const fields = exactTuple(payload, 3, 'CALL_RESULT');
997
+ positiveId(fields[0], 'CALL_RESULT.callId');
998
+ booleanValue(fields[1], 'CALL_RESULT.final');
999
+ return;
1000
+ }
1001
+ case Opcode.CallError: {
1002
+ const fields = exactTuple(payload, 5, 'CALL_ERROR');
1003
+ positiveId(fields[0], 'CALL_ERROR.callId');
1004
+ callErrorCode(fields[1], 'CALL_ERROR.code');
1005
+ booleanValue(fields[2], 'CALL_ERROR.retryable');
1006
+ stringValue(fields[3], 1, 256, 'CALL_ERROR.message');
1007
+ return;
1008
+ }
1009
+ case Opcode.CallCancel: {
1010
+ const fields = exactTuple(payload, 2, 'CALL_CANCEL');
1011
+ positiveId(fields[0], 'CALL_CANCEL.callId');
1012
+ integer(fields[1], 0, 0xffff, 'CALL_CANCEL.reasonCode');
1013
+ return;
1014
+ }
1015
+ case Opcode.CallCredit: {
1016
+ const fields = exactTuple(payload, 2, 'CALL_CREDIT');
1017
+ positiveId(fields[0], 'CALL_CREDIT.callId');
1018
+ integer(fields[1], 1, LIMITS.streamCredit, 'CALL_CREDIT.additionalCredit');
1019
+ return;
1020
+ }
1021
+ case Opcode.PresenceSnapshot: {
1022
+ const fields = exactTuple(payload, 1, 'PRESENCE_SNAPSHOT');
1023
+ const entries = asArray(fields[0], 'PRESENCE_SNAPSHOT.entries');
1024
+ const sessions = new Set();
1025
+ entries.forEach((entry, index) => {
1026
+ const item = exactTuple(entry, 2, `PRESENCE_SNAPSHOT.entries[${index}]`);
1027
+ const session = positiveId(item[0], `PRESENCE_SNAPSHOT.entries[${index}].sessionId`);
1028
+ if (sessions.has(session))
1029
+ fail('invalid_frame', 'PRESENCE_SNAPSHOT contains a duplicate session');
1030
+ sessions.add(session);
1031
+ opaqueId(item[1], `PRESENCE_SNAPSHOT.entries[${index}].userId`);
1032
+ });
1033
+ return;
1034
+ }
1035
+ case Opcode.PresenceChange: {
1036
+ const fields = exactTuple(payload, 3, 'PRESENCE_CHANGE');
1037
+ positiveId(fields[0], 'PRESENCE_CHANGE.sessionId');
1038
+ opaqueId(fields[1], 'PRESENCE_CHANGE.userId');
1039
+ integer(fields[2], 1, 2, 'PRESENCE_CHANGE.event');
1040
+ return;
1041
+ }
1042
+ default:
1043
+ return fail('unknown_opcode', 'unknown core opcode');
1044
+ }
1045
+ }
1046
+ function preflight(input) {
1047
+ if (input.length > LIMITS.frameBytes)
1048
+ fail('frame_too_large', 'frame exceeds byte limit');
1049
+ if (input.length < 2)
1050
+ fail('malformed', 'frame is missing its payload');
1051
+ const result = scanValue(input, 1, 1, { nodes: 0 });
1052
+ if (result.kind !== 'array')
1053
+ fail('invalid_frame', 'frame payload must be an array');
1054
+ if (result.end !== input.length)
1055
+ fail('malformed', 'frame has trailing bytes');
1056
+ }
1057
+ export function encodeFrame(frame) {
1058
+ if (!Number.isInteger(frame.opcode) || frame.opcode < 0 || frame.opcode > 0xff) {
1059
+ fail('unknown_opcode', 'opcode must be one byte');
1060
+ }
1061
+ validateFrame(frame);
1062
+ const payload = encodeValue(frame.payload, 1, { nodes: 0, bytes: 0 });
1063
+ const output = concat([byte(frame.opcode), payload]);
1064
+ preflight(output);
1065
+ return output;
1066
+ }
1067
+ export function decodeFrame(input) {
1068
+ preflight(input);
1069
+ const opcode = input[0];
1070
+ if (opcode < 0x80 && !KNOWN_CORE_OPCODES.has(opcode)) {
1071
+ fail('unknown_opcode', `unknown core opcode 0x${opcode.toString(16)}`);
1072
+ }
1073
+ let payload;
1074
+ try {
1075
+ payload = decode(input.subarray(1), {
1076
+ useBigInt64: false,
1077
+ maxStrLength: LIMITS.stringBytes,
1078
+ maxBinLength: LIMITS.binaryBytes,
1079
+ maxArrayLength: LIMITS.arrayItems,
1080
+ maxMapLength: LIMITS.mapEntries,
1081
+ maxExtLength: 0,
1082
+ });
1083
+ }
1084
+ catch (error) {
1085
+ fail('malformed', `MessagePack decoder rejected a preflighted payload: ${String(error)}`);
1086
+ }
1087
+ const frame = { opcode, payload: asArray(payload, 'frame payload') };
1088
+ validateFrame(frame);
1089
+ return frame;
1090
+ }
1091
+ //# sourceMappingURL=codec.js.map