@minnowdb/core 0.6.9 → 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 (103) 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.d.ts +5 -0
  10. package/dist/date-value.js +71 -41
  11. package/dist/engine/artifact-cache.js +106 -116
  12. package/dist/engine/batch.js +36 -44
  13. package/dist/engine/buffered-writer.js +156 -158
  14. package/dist/engine/byte-estimates.js +20 -22
  15. package/dist/engine/cache-limits.js +24 -25
  16. package/dist/engine/cancellation.js +5 -3
  17. package/dist/engine/catalog.js +64 -73
  18. package/dist/engine/client.js +715 -780
  19. package/dist/engine/database.js +15952 -18465
  20. package/dist/engine/defaults.js +104 -111
  21. package/dist/engine/errors.js +113 -128
  22. package/dist/engine/fts.js +233 -280
  23. package/dist/engine/group-index.js +287 -321
  24. package/dist/engine/index.js +23 -12
  25. package/dist/engine/join-index.js +177 -186
  26. package/dist/engine/keyed-live.js +168 -173
  27. package/dist/engine/live-api.js +0 -1
  28. package/dist/engine/live-equal.js +32 -37
  29. package/dist/engine/live.js +603 -647
  30. package/dist/engine/memory.js +109 -111
  31. package/dist/engine/optimizer.d.ts +19 -1
  32. package/dist/engine/optimizer.js +2642 -2515
  33. package/dist/engine/point-read.d.ts +4 -2
  34. package/dist/engine/point-read.js +121 -171
  35. package/dist/engine/query-api.js +12 -3
  36. package/dist/engine/query-cache.js +61 -69
  37. package/dist/engine/query.d.ts +45 -0
  38. package/dist/engine/query.js +8506 -9375
  39. package/dist/engine/result-wire.js +221 -252
  40. package/dist/engine/schema-wire.js +97 -112
  41. package/dist/engine/schema.js +1099 -1254
  42. package/dist/engine/sort-keys.js +315 -389
  43. package/dist/engine/sql-domains.d.ts +7 -1
  44. package/dist/engine/sql-domains.js +648 -734
  45. package/dist/engine/sql-driver.js +0 -1
  46. package/dist/engine/sql-functions.d.ts +11 -0
  47. package/dist/engine/sql-functions.js +1092 -0
  48. package/dist/engine/sql-json.js +199 -218
  49. package/dist/engine/sql-semantics.d.ts +25 -4
  50. package/dist/engine/sql-semantics.js +579 -513
  51. package/dist/engine/typed-live.js +271 -293
  52. package/dist/engine/vector.d.ts +7 -1
  53. package/dist/engine/vector.js +4513 -4580
  54. package/dist/engine/worker-host.d.ts +8 -63
  55. package/dist/engine/worker-host.js +28 -1130
  56. package/dist/engine/worker-indexeddb.d.ts +1 -0
  57. package/dist/engine/worker-indexeddb.js +3 -0
  58. package/dist/engine/worker-memory.d.ts +1 -0
  59. package/dist/engine/worker-memory.js +3 -0
  60. package/dist/engine/worker-opfs.d.ts +1 -0
  61. package/dist/engine/worker-opfs.js +3 -0
  62. package/dist/engine/worker-server.d.ts +93 -0
  63. package/dist/engine/worker-server.js +999 -0
  64. package/dist/engine/worker-store-indexeddb.d.ts +2 -0
  65. package/dist/engine/worker-store-indexeddb.js +11 -0
  66. package/dist/engine/worker-store-memory.d.ts +2 -0
  67. package/dist/engine/worker-store-memory.js +6 -0
  68. package/dist/engine/worker-store-opfs.d.ts +2 -0
  69. package/dist/engine/worker-store-opfs.js +9 -0
  70. package/dist/engine/worker.js +0 -12
  71. package/dist/engine/write-block-planner.js +93 -100
  72. package/dist/plan/index.js +15 -16
  73. package/dist/plan/model.d.ts +9 -1
  74. package/dist/plan/model.js +20 -27
  75. package/dist/storage/index.js +0 -12
  76. package/dist/storage/indexeddb.js +11776 -13171
  77. package/dist/storage/memory.js +1198 -1241
  78. package/dist/storage/opfs/files.js +238 -295
  79. package/dist/storage/opfs/index.js +9 -3
  80. package/dist/storage/opfs/leader.js +4386 -4856
  81. package/dist/storage/opfs/rpc.js +223 -259
  82. package/dist/storage/opfs/snapshot-ledger.js +219 -260
  83. package/dist/storage/opfs/store.js +884 -1003
  84. package/dist/storage/persistence.js +59 -74
  85. package/dist/storage/snapshot-stream.js +788 -826
  86. package/dist/storage/snapshot.js +36 -37
  87. package/dist/storage/toolkit/extents.js +444 -520
  88. package/dist/storage/toolkit/index.js +28 -29
  89. package/dist/storage/toolkit/record-core.js +5792 -6377
  90. package/dist/storage/toolkit/sync-file.js +34 -42
  91. package/dist/storage/toolkit/wal.js +92 -127
  92. package/dist/storage/toolkit/wire.js +232 -256
  93. package/dist/storage/types.js +2952 -3228
  94. package/dist/testing/block-store-conformance.js +1556 -1629
  95. package/dist/testing/index.js +310 -291
  96. package/dist/testing/opfs-shim.js +280 -312
  97. package/dist/testing/simulator.js +496 -534
  98. package/dist/testing/sqllogictest.js +425 -365
  99. package/dist/transactions/index.js +1464 -1712
  100. package/dist/worker-protocol/index.js +70 -69
  101. package/package.json +20 -2
  102. package/postgres-feature-profile.json +15 -5
  103. package/sql-feature-matrix.json +252 -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
+ };