distillate 0.9.0 → 0.11.0

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 (42) hide show
  1. package/README.md +44 -6
  2. package/dist/bitset-Cf2jwRJX.js +32 -0
  3. package/dist/bitset-QYGqxnEf.cjs +37 -0
  4. package/dist/blocked/index.cjs +14 -9
  5. package/dist/blocked/index.d.cts +7 -6
  6. package/dist/blocked/index.d.ts +7 -6
  7. package/dist/blocked/index.js +12 -8
  8. package/dist/bloom/index.cjs +16 -41
  9. package/dist/bloom/index.d.cts +5 -4
  10. package/dist/bloom/index.d.ts +5 -4
  11. package/dist/bloom/index.js +13 -39
  12. package/dist/bytes-DCuYtUVS.d.cts +4 -0
  13. package/dist/bytes-DCuYtUVS.d.ts +4 -0
  14. package/dist/frame/index.cjs +9 -0
  15. package/dist/frame/index.d.cts +2 -0
  16. package/dist/frame/index.d.ts +2 -0
  17. package/dist/frame/index.js +2 -0
  18. package/dist/fuse/index.cjs +26 -22
  19. package/dist/fuse/index.d.cts +7 -6
  20. package/dist/fuse/index.d.ts +7 -6
  21. package/dist/fuse/index.js +14 -11
  22. package/dist/hasher-3Cyn7Yg-.js +230 -0
  23. package/dist/hasher-MhY7aQ8F.cjs +289 -0
  24. package/dist/hll/index.cjs +12 -8
  25. package/dist/hll/index.d.cts +4 -3
  26. package/dist/hll/index.d.ts +4 -3
  27. package/dist/hll/index.js +11 -8
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.cts +2 -3
  30. package/dist/index.d.ts +2 -3
  31. package/dist/index.js +1 -1
  32. package/dist/scalable/index.cjs +340 -0
  33. package/dist/scalable/index.d.cts +173 -0
  34. package/dist/scalable/index.d.ts +173 -0
  35. package/dist/scalable/index.js +330 -0
  36. package/dist/{serialize-Bv1zWzrh.d.cts → serialize--xR6vGSW.d.cts} +44 -8
  37. package/dist/{serialize-Bv1zWzrh.d.ts → serialize--xR6vGSW.d.ts} +44 -8
  38. package/dist/serialize-CHikZ4GH.cjs +296 -0
  39. package/dist/serialize-CMSYFym-.js +201 -0
  40. package/package.json +25 -9
  41. package/dist/serialize-D59Ri81a.js +0 -392
  42. package/dist/serialize-Ul2LKFJU.cjs +0 -517
@@ -0,0 +1,330 @@
1
+ import { t as BitSet } from "../bitset-Cf2jwRJX.js";
2
+ import { i as hash128KeyInto, l as probeLanesInto, s as probeAt } from "../hasher-3Cyn7Yg-.js";
3
+ import { a as TruncatedError, c as assertBodyLength, d as bytesEqual, f as fromJSONEnvelope, g as writeFrame, h as toJSONEnvelope, i as SerializationError, l as assertMinBodyLength, m as readHeader, n as ChecksumError, o as UnknownHashVariantError, r as ReservedBitsError, s as UnknownVersionError, t as BadMagicError, u as assertParamsPadding } from "../serialize-CMSYFym-.js";
4
+ import { i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
5
+ import { t as bloomSizing } from "../sizing-BtUrtvF_.js";
6
+ //#region src/scalable/scalable.ts
7
+ const MAX_BITS = 4294967295;
8
+ const TYPE = 6;
9
+ const PARAMS_SIZE = 40;
10
+ const ENTRY_SIZE = 16;
11
+ const padded8 = (bytes) => Math.ceil(bytes / 8) * 8;
12
+ let restoring;
13
+ var ScalableParamMismatchError = class extends Error {
14
+ name = "ScalableParamMismatchError";
15
+ };
16
+ function readTable(view, stageCount) {
17
+ const entries = [];
18
+ for (let i = 0; i < stageCount; i++) {
19
+ const at = PARAMS_SIZE + ENTRY_SIZE * i;
20
+ const entry = {
21
+ m: view.getUint32(at, true),
22
+ k: view.getUint16(at + 4, true),
23
+ capacity: view.getUint32(at + 8, true),
24
+ count: view.getUint32(at + 12, true)
25
+ };
26
+ if (view.getUint16(at + 6, true) !== 0) throw new SerializationError(`scalable: stage ${String(i)} table padding is not zero`);
27
+ if (entry.m === 0 || entry.k === 0 || entry.capacity === 0) throw new SerializationError(`scalable: stage ${String(i)} declares m=${String(entry.m)}, k=${String(entry.k)}, capacity=${String(entry.capacity)}; all must be positive`);
28
+ if (entry.count > entry.capacity) throw new SerializationError(`scalable: stage ${String(i)} holds ${String(entry.count)} keys, above its capacity of ${String(entry.capacity)}`);
29
+ entries.push(entry);
30
+ }
31
+ return entries;
32
+ }
33
+ function readStages(body, entries, start) {
34
+ let at = start;
35
+ return entries.map((entry, i) => {
36
+ const length = Math.ceil(entry.m / 8);
37
+ const bits = new BitSet(entry.m);
38
+ bits.bytes.set(body.subarray(at, at + length));
39
+ for (let j = at + length; j < at + padded8(length); j++) if (body[j] !== 0) throw new SerializationError(`scalable: stage ${String(i)} bit padding is not zero`);
40
+ at += padded8(length);
41
+ return {
42
+ ...entry,
43
+ bits
44
+ };
45
+ });
46
+ }
47
+ var ScalableBloomFilter = class ScalableBloomFilter {
48
+ #n;
49
+ #epsilon;
50
+ #growth;
51
+ #tightening;
52
+ #seed;
53
+ #stages = [];
54
+ #hash = {
55
+ w0: 0,
56
+ w1: 0,
57
+ w2: 0,
58
+ w3: 0
59
+ };
60
+ #probes = new Uint32Array(0);
61
+ #newest;
62
+ static create(n, epsilon, options = {}) {
63
+ return new ScalableBloomFilter({
64
+ ...options,
65
+ n,
66
+ epsilon
67
+ });
68
+ }
69
+ static from(keys, epsilon, options = {}) {
70
+ const arr = [...keys];
71
+ const f = ScalableBloomFilter.create(Math.max(1, arr.length), epsilon, options);
72
+ for (const key of arr) f.add(key);
73
+ return f;
74
+ }
75
+ constructor({ n, epsilon, growth = 2, tightening = .85, seed = 0 }) {
76
+ assertPositiveInt(n, "n");
77
+ assertUint32(n, "n");
78
+ assertProbability(epsilon, "epsilon");
79
+ if (!Number.isFinite(growth) || growth <= 1) throw new ParamError(`growth must be a finite number greater than 1, got ${String(growth)}`);
80
+ assertProbability(tightening, "tightening");
81
+ assertUint32(seed, "seed");
82
+ this.#n = n;
83
+ this.#epsilon = epsilon;
84
+ this.#growth = growth;
85
+ this.#tightening = tightening;
86
+ this.#seed = seed;
87
+ if (restoring) {
88
+ this.#newest = this.#adopt(restoring);
89
+ return;
90
+ }
91
+ const first = this.#geometry(0);
92
+ if (first.m > MAX_BITS) throw new ParamError(`the first stage needs ${String(first.m)} bits, more than ${String(MAX_BITS)}`);
93
+ this.#newest = this.#open(first);
94
+ }
95
+ #geometry(i) {
96
+ const capacity = Math.ceil(this.#n * this.#growth ** i);
97
+ const target = this.#epsilon * (1 - this.#tightening) * this.#tightening ** i;
98
+ return {
99
+ capacity,
100
+ ...bloomSizing(capacity, target)
101
+ };
102
+ }
103
+ #open({ capacity, m, k }) {
104
+ const stage = {
105
+ bits: new BitSet(m),
106
+ m,
107
+ k,
108
+ capacity,
109
+ count: 0
110
+ };
111
+ this.#stages.push(stage);
112
+ if (k > this.#probes.length) this.#probes = new Uint32Array(k);
113
+ return stage;
114
+ }
115
+ #settingPairs(other) {
116
+ return [
117
+ [
118
+ "n",
119
+ this.#n,
120
+ other.#n
121
+ ],
122
+ [
123
+ "epsilon",
124
+ this.#epsilon,
125
+ other.#epsilon
126
+ ],
127
+ [
128
+ "growth",
129
+ this.#growth,
130
+ other.#growth
131
+ ],
132
+ [
133
+ "tightening",
134
+ this.#tightening,
135
+ other.#tightening
136
+ ],
137
+ [
138
+ "seed",
139
+ this.#seed,
140
+ other.#seed
141
+ ]
142
+ ];
143
+ }
144
+ #assertMergeable(other) {
145
+ for (const [name, a, b] of this.#settingPairs(other)) if (a !== b) throw new ScalableParamMismatchError(`cannot union scalable Bloom filters whose ${name} differs (${String(a)} vs ${String(b)})`);
146
+ this.#stages.forEach((s, i) => {
147
+ const t = other.#stages[i];
148
+ if (!t) return;
149
+ for (const field of [
150
+ "m",
151
+ "k",
152
+ "capacity"
153
+ ]) if (s[field] !== t[field]) throw new ScalableParamMismatchError(`cannot union scalable Bloom filters whose stage ${String(i)} ${field} differs (${String(s[field])} vs ${String(t[field])})`);
154
+ });
155
+ }
156
+ #adopt(stages) {
157
+ this.#stages.length = 0;
158
+ this.#stages.push(...stages);
159
+ const k = Math.max(...stages.map((s) => s.k));
160
+ if (k > this.#probes.length) this.#probes = new Uint32Array(k);
161
+ return stages.reduce((_, s) => s);
162
+ }
163
+ #probe(stage) {
164
+ probeLanesInto(this.#hash.w0, this.#hash.w1, stage.k, stage.m, this.#probes);
165
+ }
166
+ #holds(stage) {
167
+ const { w0, w1 } = this.#hash;
168
+ for (let i = 0; i < stage.k; i++) if (!stage.bits.get(probeAt(w0, w1, i, stage.m))) return false;
169
+ return true;
170
+ }
171
+ #anyHolds() {
172
+ for (let i = this.#stages.length - 1; i >= 0; i--) {
173
+ const stage = this.#stages[i];
174
+ if (stage && this.#holds(stage)) return true;
175
+ }
176
+ return false;
177
+ }
178
+ add(key) {
179
+ hash128KeyInto(key, this.#seed, this.#hash);
180
+ if (this.#anyHolds()) return;
181
+ if (this.#newest.count >= this.#newest.capacity) {
182
+ const i = this.#stages.length;
183
+ const next = this.#geometry(i);
184
+ if (next.m > MAX_BITS) throw new RangeError(`stage ${String(i)} needs ${String(next.m)} bits, more than ${String(MAX_BITS)}; the filter cannot grow further`);
185
+ this.#newest = this.#open(next);
186
+ }
187
+ const stage = this.#newest;
188
+ this.#probe(stage);
189
+ for (let i = 0; i < stage.k; i++) stage.bits.set(this.#probes[i] ?? 0);
190
+ stage.count++;
191
+ }
192
+ has(key) {
193
+ hash128KeyInto(key, this.#seed, this.#hash);
194
+ return this.#anyHolds();
195
+ }
196
+ rate() {
197
+ let miss = 1;
198
+ for (const s of this.#stages) miss *= 1 - (s.bits.count() / s.m) ** s.k;
199
+ return 1 - miss;
200
+ }
201
+ union(other) {
202
+ this.#assertMergeable(other);
203
+ const result = new ScalableBloomFilter({
204
+ n: this.#n,
205
+ epsilon: this.#epsilon,
206
+ growth: this.#growth,
207
+ tightening: this.#tightening,
208
+ seed: this.#seed
209
+ });
210
+ const [longer, shorter] = this.#stages.length >= other.#stages.length ? [this.#stages, other.#stages] : [other.#stages, this.#stages];
211
+ const stages = longer.map((stage, i) => {
212
+ const bits = new BitSet(stage.m);
213
+ bits.bytes.set(stage.bits.bytes);
214
+ const twin = shorter[i];
215
+ if (twin) {
216
+ const out = bits.bytes;
217
+ const bytes = twin.bits.bytes;
218
+ for (let j = 0; j < out.length; j++) out[j] = (out[j] ?? 0) | (bytes[j] ?? 0);
219
+ }
220
+ return {
221
+ bits,
222
+ m: stage.m,
223
+ k: stage.k,
224
+ capacity: stage.capacity,
225
+ count: Math.min(stage.capacity, stage.count + (twin?.count ?? 0))
226
+ };
227
+ });
228
+ result.#newest = result.#adopt(stages);
229
+ return result;
230
+ }
231
+ equals(other) {
232
+ if (this.#settingPairs(other).some(([, a, b]) => a !== b)) return false;
233
+ if (this.#stages.length !== other.#stages.length) return false;
234
+ return this.#stages.every((s, i) => {
235
+ const t = other.#stages[i];
236
+ if (!t) return false;
237
+ return s.m === t.m && s.k === t.k && s.capacity === t.capacity && s.count === t.count && bytesEqual(s.bits.bytes, t.bits.bytes);
238
+ });
239
+ }
240
+ static fromBytes(bytes) {
241
+ const { type, flags, body } = readHeader(bytes);
242
+ if (type !== TYPE) throw new SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
243
+ if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
244
+ assertMinBodyLength(body.length, PARAMS_SIZE, "scalable");
245
+ assertParamsPadding(body, 36, PARAMS_SIZE, "scalable");
246
+ const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
247
+ const stageCount = view.getUint32(32, true);
248
+ if (stageCount === 0) throw new SerializationError("scalable: frame declares no stages");
249
+ const tableEnd = PARAMS_SIZE + ENTRY_SIZE * stageCount;
250
+ assertMinBodyLength(body.length, tableEnd, "scalable");
251
+ const entries = readTable(view, stageCount);
252
+ const expected = entries.reduce((sum, e) => sum + padded8(Math.ceil(e.m / 8)), tableEnd);
253
+ assertBodyLength(body.length, expected, "scalable");
254
+ restoring = readStages(body, entries, tableEnd);
255
+ try {
256
+ return new ScalableBloomFilter({
257
+ n: view.getUint32(0, true),
258
+ seed: view.getUint32(4, true),
259
+ epsilon: view.getFloat64(8, true),
260
+ growth: view.getFloat64(16, true),
261
+ tightening: view.getFloat64(24, true)
262
+ });
263
+ } catch (err) {
264
+ if (err instanceof ParamError) throw new SerializationError(`scalable: ${err.message}`);
265
+ throw err;
266
+ } finally {
267
+ restoring = void 0;
268
+ }
269
+ }
270
+ toBytes() {
271
+ const payload = this.#stages.reduce((sum, s) => sum + ENTRY_SIZE + padded8(s.bits.bytes.length), 0);
272
+ return writeFrame({
273
+ version: 5,
274
+ type: TYPE,
275
+ flags: 0
276
+ }, PARAMS_SIZE, payload, (body, view) => {
277
+ view.setUint32(0, this.#n, true);
278
+ view.setUint32(4, this.#seed, true);
279
+ view.setFloat64(8, this.#epsilon, true);
280
+ view.setFloat64(16, this.#growth, true);
281
+ view.setFloat64(24, this.#tightening, true);
282
+ view.setUint32(32, this.#stages.length, true);
283
+ let at = PARAMS_SIZE + ENTRY_SIZE * this.#stages.length;
284
+ this.#stages.forEach((s, i) => {
285
+ const entry = PARAMS_SIZE + ENTRY_SIZE * i;
286
+ view.setUint32(entry, s.m, true);
287
+ view.setUint16(entry + 4, s.k, true);
288
+ view.setUint32(entry + 8, s.capacity, true);
289
+ view.setUint32(entry + 12, s.count, true);
290
+ body.set(s.bits.bytes, at);
291
+ at += padded8(s.bits.bytes.length);
292
+ });
293
+ });
294
+ }
295
+ toJSON() {
296
+ return toJSONEnvelope(this.toBytes());
297
+ }
298
+ static fromJSON(value) {
299
+ return ScalableBloomFilter.fromBytes(fromJSONEnvelope(value));
300
+ }
301
+ get count() {
302
+ return this.#stages.reduce((sum, s) => sum + s.count, 0);
303
+ }
304
+ get capacity() {
305
+ return this.#stages.reduce((sum, s) => sum + s.capacity, 0);
306
+ }
307
+ get stages() {
308
+ return this.#stages.length;
309
+ }
310
+ get m() {
311
+ return this.#stages.reduce((sum, s) => sum + s.m, 0);
312
+ }
313
+ get bitsPerKey() {
314
+ return this.m / this.capacity;
315
+ }
316
+ get seed() {
317
+ return this.#seed;
318
+ }
319
+ get epsilon() {
320
+ return this.#epsilon;
321
+ }
322
+ get growth() {
323
+ return this.#growth;
324
+ }
325
+ get tightening() {
326
+ return this.#tightening;
327
+ }
328
+ };
329
+ //#endregion
330
+ export { BadMagicError, ChecksumError, ParamError, ReservedBitsError, ScalableBloomFilter, ScalableParamMismatchError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
@@ -1,6 +1,3 @@
1
- //#region src/core/bytes.d.ts
2
- type BytesLike = string | Uint8Array | ArrayBuffer;
3
- //#endregion
4
1
  //#region src/core/serialize.d.ts
5
2
  /**
6
3
  * Base class for every defect `fromBytes` and `fromJSON` reject. Thrown
@@ -38,22 +35,36 @@ declare class BadMagicError extends SerializationError {
38
35
  }
39
36
  /**
40
37
  * Thrown when a frame or JSON envelope declares a format version this release
41
- * does not read. A reader must be at least as new as the producer, so upgrade
42
- * `distillate` or re-serialize the data with the version you run.
38
+ * does not read. The remedy depends on which side is older. A frame newer than
39
+ * this release needs `distillate` upgraded on the reading side. A frame older
40
+ * than it is readable by no current release, since each reads exactly one
41
+ * format version; rebuild it from the source keys. Being at least as new as
42
+ * the producer is necessary, not sufficient.
43
43
  */
44
44
  declare class UnknownVersionError extends SerializationError {
45
45
  /** Discriminates this error from other `Error`s. */
46
46
  override readonly name = "UnknownVersionError";
47
47
  }
48
48
  /**
49
- * Thrown when a frame's flags nibble names a hash this release cannot
50
- * reproduce, so its stored bits are unreadable. Rebuild the filter from the
49
+ * Thrown when a frame's flags nibble names a hash or index mapping this
50
+ * release cannot reproduce, so its stored bits are unreadable. Rebuild the filter from the
51
51
  * source keys with the version you run.
52
52
  */
53
53
  declare class UnknownHashVariantError extends SerializationError {
54
54
  /** Discriminates this error from other `Error`s. */
55
55
  override readonly name = "UnknownHashVariantError";
56
56
  }
57
+ /**
58
+ * Thrown when a frame sets a header bit this format reserves: the flags high
59
+ * nibble, the byte at offset 7, or the word at offset 12. Only a newer writer
60
+ * puts anything there, and reading its frame under this release's meaning
61
+ * would give a wrong answer with no error. Upgrade `distillate` on the reading
62
+ * side; a reader must be at least as new as the producer.
63
+ */
64
+ declare class ReservedBitsError extends SerializationError {
65
+ /** Discriminates this error from other `Error`s. */
66
+ override readonly name = "ReservedBitsError";
67
+ }
57
68
  /**
58
69
  * Thrown when a frame's CRC32 trailer does not match its contents, so the
59
70
  * bytes were corrupted after they were written. Discard them and re-fetch;
@@ -72,5 +83,30 @@ interface FilterJSON {
72
83
  /** Base64 of the `toBytes` frame. */
73
84
  data: string;
74
85
  }
86
+ /** One frame read in place from a larger buffer by {@link readFrameAt}. */
87
+ interface Frame {
88
+ /** Binary format version. */
89
+ version: number;
90
+ /** Structure type code; one this release does not implement still reads. */
91
+ type: number;
92
+ /** Header flags byte; the low nibble is the hash variant. */
93
+ flags: number;
94
+ /** The body, a view into the source buffer rather than a copy. */
95
+ body: Uint8Array;
96
+ /** Bytes the whole frame occupies, so `offset + byteLength` is the next one. */
97
+ byteLength: number;
98
+ }
99
+ /**
100
+ * Reads and validates the frame starting at `offset` in `bytes`, which may
101
+ * hold further frames after it: magic, version, declared length, CRC32 and
102
+ * reserved bits, without needing to know the structure type. Advance by the
103
+ * returned `byteLength` to walk a buffer of concatenated frames.
104
+ *
105
+ * @throws TruncatedError when the frame runs past the end of `bytes`.
106
+ * @throws BadMagicError, UnknownVersionError, ChecksumError or
107
+ * ReservedBitsError when the frame at `offset` is not one this release reads.
108
+ * @throws RangeError when `offset` is not a non-negative integer.
109
+ */
110
+ declare function readFrameAt(bytes: Uint8Array, offset: number): Frame;
75
111
  //#endregion
76
- export { TruncatedError as a, BytesLike as c, SerializationError as i, ChecksumError as n, UnknownHashVariantError as o, FilterJSON as r, UnknownVersionError as s, BadMagicError as t };
112
+ export { ReservedBitsError as a, UnknownHashVariantError as c, Frame as i, UnknownVersionError as l, ChecksumError as n, SerializationError as o, FilterJSON as r, TruncatedError as s, BadMagicError as t, readFrameAt as u };
@@ -1,6 +1,3 @@
1
- //#region src/core/bytes.d.ts
2
- type BytesLike = string | Uint8Array | ArrayBuffer;
3
- //#endregion
4
1
  //#region src/core/serialize.d.ts
5
2
  /**
6
3
  * Base class for every defect `fromBytes` and `fromJSON` reject. Thrown
@@ -38,22 +35,36 @@ declare class BadMagicError extends SerializationError {
38
35
  }
39
36
  /**
40
37
  * Thrown when a frame or JSON envelope declares a format version this release
41
- * does not read. A reader must be at least as new as the producer, so upgrade
42
- * `distillate` or re-serialize the data with the version you run.
38
+ * does not read. The remedy depends on which side is older. A frame newer than
39
+ * this release needs `distillate` upgraded on the reading side. A frame older
40
+ * than it is readable by no current release, since each reads exactly one
41
+ * format version; rebuild it from the source keys. Being at least as new as
42
+ * the producer is necessary, not sufficient.
43
43
  */
44
44
  declare class UnknownVersionError extends SerializationError {
45
45
  /** Discriminates this error from other `Error`s. */
46
46
  override readonly name = "UnknownVersionError";
47
47
  }
48
48
  /**
49
- * Thrown when a frame's flags nibble names a hash this release cannot
50
- * reproduce, so its stored bits are unreadable. Rebuild the filter from the
49
+ * Thrown when a frame's flags nibble names a hash or index mapping this
50
+ * release cannot reproduce, so its stored bits are unreadable. Rebuild the filter from the
51
51
  * source keys with the version you run.
52
52
  */
53
53
  declare class UnknownHashVariantError extends SerializationError {
54
54
  /** Discriminates this error from other `Error`s. */
55
55
  override readonly name = "UnknownHashVariantError";
56
56
  }
57
+ /**
58
+ * Thrown when a frame sets a header bit this format reserves: the flags high
59
+ * nibble, the byte at offset 7, or the word at offset 12. Only a newer writer
60
+ * puts anything there, and reading its frame under this release's meaning
61
+ * would give a wrong answer with no error. Upgrade `distillate` on the reading
62
+ * side; a reader must be at least as new as the producer.
63
+ */
64
+ declare class ReservedBitsError extends SerializationError {
65
+ /** Discriminates this error from other `Error`s. */
66
+ override readonly name = "ReservedBitsError";
67
+ }
57
68
  /**
58
69
  * Thrown when a frame's CRC32 trailer does not match its contents, so the
59
70
  * bytes were corrupted after they were written. Discard them and re-fetch;
@@ -72,5 +83,30 @@ interface FilterJSON {
72
83
  /** Base64 of the `toBytes` frame. */
73
84
  data: string;
74
85
  }
86
+ /** One frame read in place from a larger buffer by {@link readFrameAt}. */
87
+ interface Frame {
88
+ /** Binary format version. */
89
+ version: number;
90
+ /** Structure type code; one this release does not implement still reads. */
91
+ type: number;
92
+ /** Header flags byte; the low nibble is the hash variant. */
93
+ flags: number;
94
+ /** The body, a view into the source buffer rather than a copy. */
95
+ body: Uint8Array;
96
+ /** Bytes the whole frame occupies, so `offset + byteLength` is the next one. */
97
+ byteLength: number;
98
+ }
99
+ /**
100
+ * Reads and validates the frame starting at `offset` in `bytes`, which may
101
+ * hold further frames after it: magic, version, declared length, CRC32 and
102
+ * reserved bits, without needing to know the structure type. Advance by the
103
+ * returned `byteLength` to walk a buffer of concatenated frames.
104
+ *
105
+ * @throws TruncatedError when the frame runs past the end of `bytes`.
106
+ * @throws BadMagicError, UnknownVersionError, ChecksumError or
107
+ * ReservedBitsError when the frame at `offset` is not one this release reads.
108
+ * @throws RangeError when `offset` is not a non-negative integer.
109
+ */
110
+ declare function readFrameAt(bytes: Uint8Array, offset: number): Frame;
75
111
  //#endregion
76
- export { TruncatedError as a, BytesLike as c, SerializationError as i, ChecksumError as n, UnknownHashVariantError as o, FilterJSON as r, UnknownVersionError as s, BadMagicError as t };
112
+ export { ReservedBitsError as a, UnknownHashVariantError as c, Frame as i, UnknownVersionError as l, ChecksumError as n, SerializationError as o, FilterJSON as r, TruncatedError as s, BadMagicError as t, readFrameAt as u };