distillate 0.10.0 → 0.12.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.
@@ -0,0 +1,173 @@
1
+ import { t as BytesLike } from "../bytes-DCuYtUVS.js";
2
+ import { a as ReservedBitsError, c as UnknownHashVariantError, l as UnknownVersionError, n as ChecksumError, o as SerializationError, r as FilterJSON, s as TruncatedError, t as BadMagicError } from "../serialize--xR6vGSW.js";
3
+ import { t as ParamError } from "../params-DnqJBqLS.js";
4
+ //#region src/scalable/scalable.d.ts
5
+ /**
6
+ * Thrown by {@link ScalableBloomFilter.union} when the two filters were built
7
+ * with different settings. Stage `i` has the same geometry in both only when
8
+ * every setting matches, so no stage-by-stage merge exists otherwise.
9
+ */
10
+ export declare class ScalableParamMismatchError extends Error {
11
+ /** Discriminates this error from other `Error`s. */
12
+ override readonly name = "ScalableParamMismatchError";
13
+ }
14
+ /** Settings for a {@link ScalableBloomFilter}. */
15
+ interface ScalableBloomParams {
16
+ /** Keys the first stage holds before the next one opens. */
17
+ n: number;
18
+ /** Target false-positive rate for the whole chain, e.g. `0.01` for 1%. */
19
+ epsilon: number;
20
+ /** Capacity multiplier from one stage to the next; defaults to `2`. */
21
+ growth?: number;
22
+ /**
23
+ * Multiplier on each stage's false-positive target relative to the one
24
+ * before; defaults to `0.85`. Lower spends more bits per stage to open
25
+ * fewer of them.
26
+ */
27
+ tightening?: number;
28
+ /** Hash seed; defaults to `0`. */
29
+ seed?: number;
30
+ }
31
+ /** The optional {@link ScalableBloomParams}, as `create` and `from` take them. */
32
+ type ScalableBloomOptions = Omit<ScalableBloomParams, "n" | "epsilon">;
33
+ /**
34
+ * A Bloom filter that grows with its key count and keeps its false-positive
35
+ * rate under `epsilon`, for when the number of keys is not known up front.
36
+ *
37
+ * It is a chain of Bloom stages. Stage `i` holds `n * growth ** i` keys at a
38
+ * false-positive target of `epsilon * (1 - tightening) * tightening ** i`;
39
+ * those targets sum to at most `epsilon`, however many stages open.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const seen = ScalableBloomFilter.create(1000, 0.01);
44
+ * seen.add("alice");
45
+ * seen.has("alice"); // true
46
+ * ```
47
+ */
48
+ export declare class ScalableBloomFilter {
49
+ #private;
50
+ /**
51
+ * Creates a filter whose first stage holds `n` keys, for a chain-wide
52
+ * false-positive target.
53
+ *
54
+ * @param n - Keys the first stage holds.
55
+ * @param epsilon - Target false-positive rate for the whole chain.
56
+ * @param options - Optional growth, tightening and seed.
57
+ * @returns A new, empty filter.
58
+ */
59
+ static create(n: number, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
60
+ /**
61
+ * Builds a filter from `keys`, with the first stage sized for their count;
62
+ * it keeps growing as more keys arrive. Duplicates only oversize the first
63
+ * stage, since a key already held is not counted again.
64
+ *
65
+ * @param keys - The keys to insert.
66
+ * @param epsilon - Target false-positive rate for the whole chain.
67
+ * @param options - Optional growth, tightening and seed.
68
+ * @returns A new filter containing every key.
69
+ */
70
+ static from(keys: Iterable<BytesLike>, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
71
+ /**
72
+ * @param params - The filter's settings.
73
+ * @throws {@link ParamError} if a setting is out of range, or the first
74
+ * stage would need more than `2^32 - 1` bits.
75
+ */
76
+ constructor({ n, epsilon, growth, tightening, seed }: ScalableBloomParams);
77
+ /**
78
+ * Adds a key. A key the filter already holds is not counted again, so
79
+ * duplicates never use up a stage's capacity.
80
+ *
81
+ * @param key - The key to insert, as a string or bytes.
82
+ * @throws RangeError if the next stage would need more than `2^32 - 1`
83
+ * bits; the filter is left unchanged and still answers queries.
84
+ */
85
+ add(key: BytesLike): void;
86
+ /**
87
+ * Tests whether a key is in the set.
88
+ *
89
+ * @param key - The key to test.
90
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
91
+ */
92
+ has(key: BytesLike): boolean;
93
+ /**
94
+ * Estimates the chain's current false-positive rate from each stage's actual
95
+ * fill: a key is a false positive if any stage wrongly holds it, so this is
96
+ * `1 - prod(1 - (setBits / m) ** k)`. It rises as keys are added and stays
97
+ * under `epsilon` while every stage is within its capacity.
98
+ *
99
+ * @returns The estimated false-positive rate, in `[0, 1]`.
100
+ */
101
+ rate(): number;
102
+ /**
103
+ * Returns a new filter holding every key of this filter and `other`. Stage
104
+ * `i` of the result is the OR of both inputs' stage `i`, and the result has
105
+ * the longer of the two chains. Each stage's count becomes the smaller of
106
+ * its capacity and the two counts summed: an upper bound, so the merged
107
+ * chain opens its next stage early rather than late.
108
+ *
109
+ * @param other - A filter built with identical settings.
110
+ * @returns A new filter; neither input is changed.
111
+ * @throws {@link ScalableParamMismatchError} if any setting, or the
112
+ * geometry of a stage both filters have, differs.
113
+ */
114
+ union(other: ScalableBloomFilter): ScalableBloomFilter;
115
+ /**
116
+ * Tests structural equality: identical settings, the same stages with the
117
+ * same counts, and identical bits, which is exactly when the two serialize
118
+ * to the same bytes. Which stage a key lands in depends on when it arrived,
119
+ * so two filters given the same keys in a different order can be unequal.
120
+ *
121
+ * @param other - The filter to compare against.
122
+ * @returns `true` if the two filters are identical.
123
+ */
124
+ equals(other: ScalableBloomFilter): boolean;
125
+ /**
126
+ * Restores a filter from its {@link ScalableBloomFilter.toBytes} serialization.
127
+ *
128
+ * @param bytes - A frame produced by `toBytes`.
129
+ * @returns The reconstructed filter.
130
+ * @throws {@link SerializationError} (or a subclass) if the frame is malformed.
131
+ */
132
+ static fromBytes(bytes: Uint8Array): ScalableBloomFilter;
133
+ /**
134
+ * Serializes the filter to a portable little-endian frame, DSTL type 6.
135
+ *
136
+ * @returns The serialized filter, readable by {@link ScalableBloomFilter.fromBytes}.
137
+ */
138
+ toBytes(): Uint8Array;
139
+ /**
140
+ * Serializes the filter to a JSON-friendly envelope wrapping the base64 of
141
+ * {@link ScalableBloomFilter.toBytes}.
142
+ *
143
+ * @returns The JSON envelope.
144
+ */
145
+ toJSON(): FilterJSON;
146
+ /**
147
+ * Restores a filter from its {@link ScalableBloomFilter.toJSON} envelope.
148
+ *
149
+ * @param value - The JSON envelope.
150
+ * @returns The reconstructed filter.
151
+ */
152
+ static fromJSON(value: unknown): ScalableBloomFilter;
153
+ /** Keys added that the filter did not already hold. */
154
+ get count(): number;
155
+ /** Keys the open stages hold between them before the next one opens. */
156
+ get capacity(): number;
157
+ /** Number of open stages. */
158
+ get stages(): number;
159
+ /** Total bits across every stage. */
160
+ get m(): number;
161
+ /** Analytic design bits-per-key, `m / capacity`. */
162
+ get bitsPerKey(): number;
163
+ /** Hash seed shared by every stage. */
164
+ get seed(): number;
165
+ /** Target false-positive rate for the whole chain. */
166
+ get epsilon(): number;
167
+ /** Capacity multiplier from one stage to the next. */
168
+ get growth(): number;
169
+ /** False-positive target multiplier from one stage to the next. */
170
+ get tightening(): number;
171
+ }
172
+ //#endregion
173
+ export { BadMagicError, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, type ScalableBloomOptions, type ScalableBloomParams, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
@@ -0,0 +1,337 @@
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
+ let k = 0;
159
+ for (const s of stages) {
160
+ this.#stages.push(s);
161
+ if (s.k > k) k = s.k;
162
+ }
163
+ if (k > this.#probes.length) this.#probes = new Uint32Array(k);
164
+ return stages.reduce((_, s) => s);
165
+ }
166
+ #probe(stage) {
167
+ probeLanesInto(this.#hash.w0, this.#hash.w1, stage.k, stage.m, this.#probes);
168
+ }
169
+ #holds(stage) {
170
+ const { w0, w1 } = this.#hash;
171
+ for (let i = 0; i < stage.k; i++) if (!stage.bits.get(probeAt(w0, w1, i, stage.m))) return false;
172
+ return true;
173
+ }
174
+ #anyHolds() {
175
+ for (let i = this.#stages.length - 1; i >= 0; i--) {
176
+ const stage = this.#stages[i];
177
+ if (stage && this.#holds(stage)) return true;
178
+ }
179
+ return false;
180
+ }
181
+ add(key) {
182
+ hash128KeyInto(key, this.#seed, this.#hash);
183
+ if (this.#anyHolds()) return;
184
+ if (this.#newest.count >= this.#newest.capacity) {
185
+ const i = this.#stages.length;
186
+ const next = this.#geometry(i);
187
+ 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`);
188
+ this.#newest = this.#open(next);
189
+ }
190
+ const stage = this.#newest;
191
+ this.#probe(stage);
192
+ for (let i = 0; i < stage.k; i++) stage.bits.set(this.#probes[i] ?? 0);
193
+ stage.count++;
194
+ }
195
+ has(key) {
196
+ hash128KeyInto(key, this.#seed, this.#hash);
197
+ return this.#anyHolds();
198
+ }
199
+ rate() {
200
+ let miss = 1;
201
+ for (const s of this.#stages) miss *= 1 - (s.bits.count() / s.m) ** s.k;
202
+ return 1 - miss;
203
+ }
204
+ union(other) {
205
+ this.#assertMergeable(other);
206
+ const [longer, shorter] = this.#stages.length >= other.#stages.length ? [this.#stages, other.#stages] : [other.#stages, this.#stages];
207
+ const stages = longer.map((stage, i) => {
208
+ const bits = new BitSet(stage.m);
209
+ bits.bytes.set(stage.bits.bytes);
210
+ const twin = shorter[i];
211
+ if (twin) {
212
+ const out = bits.bytes;
213
+ const bytes = twin.bits.bytes;
214
+ for (let j = 0; j < out.length; j++) out[j] = (out[j] ?? 0) | (bytes[j] ?? 0);
215
+ }
216
+ return {
217
+ bits,
218
+ m: stage.m,
219
+ k: stage.k,
220
+ capacity: stage.capacity,
221
+ count: Math.min(stage.capacity, stage.count + (twin?.count ?? 0))
222
+ };
223
+ });
224
+ return ScalableBloomFilter.#withStages({
225
+ n: this.#n,
226
+ epsilon: this.#epsilon,
227
+ growth: this.#growth,
228
+ tightening: this.#tightening,
229
+ seed: this.#seed
230
+ }, stages);
231
+ }
232
+ equals(other) {
233
+ if (this.#settingPairs(other).some(([, a, b]) => a !== b)) return false;
234
+ if (this.#stages.length !== other.#stages.length) return false;
235
+ return this.#stages.every((s, i) => {
236
+ const t = other.#stages[i];
237
+ if (!t) return false;
238
+ return s.m === t.m && s.k === t.k && s.capacity === t.capacity && s.count === t.count && bytesEqual(s.bits.bytes, t.bits.bytes);
239
+ });
240
+ }
241
+ static fromBytes(bytes) {
242
+ const { type, flags, body } = readHeader(bytes);
243
+ if (type !== TYPE) throw new SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
244
+ if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
245
+ assertMinBodyLength(body.length, PARAMS_SIZE, "scalable");
246
+ assertParamsPadding(body, 36, PARAMS_SIZE, "scalable");
247
+ const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
248
+ const stageCount = view.getUint32(32, true);
249
+ if (stageCount === 0) throw new SerializationError("scalable: frame declares no stages");
250
+ const tableEnd = PARAMS_SIZE + ENTRY_SIZE * stageCount;
251
+ assertMinBodyLength(body.length, tableEnd, "scalable");
252
+ const entries = readTable(view, stageCount);
253
+ const expected = entries.reduce((sum, e) => sum + padded8(Math.ceil(e.m / 8)), tableEnd);
254
+ assertBodyLength(body.length, expected, "scalable");
255
+ const stages = readStages(body, entries, tableEnd);
256
+ try {
257
+ return ScalableBloomFilter.#withStages({
258
+ n: view.getUint32(0, true),
259
+ seed: view.getUint32(4, true),
260
+ epsilon: view.getFloat64(8, true),
261
+ growth: view.getFloat64(16, true),
262
+ tightening: view.getFloat64(24, true)
263
+ }, stages);
264
+ } catch (err) {
265
+ if (err instanceof ParamError) throw new SerializationError(`scalable: ${err.message}`);
266
+ throw err;
267
+ }
268
+ }
269
+ static #withStages(params, stages) {
270
+ restoring = stages;
271
+ try {
272
+ return new ScalableBloomFilter(params);
273
+ } finally {
274
+ restoring = void 0;
275
+ }
276
+ }
277
+ toBytes() {
278
+ const payload = this.#stages.reduce((sum, s) => sum + ENTRY_SIZE + padded8(s.bits.bytes.length), 0);
279
+ return writeFrame({
280
+ version: 5,
281
+ type: TYPE,
282
+ flags: 0
283
+ }, PARAMS_SIZE, payload, (body, view) => {
284
+ view.setUint32(0, this.#n, true);
285
+ view.setUint32(4, this.#seed, true);
286
+ view.setFloat64(8, this.#epsilon, true);
287
+ view.setFloat64(16, this.#growth, true);
288
+ view.setFloat64(24, this.#tightening, true);
289
+ view.setUint32(32, this.#stages.length, true);
290
+ let at = PARAMS_SIZE + ENTRY_SIZE * this.#stages.length;
291
+ this.#stages.forEach((s, i) => {
292
+ const entry = PARAMS_SIZE + ENTRY_SIZE * i;
293
+ view.setUint32(entry, s.m, true);
294
+ view.setUint16(entry + 4, s.k, true);
295
+ view.setUint32(entry + 8, s.capacity, true);
296
+ view.setUint32(entry + 12, s.count, true);
297
+ body.set(s.bits.bytes, at);
298
+ at += padded8(s.bits.bytes.length);
299
+ });
300
+ });
301
+ }
302
+ toJSON() {
303
+ return toJSONEnvelope(this.toBytes());
304
+ }
305
+ static fromJSON(value) {
306
+ return ScalableBloomFilter.fromBytes(fromJSONEnvelope(value));
307
+ }
308
+ get count() {
309
+ return this.#stages.reduce((sum, s) => sum + s.count, 0);
310
+ }
311
+ get capacity() {
312
+ return this.#stages.reduce((sum, s) => sum + s.capacity, 0);
313
+ }
314
+ get stages() {
315
+ return this.#stages.length;
316
+ }
317
+ get m() {
318
+ return this.#stages.reduce((sum, s) => sum + s.m, 0);
319
+ }
320
+ get bitsPerKey() {
321
+ return this.m / this.capacity;
322
+ }
323
+ get seed() {
324
+ return this.#seed;
325
+ }
326
+ get epsilon() {
327
+ return this.#epsilon;
328
+ }
329
+ get growth() {
330
+ return this.#growth;
331
+ }
332
+ get tightening() {
333
+ return this.#tightening;
334
+ }
335
+ };
336
+ //#endregion
337
+ export { BadMagicError, ChecksumError, ParamError, ReservedBitsError, ScalableBloomFilter, ScalableParamMismatchError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "distillate",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
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",
@@ -71,6 +71,22 @@
71
71
  "import": "./dist/frame/index.js",
72
72
  "require": "./dist/frame/index.cjs"
73
73
  },
74
+ "./scalable": {
75
+ "types": {
76
+ "import": "./dist/scalable/index.d.ts",
77
+ "require": "./dist/scalable/index.d.cts"
78
+ },
79
+ "import": "./dist/scalable/index.js",
80
+ "require": "./dist/scalable/index.cjs"
81
+ },
82
+ "./cuckoo": {
83
+ "types": {
84
+ "import": "./dist/cuckoo/index.d.ts",
85
+ "require": "./dist/cuckoo/index.d.cts"
86
+ },
87
+ "import": "./dist/cuckoo/index.js",
88
+ "require": "./dist/cuckoo/index.cjs"
89
+ },
74
90
  "./package.json": "./package.json"
75
91
  },
76
92
  "main": "./dist/index.cjs",