@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,281 +1,240 @@
1
1
  import { crc32 } from "../../block-format/checksum.js";
2
2
  import { MAX_STORED_BLOCK_BYTE_LENGTH } from "../../block-format/index.js";
3
- import { MAX_SNAPSHOT_FRAME_ITEMS, MAX_SNAPSHOT_METADATA_FRAME_BYTES, SNAPSHOT_FRAME_KINDS, validateStorageId, } from "../types.js";
3
+ import { MAX_SNAPSHOT_FRAME_ITEMS, MAX_SNAPSHOT_METADATA_FRAME_BYTES, SNAPSHOT_FRAME_KINDS, validateStorageId } from "../types.js";
4
4
  import { assertValidPlacement } from "../toolkit/extents.js";
5
5
  import { readFully, writeFully } from "../toolkit/sync-file.js";
6
6
  import { encodeSegment } from "./files.js";
7
- const LEDGER_MAGIC = 0x534c_4731; // SLG1
7
+ const LEDGER_MAGIC = 1397507889;
8
8
  const LEDGER_PREFIX_BYTES = 80;
9
9
  const MODE_INLINE = 0;
10
10
  const MODE_PLACEMENT = 1;
11
- export function snapshotLedgerPath(kind, id) {
12
- validateStorageId(id);
13
- return ["snapshots-v1", kind, encodeSegment(id)];
11
+ function snapshotLedgerPath(kind, id) {
12
+ validateStorageId(id);
13
+ return ["snapshots-v1", kind, encodeSegment(id)];
14
14
  }
15
- /**
16
- * Checksummed append-only control file for one snapshot session. A checkpoint stores only the
17
- * committed prefix length. Bytes beyond it are unpublished and truncated on recovery, so a
18
- * large import/export never creates a database-sized WAL/checkpoint collection.
19
- */
20
- export class SnapshotFrameLedger {
21
- #handle;
22
- #length;
23
- #closed = false;
24
- constructor(handle, length) {
25
- this.#handle = handle;
26
- this.#length = length;
27
- }
28
- static async open(tree, kind, id, committedLength, create) {
29
- if (!Number.isSafeInteger(committedLength) || committedLength < 0) {
30
- throw new RangeError("Snapshot ledger length is invalid");
31
- }
32
- const handle = await tree.openHandle(snapshotLedgerPath(kind, id), { create });
33
- const size = handle.getSize();
34
- if (size < committedLength) {
35
- handle.close();
36
- throw new Error(`Snapshot ledger is shorter than its committed prefix: ${String(size)} < ` +
37
- String(committedLength));
38
- }
39
- // A checkpoint's committed prefix may have a valid WAL-covered suffix after it. Keep the
40
- // physical suffix until recovery either adopts each frame or truncates it after replay.
41
- return new SnapshotFrameLedger(handle, committedLength);
42
- }
43
- get byteLength() {
44
- return this.#length;
45
- }
46
- append(frame, placement) {
47
- this.#assertOpen();
48
- const encoded = encodeLedgerRecord(frame, placement);
49
- const offset = this.#length;
50
- const nextOffset = safeLedgerSum(offset, encoded.byteLength, "Snapshot ledger length");
51
- writeFully(this.#handle, encoded, offset, "appending snapshot frame ledger");
52
- this.#length = nextOffset;
53
- return { record: ledgerRecordFromFrame(frame, placement), offset, nextOffset };
54
- }
55
- read(offset, committedLength = this.#length) {
56
- this.#assertOpen();
57
- if (!Number.isSafeInteger(offset) ||
58
- offset < 0 ||
59
- !Number.isSafeInteger(committedLength) ||
60
- committedLength < offset ||
61
- committedLength > this.#length ||
62
- committedLength - offset < LEDGER_PREFIX_BYTES) {
63
- throw new RangeError("Snapshot ledger read offset is invalid");
64
- }
65
- const prefix = new Uint8Array(LEDGER_PREFIX_BYTES);
66
- readFully(this.#handle, prefix, offset, "reading snapshot frame ledger prefix");
67
- const view = new DataView(prefix.buffer);
68
- if (view.getUint32(0, true) !== LEDGER_MAGIC) {
69
- throw new Error("Snapshot ledger record marker mismatch");
70
- }
71
- const recordLength = view.getUint32(4, true);
72
- if (recordLength < LEDGER_PREFIX_BYTES ||
73
- recordLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES + LEDGER_PREFIX_BYTES + 3_072 ||
74
- recordLength > committedLength - offset) {
75
- throw new Error("Snapshot ledger record length is invalid");
76
- }
77
- const encoded = new Uint8Array(recordLength);
78
- readFully(this.#handle, encoded, offset, "reading snapshot frame ledger record");
79
- const expectedRecordChecksum = new DataView(encoded.buffer).getUint32(76, true);
80
- new DataView(encoded.buffer).setUint32(76, 0, true);
81
- if (crc32(encoded) !== expectedRecordChecksum) {
82
- throw new Error("Snapshot ledger record checksum mismatch");
83
- }
84
- new DataView(encoded.buffer).setUint32(76, expectedRecordChecksum, true);
85
- const record = decodeLedgerRecord(encoded);
86
- return {
87
- record,
88
- offset,
89
- nextOffset: safeLedgerSum(offset, recordLength, "Snapshot ledger read offset"),
90
- };
91
- }
92
- *records(committedLength = this.#length) {
93
- let offset = 0;
94
- while (offset < committedLength) {
95
- const read = this.read(offset, committedLength);
96
- yield read;
97
- offset = read.nextOffset;
98
- }
99
- if (offset !== committedLength)
100
- throw new Error("Snapshot ledger prefix ends mid-record");
101
- }
102
- flush() {
103
- this.#assertOpen();
104
- this.#handle.flush();
105
- }
106
- truncate(length) {
107
- this.#assertOpen();
108
- if (!Number.isSafeInteger(length) || length < 0 || length > this.#length) {
109
- throw new RangeError("Snapshot ledger truncation is invalid");
110
- }
111
- this.#handle.truncate(length);
112
- this.#length = length;
113
- }
114
- /** Recovery adopts the next WAL-published prefix without copying or re-appending its bytes. */
115
- adoptLength(length) {
116
- this.#assertOpen();
117
- if (!Number.isSafeInteger(length) || length < this.#length || length > this.#handle.getSize()) {
118
- throw new RangeError("Snapshot ledger recovery length is invalid");
119
- }
120
- this.#length = length;
121
- }
122
- close() {
123
- if (this.#closed)
124
- return;
125
- this.#closed = true;
126
- this.#handle.close();
127
- }
128
- #assertOpen() {
129
- if (this.#closed)
130
- throw new Error("Snapshot ledger is closed");
15
+ class SnapshotFrameLedger {
16
+ #handle;
17
+ #length;
18
+ #closed = false;
19
+ constructor(handle, length) {
20
+ this.#handle = handle;
21
+ this.#length = length;
22
+ }
23
+ static async open(tree, kind, id, committedLength, create) {
24
+ if (!Number.isSafeInteger(committedLength) || committedLength < 0) {
25
+ throw new RangeError("Snapshot ledger length is invalid");
26
+ }
27
+ const handle = await tree.openHandle(snapshotLedgerPath(kind, id), { create });
28
+ const size = handle.getSize();
29
+ if (size < committedLength) {
30
+ handle.close();
31
+ throw new Error(`Snapshot ledger is shorter than its committed prefix: ${String(size)} < ` + String(committedLength));
32
+ }
33
+ return new SnapshotFrameLedger(handle, committedLength);
34
+ }
35
+ get byteLength() {
36
+ return this.#length;
37
+ }
38
+ append(frame, placement) {
39
+ this.#assertOpen();
40
+ const encoded = encodeLedgerRecord(frame, placement);
41
+ const offset = this.#length;
42
+ const nextOffset = safeLedgerSum(offset, encoded.byteLength, "Snapshot ledger length");
43
+ writeFully(this.#handle, encoded, offset, "appending snapshot frame ledger");
44
+ this.#length = nextOffset;
45
+ return { record: ledgerRecordFromFrame(frame, placement), offset, nextOffset };
46
+ }
47
+ read(offset, committedLength = this.#length) {
48
+ this.#assertOpen();
49
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(committedLength) || committedLength < offset || committedLength > this.#length || committedLength - offset < LEDGER_PREFIX_BYTES) {
50
+ throw new RangeError("Snapshot ledger read offset is invalid");
51
+ }
52
+ const prefix = new Uint8Array(LEDGER_PREFIX_BYTES);
53
+ readFully(this.#handle, prefix, offset, "reading snapshot frame ledger prefix");
54
+ const view = new DataView(prefix.buffer);
55
+ if (view.getUint32(0, true) !== LEDGER_MAGIC) {
56
+ throw new Error("Snapshot ledger record marker mismatch");
57
+ }
58
+ const recordLength = view.getUint32(4, true);
59
+ if (recordLength < LEDGER_PREFIX_BYTES || recordLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES + LEDGER_PREFIX_BYTES + 3072 || recordLength > committedLength - offset) {
60
+ throw new Error("Snapshot ledger record length is invalid");
131
61
  }
62
+ const encoded = new Uint8Array(recordLength);
63
+ readFully(this.#handle, encoded, offset, "reading snapshot frame ledger record");
64
+ const expectedRecordChecksum = new DataView(encoded.buffer).getUint32(76, true);
65
+ new DataView(encoded.buffer).setUint32(76, 0, true);
66
+ if (crc32(encoded) !== expectedRecordChecksum) {
67
+ throw new Error("Snapshot ledger record checksum mismatch");
68
+ }
69
+ new DataView(encoded.buffer).setUint32(76, expectedRecordChecksum, true);
70
+ const record = decodeLedgerRecord(encoded);
71
+ return {
72
+ record,
73
+ offset,
74
+ nextOffset: safeLedgerSum(offset, recordLength, "Snapshot ledger read offset")
75
+ };
76
+ }
77
+ *records(committedLength = this.#length) {
78
+ let offset = 0;
79
+ while (offset < committedLength) {
80
+ const read = this.read(offset, committedLength);
81
+ yield read;
82
+ offset = read.nextOffset;
83
+ }
84
+ if (offset !== committedLength)
85
+ throw new Error("Snapshot ledger prefix ends mid-record");
86
+ }
87
+ flush() {
88
+ this.#assertOpen();
89
+ this.#handle.flush();
90
+ }
91
+ truncate(length) {
92
+ this.#assertOpen();
93
+ if (!Number.isSafeInteger(length) || length < 0 || length > this.#length) {
94
+ throw new RangeError("Snapshot ledger truncation is invalid");
95
+ }
96
+ this.#handle.truncate(length);
97
+ this.#length = length;
98
+ }
99
+ adoptLength(length) {
100
+ this.#assertOpen();
101
+ if (!Number.isSafeInteger(length) || length < this.#length || length > this.#handle.getSize()) {
102
+ throw new RangeError("Snapshot ledger recovery length is invalid");
103
+ }
104
+ this.#length = length;
105
+ }
106
+ close() {
107
+ if (this.#closed)
108
+ return;
109
+ this.#closed = true;
110
+ this.#handle.close();
111
+ }
112
+ #assertOpen() {
113
+ if (this.#closed)
114
+ throw new Error("Snapshot ledger is closed");
115
+ }
132
116
  }
133
117
  function encodeLedgerRecord(frame, placement) {
134
- validateFrameScalars(frame);
135
- const isBlock = frame.kind === "block";
136
- if (isBlock !== (placement !== undefined)) {
137
- throw new TypeError("Snapshot ledger block placement is inconsistent");
138
- }
139
- if (placement !== undefined) {
140
- assertValidPlacement(placement);
141
- if (placement.length !== frame.payload.byteLength || placement.checksum !== frame.checksum) {
142
- throw new Error("Snapshot block placement disagrees with its frame");
143
- }
144
- }
145
- const keyBytes = frame.key === null ? new Uint8Array() : new TextEncoder().encode(frame.key);
146
- const inlineLength = placement === undefined ? frame.payload.byteLength : 0;
147
- const recordLength = safeLedgerSum(LEDGER_PREFIX_BYTES + keyBytes.byteLength, inlineLength, "Snapshot ledger record length");
148
- const encoded = new Uint8Array(recordLength);
149
- const view = new DataView(encoded.buffer);
150
- view.setUint32(0, LEDGER_MAGIC, true);
151
- view.setUint32(4, recordLength, true);
152
- view.setBigUint64(8, BigInt(frame.sequence), true);
153
- view.setUint32(16, SNAPSHOT_FRAME_KINDS.indexOf(frame.kind), true);
154
- view.setUint32(20, frame.itemCount, true);
155
- view.setUint32(24, keyBytes.byteLength, true);
156
- view.setUint32(28, placement === undefined ? MODE_INLINE : MODE_PLACEMENT, true);
157
- view.setBigUint64(32, BigInt(frame.payload.byteLength), true);
158
- view.setUint32(40, frame.checksum, true);
159
- if (placement !== undefined) {
160
- view.setBigUint64(44, BigInt(placement.extent), true);
161
- view.setBigUint64(52, BigInt(placement.offset), true);
162
- view.setBigUint64(60, BigInt(placement.length), true);
163
- view.setUint32(68, placement.checksum, true);
164
- }
165
- // 72..75 are reserved zero; 76..79 is the record checksum itself.
166
- encoded.set(keyBytes, LEDGER_PREFIX_BYTES);
167
- if (placement === undefined)
168
- encoded.set(frame.payload, LEDGER_PREFIX_BYTES + keyBytes.byteLength);
169
- view.setUint32(76, 0, true);
170
- view.setUint32(76, crc32(encoded), true);
171
- return encoded;
118
+ validateFrameScalars(frame);
119
+ const isBlock = frame.kind === "block";
120
+ if (isBlock !== (placement !== void 0)) {
121
+ throw new TypeError("Snapshot ledger block placement is inconsistent");
122
+ }
123
+ if (placement !== void 0) {
124
+ assertValidPlacement(placement);
125
+ if (placement.length !== frame.payload.byteLength || placement.checksum !== frame.checksum) {
126
+ throw new Error("Snapshot block placement disagrees with its frame");
127
+ }
128
+ }
129
+ const keyBytes = frame.key === null ? new Uint8Array() : new TextEncoder().encode(frame.key);
130
+ const inlineLength = placement === void 0 ? frame.payload.byteLength : 0;
131
+ const recordLength = safeLedgerSum(LEDGER_PREFIX_BYTES + keyBytes.byteLength, inlineLength, "Snapshot ledger record length");
132
+ const encoded = new Uint8Array(recordLength);
133
+ const view = new DataView(encoded.buffer);
134
+ view.setUint32(0, LEDGER_MAGIC, true);
135
+ view.setUint32(4, recordLength, true);
136
+ view.setBigUint64(8, BigInt(frame.sequence), true);
137
+ view.setUint32(16, SNAPSHOT_FRAME_KINDS.indexOf(frame.kind), true);
138
+ view.setUint32(20, frame.itemCount, true);
139
+ view.setUint32(24, keyBytes.byteLength, true);
140
+ view.setUint32(28, placement === void 0 ? MODE_INLINE : MODE_PLACEMENT, true);
141
+ view.setBigUint64(32, BigInt(frame.payload.byteLength), true);
142
+ view.setUint32(40, frame.checksum, true);
143
+ if (placement !== void 0) {
144
+ view.setBigUint64(44, BigInt(placement.extent), true);
145
+ view.setBigUint64(52, BigInt(placement.offset), true);
146
+ view.setBigUint64(60, BigInt(placement.length), true);
147
+ view.setUint32(68, placement.checksum, true);
148
+ }
149
+ encoded.set(keyBytes, LEDGER_PREFIX_BYTES);
150
+ if (placement === void 0)
151
+ encoded.set(frame.payload, LEDGER_PREFIX_BYTES + keyBytes.byteLength);
152
+ view.setUint32(76, 0, true);
153
+ view.setUint32(76, crc32(encoded), true);
154
+ return encoded;
172
155
  }
173
156
  function decodeLedgerRecord(encoded) {
174
- const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
175
- const sequence = Number(view.getBigUint64(8, true));
176
- const kind = SNAPSHOT_FRAME_KINDS[view.getUint32(16, true)];
177
- const itemCount = view.getUint32(20, true);
178
- const keyLength = view.getUint32(24, true);
179
- const mode = view.getUint32(28, true);
180
- const payloadLength = Number(view.getBigUint64(32, true));
181
- const checksum = view.getUint32(40, true);
182
- if (kind === undefined ||
183
- !Number.isSafeInteger(sequence) ||
184
- sequence < 0 ||
185
- itemCount < 1 ||
186
- itemCount > MAX_SNAPSHOT_FRAME_ITEMS ||
187
- !Number.isSafeInteger(payloadLength) ||
188
- payloadLength < 1 ||
189
- (kind === "block"
190
- ? payloadLength > MAX_STORED_BLOCK_BYTE_LENGTH
191
- : payloadLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES) ||
192
- (mode !== MODE_INLINE && mode !== MODE_PLACEMENT) ||
193
- encoded.subarray(72, 76).some((byte) => byte !== 0)) {
194
- throw new TypeError("Snapshot ledger record header is invalid");
195
- }
196
- const inlineLength = mode === MODE_INLINE ? payloadLength : 0;
197
- if (LEDGER_PREFIX_BYTES + keyLength + inlineLength !== encoded.byteLength) {
198
- throw new Error("Snapshot ledger record body length is invalid");
199
- }
200
- let key = null;
201
- if (keyLength > 0) {
202
- try {
203
- key = validateStorageId(new TextDecoder("utf-8", { fatal: true }).decode(encoded.subarray(LEDGER_PREFIX_BYTES, LEDGER_PREFIX_BYTES + keyLength)));
204
- }
205
- catch {
206
- throw new TypeError("Snapshot ledger key is not canonical UTF-8");
207
- }
208
- }
209
- if ((kind === "block") !== (key !== null) || (kind === "block") !== (mode === MODE_PLACEMENT)) {
210
- throw new TypeError("Snapshot ledger frame identity is inconsistent");
211
- }
212
- if (mode === MODE_INLINE) {
213
- const payload = encoded.slice(LEDGER_PREFIX_BYTES + keyLength);
214
- if (crc32(payload) !== checksum)
215
- throw new Error("Snapshot ledger payload checksum mismatch");
216
- return { sequence, kind, itemCount, key, payloadLength, checksum, payload };
217
- }
218
- const placement = {
219
- extent: Number(view.getBigUint64(44, true)),
220
- offset: Number(view.getBigUint64(52, true)),
221
- length: Number(view.getBigUint64(60, true)),
222
- checksum: view.getUint32(68, true),
223
- };
224
- assertValidPlacement(placement);
225
- if (placement.length !== payloadLength || placement.checksum !== checksum) {
226
- throw new Error("Snapshot ledger placement disagrees with its frame");
227
- }
228
- return { sequence, kind, itemCount, key, payloadLength, checksum, placement };
157
+ const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
158
+ const sequence = Number(view.getBigUint64(8, true));
159
+ const kind = SNAPSHOT_FRAME_KINDS[view.getUint32(16, true)];
160
+ const itemCount = view.getUint32(20, true);
161
+ const keyLength = view.getUint32(24, true);
162
+ const mode = view.getUint32(28, true);
163
+ const payloadLength = Number(view.getBigUint64(32, true));
164
+ const checksum = view.getUint32(40, true);
165
+ if (kind === void 0 || !Number.isSafeInteger(sequence) || sequence < 0 || itemCount < 1 || itemCount > MAX_SNAPSHOT_FRAME_ITEMS || !Number.isSafeInteger(payloadLength) || payloadLength < 1 || (kind === "block" ? payloadLength > MAX_STORED_BLOCK_BYTE_LENGTH : payloadLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES) || mode !== MODE_INLINE && mode !== MODE_PLACEMENT || encoded.subarray(72, 76).some((byte) => byte !== 0)) {
166
+ throw new TypeError("Snapshot ledger record header is invalid");
167
+ }
168
+ const inlineLength = mode === MODE_INLINE ? payloadLength : 0;
169
+ if (LEDGER_PREFIX_BYTES + keyLength + inlineLength !== encoded.byteLength) {
170
+ throw new Error("Snapshot ledger record body length is invalid");
171
+ }
172
+ let key = null;
173
+ if (keyLength > 0) {
174
+ try {
175
+ key = validateStorageId(new TextDecoder("utf-8", { fatal: true }).decode(encoded.subarray(LEDGER_PREFIX_BYTES, LEDGER_PREFIX_BYTES + keyLength)));
176
+ } catch {
177
+ throw new TypeError("Snapshot ledger key is not canonical UTF-8");
178
+ }
179
+ }
180
+ if (kind === "block" !== (key !== null) || kind === "block" !== (mode === MODE_PLACEMENT)) {
181
+ throw new TypeError("Snapshot ledger frame identity is inconsistent");
182
+ }
183
+ if (mode === MODE_INLINE) {
184
+ const payload = encoded.slice(LEDGER_PREFIX_BYTES + keyLength);
185
+ if (crc32(payload) !== checksum)
186
+ throw new Error("Snapshot ledger payload checksum mismatch");
187
+ return { sequence, kind, itemCount, key, payloadLength, checksum, payload };
188
+ }
189
+ const placement = {
190
+ extent: Number(view.getBigUint64(44, true)),
191
+ offset: Number(view.getBigUint64(52, true)),
192
+ length: Number(view.getBigUint64(60, true)),
193
+ checksum: view.getUint32(68, true)
194
+ };
195
+ assertValidPlacement(placement);
196
+ if (placement.length !== payloadLength || placement.checksum !== checksum) {
197
+ throw new Error("Snapshot ledger placement disagrees with its frame");
198
+ }
199
+ return { sequence, kind, itemCount, key, payloadLength, checksum, placement };
229
200
  }
230
201
  function ledgerRecordFromFrame(frame, placement) {
231
- return {
232
- sequence: frame.sequence,
233
- kind: frame.kind,
234
- itemCount: frame.itemCount,
235
- key: frame.key,
236
- payloadLength: frame.payload.byteLength,
237
- checksum: frame.checksum,
238
- ...(placement === undefined ? { payload: frame.payload } : { placement }),
239
- };
202
+ return {
203
+ sequence: frame.sequence,
204
+ kind: frame.kind,
205
+ itemCount: frame.itemCount,
206
+ key: frame.key,
207
+ payloadLength: frame.payload.byteLength,
208
+ checksum: frame.checksum,
209
+ ...placement === void 0 ? { payload: frame.payload } : { placement }
210
+ };
240
211
  }
241
212
  function validateFrameScalars(frame) {
242
- if (!Number.isSafeInteger(frame.sequence) ||
243
- frame.sequence < 0 ||
244
- !SNAPSHOT_FRAME_KINDS.includes(frame.kind) ||
245
- !Number.isSafeInteger(frame.itemCount) ||
246
- frame.itemCount < 1 ||
247
- frame.itemCount > MAX_SNAPSHOT_FRAME_ITEMS ||
248
- !(frame.payload instanceof Uint8Array) ||
249
- !Number.isSafeInteger(frame.checksum) ||
250
- frame.checksum < 0 ||
251
- frame.checksum > 0xffff_ffff) {
252
- throw new TypeError("Snapshot frame is invalid");
253
- }
254
- if (crc32(frame.payload) !== frame.checksum) {
255
- throw new Error("Snapshot frame checksum mismatch");
256
- }
257
- if (frame.kind === "block") {
258
- if (frame.key === null || frame.itemCount !== 1) {
259
- throw new TypeError("Snapshot block frame identity is invalid");
260
- }
261
- validateStorageId(frame.key);
262
- if (frame.payload.byteLength < 1 || frame.payload.byteLength > MAX_STORED_BLOCK_BYTE_LENGTH) {
263
- throw new RangeError("Snapshot block frame is too large");
264
- }
265
- }
266
- else if (frame.key !== null ||
267
- frame.payload.byteLength < 1 ||
268
- frame.payload.byteLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES) {
269
- throw new TypeError("Snapshot metadata frame identity is invalid");
270
- }
213
+ if (!Number.isSafeInteger(frame.sequence) || frame.sequence < 0 || !SNAPSHOT_FRAME_KINDS.includes(frame.kind) || !Number.isSafeInteger(frame.itemCount) || frame.itemCount < 1 || frame.itemCount > MAX_SNAPSHOT_FRAME_ITEMS || !(frame.payload instanceof Uint8Array) || !Number.isSafeInteger(frame.checksum) || frame.checksum < 0 || frame.checksum > 4294967295) {
214
+ throw new TypeError("Snapshot frame is invalid");
215
+ }
216
+ if (crc32(frame.payload) !== frame.checksum) {
217
+ throw new Error("Snapshot frame checksum mismatch");
218
+ }
219
+ if (frame.kind === "block") {
220
+ if (frame.key === null || frame.itemCount !== 1) {
221
+ throw new TypeError("Snapshot block frame identity is invalid");
222
+ }
223
+ validateStorageId(frame.key);
224
+ if (frame.payload.byteLength < 1 || frame.payload.byteLength > MAX_STORED_BLOCK_BYTE_LENGTH) {
225
+ throw new RangeError("Snapshot block frame is too large");
226
+ }
227
+ } else if (frame.key !== null || frame.payload.byteLength < 1 || frame.payload.byteLength > MAX_SNAPSHOT_METADATA_FRAME_BYTES) {
228
+ throw new TypeError("Snapshot metadata frame identity is invalid");
229
+ }
271
230
  }
272
231
  function safeLedgerSum(left, right, label) {
273
- if (!Number.isSafeInteger(left) ||
274
- left < 0 ||
275
- !Number.isSafeInteger(right) ||
276
- right < 0 ||
277
- left > Number.MAX_SAFE_INTEGER - right) {
278
- throw new RangeError(`${label} exceeds the safe integer range`);
279
- }
280
- return left + right;
232
+ if (!Number.isSafeInteger(left) || left < 0 || !Number.isSafeInteger(right) || right < 0 || left > Number.MAX_SAFE_INTEGER - right) {
233
+ throw new RangeError(`${label} exceeds the safe integer range`);
234
+ }
235
+ return left + right;
281
236
  }
237
+ export {
238
+ SnapshotFrameLedger,
239
+ snapshotLedgerPath
240
+ };