distillate 0.1.1 → 0.1.2

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.
package/README.md CHANGED
@@ -92,6 +92,19 @@ const precise = BinaryFuse16.from(["alice", "bob", "carol"]);
92
92
 
93
93
  Also: `toBytes` / `fromBytes`. No `add` / `delete`; rebuild `from` the new set to change membership.
94
94
 
95
+ ## Performance
96
+
97
+ Classic Bloom head-to-head at a **matched 1% false-positive rate** over the same 100k keys, measured by identical code (Node, Apple M5):
98
+
99
+ | Classic Bloom | bits/key | measured FPR | `has` throughput |
100
+ | -------------- | -------- | ------------ | ---------------- |
101
+ | **distillate** | 9.59 | 1.03% | ~7.0 M ops/s |
102
+ | bloom-filters | 9.59 | 0.99% | ~0.29 M ops/s |
103
+
104
+ Same space, same accuracy, **~24x the lookup throughput** of [`bloom-filters`](https://www.npmjs.com/package/bloom-filters) (the package distillate replaces), while hashing UTF-8 bytes with MurmurHash3 so filters stay portable and cross-language readable.
105
+
106
+ These are a point-in-time snapshot on one machine. The full report (blocked/fuse, 1M capacity, the `bloomfilter` micro-package) and exactly how it is measured live in the [distillate-bench](https://github.com/akshay-xp/distillate-bench) repo: [RESULTS.md](https://github.com/akshay-xp/distillate-bench/blob/main/RESULTS.md), [METHODOLOGY.md](https://github.com/akshay-xp/distillate-bench/blob/main/METHODOLOGY.md).
107
+
95
108
  ## Docs
96
109
 
97
110
  Design notes, the structure decision matrix, hashing, and the binary format live in [`docs/`](./docs):
@@ -99,6 +112,8 @@ Design notes, the structure decision matrix, hashing, and the binary format live
99
112
  - [overview](./docs/overview.md): what and why
100
113
  - [structures](./docs/structures.md): decision matrix and the full lineup
101
114
  - [architecture](./docs/architecture.md), [hashing](./docs/hashing.md), [serialization](./docs/serialization.md)
115
+ - [versioning](./docs/versioning.md): SemVer policy and supported-runtime baseline
116
+ - [API reference](./docs/api): generated from TSDoc (per entry point)
102
117
 
103
118
  ## License
104
119
 
@@ -9,15 +9,22 @@ const scratchHash = {
9
9
  h2lo: 0,
10
10
  h2hi: 0
11
11
  };
12
- /**
13
- * Split-block probe: derive one block index and 8 single-bit lane masks for
14
- * `key`. Writes lane word indices into `outWords` and their masks into
15
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
16
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
17
- */
12
+ /** Thrown when an operation requires two filters built with identical parameters. */
18
13
  var BlockedBloomParamMismatchError = class extends Error {
14
+ /** Discriminates this error from other `Error`s. */
19
15
  name = "BlockedBloomParamMismatchError";
20
16
  };
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
21
28
  var BlockedBloomFilter = class BlockedBloomFilter {
22
29
  #lanes;
23
30
  #numBlocks;
@@ -30,6 +37,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
30
37
  [3, 16.9],
31
38
  [4, 26.4]
32
39
  ];
40
+ /**
41
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
42
+ *
43
+ * @param n - Expected number of keys.
44
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
45
+ * @returns A new, empty filter.
46
+ */
33
47
  static create(n, epsilon) {
34
48
  const t = Math.log10(1 / epsilon);
35
49
  const a = BlockedBloomFilter.#ANCHORS;
@@ -43,15 +57,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
43
57
  capacity: n
44
58
  });
45
59
  }
60
+ /**
61
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
62
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
63
+ */
46
64
  constructor({ bitsPerKey, capacity, seed = 0 }) {
47
65
  this.#numBlocks = Math.max(1, Math.ceil(bitsPerKey * capacity / 256));
48
66
  this.#lanes = new Uint32Array(this.#numBlocks * 8);
49
67
  this.#seed = seed;
50
68
  this.#n = capacity;
51
69
  }
70
+ /** Actual bits allocated per key (`total bits / capacity`). */
52
71
  get bitsPerKey() {
53
72
  return this.#numBlocks * 256 / this.#n;
54
73
  }
74
+ /**
75
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
76
+ *
77
+ * @param bytes - The serialized filter.
78
+ * @returns The reconstructed filter.
79
+ */
55
80
  static fromBytes(bytes) {
56
81
  const { body } = require_serialize.readHeader(bytes);
57
82
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -67,6 +92,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
67
92
  new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
68
93
  return f;
69
94
  }
95
+ /**
96
+ * Serializes the filter to a portable little-endian byte layout.
97
+ *
98
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
99
+ */
70
100
  toBytes() {
71
101
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
72
102
  const body = new Uint8Array(12 + lanes.length);
@@ -81,6 +111,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
81
111
  flags: 0
82
112
  }, body);
83
113
  }
114
+ /**
115
+ * Returns a new filter containing the union of this filter and `other`.
116
+ *
117
+ * @param other - A filter built with identical parameters.
118
+ * @returns A new filter reporting membership for keys in either input.
119
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
120
+ */
84
121
  union(other) {
85
122
  if (this.#numBlocks !== other.#numBlocks || this.#seed !== other.#seed) throw new BlockedBloomParamMismatchError("cannot union blocked Bloom filters whose parameters do not match");
86
123
  const r = new BlockedBloomFilter({
@@ -91,6 +128,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
91
128
  for (let i = 0; i < this.#lanes.length; i++) r.#lanes[i] = (this.#lanes[i] ?? 0) | (other.#lanes[i] ?? 0);
92
129
  return r;
93
130
  }
131
+ /**
132
+ * Adds a key to the set.
133
+ *
134
+ * @param key - The key to insert, as a string or bytes.
135
+ */
94
136
  add(key) {
95
137
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
96
138
  for (let i = 0; i < 8; i++) {
@@ -98,6 +140,12 @@ var BlockedBloomFilter = class BlockedBloomFilter {
98
140
  this.#lanes[w] = (this.#lanes[w] ?? 0) | (this.#bits[i] ?? 0);
99
141
  }
100
142
  }
143
+ /**
144
+ * Tests whether a key is in the set.
145
+ *
146
+ * @param key - The key to test.
147
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
148
+ */
101
149
  has(key) {
102
150
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
103
151
  for (let i = 0; i < 8; i++) {
@@ -1,28 +1,80 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
2
2
  //#region src/blocked/blocked.d.ts
3
- /**
4
- * Split-block probe: derive one block index and 8 single-bit lane masks for
5
- * `key`. Writes lane word indices into `outWords` and their masks into
6
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
7
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
8
- */
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
9
4
  declare class BlockedBloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
10
6
  override readonly name = "BlockedBloomParamMismatchError";
11
7
  }
8
+ /** Low-level blocked Bloom filter parameters. */
12
9
  interface BlockedBloomParams {
10
+ /** Bits allocated per key; higher lowers the false-positive rate. */
13
11
  bitsPerKey: number;
12
+ /** Expected number of keys. */
14
13
  capacity: number;
14
+ /** Hash seed; defaults to `0`. */
15
15
  seed?: number;
16
16
  }
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
17
28
  declare class BlockedBloomFilter {
18
29
  #private;
30
+ /**
31
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
32
+ *
33
+ * @param n - Expected number of keys.
34
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
35
+ * @returns A new, empty filter.
36
+ */
19
37
  static create(n: number, epsilon: number): BlockedBloomFilter;
38
+ /**
39
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
40
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
41
+ */
20
42
  constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
43
+ /** Actual bits allocated per key (`total bits / capacity`). */
21
44
  get bitsPerKey(): number;
45
+ /**
46
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
47
+ *
48
+ * @param bytes - The serialized filter.
49
+ * @returns The reconstructed filter.
50
+ */
22
51
  static fromBytes(bytes: Uint8Array): BlockedBloomFilter;
52
+ /**
53
+ * Serializes the filter to a portable little-endian byte layout.
54
+ *
55
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
56
+ */
23
57
  toBytes(): Uint8Array;
58
+ /**
59
+ * Returns a new filter containing the union of this filter and `other`.
60
+ *
61
+ * @param other - A filter built with identical parameters.
62
+ * @returns A new filter reporting membership for keys in either input.
63
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
64
+ */
24
65
  union(other: BlockedBloomFilter): BlockedBloomFilter;
66
+ /**
67
+ * Adds a key to the set.
68
+ *
69
+ * @param key - The key to insert, as a string or bytes.
70
+ */
25
71
  add(key: BytesLike): void;
72
+ /**
73
+ * Tests whether a key is in the set.
74
+ *
75
+ * @param key - The key to test.
76
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
77
+ */
26
78
  has(key: BytesLike): boolean;
27
79
  }
28
80
  //#endregion
@@ -1,28 +1,80 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.js";
2
2
  //#region src/blocked/blocked.d.ts
3
- /**
4
- * Split-block probe: derive one block index and 8 single-bit lane masks for
5
- * `key`. Writes lane word indices into `outWords` and their masks into
6
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
7
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
8
- */
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
9
4
  declare class BlockedBloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
10
6
  override readonly name = "BlockedBloomParamMismatchError";
11
7
  }
8
+ /** Low-level blocked Bloom filter parameters. */
12
9
  interface BlockedBloomParams {
10
+ /** Bits allocated per key; higher lowers the false-positive rate. */
13
11
  bitsPerKey: number;
12
+ /** Expected number of keys. */
14
13
  capacity: number;
14
+ /** Hash seed; defaults to `0`. */
15
15
  seed?: number;
16
16
  }
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
17
28
  declare class BlockedBloomFilter {
18
29
  #private;
30
+ /**
31
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
32
+ *
33
+ * @param n - Expected number of keys.
34
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
35
+ * @returns A new, empty filter.
36
+ */
19
37
  static create(n: number, epsilon: number): BlockedBloomFilter;
38
+ /**
39
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
40
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
41
+ */
20
42
  constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
43
+ /** Actual bits allocated per key (`total bits / capacity`). */
21
44
  get bitsPerKey(): number;
45
+ /**
46
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
47
+ *
48
+ * @param bytes - The serialized filter.
49
+ * @returns The reconstructed filter.
50
+ */
22
51
  static fromBytes(bytes: Uint8Array): BlockedBloomFilter;
52
+ /**
53
+ * Serializes the filter to a portable little-endian byte layout.
54
+ *
55
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
56
+ */
23
57
  toBytes(): Uint8Array;
58
+ /**
59
+ * Returns a new filter containing the union of this filter and `other`.
60
+ *
61
+ * @param other - A filter built with identical parameters.
62
+ * @returns A new filter reporting membership for keys in either input.
63
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
64
+ */
24
65
  union(other: BlockedBloomFilter): BlockedBloomFilter;
66
+ /**
67
+ * Adds a key to the set.
68
+ *
69
+ * @param key - The key to insert, as a string or bytes.
70
+ */
25
71
  add(key: BytesLike): void;
72
+ /**
73
+ * Tests whether a key is in the set.
74
+ *
75
+ * @param key - The key to test.
76
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
77
+ */
26
78
  has(key: BytesLike): boolean;
27
79
  }
28
80
  //#endregion
@@ -8,15 +8,22 @@ const scratchHash = {
8
8
  h2lo: 0,
9
9
  h2hi: 0
10
10
  };
11
- /**
12
- * Split-block probe: derive one block index and 8 single-bit lane masks for
13
- * `key`. Writes lane word indices into `outWords` and their masks into
14
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
15
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
16
- */
11
+ /** Thrown when an operation requires two filters built with identical parameters. */
17
12
  var BlockedBloomParamMismatchError = class extends Error {
13
+ /** Discriminates this error from other `Error`s. */
18
14
  name = "BlockedBloomParamMismatchError";
19
15
  };
16
+ /**
17
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
18
+ * line, trading ~20-30% more space for cache-friendly throughput.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
23
+ * filter.add("alice");
24
+ * filter.has("alice"); // true
25
+ * ```
26
+ */
20
27
  var BlockedBloomFilter = class BlockedBloomFilter {
21
28
  #lanes;
22
29
  #numBlocks;
@@ -29,6 +36,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
29
36
  [3, 16.9],
30
37
  [4, 26.4]
31
38
  ];
39
+ /**
40
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
41
+ *
42
+ * @param n - Expected number of keys.
43
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
44
+ * @returns A new, empty filter.
45
+ */
32
46
  static create(n, epsilon) {
33
47
  const t = Math.log10(1 / epsilon);
34
48
  const a = BlockedBloomFilter.#ANCHORS;
@@ -42,15 +56,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
42
56
  capacity: n
43
57
  });
44
58
  }
59
+ /**
60
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
61
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
62
+ */
45
63
  constructor({ bitsPerKey, capacity, seed = 0 }) {
46
64
  this.#numBlocks = Math.max(1, Math.ceil(bitsPerKey * capacity / 256));
47
65
  this.#lanes = new Uint32Array(this.#numBlocks * 8);
48
66
  this.#seed = seed;
49
67
  this.#n = capacity;
50
68
  }
69
+ /** Actual bits allocated per key (`total bits / capacity`). */
51
70
  get bitsPerKey() {
52
71
  return this.#numBlocks * 256 / this.#n;
53
72
  }
73
+ /**
74
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
75
+ *
76
+ * @param bytes - The serialized filter.
77
+ * @returns The reconstructed filter.
78
+ */
54
79
  static fromBytes(bytes) {
55
80
  const { body } = readHeader(bytes);
56
81
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -66,6 +91,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
66
91
  new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
67
92
  return f;
68
93
  }
94
+ /**
95
+ * Serializes the filter to a portable little-endian byte layout.
96
+ *
97
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
98
+ */
69
99
  toBytes() {
70
100
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
71
101
  const body = new Uint8Array(12 + lanes.length);
@@ -80,6 +110,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
80
110
  flags: 0
81
111
  }, body);
82
112
  }
113
+ /**
114
+ * Returns a new filter containing the union of this filter and `other`.
115
+ *
116
+ * @param other - A filter built with identical parameters.
117
+ * @returns A new filter reporting membership for keys in either input.
118
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
119
+ */
83
120
  union(other) {
84
121
  if (this.#numBlocks !== other.#numBlocks || this.#seed !== other.#seed) throw new BlockedBloomParamMismatchError("cannot union blocked Bloom filters whose parameters do not match");
85
122
  const r = new BlockedBloomFilter({
@@ -90,6 +127,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
90
127
  for (let i = 0; i < this.#lanes.length; i++) r.#lanes[i] = (this.#lanes[i] ?? 0) | (other.#lanes[i] ?? 0);
91
128
  return r;
92
129
  }
130
+ /**
131
+ * Adds a key to the set.
132
+ *
133
+ * @param key - The key to insert, as a string or bytes.
134
+ */
93
135
  add(key) {
94
136
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
95
137
  for (let i = 0; i < 8; i++) {
@@ -97,6 +139,12 @@ var BlockedBloomFilter = class BlockedBloomFilter {
97
139
  this.#lanes[w] = (this.#lanes[w] ?? 0) | (this.#bits[i] ?? 0);
98
140
  }
99
141
  }
142
+ /**
143
+ * Tests whether a key is in the set.
144
+ *
145
+ * @param key - The key to test.
146
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
147
+ */
100
148
  has(key) {
101
149
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
102
150
  for (let i = 0; i < 8; i++) {
@@ -48,9 +48,23 @@ function optimal(n, epsilon) {
48
48
  //#endregion
49
49
  //#region src/bloom/bloom.ts
50
50
  const TYPE = 1;
51
+ /** Thrown when an operation requires two filters built with identical parameters. */
51
52
  var BloomParamMismatchError = class extends Error {
53
+ /** Discriminates this error from other `Error`s. */
52
54
  name = "BloomParamMismatchError";
53
55
  };
56
+ /**
57
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
58
+ * rate and zero false negatives.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const filter = BloomFilter.create(100_000, 0.01);
63
+ * filter.add("alice");
64
+ * filter.has("alice"); // true
65
+ * filter.has("bob"); // false (or a ~1% false positive)
66
+ * ```
67
+ */
54
68
  var BloomFilter = class BloomFilter {
55
69
  #bits;
56
70
  #m;
@@ -58,11 +72,24 @@ var BloomFilter = class BloomFilter {
58
72
  #seed;
59
73
  #scratch;
60
74
  #n;
75
+ /**
76
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
77
+ *
78
+ * @param n - Expected number of keys.
79
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
80
+ * @returns A new, empty filter.
81
+ */
61
82
  static create(n, epsilon) {
62
83
  const f = new BloomFilter(optimal(n, epsilon));
63
84
  f.#n = n;
64
85
  return f;
65
86
  }
87
+ /**
88
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
89
+ *
90
+ * @param bytes - The serialized filter.
91
+ * @returns The reconstructed filter.
92
+ */
66
93
  static fromBytes(bytes) {
67
94
  const { body } = require_serialize.readHeader(bytes);
68
95
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -77,6 +104,10 @@ var BloomFilter = class BloomFilter {
77
104
  f.#bits.bytes.set(body.subarray(9));
78
105
  return f;
79
106
  }
107
+ /**
108
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
109
+ * {@link BloomFilter.create} unless restoring a specific configuration.
110
+ */
80
111
  constructor({ m, k, seed = 0 }) {
81
112
  this.#bits = new BitSet(m);
82
113
  this.#m = m;
@@ -89,6 +120,11 @@ var BloomFilter = class BloomFilter {
89
120
  get bitsPerKey() {
90
121
  return this.#m / this.#n;
91
122
  }
123
+ /**
124
+ * Serializes the filter to a portable little-endian byte layout.
125
+ *
126
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
127
+ */
92
128
  toBytes() {
93
129
  const payload = this.#bits.bytes;
94
130
  const body = new Uint8Array(9 + payload.length);
@@ -103,6 +139,13 @@ var BloomFilter = class BloomFilter {
103
139
  flags: 0
104
140
  }, body);
105
141
  }
142
+ /**
143
+ * Returns a new filter containing the union of this filter and `other`.
144
+ *
145
+ * @param other - A filter built with identical parameters.
146
+ * @returns A new filter reporting membership for keys in either input.
147
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
148
+ */
106
149
  union(other) {
107
150
  if (this.#m !== other.#m || this.#k !== other.#k || this.#seed !== other.#seed) throw new BloomParamMismatchError("cannot union Bloom filters whose parameters do not match");
108
151
  const a = this.#bits.bytes;
@@ -117,10 +160,21 @@ var BloomFilter = class BloomFilter {
117
160
  r.#bits.bytes.set(merged);
118
161
  return r;
119
162
  }
163
+ /**
164
+ * Adds a key to the set.
165
+ *
166
+ * @param key - The key to insert, as a string or bytes.
167
+ */
120
168
  add(key) {
121
169
  require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
122
170
  for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
123
171
  }
172
+ /**
173
+ * Tests whether a key is in the set.
174
+ *
175
+ * @param key - The key to test.
176
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
177
+ */
124
178
  has(key) {
125
179
  require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
126
180
  for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
@@ -1,23 +1,81 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
2
2
  //#region src/bloom/bloom.d.ts
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
3
4
  declare class BloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
4
6
  override readonly name = "BloomParamMismatchError";
5
7
  }
8
+ /** Low-level Bloom filter parameters. */
6
9
  interface BloomParams {
10
+ /** Number of bits in the filter. */
7
11
  m: number;
12
+ /** Number of hash probes per key. */
8
13
  k: number;
14
+ /** Hash seed; defaults to `0`. */
9
15
  seed?: number;
10
16
  }
17
+ /**
18
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
19
+ * rate and zero false negatives.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * filter.has("bob"); // false (or a ~1% false positive)
27
+ * ```
28
+ */
11
29
  declare class BloomFilter {
12
30
  #private;
31
+ /**
32
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
33
+ *
34
+ * @param n - Expected number of keys.
35
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
36
+ * @returns A new, empty filter.
37
+ */
13
38
  static create(n: number, epsilon: number): BloomFilter;
39
+ /**
40
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
41
+ *
42
+ * @param bytes - The serialized filter.
43
+ * @returns The reconstructed filter.
44
+ */
14
45
  static fromBytes(bytes: Uint8Array): BloomFilter;
46
+ /**
47
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
48
+ * {@link BloomFilter.create} unless restoring a specific configuration.
49
+ */
15
50
  constructor({ m, k, seed }: BloomParams);
16
51
  /** Analytic design bits-per-key `m / n`. */
17
52
  get bitsPerKey(): number;
53
+ /**
54
+ * Serializes the filter to a portable little-endian byte layout.
55
+ *
56
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
57
+ */
18
58
  toBytes(): Uint8Array;
59
+ /**
60
+ * Returns a new filter containing the union of this filter and `other`.
61
+ *
62
+ * @param other - A filter built with identical parameters.
63
+ * @returns A new filter reporting membership for keys in either input.
64
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
65
+ */
19
66
  union(other: BloomFilter): BloomFilter;
67
+ /**
68
+ * Adds a key to the set.
69
+ *
70
+ * @param key - The key to insert, as a string or bytes.
71
+ */
20
72
  add(key: BytesLike): void;
73
+ /**
74
+ * Tests whether a key is in the set.
75
+ *
76
+ * @param key - The key to test.
77
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
78
+ */
21
79
  has(key: BytesLike): boolean;
22
80
  }
23
81
  //#endregion
@@ -1,23 +1,81 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.js";
2
2
  //#region src/bloom/bloom.d.ts
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
3
4
  declare class BloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
4
6
  override readonly name = "BloomParamMismatchError";
5
7
  }
8
+ /** Low-level Bloom filter parameters. */
6
9
  interface BloomParams {
10
+ /** Number of bits in the filter. */
7
11
  m: number;
12
+ /** Number of hash probes per key. */
8
13
  k: number;
14
+ /** Hash seed; defaults to `0`. */
9
15
  seed?: number;
10
16
  }
17
+ /**
18
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
19
+ * rate and zero false negatives.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * filter.has("bob"); // false (or a ~1% false positive)
27
+ * ```
28
+ */
11
29
  declare class BloomFilter {
12
30
  #private;
31
+ /**
32
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
33
+ *
34
+ * @param n - Expected number of keys.
35
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
36
+ * @returns A new, empty filter.
37
+ */
13
38
  static create(n: number, epsilon: number): BloomFilter;
39
+ /**
40
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
41
+ *
42
+ * @param bytes - The serialized filter.
43
+ * @returns The reconstructed filter.
44
+ */
14
45
  static fromBytes(bytes: Uint8Array): BloomFilter;
46
+ /**
47
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
48
+ * {@link BloomFilter.create} unless restoring a specific configuration.
49
+ */
15
50
  constructor({ m, k, seed }: BloomParams);
16
51
  /** Analytic design bits-per-key `m / n`. */
17
52
  get bitsPerKey(): number;
53
+ /**
54
+ * Serializes the filter to a portable little-endian byte layout.
55
+ *
56
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
57
+ */
18
58
  toBytes(): Uint8Array;
59
+ /**
60
+ * Returns a new filter containing the union of this filter and `other`.
61
+ *
62
+ * @param other - A filter built with identical parameters.
63
+ * @returns A new filter reporting membership for keys in either input.
64
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
65
+ */
19
66
  union(other: BloomFilter): BloomFilter;
67
+ /**
68
+ * Adds a key to the set.
69
+ *
70
+ * @param key - The key to insert, as a string or bytes.
71
+ */
20
72
  add(key: BytesLike): void;
73
+ /**
74
+ * Tests whether a key is in the set.
75
+ *
76
+ * @param key - The key to test.
77
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
78
+ */
21
79
  has(key: BytesLike): boolean;
22
80
  }
23
81
  //#endregion
@@ -47,9 +47,23 @@ function optimal(n, epsilon) {
47
47
  //#endregion
48
48
  //#region src/bloom/bloom.ts
49
49
  const TYPE = 1;
50
+ /** Thrown when an operation requires two filters built with identical parameters. */
50
51
  var BloomParamMismatchError = class extends Error {
52
+ /** Discriminates this error from other `Error`s. */
51
53
  name = "BloomParamMismatchError";
52
54
  };
55
+ /**
56
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
57
+ * rate and zero false negatives.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const filter = BloomFilter.create(100_000, 0.01);
62
+ * filter.add("alice");
63
+ * filter.has("alice"); // true
64
+ * filter.has("bob"); // false (or a ~1% false positive)
65
+ * ```
66
+ */
53
67
  var BloomFilter = class BloomFilter {
54
68
  #bits;
55
69
  #m;
@@ -57,11 +71,24 @@ var BloomFilter = class BloomFilter {
57
71
  #seed;
58
72
  #scratch;
59
73
  #n;
74
+ /**
75
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
76
+ *
77
+ * @param n - Expected number of keys.
78
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
79
+ * @returns A new, empty filter.
80
+ */
60
81
  static create(n, epsilon) {
61
82
  const f = new BloomFilter(optimal(n, epsilon));
62
83
  f.#n = n;
63
84
  return f;
64
85
  }
86
+ /**
87
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
88
+ *
89
+ * @param bytes - The serialized filter.
90
+ * @returns The reconstructed filter.
91
+ */
65
92
  static fromBytes(bytes) {
66
93
  const { body } = readHeader(bytes);
67
94
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -76,6 +103,10 @@ var BloomFilter = class BloomFilter {
76
103
  f.#bits.bytes.set(body.subarray(9));
77
104
  return f;
78
105
  }
106
+ /**
107
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
108
+ * {@link BloomFilter.create} unless restoring a specific configuration.
109
+ */
79
110
  constructor({ m, k, seed = 0 }) {
80
111
  this.#bits = new BitSet(m);
81
112
  this.#m = m;
@@ -88,6 +119,11 @@ var BloomFilter = class BloomFilter {
88
119
  get bitsPerKey() {
89
120
  return this.#m / this.#n;
90
121
  }
122
+ /**
123
+ * Serializes the filter to a portable little-endian byte layout.
124
+ *
125
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
126
+ */
91
127
  toBytes() {
92
128
  const payload = this.#bits.bytes;
93
129
  const body = new Uint8Array(9 + payload.length);
@@ -102,6 +138,13 @@ var BloomFilter = class BloomFilter {
102
138
  flags: 0
103
139
  }, body);
104
140
  }
141
+ /**
142
+ * Returns a new filter containing the union of this filter and `other`.
143
+ *
144
+ * @param other - A filter built with identical parameters.
145
+ * @returns A new filter reporting membership for keys in either input.
146
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
147
+ */
105
148
  union(other) {
106
149
  if (this.#m !== other.#m || this.#k !== other.#k || this.#seed !== other.#seed) throw new BloomParamMismatchError("cannot union Bloom filters whose parameters do not match");
107
150
  const a = this.#bits.bytes;
@@ -116,10 +159,21 @@ var BloomFilter = class BloomFilter {
116
159
  r.#bits.bytes.set(merged);
117
160
  return r;
118
161
  }
162
+ /**
163
+ * Adds a key to the set.
164
+ *
165
+ * @param key - The key to insert, as a string or bytes.
166
+ */
119
167
  add(key) {
120
168
  probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
121
169
  for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
122
170
  }
171
+ /**
172
+ * Tests whether a key is in the set.
173
+ *
174
+ * @param key - The key to test.
175
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
176
+ */
123
177
  has(key) {
124
178
  probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
125
179
  for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
@@ -4,7 +4,9 @@ const require_serialize = require("../serialize-CHHDM4TQ.cjs");
4
4
  const ARITY = 3;
5
5
  const TYPE_FUSE8 = 3;
6
6
  const TYPE_FUSE16 = 4;
7
+ /** Thrown when binary fuse construction fails to converge on the key set. */
7
8
  var BinaryFuseBuildError = class extends Error {
9
+ /** Discriminates this error from other `Error`s. */
8
10
  name = "BinaryFuseBuildError";
9
11
  };
10
12
  let RLO = 0;
@@ -231,6 +233,10 @@ function fuseStateFromBytes(bytes, expectedType) {
231
233
  size
232
234
  };
233
235
  }
236
+ /**
237
+ * Shared behavior for the static binary fuse filters: an immutable,
238
+ * space-efficient membership filter built once from a fixed key set.
239
+ */
234
240
  var BinaryFuse = class {
235
241
  #fp;
236
242
  #seed;
@@ -247,12 +253,19 @@ var BinaryFuse = class {
247
253
  this.#segCountLen = state.params.segCountLen;
248
254
  this.#size = state.size;
249
255
  }
256
+ /** Number of distinct keys the filter was built from. */
250
257
  get size() {
251
258
  return this.#size;
252
259
  }
260
+ /** Actual bits stored per key (`0` for an empty filter). */
253
261
  get bitsPerKey() {
254
262
  return this.#size === 0 ? 0 : this.#fp.byteLength * 8 / this.#size;
255
263
  }
264
+ /**
265
+ * Serializes the filter to a portable little-endian byte layout.
266
+ *
267
+ * @returns The serialized filter, readable by the matching `fromBytes`.
268
+ */
256
269
  toBytes() {
257
270
  const laneBytes = new Uint8Array(this.#fp.buffer, this.#fp.byteOffset, this.#fp.byteLength);
258
271
  const body = new Uint8Array(16 + laneBytes.length);
@@ -268,6 +281,12 @@ var BinaryFuse = class {
268
281
  flags: 0
269
282
  }, body);
270
283
  }
284
+ /**
285
+ * Tests whether a key is in the set.
286
+ *
287
+ * @param key - The key to test.
288
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
289
+ */
271
290
  has(key) {
272
291
  if (this.#fp.length === 0) return false;
273
292
  require_serialize.hash128KeyInto(key, 0, scratchHash);
@@ -282,18 +301,65 @@ var BinaryFuse = class {
282
301
  return ((mlo ^ mhi) & mask) === (((this.#fp[p0] ?? 0) ^ (this.#fp[p1] ?? 0) ^ (this.#fp[p2] ?? 0)) & mask);
283
302
  }
284
303
  };
304
+ /**
305
+ * A static 8-bit binary fuse filter: built once from a key set, then immutable.
306
+ * The most space-efficient option (~9 bits/key at ~0.39% false-positive rate).
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
311
+ * filter.has("alice"); // true
312
+ * filter.size; // 3
313
+ * ```
314
+ */
285
315
  var BinaryFuse8 = class BinaryFuse8 extends BinaryFuse {
316
+ /**
317
+ * Builds a filter from the given keys; duplicates are ignored.
318
+ *
319
+ * @param keys - The complete set of keys to store.
320
+ * @returns A new immutable filter.
321
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
322
+ */
286
323
  static from(keys) {
287
324
  return new BinaryFuse8(buildState(keys, (n) => new Uint8Array(n)));
288
325
  }
326
+ /**
327
+ * Restores a filter from its {@link BinaryFuse8.toBytes} serialization.
328
+ *
329
+ * @param bytes - The serialized filter.
330
+ * @returns The reconstructed filter.
331
+ */
289
332
  static fromBytes(bytes) {
290
333
  return new BinaryFuse8(fuseStateFromBytes(bytes, TYPE_FUSE8));
291
334
  }
292
335
  };
336
+ /**
337
+ * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
338
+ * space (~18 bits/key) for a far lower false-positive rate (~1/65536).
339
+ *
340
+ * @example
341
+ * ```ts
342
+ * const filter = BinaryFuse16.from(["alice", "bob", "carol"]);
343
+ * filter.has("alice"); // true
344
+ * ```
345
+ */
293
346
  var BinaryFuse16 = class BinaryFuse16 extends BinaryFuse {
347
+ /**
348
+ * Builds a filter from the given keys; duplicates are ignored.
349
+ *
350
+ * @param keys - The complete set of keys to store.
351
+ * @returns A new immutable filter.
352
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
353
+ */
294
354
  static from(keys) {
295
355
  return new BinaryFuse16(buildState(keys, (n) => new Uint16Array(n)));
296
356
  }
357
+ /**
358
+ * Restores a filter from its {@link BinaryFuse16.toBytes} serialization.
359
+ *
360
+ * @param bytes - The serialized filter.
361
+ * @returns The reconstructed filter.
362
+ */
297
363
  static fromBytes(bytes) {
298
364
  return new BinaryFuse16(fuseStateFromBytes(bytes, TYPE_FUSE16));
299
365
  }
@@ -1,6 +1,8 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
2
2
  //#region src/fuse/fuse.d.ts
3
+ /** Thrown when binary fuse construction fails to converge on the key set. */
3
4
  declare class BinaryFuseBuildError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
4
6
  override readonly name = "BinaryFuseBuildError";
5
7
  }
6
8
  interface FuseParams {
@@ -15,20 +17,84 @@ interface FuseState {
15
17
  params: FuseParams;
16
18
  size: number;
17
19
  }
20
+ /**
21
+ * Shared behavior for the static binary fuse filters: an immutable,
22
+ * space-efficient membership filter built once from a fixed key set.
23
+ */
18
24
  declare abstract class BinaryFuse {
19
25
  #private;
20
26
  protected constructor(state: FuseState);
27
+ /** Number of distinct keys the filter was built from. */
21
28
  get size(): number;
29
+ /** Actual bits stored per key (`0` for an empty filter). */
22
30
  get bitsPerKey(): number;
31
+ /**
32
+ * Serializes the filter to a portable little-endian byte layout.
33
+ *
34
+ * @returns The serialized filter, readable by the matching `fromBytes`.
35
+ */
23
36
  toBytes(): Uint8Array;
37
+ /**
38
+ * Tests whether a key is in the set.
39
+ *
40
+ * @param key - The key to test.
41
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
42
+ */
24
43
  has(key: BytesLike): boolean;
25
44
  }
45
+ /**
46
+ * A static 8-bit binary fuse filter: built once from a key set, then immutable.
47
+ * The most space-efficient option (~9 bits/key at ~0.39% false-positive rate).
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
52
+ * filter.has("alice"); // true
53
+ * filter.size; // 3
54
+ * ```
55
+ */
26
56
  declare class BinaryFuse8 extends BinaryFuse {
57
+ /**
58
+ * Builds a filter from the given keys; duplicates are ignored.
59
+ *
60
+ * @param keys - The complete set of keys to store.
61
+ * @returns A new immutable filter.
62
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
63
+ */
27
64
  static from(keys: Iterable<BytesLike>): BinaryFuse8;
65
+ /**
66
+ * Restores a filter from its {@link BinaryFuse8.toBytes} serialization.
67
+ *
68
+ * @param bytes - The serialized filter.
69
+ * @returns The reconstructed filter.
70
+ */
28
71
  static fromBytes(bytes: Uint8Array): BinaryFuse8;
29
72
  }
73
+ /**
74
+ * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
75
+ * space (~18 bits/key) for a far lower false-positive rate (~1/65536).
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const filter = BinaryFuse16.from(["alice", "bob", "carol"]);
80
+ * filter.has("alice"); // true
81
+ * ```
82
+ */
30
83
  declare class BinaryFuse16 extends BinaryFuse {
84
+ /**
85
+ * Builds a filter from the given keys; duplicates are ignored.
86
+ *
87
+ * @param keys - The complete set of keys to store.
88
+ * @returns A new immutable filter.
89
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
90
+ */
31
91
  static from(keys: Iterable<BytesLike>): BinaryFuse16;
92
+ /**
93
+ * Restores a filter from its {@link BinaryFuse16.toBytes} serialization.
94
+ *
95
+ * @param bytes - The serialized filter.
96
+ * @returns The reconstructed filter.
97
+ */
32
98
  static fromBytes(bytes: Uint8Array): BinaryFuse16;
33
99
  }
34
100
  //#endregion
@@ -1,6 +1,8 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.js";
2
2
  //#region src/fuse/fuse.d.ts
3
+ /** Thrown when binary fuse construction fails to converge on the key set. */
3
4
  declare class BinaryFuseBuildError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
4
6
  override readonly name = "BinaryFuseBuildError";
5
7
  }
6
8
  interface FuseParams {
@@ -15,20 +17,84 @@ interface FuseState {
15
17
  params: FuseParams;
16
18
  size: number;
17
19
  }
20
+ /**
21
+ * Shared behavior for the static binary fuse filters: an immutable,
22
+ * space-efficient membership filter built once from a fixed key set.
23
+ */
18
24
  declare abstract class BinaryFuse {
19
25
  #private;
20
26
  protected constructor(state: FuseState);
27
+ /** Number of distinct keys the filter was built from. */
21
28
  get size(): number;
29
+ /** Actual bits stored per key (`0` for an empty filter). */
22
30
  get bitsPerKey(): number;
31
+ /**
32
+ * Serializes the filter to a portable little-endian byte layout.
33
+ *
34
+ * @returns The serialized filter, readable by the matching `fromBytes`.
35
+ */
23
36
  toBytes(): Uint8Array;
37
+ /**
38
+ * Tests whether a key is in the set.
39
+ *
40
+ * @param key - The key to test.
41
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
42
+ */
24
43
  has(key: BytesLike): boolean;
25
44
  }
45
+ /**
46
+ * A static 8-bit binary fuse filter: built once from a key set, then immutable.
47
+ * The most space-efficient option (~9 bits/key at ~0.39% false-positive rate).
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
52
+ * filter.has("alice"); // true
53
+ * filter.size; // 3
54
+ * ```
55
+ */
26
56
  declare class BinaryFuse8 extends BinaryFuse {
57
+ /**
58
+ * Builds a filter from the given keys; duplicates are ignored.
59
+ *
60
+ * @param keys - The complete set of keys to store.
61
+ * @returns A new immutable filter.
62
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
63
+ */
27
64
  static from(keys: Iterable<BytesLike>): BinaryFuse8;
65
+ /**
66
+ * Restores a filter from its {@link BinaryFuse8.toBytes} serialization.
67
+ *
68
+ * @param bytes - The serialized filter.
69
+ * @returns The reconstructed filter.
70
+ */
28
71
  static fromBytes(bytes: Uint8Array): BinaryFuse8;
29
72
  }
73
+ /**
74
+ * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
75
+ * space (~18 bits/key) for a far lower false-positive rate (~1/65536).
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const filter = BinaryFuse16.from(["alice", "bob", "carol"]);
80
+ * filter.has("alice"); // true
81
+ * ```
82
+ */
30
83
  declare class BinaryFuse16 extends BinaryFuse {
84
+ /**
85
+ * Builds a filter from the given keys; duplicates are ignored.
86
+ *
87
+ * @param keys - The complete set of keys to store.
88
+ * @returns A new immutable filter.
89
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
90
+ */
31
91
  static from(keys: Iterable<BytesLike>): BinaryFuse16;
92
+ /**
93
+ * Restores a filter from its {@link BinaryFuse16.toBytes} serialization.
94
+ *
95
+ * @param bytes - The serialized filter.
96
+ * @returns The reconstructed filter.
97
+ */
32
98
  static fromBytes(bytes: Uint8Array): BinaryFuse16;
33
99
  }
34
100
  //#endregion
@@ -3,7 +3,9 @@ import { i as hash128KeyInto, n as readHeader, r as writeHeader, t as Serializat
3
3
  const ARITY = 3;
4
4
  const TYPE_FUSE8 = 3;
5
5
  const TYPE_FUSE16 = 4;
6
+ /** Thrown when binary fuse construction fails to converge on the key set. */
6
7
  var BinaryFuseBuildError = class extends Error {
8
+ /** Discriminates this error from other `Error`s. */
7
9
  name = "BinaryFuseBuildError";
8
10
  };
9
11
  let RLO = 0;
@@ -230,6 +232,10 @@ function fuseStateFromBytes(bytes, expectedType) {
230
232
  size
231
233
  };
232
234
  }
235
+ /**
236
+ * Shared behavior for the static binary fuse filters: an immutable,
237
+ * space-efficient membership filter built once from a fixed key set.
238
+ */
233
239
  var BinaryFuse = class {
234
240
  #fp;
235
241
  #seed;
@@ -246,12 +252,19 @@ var BinaryFuse = class {
246
252
  this.#segCountLen = state.params.segCountLen;
247
253
  this.#size = state.size;
248
254
  }
255
+ /** Number of distinct keys the filter was built from. */
249
256
  get size() {
250
257
  return this.#size;
251
258
  }
259
+ /** Actual bits stored per key (`0` for an empty filter). */
252
260
  get bitsPerKey() {
253
261
  return this.#size === 0 ? 0 : this.#fp.byteLength * 8 / this.#size;
254
262
  }
263
+ /**
264
+ * Serializes the filter to a portable little-endian byte layout.
265
+ *
266
+ * @returns The serialized filter, readable by the matching `fromBytes`.
267
+ */
255
268
  toBytes() {
256
269
  const laneBytes = new Uint8Array(this.#fp.buffer, this.#fp.byteOffset, this.#fp.byteLength);
257
270
  const body = new Uint8Array(16 + laneBytes.length);
@@ -267,6 +280,12 @@ var BinaryFuse = class {
267
280
  flags: 0
268
281
  }, body);
269
282
  }
283
+ /**
284
+ * Tests whether a key is in the set.
285
+ *
286
+ * @param key - The key to test.
287
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
288
+ */
270
289
  has(key) {
271
290
  if (this.#fp.length === 0) return false;
272
291
  hash128KeyInto(key, 0, scratchHash);
@@ -281,18 +300,65 @@ var BinaryFuse = class {
281
300
  return ((mlo ^ mhi) & mask) === (((this.#fp[p0] ?? 0) ^ (this.#fp[p1] ?? 0) ^ (this.#fp[p2] ?? 0)) & mask);
282
301
  }
283
302
  };
303
+ /**
304
+ * A static 8-bit binary fuse filter: built once from a key set, then immutable.
305
+ * The most space-efficient option (~9 bits/key at ~0.39% false-positive rate).
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
310
+ * filter.has("alice"); // true
311
+ * filter.size; // 3
312
+ * ```
313
+ */
284
314
  var BinaryFuse8 = class BinaryFuse8 extends BinaryFuse {
315
+ /**
316
+ * Builds a filter from the given keys; duplicates are ignored.
317
+ *
318
+ * @param keys - The complete set of keys to store.
319
+ * @returns A new immutable filter.
320
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
321
+ */
285
322
  static from(keys) {
286
323
  return new BinaryFuse8(buildState(keys, (n) => new Uint8Array(n)));
287
324
  }
325
+ /**
326
+ * Restores a filter from its {@link BinaryFuse8.toBytes} serialization.
327
+ *
328
+ * @param bytes - The serialized filter.
329
+ * @returns The reconstructed filter.
330
+ */
288
331
  static fromBytes(bytes) {
289
332
  return new BinaryFuse8(fuseStateFromBytes(bytes, TYPE_FUSE8));
290
333
  }
291
334
  };
335
+ /**
336
+ * A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
337
+ * space (~18 bits/key) for a far lower false-positive rate (~1/65536).
338
+ *
339
+ * @example
340
+ * ```ts
341
+ * const filter = BinaryFuse16.from(["alice", "bob", "carol"]);
342
+ * filter.has("alice"); // true
343
+ * ```
344
+ */
292
345
  var BinaryFuse16 = class BinaryFuse16 extends BinaryFuse {
346
+ /**
347
+ * Builds a filter from the given keys; duplicates are ignored.
348
+ *
349
+ * @param keys - The complete set of keys to store.
350
+ * @returns A new immutable filter.
351
+ * @throws {@link BinaryFuseBuildError} if construction fails to converge.
352
+ */
293
353
  static from(keys) {
294
354
  return new BinaryFuse16(buildState(keys, (n) => new Uint16Array(n)));
295
355
  }
356
+ /**
357
+ * Restores a filter from its {@link BinaryFuse16.toBytes} serialization.
358
+ *
359
+ * @param bytes - The serialized filter.
360
+ * @returns The reconstructed filter.
361
+ */
296
362
  static fromBytes(bytes) {
297
363
  return new BinaryFuse16(fuseStateFromBytes(bytes, TYPE_FUSE16));
298
364
  }
package/dist/index.cjs CHANGED
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#endregion
2
3
  //#region src/index.ts
3
- const VERSION = "0.1.0";
4
+ /** The installed `distillate` package version. */
5
+ const VERSION = "0.1.2";
4
6
  //#endregion
5
7
  exports.VERSION = VERSION;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  //#region src/index.d.ts
2
- declare const VERSION = "0.1.0";
2
+ /** The installed `distillate` package version. */
3
+ declare const VERSION: string;
3
4
  //#endregion
4
5
  export { VERSION };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  //#region src/index.d.ts
2
- declare const VERSION = "0.1.0";
2
+ /** The installed `distillate` package version. */
3
+ declare const VERSION: string;
3
4
  //#endregion
4
5
  export { VERSION };
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
+ //#endregion
1
2
  //#region src/index.ts
2
- const VERSION = "0.1.0";
3
+ /** The installed `distillate` package version. */
4
+ const VERSION = "0.1.2";
3
5
  //#endregion
4
6
  export { VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "distillate",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Probabilistic data structures for JavaScript. Space-efficient, approximate-membership filters (Bloom, Blocked Bloom, Binary Fuse) with tunable error and a portable binary format; zero dependencies, universal.",
5
5
  "keywords": [
6
6
  "bloom-filter",
@@ -76,9 +76,12 @@
76
76
  "@commitlint/cli": "^21.2.1",
77
77
  "@commitlint/config-conventional": "^21.2.0",
78
78
  "@eslint/js": "^10.0.1",
79
+ "@microsoft/api-extractor": "^7.58.12",
79
80
  "@types/node": "^26.1.2",
81
+ "@vitest/coverage-v8": "^4.1.10",
80
82
  "eslint": "^10.8.0",
81
83
  "eslint-config-prettier": "^10.1.8",
84
+ "eslint-plugin-tsdoc": "^0.5.2",
82
85
  "fast-check": "^4.9.0",
83
86
  "husky": "^9.1.7",
84
87
  "lint-staged": "^17.2.0",
@@ -87,6 +90,8 @@
87
90
  "publint": "^0.3.22",
88
91
  "tsdown": "^0.22.14",
89
92
  "tsx": "^4.23.1",
93
+ "typedoc": "^0.28.20",
94
+ "typedoc-plugin-markdown": "^4.12.0",
90
95
  "typescript": "^5.9.3",
91
96
  "typescript-eslint": "^8.65.0",
92
97
  "vitest": "^4.1.10"
@@ -100,6 +105,10 @@
100
105
  "check": "publint --strict && attw --pack . --profile node16",
101
106
  "attw": "attw --pack . --profile node16",
102
107
  "test": "vitest run",
108
+ "coverage": "vitest run --coverage",
109
+ "api:report": "node scripts/api-extractor.mjs --local",
110
+ "api:check": "node scripts/api-extractor.mjs",
111
+ "docs:api": "typedoc",
103
112
  "changeset": "changeset",
104
113
  "version": "changeset version",
105
114
  "release": "changeset publish",