@minnowdb/core 0.7.0 → 0.7.1

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 (95) hide show
  1. package/dist/block-format/block.js +254 -325
  2. package/dist/block-format/checksum.js +30 -52
  3. package/dist/block-format/codecs.js +112 -125
  4. package/dist/block-format/column.js +187 -205
  5. package/dist/block-format/index.js +5 -1
  6. package/dist/block-format/physical.js +371 -387
  7. package/dist/block-format/types.js +4 -1
  8. package/dist/block-format/unicode.js +30 -39
  9. package/dist/date-value.js +67 -78
  10. package/dist/engine/artifact-cache.js +106 -116
  11. package/dist/engine/batch.js +36 -44
  12. package/dist/engine/buffered-writer.js +156 -158
  13. package/dist/engine/byte-estimates.js +20 -22
  14. package/dist/engine/cache-limits.js +24 -25
  15. package/dist/engine/cancellation.js +5 -3
  16. package/dist/engine/catalog.js +64 -73
  17. package/dist/engine/client.js +715 -780
  18. package/dist/engine/database.js +15941 -19313
  19. package/dist/engine/defaults.js +104 -111
  20. package/dist/engine/errors.js +113 -128
  21. package/dist/engine/fts.js +233 -280
  22. package/dist/engine/group-index.js +287 -321
  23. package/dist/engine/index.js +23 -12
  24. package/dist/engine/join-index.js +177 -186
  25. package/dist/engine/keyed-live.js +168 -173
  26. package/dist/engine/live-api.js +0 -1
  27. package/dist/engine/live-equal.js +32 -37
  28. package/dist/engine/live.js +603 -647
  29. package/dist/engine/memory.js +109 -111
  30. package/dist/engine/optimizer.d.ts +19 -1
  31. package/dist/engine/optimizer.js +2633 -2934
  32. package/dist/engine/point-read.js +122 -182
  33. package/dist/engine/query-api.js +12 -3
  34. package/dist/engine/query-cache.js +61 -69
  35. package/dist/engine/query.d.ts +6 -0
  36. package/dist/engine/query.js +8503 -9980
  37. package/dist/engine/result-wire.js +221 -252
  38. package/dist/engine/schema-wire.js +97 -112
  39. package/dist/engine/schema.js +1099 -1254
  40. package/dist/engine/sort-keys.js +315 -389
  41. package/dist/engine/sql-domains.js +646 -769
  42. package/dist/engine/sql-driver.js +0 -1
  43. package/dist/engine/sql-functions.js +1031 -1125
  44. package/dist/engine/sql-json.js +199 -218
  45. package/dist/engine/sql-semantics.js +572 -639
  46. package/dist/engine/typed-live.js +271 -293
  47. package/dist/engine/vector.js +4484 -5145
  48. package/dist/engine/worker-host.d.ts +8 -63
  49. package/dist/engine/worker-host.js +28 -1130
  50. package/dist/engine/worker-indexeddb.d.ts +1 -0
  51. package/dist/engine/worker-indexeddb.js +3 -0
  52. package/dist/engine/worker-memory.d.ts +1 -0
  53. package/dist/engine/worker-memory.js +3 -0
  54. package/dist/engine/worker-opfs.d.ts +1 -0
  55. package/dist/engine/worker-opfs.js +3 -0
  56. package/dist/engine/worker-server.d.ts +93 -0
  57. package/dist/engine/worker-server.js +999 -0
  58. package/dist/engine/worker-store-indexeddb.d.ts +2 -0
  59. package/dist/engine/worker-store-indexeddb.js +11 -0
  60. package/dist/engine/worker-store-memory.d.ts +2 -0
  61. package/dist/engine/worker-store-memory.js +6 -0
  62. package/dist/engine/worker-store-opfs.d.ts +2 -0
  63. package/dist/engine/worker-store-opfs.js +9 -0
  64. package/dist/engine/worker.js +0 -12
  65. package/dist/engine/write-block-planner.js +93 -100
  66. package/dist/plan/index.js +15 -16
  67. package/dist/plan/model.d.ts +6 -0
  68. package/dist/plan/model.js +20 -27
  69. package/dist/storage/index.js +0 -12
  70. package/dist/storage/indexeddb.js +11776 -13171
  71. package/dist/storage/memory.js +1198 -1241
  72. package/dist/storage/opfs/files.js +238 -295
  73. package/dist/storage/opfs/index.js +9 -3
  74. package/dist/storage/opfs/leader.js +4386 -4856
  75. package/dist/storage/opfs/rpc.js +223 -259
  76. package/dist/storage/opfs/snapshot-ledger.js +219 -260
  77. package/dist/storage/opfs/store.js +884 -1003
  78. package/dist/storage/persistence.js +59 -74
  79. package/dist/storage/snapshot-stream.js +788 -826
  80. package/dist/storage/snapshot.js +36 -37
  81. package/dist/storage/toolkit/extents.js +444 -520
  82. package/dist/storage/toolkit/index.js +28 -29
  83. package/dist/storage/toolkit/record-core.js +5792 -6377
  84. package/dist/storage/toolkit/sync-file.js +34 -42
  85. package/dist/storage/toolkit/wal.js +92 -127
  86. package/dist/storage/toolkit/wire.js +232 -256
  87. package/dist/storage/types.js +2948 -3250
  88. package/dist/testing/block-store-conformance.js +1556 -1629
  89. package/dist/testing/index.js +310 -291
  90. package/dist/testing/opfs-shim.js +280 -312
  91. package/dist/testing/simulator.js +496 -534
  92. package/dist/testing/sqllogictest.js +425 -365
  93. package/dist/transactions/index.js +1464 -1712
  94. package/dist/worker-protocol/index.js +70 -69
  95. package/package.json +20 -2
@@ -1,289 +1,265 @@
1
1
  import { crc32 } from "../../block-format/index.js";
2
2
  import { MAX_ROW_ID_EXCLUSIVE_END, StorageFormatVersionError } from "../types.js";
3
- /**
4
- * Payload encodings for a log-structured adapter's control data: checkpoint files, immutable
5
- * artifact chunks, and the JSON codec write-ahead-log frames carry. Every envelope is
6
- * checksummed so a torn write — the only artifact a crash can leave — is detectable and
7
- * indistinguishable from "not written".
8
- *
9
- * Record payloads are JSON with one extension: bigints (segment row ids, counters, full-text
10
- * posting row ids, compaction rewrite plans) encode as `{"$n":"<decimal>"}`. No record shape
11
- * can produce that object naturally — keys are ids and column names, values are JSON scalars —
12
- * and the reviver only converts an object whose sole key is `$n`.
13
- */
14
- export const LOG_FORMAT_VERSION = 5;
3
+ const LOG_FORMAT_VERSION = 5;
15
4
  const textEncoder = new TextEncoder();
16
5
  const textDecoder = new TextDecoder("utf-8", { fatal: true });
17
- export function encodeRecordJson(value) {
18
- // The replacer costs ~3x on big payloads because it runs per node; most frames carry no
19
- // bigints at all, and stringify announces one by throwing.
20
- let json;
21
- try {
22
- json = JSON.stringify(value);
23
- }
24
- catch {
25
- json = JSON.stringify(value, (_key, entry) => {
26
- if (typeof entry !== "bigint")
27
- return entry;
28
- if (entry < 0n || entry > MAX_ROW_ID_EXCLUSIVE_END) {
29
- throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
30
- }
31
- return { $n: entry.toString() };
32
- });
33
- }
34
- if (typeof json !== "string")
35
- throw new TypeError("Record value is not JSON-serializable");
36
- return textEncoder.encode(json);
37
- }
38
- export function decodeRecordJson(bytes) {
39
- const json = textDecoder.decode(bytes);
40
- return JSON.parse(json, (_key, entry) => {
41
- if (typeof entry === "object" &&
42
- entry !== null &&
43
- !Array.isArray(entry) &&
44
- Object.keys(entry).length === 1 &&
45
- typeof entry.$n === "string") {
46
- const decimal = entry.$n;
47
- if (!/^(?:0|[1-9][0-9]{0,19})$/.test(decimal)) {
48
- throw new TypeError("Record bigint is not a canonical bounded unsigned decimal");
49
- }
50
- const value = BigInt(decimal);
51
- if (value > MAX_ROW_ID_EXCLUSIVE_END) {
52
- throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
53
- }
54
- return value;
55
- }
6
+ function encodeRecordJson(value) {
7
+ let json;
8
+ try {
9
+ json = JSON.stringify(value);
10
+ } catch {
11
+ json = JSON.stringify(value, (_key, entry) => {
12
+ if (typeof entry !== "bigint")
56
13
  return entry;
14
+ if (entry < 0n || entry > MAX_ROW_ID_EXCLUSIVE_END) {
15
+ throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
16
+ }
17
+ return { $n: entry.toString() };
57
18
  });
19
+ }
20
+ if (typeof json !== "string")
21
+ throw new TypeError("Record value is not JSON-serializable");
22
+ return textEncoder.encode(json);
23
+ }
24
+ function decodeRecordJson(bytes) {
25
+ const json = textDecoder.decode(bytes);
26
+ return JSON.parse(json, (_key, entry) => {
27
+ if (typeof entry === "object" && entry !== null && !Array.isArray(entry) && Object.keys(entry).length === 1 && typeof entry.$n === "string") {
28
+ const decimal = entry.$n;
29
+ if (!/^(?:0|[1-9][0-9]{0,19})$/.test(decimal)) {
30
+ throw new TypeError("Record bigint is not a canonical bounded unsigned decimal");
31
+ }
32
+ const value = BigInt(decimal);
33
+ if (value > MAX_ROW_ID_EXCLUSIVE_END) {
34
+ throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
35
+ }
36
+ return value;
37
+ }
38
+ return entry;
39
+ });
58
40
  }
59
41
  const ENVELOPE_HEADER_BYTES = 8 + 4 + 4 + 4;
60
42
  function encodeEnvelope(magic, payload) {
61
- const bytes = new Uint8Array(ENVELOPE_HEADER_BYTES + payload.byteLength);
62
- const view = new DataView(bytes.buffer);
63
- textEncoder.encodeInto(magic, bytes);
64
- view.setUint32(8, LOG_FORMAT_VERSION, true);
65
- view.setUint32(12, payload.byteLength, true);
66
- view.setUint32(16, crc32(payload), true);
67
- bytes.set(payload, ENVELOPE_HEADER_BYTES);
68
- return bytes;
43
+ const bytes = new Uint8Array(ENVELOPE_HEADER_BYTES + payload.byteLength);
44
+ const view = new DataView(bytes.buffer);
45
+ textEncoder.encodeInto(magic, bytes);
46
+ view.setUint32(8, LOG_FORMAT_VERSION, true);
47
+ view.setUint32(12, payload.byteLength, true);
48
+ view.setUint32(16, crc32(payload), true);
49
+ bytes.set(payload, ENVELOPE_HEADER_BYTES);
50
+ return bytes;
69
51
  }
70
- /**
71
- * `undefined` means the bytes are torn or foreign — treat as "not written". A recognized magic
72
- * with an unknown format version throws instead: reading a different layout as the current one
73
- * would silently corrupt or roll back the database.
74
- */
75
52
  function decodeEnvelope(magic, bytes) {
76
- if (bytes.byteLength < ENVELOPE_HEADER_BYTES)
77
- return undefined;
78
- for (let index = 0; index < 8; index += 1) {
79
- if (bytes[index] !== magic.charCodeAt(index))
80
- return undefined;
81
- }
82
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
83
- const version = view.getUint32(8, true);
84
- if (version !== LOG_FORMAT_VERSION) {
85
- throw new StorageFormatVersionError("opfs", `envelope/${magic}`, version, LOG_FORMAT_VERSION, version < LOG_FORMAT_VERSION ? "older" : "newer");
86
- }
87
- const payloadLength = view.getUint32(12, true);
88
- if (bytes.byteLength !== ENVELOPE_HEADER_BYTES + payloadLength)
89
- return undefined;
90
- const payload = bytes.subarray(ENVELOPE_HEADER_BYTES, ENVELOPE_HEADER_BYTES + payloadLength);
91
- if (crc32(payload) !== view.getUint32(16, true))
92
- return undefined;
93
- return payload;
53
+ if (bytes.byteLength < ENVELOPE_HEADER_BYTES)
54
+ return void 0;
55
+ for (let index = 0; index < 8; index += 1) {
56
+ if (bytes[index] !== magic.charCodeAt(index))
57
+ return void 0;
58
+ }
59
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
60
+ const version = view.getUint32(8, true);
61
+ if (version !== LOG_FORMAT_VERSION) {
62
+ throw new StorageFormatVersionError("opfs", `envelope/${magic}`, version, LOG_FORMAT_VERSION, version < LOG_FORMAT_VERSION ? "older" : "newer");
63
+ }
64
+ const payloadLength = view.getUint32(12, true);
65
+ if (bytes.byteLength !== ENVELOPE_HEADER_BYTES + payloadLength)
66
+ return void 0;
67
+ const payload = bytes.subarray(ENVELOPE_HEADER_BYTES, ENVELOPE_HEADER_BYTES + payloadLength);
68
+ if (crc32(payload) !== view.getUint32(16, true))
69
+ return void 0;
70
+ return payload;
94
71
  }
95
72
  const CHUNK_MAGIC = "MNWCHNK1";
96
73
  const POSTING_CHUNK_MAGIC = "MNWPOST1";
97
74
  const SYNC_CHECKPOINT_MAGIC = "MNWCKPS1";
98
- /** Immutable artifact payloads (full-text base chunks) inside extents. */
99
- export function encodeChunk(value) {
100
- return encodeEnvelope(CHUNK_MAGIC, encodeRecordJson(value));
75
+ function encodeChunk(value) {
76
+ return encodeEnvelope(CHUNK_MAGIC, encodeRecordJson(value));
101
77
  }
102
- export function decodeChunk(bytes) {
103
- const payload = decodeEnvelope(CHUNK_MAGIC, bytes);
104
- return payload === undefined ? undefined : decodeRecordJson(payload);
78
+ function decodeChunk(bytes) {
79
+ const payload = decodeEnvelope(CHUNK_MAGIC, bytes);
80
+ return payload === void 0 ? void 0 : decodeRecordJson(payload);
105
81
  }
106
- /**
107
- * Compact postings payload for OPFS. Terms are UTF-8 and the already-sorted row locators are
108
- * delta-varints, keeping secondary indexes smaller than the table they accelerate.
109
- * The envelope keeps the same version and checksum guarantees as every other immutable chunk.
110
- */
111
- export function encodePostingChunk(entries) {
112
- const writer = new BinaryWriter();
113
- writer.varuint(BigInt(entries.length));
114
- for (const entry of entries) {
115
- if (entry.rowIds.length !== entry.tf.length) {
116
- throw new TypeError("Posting row IDs and term frequencies must have the same length");
117
- }
118
- const term = textEncoder.encode(entry.term);
119
- if (term.byteLength === 0)
120
- throw new TypeError("Posting terms cannot be empty");
121
- if (entry.rowIds.length === 0)
122
- throw new TypeError("Postings cannot have no row IDs");
123
- writer.varuint(BigInt(term.byteLength));
124
- writer.bytes(term);
125
- writer.varuint(BigInt(entry.rowIds.length));
126
- let previous = 0n;
127
- for (const rowId of entry.rowIds) {
128
- if (rowId <= previous || rowId > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
129
- throw new TypeError("Posting row IDs must be positive, uint64, and strictly sorted");
130
- }
131
- writer.varuint(rowId - previous);
132
- previous = rowId;
133
- }
134
- for (const frequency of entry.tf) {
135
- if (!Number.isSafeInteger(frequency) || frequency <= 0) {
136
- throw new TypeError("Posting term frequencies must be positive whole numbers");
137
- }
138
- writer.varuint(BigInt(frequency));
139
- }
82
+ function encodePostingChunk(entries) {
83
+ const writer = new BinaryWriter();
84
+ writer.varuint(BigInt(entries.length));
85
+ for (const entry of entries) {
86
+ if (entry.rowIds.length !== entry.tf.length) {
87
+ throw new TypeError("Posting row IDs and term frequencies must have the same length");
140
88
  }
141
- return encodeEnvelope(POSTING_CHUNK_MAGIC, writer.finish());
142
- }
143
- /** `undefined` means the bytes are torn or are not a canonical postings envelope. */
144
- export function decodePostingChunk(bytes) {
145
- const payload = decodeEnvelope(POSTING_CHUNK_MAGIC, bytes);
146
- if (payload === undefined)
147
- return undefined;
148
- const reader = new BinaryReader(payload);
149
- const count = reader.safeInteger("posting count");
150
- if (count > Math.floor(reader.remaining / 5)) {
151
- throw new Error("Posting count exceeds the remaining payload");
89
+ const term = textEncoder.encode(entry.term);
90
+ if (term.byteLength === 0)
91
+ throw new TypeError("Posting terms cannot be empty");
92
+ if (entry.rowIds.length === 0)
93
+ throw new TypeError("Postings cannot have no row IDs");
94
+ writer.varuint(BigInt(term.byteLength));
95
+ writer.bytes(term);
96
+ writer.varuint(BigInt(entry.rowIds.length));
97
+ let previous = 0n;
98
+ for (const rowId of entry.rowIds) {
99
+ if (rowId <= previous || rowId > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
100
+ throw new TypeError("Posting row IDs must be positive, uint64, and strictly sorted");
101
+ }
102
+ writer.varuint(rowId - previous);
103
+ previous = rowId;
152
104
  }
153
- const entries = new Array(count);
154
- for (let entryIndex = 0; entryIndex < count; entryIndex += 1) {
155
- const termLength = reader.safeInteger("posting term length");
156
- if (termLength < 1 || termLength > reader.remaining) {
157
- throw new Error("Posting term length exceeds the remaining payload");
158
- }
159
- const term = textDecoder.decode(reader.bytes(termLength));
160
- const rowCount = reader.safeInteger("posting row count");
161
- if (rowCount < 1 || rowCount > Math.floor(reader.remaining / 2)) {
162
- throw new Error("Posting row count exceeds the remaining payload");
163
- }
164
- const rowIds = new Array(rowCount);
165
- let previous = 0n;
166
- for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
167
- previous += reader.varuint();
168
- if (previous < 1n || previous > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
169
- throw new RangeError("Posting row ID exceeds the uint64 range");
170
- }
171
- if (rowIndex > 0 && previous <= (rowIds[rowIndex - 1] ?? 0n)) {
172
- throw new TypeError("Posting row IDs are not strictly sorted");
173
- }
174
- rowIds[rowIndex] = previous;
175
- }
176
- const tf = new Array(rowCount);
177
- for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
178
- const frequency = reader.safeInteger("posting term frequency");
179
- if (frequency <= 0)
180
- throw new TypeError("Posting term frequency must be positive");
181
- tf[rowIndex] = frequency;
182
- }
183
- entries[entryIndex] = { term, rowIds, tf };
105
+ for (const frequency of entry.tf) {
106
+ if (!Number.isSafeInteger(frequency) || frequency <= 0) {
107
+ throw new TypeError("Posting term frequencies must be positive whole numbers");
108
+ }
109
+ writer.varuint(BigInt(frequency));
184
110
  }
185
- if (!reader.done)
186
- throw new Error("Posting chunk has trailing bytes");
187
- return entries;
111
+ }
112
+ return encodeEnvelope(POSTING_CHUNK_MAGIC, writer.finish());
188
113
  }
189
- class BinaryWriter {
190
- #buffer = new Uint8Array(1_024);
191
- #length = 0;
192
- bytes(bytes) {
193
- this.#reserve(bytes.byteLength);
194
- this.#buffer.set(bytes, this.#length);
195
- this.#length += bytes.byteLength;
114
+ function decodePostingChunk(bytes) {
115
+ const payload = decodeEnvelope(POSTING_CHUNK_MAGIC, bytes);
116
+ if (payload === void 0)
117
+ return void 0;
118
+ const reader = new BinaryReader(payload);
119
+ const count = reader.safeInteger("posting count");
120
+ if (count > Math.floor(reader.remaining / 5)) {
121
+ throw new Error("Posting count exceeds the remaining payload");
122
+ }
123
+ const entries = new Array(count);
124
+ for (let entryIndex = 0; entryIndex < count; entryIndex += 1) {
125
+ const termLength = reader.safeInteger("posting term length");
126
+ if (termLength < 1 || termLength > reader.remaining) {
127
+ throw new Error("Posting term length exceeds the remaining payload");
196
128
  }
197
- varuint(value) {
198
- if (value < 0n || value >= MAX_ROW_ID_EXCLUSIVE_END) {
199
- throw new RangeError("A varuint must fit canonical uint64");
200
- }
201
- do {
202
- this.#reserve(1);
203
- const byte = Number(value & 0x7fn);
204
- value >>= 7n;
205
- this.#buffer[this.#length] = value === 0n ? byte : byte | 0x80;
206
- this.#length += 1;
207
- } while (value !== 0n);
129
+ const term = textDecoder.decode(reader.bytes(termLength));
130
+ const rowCount = reader.safeInteger("posting row count");
131
+ if (rowCount < 1 || rowCount > Math.floor(reader.remaining / 2)) {
132
+ throw new Error("Posting row count exceeds the remaining payload");
208
133
  }
209
- finish() {
210
- return this.#buffer.slice(0, this.#length);
134
+ const rowIds = new Array(rowCount);
135
+ let previous = 0n;
136
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
137
+ previous += reader.varuint();
138
+ if (previous < 1n || previous > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
139
+ throw new RangeError("Posting row ID exceeds the uint64 range");
140
+ }
141
+ if (rowIndex > 0 && previous <= (rowIds[rowIndex - 1] ?? 0n)) {
142
+ throw new TypeError("Posting row IDs are not strictly sorted");
143
+ }
144
+ rowIds[rowIndex] = previous;
211
145
  }
212
- #reserve(extra) {
213
- if (!Number.isSafeInteger(extra) || extra < 0)
214
- throw new RangeError("Invalid binary growth");
215
- const needed = this.#length + extra;
216
- if (!Number.isSafeInteger(needed))
217
- throw new RangeError("Binary payload is too large");
218
- if (needed <= this.#buffer.byteLength)
219
- return;
220
- let capacity = this.#buffer.byteLength;
221
- while (capacity < needed) {
222
- const doubled = capacity * 2;
223
- capacity = Number.isSafeInteger(doubled) ? doubled : needed;
224
- }
225
- const grown = new Uint8Array(capacity);
226
- grown.set(this.#buffer);
227
- this.#buffer = grown;
146
+ const tf = new Array(rowCount);
147
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
148
+ const frequency = reader.safeInteger("posting term frequency");
149
+ if (frequency <= 0)
150
+ throw new TypeError("Posting term frequency must be positive");
151
+ tf[rowIndex] = frequency;
228
152
  }
153
+ entries[entryIndex] = { term, rowIds, tf };
154
+ }
155
+ if (!reader.done)
156
+ throw new Error("Posting chunk has trailing bytes");
157
+ return entries;
229
158
  }
230
- class BinaryReader {
231
- source;
232
- #offset = 0;
233
- constructor(source) {
234
- this.source = source;
235
- }
236
- get done() {
237
- return this.#offset === this.source.byteLength;
238
- }
239
- get remaining() {
240
- return this.source.byteLength - this.#offset;
159
+ class BinaryWriter {
160
+ #buffer = new Uint8Array(1024);
161
+ #length = 0;
162
+ bytes(bytes) {
163
+ this.#reserve(bytes.byteLength);
164
+ this.#buffer.set(bytes, this.#length);
165
+ this.#length += bytes.byteLength;
166
+ }
167
+ varuint(value) {
168
+ if (value < 0n || value >= MAX_ROW_ID_EXCLUSIVE_END) {
169
+ throw new RangeError("A varuint must fit canonical uint64");
241
170
  }
242
- bytes(length) {
243
- if (!Number.isSafeInteger(length) || length < 0 || length > this.remaining)
244
- throw new Error("Posting chunk is truncated");
245
- const bytes = this.source.subarray(this.#offset, this.#offset + length);
246
- this.#offset += length;
247
- return bytes;
171
+ do {
172
+ this.#reserve(1);
173
+ const byte = Number(value & 0x7fn);
174
+ value >>= 7n;
175
+ this.#buffer[this.#length] = value === 0n ? byte : byte | 128;
176
+ this.#length += 1;
177
+ } while (value !== 0n);
178
+ }
179
+ finish() {
180
+ return this.#buffer.slice(0, this.#length);
181
+ }
182
+ #reserve(extra) {
183
+ if (!Number.isSafeInteger(extra) || extra < 0)
184
+ throw new RangeError("Invalid binary growth");
185
+ const needed = this.#length + extra;
186
+ if (!Number.isSafeInteger(needed))
187
+ throw new RangeError("Binary payload is too large");
188
+ if (needed <= this.#buffer.byteLength)
189
+ return;
190
+ let capacity = this.#buffer.byteLength;
191
+ while (capacity < needed) {
192
+ const doubled = capacity * 2;
193
+ capacity = Number.isSafeInteger(doubled) ? doubled : needed;
248
194
  }
249
- varuint() {
250
- let value = 0n;
251
- let shift = 0n;
252
- for (let byteIndex = 0; byteIndex < 10; byteIndex += 1) {
253
- const byte = this.source[this.#offset];
254
- if (byte === undefined)
255
- throw new Error("Posting chunk is truncated");
256
- this.#offset += 1;
257
- if (byteIndex === 9 && (byte & 0xfe) !== 0) {
258
- throw new Error("Posting chunk varuint exceeds uint64");
259
- }
260
- value |= BigInt(byte & 0x7f) << shift;
261
- if ((byte & 0x80) === 0) {
262
- if (byteIndex > 0 && (byte & 0x7f) === 0) {
263
- throw new Error("Posting chunk varuint is overlong");
264
- }
265
- return value;
266
- }
267
- shift += 7n;
195
+ const grown = new Uint8Array(capacity);
196
+ grown.set(this.#buffer);
197
+ this.#buffer = grown;
198
+ }
199
+ }
200
+ class BinaryReader {
201
+ source;
202
+ #offset = 0;
203
+ constructor(source) {
204
+ this.source = source;
205
+ }
206
+ get done() {
207
+ return this.#offset === this.source.byteLength;
208
+ }
209
+ get remaining() {
210
+ return this.source.byteLength - this.#offset;
211
+ }
212
+ bytes(length) {
213
+ if (!Number.isSafeInteger(length) || length < 0 || length > this.remaining)
214
+ throw new Error("Posting chunk is truncated");
215
+ const bytes = this.source.subarray(this.#offset, this.#offset + length);
216
+ this.#offset += length;
217
+ return bytes;
218
+ }
219
+ varuint() {
220
+ let value = 0n;
221
+ let shift = 0n;
222
+ for (let byteIndex = 0; byteIndex < 10; byteIndex += 1) {
223
+ const byte = this.source[this.#offset];
224
+ if (byte === void 0)
225
+ throw new Error("Posting chunk is truncated");
226
+ this.#offset += 1;
227
+ if (byteIndex === 9 && (byte & 254) !== 0) {
228
+ throw new Error("Posting chunk varuint exceeds uint64");
229
+ }
230
+ value |= BigInt(byte & 127) << shift;
231
+ if ((byte & 128) === 0) {
232
+ if (byteIndex > 0 && (byte & 127) === 0) {
233
+ throw new Error("Posting chunk varuint is overlong");
268
234
  }
269
- throw new Error("Posting chunk varuint is too wide");
270
- }
271
- safeInteger(label) {
272
- const value = this.varuint();
273
- if (value > BigInt(Number.MAX_SAFE_INTEGER))
274
- throw new RangeError(`${label} is too large`);
275
- return Number(value);
235
+ return value;
236
+ }
237
+ shift += 7n;
276
238
  }
239
+ throw new Error("Posting chunk varuint is too wide");
240
+ }
241
+ safeInteger(label) {
242
+ const value = this.varuint();
243
+ if (value > BigInt(Number.MAX_SAFE_INTEGER))
244
+ throw new RangeError(`${label} is too large`);
245
+ return Number(value);
246
+ }
277
247
  }
278
- /**
279
- * The leader's checkpoint slots are written synchronously (no compression — that needs an
280
- * await, and the checkpoint must land in the same synchronous run that resets the WAL, or
281
- * frames appended in between would be lost by the reset).
282
- */
283
- export function encodeSyncCheckpoint(state) {
284
- return encodeEnvelope(SYNC_CHECKPOINT_MAGIC, encodeRecordJson(state));
248
+ function encodeSyncCheckpoint(state) {
249
+ return encodeEnvelope(SYNC_CHECKPOINT_MAGIC, encodeRecordJson(state));
285
250
  }
286
- export function decodeSyncCheckpoint(bytes) {
287
- const payload = decodeEnvelope(SYNC_CHECKPOINT_MAGIC, bytes);
288
- return payload === undefined ? undefined : decodeRecordJson(payload);
251
+ function decodeSyncCheckpoint(bytes) {
252
+ const payload = decodeEnvelope(SYNC_CHECKPOINT_MAGIC, bytes);
253
+ return payload === void 0 ? void 0 : decodeRecordJson(payload);
289
254
  }
255
+ export {
256
+ LOG_FORMAT_VERSION,
257
+ decodeChunk,
258
+ decodePostingChunk,
259
+ decodeRecordJson,
260
+ decodeSyncCheckpoint,
261
+ encodeChunk,
262
+ encodePostingChunk,
263
+ encodeRecordJson,
264
+ encodeSyncCheckpoint
265
+ };