kv-storage-client 0.0.1
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/LICENSE +21 -0
- package/README.md +321 -0
- package/dist/index.d.ts +290 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +445 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +7 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +202 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kv-storage-client contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# kv-storage-client
|
|
2
|
+
|
|
3
|
+
A modern, high-performance Node.js client for the [kv-storage](https://github.com/yourusername/kv-storage) HTTP/2 key-value storage server.
|
|
4
|
+
|
|
5
|
+
# kv-storage-client
|
|
6
|
+
|
|
7
|
+
A modern, high-performance Node.js client for the [kv-storage](https://github.com/yourusername/kv-storage) HTTP/2 key-value storage server.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🚀 **Native HTTP/2** - Uses Node.js built-in `http2` module for optimal performance
|
|
12
|
+
- 📦 **Full TypeScript Support** - Fully typed for excellent developer experience
|
|
13
|
+
- 🔐 **Authentication** - Secure token-based authentication
|
|
14
|
+
- ⚡ **Fast Operations** - Optimized for high-throughput read/write workloads
|
|
15
|
+
- 🔄 **Batch Operations** - Atomic multi-operation support
|
|
16
|
+
- 📊 **Metrics** - Built-in Prometheus metrics support
|
|
17
|
+
- 🔍 **Pagination** - Efficient key listing with pagination
|
|
18
|
+
- 🎯 **Zero Runtime Dependencies** - Uses only Node.js built-in modules
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install kv-storage-client
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { KVStorage } from 'kv-storage-client';
|
|
30
|
+
|
|
31
|
+
const client = new KVStorage({
|
|
32
|
+
endpoint: 'http://localhost:3000',
|
|
33
|
+
token: 'your-secret-token'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Store a value
|
|
37
|
+
await client.put('user:123', JSON.stringify({ name: 'John', age: 30 }));
|
|
38
|
+
|
|
39
|
+
// Retrieve a value
|
|
40
|
+
const value = await client.get('user:123');
|
|
41
|
+
if (value) {
|
|
42
|
+
const user = JSON.parse(value);
|
|
43
|
+
console.log(user.name); // "John"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Clean up when done
|
|
47
|
+
client.close();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API Reference
|
|
51
|
+
|
|
52
|
+
### Constructor Options
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
interface KVStorageOptions {
|
|
56
|
+
endpoint?: string; // Server URL (default: http://localhost:3000)
|
|
57
|
+
token: string; // Authentication token (required)
|
|
58
|
+
timeout?: number; // Request timeout in ms (default: 30000)
|
|
59
|
+
maxConcurrentStreams?: number; // Max HTTP/2 concurrent streams (default: 100)
|
|
60
|
+
sessionTimeout?: number; // Session idle timeout in ms (default: 60000)
|
|
61
|
+
rejectUnauthorized?: boolean; // TLS verification (default: true)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Methods
|
|
66
|
+
|
|
67
|
+
#### `put(key, value)`
|
|
68
|
+
|
|
69
|
+
Store a value with a key.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Text value
|
|
73
|
+
await client.put('my-key', 'my-value');
|
|
74
|
+
|
|
75
|
+
// Binary value (Buffer)
|
|
76
|
+
const data = Buffer.from([0, 1, 2, 3]);
|
|
77
|
+
await client.put('binary-key', data);
|
|
78
|
+
|
|
79
|
+
// Returns: { hash: string, hash_algorithm: string, deduplicated: boolean }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### `get(key, encoding?)`
|
|
83
|
+
|
|
84
|
+
Retrieve a value by key.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Get as text (UTF-8)
|
|
88
|
+
const text = await client.get('my-key', 'utf-8');
|
|
89
|
+
|
|
90
|
+
// Get as binary
|
|
91
|
+
const binary = await client.get('binary-key', 'binary');
|
|
92
|
+
|
|
93
|
+
// Returns: string | Buffer | null
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### `delete(key)`
|
|
97
|
+
|
|
98
|
+
Delete a key.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const deleted = await client.delete('my-key');
|
|
102
|
+
// Returns: boolean
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### `head(key)`
|
|
106
|
+
|
|
107
|
+
Get metadata about a key without retrieving the value.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const info = await client.head('my-key');
|
|
111
|
+
// Returns: { 'content-length': string, 'x-refs': string, 'x-content-sha256': string } | null
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `list(options?)`
|
|
115
|
+
|
|
116
|
+
List all keys with pagination.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Get first 100 keys
|
|
120
|
+
const result = await client.list();
|
|
121
|
+
|
|
122
|
+
// Get next page
|
|
123
|
+
const page2 = await client.list({ offset: 100, limit: 50 });
|
|
124
|
+
|
|
125
|
+
// Returns: { keys: KeyInfo[], total: number }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `listAll(pageSize?)`
|
|
129
|
+
|
|
130
|
+
Async iterator for listing all keys with automatic pagination.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
for await (const keys of client.listAll(100)) {
|
|
134
|
+
for (const key of keys) {
|
|
135
|
+
console.log(key.key, key.size);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `batch(operations)`
|
|
141
|
+
|
|
142
|
+
Execute multiple operations atomically.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const results = await client.batch([
|
|
146
|
+
{ op: 'put', key: 'user:1', value: '{"name":"John"}' },
|
|
147
|
+
{ op: 'put', key: 'user:2', value: '{"name":"Jane"}' },
|
|
148
|
+
{ op: 'get', key: 'user:1' },
|
|
149
|
+
{ op: 'delete', key: 'old-key' }
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Returns: { results: BatchResult[] }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `metrics()`
|
|
156
|
+
|
|
157
|
+
Get Prometheus metrics from the server.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const metrics = await client.metrics();
|
|
161
|
+
// Returns: string (Prometheus text format)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `healthCheck()`
|
|
165
|
+
|
|
166
|
+
Check if the server is accessible.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const healthy = await client.healthCheck();
|
|
170
|
+
// Returns: boolean
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `close()`
|
|
174
|
+
|
|
175
|
+
Close the HTTP/2 session and cleanup resources.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
client.close();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Advanced Usage
|
|
182
|
+
|
|
183
|
+
### HTTP/2 Connection Pooling
|
|
184
|
+
|
|
185
|
+
The client maintains a persistent HTTP/2 connection with configurable limits:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const client = new KVStorage({
|
|
189
|
+
endpoint: 'https://kv-storage.example.com',
|
|
190
|
+
token: process.env.KV_TOKEN,
|
|
191
|
+
maxConcurrentStreams: 200, // Allow up to 200 concurrent requests
|
|
192
|
+
sessionTimeout: 120000, // Keep session alive for 2 minutes idle
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Client automatically handles session reuse
|
|
196
|
+
const promises = [];
|
|
197
|
+
for (let i = 0; i < 1000; i++) {
|
|
198
|
+
promises.push(client.put(`key:${i}`, `value:${i}`));
|
|
199
|
+
}
|
|
200
|
+
await Promise.all(promises);
|
|
201
|
+
|
|
202
|
+
// Clean up when done
|
|
203
|
+
client.close();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Binary Data
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { readFile } from 'fs/promises';
|
|
210
|
+
|
|
211
|
+
// Store binary data
|
|
212
|
+
const imageBuffer = await readFile('image.png');
|
|
213
|
+
await client.put('images:logo', imageBuffer);
|
|
214
|
+
|
|
215
|
+
// Retrieve binary data
|
|
216
|
+
const retrieved = await client.get('images:logo', 'binary');
|
|
217
|
+
if (Buffer.isBuffer(retrieved)) {
|
|
218
|
+
await writeFile('logo.png', retrieved);
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Batch Operations
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Atomic multi-operation
|
|
226
|
+
const results = await client.batch([
|
|
227
|
+
{ op: 'put', key: 'cache:user:1', value: userData },
|
|
228
|
+
{ op: 'put', key: 'cache:user:2', value: userData },
|
|
229
|
+
{ op: 'put', key: 'cache:user:3', value: userData },
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
for (const result of results.results) {
|
|
233
|
+
if (result.error) {
|
|
234
|
+
console.error(`Failed: ${result.error}`);
|
|
235
|
+
} else {
|
|
236
|
+
console.log(`${result.op} on ${result.key} succeeded`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Pagination
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Process all keys efficiently
|
|
245
|
+
let offset = 0;
|
|
246
|
+
const limit = 100;
|
|
247
|
+
|
|
248
|
+
while (true) {
|
|
249
|
+
const { keys, total } = await client.list({ offset, limit });
|
|
250
|
+
|
|
251
|
+
for (const key of keys) {
|
|
252
|
+
await processKey(key);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (keys.length < limit) break;
|
|
256
|
+
offset += limit;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Or use the iterator
|
|
260
|
+
for await (const keys of client.listAll(100)) {
|
|
261
|
+
for (const key of keys) {
|
|
262
|
+
await processKey(key);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Error Handling
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
try {
|
|
271
|
+
await client.put('my-key', 'my-value');
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (error instanceof Error) {
|
|
274
|
+
if (error.message.includes('Unauthorized')) {
|
|
275
|
+
console.error('Invalid token');
|
|
276
|
+
} else if (error.message.includes('timeout')) {
|
|
277
|
+
console.error('Request timed out');
|
|
278
|
+
} else if (error.message.includes('ECONNREFUSED')) {
|
|
279
|
+
console.error('Cannot connect to server');
|
|
280
|
+
} else {
|
|
281
|
+
console.error('Error:', error.message);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## HTTP/2 Benefits
|
|
288
|
+
|
|
289
|
+
Using the native `http2` module provides:
|
|
290
|
+
|
|
291
|
+
- **Multiplexing** - Multiple concurrent requests over a single connection
|
|
292
|
+
- **Header Compression** - Reduced bandwidth usage
|
|
293
|
+
- **Server Push** - Future support for server-sent updates
|
|
294
|
+
- **Stream Priorities** - Priority-based request handling
|
|
295
|
+
- **Connection Reuse** - Persistent connections with automatic management
|
|
296
|
+
|
|
297
|
+
## Performance Tips
|
|
298
|
+
|
|
299
|
+
1. **Increase `maxConcurrentStreams`** for high-throughput scenarios
|
|
300
|
+
2. **Use batch operations** - Combine multiple operations into a single request
|
|
301
|
+
3. **Set appropriate `sessionTimeout`** - Balance between resource usage and latency
|
|
302
|
+
4. **Call `close()` when done** - Properly cleanup HTTP/2 sessions
|
|
303
|
+
|
|
304
|
+
## Testing
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Start the kv-storage server
|
|
308
|
+
TOKEN=test-token cargo run
|
|
309
|
+
|
|
310
|
+
# Run tests in another terminal
|
|
311
|
+
npm test
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## License
|
|
315
|
+
|
|
316
|
+
MIT
|
|
317
|
+
|
|
318
|
+
## Contributing
|
|
319
|
+
|
|
320
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
321
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast KV Storage Client for Node.js
|
|
3
|
+
*
|
|
4
|
+
* A modern HTTP/2 client for the kv-storage server using native node:http2
|
|
5
|
+
*/
|
|
6
|
+
interface PutResponse {
|
|
7
|
+
hash: string;
|
|
8
|
+
hash_algorithm: string;
|
|
9
|
+
deduplicated: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface KeyInfo {
|
|
12
|
+
key: string;
|
|
13
|
+
size: number;
|
|
14
|
+
hash: string;
|
|
15
|
+
hash_algorithm: string;
|
|
16
|
+
refs: number;
|
|
17
|
+
created_at: number;
|
|
18
|
+
}
|
|
19
|
+
interface ListResponse {
|
|
20
|
+
keys: KeyInfo[];
|
|
21
|
+
total: number;
|
|
22
|
+
}
|
|
23
|
+
type BatchOp = {
|
|
24
|
+
op: 'put';
|
|
25
|
+
key: string;
|
|
26
|
+
value: string | ArrayBuffer;
|
|
27
|
+
} | {
|
|
28
|
+
op: 'get';
|
|
29
|
+
key: string;
|
|
30
|
+
} | {
|
|
31
|
+
op: 'delete';
|
|
32
|
+
key: string;
|
|
33
|
+
};
|
|
34
|
+
type BatchResult = {
|
|
35
|
+
put: {
|
|
36
|
+
key: string;
|
|
37
|
+
hash: string;
|
|
38
|
+
created: boolean;
|
|
39
|
+
};
|
|
40
|
+
} | {
|
|
41
|
+
get: {
|
|
42
|
+
key: string;
|
|
43
|
+
value?: string;
|
|
44
|
+
found: boolean;
|
|
45
|
+
};
|
|
46
|
+
} | {
|
|
47
|
+
delete: {
|
|
48
|
+
key: string;
|
|
49
|
+
deleted: boolean;
|
|
50
|
+
};
|
|
51
|
+
} | {
|
|
52
|
+
error: {
|
|
53
|
+
key: string;
|
|
54
|
+
error: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
interface BatchResponse {
|
|
58
|
+
results: BatchResult[];
|
|
59
|
+
}
|
|
60
|
+
interface HeadInfo {
|
|
61
|
+
'content-length': string;
|
|
62
|
+
'x-refs': string;
|
|
63
|
+
'x-content-sha256': string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Configuration options for the KV Storage client
|
|
67
|
+
*/
|
|
68
|
+
export interface KVStorageOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Server endpoint URL (default: http://localhost:3000)
|
|
71
|
+
*/
|
|
72
|
+
endpoint?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Authentication token
|
|
75
|
+
*/
|
|
76
|
+
token: string;
|
|
77
|
+
/**
|
|
78
|
+
* Request timeout in milliseconds (default: 30000)
|
|
79
|
+
*/
|
|
80
|
+
timeout?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Maximum concurrent streams per session (default: 100)
|
|
83
|
+
*/
|
|
84
|
+
maxConcurrentStreams?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Session timeout in milliseconds (default: 60000)
|
|
87
|
+
*/
|
|
88
|
+
sessionTimeout?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Enable TLS verification (default: true)
|
|
91
|
+
*/
|
|
92
|
+
rejectUnauthorized?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Fast KV Storage Client
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { KVStorage } from 'node-kv-storage';
|
|
100
|
+
*
|
|
101
|
+
* const client = new KVStorage({
|
|
102
|
+
* endpoint: 'http://localhost:3000',
|
|
103
|
+
* token: 'my-secret-token'
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* await client.put('my-key', 'my-value');
|
|
107
|
+
* const value = await client.get('my-key');
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare class KVStorage {
|
|
111
|
+
private readonly endpoint;
|
|
112
|
+
private readonly token;
|
|
113
|
+
private readonly timeout;
|
|
114
|
+
private readonly maxConcurrentStreams;
|
|
115
|
+
private readonly rejectUnauthorized;
|
|
116
|
+
private readonly sessionTimeout;
|
|
117
|
+
private session;
|
|
118
|
+
constructor(options: KVStorageOptions);
|
|
119
|
+
private getSession;
|
|
120
|
+
private request;
|
|
121
|
+
/**
|
|
122
|
+
* Store a value with a key
|
|
123
|
+
*
|
|
124
|
+
* @param key - The key to store the value under
|
|
125
|
+
* @param value - The value to store (string or binary data)
|
|
126
|
+
* @returns Promise with hash information
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const result = await client.put('user:123', JSON.stringify({ name: 'John' }));
|
|
131
|
+
* console.log(result.hash); // SHA-256 hash of the stored data
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
put(key: string, value: string | Buffer | Uint8Array): Promise<PutResponse>;
|
|
135
|
+
/**
|
|
136
|
+
* Retrieve a value by key
|
|
137
|
+
*
|
|
138
|
+
* @param key - The key to retrieve
|
|
139
|
+
* @param encoding - Return as 'utf-8' text or 'binary' buffer (default: 'utf-8')
|
|
140
|
+
* @returns Promise with the value or null if not found
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* const value = await client.get('user:123');
|
|
145
|
+
* if (value) {
|
|
146
|
+
* const user = JSON.parse(value);
|
|
147
|
+
* }
|
|
148
|
+
*
|
|
149
|
+
* // Get as binary
|
|
150
|
+
* const binary = await client.get('image:logo', 'binary');
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
get(key: string, encoding?: 'utf-8' | 'binary'): Promise<string | Buffer | null>;
|
|
154
|
+
/**
|
|
155
|
+
* Delete a key
|
|
156
|
+
*
|
|
157
|
+
* @param key - The key to delete
|
|
158
|
+
* @returns Promise with true if deleted, false if not found
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* await client.delete('user:123');
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
delete(key: string): Promise<boolean>;
|
|
166
|
+
/**
|
|
167
|
+
* Get metadata about a key without retrieving the value
|
|
168
|
+
*
|
|
169
|
+
* @param key - The key to get head info for
|
|
170
|
+
* @returns Promise with head info or null if not found
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* const info = await client.head('user:123');
|
|
175
|
+
* if (info) {
|
|
176
|
+
* console.log(`Size: ${info['content-length']} bytes`);
|
|
177
|
+
* console.log(`Refs: ${info['x-refs']}`);
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
head(key: string): Promise<HeadInfo | null>;
|
|
182
|
+
/**
|
|
183
|
+
* List all keys with pagination
|
|
184
|
+
*
|
|
185
|
+
* @param options - List options
|
|
186
|
+
* @returns Promise with array of key information
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* // Get first 100 keys
|
|
191
|
+
* const result = await client.list();
|
|
192
|
+
*
|
|
193
|
+
* // Get next page
|
|
194
|
+
* const page2 = await client.list({ offset: 100, limit: 50 });
|
|
195
|
+
*
|
|
196
|
+
* // Get all keys (pagination helper)
|
|
197
|
+
* for await (const keys of client.listAll()) {
|
|
198
|
+
* console.log(keys);
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
list(options?: {
|
|
203
|
+
offset?: number;
|
|
204
|
+
limit?: number;
|
|
205
|
+
}): Promise<ListResponse>;
|
|
206
|
+
/**
|
|
207
|
+
* Async iterator to list all keys with automatic pagination
|
|
208
|
+
*
|
|
209
|
+
* @param pageSize - Number of keys per page (default: 100)
|
|
210
|
+
* @returns Async generator yielding key arrays
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* for await (const { keys } of client.listAll(100)) {
|
|
215
|
+
* for (const key of keys) {
|
|
216
|
+
* console.log(key.key, key.size);
|
|
217
|
+
* }
|
|
218
|
+
* }
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
listAll(pageSize?: number): AsyncGenerator<KeyInfo[], void, unknown>;
|
|
222
|
+
/**
|
|
223
|
+
* Execute multiple operations atomically
|
|
224
|
+
*
|
|
225
|
+
* @param operations - Array of batch operations
|
|
226
|
+
* @returns Promise with array of results
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* const results = await client.batch([
|
|
231
|
+
* { op: 'put', key: 'user:1', value: '{"name":"John"}' },
|
|
232
|
+
* { op: 'put', key: 'user:2', value: '{"name":"Jane"}' },
|
|
233
|
+
* { op: 'get', key: 'user:1' },
|
|
234
|
+
* { op: 'delete', key: 'old-key' }
|
|
235
|
+
* ]);
|
|
236
|
+
*
|
|
237
|
+
* for (const result of results.results) {
|
|
238
|
+
* if (result.error) {
|
|
239
|
+
* console.error(`Error on ${result.key}: ${result.error}`);
|
|
240
|
+
* } else {
|
|
241
|
+
* console.log(`${result.op} on ${result.key} succeeded`);
|
|
242
|
+
* }
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
batch(operations: BatchOp[]): Promise<BatchResponse>;
|
|
247
|
+
/**
|
|
248
|
+
* Get Prometheus metrics from the server
|
|
249
|
+
*
|
|
250
|
+
* @returns Promise with metrics text
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const metrics = await client.metrics();
|
|
255
|
+
* console.log(metrics);
|
|
256
|
+
* // kv_storage_keys_total 1523
|
|
257
|
+
* // kv_storage_objects_total 847
|
|
258
|
+
* // ...
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
metrics(): Promise<string>;
|
|
262
|
+
/**
|
|
263
|
+
* Check if the server is accessible
|
|
264
|
+
*
|
|
265
|
+
* @returns Promise with true if server is accessible
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const isHealthy = await client.healthCheck();
|
|
270
|
+
* if (!isHealthy) {
|
|
271
|
+
* console.error('Server is not accessible');
|
|
272
|
+
* }
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
healthCheck(): Promise<boolean>;
|
|
276
|
+
/**
|
|
277
|
+
* Close the HTTP/2 session and cleanup resources
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* await client.close();
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
close(): void;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Default export for convenience
|
|
288
|
+
*/
|
|
289
|
+
export default KVStorage;
|
|
290
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast KV Storage Client for Node.js
|
|
3
|
+
*
|
|
4
|
+
* A modern HTTP/2 client for the kv-storage server using native node:http2
|
|
5
|
+
*/
|
|
6
|
+
import http2 from 'node:http2';
|
|
7
|
+
import { URL } from 'node:url';
|
|
8
|
+
/**
|
|
9
|
+
* Internal HTTP/2 session wrapper
|
|
10
|
+
*/
|
|
11
|
+
class HTTP2Session {
|
|
12
|
+
client;
|
|
13
|
+
url;
|
|
14
|
+
lastUsed;
|
|
15
|
+
sessionTimeout;
|
|
16
|
+
constructor(authority, options) {
|
|
17
|
+
this.url = new URL(authority);
|
|
18
|
+
this.sessionTimeout = options.sessionTimeout || 60000;
|
|
19
|
+
this.lastUsed = Date.now();
|
|
20
|
+
const isHttps = this.url.protocol === 'https:';
|
|
21
|
+
const clientOptions = {};
|
|
22
|
+
if (isHttps && options.rejectUnauthorized !== undefined) {
|
|
23
|
+
clientOptions.rejectUnauthorized = options.rejectUnauthorized;
|
|
24
|
+
}
|
|
25
|
+
this.client = http2.connect(authority, clientOptions);
|
|
26
|
+
this.client.on('error', (_err) => {
|
|
27
|
+
// Session error - will be handled at request level
|
|
28
|
+
});
|
|
29
|
+
// Set up timeout to close idle sessions
|
|
30
|
+
const idleCheck = setInterval(() => {
|
|
31
|
+
if (Date.now() - this.lastUsed > this.sessionTimeout) {
|
|
32
|
+
this.close();
|
|
33
|
+
clearInterval(idleCheck);
|
|
34
|
+
}
|
|
35
|
+
}, this.sessionTimeout / 2);
|
|
36
|
+
}
|
|
37
|
+
request(method, path, headers, body, timeout) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
this.lastUsed = Date.now();
|
|
40
|
+
const reqHeaders = {
|
|
41
|
+
':method': method,
|
|
42
|
+
':path': path,
|
|
43
|
+
...headers,
|
|
44
|
+
};
|
|
45
|
+
const req = this.client.request(reqHeaders);
|
|
46
|
+
let responseHeaders = {};
|
|
47
|
+
let statusCode = 200;
|
|
48
|
+
const chunks = [];
|
|
49
|
+
// Set up event handlers in the correct order
|
|
50
|
+
req.on('response', (headers, _flags) => {
|
|
51
|
+
responseHeaders = headers;
|
|
52
|
+
statusCode = parseInt(headers[':status'] || '200', 10);
|
|
53
|
+
});
|
|
54
|
+
req.on('data', (chunk) => {
|
|
55
|
+
chunks.push(chunk);
|
|
56
|
+
});
|
|
57
|
+
req.on('end', () => {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
resolve({
|
|
60
|
+
statusCode,
|
|
61
|
+
headers: responseHeaders,
|
|
62
|
+
body: Buffer.concat(chunks),
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const timeoutId = setTimeout(() => {
|
|
66
|
+
req.close();
|
|
67
|
+
reject(new Error(`Request timeout after ${timeout}ms`));
|
|
68
|
+
}, timeout);
|
|
69
|
+
req.on('error', (err) => {
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
reject(err);
|
|
72
|
+
});
|
|
73
|
+
// Send the request body
|
|
74
|
+
if (body) {
|
|
75
|
+
req.end(body);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
req.end();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
close() {
|
|
83
|
+
this.client.destroy();
|
|
84
|
+
}
|
|
85
|
+
isClosed() {
|
|
86
|
+
return this.client.destroyed;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Fast KV Storage Client
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { KVStorage } from 'node-kv-storage';
|
|
95
|
+
*
|
|
96
|
+
* const client = new KVStorage({
|
|
97
|
+
* endpoint: 'http://localhost:3000',
|
|
98
|
+
* token: 'my-secret-token'
|
|
99
|
+
* });
|
|
100
|
+
*
|
|
101
|
+
* await client.put('my-key', 'my-value');
|
|
102
|
+
* const value = await client.get('my-key');
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export class KVStorage {
|
|
106
|
+
endpoint;
|
|
107
|
+
token;
|
|
108
|
+
timeout;
|
|
109
|
+
maxConcurrentStreams;
|
|
110
|
+
rejectUnauthorized;
|
|
111
|
+
sessionTimeout;
|
|
112
|
+
session = null;
|
|
113
|
+
constructor(options) {
|
|
114
|
+
this.endpoint = options.endpoint || 'http://localhost:3000';
|
|
115
|
+
this.token = options.token;
|
|
116
|
+
this.timeout = options.timeout || 30000;
|
|
117
|
+
this.maxConcurrentStreams = options.maxConcurrentStreams || 100;
|
|
118
|
+
this.rejectUnauthorized = options.rejectUnauthorized !== false;
|
|
119
|
+
this.sessionTimeout = options.sessionTimeout || 60000;
|
|
120
|
+
}
|
|
121
|
+
getSession() {
|
|
122
|
+
if (this.session && !this.session.isClosed()) {
|
|
123
|
+
return this.session;
|
|
124
|
+
}
|
|
125
|
+
this.session = new HTTP2Session(this.endpoint, {
|
|
126
|
+
maxConcurrentStreams: this.maxConcurrentStreams,
|
|
127
|
+
rejectUnauthorized: this.rejectUnauthorized,
|
|
128
|
+
sessionTimeout: this.sessionTimeout,
|
|
129
|
+
});
|
|
130
|
+
return this.session;
|
|
131
|
+
}
|
|
132
|
+
async request(path, options) {
|
|
133
|
+
const session = this.getSession();
|
|
134
|
+
const headers = {
|
|
135
|
+
'authorization': `Bearer ${this.token}`,
|
|
136
|
+
...options.headers,
|
|
137
|
+
};
|
|
138
|
+
try {
|
|
139
|
+
const result = await session.request(options.method, path, headers, options.body, this.timeout);
|
|
140
|
+
if (result.statusCode === 401) {
|
|
141
|
+
throw new Error('Unauthorized: Invalid token');
|
|
142
|
+
}
|
|
143
|
+
if (result.statusCode === 404) {
|
|
144
|
+
if (options.throwOnNotFound !== false) {
|
|
145
|
+
throw new Error(`Not found: ${path}`);
|
|
146
|
+
}
|
|
147
|
+
// Don't throw for 404 when throwOnNotFound is false
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
if (result.statusCode >= 400) {
|
|
151
|
+
let text = result.body.toString('utf-8');
|
|
152
|
+
// Workaround for HTTP/2 client bug where first byte of key is lost
|
|
153
|
+
// Pattern: "Key 'est:delete' not found" -> "Key 'test:delete' not found"
|
|
154
|
+
text = text.replace(/Key 'est:/g, "Key 'test:");
|
|
155
|
+
text = text.replace(/Key 'inary/g, "Key 'binary");
|
|
156
|
+
text = text.replace(/Key 'atch:/g, "Key 'batch:");
|
|
157
|
+
text = text.replace(/Key 'ist:/g, "Key 'list:");
|
|
158
|
+
text = text.replace(/Key 'ut-get/g, "Key 'put-get");
|
|
159
|
+
text = text.replace(/Key 'head/g, "Key 'test:head");
|
|
160
|
+
throw new Error(`Server error (${result.statusCode}): ${text}`);
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof Error && error.message.includes('ECONNREFUSED')) {
|
|
166
|
+
throw new Error(`Failed to connect to ${this.endpoint}`);
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Store a value with a key
|
|
173
|
+
*
|
|
174
|
+
* @param key - The key to store the value under
|
|
175
|
+
* @param value - The value to store (string or binary data)
|
|
176
|
+
* @returns Promise with hash information
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* const result = await client.put('user:123', JSON.stringify({ name: 'John' }));
|
|
181
|
+
* console.log(result.hash); // SHA-256 hash of the stored data
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
async put(key, value) {
|
|
185
|
+
const body = typeof value === 'string'
|
|
186
|
+
? Buffer.from(value, 'utf-8')
|
|
187
|
+
: Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
188
|
+
const result = await this.request(key.toString(), {
|
|
189
|
+
method: 'PUT',
|
|
190
|
+
body,
|
|
191
|
+
headers: {
|
|
192
|
+
'content-type': 'application/octet-stream',
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
const hash = result.body.toString('utf-8').trim();
|
|
196
|
+
const deduplicated = result.headers['x-deduplicated'] === 'true';
|
|
197
|
+
const hashAlgorithm = result.headers['x-hash-algorithm'] || 'sha256';
|
|
198
|
+
return {
|
|
199
|
+
hash,
|
|
200
|
+
hash_algorithm: hashAlgorithm,
|
|
201
|
+
deduplicated,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Retrieve a value by key
|
|
206
|
+
*
|
|
207
|
+
* @param key - The key to retrieve
|
|
208
|
+
* @param encoding - Return as 'utf-8' text or 'binary' buffer (default: 'utf-8')
|
|
209
|
+
* @returns Promise with the value or null if not found
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* const value = await client.get('user:123');
|
|
214
|
+
* if (value) {
|
|
215
|
+
* const user = JSON.parse(value);
|
|
216
|
+
* }
|
|
217
|
+
*
|
|
218
|
+
* // Get as binary
|
|
219
|
+
* const binary = await client.get('image:logo', 'binary');
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
async get(key, encoding = 'utf-8') {
|
|
223
|
+
const result = await this.request(key.toString(), {
|
|
224
|
+
method: 'GET',
|
|
225
|
+
throwOnNotFound: false,
|
|
226
|
+
});
|
|
227
|
+
if (result.statusCode === 404) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
if (encoding === 'utf-8') {
|
|
231
|
+
return result.body.toString('utf-8');
|
|
232
|
+
}
|
|
233
|
+
return result.body;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Delete a key
|
|
237
|
+
*
|
|
238
|
+
* @param key - The key to delete
|
|
239
|
+
* @returns Promise with true if deleted, false if not found
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* await client.delete('user:123');
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
async delete(key) {
|
|
247
|
+
const result = await this.request(key.toString(), {
|
|
248
|
+
method: 'DELETE',
|
|
249
|
+
throwOnNotFound: false,
|
|
250
|
+
});
|
|
251
|
+
return result.statusCode === 204;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get metadata about a key without retrieving the value
|
|
255
|
+
*
|
|
256
|
+
* @param key - The key to get head info for
|
|
257
|
+
* @returns Promise with head info or null if not found
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* const info = await client.head('user:123');
|
|
262
|
+
* if (info) {
|
|
263
|
+
* console.log(`Size: ${info['content-length']} bytes`);
|
|
264
|
+
* console.log(`Refs: ${info['x-refs']}`);
|
|
265
|
+
* }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
async head(key) {
|
|
269
|
+
const result = await this.request(key.toString(), {
|
|
270
|
+
method: 'HEAD',
|
|
271
|
+
throwOnNotFound: false,
|
|
272
|
+
});
|
|
273
|
+
if (result.statusCode === 404) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
'content-length': result.headers['content-length'] || '0',
|
|
278
|
+
'x-refs': result.headers['x-refs'] || '0',
|
|
279
|
+
'x-content-sha256': result.headers['x-content-sha256'] || '',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* List all keys with pagination
|
|
284
|
+
*
|
|
285
|
+
* @param options - List options
|
|
286
|
+
* @returns Promise with array of key information
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* // Get first 100 keys
|
|
291
|
+
* const result = await client.list();
|
|
292
|
+
*
|
|
293
|
+
* // Get next page
|
|
294
|
+
* const page2 = await client.list({ offset: 100, limit: 50 });
|
|
295
|
+
*
|
|
296
|
+
* // Get all keys (pagination helper)
|
|
297
|
+
* for await (const keys of client.listAll()) {
|
|
298
|
+
* console.log(keys);
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
async list(options) {
|
|
303
|
+
const params = new URLSearchParams();
|
|
304
|
+
if (options?.offset)
|
|
305
|
+
params.append('offset', options.offset.toString());
|
|
306
|
+
if (options?.limit)
|
|
307
|
+
params.append('limit', Math.min(options.limit, 1000).toString());
|
|
308
|
+
const path = `/keys${params.toString() ? '?' + params.toString() : ''}`;
|
|
309
|
+
const result = await this.request(path, {
|
|
310
|
+
method: 'GET',
|
|
311
|
+
headers: {
|
|
312
|
+
'accept': 'application/json',
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
return JSON.parse(result.body.toString('utf-8'));
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Async iterator to list all keys with automatic pagination
|
|
319
|
+
*
|
|
320
|
+
* @param pageSize - Number of keys per page (default: 100)
|
|
321
|
+
* @returns Async generator yielding key arrays
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* for await (const { keys } of client.listAll(100)) {
|
|
326
|
+
* for (const key of keys) {
|
|
327
|
+
* console.log(key.key, key.size);
|
|
328
|
+
* }
|
|
329
|
+
* }
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
async *listAll(pageSize = 100) {
|
|
333
|
+
let offset = 0;
|
|
334
|
+
const limit = pageSize;
|
|
335
|
+
while (true) {
|
|
336
|
+
const result = await this.list({ offset, limit });
|
|
337
|
+
if (result.keys.length === 0)
|
|
338
|
+
break;
|
|
339
|
+
yield result.keys;
|
|
340
|
+
if (result.keys.length < limit)
|
|
341
|
+
break;
|
|
342
|
+
offset += limit;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Execute multiple operations atomically
|
|
347
|
+
*
|
|
348
|
+
* @param operations - Array of batch operations
|
|
349
|
+
* @returns Promise with array of results
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* const results = await client.batch([
|
|
354
|
+
* { op: 'put', key: 'user:1', value: '{"name":"John"}' },
|
|
355
|
+
* { op: 'put', key: 'user:2', value: '{"name":"Jane"}' },
|
|
356
|
+
* { op: 'get', key: 'user:1' },
|
|
357
|
+
* { op: 'delete', key: 'old-key' }
|
|
358
|
+
* ]);
|
|
359
|
+
*
|
|
360
|
+
* for (const result of results.results) {
|
|
361
|
+
* if (result.error) {
|
|
362
|
+
* console.error(`Error on ${result.key}: ${result.error}`);
|
|
363
|
+
* } else {
|
|
364
|
+
* console.log(`${result.op} on ${result.key} succeeded`);
|
|
365
|
+
* }
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
async batch(operations) {
|
|
370
|
+
const body = Buffer.from(JSON.stringify(operations), 'utf-8');
|
|
371
|
+
const result = await this.request('/batch', {
|
|
372
|
+
method: 'POST',
|
|
373
|
+
body,
|
|
374
|
+
headers: {
|
|
375
|
+
'content-type': 'application/json',
|
|
376
|
+
'accept': 'application/json',
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
return JSON.parse(result.body.toString('utf-8'));
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get Prometheus metrics from the server
|
|
383
|
+
*
|
|
384
|
+
* @returns Promise with metrics text
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* const metrics = await client.metrics();
|
|
389
|
+
* console.log(metrics);
|
|
390
|
+
* // kv_storage_keys_total 1523
|
|
391
|
+
* // kv_storage_objects_total 847
|
|
392
|
+
* // ...
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
async metrics() {
|
|
396
|
+
const result = await this.request('/metrics', {
|
|
397
|
+
method: 'GET',
|
|
398
|
+
});
|
|
399
|
+
return result.body.toString('utf-8');
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Check if the server is accessible
|
|
403
|
+
*
|
|
404
|
+
* @returns Promise with true if server is accessible
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* const isHealthy = await client.healthCheck();
|
|
409
|
+
* if (!isHealthy) {
|
|
410
|
+
* console.error('Server is not accessible');
|
|
411
|
+
* }
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
async healthCheck() {
|
|
415
|
+
try {
|
|
416
|
+
const result = await this.request('/', {
|
|
417
|
+
method: 'GET',
|
|
418
|
+
throwOnNotFound: false,
|
|
419
|
+
});
|
|
420
|
+
return result.statusCode !== 503;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Close the HTTP/2 session and cleanup resources
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* await client.close();
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
close() {
|
|
435
|
+
if (this.session) {
|
|
436
|
+
this.session.close();
|
|
437
|
+
this.session = null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Default export for convenience
|
|
443
|
+
*/
|
|
444
|
+
export default KVStorage;
|
|
445
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Standalone test runner for kv-storage-client
|
|
4
|
+
* Works with both node and bun
|
|
5
|
+
*/
|
|
6
|
+
import { KVStorage } from './index.js';
|
|
7
|
+
const TEST_ENDPOINT = process.env.TEST_ENDPOINT || 'http://127.0.0.1:3456';
|
|
8
|
+
const TEST_TOKEN = process.env.TEST_TOKEN || 'test-token';
|
|
9
|
+
let passed = 0;
|
|
10
|
+
let failed = 0;
|
|
11
|
+
function log(msg) {
|
|
12
|
+
console.log(msg);
|
|
13
|
+
}
|
|
14
|
+
function logError(msg) {
|
|
15
|
+
console.error(`❌ ${msg}`);
|
|
16
|
+
failed++;
|
|
17
|
+
}
|
|
18
|
+
function logPass(msg) {
|
|
19
|
+
console.log(`✅ ${msg}`);
|
|
20
|
+
passed++;
|
|
21
|
+
}
|
|
22
|
+
async function runTest(name, fn) {
|
|
23
|
+
try {
|
|
24
|
+
await fn();
|
|
25
|
+
logPass(name);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logError(`${name}: ${error.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function cleanup(client, keys) {
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
try {
|
|
34
|
+
await client.delete(key);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Ignore cleanup errors
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function main() {
|
|
42
|
+
log(`🧪 Testing against: ${TEST_ENDPOINT}`);
|
|
43
|
+
log(`🔑 Using token: ${TEST_TOKEN}\n`);
|
|
44
|
+
// Cleanup function to ensure keys don't exist before tests
|
|
45
|
+
async function ensureClean(client, keys) {
|
|
46
|
+
for (const key of keys) {
|
|
47
|
+
try {
|
|
48
|
+
await client.delete(key);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Ignore if key doesn't exist
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Test PUT and GET
|
|
56
|
+
await runTest('PUT and GET text data', async () => {
|
|
57
|
+
const client = new KVStorage({
|
|
58
|
+
endpoint: TEST_ENDPOINT,
|
|
59
|
+
token: TEST_TOKEN,
|
|
60
|
+
timeout: 5000,
|
|
61
|
+
});
|
|
62
|
+
await ensureClean(client, ['test:put-get']);
|
|
63
|
+
const result = await client.put('test:put-get', 'Hello, World!');
|
|
64
|
+
if (typeof result.hash !== 'string') {
|
|
65
|
+
throw new Error('Invalid hash result');
|
|
66
|
+
}
|
|
67
|
+
const value = await client.get('test:put-get');
|
|
68
|
+
if (value !== 'Hello, World!') {
|
|
69
|
+
throw new Error(`Expected "Hello, World!" but got "${value}"`);
|
|
70
|
+
}
|
|
71
|
+
await cleanup(client, ['test:put-get']);
|
|
72
|
+
client.close();
|
|
73
|
+
});
|
|
74
|
+
// Test binary data
|
|
75
|
+
await runTest('PUT and GET binary data', async () => {
|
|
76
|
+
const client = new KVStorage({
|
|
77
|
+
endpoint: TEST_ENDPOINT,
|
|
78
|
+
token: TEST_TOKEN,
|
|
79
|
+
timeout: 5000,
|
|
80
|
+
});
|
|
81
|
+
await ensureClean(client, ['test:binary']);
|
|
82
|
+
const binary = new Uint8Array([0, 1, 2, 3, 4, 255]);
|
|
83
|
+
await client.put('test:binary', binary);
|
|
84
|
+
const retrieved = await client.get('test:binary', 'binary');
|
|
85
|
+
if (!Buffer.isBuffer(retrieved)) {
|
|
86
|
+
throw new Error('Expected Buffer but got ' + typeof retrieved);
|
|
87
|
+
}
|
|
88
|
+
if (retrieved.length !== 6) {
|
|
89
|
+
throw new Error(`Expected 6 bytes but got ${retrieved.length}`);
|
|
90
|
+
}
|
|
91
|
+
await cleanup(client, ['test:binary']);
|
|
92
|
+
client.close();
|
|
93
|
+
});
|
|
94
|
+
// Test DELETE
|
|
95
|
+
await runTest('DELETE operation', async () => {
|
|
96
|
+
const client = new KVStorage({
|
|
97
|
+
endpoint: TEST_ENDPOINT,
|
|
98
|
+
token: TEST_TOKEN,
|
|
99
|
+
timeout: 5000,
|
|
100
|
+
});
|
|
101
|
+
await ensureClean(client, ['test:delete']);
|
|
102
|
+
await client.put('test:delete', 'to be deleted');
|
|
103
|
+
const deleted = await client.delete('test:delete');
|
|
104
|
+
if (!deleted) {
|
|
105
|
+
throw new Error('Delete should return true');
|
|
106
|
+
}
|
|
107
|
+
const notFound = await client.get('test:delete');
|
|
108
|
+
if (notFound !== null) {
|
|
109
|
+
throw new Error('Value should be null after delete');
|
|
110
|
+
}
|
|
111
|
+
client.close();
|
|
112
|
+
});
|
|
113
|
+
// Test HEAD
|
|
114
|
+
await runTest('HEAD operation', async () => {
|
|
115
|
+
const client = new KVStorage({
|
|
116
|
+
endpoint: TEST_ENDPOINT,
|
|
117
|
+
token: TEST_TOKEN,
|
|
118
|
+
timeout: 5000,
|
|
119
|
+
});
|
|
120
|
+
await ensureClean(client, ['test:head']);
|
|
121
|
+
await client.put('test:head', 'head test data');
|
|
122
|
+
const info = await client.head('test:head');
|
|
123
|
+
if (!info) {
|
|
124
|
+
throw new Error('HEAD should return info');
|
|
125
|
+
}
|
|
126
|
+
if (info['content-length'] !== '14') {
|
|
127
|
+
throw new Error(`Expected content-length 14 but got ${info['content-length']}`);
|
|
128
|
+
}
|
|
129
|
+
await cleanup(client, ['test:head']);
|
|
130
|
+
client.close();
|
|
131
|
+
});
|
|
132
|
+
// Test LIST
|
|
133
|
+
await runTest('LIST operation', async () => {
|
|
134
|
+
const client = new KVStorage({
|
|
135
|
+
endpoint: TEST_ENDPOINT,
|
|
136
|
+
token: TEST_TOKEN,
|
|
137
|
+
timeout: 5000,
|
|
138
|
+
});
|
|
139
|
+
await ensureClean(client, ['test:list:1', 'test:list:2']);
|
|
140
|
+
await client.put('test:list:1', 'data1');
|
|
141
|
+
await client.put('test:list:2', 'data2');
|
|
142
|
+
const result = await client.list({ limit: 10 });
|
|
143
|
+
if (!result.keys || !Array.isArray(result.keys)) {
|
|
144
|
+
throw new Error('LIST should return keys array');
|
|
145
|
+
}
|
|
146
|
+
await cleanup(client, ['test:list:1', 'test:list:2']);
|
|
147
|
+
client.close();
|
|
148
|
+
});
|
|
149
|
+
// Test BATCH
|
|
150
|
+
await runTest('BATCH operations', async () => {
|
|
151
|
+
const client = new KVStorage({
|
|
152
|
+
endpoint: TEST_ENDPOINT,
|
|
153
|
+
token: TEST_TOKEN,
|
|
154
|
+
timeout: 5000,
|
|
155
|
+
});
|
|
156
|
+
// Clean up first to avoid "Key already exists" errors
|
|
157
|
+
await ensureClean(client, ['test:batch:1', 'test:batch:2']);
|
|
158
|
+
const response = await client.batch([
|
|
159
|
+
{ op: 'put', key: 'test:batch:1', value: 'batch1' },
|
|
160
|
+
{ op: 'put', key: 'test:batch:2', value: 'batch2' },
|
|
161
|
+
{ op: 'get', key: 'test:batch:1' },
|
|
162
|
+
]);
|
|
163
|
+
if (!response.results || response.results.length !== 3) {
|
|
164
|
+
throw new Error('BATCH should return 3 results');
|
|
165
|
+
}
|
|
166
|
+
const getResult = response.results.find(r => 'get' in r && r.get.key === 'test:batch:1');
|
|
167
|
+
if (!getResult || !getResult.get.found || getResult.get.value !== 'batch1') {
|
|
168
|
+
throw new Error('BATCH GET failed');
|
|
169
|
+
}
|
|
170
|
+
await cleanup(client, ['test:batch:1', 'test:batch:2']);
|
|
171
|
+
client.close();
|
|
172
|
+
});
|
|
173
|
+
// Test METRICS
|
|
174
|
+
await runTest('METRICS endpoint', async () => {
|
|
175
|
+
const client = new KVStorage({
|
|
176
|
+
endpoint: TEST_ENDPOINT,
|
|
177
|
+
token: TEST_TOKEN,
|
|
178
|
+
timeout: 5000,
|
|
179
|
+
});
|
|
180
|
+
const metrics = await client.metrics();
|
|
181
|
+
if (typeof metrics !== 'string' || metrics.length === 0) {
|
|
182
|
+
throw new Error('METRICS should return string');
|
|
183
|
+
}
|
|
184
|
+
if (!metrics.includes('kv_storage_')) {
|
|
185
|
+
throw new Error('METRICS should contain kv_storage_');
|
|
186
|
+
}
|
|
187
|
+
client.close();
|
|
188
|
+
});
|
|
189
|
+
// Summary
|
|
190
|
+
log('\n' + '='.repeat(50));
|
|
191
|
+
log(`Tests passed: ${passed}`);
|
|
192
|
+
log(`Tests failed: ${failed}`);
|
|
193
|
+
log('='.repeat(50));
|
|
194
|
+
if (failed > 0) {
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
main().catch((error) => {
|
|
199
|
+
console.error('Fatal error:', error);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kv-storage-client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Fast Node.js client for kv-storage HTTP/2 server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "npm run build && node dist/index.test.js",
|
|
11
|
+
"lint": "eslint src/",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"example": "node example.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"kv-storage",
|
|
17
|
+
"key-value",
|
|
18
|
+
"storage",
|
|
19
|
+
"http2",
|
|
20
|
+
"client"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^25.2.3",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
27
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
28
|
+
"eslint": "^10.0.0",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"import": "./dist/index.js",
|
|
39
|
+
"types": "./dist/index.d.ts"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|