@elaraai/east 1.0.45 → 1.0.46

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 (90) hide show
  1. package/dist/src/containers/sortedmap.d.ts +22 -0
  2. package/dist/src/containers/sortedmap.d.ts.map +1 -1
  3. package/dist/src/containers/sortedmap.js +31 -2
  4. package/dist/src/containers/sortedmap.js.map +1 -1
  5. package/dist/src/containers/sortedset.d.ts +22 -0
  6. package/dist/src/containers/sortedset.d.ts.map +1 -1
  7. package/dist/src/containers/sortedset.js +31 -2
  8. package/dist/src/containers/sortedset.js.map +1 -1
  9. package/dist/src/expr/blob.d.ts +6 -1
  10. package/dist/src/expr/blob.d.ts.map +1 -1
  11. package/dist/src/expr/blob.js +6 -1
  12. package/dist/src/expr/blob.js.map +1 -1
  13. package/dist/src/expr/libs/blob.d.ts +10 -1
  14. package/dist/src/expr/libs/blob.d.ts.map +1 -1
  15. package/dist/src/expr/libs/blob.js +10 -1
  16. package/dist/src/expr/libs/blob.js.map +1 -1
  17. package/dist/src/fuzz.d.ts.map +1 -1
  18. package/dist/src/fuzz.js +11 -2
  19. package/dist/src/fuzz.js.map +1 -1
  20. package/dist/src/serialization/beast2/index.d.ts +76 -22
  21. package/dist/src/serialization/beast2/index.d.ts.map +1 -1
  22. package/dist/src/serialization/beast2/index.js +87 -720
  23. package/dist/src/serialization/beast2/index.js.map +1 -1
  24. package/dist/src/serialization/beast2/index.spec.js +147 -4
  25. package/dist/src/serialization/beast2/index.spec.js.map +1 -1
  26. package/dist/src/serialization/beast2/shared.d.ts +79 -0
  27. package/dist/src/serialization/beast2/shared.d.ts.map +1 -0
  28. package/dist/src/serialization/beast2/shared.js +143 -0
  29. package/dist/src/serialization/beast2/shared.js.map +1 -0
  30. package/dist/src/serialization/beast2/v4/container.d.ts +67 -0
  31. package/dist/src/serialization/beast2/v4/container.d.ts.map +1 -0
  32. package/dist/src/serialization/beast2/v4/container.js +727 -0
  33. package/dist/src/serialization/beast2/v4/container.js.map +1 -0
  34. package/dist/src/serialization/beast2/{sourcemap-table.d.ts → v4/sourcemap-table.d.ts} +2 -2
  35. package/dist/src/serialization/beast2/v4/sourcemap-table.d.ts.map +1 -0
  36. package/dist/src/serialization/beast2/{sourcemap-table.js → v4/sourcemap-table.js} +2 -2
  37. package/dist/src/serialization/beast2/v4/sourcemap-table.js.map +1 -0
  38. package/dist/src/serialization/beast2/{string-table.d.ts → v4/string-table.d.ts} +1 -1
  39. package/dist/src/serialization/beast2/v4/string-table.d.ts.map +1 -0
  40. package/dist/src/serialization/beast2/{string-table.js → v4/string-table.js} +1 -1
  41. package/dist/src/serialization/beast2/v4/string-table.js.map +1 -0
  42. package/dist/src/serialization/beast2/{type-table.d.ts → v4/type-table.d.ts} +6 -4
  43. package/dist/src/serialization/beast2/v4/type-table.d.ts.map +1 -0
  44. package/dist/src/serialization/beast2/{type-table.js → v4/type-table.js} +103 -10
  45. package/dist/src/serialization/beast2/v4/type-table.js.map +1 -0
  46. package/dist/src/serialization/beast2/{value-table.d.ts → v4/value-table.d.ts} +2 -2
  47. package/dist/src/serialization/beast2/v4/value-table.d.ts.map +1 -0
  48. package/dist/src/serialization/beast2/{value-table.js → v4/value-table.js} +3 -3
  49. package/dist/src/serialization/beast2/v4/value-table.js.map +1 -0
  50. package/dist/src/serialization/beast2/v5/codec.d.ts +213 -0
  51. package/dist/src/serialization/beast2/v5/codec.d.ts.map +1 -0
  52. package/dist/src/serialization/beast2/v5/codec.js +954 -0
  53. package/dist/src/serialization/beast2/v5/codec.js.map +1 -0
  54. package/dist/src/serialization/beast2/v5/deflate.d.ts +16 -0
  55. package/dist/src/serialization/beast2/v5/deflate.d.ts.map +1 -0
  56. package/dist/src/serialization/beast2/v5/deflate.js +172 -0
  57. package/dist/src/serialization/beast2/v5/deflate.js.map +1 -0
  58. package/dist/src/serialization/beast2/v5/frames.d.ts +150 -0
  59. package/dist/src/serialization/beast2/v5/frames.d.ts.map +1 -0
  60. package/dist/src/serialization/beast2/v5/frames.js +284 -0
  61. package/dist/src/serialization/beast2/v5/frames.js.map +1 -0
  62. package/dist/src/serialization/beast2/v5/index.spec.d.ts +6 -0
  63. package/dist/src/serialization/beast2/v5/index.spec.d.ts.map +1 -0
  64. package/dist/src/serialization/beast2/v5/index.spec.js +484 -0
  65. package/dist/src/serialization/beast2/v5/index.spec.js.map +1 -0
  66. package/dist/src/serialization/beast2/v5/stream.d.ts +177 -0
  67. package/dist/src/serialization/beast2/v5/stream.d.ts.map +1 -0
  68. package/dist/src/serialization/beast2/v5/stream.js +447 -0
  69. package/dist/src/serialization/beast2/v5/stream.js.map +1 -0
  70. package/dist/src/serialization/beast2/v5/type-section.d.ts +81 -0
  71. package/dist/src/serialization/beast2/v5/type-section.d.ts.map +1 -0
  72. package/dist/src/serialization/beast2/v5/type-section.js +211 -0
  73. package/dist/src/serialization/beast2/v5/type-section.js.map +1 -0
  74. package/dist/src/serialization/beast2/version.d.ts +40 -0
  75. package/dist/src/serialization/beast2/version.d.ts.map +1 -0
  76. package/dist/src/serialization/beast2/version.js +23 -0
  77. package/dist/src/serialization/beast2/version.js.map +1 -0
  78. package/dist/src/serialization/index.d.ts +1 -1
  79. package/dist/src/serialization/index.d.ts.map +1 -1
  80. package/dist/src/serialization/index.js +1 -1
  81. package/dist/src/serialization/index.js.map +1 -1
  82. package/package.json +1 -1
  83. package/dist/src/serialization/beast2/sourcemap-table.d.ts.map +0 -1
  84. package/dist/src/serialization/beast2/sourcemap-table.js.map +0 -1
  85. package/dist/src/serialization/beast2/string-table.d.ts.map +0 -1
  86. package/dist/src/serialization/beast2/string-table.js.map +0 -1
  87. package/dist/src/serialization/beast2/type-table.d.ts.map +0 -1
  88. package/dist/src/serialization/beast2/type-table.js.map +0 -1
  89. package/dist/src/serialization/beast2/value-table.d.ts.map +0 -1
  90. package/dist/src/serialization/beast2/value-table.js.map +0 -1
@@ -0,0 +1,727 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Beast2 v4 container codec — the globally-sectioned format.
7
+ *
8
+ * File layout: magic(8) + type_table_section + string_table_section +
9
+ * source_map_section + value_table_section + value_stream.
10
+ * Type table: flat array of unique types, referenced by varint index.
11
+ * String table: flat array of unique strings, referenced by varint index.
12
+ * Value stream: type-driven positional encoding, no inline type tags.
13
+ * Functions: IR encoded directly — EastTypeType values are regular variants.
14
+ *
15
+ * See v4/SPEC.md for the wire specification. Version-agnostic entry points
16
+ * (magic dispatch across v4/v5) live in `../index.ts`.
17
+ */
18
+ import { toEastTypeValue } from "../../../type_of_type.js";
19
+ import { isVariant, variant } from "../../../containers/variant.js";
20
+ import { BufferWriter, BufferReader } from "../../binary-utils.js";
21
+ import { ref } from "../../../containers/ref.js";
22
+ import { SortedSet } from "../../../containers/sortedset.js";
23
+ import { SortedMap } from "../../../containers/sortedmap.js";
24
+ import { compareFor } from "../../../comparison.js";
25
+ import { matrix } from "../../../containers/matrix.js";
26
+ import { EAST_IR_SYMBOL, EAST_CAPTURES_SYMBOL, EAST_SOURCE_MAP_SYMBOL } from "../../../compile.js";
27
+ import { InternalError } from "../../../error.js";
28
+ import { TypeTableBuilder, writeTypeTableSection, readTypeTableSection } from "./type-table.js";
29
+ import { writeStringTableSection, readStringTableSection } from "./string-table.js";
30
+ import { writeSourceMapSection, readSourceMapSection } from "./sourcemap-table.js";
31
+ import { buildValueTable, buildIndexMap, isMutableType, TAG_ARRAY, TAG_DICT, TAG_SET, TAG_REF } from "./value-table.js";
32
+ import { buildPlatformContext, describeNoIrValue, finishDecodedFunction, irTypeValue } from "../shared.js";
33
+ // v4: EastTypeType values are encoded as regular variants (no special type-table-index encoding).
34
+ // This ensures uniform encoding across value stream and value table, and simplifies multi-runtime implementations.
35
+ // =============================================================================
36
+ // Magic bytes (v4: last byte = 0x04)
37
+ // =============================================================================
38
+ export const MAGIC_BYTES = new Uint8Array([0x89, 0x45, 0x61, 0x73, 0x74, 0x0D, 0x0A, 0x04]);
39
+ function verifyMagic(data) {
40
+ if (data.length < 8) {
41
+ throw new Error(`Data too short for Beast2 format: ${data.length} bytes`);
42
+ }
43
+ for (let i = 0; i < 8; i++) {
44
+ if (data[i] !== MAGIC_BYTES[i]) {
45
+ if (i < 7)
46
+ throw new Error(`Invalid Beast2 magic at offset ${i}: expected 0x${MAGIC_BYTES[i].toString(16)}, got 0x${data[i].toString(16)}`);
47
+ throw new Error(`Unknown Beast2 version: 0x${data[i].toString(16)}`);
48
+ }
49
+ }
50
+ }
51
+ // =============================================================================
52
+ // Value encoder factory
53
+ // =============================================================================
54
+ /**
55
+ * Build a value encoder closure tree for the given type.
56
+ * The tree is built once and reused for every encode call.
57
+ */
58
+ function buildEncoder(type, typeCtx = new Map()) {
59
+ switch (type.type) {
60
+ case "Never":
61
+ return () => { throw new Error("Cannot encode value of type Never"); };
62
+ case "Null":
63
+ return () => { };
64
+ case "Boolean":
65
+ return (value, writer) => writer.writeUint8(value ? 1 : 0);
66
+ case "Integer":
67
+ return (value, writer) => writer.writeZigzag(value);
68
+ case "Float":
69
+ return (value, writer) => writer.writeFloat64LE(value);
70
+ case "String":
71
+ return (value, writer, ctx) => {
72
+ let idx = ctx.stringTable.get(value);
73
+ if (idx === undefined) {
74
+ idx = ctx.stringTable.size;
75
+ ctx.stringTable.set(value, idx);
76
+ }
77
+ writer.writeVarint(idx);
78
+ };
79
+ case "DateTime":
80
+ return (value, writer) => writer.writeZigzag(BigInt(value.valueOf()));
81
+ case "Blob":
82
+ return (value, writer) => {
83
+ writer.writeVarint(value.length);
84
+ writer.writeBytes(value);
85
+ };
86
+ case "Ref":
87
+ case "Array":
88
+ case "Set":
89
+ case "Dict": {
90
+ // v4: all mutable containers are in the value table, encoded as varint(index)
91
+ return (value, writer, ctx) => {
92
+ const idx = ctx.indexMap.get(value);
93
+ if (idx === undefined)
94
+ throw new InternalError(`Mutable ${type.type} not found in value table during encode`);
95
+ writer.writeVarint(idx);
96
+ };
97
+ }
98
+ case "Struct": {
99
+ const fieldEncoders = [];
100
+ const ret = (value, writer, ctx) => {
101
+ for (const [name, enc] of fieldEncoders)
102
+ enc(value[name], writer, ctx);
103
+ };
104
+ for (const { name, type: fieldType } of type.value) {
105
+ fieldEncoders.push([name, buildEncoder(fieldType, typeCtx)]);
106
+ }
107
+ return ret;
108
+ }
109
+ case "Variant": {
110
+ const caseEncoders = {};
111
+ const caseTags = {};
112
+ const ret = (value, writer, ctx) => {
113
+ writer.writeVarint(caseTags[value.type]);
114
+ caseEncoders[value.type](value.value, writer, ctx);
115
+ };
116
+ for (let i = 0; i < type.value.length; i++) {
117
+ const { name, type: caseType } = type.value[i];
118
+ caseTags[name] = i;
119
+ caseEncoders[name] = buildEncoder(caseType, typeCtx);
120
+ }
121
+ return ret;
122
+ }
123
+ case "Recursive": {
124
+ if (type.value.type === "wrapper") {
125
+ let inner;
126
+ const ret = (value, writer, ctx) => inner(value, writer, ctx);
127
+ typeCtx.set(type.value.value.id, ret);
128
+ inner = buildEncoder(type.value.value.inner, typeCtx);
129
+ return ret;
130
+ }
131
+ const target = typeCtx.get(type.value.value);
132
+ if (!target)
133
+ throw new InternalError("Recursive type context not found during encoder build");
134
+ return target;
135
+ }
136
+ case "Function":
137
+ case "AsyncFunction": {
138
+ // Build IR encoder in the same typeCtx so recursive types resolve correctly
139
+ const fnIrEncoder = buildEncoder(irTypeValue, typeCtx);
140
+ const captureEncoderCache = new Map();
141
+ return (value, writer, ctx) => {
142
+ const ir = value[EAST_IR_SYMBOL];
143
+ if (!ir)
144
+ throw new Error(`Cannot serialize function: no IR attached (${describeNoIrValue(value)})`);
145
+ fnIrEncoder(ir, writer, ctx);
146
+ const captures = value[EAST_CAPTURES_SYMBOL];
147
+ const captureList = ir.value.captures;
148
+ writer.writeVarint(captureList.length);
149
+ for (const captureVar of captureList) {
150
+ const name = captureVar.value.name;
151
+ const captureType = captureVar.value.type;
152
+ if (!captures)
153
+ throw new InternalError("Function has captures but no EAST_CAPTURES_SYMBOL");
154
+ const entry = captures[name];
155
+ if (!entry)
156
+ throw new InternalError(`Capture '${name}' not found`);
157
+ let enc = captureEncoderCache.get(captureType);
158
+ if (!enc) {
159
+ enc = buildEncoder(captureType, typeCtx);
160
+ captureEncoderCache.set(captureType, enc);
161
+ }
162
+ enc(entry.value, writer, ctx);
163
+ }
164
+ };
165
+ }
166
+ case "Vector":
167
+ return (value, writer) => {
168
+ writer.writeVarint(value.length);
169
+ writer.writeBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
170
+ };
171
+ case "Matrix":
172
+ return (value, writer) => {
173
+ writer.writeVarint(value.rows);
174
+ writer.writeVarint(value.cols);
175
+ writer.writeBytes(new Uint8Array(value.data.buffer, value.data.byteOffset, value.data.byteLength));
176
+ };
177
+ default:
178
+ throw new Error(`Unknown type: ${type.type}`);
179
+ }
180
+ }
181
+ // =============================================================================
182
+ // Value decoder factory
183
+ // =============================================================================
184
+ /**
185
+ * Build a value decoder closure tree for the given type.
186
+ * The tree is built once and reused for every decode call.
187
+ */
188
+ function buildDecoder(type, typeCtx = new Map()) {
189
+ switch (type.type) {
190
+ case "Never":
191
+ return () => { throw new Error("Cannot decode value of type Never"); };
192
+ case "Null":
193
+ return () => null;
194
+ case "Boolean":
195
+ return (reader) => reader.readBoolean();
196
+ case "Integer":
197
+ return (reader) => reader.readZigzag();
198
+ case "Float":
199
+ return (reader) => reader.readFloat64LE();
200
+ case "String":
201
+ return (reader, ctx) => {
202
+ const idx = reader.readVarint();
203
+ if (idx >= ctx.stringTable.length)
204
+ throw new Error(`String table index ${idx} out of bounds (table has ${ctx.stringTable.length} entries)`);
205
+ return ctx.stringTable[idx];
206
+ };
207
+ case "DateTime":
208
+ return (reader) => new Date(Number(reader.readZigzag()));
209
+ case "Blob":
210
+ return (reader) => reader.readBytes(reader.readVarint());
211
+ case "Ref":
212
+ case "Array":
213
+ case "Set":
214
+ case "Dict": {
215
+ // v4: mutable containers are read as varint(table_index) — the actual
216
+ // content was decoded from the mutable_value_table_section.
217
+ return (reader, ctx) => {
218
+ const idx = reader.readVarint();
219
+ if (idx >= ctx.mutableValues.length)
220
+ throw new Error(`Invalid mutable value table index ${idx} (table has ${ctx.mutableValues.length} entries)`);
221
+ return ctx.mutableValues[idx];
222
+ };
223
+ }
224
+ case "Struct": {
225
+ const fields = type.value;
226
+ const names = [];
227
+ const decoders = [];
228
+ const ret = (reader, ctx) => {
229
+ const result = {};
230
+ for (let i = 0; i < names.length; i++)
231
+ result[names[i]] = decoders[i](reader, ctx);
232
+ return result;
233
+ };
234
+ for (const { name, type: fieldType } of fields) {
235
+ names.push(name);
236
+ decoders.push(buildDecoder(fieldType, typeCtx));
237
+ }
238
+ return ret;
239
+ }
240
+ case "Variant": {
241
+ const caseDecoders = [];
242
+ const ret = (reader, ctx) => {
243
+ const tagIndex = reader.readVarint();
244
+ if (tagIndex >= caseDecoders.length)
245
+ throw new Error(`Invalid variant tag ${tagIndex}`);
246
+ const [caseName, caseDec] = caseDecoders[tagIndex];
247
+ return variant(caseName, caseDec(reader, ctx));
248
+ };
249
+ for (const { name, type: caseType } of type.value) {
250
+ caseDecoders.push([name, buildDecoder(caseType, typeCtx)]);
251
+ }
252
+ return ret;
253
+ }
254
+ case "Recursive": {
255
+ if (type.value.type === "wrapper") {
256
+ let inner;
257
+ const ret = (reader, ctx) => inner(reader, ctx);
258
+ typeCtx.set(type.value.value.id, ret);
259
+ inner = buildDecoder(type.value.value.inner, typeCtx);
260
+ return ret;
261
+ }
262
+ const target = typeCtx.get(type.value.value);
263
+ if (!target)
264
+ throw new InternalError("Recursive type context not found during decoder build");
265
+ return target;
266
+ }
267
+ case "Function":
268
+ case "AsyncFunction": {
269
+ const isAsync = type.type === "AsyncFunction";
270
+ const fnType = type;
271
+ const fnIrDecoder = buildDecoder(irTypeValue, typeCtx);
272
+ const captureDecoderCache = new Map();
273
+ return (reader, ctx) => {
274
+ const ir = fnIrDecoder(reader, ctx);
275
+ if (ir.type !== (isAsync ? "AsyncFunction" : "Function")) {
276
+ throw new Error(`Expected ${fnType.type} IR, got ${ir.type}`);
277
+ }
278
+ // Decode captures
279
+ const captureCount = reader.readVarint();
280
+ if (captureCount !== ir.value.captures.length) {
281
+ throw new Error(`Capture count mismatch: IR has ${ir.value.captures.length}, data has ${captureCount}`);
282
+ }
283
+ const captureContext = {};
284
+ const typeContext = {};
285
+ for (const captureVar of ir.value.captures) {
286
+ const name = captureVar.value.name;
287
+ const captureType = captureVar.value.type;
288
+ // Get or build cached decoder for this capture type
289
+ let dec = captureDecoderCache.get(captureType);
290
+ if (!dec) {
291
+ dec = buildDecoder(captureType, typeCtx);
292
+ captureDecoderCache.set(captureType, dec);
293
+ }
294
+ const captureValue = dec(reader, ctx);
295
+ captureContext[name] = captureVar.value.mutable
296
+ ? variant("boxed", captureValue)
297
+ : variant("value", captureValue);
298
+ typeContext[name] = captureType;
299
+ }
300
+ return finishDecodedFunction(ir, isAsync, captureContext, typeContext, ctx, ctx.sourceMap);
301
+ };
302
+ }
303
+ case "Vector": {
304
+ const elemType = type.value.type;
305
+ const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
306
+ return (reader) => {
307
+ const len = reader.readVarint();
308
+ const raw = new Uint8Array(reader.readBytesView(len * bpe));
309
+ if (elemType === "Float")
310
+ return new Float64Array(raw.buffer, 0, len);
311
+ if (elemType === "Integer")
312
+ return new BigInt64Array(raw.buffer, 0, len);
313
+ return new Uint8ClampedArray(raw.buffer, 0, len);
314
+ };
315
+ }
316
+ case "Matrix": {
317
+ const elemType = type.value.type;
318
+ const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
319
+ return (reader) => {
320
+ const rows = reader.readVarint();
321
+ const cols = reader.readVarint();
322
+ const raw = new Uint8Array(reader.readBytesView(rows * cols * bpe));
323
+ if (elemType === "Float")
324
+ return matrix(new Float64Array(raw.buffer, 0, rows * cols), rows, cols);
325
+ if (elemType === "Integer")
326
+ return matrix(new BigInt64Array(raw.buffer, 0, rows * cols), rows, cols);
327
+ return matrix(new Uint8ClampedArray(raw.buffer, 0, rows * cols), rows, cols);
328
+ };
329
+ }
330
+ default:
331
+ throw new Error(`Unknown type: ${type.type}`);
332
+ }
333
+ }
334
+ // =============================================================================
335
+ // Mutable value table section (v4)
336
+ // =============================================================================
337
+ function writeValueTableEntry(entry, writer, ctx, encTypeCtx) {
338
+ const type = entry.type;
339
+ const value = entry.value;
340
+ // Write kind tag
341
+ writer.writeUint8(entry.kind);
342
+ // Element encoder — handles both inline (non-mutable) and table-ref (mutable) cases
343
+ const encodeElem = (elemType, val, w, c) => {
344
+ if (isMutableType(elemType)) {
345
+ const idx = c.indexMap.get(val);
346
+ if (idx === undefined)
347
+ throw new InternalError("Nested mutable value not in value table");
348
+ w.writeVarint(idx);
349
+ }
350
+ else {
351
+ buildEncoder(elemType, encTypeCtx)(val, w, c);
352
+ }
353
+ };
354
+ switch (type.type) {
355
+ case "Array": {
356
+ if (!ctx.typeTable.has(type.value))
357
+ ctx.typeTable.addETV(type.value);
358
+ writer.writeVarint(ctx.typeTable.indexOf(type.value));
359
+ writer.writeVarint(value.length);
360
+ for (const item of value)
361
+ encodeElem(type.value, item, writer, ctx);
362
+ break;
363
+ }
364
+ case "Set": {
365
+ if (!ctx.typeTable.has(type.value))
366
+ ctx.typeTable.addETV(type.value);
367
+ writer.writeVarint(ctx.typeTable.indexOf(type.value));
368
+ writer.writeVarint(value.size);
369
+ for (const key of value)
370
+ encodeElem(type.value, key, writer, ctx);
371
+ break;
372
+ }
373
+ case "Dict": {
374
+ if (!ctx.typeTable.has(type.value.key))
375
+ ctx.typeTable.addETV(type.value.key);
376
+ if (!ctx.typeTable.has(type.value.value))
377
+ ctx.typeTable.addETV(type.value.value);
378
+ writer.writeVarint(ctx.typeTable.indexOf(type.value.key));
379
+ writer.writeVarint(ctx.typeTable.indexOf(type.value.value));
380
+ writer.writeVarint(value.size);
381
+ for (const [k, v] of value) {
382
+ encodeElem(type.value.key, k, writer, ctx);
383
+ encodeElem(type.value.value, v, writer, ctx);
384
+ }
385
+ break;
386
+ }
387
+ case "Ref": {
388
+ if (!ctx.typeTable.has(type.value))
389
+ ctx.typeTable.addETV(type.value);
390
+ writer.writeVarint(ctx.typeTable.indexOf(type.value));
391
+ encodeElem(type.value, value.value, writer, ctx);
392
+ break;
393
+ }
394
+ }
395
+ }
396
+ /** Built decoder trees, keyed on the decoded type table itself.
397
+ *
398
+ * These trees were rebuilt on EVERY decode, which for a large recursive
399
+ * schema is the dominant cost of decoding at all: a UIComponentType blob
400
+ * (1127 type-table entries, 5 recursive wrappers) spent ~200 ms per decode
401
+ * with 86% of it in buildDecoder plus the GC churn from the closures it
402
+ * allocates — while east-c, whose decoder is a switch over types and
403
+ * allocates nothing, decoded the same blob in 0.04 ms.
404
+ *
405
+ * Decoders take their DecodeContext as a parameter and close over nothing
406
+ * per-decode, so they are safe to share. The key is the type-table array,
407
+ * which the section skip-cache (#417) returns as one frozen instance per
408
+ * distinct schema — so repeat decodes of a schema hit this cache, and a
409
+ * genuinely new schema simply builds once. */
410
+ const tableDecoderCache = new WeakMap();
411
+ function decodersForTypeTable(typeTable) {
412
+ const cached = tableDecoderCache.get(typeTable);
413
+ if (cached)
414
+ return cached;
415
+ const decTypeCtx = new Map();
416
+ for (const t of typeTable) {
417
+ if (t.type === "Recursive" && t.value?.type === "wrapper") {
418
+ buildDecoder(t, decTypeCtx);
419
+ }
420
+ }
421
+ const entry = { decTypeCtx, decoderCache: new Map() };
422
+ tableDecoderCache.set(typeTable, entry);
423
+ return entry;
424
+ }
425
+ function readMutableValueTableSection(reader, ctx) {
426
+ const sectionByteLength = reader.readVarint();
427
+ const sectionEnd = reader.offset + sectionByteLength;
428
+ const entryCount = reader.readVarint();
429
+ if (entryCount === 0) {
430
+ if (reader.offset !== sectionEnd)
431
+ throw new Error(`Value table section size mismatch`);
432
+ return;
433
+ }
434
+ // Two-pass decode: pre-allocate all containers, then fill elements.
435
+ // Each entry is prefixed with varint(byte_length), enabling trivial skip in pass 1.
436
+ // Pass 1: read entry headers, pre-allocate empty containers, skip element data by byte length
437
+ const entryOffsets = new Array(entryCount);
438
+ const entryLengths = new Array(entryCount);
439
+ for (let i = 0; i < entryCount; i++) {
440
+ const entryByteLength = reader.readVarint();
441
+ const entryStart = reader.offset;
442
+ const kindTag = reader.readUint8();
443
+ switch (kindTag) {
444
+ case TAG_ARRAY: {
445
+ reader.readVarint(); // elem type idx (skip)
446
+ const count = reader.readVarint();
447
+ ctx.mutableValues.push(new Array(count));
448
+ break;
449
+ }
450
+ case TAG_SET: {
451
+ // Sets and Dicts are ORDERED by East's total order — a decoded one
452
+ // must be a SortedSet/SortedMap, not a plain Set/Map. A plain
453
+ // container preserves wire order instead, which (a) violates the
454
+ // ordering invariant every East-created collection holds (see
455
+ // compile.ts) and (b) made TS re-encode a value to different bytes
456
+ // than east-c, whose btrees always sort. That broke cross-runtime
457
+ // byte parity, and with it content-addressing.
458
+ const setElemIdx = reader.readVarint();
459
+ reader.readVarint(); // count (skip — SortedSet doesn't pre-allocate)
460
+ ctx.mutableValues.push(new SortedSet(undefined, compareFor(ctx.typeTable[setElemIdx])));
461
+ break;
462
+ }
463
+ case TAG_DICT: {
464
+ const dictKeyIdx = reader.readVarint();
465
+ reader.readVarint(); // val type idx (skip)
466
+ reader.readVarint(); // count (skip)
467
+ ctx.mutableValues.push(new SortedMap(undefined, compareFor(ctx.typeTable[dictKeyIdx])));
468
+ break;
469
+ }
470
+ case TAG_REF: {
471
+ reader.readVarint(); // inner type idx (skip)
472
+ ctx.mutableValues.push(ref(undefined));
473
+ break;
474
+ }
475
+ default:
476
+ throw new Error(`Unknown value table kind tag 0x${kindTag.toString(16)}`);
477
+ }
478
+ entryOffsets[i] = entryStart;
479
+ entryLengths[i] = entryByteLength;
480
+ // Skip to next entry using byte length
481
+ reader.offset = entryStart + entryByteLength;
482
+ }
483
+ if (reader.offset !== sectionEnd) {
484
+ throw new Error(`Value table section size mismatch: expected ${sectionEnd}, got ${reader.offset}`);
485
+ }
486
+ // Decoder trees for the decoded type table, shared across decodes of the
487
+ // same schema (see decodersForTypeTable).
488
+ const { decTypeCtx, decoderCache } = decodersForTypeTable(ctx.typeTable);
489
+ const getCachedDecoder = (typeIdx) => {
490
+ let dec = decoderCache.get(typeIdx);
491
+ if (!dec) {
492
+ dec = buildDecoder(ctx.typeTable[typeIdx], decTypeCtx);
493
+ decoderCache.set(typeIdx, dec);
494
+ }
495
+ return dec;
496
+ };
497
+ // Pass 2: fill elements in REVERSE order — children (higher indices) before parents.
498
+ // The walker produces parents-before-children, so reversing ensures a parent's
499
+ // element decoders (e.g., Function compilers) see fully-populated child entries.
500
+ for (let i = entryCount - 1; i >= 0; i--) {
501
+ const r = new BufferReader(reader.buffer, entryOffsets[i]);
502
+ const kindTag = r.readUint8();
503
+ switch (kindTag) {
504
+ case TAG_ARRAY: {
505
+ const elemTypeIdx = r.readVarint();
506
+ const elemType = ctx.typeTable[elemTypeIdx];
507
+ const count = r.readVarint();
508
+ const arr = ctx.mutableValues[i];
509
+ const isMut = isMutableType(elemType);
510
+ const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
511
+ for (let j = 0; j < count; j++) {
512
+ arr[j] = isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx);
513
+ }
514
+ break;
515
+ }
516
+ case TAG_SET: {
517
+ const elemTypeIdx = r.readVarint();
518
+ const elemType = ctx.typeTable[elemTypeIdx];
519
+ const count = r.readVarint();
520
+ const set = ctx.mutableValues[i];
521
+ const isMut = isMutableType(elemType);
522
+ const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
523
+ for (let j = 0; j < count; j++) {
524
+ set.add(isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx));
525
+ }
526
+ break;
527
+ }
528
+ case TAG_DICT: {
529
+ const keyTypeIdx = r.readVarint();
530
+ const valTypeIdx = r.readVarint();
531
+ const keyType = ctx.typeTable[keyTypeIdx];
532
+ const valType = ctx.typeTable[valTypeIdx];
533
+ const count = r.readVarint();
534
+ const map = ctx.mutableValues[i];
535
+ const keyMut = isMutableType(keyType);
536
+ const valMut = isMutableType(valType);
537
+ const keyDec = keyMut ? null : getCachedDecoder(keyTypeIdx);
538
+ const valDec = valMut ? null : getCachedDecoder(valTypeIdx);
539
+ for (let j = 0; j < count; j++) {
540
+ const k = keyMut ? ctx.mutableValues[r.readVarint()] : keyDec(r, ctx);
541
+ const v = valMut ? ctx.mutableValues[r.readVarint()] : valDec(r, ctx);
542
+ map.set(k, v);
543
+ }
544
+ break;
545
+ }
546
+ case TAG_REF: {
547
+ const innerTypeIdx = r.readVarint();
548
+ const innerType = ctx.typeTable[innerTypeIdx];
549
+ const rv = ctx.mutableValues[i];
550
+ if (isMutableType(innerType)) {
551
+ rv.value = ctx.mutableValues[r.readVarint()];
552
+ }
553
+ else {
554
+ rv.value = getCachedDecoder(innerTypeIdx)(r, ctx);
555
+ }
556
+ break;
557
+ }
558
+ }
559
+ }
560
+ }
561
+ const baseTypeTableCache = new WeakMap();
562
+ function baseTypeTableFor(type, eastType, typeValue) {
563
+ const cached = baseTypeTableCache.get(type);
564
+ if (cached)
565
+ return cached;
566
+ const builder = new TypeTableBuilder();
567
+ const rootIdx = eastType ? builder.add(eastType) : builder.add(typeValue);
568
+ const sw = new BufferWriter();
569
+ writeTypeTableSection(rootIdx, builder.entries, sw);
570
+ const entry = { builder, rootIdx, sectionBytes: sw.toUint8Array() };
571
+ baseTypeTableCache.set(type, entry);
572
+ return entry;
573
+ }
574
+ export function encodeBeast2V4For(type, options) {
575
+ const eastType = isVariant(type) ? undefined : type;
576
+ const typeValue = isVariant(type) ? type : toEastTypeValue(type);
577
+ // Build encoder tree once at setup — save the typeCtx for reuse by value table entries
578
+ const setupTypeCtx = new Map();
579
+ const valueEncoder = buildEncoder(typeValue, setupTypeCtx);
580
+ // Root-closure type table (stable across encode calls AND closures — #417).
581
+ const base = baseTypeTableFor(type, eastType, typeValue);
582
+ const baseRootIdx = base.rootIdx;
583
+ const baseEntryCount = base.builder.entries.length;
584
+ return (value) => {
585
+ const builder = base.builder.clone();
586
+ const stringTable = new Map();
587
+ // Build the mutable value table by walking the value graph.
588
+ // The walk also discovers the source map from any function in the value graph.
589
+ const vtResult = buildValueTable(value, typeValue, builder);
590
+ const vtEntries = vtResult.entries;
591
+ const indexMap = buildIndexMap(vtEntries);
592
+ // Source map: explicit option > root value > discovered during walk > null
593
+ const sourceMap = options?.sourceMap
594
+ ?? value?.[EAST_SOURCE_MAP_SYMBOL]
595
+ ?? vtResult.sourceMap
596
+ ?? null;
597
+ const ctx = { indexMap, typeTable: builder, stringTable };
598
+ // Encode value table entries FIRST — this discovers types and strings
599
+ // from inside mutable containers (which are varint refs in the value stream)
600
+ const vtWriter = new BufferWriter();
601
+ vtWriter.writeVarint(vtEntries.length);
602
+ for (const entry of vtEntries) {
603
+ // Buffer each entry, then write varint(byte_length) + entry bytes
604
+ const entryWriter = new BufferWriter();
605
+ writeValueTableEntry(entry, entryWriter, ctx, setupTypeCtx);
606
+ const entryBytes = entryWriter.toUint8Array();
607
+ vtWriter.writeVarint(entryBytes.length);
608
+ vtWriter.writeBytes(entryBytes);
609
+ }
610
+ // Encode the value stream (mutable containers are written as varint indices)
611
+ const valueWriter = new BufferWriter();
612
+ valueEncoder(value, valueWriter, ctx);
613
+ // Source map filenames must be added to stringTable before writing string table section
614
+ const smWriter = new BufferWriter();
615
+ writeSourceMapSection(sourceMap, stringTable, smWriter);
616
+ // Write the final blob: magic + type table + string table + source map + value table + value data
617
+ const writer = new BufferWriter();
618
+ writer.writeBytes(MAGIC_BYTES);
619
+ if (builder.entries.length === baseEntryCount) {
620
+ // The value walk added no types — the section is the cached root
621
+ // closure, byte for byte (#417).
622
+ writer.writeBytes(base.sectionBytes);
623
+ }
624
+ else {
625
+ writeTypeTableSection(baseRootIdx, builder.entries, writer);
626
+ }
627
+ writeStringTableSection(stringTable, writer);
628
+ writer.writeBytes(smWriter.toUint8Array());
629
+ // Write value table section with byte-length prefix
630
+ const vtBytes = vtWriter.toUint8Array();
631
+ writer.writeVarint(vtBytes.length);
632
+ writer.writeBytes(vtBytes);
633
+ writer.writeBytes(valueWriter.toUint8Array());
634
+ return writer.toUint8Array();
635
+ };
636
+ }
637
+ export function decodeBeast2V4For(type, options) {
638
+ if (!isVariant(type))
639
+ type = toEastTypeValue(type);
640
+ const typeValue = type;
641
+ // Pre-build value decoder once (reused on every decode call)
642
+ const valueDecoder = buildDecoder(typeValue);
643
+ const platformCtx = buildPlatformContext(options);
644
+ return (data) => {
645
+ verifyMagic(data);
646
+ const reader = new BufferReader(data, MAGIC_BYTES.length);
647
+ const { typeTable } = readTypeTableSection(reader);
648
+ const stringTable = readStringTableSection(reader);
649
+ const sourceMap = readSourceMapSection(reader, stringTable);
650
+ const ctx = {
651
+ mutableValues: [],
652
+ typeTable,
653
+ stringTable,
654
+ sourceMap,
655
+ ...platformCtx,
656
+ };
657
+ readMutableValueTableSection(reader, ctx);
658
+ const value = valueDecoder(reader, ctx);
659
+ if (reader.offset !== data.length) {
660
+ throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
661
+ }
662
+ return value;
663
+ };
664
+ }
665
+ // =============================================================================
666
+ // Public API — IR + source map (v4 container)
667
+ // =============================================================================
668
+ /**
669
+ * Decodes a v4 IRType blob and returns both the IR value and the decoded
670
+ * source map. Backs the version-agnostic `decodeEastIR` / `decodeAsyncEastIR`.
671
+ *
672
+ * @param data - a v4 beast2 blob whose root is a Function/AsyncFunction IR
673
+ * @returns the decoded IR value and the blob's source map (or `null`)
674
+ */
675
+ export function decodeIRWithSourceMapV4(data) {
676
+ verifyMagic(data);
677
+ const reader = new BufferReader(data, MAGIC_BYTES.length);
678
+ const { typeTable } = readTypeTableSection(reader);
679
+ const stringTable = readStringTableSection(reader);
680
+ const sourceMap = readSourceMapSection(reader, stringTable);
681
+ const valueDecoder = buildDecoder(irTypeValue);
682
+ const ctx = {
683
+ mutableValues: [],
684
+ typeTable,
685
+ stringTable,
686
+ sourceMap,
687
+ ...buildPlatformContext(),
688
+ };
689
+ readMutableValueTableSection(reader, ctx);
690
+ const ir = valueDecoder(reader, ctx);
691
+ if (reader.offset !== data.length) {
692
+ throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
693
+ }
694
+ return { ir, sourceMap };
695
+ }
696
+ // =============================================================================
697
+ // Public API — Decode (v4 container, self-describing)
698
+ // =============================================================================
699
+ /**
700
+ * Decodes a self-describing v4 blob using its embedded root type.
701
+ *
702
+ * @param data - a v4 beast2 blob
703
+ * @param options - decode options (platform functions for decoded functions)
704
+ * @returns the blob's root type and decoded value
705
+ */
706
+ export function decodeBeast2V4(data, options) {
707
+ verifyMagic(data);
708
+ const reader = new BufferReader(data, MAGIC_BYTES.length);
709
+ const { rootType, typeTable } = readTypeTableSection(reader);
710
+ const stringTable = readStringTableSection(reader);
711
+ const sourceMap = readSourceMapSection(reader, stringTable);
712
+ const valueDecoder = buildDecoder(rootType);
713
+ const ctx = {
714
+ mutableValues: [],
715
+ typeTable,
716
+ stringTable,
717
+ sourceMap,
718
+ ...buildPlatformContext(options),
719
+ };
720
+ readMutableValueTableSection(reader, ctx);
721
+ const value = valueDecoder(reader, ctx);
722
+ if (reader.offset !== data.length) {
723
+ throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
724
+ }
725
+ return { type: rootType, value };
726
+ }
727
+ //# sourceMappingURL=container.js.map