memcache 0.2.0 → 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.
@@ -0,0 +1,501 @@
1
+ import { Hookified } from 'hookified';
2
+ import { Socket } from 'node:net';
3
+
4
+ interface MemcacheNodeOptions {
5
+ timeout?: number;
6
+ keepAlive?: boolean;
7
+ keepAliveDelay?: number;
8
+ weight?: number;
9
+ }
10
+ interface CommandOptions {
11
+ isMultiline?: boolean;
12
+ isStats?: boolean;
13
+ requestedKeys?: string[];
14
+ }
15
+ type CommandQueueItem = {
16
+ command: string;
17
+ resolve: (value: any) => void;
18
+ reject: (reason?: any) => void;
19
+ isMultiline?: boolean;
20
+ isStats?: boolean;
21
+ requestedKeys?: string[];
22
+ foundKeys?: string[];
23
+ };
24
+ /**
25
+ * MemcacheNode represents a single memcache server connection.
26
+ * It handles the socket connection, command queue, and protocol parsing for one node.
27
+ */
28
+ declare class MemcacheNode extends Hookified {
29
+ private _host;
30
+ private _port;
31
+ private _socket;
32
+ private _timeout;
33
+ private _keepAlive;
34
+ private _keepAliveDelay;
35
+ private _weight;
36
+ private _connected;
37
+ private _commandQueue;
38
+ private _buffer;
39
+ private _currentCommand;
40
+ private _multilineData;
41
+ private _pendingValueBytes;
42
+ constructor(host: string, port: number, options?: MemcacheNodeOptions);
43
+ /**
44
+ * Get the host of this node
45
+ */
46
+ get host(): string;
47
+ /**
48
+ * Get the port of this node
49
+ */
50
+ get port(): number;
51
+ /**
52
+ * Get the unique identifier for this node (host:port format)
53
+ */
54
+ get id(): string;
55
+ /**
56
+ * Get the full uri like memcache://localhost:11211
57
+ */
58
+ get uri(): string;
59
+ /**
60
+ * Get the socket connection
61
+ */
62
+ get socket(): Socket | undefined;
63
+ /**
64
+ * Get the weight of this node (used for consistent hashing distribution)
65
+ */
66
+ get weight(): number;
67
+ /**
68
+ * Set the weight of this node (used for consistent hashing distribution)
69
+ */
70
+ set weight(value: number);
71
+ /**
72
+ * Get the keepAlive setting for this node
73
+ */
74
+ get keepAlive(): boolean;
75
+ /**
76
+ * Set the keepAlive setting for this node
77
+ */
78
+ set keepAlive(value: boolean);
79
+ /**
80
+ * Get the keepAliveDelay setting for this node
81
+ */
82
+ get keepAliveDelay(): number;
83
+ /**
84
+ * Set the keepAliveDelay setting for this node
85
+ */
86
+ set keepAliveDelay(value: number);
87
+ /**
88
+ * Get the command queue
89
+ */
90
+ get commandQueue(): CommandQueueItem[];
91
+ /**
92
+ * Connect to the memcache server
93
+ */
94
+ connect(): Promise<void>;
95
+ /**
96
+ * Disconnect from the memcache server
97
+ */
98
+ disconnect(): Promise<void>;
99
+ /**
100
+ * Reconnect to the memcache server by disconnecting and connecting again
101
+ */
102
+ reconnect(): Promise<void>;
103
+ /**
104
+ * Gracefully quit the connection (send quit command then disconnect)
105
+ */
106
+ quit(): Promise<void>;
107
+ /**
108
+ * Check if connected to the memcache server
109
+ */
110
+ isConnected(): boolean;
111
+ /**
112
+ * Send a generic command to the memcache server
113
+ * @param cmd The command string to send (without trailing \r\n)
114
+ * @param options Command options for response parsing
115
+ */
116
+ command(cmd: string, options?: CommandOptions): Promise<any>;
117
+ private handleData;
118
+ private processLine;
119
+ private rejectPendingCommands;
120
+ }
121
+ /**
122
+ * Factory function to create a new MemcacheNode instance.
123
+ * @param host - The hostname or IP address of the memcache server
124
+ * @param port - The port number of the memcache server
125
+ * @param options - Optional configuration for the node
126
+ * @returns A new MemcacheNode instance
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const node = createNode('localhost', 11211, {
131
+ * timeout: 5000,
132
+ * keepAlive: true,
133
+ * weight: 1
134
+ * });
135
+ * await node.connect();
136
+ * ```
137
+ */
138
+ declare function createNode(host: string, port: number, options?: MemcacheNodeOptions): MemcacheNode;
139
+
140
+ declare enum MemcacheEvents {
141
+ CONNECT = "connect",
142
+ QUIT = "quit",
143
+ HIT = "hit",
144
+ MISS = "miss",
145
+ ERROR = "error",
146
+ WARN = "warn",
147
+ INFO = "info",
148
+ TIMEOUT = "timeout",
149
+ CLOSE = "close"
150
+ }
151
+ interface HashProvider {
152
+ name: string;
153
+ nodes: Array<MemcacheNode>;
154
+ addNode: (node: MemcacheNode) => void;
155
+ removeNode: (id: string) => void;
156
+ getNode: (id: string) => MemcacheNode | undefined;
157
+ getNodesByKey: (key: string) => Array<MemcacheNode>;
158
+ }
159
+ interface MemcacheOptions {
160
+ /**
161
+ * Array of node URIs or MemcacheNode instances to add to the consistent hashing ring.
162
+ * Examples: ["localhost:11211", "memcache://192.168.1.100:11212", "server3:11213"]
163
+ * Can also pass MemcacheNode instances directly: [createNode("localhost", 11211), createNode("server2", 11211)]
164
+ */
165
+ nodes?: (string | MemcacheNode)[];
166
+ /**
167
+ * The timeout for Memcache operations.
168
+ * @default 5000
169
+ */
170
+ timeout?: number;
171
+ /**
172
+ * Whether to keep the connection alive.
173
+ * @default true
174
+ */
175
+ keepAlive?: boolean;
176
+ /**
177
+ * The delay before the connection is kept alive.
178
+ * @default 1000
179
+ */
180
+ keepAliveDelay?: number;
181
+ /**
182
+ * The hash provider used to determine the distribution on each item is placed based
183
+ * on the number of nodes and hashing. By default it uses KetamaHash as the provider
184
+ */
185
+ hash?: HashProvider;
186
+ }
187
+ interface MemcacheStats {
188
+ [key: string]: string;
189
+ }
190
+ declare class Memcache extends Hookified {
191
+ private _nodes;
192
+ private _timeout;
193
+ private _keepAlive;
194
+ private _keepAliveDelay;
195
+ private _hash;
196
+ constructor(options?: MemcacheOptions);
197
+ /**
198
+ * Get the list of nodes
199
+ * @returns {MemcacheNode[]} Array of MemcacheNode
200
+ */
201
+ get nodes(): MemcacheNode[];
202
+ /**
203
+ * Get the list of node IDs (e.g., ["localhost:11211", "127.0.0.1:11212"])
204
+ * @returns {string[]} Array of node ID strings
205
+ */
206
+ get nodeIds(): string[];
207
+ /**
208
+ * Get the hash provider used for consistent hashing distribution.
209
+ * @returns {HashProvider} The current hash provider instance
210
+ * @default KetamaHash
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const client = new Memcache();
215
+ * const hashProvider = client.hash;
216
+ * console.log(hashProvider.name); // "ketama"
217
+ * ```
218
+ */
219
+ get hash(): HashProvider;
220
+ /**
221
+ * Set the hash provider used for consistent hashing distribution.
222
+ * This allows you to customize the hashing strategy for distributing keys across nodes.
223
+ * @param {HashProvider} hash - The hash provider instance to use
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * const client = new Memcache();
228
+ * const customHashProvider = new KetamaHash();
229
+ * client.hash = customHashProvider;
230
+ * ```
231
+ */
232
+ set hash(hash: HashProvider);
233
+ /**
234
+ * Get the timeout for Memcache operations.
235
+ * @returns {number}
236
+ * @default 5000
237
+ */
238
+ get timeout(): number;
239
+ /**
240
+ * Set the timeout for Memcache operations.
241
+ * @param {number} value
242
+ * @default 5000
243
+ */
244
+ set timeout(value: number);
245
+ /**
246
+ * Get the keepAlive setting for the Memcache connection.
247
+ * @returns {boolean}
248
+ * @default true
249
+ */
250
+ get keepAlive(): boolean;
251
+ /**
252
+ * Set the keepAlive setting for the Memcache connection.
253
+ * Updates all existing nodes with the new value.
254
+ * Note: To apply the new value, you need to call reconnect() on the nodes.
255
+ * @param {boolean} value
256
+ * @default true
257
+ */
258
+ set keepAlive(value: boolean);
259
+ /**
260
+ * Get the delay before the connection is kept alive.
261
+ * @returns {number}
262
+ * @default 1000
263
+ */
264
+ get keepAliveDelay(): number;
265
+ /**
266
+ * Set the delay before the connection is kept alive.
267
+ * Updates all existing nodes with the new value.
268
+ * Note: To apply the new value, you need to call reconnect() on the nodes.
269
+ * @param {number} value
270
+ * @default 1000
271
+ */
272
+ set keepAliveDelay(value: number);
273
+ /**
274
+ * Get an array of all MemcacheNode instances
275
+ * @returns {MemcacheNode[]}
276
+ */
277
+ getNodes(): MemcacheNode[];
278
+ /**
279
+ * Get a specific node by its ID
280
+ * @param {string} id - The node ID (e.g., "localhost:11211")
281
+ * @returns {MemcacheNode | undefined}
282
+ */
283
+ getNode(id: string): MemcacheNode | undefined;
284
+ /**
285
+ * Add a new node to the cluster
286
+ * @param {string | MemcacheNode} uri - Node URI (e.g., "localhost:11212") or a MemcacheNode instance
287
+ * @param {number} weight - Optional weight for consistent hashing (only used for string URIs)
288
+ */
289
+ addNode(uri: string | MemcacheNode, weight?: number): Promise<void>;
290
+ /**
291
+ * Remove a node from the cluster
292
+ * @param {string} uri - Node URI (e.g., "localhost:11212")
293
+ */
294
+ removeNode(uri: string): Promise<void>;
295
+ /**
296
+ * Parse a URI string into host and port.
297
+ * Supports multiple formats:
298
+ * - Simple: "localhost:11211" or "localhost"
299
+ * - Protocol: "memcache://localhost:11211", "memcached://localhost:11211", "tcp://localhost:11211"
300
+ * - IPv6: "[::1]:11211" or "memcache://[2001:db8::1]:11212"
301
+ * - Unix socket: "/var/run/memcached.sock" or "unix:///var/run/memcached.sock"
302
+ *
303
+ * @param {string} uri - URI string
304
+ * @returns {{ host: string; port: number }} Object containing host and port (port is 0 for Unix sockets)
305
+ * @throws {Error} If URI format is invalid
306
+ */
307
+ parseUri(uri: string): {
308
+ host: string;
309
+ port: number;
310
+ };
311
+ /**
312
+ * Connect to all Memcache servers or a specific node.
313
+ * @param {string} nodeId - Optional node ID to connect to (e.g., "localhost:11211")
314
+ * @returns {Promise<void>}
315
+ */
316
+ connect(nodeId?: string): Promise<void>;
317
+ /**
318
+ * Get a value from the Memcache server.
319
+ * When multiple nodes are returned by the hash provider (for replication),
320
+ * queries all nodes and returns the first successful result.
321
+ * @param {string} key
322
+ * @returns {Promise<string | undefined>}
323
+ */
324
+ get(key: string): Promise<string | undefined>;
325
+ /**
326
+ * Get multiple values from the Memcache server.
327
+ * When multiple nodes are returned by the hash provider (for replication),
328
+ * queries all replica nodes and returns the first successful result for each key.
329
+ * @param keys {string[]}
330
+ * @returns {Promise<Map<string, string>>}
331
+ */
332
+ gets(keys: string[]): Promise<Map<string, string>>;
333
+ /**
334
+ * Check-And-Set: Store a value only if it hasn't been modified since last fetch.
335
+ * When multiple nodes are returned by the hash provider (for replication),
336
+ * executes on all nodes and returns true only if all succeed.
337
+ * @param key {string}
338
+ * @param value {string}
339
+ * @param casToken {string}
340
+ * @param exptime {number}
341
+ * @param flags {number}
342
+ * @returns {Promise<boolean>}
343
+ */
344
+ cas(key: string, value: string, casToken: string, exptime?: number, flags?: number): Promise<boolean>;
345
+ /**
346
+ * Set a value in the Memcache server.
347
+ * When multiple nodes are returned by the hash provider (for replication),
348
+ * executes on all nodes and returns true only if all succeed.
349
+ * @param key {string}
350
+ * @param value {string}
351
+ * @param exptime {number}
352
+ * @param flags {number}
353
+ * @returns {Promise<boolean>}
354
+ */
355
+ set(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>;
356
+ /**
357
+ * Add a value to the Memcache server (only if key doesn't exist).
358
+ * When multiple nodes are returned by the hash provider (for replication),
359
+ * executes on all nodes and returns true only if all succeed.
360
+ * @param key {string}
361
+ * @param value {string}
362
+ * @param exptime {number}
363
+ * @param flags {number}
364
+ * @returns {Promise<boolean>}
365
+ */
366
+ add(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>;
367
+ /**
368
+ * Replace a value in the Memcache server (only if key exists).
369
+ * When multiple nodes are returned by the hash provider (for replication),
370
+ * executes on all nodes and returns true only if all succeed.
371
+ * @param key {string}
372
+ * @param value {string}
373
+ * @param exptime {number}
374
+ * @param flags {number}
375
+ * @returns {Promise<boolean>}
376
+ */
377
+ replace(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>;
378
+ /**
379
+ * Append a value to an existing key in the Memcache server.
380
+ * When multiple nodes are returned by the hash provider (for replication),
381
+ * executes on all nodes and returns true only if all succeed.
382
+ * @param key {string}
383
+ * @param value {string}
384
+ * @returns {Promise<boolean>}
385
+ */
386
+ append(key: string, value: string): Promise<boolean>;
387
+ /**
388
+ * Prepend a value to an existing key in the Memcache server.
389
+ * When multiple nodes are returned by the hash provider (for replication),
390
+ * executes on all nodes and returns true only if all succeed.
391
+ * @param key {string}
392
+ * @param value {string}
393
+ * @returns {Promise<boolean>}
394
+ */
395
+ prepend(key: string, value: string): Promise<boolean>;
396
+ /**
397
+ * Delete a value from the Memcache server.
398
+ * When multiple nodes are returned by the hash provider (for replication),
399
+ * executes on all nodes and returns true only if all succeed.
400
+ * @param key {string}
401
+ * @returns {Promise<boolean>}
402
+ */
403
+ delete(key: string): Promise<boolean>;
404
+ /**
405
+ * Increment a value in the Memcache server.
406
+ * When multiple nodes are returned by the hash provider (for replication),
407
+ * executes on all nodes and returns the first successful result.
408
+ * @param key {string}
409
+ * @param value {number}
410
+ * @returns {Promise<number | undefined>}
411
+ */
412
+ incr(key: string, value?: number): Promise<number | undefined>;
413
+ /**
414
+ * Decrement a value in the Memcache server.
415
+ * When multiple nodes are returned by the hash provider (for replication),
416
+ * executes on all nodes and returns the first successful result.
417
+ * @param key {string}
418
+ * @param value {number}
419
+ * @returns {Promise<number | undefined>}
420
+ */
421
+ decr(key: string, value?: number): Promise<number | undefined>;
422
+ /**
423
+ * Touch a value in the Memcache server (update expiration time).
424
+ * When multiple nodes are returned by the hash provider (for replication),
425
+ * executes on all nodes and returns true only if all succeed.
426
+ * @param key {string}
427
+ * @param exptime {number}
428
+ * @returns {Promise<boolean>}
429
+ */
430
+ touch(key: string, exptime: number): Promise<boolean>;
431
+ /**
432
+ * Flush all values from all Memcache servers.
433
+ * @param delay {number}
434
+ * @returns {Promise<boolean>}
435
+ */
436
+ flush(delay?: number): Promise<boolean>;
437
+ /**
438
+ * Get statistics from all Memcache servers.
439
+ * @param type {string}
440
+ * @returns {Promise<Map<string, MemcacheStats>>}
441
+ */
442
+ stats(type?: string): Promise<Map<string, MemcacheStats>>;
443
+ /**
444
+ * Get the Memcache server version from all nodes.
445
+ * @returns {Promise<Map<string, string>>} Map of node IDs to version strings
446
+ */
447
+ version(): Promise<Map<string, string>>;
448
+ /**
449
+ * Quit all connections gracefully.
450
+ * @returns {Promise<void>}
451
+ */
452
+ quit(): Promise<void>;
453
+ /**
454
+ * Disconnect all connections.
455
+ * @returns {Promise<void>}
456
+ */
457
+ disconnect(): Promise<void>;
458
+ /**
459
+ * Reconnect all nodes by disconnecting and connecting them again.
460
+ * @returns {Promise<void>}
461
+ */
462
+ reconnect(): Promise<void>;
463
+ /**
464
+ * Check if any node is connected to a Memcache server.
465
+ * @returns {boolean}
466
+ */
467
+ isConnected(): boolean;
468
+ /**
469
+ * Get the nodes for a given key using consistent hashing, with lazy connection.
470
+ * This method will automatically connect to the nodes if they're not already connected.
471
+ * Returns an array to support replication strategies.
472
+ * @param {string} key - The cache key
473
+ * @returns {Promise<Array<MemcacheNode>>} The nodes responsible for this key
474
+ * @throws {Error} If no nodes are available for the key
475
+ */
476
+ getNodesByKey(key: string): Promise<Array<MemcacheNode>>;
477
+ /**
478
+ * Validates a Memcache key according to protocol requirements.
479
+ * @param {string} key - The key to validate
480
+ * @throws {Error} If the key is empty, exceeds 250 characters, or contains invalid characters
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * client.validateKey("valid-key"); // OK
485
+ * client.validateKey(""); // Throws: Key cannot be empty
486
+ * client.validateKey("a".repeat(251)); // Throws: Key length cannot exceed 250 characters
487
+ * client.validateKey("key with spaces"); // Throws: Key cannot contain spaces, newlines, or null characters
488
+ * ```
489
+ */
490
+ validateKey(key: string): void;
491
+ /**
492
+ * Update all nodes with current keepAlive settings
493
+ */
494
+ private updateNodes;
495
+ /**
496
+ * Forward events from a MemcacheNode to the Memcache instance
497
+ */
498
+ private forwardNodeEvents;
499
+ }
500
+
501
+ export { type HashProvider, Memcache, MemcacheEvents, type MemcacheOptions, type MemcacheStats, createNode, Memcache as default };