bloom-filter-driver 1.0.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.
package/README.md ADDED
@@ -0,0 +1,321 @@
1
+ # Bloom Filter Driver (TypeScript / Node.js HTTP Client)
2
+
3
+ **BloomFilterDriver** là thư viện client (SDK) nhỏ gọn được viết bằng TypeScript, đóng vai trò cầu nối giao tiếp giữa các ứng dụng Node.js/TypeScript (như `BloomFilterApplication`) và máy chủ xử lý dữ liệu Bloom Filter (`BloomFilterProcess`) thông qua giao thức HTTP REST.
4
+
5
+ Thư viện được thiết kế độc lập, tuân thủ nguyên tắc kiến trúc sạch (Clean Architecture) với các lớp phân tách rõ ràng, hỗ trợ đầy đủ kiểu dữ liệu TypeScript (Type Safety), cơ chế kiểm tra tính hợp lệ dữ liệu (Validation) chặt chẽ và khả năng quản lý vòng đời request (Timeout & Cancellation) thông qua `AbortSignal`.
6
+
7
+ ---
8
+
9
+ ## 📦 Cài đặt
10
+
11
+ Bạn có thể cài đặt thư viện thông qua các công cụ quản lý gói thông dụng:
12
+
13
+ ```bash
14
+ npm install bloom-filter-driver
15
+ # hoặc
16
+ yarn add bloom-filter-driver
17
+ # hoặc
18
+ pnpm add bloom-filter-driver
19
+ ```
20
+
21
+ > **Lưu ý khi phát triển cục bộ (Local Development):**
22
+ > Nếu bạn đang làm việc trực tiếp trong repository chứa mã nguồn của driver, hãy cài đặt các gói phụ thuộc và biên dịch mã nguồn trước khi sử dụng hoặc liên kết (link) tới ứng dụng:
23
+ > ```bash
24
+ > npm install
25
+ > npm run build
26
+ > ```
27
+
28
+ ---
29
+
30
+ ## 🚀 Khởi tạo & Cấu hình Driver
31
+
32
+ Để sử dụng, bạn cần khởi tạo một đối tượng `BloomFilterDriver` với cấu hình `BloomDriverConfig`.
33
+
34
+ ```typescript
35
+ import { BloomFilterDriver } from 'bloom-filter-driver';
36
+
37
+ const driver = new BloomFilterDriver({
38
+ baseUrl: 'http://localhost:8080', // Địa chỉ gốc của máy chủ BloomFilterProcess
39
+ timeoutMs: 5000, // (Tùy chọn) Thời gian chờ tối đa cho mỗi request (mặc định: 5000ms)
40
+ // fetchImpl: customFetch // (Tùy chọn) Hàm fetch tùy chỉnh (hữu ích khi viết mock/unit test)
41
+ });
42
+ ```
43
+
44
+ ### Quản lý vòng đời Request (Timeout & Cancellation)
45
+
46
+ Tất cả các phương thức API trong driver đều hỗ trợ tham số tùy chọn `options?: { signal?: AbortSignal }`. Điều này cho phép bạn:
47
+ - Tự động hủy các request vượt quá thời gian `timeoutMs` đã cấu hình.
48
+ - Chủ động hủy request từ bên ngoài thông qua `AbortController`.
49
+
50
+ ```typescript
51
+ const controller = new AbortController();
52
+
53
+ try {
54
+ await driver.add(
55
+ { bfName: 'users-bf', data: 'alice@example.com' },
56
+ { signal: controller.signal }
57
+ );
58
+ } catch (error) {
59
+ // Xử lý khi request bị hủy hoặc lỗi
60
+ }
61
+
62
+ // Khi muốn chủ động hủy request đang thực thi:
63
+ controller.abort();
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🏗️ Kiến trúc & Thiết kế
69
+
70
+ Driver được phân chia thành các module chuyên biệt nhằm đảm bảo tính dễ bảo trì và mở rộng:
71
+
72
+ ```
73
+ ┌─────────────────────────────────────────────────────────┐
74
+ │ BloomFilterDriver │ (Lớp Facade tổng quan)
75
+ └────────────┬───────────────────────────────┬────────────┘
76
+ │ │
77
+ ▼ ▼
78
+ ┌─────────────────────────┐ ┌─────────────────────────┐
79
+ │ BloomApiClient │ │ RangeApiClient │ (Lớp Client nghiệp vụ)
80
+ └────────────┬────────────┘ └────────────┬────────────┘
81
+ │ │
82
+ └───────────────┬───────────────┘
83
+
84
+
85
+ ┌─────────────────────────────────────────────────────────┐
86
+ │ HttpClient │ (Lớp xử lý HTTP & Timeout)
87
+ └────────────────────────────┬────────────────────────────┘
88
+
89
+
90
+ ┌─────────────────────────────────────────────────────────┐
91
+ │ DriverValidator │ (Lớp kiểm tra dữ liệu)
92
+ └─────────────────────────────────────────────────────────┘
93
+ ```
94
+
95
+ - **`BloomFilterDriver`**: Lớp giao diện chính (Facade) tổng hợp toàn bộ các phương thức thao tác với Bloom Filter và RangeLH.
96
+ - **`BloomApiClient`**: Quản lý các endpoint liên quan đến cấu trúc Bloom Filter (`/bloom/*`).
97
+ - **`RangeApiClient`**: Quản lý các endpoint liên quan đến cấu trúc phân cấp/chỉ mục dải RangeLH (`/rangelh/*`).
98
+ - **`HttpClient`**: Đóng gói hàm `fetch`, xử lý nối chuỗi URL, thiết lập HTTP headers, quản lý `AbortController` và chuẩn hóa lỗi HTTP.
99
+ - **`DriverValidator`**: Kiểm tra tính hợp lệ của dữ liệu đầu vào (tên bộ lọc, dải giá trị, xác suất, tham số cấu hình) trước khi gửi request tới máy chủ.
100
+
101
+ ---
102
+
103
+ ## 📚 Tài liệu API Tham khảo (API Reference)
104
+
105
+ ---
106
+
107
+ ### 1. Bloom Filter API (`BloomApiClient`)
108
+
109
+ Các API chuyên biệt dành cho bộ lọc Bloom thông thường (Standard Bloom Filter) và bộ lọc Bloom có đếm (Counting Bloom Filter).
110
+
111
+ #### `createBloom(payload: BloomCreatePayload, options?: DriverRequestOptions): Promise<void>`
112
+ Khởi tạo và cấu hình một bộ lọc Bloom mới trên máy chủ trước khi sử dụng.
113
+
114
+ ```typescript
115
+ await driver.createBloom({
116
+ bfName: 'users-bf', // Tên định danh của bộ lọc
117
+ expectedValues: 100000, // Số lượng phần tử dự kiến thêm vào
118
+ bloomType: 'standard', // 'standard' | 'counting' (mặc định: 'standard')
119
+ falsePositiveProbability: 0.01, // (Tùy chọn) Xác suất dương tính giả mong muốn (ví dụ: 1%)
120
+ counterBits: 4 // (Tùy chọn) Số bit cho mỗi bộ đếm (chỉ dùng khi bloomType là 'counting', từ 1-8)
121
+ });
122
+ ```
123
+ > *Lưu ý:* Thư viện cung cấp hàm alias `configureBloom` tương đương với `createBloom` nhằm mục đích tương thích ngược. Khuyến nghị sử dụng `createBloom` cho các dự án mới.
124
+
125
+ #### `add(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>`
126
+ Thêm một phần tử (chuỗi dữ liệu) vào bộ lọc Bloom.
127
+
128
+ ```typescript
129
+ await driver.add({
130
+ bfName: 'users-bf',
131
+ data: 'alice@example.com'
132
+ });
133
+ ```
134
+
135
+ #### `mightContain(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<boolean>`
136
+ Kiểm tra xem một phần tử **có thể** tồn tại trong bộ lọc Bloom hay không.
137
+
138
+ ```typescript
139
+ const isPresent = await driver.mightContain({
140
+ bfName: 'users-bf',
141
+ data: 'alice@example.com'
142
+ });
143
+
144
+ if (isPresent) {
145
+ console.log('Phần tử CÓ THỂ tồn tại (có rủi ro dương tính giả theo xác suất đã cấu hình).');
146
+ } else {
147
+ console.log('Phần tử CHẮC CHẮN KHÔNG tồn tại trong bộ lọc.');
148
+ }
149
+ ```
150
+
151
+ #### `remove(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>`
152
+ Xóa một phần tử khỏi bộ lọc Bloom. (Yêu cầu bộ lọc phải được khởi tạo với `bloomType: 'counting'`).
153
+
154
+ ```typescript
155
+ await driver.remove({
156
+ bfName: 'users-bf',
157
+ data: 'alice@example.com'
158
+ });
159
+ ```
160
+
161
+ #### `benchLoadSequentialKeys(payload: BloomBenchSequentialPayload, options?: DriverRequestOptions): Promise<void>`
162
+ API hỗ trợ nạp dữ liệu giả lập hàng loạt (bulk insert) trực tiếp trên máy chủ với hiệu năng cao (chỉ tốn 1 lần gọi HTTP và 1 lần lưu trữ/persist). Thường dùng cho mục đích benchmark hoặc khởi tạo dữ liệu mẫu.
163
+
164
+ ```typescript
165
+ await driver.benchLoadSequentialKeys({
166
+ bfName: 'users-bf',
167
+ from: 1, // (Tùy chọn) Chỉ số bắt đầu (mặc định: 1)
168
+ to: 100000, // Chỉ số kết thúc
169
+ keyWidth: 10 // (Tùy chọn) Độ rộng chuỗi số được đệm zero (zero-padding, mặc định: 10)
170
+ });
171
+ // Máy chủ sẽ tự động nạp các key dạng: "k-0000000001", "k-0000000002", ..., "k-0000100000"
172
+ ```
173
+
174
+ ---
175
+
176
+ ### 2. RangeLH API (`RangeApiClient`)
177
+
178
+ RangeLH (Range Linear Hashing) là cấu trúc chỉ mục nâng cao phân bổ theo `key`, hỗ trợ tối ưu hóa các truy vấn điểm (point query), truy vấn khoảng (range query) và tổng hợp dữ liệu (aggregation).
179
+
180
+ #### `createRange(payload: RangeCreatePayload, options?: DriverRequestOptions): Promise<void>`
181
+ Khởi tạo cấu trúc chỉ mục RangeLH với các tham số tinh chỉnh chuyên sâu.
182
+
183
+ ```typescript
184
+ await driver.createRange({
185
+ bfName: 'orders-range',
186
+ config: {
187
+ masterInitNumBucket: 2, // Số lượng bucket khởi tạo ban đầu cho Master Index
188
+ masterSplitPolicy: 0.5, // Tỷ lệ/Chính sách tách bucket (split policy)
189
+ expectedNItems: 1000000, // Tổng số lượng phần tử dự kiến
190
+ fpProbBloomRF: 0.01, // Xác suất dương tính giả cho các Bloom Filter con
191
+ deltaBloomRF: 10, // Tham số dải delta cho bộ lọc
192
+ keyLength: 20, // Độ dài cố định của chuỗi key (từ 1-64)
193
+ maxBytesString: 8, // Số byte tối đa cho phần chuỗi giá trị (từ 1-8)
194
+ floatScale: 100 // Tỷ lệ nhân (scale) cho dữ liệu số thực
195
+ }
196
+ });
197
+ ```
198
+
199
+ #### `addRange(payload: RangePayload, options?: Driver: RequestOptions): Promise<void>`
200
+ Thêm một cặp định danh/khóa (key) đơn lẻ vào chỉ mục RangeLH.
201
+
202
+ ```typescript
203
+ await driver.addRange({
204
+ bfName: 'orders-range',
205
+ key: '2026-05-17T12:00:00Z'
206
+ });
207
+ ```
208
+
209
+ #### `addRangeBulk(payload: RangeBulkAddPayload, options?: DriverRequestOptions): Promise<void>`
210
+ Thêm nhiều khóa cùng lúc (bulk insert) vào chỉ mục RangeLH, giúp tiết kiệm băng thông và giảm thiểu độ trễ mạng.
211
+
212
+ ```typescript
213
+ await driver.addRangeBulk({
214
+ bfName: 'orders-range',
215
+ keys: [
216
+ '2026-05-17T12:00:00Z',
217
+ '2026-05-17T13:00:00Z',
218
+ '2026-05-17T14:00:00Z'
219
+ ]
220
+ });
221
+ ```
222
+
223
+ #### `removeRange(payload: RangePayload, options?: DriverRequestOptions): Promise<void>`
224
+ Xóa một khóa khỏi chỉ mục RangeLH.
225
+
226
+ ```typescript
227
+ await driver.removeRange({
228
+ bfName: 'orders-range',
229
+ key: '2026-05-17T12:00:00Z'
230
+ });
231
+ ```
232
+
233
+ #### `pointQuery(payload: PointQueryPayload, options?: DriverRequestOptions): Promise<string[]>`
234
+ Truy vấn điểm để lấy ra danh sách các khóa khớp chính xác.
235
+
236
+ ```typescript
237
+ const results = await driver.pointQuery({
238
+ bfName: 'orders-range',
239
+ key: '2026-05-17T12:00:00Z'
240
+ });
241
+ // results: string[] (Danh sách các giá trị/khóa tìm thấy)
242
+ ```
243
+
244
+ #### `rangeQuery(payload: RangeQueryPayload, options?: DriverRequestOptions): Promise<string[]>`
245
+ Truy vấn khoảng để lấy ra danh sách các khóa nằm trong dải từ `from` đến `to`.
246
+
247
+ ```typescript
248
+ const results = await driver.rangeQuery({
249
+ bfName: 'orders-range',
250
+ from: '2026-05-01T00:00:00Z',
251
+ to: '2026-05-31T23:59:59Z'
252
+ });
253
+ // results: string[] (Danh sách các khóa thuộc khoảng thời gian/dải truy vấn)
254
+ ```
255
+
256
+ #### `aggregate(payload: RangeAggregatePayload, options?: DriverRequestOptions): Promise<number>`
257
+ Thực hiện truy vấn tổng hợp (aggregation) dữ liệu trên một dải khóa cụ thể.
258
+
259
+ ```typescript
260
+ const totalOrders = await driver.aggregate({
261
+ bfName: 'orders-range',
262
+ from: '2026-05-01T00:00:00Z',
263
+ to: '2026-05-31T23:59:59Z',
264
+ op: 'count' // Các phép toán hỗ trợ: 'sum' | 'avg' | 'min' | 'max' | 'count'
265
+ });
266
+ console.log(`Tổng số đơn hàng trong tháng 5: ${totalOrders}`);
267
+ ```
268
+
269
+ ---
270
+
271
+ ## ⚠️ Xử lý lỗi (Error Handling)
272
+
273
+ Tất cả các lỗi phát sinh từ driver (bao gồm lỗi kiểm tra dữ liệu đầu vào, lỗi mạng, lỗi timeout hoặc lỗi nghiệp vụ từ phía máy chủ) đều được ném ra dưới dạng đối tượng `BloomDriverError`.
274
+
275
+ ```typescript
276
+ import { BloomFilterDriver, BloomDriverError } from 'bloom-filter-driver';
277
+
278
+ const driver = new BloomFilterDriver({ baseUrl: 'http://localhost:8080' });
279
+
280
+ async function execute() {
281
+ try {
282
+ // Thử gửi dữ liệu không hợp lệ (chuỗi rỗng)
283
+ await driver.add({ bfName: 'users-bf', data: '' });
284
+ } catch (error) {
285
+ if (error instanceof BloomDriverError) {
286
+ console.error('[BloomDriverError] Thông báo lỗi:', error.message);
287
+
288
+ if (error.status) {
289
+ console.error('Mã trạng thái HTTP:', error.status);
290
+ }
291
+ if (error.responseBody) {
292
+ console.error('Nội dung phản hồi từ máy chủ:', error.responseBody);
293
+ }
294
+ } else {
295
+ console.error('[Lỗi không xác định]:', error);
296
+ }
297
+ }
298
+ }
299
+ ```
300
+
301
+ ---
302
+
303
+ ## 🛠️ Build & Publish (Dành cho nhà phát triển)
304
+
305
+ Các lệnh thao tác chính khi tham gia phát triển gói thư viện `bloom-filter-driver`:
306
+
307
+ - **Kiểm tra lỗi cú pháp và định dạng (Lint & Format):**
308
+ ```bash
309
+ npm run lint
310
+ ```
311
+
312
+ - **Biên dịch mã nguồn TypeScript sang JavaScript (thư mục `dist`):**
313
+ ```bash
314
+ npm run build
315
+ ```
316
+
317
+ - **Đóng gói và phát hành lên NPM registry:**
318
+ ```bash
319
+ npm publish
320
+ ```
321
+ *(Lưu ý: Script `prepublishOnly` trong `package.json` sẽ tự động kích hoạt tiến trình `build` trước khi publish để đảm bảo mã nguồn phát hành luôn mới nhất).*
@@ -0,0 +1,13 @@
1
+ import type { BloomBenchSequentialPayload, BloomCreatePayload, BloomDataPayload, DriverRequestOptions } from './types';
2
+ import { DriverValidator } from './DriverValidator';
3
+ import { HttpClient } from './HttpClient';
4
+ export declare class BloomApiClient {
5
+ private readonly http;
6
+ private readonly validator;
7
+ constructor(http: HttpClient, validator: DriverValidator);
8
+ add(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>;
9
+ create(payload: BloomCreatePayload, options?: DriverRequestOptions): Promise<void>;
10
+ benchLoadSequentialKeys(payload: BloomBenchSequentialPayload, options?: DriverRequestOptions): Promise<void>;
11
+ mightContain(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<boolean>;
12
+ remove(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>;
13
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BloomApiClient = void 0;
4
+ class BloomApiClient {
5
+ http;
6
+ validator;
7
+ constructor(http, validator) {
8
+ this.http = http;
9
+ this.validator = validator;
10
+ }
11
+ async add(payload, options) {
12
+ await this.http.post('/bloom/add', {
13
+ bfName: this.validator.bfName(payload.bfName),
14
+ data: this.validator.nonEmpty(payload.data, 'data')
15
+ }, options);
16
+ }
17
+ async create(payload, options) {
18
+ const bloomType = this.validator.bloomType(payload.bloomType);
19
+ const requestBody = {
20
+ bfName: this.validator.bfName(payload.bfName),
21
+ expectedValues: this.validator.positiveInt(payload.expectedValues, 'expectedValues'),
22
+ bloomType
23
+ };
24
+ if (payload.falsePositiveProbability !== undefined) {
25
+ requestBody.falsePositiveProbability = this.validator.probability(payload.falsePositiveProbability, 'falsePositiveProbability');
26
+ }
27
+ if (payload.counterBits !== undefined) {
28
+ requestBody.counterBits = this.validator.counterBits(payload.counterBits, bloomType);
29
+ }
30
+ await this.http.post('/bloom/create', requestBody, options);
31
+ }
32
+ async benchLoadSequentialKeys(payload, options) {
33
+ const requestBody = {
34
+ bfName: this.validator.bfName(payload.bfName),
35
+ to: this.validator.positiveInt(payload.to, 'to')
36
+ };
37
+ if (payload.from !== undefined) {
38
+ requestBody.from = this.validator.positiveInt(payload.from, 'from');
39
+ }
40
+ if (payload.keyWidth !== undefined) {
41
+ requestBody.keyWidth = this.validator.positiveInt(payload.keyWidth, 'keyWidth');
42
+ }
43
+ await this.http.post('/bloom/bench/load-sequential-keys', requestBody, options);
44
+ }
45
+ async mightContain(payload, options) {
46
+ const response = await this.http.get('/bloom/might-contain', {
47
+ bfName: this.validator.bfName(payload.bfName),
48
+ data: this.validator.nonEmpty(payload.data, 'data')
49
+ }, options);
50
+ return response === 'true';
51
+ }
52
+ async remove(payload, options) {
53
+ await this.http.post('/bloom/remove', {
54
+ bfName: this.validator.bfName(payload.bfName),
55
+ data: this.validator.nonEmpty(payload.data, 'data')
56
+ }, options);
57
+ }
58
+ }
59
+ exports.BloomApiClient = BloomApiClient;
@@ -0,0 +1,106 @@
1
+ import type { BloomBenchSequentialPayload, BloomCreatePayload, BloomDataPayload, BloomDriverConfig, DriverRequestOptions, PointQueryPayload, RangeAggregatePayload, RangeBulkAddPayload, RangeCreatePayload, RangePayload, RangeQueryPayload } from './types';
2
+ export declare class BloomFilterDriver {
3
+ private readonly bloomApi;
4
+ private readonly rangeApi;
5
+ /**
6
+ * Create a driver instance for talking to the Bloom Filter process service.
7
+ *
8
+ * @param config Driver configuration (base URL, defaults, timeouts, optional fetch implementation)
9
+ */
10
+ constructor(config: BloomDriverConfig);
11
+ /**
12
+ * Add a value to a Bloom Filter.
13
+ *
14
+ * @param payload Target bloom filter name and data to add
15
+ * @param options Optional request options (supports cancellation via `options.signal`)
16
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
17
+ */
18
+ add(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>;
19
+ /**
20
+ * Create a bloom filter namespace before first use.
21
+ *
22
+ * @param payload Target bloom filter name and expected number of values
23
+ * @param options Optional request options (supports cancellation via `options.signal`)
24
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
25
+ */
26
+ createBloom(payload: BloomCreatePayload, options?: DriverRequestOptions): Promise<void>;
27
+ /**
28
+ * Backward-compatible alias for createBloom.
29
+ * Prefer calling createBloom in new code.
30
+ */
31
+ configureBloom(payload: BloomCreatePayload, options?: DriverRequestOptions): Promise<void>;
32
+ /**
33
+ * Bulk-load keys `k-{zeroPadded(i)}` for i in [from, to] via benchmark API (single HTTP, one persist).
34
+ */
35
+ benchLoadSequentialKeys(payload: BloomBenchSequentialPayload, options?: DriverRequestOptions): Promise<void>;
36
+ /**
37
+ * Check whether a value might be contained in the Bloom Filter.
38
+ *
39
+ * Note: Bloom filters can return false positives. `false` means "definitely not present".
40
+ *
41
+ * @param payload Target bloom filter name and data to check
42
+ * @param options Optional request options (supports cancellation via `options.signal`)
43
+ * @returns `true` if the process returns "true"; otherwise `false`.
44
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
45
+ */
46
+ mightContain(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<boolean>;
47
+ /**
48
+ * Remove a value from a (counting) Bloom Filter.
49
+ *
50
+ * @param payload Target bloom filter name and data to remove
51
+ * @param options Optional request options (supports cancellation via `options.signal`)
52
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
53
+ */
54
+ remove(payload: BloomDataPayload, options?: DriverRequestOptions): Promise<void>;
55
+ /**
56
+ * Add a key into the RangeLH structure.
57
+ *
58
+ * @param payload Target bloom filter name and key to add
59
+ * @param options Optional request options (supports cancellation via `options.signal`)
60
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
61
+ */
62
+ addRange(payload: RangePayload, options?: DriverRequestOptions): Promise<void>;
63
+ /**
64
+ * Create a RangeLH namespace before first use.
65
+ */
66
+ createRange(payload: RangeCreatePayload, options?: DriverRequestOptions): Promise<void>;
67
+ /**
68
+ * Add many keys into RangeLH with a single request.
69
+ */
70
+ addRangeBulk(payload: RangeBulkAddPayload, options?: DriverRequestOptions): Promise<void>;
71
+ /**
72
+ * Remove a key from the RangeLH structure.
73
+ *
74
+ * @param payload Target bloom filter name and key to remove
75
+ * @param options Optional request options (supports cancellation via `options.signal`)
76
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
77
+ */
78
+ removeRange(payload: RangePayload, options?: DriverRequestOptions): Promise<void>;
79
+ /**
80
+ * Run a point query over the RangeLH structure.
81
+ *
82
+ * @param payload Target bloom filter name and query key
83
+ * @param options Optional request options (supports cancellation via `options.signal`)
84
+ * @returns List of keys returned by the process (may be empty).
85
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
86
+ */
87
+ pointQuery(payload: PointQueryPayload, options?: DriverRequestOptions): Promise<string[]>;
88
+ /**
89
+ * Run a range query over the RangeLH structure.
90
+ *
91
+ * @param payload Target bloom filter name and range bounds (inclusive/exclusive depends on the process)
92
+ * @param options Optional request options (supports cancellation via `options.signal`)
93
+ * @returns List of keys returned by the process (may be empty).
94
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
95
+ */
96
+ rangeQuery(payload: RangeQueryPayload, options?: DriverRequestOptions): Promise<string[]>;
97
+ /**
98
+ * Run aggregate query over a key range.
99
+ *
100
+ * @param payload Target bloom filter name, range bounds, and aggregate operation.
101
+ * @param options Optional request options (supports cancellation via `options.signal`)
102
+ * @returns Numeric aggregate result from process.
103
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
104
+ */
105
+ aggregate(payload: RangeAggregatePayload, options?: DriverRequestOptions): Promise<number>;
106
+ }
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BloomFilterDriver = void 0;
4
+ const BloomApiClient_1 = require("./BloomApiClient");
5
+ const HttpClient_1 = require("./HttpClient");
6
+ const RangeApiClient_1 = require("./RangeApiClient");
7
+ const DriverValidator_1 = require("./DriverValidator");
8
+ class BloomFilterDriver {
9
+ bloomApi;
10
+ rangeApi;
11
+ /**
12
+ * Create a driver instance for talking to the Bloom Filter process service.
13
+ *
14
+ * @param config Driver configuration (base URL, defaults, timeouts, optional fetch implementation)
15
+ */
16
+ constructor(config) {
17
+ const httpClient = new HttpClient_1.HttpClient(config);
18
+ const validator = new DriverValidator_1.DriverValidator();
19
+ this.bloomApi = new BloomApiClient_1.BloomApiClient(httpClient, validator);
20
+ this.rangeApi = new RangeApiClient_1.RangeApiClient(httpClient, validator);
21
+ }
22
+ /**
23
+ * Add a value to a Bloom Filter.
24
+ *
25
+ * @param payload Target bloom filter name and data to add
26
+ * @param options Optional request options (supports cancellation via `options.signal`)
27
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
28
+ */
29
+ async add(payload, options) {
30
+ await this.bloomApi.add(payload, options);
31
+ }
32
+ /**
33
+ * Create a bloom filter namespace before first use.
34
+ *
35
+ * @param payload Target bloom filter name and expected number of values
36
+ * @param options Optional request options (supports cancellation via `options.signal`)
37
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
38
+ */
39
+ async createBloom(payload, options) {
40
+ await this.bloomApi.create(payload, options);
41
+ }
42
+ /**
43
+ * Backward-compatible alias for createBloom.
44
+ * Prefer calling createBloom in new code.
45
+ */
46
+ async configureBloom(payload, options) {
47
+ await this.createBloom(payload, options);
48
+ }
49
+ /**
50
+ * Bulk-load keys `k-{zeroPadded(i)}` for i in [from, to] via benchmark API (single HTTP, one persist).
51
+ */
52
+ async benchLoadSequentialKeys(payload, options) {
53
+ await this.bloomApi.benchLoadSequentialKeys(payload, options);
54
+ }
55
+ /**
56
+ * Check whether a value might be contained in the Bloom Filter.
57
+ *
58
+ * Note: Bloom filters can return false positives. `false` means "definitely not present".
59
+ *
60
+ * @param payload Target bloom filter name and data to check
61
+ * @param options Optional request options (supports cancellation via `options.signal`)
62
+ * @returns `true` if the process returns "true"; otherwise `false`.
63
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
64
+ */
65
+ async mightContain(payload, options) {
66
+ return this.bloomApi.mightContain(payload, options);
67
+ }
68
+ /**
69
+ * Remove a value from a (counting) Bloom Filter.
70
+ *
71
+ * @param payload Target bloom filter name and data to remove
72
+ * @param options Optional request options (supports cancellation via `options.signal`)
73
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
74
+ */
75
+ async remove(payload, options) {
76
+ await this.bloomApi.remove(payload, options);
77
+ }
78
+ /**
79
+ * Add a key into the RangeLH structure.
80
+ *
81
+ * @param payload Target bloom filter name and key to add
82
+ * @param options Optional request options (supports cancellation via `options.signal`)
83
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
84
+ */
85
+ async addRange(payload, options) {
86
+ await this.rangeApi.addRange(payload, options);
87
+ }
88
+ /**
89
+ * Create a RangeLH namespace before first use.
90
+ */
91
+ async createRange(payload, options) {
92
+ await this.rangeApi.createRange(payload, options);
93
+ }
94
+ /**
95
+ * Add many keys into RangeLH with a single request.
96
+ */
97
+ async addRangeBulk(payload, options) {
98
+ await this.rangeApi.addRangeBulk(payload, options);
99
+ }
100
+ /**
101
+ * Remove a key from the RangeLH structure.
102
+ *
103
+ * @param payload Target bloom filter name and key to remove
104
+ * @param options Optional request options (supports cancellation via `options.signal`)
105
+ * @throws {BloomDriverError} If inputs are invalid or the process request fails.
106
+ */
107
+ async removeRange(payload, options) {
108
+ await this.rangeApi.removeRange(payload, options);
109
+ }
110
+ /**
111
+ * Run a point query over the RangeLH structure.
112
+ *
113
+ * @param payload Target bloom filter name and query key
114
+ * @param options Optional request options (supports cancellation via `options.signal`)
115
+ * @returns List of keys returned by the process (may be empty).
116
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
117
+ */
118
+ async pointQuery(payload, options) {
119
+ return this.rangeApi.pointQuery(payload, options);
120
+ }
121
+ /**
122
+ * Run a range query over the RangeLH structure.
123
+ *
124
+ * @param payload Target bloom filter name and range bounds (inclusive/exclusive depends on the process)
125
+ * @param options Optional request options (supports cancellation via `options.signal`)
126
+ * @returns List of keys returned by the process (may be empty).
127
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
128
+ */
129
+ async rangeQuery(payload, options) {
130
+ return this.rangeApi.rangeQuery(payload, options);
131
+ }
132
+ /**
133
+ * Run aggregate query over a key range.
134
+ *
135
+ * @param payload Target bloom filter name, range bounds, and aggregate operation.
136
+ * @param options Optional request options (supports cancellation via `options.signal`)
137
+ * @returns Numeric aggregate result from process.
138
+ * @throws {BloomDriverError} If inputs are invalid, response parsing fails, or the process request fails.
139
+ */
140
+ async aggregate(payload, options) {
141
+ return this.rangeApi.aggregate(payload, options);
142
+ }
143
+ }
144
+ exports.BloomFilterDriver = BloomFilterDriver;
@@ -0,0 +1,16 @@
1
+ import type { BloomType, RangeAggregateOp, RangeConfigPayload } from './types';
2
+ export declare class DriverValidator {
3
+ bfName(value: string): string;
4
+ nonEmpty(value: string | undefined, fieldName: string): string;
5
+ positiveInt(value: number, fieldName: string): number;
6
+ bloomType(value: BloomType | undefined): BloomType;
7
+ counterBits(value: number, bloomType: BloomType): number;
8
+ probability(value: number, fieldName: string): number;
9
+ aggregateOp(value: RangeAggregateOp): RangeAggregateOp;
10
+ parseStringList(body: string): string[];
11
+ parseNumericValue(body: string): number;
12
+ positiveNumberInZeroOne(value: number, fieldName: string): number;
13
+ keysArray(values: string[]): string[];
14
+ rangeConfig(config: RangeConfigPayload): RangeConfigPayload;
15
+ integerInRange(value: number, fieldName: string, min: number, max: number): number;
16
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DriverValidator = void 0;
4
+ const errors_1 = require("./errors");
5
+ class DriverValidator {
6
+ bfName(value) {
7
+ return this.nonEmpty(value, 'bfName');
8
+ }
9
+ nonEmpty(value, fieldName) {
10
+ if (typeof value !== 'string' || value.trim().length === 0) {
11
+ throw new errors_1.BloomDriverError(`"${fieldName}" must be a non-empty string`);
12
+ }
13
+ return value;
14
+ }
15
+ positiveInt(value, fieldName) {
16
+ if (!Number.isInteger(value) || value <= 0) {
17
+ throw new errors_1.BloomDriverError(`"${fieldName}" must be a positive integer`);
18
+ }
19
+ return value;
20
+ }
21
+ bloomType(value) {
22
+ if (value === undefined) {
23
+ return 'standard';
24
+ }
25
+ if (value !== 'standard' && value !== 'counting') {
26
+ throw new errors_1.BloomDriverError('"bloomType" must be either "standard" or "counting"');
27
+ }
28
+ return value;
29
+ }
30
+ counterBits(value, bloomType) {
31
+ if (bloomType !== 'counting') {
32
+ throw new errors_1.BloomDriverError('"counterBits" is only valid when bloomType is "counting"');
33
+ }
34
+ if (!Number.isInteger(value) || value < 1 || value > 8) {
35
+ throw new errors_1.BloomDriverError('"counterBits" must be an integer in [1, 8]');
36
+ }
37
+ return value;
38
+ }
39
+ probability(value, fieldName) {
40
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {
41
+ throw new errors_1.BloomDriverError(`"${fieldName}" must be a number in (0, 1)`);
42
+ }
43
+ return value;
44
+ }
45
+ aggregateOp(value) {
46
+ if (value !== 'sum' && value !== 'avg' && value !== 'min' && value !== 'max' && value !== 'count') {
47
+ throw new errors_1.BloomDriverError('"op" must be one of: sum, avg, min, max, count');
48
+ }
49
+ return value;
50
+ }
51
+ parseStringList(body) {
52
+ if (body.length === 0) {
53
+ return [];
54
+ }
55
+ return body
56
+ .split(',')
57
+ .map((value) => value.trim())
58
+ .filter((value) => value.length > 0);
59
+ }
60
+ parseNumericValue(body) {
61
+ const trimmed = body.trim();
62
+ const value = Number.parseFloat(trimmed);
63
+ if (!Number.isFinite(value)) {
64
+ throw new errors_1.BloomDriverError(`Unable to parse numeric value "${trimmed}" from process response`);
65
+ }
66
+ return value;
67
+ }
68
+ positiveNumberInZeroOne(value, fieldName) {
69
+ return this.probability(value, fieldName);
70
+ }
71
+ keysArray(values) {
72
+ if (!Array.isArray(values) || values.length === 0) {
73
+ throw new errors_1.BloomDriverError('"keys" must be a non-empty array');
74
+ }
75
+ return values.map((value, index) => this.nonEmpty(value, `keys[${index}]`));
76
+ }
77
+ rangeConfig(config) {
78
+ return {
79
+ masterInitNumBucket: this.positiveInt(config.masterInitNumBucket, 'masterInitNumBucket'),
80
+ masterSplitPolicy: this.probability(config.masterSplitPolicy, 'masterSplitPolicy'),
81
+ expectedNItems: this.positiveInt(config.expectedNItems, 'expectedNItems'),
82
+ fpProbBloomRF: this.probability(config.fpProbBloomRF, 'fpProbBloomRF'),
83
+ deltaBloomRF: this.integerInRange(config.deltaBloomRF, 'deltaBloomRF', 1, 31),
84
+ keyLength: this.integerInRange(config.keyLength, 'keyLength', 1, 64),
85
+ maxBytesString: this.integerInRange(config.maxBytesString, 'maxBytesString', 1, 8),
86
+ floatScale: this.positiveInt(config.floatScale, 'floatScale')
87
+ };
88
+ }
89
+ integerInRange(value, fieldName, min, max) {
90
+ if (!Number.isInteger(value) || value < min || value > max) {
91
+ throw new errors_1.BloomDriverError(`"${fieldName}" must be an integer in [${min}, ${max}]`);
92
+ }
93
+ return value;
94
+ }
95
+ }
96
+ exports.DriverValidator = DriverValidator;
@@ -0,0 +1,10 @@
1
+ import type { BloomDriverConfig, DriverRequestOptions } from './types';
2
+ export declare class HttpClient {
3
+ private readonly baseUrl;
4
+ private readonly timeoutMs;
5
+ private readonly fetchImpl;
6
+ constructor(config: BloomDriverConfig);
7
+ get(path: string, query: Record<string, string>, options?: DriverRequestOptions): Promise<string>;
8
+ post(path: string, body: unknown, options?: DriverRequestOptions): Promise<string>;
9
+ private request;
10
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpClient = void 0;
4
+ const errors_1 = require("./errors");
5
+ const DEFAULT_TIMEOUT_MS = 5_000;
6
+ class HttpClient {
7
+ baseUrl;
8
+ timeoutMs;
9
+ fetchImpl;
10
+ constructor(config) {
11
+ this.baseUrl = config.baseUrl.replace(/\/+$/, '');
12
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
13
+ this.fetchImpl = config.fetchImpl ?? fetch;
14
+ }
15
+ async get(path, query, options) {
16
+ const searchParams = new URLSearchParams(query);
17
+ return this.request(`${path}?${searchParams.toString()}`, {
18
+ method: 'GET',
19
+ signal: options?.signal
20
+ });
21
+ }
22
+ async post(path, body, options) {
23
+ return this.request(path, {
24
+ method: 'POST',
25
+ headers: {
26
+ 'Content-Type': 'application/json'
27
+ },
28
+ body: JSON.stringify(body),
29
+ signal: options?.signal
30
+ });
31
+ }
32
+ async request(path, init) {
33
+ const controller = new AbortController();
34
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
35
+ if (init.signal) {
36
+ init.signal.addEventListener('abort', () => controller.abort(), { once: true });
37
+ }
38
+ try {
39
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
40
+ ...init,
41
+ signal: controller.signal
42
+ });
43
+ const body = (await response.text()).trim();
44
+ if (!response.ok) {
45
+ throw new errors_1.BloomDriverError(`Process request failed with status ${response.status}`, {
46
+ status: response.status,
47
+ responseBody: body
48
+ });
49
+ }
50
+ return body;
51
+ }
52
+ catch (error) {
53
+ if (error instanceof errors_1.BloomDriverError) {
54
+ throw error;
55
+ }
56
+ if (error instanceof Error && error.name === 'AbortError') {
57
+ throw new errors_1.BloomDriverError(`Process request timed out after ${this.timeoutMs}ms`);
58
+ }
59
+ throw new errors_1.BloomDriverError(error instanceof Error ? error.message : 'Unknown process request error');
60
+ }
61
+ finally {
62
+ clearTimeout(timeout);
63
+ }
64
+ }
65
+ }
66
+ exports.HttpClient = HttpClient;
@@ -0,0 +1,15 @@
1
+ import type { DriverRequestOptions, PointQueryPayload, RangeAggregatePayload, RangeBulkAddPayload, RangeCreatePayload, RangePayload, RangeQueryPayload } from './types';
2
+ import { DriverValidator } from './DriverValidator';
3
+ import { HttpClient } from './HttpClient';
4
+ export declare class RangeApiClient {
5
+ private readonly http;
6
+ private readonly validator;
7
+ constructor(http: HttpClient, validator: DriverValidator);
8
+ createRange(payload: RangeCreatePayload, options?: DriverRequestOptions): Promise<void>;
9
+ addRange(payload: RangePayload, options?: DriverRequestOptions): Promise<void>;
10
+ addRangeBulk(payload: RangeBulkAddPayload, options?: DriverRequestOptions): Promise<void>;
11
+ removeRange(payload: RangePayload, options?: DriverRequestOptions): Promise<void>;
12
+ pointQuery(payload: PointQueryPayload, options?: DriverRequestOptions): Promise<string[]>;
13
+ rangeQuery(payload: RangeQueryPayload, options?: DriverRequestOptions): Promise<string[]>;
14
+ aggregate(payload: RangeAggregatePayload, options?: DriverRequestOptions): Promise<number>;
15
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RangeApiClient = void 0;
4
+ class RangeApiClient {
5
+ http;
6
+ validator;
7
+ constructor(http, validator) {
8
+ this.http = http;
9
+ this.validator = validator;
10
+ }
11
+ async createRange(payload, options) {
12
+ const config = this.validator.rangeConfig(payload.config);
13
+ await this.http.post('/rangelh/create', {
14
+ bfName: this.validator.bfName(payload.bfName),
15
+ masterInitNumBucket: config.masterInitNumBucket,
16
+ masterSplitPolicy: config.masterSplitPolicy,
17
+ expectedNItems: config.expectedNItems,
18
+ fpProbBloomRF: config.fpProbBloomRF,
19
+ deltaBloomRF: config.deltaBloomRF,
20
+ keyLength: config.keyLength,
21
+ maxBytesString: config.maxBytesString,
22
+ floatScale: config.floatScale
23
+ }, options);
24
+ }
25
+ async addRange(payload, options) {
26
+ await this.http.post('/rangelh/add', {
27
+ bfName: this.validator.bfName(payload.bfName),
28
+ key: this.validator.nonEmpty(payload.key, 'key')
29
+ }, options);
30
+ }
31
+ async addRangeBulk(payload, options) {
32
+ await this.http.post('/rangelh/add-bulk', {
33
+ bfName: this.validator.bfName(payload.bfName),
34
+ keys: this.validator.keysArray(payload.keys)
35
+ }, options);
36
+ }
37
+ async removeRange(payload, options) {
38
+ await this.http.post('/rangelh/remove', {
39
+ bfName: this.validator.bfName(payload.bfName),
40
+ key: this.validator.nonEmpty(payload.key, 'key')
41
+ }, options);
42
+ }
43
+ async pointQuery(payload, options) {
44
+ const response = await this.http.get('/rangelh/point-query', {
45
+ bfName: this.validator.bfName(payload.bfName),
46
+ key: this.validator.nonEmpty(payload.key, 'key')
47
+ }, options);
48
+ return this.validator.parseStringList(response);
49
+ }
50
+ async rangeQuery(payload, options) {
51
+ const response = await this.http.get('/rangelh/range-query', {
52
+ bfName: this.validator.bfName(payload.bfName),
53
+ from: this.validator.nonEmpty(payload.from, 'from'),
54
+ to: this.validator.nonEmpty(payload.to, 'to')
55
+ }, options);
56
+ return this.validator.parseStringList(response);
57
+ }
58
+ async aggregate(payload, options) {
59
+ const response = await this.http.get('/rangelh/aggregate', {
60
+ bfName: this.validator.bfName(payload.bfName),
61
+ from: this.validator.nonEmpty(payload.from, 'from'),
62
+ to: this.validator.nonEmpty(payload.to, 'to'),
63
+ op: this.validator.aggregateOp(payload.op)
64
+ }, options);
65
+ return this.validator.parseNumericValue(response);
66
+ }
67
+ }
68
+ exports.RangeApiClient = RangeApiClient;
@@ -0,0 +1,8 @@
1
+ export declare class BloomDriverError extends Error {
2
+ readonly status?: number;
3
+ readonly responseBody?: string;
4
+ constructor(message: string, options?: {
5
+ status?: number;
6
+ responseBody?: string;
7
+ });
8
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BloomDriverError = void 0;
4
+ class BloomDriverError extends Error {
5
+ status;
6
+ responseBody;
7
+ constructor(message, options) {
8
+ super(message);
9
+ this.name = 'BloomDriverError';
10
+ this.status = options?.status;
11
+ this.responseBody = options?.responseBody;
12
+ }
13
+ }
14
+ exports.BloomDriverError = BloomDriverError;
@@ -0,0 +1,7 @@
1
+ export * from './BloomFilterDriver';
2
+ export * from './BloomApiClient';
3
+ export * from './RangeApiClient';
4
+ export * from './HttpClient';
5
+ export * from './DriverValidator';
6
+ export * from './errors';
7
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./BloomFilterDriver"), exports);
18
+ __exportStar(require("./BloomApiClient"), exports);
19
+ __exportStar(require("./RangeApiClient"), exports);
20
+ __exportStar(require("./HttpClient"), exports);
21
+ __exportStar(require("./DriverValidator"), exports);
22
+ __exportStar(require("./errors"), exports);
23
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,68 @@
1
+ export interface BloomDriverConfig {
2
+ baseUrl: string;
3
+ timeoutMs?: number;
4
+ fetchImpl?: typeof fetch;
5
+ }
6
+ export interface BloomDataPayload {
7
+ bfName: string;
8
+ data: string;
9
+ }
10
+ export type BloomType = 'standard' | 'counting';
11
+ export interface BloomCreatePayload {
12
+ bfName: string;
13
+ expectedValues: number;
14
+ bloomType?: BloomType;
15
+ falsePositiveProbability?: number;
16
+ counterBits?: number;
17
+ }
18
+ /** Matches POST /bloom/bench/load-sequential-keys (in-process bulk insert, one save). */
19
+ export interface BloomBenchSequentialPayload {
20
+ bfName: string;
21
+ /** Inclusive start index; default 1 on server if omitted. */
22
+ from?: number;
23
+ /** Inclusive end index. */
24
+ to: number;
25
+ /** Zero-pad width for numeric part; default 10 on server. */
26
+ keyWidth?: number;
27
+ }
28
+ export interface RangePayload {
29
+ bfName: string;
30
+ key: string;
31
+ }
32
+ export interface RangeConfigPayload {
33
+ masterInitNumBucket: number;
34
+ masterSplitPolicy: number;
35
+ expectedNItems: number;
36
+ fpProbBloomRF: number;
37
+ deltaBloomRF: number;
38
+ keyLength: number;
39
+ maxBytesString: number;
40
+ floatScale: number;
41
+ }
42
+ export interface RangeCreatePayload {
43
+ bfName: string;
44
+ config: RangeConfigPayload;
45
+ }
46
+ export interface RangeBulkAddPayload {
47
+ bfName: string;
48
+ keys: string[];
49
+ }
50
+ export interface PointQueryPayload {
51
+ bfName: string;
52
+ key: string;
53
+ }
54
+ export interface RangeQueryPayload {
55
+ bfName: string;
56
+ from: string;
57
+ to: string;
58
+ }
59
+ export type RangeAggregateOp = 'sum' | 'avg' | 'min' | 'max' | 'count';
60
+ export interface RangeAggregatePayload {
61
+ bfName: string;
62
+ from: string;
63
+ to: string;
64
+ op: RangeAggregateOp;
65
+ }
66
+ export interface DriverRequestOptions {
67
+ signal?: AbortSignal;
68
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "bloom-filter-driver",
3
+ "version": "1.0.0",
4
+ "description": "HTTP driver for BloomFilterProcess",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "rimraf ./dist && tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "bloom-filter",
16
+ "driver",
17
+ "http-client"
18
+ ],
19
+ "license": "ISC",
20
+ "devDependencies": {
21
+ "@types/jest": "^30.0.0",
22
+ "@types/node": "^25.5.0",
23
+ "jest": "^30.3.0",
24
+ "rimraf": "^6.1.3",
25
+ "ts-jest": "^29.4.9",
26
+ "typescript": "~5.9.3"
27
+ }
28
+ }