distillate 0.5.0 → 0.6.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.
@@ -1,18 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-fa-pEUGq.cjs");
3
- const require_params = require("../params-J8p3bKq5.cjs");
2
+ const require_serialize = require("../serialize-BIIKUHH6.cjs");
3
+ const require_params = require("../params--8CNXYWu.cjs");
4
4
  //#region src/core/bitset.ts
5
5
  var BitSetRangeError = class extends RangeError {
6
6
  name = "BitSetRangeError";
7
7
  };
8
8
  const MAX_BITS = 2 ** 32;
9
- var BitSet = class BitSet {
9
+ var BitSet = class {
10
10
  #bits;
11
- static fromBytes(bytes) {
12
- const bs = new BitSet(bytes.length * 8);
13
- bs.#bits.set(bytes);
14
- return bs;
15
- }
16
11
  constructor(nbits) {
17
12
  if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
18
13
  this.#bits = new Uint8Array(Math.ceil(nbits / 8));
@@ -82,9 +77,23 @@ var BloomFilter = class BloomFilter {
82
77
  */
83
78
  static create(n, epsilon) {
84
79
  require_params.assertPositiveInt(n, "n");
80
+ require_params.assertUint32(n, "n");
85
81
  require_params.assertProbability(epsilon, "epsilon");
86
- const f = new BloomFilter(optimal(n, epsilon));
87
- f.#n = n;
82
+ return BloomFilter.#withN(optimal(n, epsilon), n);
83
+ }
84
+ /**
85
+ * Builds a filter from `keys`, sized for their count at the target
86
+ * false-positive rate. The ergonomic entry point when the key set is already
87
+ * in hand; use {@link BloomFilter.create} to size for a count known ahead.
88
+ *
89
+ * @param keys - The keys to insert.
90
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
91
+ * @returns A new filter containing every key.
92
+ */
93
+ static from(keys, epsilon) {
94
+ const arr = [...keys];
95
+ const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
96
+ for (const k of arr) f.add(k);
88
97
  return f;
89
98
  }
90
99
  /**
@@ -96,7 +105,7 @@ var BloomFilter = class BloomFilter {
96
105
  static fromBytes(bytes) {
97
106
  const { type, flags, body } = require_serialize.readHeader(bytes);
98
107
  if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
99
- if ((flags & 15) !== 1) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
108
+ if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
100
109
  require_serialize.assertMinBodyLength(body.length, 14, "bloom");
101
110
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
102
111
  const m = dv.getUint32(0, true);
@@ -104,12 +113,11 @@ var BloomFilter = class BloomFilter {
104
113
  const seed = dv.getUint32(6, true);
105
114
  const n = dv.getUint32(10, true);
106
115
  require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
107
- const f = new BloomFilter({
116
+ const f = BloomFilter.#withN({
108
117
  m,
109
118
  k,
110
119
  seed
111
- });
112
- f.#n = n;
120
+ }, n);
113
121
  f.#bits.bytes.set(body.subarray(14));
114
122
  return f;
115
123
  }
@@ -119,7 +127,9 @@ var BloomFilter = class BloomFilter {
119
127
  */
120
128
  constructor({ m, k, seed = 0 }) {
121
129
  require_params.assertPositiveInt(m, "m");
130
+ require_params.assertUint32(m, "m");
122
131
  require_params.assertPositiveInt(k, "k");
132
+ require_params.assertUint16(k, "k");
123
133
  require_params.assertUint32(seed, "seed");
124
134
  this.#bits = new BitSet(m);
125
135
  this.#m = m;
@@ -128,6 +138,11 @@ var BloomFilter = class BloomFilter {
128
138
  this.#scratch = new Uint32Array(k);
129
139
  this.#n = Math.round(m * Math.LN2 / k);
130
140
  }
141
+ static #withN(params, n) {
142
+ const f = new BloomFilter(params);
143
+ f.#n = n;
144
+ return f;
145
+ }
131
146
  /** Number of bits in the filter. */
132
147
  get m() {
133
148
  return this.#m;
@@ -165,18 +180,45 @@ var BloomFilter = class BloomFilter {
165
180
  */
166
181
  toBytes() {
167
182
  const payload = this.#bits.bytes;
168
- const body = new Uint8Array(14 + payload.length);
169
- const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
170
- dv.setUint32(0, this.#m, true);
171
- dv.setUint16(4, this.#k, true);
172
- dv.setUint32(6, this.#seed, true);
173
- dv.setUint32(10, this.#n, true);
174
- body.set(payload, 14);
175
- return require_serialize.writeHeader({
176
- version: 2,
183
+ return require_serialize.writeFrame({
184
+ version: 3,
177
185
  type: TYPE,
178
- flags: 1
179
- }, body);
186
+ flags: 0
187
+ }, 14 + payload.length, (body, dv) => {
188
+ dv.setUint32(0, this.#m, true);
189
+ dv.setUint16(4, this.#k, true);
190
+ dv.setUint32(6, this.#seed, true);
191
+ dv.setUint32(10, this.#n, true);
192
+ body.set(payload, 14);
193
+ });
194
+ }
195
+ /**
196
+ * Tests structural equality: `true` when `other` serializes to identical
197
+ * bytes, meaning identical parameters and set bits.
198
+ *
199
+ * @param other - The filter to compare against.
200
+ * @returns `true` if the two filters are byte-for-byte identical.
201
+ */
202
+ equals(other) {
203
+ return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
204
+ }
205
+ /**
206
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
207
+ * {@link BloomFilter.toBytes}.
208
+ *
209
+ * @returns The envelope, readable by {@link BloomFilter.fromJSON}.
210
+ */
211
+ toJSON() {
212
+ return require_serialize.toJSONEnvelope(this.toBytes());
213
+ }
214
+ /**
215
+ * Restores a filter from its {@link BloomFilter.toJSON} envelope.
216
+ *
217
+ * @param value - The JSON envelope.
218
+ * @returns The reconstructed filter.
219
+ */
220
+ static fromJSON(value) {
221
+ return BloomFilter.fromBytes(require_serialize.fromJSONEnvelope(value));
180
222
  }
181
223
  /**
182
224
  * Returns a new filter containing the union of this filter and `other`.
@@ -191,11 +233,11 @@ var BloomFilter = class BloomFilter {
191
233
  const b = other.#bits.bytes;
192
234
  const merged = new Uint8Array(a.length);
193
235
  for (let i = 0; i < a.length; i++) merged[i] = (a[i] ?? 0) | (b[i] ?? 0);
194
- const r = new BloomFilter({
236
+ const r = BloomFilter.#withN({
195
237
  m: this.#m,
196
238
  k: this.#k,
197
239
  seed: this.#seed
198
- });
240
+ }, this.#n);
199
241
  r.#bits.bytes.set(merged);
200
242
  return r;
201
243
  }
@@ -1,4 +1,4 @@
1
- import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
1
+ import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.cjs";
2
2
  import { t as ParamError } from "../params-DnqJBqLS.cjs";
3
3
  //#region src/bloom/bloom.d.ts
4
4
  /** Thrown when an operation requires two filters built with identical parameters. */
@@ -37,6 +37,16 @@ declare class BloomFilter {
37
37
  * @returns A new, empty filter.
38
38
  */
39
39
  static create(n: number, epsilon: number): BloomFilter;
40
+ /**
41
+ * Builds a filter from `keys`, sized for their count at the target
42
+ * false-positive rate. The ergonomic entry point when the key set is already
43
+ * in hand; use {@link BloomFilter.create} to size for a count known ahead.
44
+ *
45
+ * @param keys - The keys to insert.
46
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
47
+ * @returns A new filter containing every key.
48
+ */
49
+ static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
40
50
  /**
41
51
  * Restores a filter from its {@link BloomFilter.toBytes} serialization.
42
52
  *
@@ -73,6 +83,28 @@ declare class BloomFilter {
73
83
  * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
74
84
  */
75
85
  toBytes(): Uint8Array;
86
+ /**
87
+ * Tests structural equality: `true` when `other` serializes to identical
88
+ * bytes, meaning identical parameters and set bits.
89
+ *
90
+ * @param other - The filter to compare against.
91
+ * @returns `true` if the two filters are byte-for-byte identical.
92
+ */
93
+ equals(other: BloomFilter): boolean;
94
+ /**
95
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
96
+ * {@link BloomFilter.toBytes}.
97
+ *
98
+ * @returns The envelope, readable by {@link BloomFilter.fromJSON}.
99
+ */
100
+ toJSON(): FilterJSON;
101
+ /**
102
+ * Restores a filter from its {@link BloomFilter.toJSON} envelope.
103
+ *
104
+ * @param value - The JSON envelope.
105
+ * @returns The reconstructed filter.
106
+ */
107
+ static fromJSON(value: unknown): BloomFilter;
76
108
  /**
77
109
  * Returns a new filter containing the union of this filter and `other`.
78
110
  *
@@ -96,4 +128,4 @@ declare class BloomFilter {
96
128
  has(key: BytesLike): boolean;
97
129
  }
98
130
  //#endregion
99
- export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
131
+ export { BloomFilter, BloomParamMismatchError, type BloomParams, type FilterJSON, ParamError };
@@ -1,4 +1,4 @@
1
- import { t as BytesLike } from "../bytes-DCuYtUVS.js";
1
+ import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.js";
2
2
  import { t as ParamError } from "../params-DnqJBqLS.js";
3
3
  //#region src/bloom/bloom.d.ts
4
4
  /** Thrown when an operation requires two filters built with identical parameters. */
@@ -37,6 +37,16 @@ declare class BloomFilter {
37
37
  * @returns A new, empty filter.
38
38
  */
39
39
  static create(n: number, epsilon: number): BloomFilter;
40
+ /**
41
+ * Builds a filter from `keys`, sized for their count at the target
42
+ * false-positive rate. The ergonomic entry point when the key set is already
43
+ * in hand; use {@link BloomFilter.create} to size for a count known ahead.
44
+ *
45
+ * @param keys - The keys to insert.
46
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
47
+ * @returns A new filter containing every key.
48
+ */
49
+ static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
40
50
  /**
41
51
  * Restores a filter from its {@link BloomFilter.toBytes} serialization.
42
52
  *
@@ -73,6 +83,28 @@ declare class BloomFilter {
73
83
  * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
74
84
  */
75
85
  toBytes(): Uint8Array;
86
+ /**
87
+ * Tests structural equality: `true` when `other` serializes to identical
88
+ * bytes, meaning identical parameters and set bits.
89
+ *
90
+ * @param other - The filter to compare against.
91
+ * @returns `true` if the two filters are byte-for-byte identical.
92
+ */
93
+ equals(other: BloomFilter): boolean;
94
+ /**
95
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
96
+ * {@link BloomFilter.toBytes}.
97
+ *
98
+ * @returns The envelope, readable by {@link BloomFilter.fromJSON}.
99
+ */
100
+ toJSON(): FilterJSON;
101
+ /**
102
+ * Restores a filter from its {@link BloomFilter.toJSON} envelope.
103
+ *
104
+ * @param value - The JSON envelope.
105
+ * @returns The reconstructed filter.
106
+ */
107
+ static fromJSON(value: unknown): BloomFilter;
76
108
  /**
77
109
  * Returns a new filter containing the union of this filter and `other`.
78
110
  *
@@ -96,4 +128,4 @@ declare class BloomFilter {
96
128
  has(key: BytesLike): boolean;
97
129
  }
98
130
  //#endregion
99
- export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
131
+ export { BloomFilter, BloomParamMismatchError, type BloomParams, type FilterJSON, ParamError };
@@ -1,17 +1,12 @@
1
- import { a as readHeader, i as assertMinBodyLength, n as UnknownHashVariantError, o as writeHeader, p as probeInto, r as assertBodyLength, t as SerializationError } from "../serialize-BQ2UzZqq.js";
2
- import { a as assertUint32, i as assertProbability, r as assertPositiveInt, t as ParamError } from "../params-ChTRNxM9.js";
1
+ import { a as bytesEqual, c as toJSONEnvelope, g as probeInto, i as assertMinBodyLength, l as writeFrame, n as UnknownHashVariantError, o as fromJSONEnvelope, r as assertBodyLength, s as readHeader, t as SerializationError } from "../serialize-BnwtzcMw.js";
2
+ import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-BrWuBC2A.js";
3
3
  //#region src/core/bitset.ts
4
4
  var BitSetRangeError = class extends RangeError {
5
5
  name = "BitSetRangeError";
6
6
  };
7
7
  const MAX_BITS = 2 ** 32;
8
- var BitSet = class BitSet {
8
+ var BitSet = class {
9
9
  #bits;
10
- static fromBytes(bytes) {
11
- const bs = new BitSet(bytes.length * 8);
12
- bs.#bits.set(bytes);
13
- return bs;
14
- }
15
10
  constructor(nbits) {
16
11
  if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
17
12
  this.#bits = new Uint8Array(Math.ceil(nbits / 8));
@@ -81,9 +76,23 @@ var BloomFilter = class BloomFilter {
81
76
  */
82
77
  static create(n, epsilon) {
83
78
  assertPositiveInt(n, "n");
79
+ assertUint32(n, "n");
84
80
  assertProbability(epsilon, "epsilon");
85
- const f = new BloomFilter(optimal(n, epsilon));
86
- f.#n = n;
81
+ return BloomFilter.#withN(optimal(n, epsilon), n);
82
+ }
83
+ /**
84
+ * Builds a filter from `keys`, sized for their count at the target
85
+ * false-positive rate. The ergonomic entry point when the key set is already
86
+ * in hand; use {@link BloomFilter.create} to size for a count known ahead.
87
+ *
88
+ * @param keys - The keys to insert.
89
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
90
+ * @returns A new filter containing every key.
91
+ */
92
+ static from(keys, epsilon) {
93
+ const arr = [...keys];
94
+ const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
95
+ for (const k of arr) f.add(k);
87
96
  return f;
88
97
  }
89
98
  /**
@@ -95,7 +104,7 @@ var BloomFilter = class BloomFilter {
95
104
  static fromBytes(bytes) {
96
105
  const { type, flags, body } = readHeader(bytes);
97
106
  if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
98
- if ((flags & 15) !== 1) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
107
+ if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
99
108
  assertMinBodyLength(body.length, 14, "bloom");
100
109
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
101
110
  const m = dv.getUint32(0, true);
@@ -103,12 +112,11 @@ var BloomFilter = class BloomFilter {
103
112
  const seed = dv.getUint32(6, true);
104
113
  const n = dv.getUint32(10, true);
105
114
  assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
106
- const f = new BloomFilter({
115
+ const f = BloomFilter.#withN({
107
116
  m,
108
117
  k,
109
118
  seed
110
- });
111
- f.#n = n;
119
+ }, n);
112
120
  f.#bits.bytes.set(body.subarray(14));
113
121
  return f;
114
122
  }
@@ -118,7 +126,9 @@ var BloomFilter = class BloomFilter {
118
126
  */
119
127
  constructor({ m, k, seed = 0 }) {
120
128
  assertPositiveInt(m, "m");
129
+ assertUint32(m, "m");
121
130
  assertPositiveInt(k, "k");
131
+ assertUint16(k, "k");
122
132
  assertUint32(seed, "seed");
123
133
  this.#bits = new BitSet(m);
124
134
  this.#m = m;
@@ -127,6 +137,11 @@ var BloomFilter = class BloomFilter {
127
137
  this.#scratch = new Uint32Array(k);
128
138
  this.#n = Math.round(m * Math.LN2 / k);
129
139
  }
140
+ static #withN(params, n) {
141
+ const f = new BloomFilter(params);
142
+ f.#n = n;
143
+ return f;
144
+ }
130
145
  /** Number of bits in the filter. */
131
146
  get m() {
132
147
  return this.#m;
@@ -164,18 +179,45 @@ var BloomFilter = class BloomFilter {
164
179
  */
165
180
  toBytes() {
166
181
  const payload = this.#bits.bytes;
167
- const body = new Uint8Array(14 + payload.length);
168
- const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
169
- dv.setUint32(0, this.#m, true);
170
- dv.setUint16(4, this.#k, true);
171
- dv.setUint32(6, this.#seed, true);
172
- dv.setUint32(10, this.#n, true);
173
- body.set(payload, 14);
174
- return writeHeader({
175
- version: 2,
182
+ return writeFrame({
183
+ version: 3,
176
184
  type: TYPE,
177
- flags: 1
178
- }, body);
185
+ flags: 0
186
+ }, 14 + payload.length, (body, dv) => {
187
+ dv.setUint32(0, this.#m, true);
188
+ dv.setUint16(4, this.#k, true);
189
+ dv.setUint32(6, this.#seed, true);
190
+ dv.setUint32(10, this.#n, true);
191
+ body.set(payload, 14);
192
+ });
193
+ }
194
+ /**
195
+ * Tests structural equality: `true` when `other` serializes to identical
196
+ * bytes, meaning identical parameters and set bits.
197
+ *
198
+ * @param other - The filter to compare against.
199
+ * @returns `true` if the two filters are byte-for-byte identical.
200
+ */
201
+ equals(other) {
202
+ return bytesEqual(this.toBytes(), other.toBytes());
203
+ }
204
+ /**
205
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
206
+ * {@link BloomFilter.toBytes}.
207
+ *
208
+ * @returns The envelope, readable by {@link BloomFilter.fromJSON}.
209
+ */
210
+ toJSON() {
211
+ return toJSONEnvelope(this.toBytes());
212
+ }
213
+ /**
214
+ * Restores a filter from its {@link BloomFilter.toJSON} envelope.
215
+ *
216
+ * @param value - The JSON envelope.
217
+ * @returns The reconstructed filter.
218
+ */
219
+ static fromJSON(value) {
220
+ return BloomFilter.fromBytes(fromJSONEnvelope(value));
179
221
  }
180
222
  /**
181
223
  * Returns a new filter containing the union of this filter and `other`.
@@ -190,11 +232,11 @@ var BloomFilter = class BloomFilter {
190
232
  const b = other.#bits.bytes;
191
233
  const merged = new Uint8Array(a.length);
192
234
  for (let i = 0; i < a.length; i++) merged[i] = (a[i] ?? 0) | (b[i] ?? 0);
193
- const r = new BloomFilter({
235
+ const r = BloomFilter.#withN({
194
236
  m: this.#m,
195
237
  k: this.#k,
196
238
  seed: this.#seed
197
- });
239
+ }, this.#n);
198
240
  r.#bits.bytes.set(merged);
199
241
  return r;
200
242
  }
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-fa-pEUGq.cjs");
2
+ const require_serialize = require("../serialize-BIIKUHH6.cjs");
3
3
  //#region src/fuse/fuse.ts
4
4
  const ARITY = 3;
5
5
  const TYPE_FUSE8 = 3;
@@ -10,10 +10,10 @@ var BinaryFuseBuildError = class extends Error {
10
10
  name = "BinaryFuseBuildError";
11
11
  };
12
12
  const scratchHash = {
13
- h1lo: 0,
14
- h1hi: 0,
15
- h2lo: 0,
16
- h2hi: 0
13
+ w0: 0,
14
+ w1: 0,
15
+ w2: 0,
16
+ w3: 0
17
17
  };
18
18
  function mixSeed(lo, hi, seed) {
19
19
  const l = (lo >>> 0) + (seed >>> 0);
@@ -136,8 +136,8 @@ function buildState(keys, alloc) {
136
136
  const seen = /* @__PURE__ */ new Set();
137
137
  for (const key of keys) {
138
138
  require_serialize.hash128KeyInto(key, 0, scratchHash);
139
- const lo = scratchHash.h1lo >>> 0;
140
- const hi = scratchHash.h1hi >>> 0;
139
+ const lo = scratchHash.w0 >>> 0;
140
+ const hi = scratchHash.w1 >>> 0;
141
141
  const id = `${String(lo)},${String(hi)}`;
142
142
  if (seen.has(id)) continue;
143
143
  seen.add(id);
@@ -161,14 +161,17 @@ function buildState(keys, alloc) {
161
161
  };
162
162
  }
163
163
  function fuseStateFromBytes(bytes, expectedType) {
164
- const { type, body } = require_serialize.readHeader(bytes);
164
+ const { type, flags, body } = require_serialize.readHeader(bytes);
165
165
  if (type !== expectedType) throw new require_serialize.SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
166
+ if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
166
167
  require_serialize.assertMinBodyLength(body.length, 16, "fuse");
167
168
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
168
169
  const seed = dv.getUint32(0, true);
169
170
  const seg = dv.getUint32(4, true);
170
171
  const segCountLen = dv.getUint32(8, true);
171
172
  const size = dv.getUint32(12, true);
173
+ if (seg === 0 || (seg & seg - 1) !== 0 || seg > 1 << 18) throw new require_serialize.SerializationError(`invalid fuse segment length ${String(seg)}`);
174
+ if (segCountLen % seg !== 0) throw new require_serialize.SerializationError(`fuse segment count length ${String(segCountLen)} is not a multiple of segment length ${String(seg)}`);
172
175
  const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
173
176
  const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
174
177
  require_serialize.assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
@@ -212,6 +215,10 @@ var BinaryFuse = class {
212
215
  get size() {
213
216
  return this.#size;
214
217
  }
218
+ /** Hash seed selected during construction (may differ from 0 after a peel retry). */
219
+ get seed() {
220
+ return this.#seed;
221
+ }
215
222
  /** Actual bits stored per key (`0` for an empty filter). */
216
223
  get bitsPerKey() {
217
224
  return this.#size === 0 ? 0 : this.#fp.byteLength * 8 / this.#size;
@@ -223,18 +230,17 @@ var BinaryFuse = class {
223
230
  */
224
231
  toBytes() {
225
232
  const laneBytes = new Uint8Array(this.#fp.buffer, this.#fp.byteOffset, this.#fp.byteLength);
226
- const body = new Uint8Array(16 + laneBytes.length);
227
- const dv = new DataView(body.buffer);
228
- dv.setUint32(0, this.#seed, true);
229
- dv.setUint32(4, this.#seg, true);
230
- dv.setUint32(8, this.#segCountLen, true);
231
- dv.setUint32(12, this.#size, true);
232
- body.set(laneBytes, 16);
233
- return require_serialize.writeHeader({
234
- version: 2,
233
+ return require_serialize.writeFrame({
234
+ version: 3,
235
235
  type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
236
236
  flags: 0
237
- }, body);
237
+ }, 16 + laneBytes.length, (body, dv) => {
238
+ dv.setUint32(0, this.#seed, true);
239
+ dv.setUint32(4, this.#seg, true);
240
+ dv.setUint32(8, this.#segCountLen, true);
241
+ dv.setUint32(12, this.#size, true);
242
+ body.set(laneBytes, 16);
243
+ });
238
244
  }
239
245
  /**
240
246
  * Tests whether a key is in the set.
@@ -245,7 +251,7 @@ var BinaryFuse = class {
245
251
  has(key) {
246
252
  if (this.#fp.length === 0) return false;
247
253
  require_serialize.hash128KeyInto(key, 0, scratchHash);
248
- mixSeed(scratchHash.h1lo >>> 0, scratchHash.h1hi >>> 0, this.#seed);
254
+ mixSeed(scratchHash.w0 >>> 0, scratchHash.w1 >>> 0, this.#seed);
249
255
  const mlo = require_serialize.RLO;
250
256
  const mhi = require_serialize.RHI;
251
257
  positionsInto(mlo, mhi, this.#seg, this.#segMask, this.#segCountLen, this.#pos);
@@ -255,6 +261,26 @@ var BinaryFuse = class {
255
261
  const p2 = this.#pos[2] ?? 0;
256
262
  return ((mlo ^ mhi) & mask) === (((this.#fp[p0] ?? 0) ^ (this.#fp[p1] ?? 0) ^ (this.#fp[p2] ?? 0)) & mask);
257
263
  }
264
+ /**
265
+ * Tests structural equality: `true` when `other` serializes to identical
266
+ * bytes. A {@link BinaryFuse8} and a {@link BinaryFuse16} are never equal,
267
+ * since their frames carry different type bytes.
268
+ *
269
+ * @param other - The filter to compare against.
270
+ * @returns `true` if the two filters are byte-for-byte identical.
271
+ */
272
+ equals(other) {
273
+ return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
274
+ }
275
+ /**
276
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
277
+ * the `toBytes` frame.
278
+ *
279
+ * @returns The envelope, readable by the matching `fromJSON`.
280
+ */
281
+ toJSON() {
282
+ return require_serialize.toJSONEnvelope(this.toBytes());
283
+ }
258
284
  };
259
285
  /**
260
286
  * A static 8-bit binary fuse filter: built once from a key set, then immutable.
@@ -287,6 +313,15 @@ var BinaryFuse8 = class BinaryFuse8 extends BinaryFuse {
287
313
  static fromBytes(bytes) {
288
314
  return new BinaryFuse8(fuseStateFromBytes(bytes, TYPE_FUSE8));
289
315
  }
316
+ /**
317
+ * Restores a filter from its {@link BinaryFuse8.toJSON} envelope.
318
+ *
319
+ * @param value - The JSON envelope.
320
+ * @returns The reconstructed filter.
321
+ */
322
+ static fromJSON(value) {
323
+ return BinaryFuse8.fromBytes(require_serialize.fromJSONEnvelope(value));
324
+ }
290
325
  };
291
326
  /**
292
327
  * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
@@ -318,6 +353,15 @@ var BinaryFuse16 = class BinaryFuse16 extends BinaryFuse {
318
353
  static fromBytes(bytes) {
319
354
  return new BinaryFuse16(fuseStateFromBytes(bytes, TYPE_FUSE16));
320
355
  }
356
+ /**
357
+ * Restores a filter from its {@link BinaryFuse16.toJSON} envelope.
358
+ *
359
+ * @param value - The JSON envelope.
360
+ * @returns The reconstructed filter.
361
+ */
362
+ static fromJSON(value) {
363
+ return BinaryFuse16.fromBytes(require_serialize.fromJSONEnvelope(value));
364
+ }
321
365
  };
322
366
  //#endregion
323
367
  exports.BinaryFuse16 = BinaryFuse16;
@@ -1,4 +1,4 @@
1
- import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
1
+ import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.cjs";
2
2
  //#region src/fuse/fuse.d.ts
3
3
  /** Thrown when binary fuse construction fails to converge on the key set. */
4
4
  declare class BinaryFuseBuildError extends Error {
@@ -26,6 +26,8 @@ declare abstract class BinaryFuse {
26
26
  protected constructor(state: FuseState);
27
27
  /** Number of distinct keys the filter was built from. */
28
28
  get size(): number;
29
+ /** Hash seed selected during construction (may differ from 0 after a peel retry). */
30
+ get seed(): number;
29
31
  /** Actual bits stored per key (`0` for an empty filter). */
30
32
  get bitsPerKey(): number;
31
33
  /**
@@ -41,6 +43,22 @@ declare abstract class BinaryFuse {
41
43
  * @returns `true` if present (possibly a false positive); `false` guarantees absence.
42
44
  */
43
45
  has(key: BytesLike): boolean;
46
+ /**
47
+ * Tests structural equality: `true` when `other` serializes to identical
48
+ * bytes. A {@link BinaryFuse8} and a {@link BinaryFuse16} are never equal,
49
+ * since their frames carry different type bytes.
50
+ *
51
+ * @param other - The filter to compare against.
52
+ * @returns `true` if the two filters are byte-for-byte identical.
53
+ */
54
+ equals(other: BinaryFuse8 | BinaryFuse16): boolean;
55
+ /**
56
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
57
+ * the `toBytes` frame.
58
+ *
59
+ * @returns The envelope, readable by the matching `fromJSON`.
60
+ */
61
+ toJSON(): FilterJSON;
44
62
  }
45
63
  /**
46
64
  * A static 8-bit binary fuse filter: built once from a key set, then immutable.
@@ -69,6 +87,13 @@ declare class BinaryFuse8 extends BinaryFuse {
69
87
  * @returns The reconstructed filter.
70
88
  */
71
89
  static fromBytes(bytes: Uint8Array): BinaryFuse8;
90
+ /**
91
+ * Restores a filter from its {@link BinaryFuse8.toJSON} envelope.
92
+ *
93
+ * @param value - The JSON envelope.
94
+ * @returns The reconstructed filter.
95
+ */
96
+ static fromJSON(value: unknown): BinaryFuse8;
72
97
  }
73
98
  /**
74
99
  * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
@@ -96,6 +121,13 @@ declare class BinaryFuse16 extends BinaryFuse {
96
121
  * @returns The reconstructed filter.
97
122
  */
98
123
  static fromBytes(bytes: Uint8Array): BinaryFuse16;
124
+ /**
125
+ * Restores a filter from its {@link BinaryFuse16.toJSON} envelope.
126
+ *
127
+ * @param value - The JSON envelope.
128
+ * @returns The reconstructed filter.
129
+ */
130
+ static fromJSON(value: unknown): BinaryFuse16;
99
131
  }
100
132
  //#endregion
101
- export { BinaryFuse16, BinaryFuse8, BinaryFuseBuildError };
133
+ export { BinaryFuse16, BinaryFuse8, BinaryFuseBuildError, type FilterJSON };