@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
@@ -3,696 +3,101 @@
3
3
  * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
4
  */
5
5
  /**
6
- * Beast2 v2 — complete encoder and decoder.
6
+ * Beast2 container — version-agnostic public entry points.
7
7
  *
8
- * File layout: magic(8) + type_table_section + string_table_section + value_data
9
- * Type table: flat array of unique types, referenced by varint index.
10
- * String table: flat array of unique strings, referenced by varint index.
11
- * Value data: type-driven positional encoding, no inline type tags.
12
- * Functions: IR encoded directly — EastTypeType schema positions auto-emit type table indices.
13
- *
14
- * See devdocs/BEAST2.md for the full specification.
8
+ * Decoders dispatch on the magic's version byte (`v4` = 0x04, `v5` = 0x05) so
9
+ * every entry point accepts blobs of either container version. Encoders write
10
+ * the v4 container by default. See `SPEC.md` for the magic registry and
11
+ * version policy, `v4/SPEC.md` and `v5/SPEC.md` for the wire formats.
15
12
  */
16
13
  import { toEastTypeValue } from "../../type_of_type.js";
17
- import { isVariant, variant } from "../../containers/variant.js";
18
- import { BufferWriter, BufferReader } from "../binary-utils.js";
19
- import { ref } from "../../containers/ref.js";
20
- import { matrix } from "../../containers/matrix.js";
21
- import { EAST_IR_SYMBOL, EAST_CAPTURES_SYMBOL, EAST_SOURCE_MAP_SYMBOL, ReturnException, compile_internal } from "../../compile.js";
22
- import { InternalError } from "../../error.js";
14
+ import { isVariant } from "../../containers/variant.js";
23
15
  import { IRType } from "../../ir.js";
24
16
  import { EastIR, AsyncEastIR } from "../../eastir.js";
25
- import { TypeTableBuilder, writeTypeTableSection, readTypeTableSection } from "./type-table.js";
26
- import { writeStringTableSection, readStringTableSection } from "./string-table.js";
27
- import { writeSourceMapSection, readSourceMapSection } from "./sourcemap-table.js";
28
- import { buildValueTable, buildIndexMap, isMutableType, TAG_ARRAY, TAG_DICT, TAG_SET, TAG_REF } from "./value-table.js";
29
- // v4: EastTypeType values are encoded as regular variants (no special type-table-index encoding).
30
- // This ensures uniform encoding across value stream and value table, and simplifies multi-runtime implementations.
31
- // Shared empty set for compile_internal's compilingNodes parameter (avoids per-call allocation)
32
- const EMPTY_SET = new Set();
17
+ import {} from "./shared.js";
18
+ import { MAGIC_BYTES, encodeBeast2V4For, decodeBeast2V4For, decodeBeast2V4, decodeIRWithSourceMapV4, } from "./v4/container.js";
19
+ import { encodeBeast2V5For, decodeBeast2V5For, decodeBeast2V5ForAsync, decodeBeast2V5, decodeIRWithSourceMapV5, } from "./v5/codec.js";
20
+ import { BEAST2_WRITE_VERSION } from "./version.js";
21
+ export { MAGIC_BYTES } from "./v4/container.js";
22
+ export { MAGIC_BYTES_V5 } from "./v5/codec.js";
23
+ export { Beast2Writer, encodeBeast2SegmentsFor, iterBeast2SegmentsFor, Beast2Pages, openBeast2PagesFor, } from "./v5/stream.js";
24
+ // =============================================================================
25
+ // Magic dispatch
26
+ // =============================================================================
27
+ export { BEAST2_WRITE_VERSION, BEAST2_READ_VERSIONS } from "./version.js";
33
28
  /**
34
- * Build a safe, bounded description of a value that reached the Function encoder
35
- * with no compiled IR, for the "no IR attached" diagnostic.
29
+ * Sniffs the container version from a blob's magic bytes.
36
30
  *
37
- * The encoder expects a function carrying {@link EAST_IR_SYMBOL}; when that symbol
38
- * is absent the *value itself* is suspect (it may not even be a function — a
39
- * non-function in a `FunctionType` slot lands here too — and it may be a Proxy or
40
- * carry a throwing `toString`). Every inspection is therefore either total
41
- * (`typeof`, `Object.prototype.toString`) or guarded, so the diagnostic can never
42
- * throw and mask the real failure.
43
- *
44
- * @param value - the value found where an East function with IR was expected
45
- * @returns a single-line, length-bounded description
31
+ * @param data - the blob to inspect
32
+ * @returns the container version (4 or 5)
33
+ * @throws {Error} When the data is too short, the magic prefix is wrong, or
34
+ * the version byte is unknown.
46
35
  */
47
- function describeNoIrValue(value) {
48
- const kind = typeof value; // total — never throws
49
- const tag = Object.prototype.toString.call(value); // safe [[Class]]; no user code
50
- const parts = [`typeof=${kind}`, `tag=${tag}`];
51
- try {
52
- if (kind === "function") {
53
- const name = value.name;
54
- if (typeof name === "string" && name)
55
- parts.push(`name=${name}`);
56
- // Built-in toString (not the possibly-overridden value.toString) → the source text.
57
- const src = Function.prototype.toString.call(value).replace(/\s+/g, " ");
58
- parts.push(`source=${src.length > 160 ? `${src.slice(0, 160)}…` : src}`);
59
- parts.push(`hasCaptures=${value[EAST_CAPTURES_SYMBOL] !== undefined}`);
60
- parts.push(`hasSourceMap=${value[EAST_SOURCE_MAP_SYMBOL] !== undefined}`);
61
- }
62
- else if (value !== null && value !== undefined) {
63
- const ctorName = value.constructor?.name;
64
- if (typeof ctorName === "string")
65
- parts.push(`constructor=${ctorName}`);
66
- }
67
- }
68
- catch {
69
- parts.push("(preview unavailable)");
70
- }
71
- return parts.join(" ");
72
- }
73
- // =============================================================================
74
- // Magic bytes (v2: last byte = 0x02)
75
- // =============================================================================
76
- export const MAGIC_BYTES = new Uint8Array([0x89, 0x45, 0x61, 0x73, 0x74, 0x0D, 0x0A, 0x04]);
77
- function verifyMagic(data) {
36
+ function sniffVersion(data) {
78
37
  if (data.length < 8) {
79
38
  throw new Error(`Data too short for Beast2 format: ${data.length} bytes`);
80
39
  }
81
- for (let i = 0; i < 8; i++) {
40
+ for (let i = 0; i < 7; i++) {
82
41
  if (data[i] !== MAGIC_BYTES[i]) {
83
- if (i < 7)
84
- throw new Error(`Invalid Beast2 magic at offset ${i}: expected 0x${MAGIC_BYTES[i].toString(16)}, got 0x${data[i].toString(16)}`);
85
- throw new Error(`Unknown Beast2 version: 0x${data[i].toString(16)}`);
86
- }
87
- }
88
- }
89
- // =============================================================================
90
- // Value encoder factory
91
- // =============================================================================
92
- /**
93
- * Build a value encoder closure tree for the given type.
94
- * The tree is built once and reused for every encode call.
95
- */
96
- function buildEncoder(type, typeCtx = new Map()) {
97
- switch (type.type) {
98
- case "Never":
99
- return () => { throw new Error("Cannot encode value of type Never"); };
100
- case "Null":
101
- return () => { };
102
- case "Boolean":
103
- return (value, writer) => writer.writeUint8(value ? 1 : 0);
104
- case "Integer":
105
- return (value, writer) => writer.writeZigzag(value);
106
- case "Float":
107
- return (value, writer) => writer.writeFloat64LE(value);
108
- case "String":
109
- return (value, writer, ctx) => {
110
- let idx = ctx.stringTable.get(value);
111
- if (idx === undefined) {
112
- idx = ctx.stringTable.size;
113
- ctx.stringTable.set(value, idx);
114
- }
115
- writer.writeVarint(idx);
116
- };
117
- case "DateTime":
118
- return (value, writer) => writer.writeZigzag(BigInt(value.valueOf()));
119
- case "Blob":
120
- return (value, writer) => {
121
- writer.writeVarint(value.length);
122
- writer.writeBytes(value);
123
- };
124
- case "Ref":
125
- case "Array":
126
- case "Set":
127
- case "Dict": {
128
- // v4: all mutable containers are in the value table, encoded as varint(index)
129
- return (value, writer, ctx) => {
130
- const idx = ctx.indexMap.get(value);
131
- if (idx === undefined)
132
- throw new InternalError(`Mutable ${type.type} not found in value table during encode`);
133
- writer.writeVarint(idx);
134
- };
135
- }
136
- case "Struct": {
137
- const fieldEncoders = [];
138
- const ret = (value, writer, ctx) => {
139
- for (const [name, enc] of fieldEncoders)
140
- enc(value[name], writer, ctx);
141
- };
142
- for (const { name, type: fieldType } of type.value) {
143
- fieldEncoders.push([name, buildEncoder(fieldType, typeCtx)]);
144
- }
145
- return ret;
146
- }
147
- case "Variant": {
148
- const caseEncoders = {};
149
- const caseTags = {};
150
- const ret = (value, writer, ctx) => {
151
- writer.writeVarint(caseTags[value.type]);
152
- caseEncoders[value.type](value.value, writer, ctx);
153
- };
154
- for (let i = 0; i < type.value.length; i++) {
155
- const { name, type: caseType } = type.value[i];
156
- caseTags[name] = i;
157
- caseEncoders[name] = buildEncoder(caseType, typeCtx);
158
- }
159
- return ret;
160
- }
161
- case "Recursive": {
162
- if (type.value.type === "wrapper") {
163
- let inner;
164
- const ret = (value, writer, ctx) => inner(value, writer, ctx);
165
- typeCtx.set(type.value.value.id, ret);
166
- inner = buildEncoder(type.value.value.inner, typeCtx);
167
- return ret;
168
- }
169
- const target = typeCtx.get(type.value.value);
170
- if (!target)
171
- throw new InternalError("Recursive type context not found during encoder build");
172
- return target;
42
+ throw new Error(`Invalid Beast2 magic at offset ${i}: expected 0x${MAGIC_BYTES[i].toString(16)}, got 0x${data[i].toString(16)}`);
173
43
  }
174
- case "Function":
175
- case "AsyncFunction": {
176
- // Build IR encoder in the same typeCtx so recursive types resolve correctly
177
- const fnIrEncoder = buildEncoder(irTypeValue, typeCtx);
178
- const captureEncoderCache = new Map();
179
- return (value, writer, ctx) => {
180
- const ir = value[EAST_IR_SYMBOL];
181
- if (!ir)
182
- throw new Error(`Cannot serialize function: no IR attached (${describeNoIrValue(value)})`);
183
- fnIrEncoder(ir, writer, ctx);
184
- const captures = value[EAST_CAPTURES_SYMBOL];
185
- const captureList = ir.value.captures;
186
- writer.writeVarint(captureList.length);
187
- for (const captureVar of captureList) {
188
- const name = captureVar.value.name;
189
- const captureType = captureVar.value.type;
190
- if (!captures)
191
- throw new InternalError("Function has captures but no EAST_CAPTURES_SYMBOL");
192
- const entry = captures[name];
193
- if (!entry)
194
- throw new InternalError(`Capture '${name}' not found`);
195
- let enc = captureEncoderCache.get(captureType);
196
- if (!enc) {
197
- enc = buildEncoder(captureType, typeCtx);
198
- captureEncoderCache.set(captureType, enc);
199
- }
200
- enc(entry.value, writer, ctx);
201
- }
202
- };
203
- }
204
- case "Vector":
205
- return (value, writer) => {
206
- writer.writeVarint(value.length);
207
- writer.writeBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
208
- };
209
- case "Matrix":
210
- return (value, writer) => {
211
- writer.writeVarint(value.rows);
212
- writer.writeVarint(value.cols);
213
- writer.writeBytes(new Uint8Array(value.data.buffer, value.data.byteOffset, value.data.byteLength));
214
- };
215
- default:
216
- throw new Error(`Unknown type: ${type.type}`);
217
44
  }
45
+ const version = data[7];
46
+ if (version === 0x04)
47
+ return 4;
48
+ if (version === 0x05)
49
+ return 5;
50
+ throw new Error(`Unknown Beast2 version: 0x${version.toString(16)}`);
218
51
  }
219
- // =============================================================================
220
- // Value decoder factory
221
- // =============================================================================
222
- /**
223
- * Build a value decoder closure tree for the given type.
224
- * The tree is built once and reused for every decode call.
225
- */
226
- function buildDecoder(type, typeCtx = new Map()) {
227
- switch (type.type) {
228
- case "Never":
229
- return () => { throw new Error("Cannot decode value of type Never"); };
230
- case "Null":
231
- return () => null;
232
- case "Boolean":
233
- return (reader) => reader.readBoolean();
234
- case "Integer":
235
- return (reader) => reader.readZigzag();
236
- case "Float":
237
- return (reader) => reader.readFloat64LE();
238
- case "String":
239
- return (reader, ctx) => {
240
- const idx = reader.readVarint();
241
- if (idx >= ctx.stringTable.length)
242
- throw new Error(`String table index ${idx} out of bounds (table has ${ctx.stringTable.length} entries)`);
243
- return ctx.stringTable[idx];
244
- };
245
- case "DateTime":
246
- return (reader) => new Date(Number(reader.readZigzag()));
247
- case "Blob":
248
- return (reader) => reader.readBytes(reader.readVarint());
249
- case "Ref":
250
- case "Array":
251
- case "Set":
252
- case "Dict": {
253
- // v4: mutable containers are read as varint(table_index) — the actual
254
- // content was decoded from the mutable_value_table_section.
255
- return (reader, ctx) => {
256
- const idx = reader.readVarint();
257
- if (idx >= ctx.mutableValues.length)
258
- throw new Error(`Invalid mutable value table index ${idx} (table has ${ctx.mutableValues.length} entries)`);
259
- return ctx.mutableValues[idx];
260
- };
261
- }
262
- case "Struct": {
263
- const fields = type.value;
264
- const names = [];
265
- const decoders = [];
266
- const ret = (reader, ctx) => {
267
- const result = {};
268
- for (let i = 0; i < names.length; i++)
269
- result[names[i]] = decoders[i](reader, ctx);
270
- return result;
271
- };
272
- for (const { name, type: fieldType } of fields) {
273
- names.push(name);
274
- decoders.push(buildDecoder(fieldType, typeCtx));
275
- }
276
- return ret;
277
- }
278
- case "Variant": {
279
- const caseDecoders = [];
280
- const ret = (reader, ctx) => {
281
- const tagIndex = reader.readVarint();
282
- if (tagIndex >= caseDecoders.length)
283
- throw new Error(`Invalid variant tag ${tagIndex}`);
284
- const [caseName, caseDec] = caseDecoders[tagIndex];
285
- return variant(caseName, caseDec(reader, ctx));
286
- };
287
- for (const { name, type: caseType } of type.value) {
288
- caseDecoders.push([name, buildDecoder(caseType, typeCtx)]);
289
- }
290
- return ret;
291
- }
292
- case "Recursive": {
293
- if (type.value.type === "wrapper") {
294
- let inner;
295
- const ret = (reader, ctx) => inner(reader, ctx);
296
- typeCtx.set(type.value.value.id, ret);
297
- inner = buildDecoder(type.value.value.inner, typeCtx);
298
- return ret;
299
- }
300
- const target = typeCtx.get(type.value.value);
301
- if (!target)
302
- throw new InternalError("Recursive type context not found during decoder build");
303
- return target;
304
- }
305
- case "Function":
306
- case "AsyncFunction": {
307
- const isAsync = type.type === "AsyncFunction";
308
- const fnType = type;
309
- const fnIrDecoder = buildDecoder(irTypeValue, typeCtx);
310
- const captureDecoderCache = new Map();
311
- return (reader, ctx) => {
312
- const ir = fnIrDecoder(reader, ctx);
313
- if (ir.type !== (isAsync ? "AsyncFunction" : "Function")) {
314
- throw new Error(`Expected ${fnType.type} IR, got ${ir.type}`);
315
- }
316
- // Decode captures
317
- const captureCount = reader.readVarint();
318
- if (captureCount !== ir.value.captures.length) {
319
- throw new Error(`Capture count mismatch: IR has ${ir.value.captures.length}, data has ${captureCount}`);
320
- }
321
- const captureContext = {};
322
- const typeContext = {};
323
- for (const captureVar of ir.value.captures) {
324
- const name = captureVar.value.name;
325
- const captureType = captureVar.value.type;
326
- // Get or build cached decoder for this capture type
327
- let dec = captureDecoderCache.get(captureType);
328
- if (!dec) {
329
- dec = buildDecoder(captureType, typeCtx);
330
- captureDecoderCache.set(captureType, dec);
331
- }
332
- const captureValue = dec(reader, ctx);
333
- captureContext[name] = captureVar.value.mutable
334
- ? variant("boxed", captureValue)
335
- : variant("value", captureValue);
336
- typeContext[name] = captureType;
337
- }
338
- // Compile IR to callable function — mutate in place to avoid object spread allocations
339
- ir.value.isAsync = isAsync;
340
- const compiled = compile_internal(ir, typeContext, ctx.platformFns, ctx.asyncPlatformFns, ctx.platform, true, EMPTY_SET);
341
- const rawFn = compiled(captureContext);
342
- const fn = isAsync
343
- ? async (...inputs) => {
344
- try {
345
- return await rawFn(...inputs);
346
- }
347
- catch (e) {
348
- if (e instanceof ReturnException)
349
- return e.value;
350
- throw e;
351
- }
352
- }
353
- : (...inputs) => {
354
- try {
355
- return rawFn(...inputs);
356
- }
357
- catch (e) {
358
- if (e instanceof ReturnException)
359
- return e.value;
360
- throw e;
361
- }
362
- };
363
- // Attach IR, captures, and source map for re-serialization
364
- Object.defineProperty(fn, EAST_IR_SYMBOL, { value: ir, writable: false, enumerable: false, configurable: false });
365
- Object.defineProperty(fn, EAST_CAPTURES_SYMBOL, { value: captureContext, writable: false, enumerable: false, configurable: false });
366
- if (ctx.sourceMap) {
367
- Object.defineProperty(fn, EAST_SOURCE_MAP_SYMBOL, { value: ctx.sourceMap, writable: false, enumerable: false, configurable: false });
368
- }
369
- return fn;
370
- };
371
- }
372
- case "Vector": {
373
- const elemType = type.value.type;
374
- const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
375
- return (reader) => {
376
- const len = reader.readVarint();
377
- const raw = new Uint8Array(reader.readBytesView(len * bpe));
378
- if (elemType === "Float")
379
- return new Float64Array(raw.buffer, 0, len);
380
- if (elemType === "Integer")
381
- return new BigInt64Array(raw.buffer, 0, len);
382
- return new Uint8ClampedArray(raw.buffer, 0, len);
383
- };
384
- }
385
- case "Matrix": {
386
- const elemType = type.value.type;
387
- const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
388
- return (reader) => {
389
- const rows = reader.readVarint();
390
- const cols = reader.readVarint();
391
- const raw = new Uint8Array(reader.readBytesView(rows * cols * bpe));
392
- if (elemType === "Float")
393
- return matrix(new Float64Array(raw.buffer, 0, rows * cols), rows, cols);
394
- if (elemType === "Integer")
395
- return matrix(new BigInt64Array(raw.buffer, 0, rows * cols), rows, cols);
396
- return matrix(new Uint8ClampedArray(raw.buffer, 0, rows * cols), rows, cols);
397
- };
398
- }
399
- default:
400
- throw new Error(`Unknown type: ${type.type}`);
401
- }
402
- }
403
- // =============================================================================
404
- // String table section
405
- // =============================================================================
406
- // String table functions extracted to beast2-string-table.ts
407
- // =============================================================================
408
- // IR encoder/decoder (module-level singletons)
409
- // =============================================================================
410
- const irTypeValue = toEastTypeValue(IRType);
411
- // =============================================================================
412
- // Mutable value table section (v4)
413
- // =============================================================================
414
- function writeValueTableEntry(entry, writer, ctx, encTypeCtx) {
415
- const type = entry.type;
416
- const value = entry.value;
417
- // Write kind tag
418
- writer.writeUint8(entry.kind);
419
- // Element encoder — handles both inline (non-mutable) and table-ref (mutable) cases
420
- const encodeElem = (elemType, val, w, c) => {
421
- if (isMutableType(elemType)) {
422
- const idx = c.indexMap.get(val);
423
- if (idx === undefined)
424
- throw new InternalError("Nested mutable value not in value table");
425
- w.writeVarint(idx);
426
- }
427
- else {
428
- buildEncoder(elemType, encTypeCtx)(val, w, c);
429
- }
430
- };
431
- switch (type.type) {
432
- case "Array": {
433
- if (!ctx.typeTable.has(type.value))
434
- ctx.typeTable.addETV(type.value);
435
- writer.writeVarint(ctx.typeTable.indexOf(type.value));
436
- writer.writeVarint(value.length);
437
- for (const item of value)
438
- encodeElem(type.value, item, writer, ctx);
439
- break;
440
- }
441
- case "Set": {
442
- if (!ctx.typeTable.has(type.value))
443
- ctx.typeTable.addETV(type.value);
444
- writer.writeVarint(ctx.typeTable.indexOf(type.value));
445
- writer.writeVarint(value.size);
446
- for (const key of value)
447
- encodeElem(type.value, key, writer, ctx);
448
- break;
449
- }
450
- case "Dict": {
451
- if (!ctx.typeTable.has(type.value.key))
452
- ctx.typeTable.addETV(type.value.key);
453
- if (!ctx.typeTable.has(type.value.value))
454
- ctx.typeTable.addETV(type.value.value);
455
- writer.writeVarint(ctx.typeTable.indexOf(type.value.key));
456
- writer.writeVarint(ctx.typeTable.indexOf(type.value.value));
457
- writer.writeVarint(value.size);
458
- for (const [k, v] of value) {
459
- encodeElem(type.value.key, k, writer, ctx);
460
- encodeElem(type.value.value, v, writer, ctx);
461
- }
462
- break;
463
- }
464
- case "Ref": {
465
- if (!ctx.typeTable.has(type.value))
466
- ctx.typeTable.addETV(type.value);
467
- writer.writeVarint(ctx.typeTable.indexOf(type.value));
468
- encodeElem(type.value, value.value, writer, ctx);
469
- break;
470
- }
52
+ export function encodeBeast2For(type, options) {
53
+ if ((options?.version ?? BEAST2_WRITE_VERSION) === 4) {
54
+ return encodeBeast2V4For(type, options);
471
55
  }
56
+ return encodeBeast2V5For(type, options);
472
57
  }
473
- function readMutableValueTableSection(reader, ctx) {
474
- const sectionByteLength = reader.readVarint();
475
- const sectionEnd = reader.offset + sectionByteLength;
476
- const entryCount = reader.readVarint();
477
- if (entryCount === 0) {
478
- if (reader.offset !== sectionEnd)
479
- throw new Error(`Value table section size mismatch`);
480
- return;
481
- }
482
- // Two-pass decode: pre-allocate all containers, then fill elements.
483
- // Each entry is prefixed with varint(byte_length), enabling trivial skip in pass 1.
484
- // Pass 1: read entry headers, pre-allocate empty containers, skip element data by byte length
485
- const entryOffsets = new Array(entryCount);
486
- const entryLengths = new Array(entryCount);
487
- for (let i = 0; i < entryCount; i++) {
488
- const entryByteLength = reader.readVarint();
489
- const entryStart = reader.offset;
490
- const kindTag = reader.readUint8();
491
- switch (kindTag) {
492
- case TAG_ARRAY: {
493
- reader.readVarint(); // elem type idx (skip)
494
- const count = reader.readVarint();
495
- ctx.mutableValues.push(new Array(count));
496
- break;
497
- }
498
- case TAG_SET: {
499
- reader.readVarint(); // elem type idx (skip)
500
- reader.readVarint(); // count (skip — Set doesn't pre-allocate by count)
501
- ctx.mutableValues.push(new Set());
502
- break;
503
- }
504
- case TAG_DICT: {
505
- reader.readVarint(); // key type idx (skip)
506
- reader.readVarint(); // val type idx (skip)
507
- reader.readVarint(); // count (skip)
508
- ctx.mutableValues.push(new Map());
509
- break;
510
- }
511
- case TAG_REF: {
512
- reader.readVarint(); // inner type idx (skip)
513
- ctx.mutableValues.push(ref(undefined));
514
- break;
515
- }
516
- default:
517
- throw new Error(`Unknown value table kind tag 0x${kindTag.toString(16)}`);
518
- }
519
- entryOffsets[i] = entryStart;
520
- entryLengths[i] = entryByteLength;
521
- // Skip to next entry using byte length
522
- reader.offset = entryStart + entryByteLength;
523
- }
524
- if (reader.offset !== sectionEnd) {
525
- throw new Error(`Value table section size mismatch: expected ${sectionEnd}, got ${reader.offset}`);
526
- }
527
- // Pre-build decoders for ALL recursive types in the decoded type table into
528
- // a shared typeCtx. This ensures cross-type references resolve correctly
529
- // (e.g., IR arrays inside Functions reference both IRType and EastTypeType).
530
- const decTypeCtx = new Map();
531
- for (const t of ctx.typeTable) {
532
- if (t.type === "Recursive" && t.value?.type === "wrapper") {
533
- buildDecoder(t, decTypeCtx);
534
- }
535
- }
536
- // Cache decoders by element type index — most entries share the same type
537
- const decoderCache = new Map();
538
- const getCachedDecoder = (typeIdx) => {
539
- let dec = decoderCache.get(typeIdx);
540
- if (!dec) {
541
- dec = buildDecoder(ctx.typeTable[typeIdx], decTypeCtx);
542
- decoderCache.set(typeIdx, dec);
58
+ export function decodeBeast2For(type, options) {
59
+ const typeValue = toTypeValue(type);
60
+ // Version decoders are built lazily on first use — most call sites only ever
61
+ // see one container version.
62
+ let v4;
63
+ let v5;
64
+ return (data) => {
65
+ if (sniffVersion(data) === 5) {
66
+ v5 ??= decodeBeast2V5For(typeValue, options);
67
+ return v5(data);
543
68
  }
544
- return dec;
69
+ v4 ??= decodeBeast2V4For(typeValue, options);
70
+ return v4(data);
545
71
  };
546
- // Pass 2: fill elements in REVERSE order — children (higher indices) before parents.
547
- // The walker produces parents-before-children, so reversing ensures a parent's
548
- // element decoders (e.g., Function compilers) see fully-populated child entries.
549
- for (let i = entryCount - 1; i >= 0; i--) {
550
- const r = new BufferReader(reader.buffer, entryOffsets[i]);
551
- const kindTag = r.readUint8();
552
- switch (kindTag) {
553
- case TAG_ARRAY: {
554
- const elemTypeIdx = r.readVarint();
555
- const elemType = ctx.typeTable[elemTypeIdx];
556
- const count = r.readVarint();
557
- const arr = ctx.mutableValues[i];
558
- const isMut = isMutableType(elemType);
559
- const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
560
- for (let j = 0; j < count; j++) {
561
- arr[j] = isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx);
562
- }
563
- break;
564
- }
565
- case TAG_SET: {
566
- const elemTypeIdx = r.readVarint();
567
- const elemType = ctx.typeTable[elemTypeIdx];
568
- const count = r.readVarint();
569
- const set = ctx.mutableValues[i];
570
- const isMut = isMutableType(elemType);
571
- const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
572
- for (let j = 0; j < count; j++) {
573
- set.add(isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx));
574
- }
575
- break;
576
- }
577
- case TAG_DICT: {
578
- const keyTypeIdx = r.readVarint();
579
- const valTypeIdx = r.readVarint();
580
- const keyType = ctx.typeTable[keyTypeIdx];
581
- const valType = ctx.typeTable[valTypeIdx];
582
- const count = r.readVarint();
583
- const map = ctx.mutableValues[i];
584
- const keyMut = isMutableType(keyType);
585
- const valMut = isMutableType(valType);
586
- const keyDec = keyMut ? null : getCachedDecoder(keyTypeIdx);
587
- const valDec = valMut ? null : getCachedDecoder(valTypeIdx);
588
- for (let j = 0; j < count; j++) {
589
- const k = keyMut ? ctx.mutableValues[r.readVarint()] : keyDec(r, ctx);
590
- const v = valMut ? ctx.mutableValues[r.readVarint()] : valDec(r, ctx);
591
- map.set(k, v);
592
- }
593
- break;
594
- }
595
- case TAG_REF: {
596
- const innerTypeIdx = r.readVarint();
597
- const innerType = ctx.typeTable[innerTypeIdx];
598
- const rv = ctx.mutableValues[i];
599
- if (isMutableType(innerType)) {
600
- rv.value = ctx.mutableValues[r.readVarint()];
601
- }
602
- else {
603
- rv.value = getCachedDecoder(innerTypeIdx)(r, ctx);
604
- }
605
- break;
606
- }
607
- }
608
- }
609
72
  }
610
- export function encodeBeast2For(type, options) {
611
- const eastType = isVariant(type) ? undefined : type;
612
- const typeValue = isVariant(type) ? type : toEastTypeValue(type);
613
- // Build encoder tree once at setup — save the typeCtx for reuse by value table entries
614
- const setupTypeCtx = new Map();
615
- const valueEncoder = buildEncoder(typeValue, setupTypeCtx);
616
- // Pre-build the type table for the root type (stable across encode calls).
617
- const baseBuilder = new TypeTableBuilder();
618
- const baseRootIdx = eastType ? baseBuilder.add(eastType) : baseBuilder.add(typeValue);
619
- return (value) => {
620
- const builder = baseBuilder.clone();
621
- const stringTable = new Map();
622
- // Build the mutable value table by walking the value graph.
623
- // The walk also discovers the source map from any function in the value graph.
624
- const vtResult = buildValueTable(value, typeValue, builder);
625
- const vtEntries = vtResult.entries;
626
- const indexMap = buildIndexMap(vtEntries);
627
- // Source map: explicit option > root value > discovered during walk > null
628
- const sourceMap = options?.sourceMap
629
- ?? value?.[EAST_SOURCE_MAP_SYMBOL]
630
- ?? vtResult.sourceMap
631
- ?? null;
632
- const ctx = { indexMap, typeTable: builder, stringTable };
633
- // Encode value table entries FIRST — this discovers types and strings
634
- // from inside mutable containers (which are varint refs in the value stream)
635
- const vtWriter = new BufferWriter();
636
- vtWriter.writeVarint(vtEntries.length);
637
- for (const entry of vtEntries) {
638
- // Buffer each entry, then write varint(byte_length) + entry bytes
639
- const entryWriter = new BufferWriter();
640
- writeValueTableEntry(entry, entryWriter, ctx, setupTypeCtx);
641
- const entryBytes = entryWriter.toUint8Array();
642
- vtWriter.writeVarint(entryBytes.length);
643
- vtWriter.writeBytes(entryBytes);
644
- }
645
- // Encode the value stream (mutable containers are written as varint indices)
646
- const valueWriter = new BufferWriter();
647
- valueEncoder(value, valueWriter, ctx);
648
- // Source map filenames must be added to stringTable before writing string table section
649
- const smWriter = new BufferWriter();
650
- writeSourceMapSection(sourceMap, stringTable, smWriter);
651
- // Write the final blob: magic + type table + string table + source map + value table + value data
652
- const writer = new BufferWriter();
653
- writer.writeBytes(MAGIC_BYTES);
654
- writeTypeTableSection(baseRootIdx, builder.entries, writer);
655
- writeStringTableSection(stringTable, writer);
656
- writer.writeBytes(smWriter.toUint8Array());
657
- // Write value table section with byte-length prefix
658
- const vtBytes = vtWriter.toUint8Array();
659
- writer.writeVarint(vtBytes.length);
660
- writer.writeBytes(vtBytes);
661
- writer.writeBytes(valueWriter.toUint8Array());
662
- return writer.toUint8Array();
73
+ export function decodeBeast2ForAsync(type, options) {
74
+ const typeValue = toTypeValue(type);
75
+ let v4;
76
+ let v5;
77
+ return async (data) => {
78
+ if (sniffVersion(data) === 5) {
79
+ v5 ??= decodeBeast2V5ForAsync(typeValue, options);
80
+ return v5(data);
81
+ }
82
+ v4 ??= decodeBeast2V4For(typeValue, options);
83
+ return v4(data);
663
84
  };
664
85
  }
665
- export function decodeBeast2For(type, options) {
666
- if (!isVariant(type))
667
- type = toEastTypeValue(type);
668
- const typeValue = type;
669
- // Pre-build value decoder once (reused on every decode call)
670
- const valueDecoder = buildDecoder(typeValue);
671
- const platform = options?.platform ?? [];
672
- const platformFns = Object.fromEntries(platform.map(fn => [fn.name, fn.fn]));
673
- const asyncPlatformFns = new Set(platform.filter(fn => fn.type === 'async').map(fn => fn.name));
674
- return (data) => {
675
- verifyMagic(data);
676
- const reader = new BufferReader(data, MAGIC_BYTES.length);
677
- const { typeTable } = readTypeTableSection(reader);
678
- const stringTable = readStringTableSection(reader);
679
- const sourceMap = readSourceMapSection(reader, stringTable);
680
- const ctx = {
681
- mutableValues: [],
682
- typeTable,
683
- stringTable,
684
- sourceMap,
685
- platform,
686
- platformFns,
687
- asyncPlatformFns,
688
- };
689
- readMutableValueTableSection(reader, ctx);
690
- const value = 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 value;
695
- };
86
+ // =============================================================================
87
+ // Public API — Decode (self-describing)
88
+ // =============================================================================
89
+ /**
90
+ * Decodes a self-describing blob using its embedded root type.
91
+ *
92
+ * @param data - a beast2 blob of any supported container version
93
+ * @param options - decode options (platform functions for decoded functions)
94
+ * @returns the blob's root type and decoded value
95
+ */
96
+ export function decodeBeast2(data, options) {
97
+ if (sniffVersion(data) === 5) {
98
+ return decodeBeast2V5(data, options);
99
+ }
100
+ return decodeBeast2V4(data, options);
696
101
  }
697
102
  // =============================================================================
698
103
  // Public API — EastIR bundle (IR + source map travelling together)
@@ -705,8 +110,8 @@ export function decodeBeast2For(type, options) {
705
110
  /** Encode an EastIR (or AsyncEastIR) as a beast2 blob that carries both the
706
111
  * IR and its source map. Prefer this over `encodeBeast2For(IRType)(ir)` when
707
112
  * encoding a program rather than opaque IR data. */
708
- export function encodeEastIR(eastIR) {
709
- return encodeBeast2For(IRType, { sourceMap: eastIR.source_map })(eastIR.ir);
113
+ export function encodeEastIR(eastIR, options) {
114
+ return encodeBeast2For(IRType, { ...options, sourceMap: eastIR.source_map })(eastIR.ir);
710
115
  }
711
116
  /** Decode a beast2 blob whose root is a Function IR into an EastIR. The
712
117
  * returned wrapper has its source_map populated from the blob's source_map
@@ -730,58 +135,13 @@ export function decodeAsyncEastIR(data) {
730
135
  result.source_map = sourceMap;
731
136
  return result;
732
137
  }
733
- /** Internal: decode IRType blob and return both the IR value and the decoded
734
- * source map. Shared by decodeEastIR / decodeAsyncEastIR. */
138
+ /** Internal: decode an IRType blob of any container version and return both
139
+ * the IR value and the decoded source map. */
735
140
  function decodeIRWithSourceMap(data) {
736
- verifyMagic(data);
737
- const reader = new BufferReader(data, MAGIC_BYTES.length);
738
- const { typeTable } = readTypeTableSection(reader);
739
- const stringTable = readStringTableSection(reader);
740
- const sourceMap = readSourceMapSection(reader, stringTable);
741
- const irTypeValue = toEastTypeValue(IRType);
742
- const valueDecoder = buildDecoder(irTypeValue);
743
- const ctx = {
744
- mutableValues: [],
745
- typeTable,
746
- stringTable,
747
- sourceMap,
748
- platform: [],
749
- platformFns: {},
750
- asyncPlatformFns: new Set(),
751
- };
752
- readMutableValueTableSection(reader, ctx);
753
- const ir = valueDecoder(reader, ctx);
754
- if (reader.offset !== data.length) {
755
- throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
141
+ if (sniffVersion(data) === 5) {
142
+ return decodeIRWithSourceMapV5(data);
756
143
  }
757
- return { ir, sourceMap };
758
- }
759
- // =============================================================================
760
- // Public API — Decode (self-describing)
761
- // =============================================================================
762
- export function decodeBeast2(data, options) {
763
- verifyMagic(data);
764
- const reader = new BufferReader(data, MAGIC_BYTES.length);
765
- const { rootType, typeTable } = readTypeTableSection(reader);
766
- const stringTable = readStringTableSection(reader);
767
- const sourceMap = readSourceMapSection(reader, stringTable);
768
- const valueDecoder = buildDecoder(rootType);
769
- const platform = options?.platform ?? [];
770
- const ctx = {
771
- mutableValues: [],
772
- typeTable,
773
- stringTable,
774
- sourceMap,
775
- platform,
776
- platformFns: Object.fromEntries(platform.map(fn => [fn.name, fn.fn])),
777
- asyncPlatformFns: new Set(platform.filter(fn => fn.type === 'async').map(fn => fn.name)),
778
- };
779
- readMutableValueTableSection(reader, ctx);
780
- const value = valueDecoder(reader, ctx);
781
- if (reader.offset !== data.length) {
782
- throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
783
- }
784
- return { type: rootType, value };
144
+ return decodeIRWithSourceMapV4(data);
785
145
  }
786
146
  // =============================================================================
787
147
  // Re-exports
@@ -793,4 +153,11 @@ export function compileFunctionIR(ir, platform) {
793
153
  export function compileAsyncFunctionIR(ir, platform) {
794
154
  return new AsyncEastIR(ir).compile(platform);
795
155
  }
156
+ // =============================================================================
157
+ // Internal helpers
158
+ // =============================================================================
159
+ /** Normalizes an `EastType | EastTypeValue` to `EastTypeValue`. */
160
+ function toTypeValue(type) {
161
+ return isVariant(type) ? type : toEastTypeValue(type);
162
+ }
796
163
  //# sourceMappingURL=index.js.map