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.
package/README.md CHANGED
@@ -1,106 +1,404 @@
1
- node.js memcached client
2
- ========================
1
+ [<img src="./site/logo_medium.png" alt="Memcache Logo" align="center">](https://memcachejs.org)
2
+
3
+ [![codecov](https://codecov.io/gh/jaredwray/memcache/graph/badge.svg?token=4DUANNWiIE)](https://codecov.io/gh/jaredwray/memcache)
4
+ [![tests](https://github.com/jaredwray/memcache/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/memcache/actions/workflows/tests.yml)
5
+ [![npm](https://img.shields.io/npm/v/memcache)](https://www.npmjs.com/package/memcache)
6
+ [![npm](https://img.shields.io/npm/dm/memcache)](https://www.npmjs.com/package/memcache)
7
+ [![license](https://img.shields.io/github/license/jaredwray/memcache)](https://github.com/jaredwray/memcache/blob/main/LICENSE)
8
+
9
+ # Memcache
10
+ Nodejs Memcache Client
11
+
12
+ # Table of Contents
13
+
14
+ - [Getting Started](#getting-started)
15
+ - [Installation](#installation)
16
+ - [Basic Usage](#basic-usage)
17
+ - [Custom Connection](#custom-connection)
18
+ - [API](#api)
19
+ - [Constructor](#constructor)
20
+ - [Properties](#properties)
21
+ - [Connection Management](#connection-management)
22
+ - [Node Management](#node-management)
23
+ - [Data Storage Operations](#data-storage-operations)
24
+ - [String Modification Operations](#string-modification-operations)
25
+ - [Deletion & Expiration](#deletion--expiration)
26
+ - [Numeric Operations](#numeric-operations)
27
+ - [Server Management & Statistics](#server-management--statistics)
28
+ - [Validation](#validation)
29
+ - [Helper Functions](#helper-functions)
30
+ - [Hooks and Events](#hooks-and-events)
31
+ - [Events](#events)
32
+ - [Available Events](#available-events)
33
+ - [Hooks](#hooks)
34
+ - [Available Hooks](#available-hooks)
35
+ - [get(key)](#getkey)
36
+ - [set(key, value, exptime?, flags?)](#setkey-value-exptime-flags)
37
+ - [gets(keys[])](#getskeys)
38
+ - [add(key, value, exptime?, flags?)](#addkey-value-exptime-flags)
39
+ - [replace(key, value, exptime?, flags?)](#replacekey-value-exptime-flags)
40
+ - [append(key, value)](#appendkey-value)
41
+ - [prepend(key, value)](#prependkey-value)
42
+ - [delete(key)](#deletekey)
43
+ - [incr(key, value?)](#incrkey-value)
44
+ - [decr(key, value?)](#decrkey-value)
45
+ - [touch(key, exptime)](#touchkey-exptime)
46
+ - [Hook Examples](#hook-examples)
47
+ - [Contributing](#contributing)
48
+ - [License and Copyright](#license-and-copyright)
3
49
 
4
- A pure-JavaScript memcached library for node.
50
+ # Getting Started
5
51
 
52
+ ## Installation
6
53
 
7
- Tests
8
- -----
54
+ ```bash
55
+ npm install memcache
56
+ ```
9
57
 
10
- To run the test suite, first insall <a href="http://github.com/visionmedia/expresso">expresso</a>,
11
- then run <code>make test</code>.
58
+ or with pnpm:
12
59
 
13
- If you have <a href="http://github.com/visionmedia/node-jscoverage">node-jscoverage</a> you can
14
- also <code>make test-cov</code> for coverage, but that's pretty nerdy.
60
+ ```bash
61
+ pnpm add memcache
62
+ ```
15
63
 
64
+ ## Basic Usage
16
65
 
17
- Usage
18
- -----
66
+ ```javascript
67
+ import { Memcache } from 'memcache';
19
68
 
20
- Create a Client object to start working.
21
- Host and port can be passed to the constructor or set afterwards.
22
- They have sensible defaults.
69
+ // Create a new client
70
+ const client = new Memcache();
23
71
 
24
- var memcache = require('./memcache');
72
+ // Set a value
73
+ await client.set('mykey', 'Hello, Memcache!');
25
74
 
26
- var client = new memcache.Client(port, host);
27
- client.port = 11211;
28
- client.host = 'localhost';
75
+ // Get a value
76
+ const value = await client.get('mykey');
77
+ console.log(value); // ['Hello, Memcache!']
29
78
 
30
- The Client object emits 4 important events - connect, close, timeout and error.
79
+ // Delete a value
80
+ await client.delete('mykey');
31
81
 
32
- client.on('connect', function(){
33
- // no arguments - we've connected
34
- });
82
+ // Close the connection
83
+ await client.quit();
84
+ ```
35
85
 
36
- client.on('close', function(){
37
- // no arguments - connection has been closed
38
- });
86
+ # API
39
87
 
40
- client.on('timeout', function(){
41
- // no arguments - socket timed out
42
- });
88
+ ## Constructor
43
89
 
44
- client.on('error', function(e){
45
- // there was an error - exception is 1st argument
46
- });
90
+ ```typescript
91
+ new Memcache(options?: MemcacheOptions)
92
+ ```
47
93
 
48
- After connecting, you can start to make requests.
94
+ Creates a new Memcache client instance.
49
95
 
50
- client.get('key', function(result, error){
96
+ ### Options
51
97
 
52
- // all of the callbacks have two arguments.
53
- // 'result' may contain things which aren't great, but
54
- // aren't really errors, like 'NOT_STORED'
98
+ - `nodes?: (string | MemcacheNode)[]` - Array of node URIs or MemcacheNode instances
99
+ - Examples: `["localhost:11211", "memcache://192.168.1.100:11212"]`
100
+ - `timeout?: number` - Operation timeout in milliseconds (default: 5000)
101
+ - `keepAlive?: boolean` - Keep connection alive (default: true)
102
+ - `keepAliveDelay?: number` - Keep alive delay in milliseconds (default: 1000)
103
+ - `hash?: HashProvider` - Hash provider for consistent hashing (default: KetamaHash)
55
104
 
56
- });
105
+ ## Properties
57
106
 
58
- client.set('key', 'value', function(result, error){
107
+ ### `nodes: MemcacheNode[]` (readonly)
108
+ Returns the list of all MemcacheNode instances in the cluster.
59
109
 
60
- // lifetime is optional. the default is
61
- // to never expire (0)
110
+ ### `nodeIds: string[]` (readonly)
111
+ Returns the list of node IDs (e.g., `["localhost:11211", "127.0.0.1:11212"]`).
62
112
 
63
- }, lifetime);
113
+ ### `hash: HashProvider`
114
+ Get or set the hash provider used for consistent hashing distribution.
64
115
 
65
- client.delete('key', function(result, error){
116
+ ### `timeout: number`
117
+ Get or set the timeout for operations in milliseconds (default: 5000).
66
118
 
67
- // delete a key from cache.
68
- });
119
+ ### `keepAlive: boolean`
120
+ Get or set the keepAlive setting. Updates all existing nodes. Requires `reconnect()` to apply changes.
69
121
 
70
- client.version(function(result, error)){
122
+ ### `keepAliveDelay: number`
123
+ Get or set the keep alive delay in milliseconds. Updates all existing nodes. Requires `reconnect()` to apply changes.
71
124
 
72
- // grab the server version
73
- });
125
+ ## Connection Management
74
126
 
127
+ ### `connect(nodeId?: string): Promise<void>`
128
+ Connect to all Memcache servers or a specific node.
75
129
 
76
- There are all the commands you would expect.
130
+ ### `disconnect(): Promise<void>`
131
+ Disconnect all connections.
77
132
 
78
- // all of the different "store" operations
79
- // (lifetime & flags are both optional)
80
- client.set(key, value, callback, lifetime, flags);
81
- client.add(key, value, callback, lifetime, flags);
82
- client.replace(key, value, callback, lifetime, flags);
83
- client.append(key, value, callback, lifetime, flags);
84
- client.prepend(key, value, callback, lifetime, flags);
85
- client.cas(key, value, unique, callback, lifetime, flags);
133
+ ### `reconnect(): Promise<void>`
134
+ Reconnect all nodes by disconnecting and connecting them again.
86
135
 
87
- // increment and decrement (named differently to the server commands - for now!)
88
- // (value is optional, defaults to 1)
89
- client.increment('key', value, callback);
90
- client.decrement('key', value, callback);
136
+ ### `quit(): Promise<void>`
137
+ Quit all connections gracefully.
91
138
 
92
- // statistics. the success argument to the callback
93
- // is a key=>value object
94
- client.stats(callback);
95
- client.stats('settings', callback);
96
- client.stats('items', callback);
97
- client.stats('mongeese', callback);
139
+ ### `isConnected(): boolean`
140
+ Check if any node is connected to a Memcache server.
98
141
 
99
- Once you're done, close the connection.
142
+ ## Node Management
100
143
 
101
- client.close();
144
+ ### `getNodes(): MemcacheNode[]`
145
+ Get an array of all MemcacheNode instances.
102
146
 
103
- There might be bugs. I'd like to know about them.
147
+ ### `getNode(id: string): MemcacheNode | undefined`
148
+ Get a specific node by its ID (e.g., `"localhost:11211"`).
104
149
 
105
- I bet you also want to read the <a href="http://github.com/memcached/memcached/blob/master/doc/protocol.txt">memcached
106
- protocol doc</a>. It's exciting! It also explains possible error messages.
150
+ ### `addNode(uri: string | MemcacheNode, weight?: number): Promise<void>`
151
+ Add a new node to the cluster. Throws error if node already exists.
152
+
153
+ ### `removeNode(uri: string): Promise<void>`
154
+ Remove a node from the cluster.
155
+
156
+ ### `getNodesByKey(key: string): Promise<MemcacheNode[]>`
157
+ Get the nodes for a given key using consistent hashing. Automatically connects to nodes if not already connected.
158
+
159
+ ### `parseUri(uri: string): { host: string; port: number }`
160
+ Parse a URI string into host and port. Supports formats:
161
+ - Simple: `"localhost:11211"` or `"localhost"`
162
+ - Protocol: `"memcache://localhost:11211"`, `"tcp://localhost:11211"`
163
+ - IPv6: `"[::1]:11211"` or `"memcache://[2001:db8::1]:11212"`
164
+ - Unix socket: `"/var/run/memcached.sock"` or `"unix:///var/run/memcached.sock"`
165
+
166
+ ## Data Storage Operations
167
+
168
+ ### `get(key: string): Promise<string | undefined>`
169
+ Get a value from the Memcache server. Returns the first successful result from replica nodes.
170
+
171
+ ### `gets(keys: string[]): Promise<Map<string, string>>`
172
+ Get multiple values from the Memcache server. Returns a Map with keys to values.
173
+
174
+ ### `set(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>`
175
+ Set a value in the Memcache server. Returns true only if all replica nodes succeed.
176
+ - `exptime` - Expiration time in seconds (default: 0 = never expire)
177
+ - `flags` - Flags/metadata (default: 0)
178
+
179
+ ### `add(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>`
180
+ Add a value (only if key doesn't exist). Returns true only if all replica nodes succeed.
181
+
182
+ ### `replace(key: string, value: string, exptime?: number, flags?: number): Promise<boolean>`
183
+ Replace a value (only if key exists). Returns true only if all replica nodes succeed.
184
+
185
+ ### `cas(key: string, value: string, casToken: string, exptime?: number, flags?: number): Promise<boolean>`
186
+ Check-And-Set: Store a value only if it hasn't been modified since last fetch. Returns true only if all replica nodes succeed.
187
+
188
+ ## String Modification Operations
189
+
190
+ ### `append(key: string, value: string): Promise<boolean>`
191
+ Append a value to an existing key. Returns true only if all replica nodes succeed.
192
+
193
+ ### `prepend(key: string, value: string): Promise<boolean>`
194
+ Prepend a value to an existing key. Returns true only if all replica nodes succeed.
195
+
196
+ ## Deletion & Expiration
197
+
198
+ ### `delete(key: string): Promise<boolean>`
199
+ Delete a value from the Memcache server. Returns true only if all replica nodes succeed.
200
+
201
+ ### `touch(key: string, exptime: number): Promise<boolean>`
202
+ Update expiration time without retrieving value. Returns true only if all replica nodes succeed.
203
+
204
+ ## Numeric Operations
205
+
206
+ ### `incr(key: string, value?: number): Promise<number | undefined>`
207
+ Increment a value. Returns the new value or undefined on failure.
208
+ - `value` - Amount to increment (default: 1)
209
+
210
+ ### `decr(key: string, value?: number): Promise<number | undefined>`
211
+ Decrement a value. Returns the new value or undefined on failure.
212
+ - `value` - Amount to decrement (default: 1)
213
+
214
+ ## Server Management & Statistics
215
+
216
+ ### `flush(delay?: number): Promise<boolean>`
217
+ Flush all values from all Memcache servers. Returns true if all nodes successfully flushed.
218
+ - `delay` - Optional delay in seconds before flushing
219
+
220
+ ### `stats(type?: string): Promise<Map<string, MemcacheStats>>`
221
+ Get statistics from all Memcache servers. Returns a Map of node IDs to their stats.
222
+
223
+ ### `version(): Promise<Map<string, string>>`
224
+ Get the Memcache server version from all nodes. Returns a Map of node IDs to version strings.
225
+
226
+ ## Validation
227
+
228
+ ### `validateKey(key: string): void`
229
+ Validates a Memcache key according to protocol requirements. Throws error if:
230
+ - Key is empty
231
+ - Key exceeds 250 characters
232
+ - Key contains spaces, newlines, or null characters
233
+
234
+ ## Helper Functions
235
+
236
+ ### `createNode(host: string, port: number, options?: MemcacheNodeOptions): MemcacheNode`
237
+ Factory function to create a new MemcacheNode instance.
238
+
239
+ ```javascript
240
+ import { createNode } from 'memcache';
241
+
242
+ const node = createNode('localhost', 11211, {
243
+ timeout: 5000,
244
+ keepAlive: true,
245
+ weight: 1
246
+ });
247
+ ```
248
+
249
+ # Hooks and Events
250
+
251
+ The Memcache client extends [Hookified](https://github.com/jaredwray/hookified) to provide powerful hooks and events for monitoring and customizing behavior.
252
+
253
+ ## Events
254
+
255
+ The client emits various events during operations that you can listen to:
256
+
257
+ ```javascript
258
+ const client = new Memcache();
259
+
260
+ // Connection events
261
+ client.on('connect', () => {
262
+ console.log('Connected to Memcache server');
263
+ });
264
+
265
+ client.on('close', () => {
266
+ console.log('Connection closed');
267
+ });
268
+
269
+ client.on('error', (error) => {
270
+ console.error('Error:', error);
271
+ });
272
+
273
+ client.on('timeout', () => {
274
+ console.log('Connection timeout');
275
+ });
276
+
277
+ // Cache hit/miss events
278
+ client.on('hit', (key, value) => {
279
+ console.log(`Cache hit for key: ${key}`);
280
+ });
281
+
282
+ client.on('miss', (key) => {
283
+ console.log(`Cache miss for key: ${key}`);
284
+ });
285
+ ```
286
+
287
+ ## Available Events
288
+
289
+ - `connect` - Emitted when connection to Memcache server is established
290
+ - `close` - Emitted when connection is closed
291
+ - `error` - Emitted when an error occurs
292
+ - `timeout` - Emitted when a connection timeout occurs
293
+ - `hit` - Emitted when a key is found in cache (includes key and value)
294
+ - `miss` - Emitted when a key is not found in cache
295
+ - `quit` - Emitted when quit command is sent
296
+ - `warn` - Emitted for warning messages
297
+ - `info` - Emitted for informational messages
298
+
299
+ ## Hooks
300
+
301
+ Hooks allow you to intercept and modify behavior before and after operations. Every operation supports `before` and `after` hooks.
302
+
303
+ ```javascript
304
+ const client = new Memcache();
305
+
306
+ // Add a before hook for get operations
307
+ client.onHook('before:get', async ({ key }) => {
308
+ console.log(`Getting key: ${key}`);
309
+ });
310
+
311
+ // Add an after hook for set operations
312
+ client.onHook('after:set', async ({ key, value, success }) => {
313
+ if (success) {
314
+ console.log(`Successfully set ${key}`);
315
+ }
316
+ });
317
+
318
+ // Hooks can be async and modify behavior
319
+ client.onHook('before:set', async ({ key, value }) => {
320
+ console.log(`About to set ${key} = ${value}`);
321
+ // Perform validation, logging, etc.
322
+ });
323
+ ```
324
+
325
+ ## Available Hooks
326
+
327
+ All operations support before and after hooks with specific parameters:
328
+
329
+ ## get(key)
330
+ - `before:get` - `{ key }`
331
+ - `after:get` - `{ key, value }` (value is array or undefined)
332
+
333
+ ## set(key, value, exptime?, flags?)
334
+ - `before:set` - `{ key, value, exptime, flags }`
335
+ - `after:set` - `{ key, value, exptime, flags, success }`
336
+
337
+ ## gets(keys[])
338
+ - `before:gets` - `{ keys }`
339
+ - `after:gets` - `{ keys, values }` (values is a Map)
340
+
341
+ ## add(key, value, exptime?, flags?)
342
+ - `before:add` - `{ key, value, exptime, flags }`
343
+ - `after:add` - `{ key, value, exptime, flags, success }`
344
+
345
+ ## replace(key, value, exptime?, flags?)
346
+ - `before:replace` - `{ key, value, exptime, flags }`
347
+ - `after:replace` - `{ key, value, exptime, flags, success }`
348
+
349
+ ## append(key, value)
350
+ - `before:append` - `{ key, value }`
351
+ - `after:append` - `{ key, value, success }`
352
+
353
+ ## prepend(key, value)
354
+ - `before:prepend` - `{ key, value }`
355
+ - `after:prepend` - `{ key, value, success }`
356
+
357
+ ## delete(key)
358
+ - `before:delete` - `{ key }`
359
+ - `after:delete` - `{ key, success }`
360
+
361
+ ## incr(key, value?)
362
+ - `before:incr` - `{ key, value }`
363
+ - `after:incr` - `{ key, value, newValue }`
364
+
365
+ ## decr(key, value?)
366
+ - `before:decr` - `{ key, value }`
367
+ - `after:decr` - `{ key, value, newValue }`
368
+
369
+ ## touch(key, exptime)
370
+ - `before:touch` - `{ key, exptime }`
371
+ - `after:touch` - `{ key, exptime, success }`
372
+
373
+ ## Hook Examples
374
+
375
+ ```javascript
376
+ const client = new Memcache();
377
+
378
+ // Log all get operations
379
+ client.onHook('before:get', async ({ key }) => {
380
+ console.log(`[GET] Fetching key: ${key}`);
381
+ });
382
+
383
+ client.onHook('after:get', async ({ key, value }) => {
384
+ console.log(`[GET] Key: ${key}, Found: ${value !== undefined}`);
385
+ });
386
+
387
+ // Log all set operations with timing
388
+ client.onHook('before:set', async (context) => {
389
+ context.startTime = Date.now();
390
+ });
391
+
392
+ client.onHook('after:set', async (context) => {
393
+ const duration = Date.now() - context.startTime;
394
+ console.log(`[SET] Key: ${context.key}, Success: ${context.success}, Time: ${duration}ms`);
395
+ });
396
+ ```
397
+
398
+ # Contributing
399
+
400
+ Please read our [Contributing Guidelines](./CONTRIBUTING.md) and also our [Code of Conduct](./CODE_OF_CONDUCT.md).
401
+
402
+ # License and Copyright
403
+
404
+ [MIT & Copyright (c) Jared Wray](https://github.com/jaredwray/memcache/blob/main/LICENSE)
package/SECURITY.md ADDED
@@ -0,0 +1,3 @@
1
+ # Security Policy
2
+
3
+ We work hard to keep up to date with continual updates to this project. If there are concerns / security related issues please create an issue as soon as possible with how to replicate. If it is urgent please email me@jaredwray.com and I will respond as soon as possible as we take these matters seriously.
package/biome.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3
+ "vcs": {
4
+ "enabled": false,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false,
10
+ "includes": ["src/**/*", "test/**/*", "benchmark/**/*"]
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "tab"
15
+ },
16
+ "linter": {
17
+ "enabled": true,
18
+ "rules": {
19
+ "recommended": true
20
+ }
21
+ },
22
+ "javascript": {
23
+ "formatter": {
24
+ "quoteStyle": "double"
25
+ }
26
+ },
27
+ "assist": {
28
+ "enabled": true,
29
+ "actions": {
30
+ "source": {
31
+ "organizeImports": "on"
32
+ }
33
+ }
34
+ }
35
+ }