memcache 1.0.0 → 1.2.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 +82 -4
- package/dist/index.cjs +50 -106
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +50 -106
- package/package.json +31 -21
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -14
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -6
- package/.github/workflows/code-coverage.yaml +0 -41
- package/.github/workflows/codeql.yaml +0 -75
- package/.github/workflows/release.yaml +0 -41
- package/.github/workflows/tests.yaml +0 -40
- package/.nvmrc +0 -1
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -27
- package/SECURITY.md +0 -3
- package/biome.json +0 -35
- package/docker-compose.yml +0 -24
- package/pnpm-workspace.yaml +0 -2
- package/site/favicon.ico +0 -0
- package/site/logo.ai +16 -7243
- package/site/logo.png +0 -0
- package/site/logo.svg +0 -7
- package/site/logo.webp +0 -0
- package/site/logo_medium.png +0 -0
- package/site/logo_small.png +0 -0
- package/src/index.ts +0 -1130
- package/src/ketama.ts +0 -449
- package/src/node.ts +0 -488
- package/test/index.test.ts +0 -2734
- package/test/ketama.test.ts +0 -526
- package/test/memcache-node-instances.test.ts +0 -102
- package/test/node.test.ts +0 -809
- package/tsconfig.json +0 -29
- package/vitest.config.ts +0 -16
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
[<img src="
|
|
1
|
+
[<img src="https://jaredwray.com/images/memcache.svg" alt="Memcache Logo" align="center">](https://memcachejs.org)
|
|
2
2
|
|
|
3
3
|
[](https://codecov.io/gh/jaredwray/memcache)
|
|
4
|
-
[](https://github.com/jaredwray/memcache/actions/workflows/tests.yaml)
|
|
5
5
|
[](https://www.npmjs.com/package/memcache)
|
|
6
6
|
[](https://www.npmjs.com/package/memcache)
|
|
7
7
|
[](https://github.com/jaredwray/memcache/blob/main/LICENSE)
|
|
@@ -83,15 +83,93 @@ await client.delete('mykey');
|
|
|
83
83
|
await client.quit();
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
You can also just pass in the `uri` into the constructor
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// Single node as string
|
|
90
|
+
const client = new Memcache('localhost:11211');
|
|
91
|
+
|
|
92
|
+
// Single node with protocol
|
|
93
|
+
const client = new Memcache('memcache://192.168.1.100:11211');
|
|
94
|
+
|
|
95
|
+
// Multiple nodes with options
|
|
96
|
+
const client = new Memcache({
|
|
97
|
+
nodes: ['localhost:11211', 'server2:11211'],
|
|
98
|
+
timeout: 10000
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
You can specify multiple Memcache nodes by passing an array of connection strings:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
import { Memcache } from 'memcache';
|
|
106
|
+
|
|
107
|
+
// Create a client with multiple nodes
|
|
108
|
+
const client = new Memcache({
|
|
109
|
+
nodes: ['localhost:11211', '192.168.1.100:11211', 'memcache://192.168.1.101:11211']
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Set and get values (automatically distributed across nodes)
|
|
113
|
+
await client.set('mykey', 'Hello, Memcache!');
|
|
114
|
+
const value = await client.get('mykey');
|
|
115
|
+
console.log(value); // ['Hello, Memcache!']
|
|
116
|
+
|
|
117
|
+
// Close the connection
|
|
118
|
+
await client.quit();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
You can also pass an array of MemcacheNode instances for advanced configuration:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
import { Memcache, createNode } from 'memcache';
|
|
125
|
+
|
|
126
|
+
// Create nodes with custom settings
|
|
127
|
+
const node1 = createNode('localhost', 11211, { weight: 2 });
|
|
128
|
+
const node2 = createNode('192.168.1.100', 11211, { weight: 1 });
|
|
129
|
+
const node3 = createNode('192.168.1.101', 11211, { weight: 1 });
|
|
130
|
+
|
|
131
|
+
// Create a client with MemcacheNode instances
|
|
132
|
+
const client = new Memcache({
|
|
133
|
+
nodes: [node1, node2, node3],
|
|
134
|
+
timeout: 10000
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// node1 will receive twice as much traffic due to higher weight
|
|
138
|
+
await client.set('mykey', 'Hello, Memcache!');
|
|
139
|
+
const value = await client.get('mykey');
|
|
140
|
+
console.log(value); // ['Hello, Memcache!']
|
|
141
|
+
|
|
142
|
+
// Close the connection
|
|
143
|
+
await client.quit();
|
|
144
|
+
```
|
|
145
|
+
|
|
86
146
|
# API
|
|
87
147
|
|
|
88
148
|
## Constructor
|
|
89
149
|
|
|
90
150
|
```typescript
|
|
91
|
-
new Memcache(options?: MemcacheOptions)
|
|
151
|
+
new Memcache(options?: string | MemcacheOptions)
|
|
92
152
|
```
|
|
93
153
|
|
|
94
|
-
Creates a new Memcache client instance.
|
|
154
|
+
Creates a new Memcache client instance. You can pass either:
|
|
155
|
+
- A **string** representing a single node URI (uses default settings)
|
|
156
|
+
- A **MemcacheOptions** object for custom configuration
|
|
157
|
+
|
|
158
|
+
**Examples:**
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
// Single node as string
|
|
162
|
+
const client = new Memcache('localhost:11211');
|
|
163
|
+
|
|
164
|
+
// Single node with protocol
|
|
165
|
+
const client = new Memcache('memcache://192.168.1.100:11211');
|
|
166
|
+
|
|
167
|
+
// Multiple nodes with options
|
|
168
|
+
const client = new Memcache({
|
|
169
|
+
nodes: ['localhost:11211', 'server2:11211'],
|
|
170
|
+
timeout: 10000
|
|
171
|
+
});
|
|
172
|
+
```
|
|
95
173
|
|
|
96
174
|
### Options
|
|
97
175
|
|
package/dist/index.cjs
CHANGED
|
@@ -702,12 +702,19 @@ var Memcache = class extends import_hookified2.Hookified {
|
|
|
702
702
|
constructor(options) {
|
|
703
703
|
super();
|
|
704
704
|
this._hash = new KetamaHash();
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
705
|
+
if (typeof options === "string") {
|
|
706
|
+
this._timeout = 5e3;
|
|
707
|
+
this._keepAlive = true;
|
|
708
|
+
this._keepAliveDelay = 1e3;
|
|
709
|
+
this.addNode(options);
|
|
710
|
+
} else {
|
|
711
|
+
this._timeout = options?.timeout || 5e3;
|
|
712
|
+
this._keepAlive = options?.keepAlive !== false;
|
|
713
|
+
this._keepAliveDelay = options?.keepAliveDelay || 1e3;
|
|
714
|
+
const nodeUris = options?.nodes || ["localhost:11211"];
|
|
715
|
+
for (const nodeUri of nodeUris) {
|
|
716
|
+
this.addNode(nodeUri);
|
|
717
|
+
}
|
|
711
718
|
}
|
|
712
719
|
}
|
|
713
720
|
/**
|
|
@@ -1056,16 +1063,8 @@ var Memcache = class extends import_hookified2.Hookified {
|
|
|
1056
1063
|
const command = `cas ${key} ${flags} ${exptime} ${bytes} ${casToken}\r
|
|
1057
1064
|
${valueStr}`;
|
|
1058
1065
|
const nodes = await this.getNodesByKey(key);
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
const result = await node.command(command);
|
|
1062
|
-
return result === "STORED";
|
|
1063
|
-
} catch {
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
});
|
|
1067
|
-
const results = await Promise.all(promises);
|
|
1068
|
-
const success = results.every((result) => result === true);
|
|
1066
|
+
const results = await this.execute(command, nodes);
|
|
1067
|
+
const success = results.every((result) => result === "STORED");
|
|
1069
1068
|
await this.afterHook("cas", {
|
|
1070
1069
|
key,
|
|
1071
1070
|
value,
|
|
@@ -1094,16 +1093,8 @@ ${valueStr}`;
|
|
|
1094
1093
|
const command = `set ${key} ${flags} ${exptime} ${bytes}\r
|
|
1095
1094
|
${valueStr}`;
|
|
1096
1095
|
const nodes = await this.getNodesByKey(key);
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
const result = await node.command(command);
|
|
1100
|
-
return result === "STORED";
|
|
1101
|
-
} catch {
|
|
1102
|
-
return false;
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
const results = await Promise.all(promises);
|
|
1106
|
-
const success = results.every((result) => result === true);
|
|
1096
|
+
const results = await this.execute(command, nodes);
|
|
1097
|
+
const success = results.every((result) => result === "STORED");
|
|
1107
1098
|
await this.afterHook("set", { key, value, exptime, flags, success });
|
|
1108
1099
|
return success;
|
|
1109
1100
|
}
|
|
@@ -1125,16 +1116,8 @@ ${valueStr}`;
|
|
|
1125
1116
|
const command = `add ${key} ${flags} ${exptime} ${bytes}\r
|
|
1126
1117
|
${valueStr}`;
|
|
1127
1118
|
const nodes = await this.getNodesByKey(key);
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
const result = await node.command(command);
|
|
1131
|
-
return result === "STORED";
|
|
1132
|
-
} catch {
|
|
1133
|
-
return false;
|
|
1134
|
-
}
|
|
1135
|
-
});
|
|
1136
|
-
const results = await Promise.all(promises);
|
|
1137
|
-
const success = results.every((result) => result === true);
|
|
1119
|
+
const results = await this.execute(command, nodes);
|
|
1120
|
+
const success = results.every((result) => result === "STORED");
|
|
1138
1121
|
await this.afterHook("add", { key, value, exptime, flags, success });
|
|
1139
1122
|
return success;
|
|
1140
1123
|
}
|
|
@@ -1156,16 +1139,8 @@ ${valueStr}`;
|
|
|
1156
1139
|
const command = `replace ${key} ${flags} ${exptime} ${bytes}\r
|
|
1157
1140
|
${valueStr}`;
|
|
1158
1141
|
const nodes = await this.getNodesByKey(key);
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
const result = await node.command(command);
|
|
1162
|
-
return result === "STORED";
|
|
1163
|
-
} catch {
|
|
1164
|
-
return false;
|
|
1165
|
-
}
|
|
1166
|
-
});
|
|
1167
|
-
const results = await Promise.all(promises);
|
|
1168
|
-
const success = results.every((result) => result === true);
|
|
1142
|
+
const results = await this.execute(command, nodes);
|
|
1143
|
+
const success = results.every((result) => result === "STORED");
|
|
1169
1144
|
await this.afterHook("replace", { key, value, exptime, flags, success });
|
|
1170
1145
|
return success;
|
|
1171
1146
|
}
|
|
@@ -1185,16 +1160,8 @@ ${valueStr}`;
|
|
|
1185
1160
|
const command = `append ${key} 0 0 ${bytes}\r
|
|
1186
1161
|
${valueStr}`;
|
|
1187
1162
|
const nodes = await this.getNodesByKey(key);
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
const result = await node.command(command);
|
|
1191
|
-
return result === "STORED";
|
|
1192
|
-
} catch {
|
|
1193
|
-
return false;
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
|
-
const results = await Promise.all(promises);
|
|
1197
|
-
const success = results.every((result) => result === true);
|
|
1163
|
+
const results = await this.execute(command, nodes);
|
|
1164
|
+
const success = results.every((result) => result === "STORED");
|
|
1198
1165
|
await this.afterHook("append", { key, value, success });
|
|
1199
1166
|
return success;
|
|
1200
1167
|
}
|
|
@@ -1214,16 +1181,8 @@ ${valueStr}`;
|
|
|
1214
1181
|
const command = `prepend ${key} 0 0 ${bytes}\r
|
|
1215
1182
|
${valueStr}`;
|
|
1216
1183
|
const nodes = await this.getNodesByKey(key);
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
const result = await node.command(command);
|
|
1220
|
-
return result === "STORED";
|
|
1221
|
-
} catch {
|
|
1222
|
-
return false;
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
const results = await Promise.all(promises);
|
|
1226
|
-
const success = results.every((result) => result === true);
|
|
1184
|
+
const results = await this.execute(command, nodes);
|
|
1185
|
+
const success = results.every((result) => result === "STORED");
|
|
1227
1186
|
await this.afterHook("prepend", { key, value, success });
|
|
1228
1187
|
return success;
|
|
1229
1188
|
}
|
|
@@ -1238,16 +1197,8 @@ ${valueStr}`;
|
|
|
1238
1197
|
await this.beforeHook("delete", { key });
|
|
1239
1198
|
this.validateKey(key);
|
|
1240
1199
|
const nodes = await this.getNodesByKey(key);
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
const result = await node.command(`delete ${key}`);
|
|
1244
|
-
return result === "DELETED";
|
|
1245
|
-
} catch {
|
|
1246
|
-
return false;
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1249
|
-
const results = await Promise.all(promises);
|
|
1250
|
-
const success = results.every((result) => result === true);
|
|
1200
|
+
const results = await this.execute(`delete ${key}`, nodes);
|
|
1201
|
+
const success = results.every((result) => result === "DELETED");
|
|
1251
1202
|
await this.afterHook("delete", { key, success });
|
|
1252
1203
|
return success;
|
|
1253
1204
|
}
|
|
@@ -1263,16 +1214,8 @@ ${valueStr}`;
|
|
|
1263
1214
|
await this.beforeHook("incr", { key, value });
|
|
1264
1215
|
this.validateKey(key);
|
|
1265
1216
|
const nodes = await this.getNodesByKey(key);
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
const result = await node.command(`incr ${key} ${value}`);
|
|
1269
|
-
return typeof result === "number" ? result : void 0;
|
|
1270
|
-
} catch {
|
|
1271
|
-
return void 0;
|
|
1272
|
-
}
|
|
1273
|
-
});
|
|
1274
|
-
const results = await Promise.all(promises);
|
|
1275
|
-
const newValue = results.find((v) => v !== void 0);
|
|
1217
|
+
const results = await this.execute(`incr ${key} ${value}`, nodes);
|
|
1218
|
+
const newValue = results.find((v) => typeof v === "number");
|
|
1276
1219
|
await this.afterHook("incr", { key, value, newValue });
|
|
1277
1220
|
return newValue;
|
|
1278
1221
|
}
|
|
@@ -1288,16 +1231,8 @@ ${valueStr}`;
|
|
|
1288
1231
|
await this.beforeHook("decr", { key, value });
|
|
1289
1232
|
this.validateKey(key);
|
|
1290
1233
|
const nodes = await this.getNodesByKey(key);
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
const result = await node.command(`decr ${key} ${value}`);
|
|
1294
|
-
return typeof result === "number" ? result : void 0;
|
|
1295
|
-
} catch {
|
|
1296
|
-
return void 0;
|
|
1297
|
-
}
|
|
1298
|
-
});
|
|
1299
|
-
const results = await Promise.all(promises);
|
|
1300
|
-
const newValue = results.find((v) => v !== void 0);
|
|
1234
|
+
const results = await this.execute(`decr ${key} ${value}`, nodes);
|
|
1235
|
+
const newValue = results.find((v) => typeof v === "number");
|
|
1301
1236
|
await this.afterHook("decr", { key, value, newValue });
|
|
1302
1237
|
return newValue;
|
|
1303
1238
|
}
|
|
@@ -1313,16 +1248,8 @@ ${valueStr}`;
|
|
|
1313
1248
|
await this.beforeHook("touch", { key, exptime });
|
|
1314
1249
|
this.validateKey(key);
|
|
1315
1250
|
const nodes = await this.getNodesByKey(key);
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
const result = await node.command(`touch ${key} ${exptime}`);
|
|
1319
|
-
return result === "TOUCHED";
|
|
1320
|
-
} catch {
|
|
1321
|
-
return false;
|
|
1322
|
-
}
|
|
1323
|
-
});
|
|
1324
|
-
const results = await Promise.all(promises);
|
|
1325
|
-
const success = results.every((result) => result === true);
|
|
1251
|
+
const results = await this.execute(`touch ${key} ${exptime}`, nodes);
|
|
1252
|
+
const success = results.every((result) => result === "TOUCHED");
|
|
1326
1253
|
await this.afterHook("touch", { key, exptime, success });
|
|
1327
1254
|
return success;
|
|
1328
1255
|
}
|
|
@@ -1438,6 +1365,23 @@ ${valueStr}`;
|
|
|
1438
1365
|
}
|
|
1439
1366
|
return nodes;
|
|
1440
1367
|
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Execute a command on the specified nodes.
|
|
1370
|
+
* @param {string} command - The memcache command string to execute
|
|
1371
|
+
* @param {MemcacheNode[]} nodes - Array of MemcacheNode instances to execute on
|
|
1372
|
+
* @param {ExecuteOptions} options - Optional execution options
|
|
1373
|
+
* @returns {Promise<unknown[]>} Promise resolving to array of results from each node
|
|
1374
|
+
*/
|
|
1375
|
+
async execute(command, nodes, options) {
|
|
1376
|
+
const promises = nodes.map(async (node) => {
|
|
1377
|
+
try {
|
|
1378
|
+
return await node.command(command, options?.commandOptions);
|
|
1379
|
+
} catch {
|
|
1380
|
+
return void 0;
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
return Promise.all(promises);
|
|
1384
|
+
}
|
|
1441
1385
|
/**
|
|
1442
1386
|
* Validates a Memcache key according to protocol requirements.
|
|
1443
1387
|
* @param {string} key - The key to validate
|
package/dist/index.d.cts
CHANGED
|
@@ -187,13 +187,17 @@ interface MemcacheOptions {
|
|
|
187
187
|
interface MemcacheStats {
|
|
188
188
|
[key: string]: string;
|
|
189
189
|
}
|
|
190
|
+
interface ExecuteOptions {
|
|
191
|
+
/** Command options passed to node.command() */
|
|
192
|
+
commandOptions?: CommandOptions;
|
|
193
|
+
}
|
|
190
194
|
declare class Memcache extends Hookified {
|
|
191
195
|
private _nodes;
|
|
192
196
|
private _timeout;
|
|
193
197
|
private _keepAlive;
|
|
194
198
|
private _keepAliveDelay;
|
|
195
199
|
private _hash;
|
|
196
|
-
constructor(options?: MemcacheOptions);
|
|
200
|
+
constructor(options?: string | MemcacheOptions);
|
|
197
201
|
/**
|
|
198
202
|
* Get the list of nodes
|
|
199
203
|
* @returns {MemcacheNode[]} Array of MemcacheNode
|
|
@@ -474,6 +478,14 @@ declare class Memcache extends Hookified {
|
|
|
474
478
|
* @throws {Error} If no nodes are available for the key
|
|
475
479
|
*/
|
|
476
480
|
getNodesByKey(key: string): Promise<Array<MemcacheNode>>;
|
|
481
|
+
/**
|
|
482
|
+
* Execute a command on the specified nodes.
|
|
483
|
+
* @param {string} command - The memcache command string to execute
|
|
484
|
+
* @param {MemcacheNode[]} nodes - Array of MemcacheNode instances to execute on
|
|
485
|
+
* @param {ExecuteOptions} options - Optional execution options
|
|
486
|
+
* @returns {Promise<unknown[]>} Promise resolving to array of results from each node
|
|
487
|
+
*/
|
|
488
|
+
execute(command: string, nodes: MemcacheNode[], options?: ExecuteOptions): Promise<unknown[]>;
|
|
477
489
|
/**
|
|
478
490
|
* Validates a Memcache key according to protocol requirements.
|
|
479
491
|
* @param {string} key - The key to validate
|
|
@@ -498,4 +510,4 @@ declare class Memcache extends Hookified {
|
|
|
498
510
|
private forwardNodeEvents;
|
|
499
511
|
}
|
|
500
512
|
|
|
501
|
-
export { type HashProvider, Memcache, MemcacheEvents, type MemcacheOptions, type MemcacheStats, createNode, Memcache as default };
|
|
513
|
+
export { type ExecuteOptions, type HashProvider, Memcache, MemcacheEvents, type MemcacheOptions, type MemcacheStats, createNode, Memcache as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -187,13 +187,17 @@ interface MemcacheOptions {
|
|
|
187
187
|
interface MemcacheStats {
|
|
188
188
|
[key: string]: string;
|
|
189
189
|
}
|
|
190
|
+
interface ExecuteOptions {
|
|
191
|
+
/** Command options passed to node.command() */
|
|
192
|
+
commandOptions?: CommandOptions;
|
|
193
|
+
}
|
|
190
194
|
declare class Memcache extends Hookified {
|
|
191
195
|
private _nodes;
|
|
192
196
|
private _timeout;
|
|
193
197
|
private _keepAlive;
|
|
194
198
|
private _keepAliveDelay;
|
|
195
199
|
private _hash;
|
|
196
|
-
constructor(options?: MemcacheOptions);
|
|
200
|
+
constructor(options?: string | MemcacheOptions);
|
|
197
201
|
/**
|
|
198
202
|
* Get the list of nodes
|
|
199
203
|
* @returns {MemcacheNode[]} Array of MemcacheNode
|
|
@@ -474,6 +478,14 @@ declare class Memcache extends Hookified {
|
|
|
474
478
|
* @throws {Error} If no nodes are available for the key
|
|
475
479
|
*/
|
|
476
480
|
getNodesByKey(key: string): Promise<Array<MemcacheNode>>;
|
|
481
|
+
/**
|
|
482
|
+
* Execute a command on the specified nodes.
|
|
483
|
+
* @param {string} command - The memcache command string to execute
|
|
484
|
+
* @param {MemcacheNode[]} nodes - Array of MemcacheNode instances to execute on
|
|
485
|
+
* @param {ExecuteOptions} options - Optional execution options
|
|
486
|
+
* @returns {Promise<unknown[]>} Promise resolving to array of results from each node
|
|
487
|
+
*/
|
|
488
|
+
execute(command: string, nodes: MemcacheNode[], options?: ExecuteOptions): Promise<unknown[]>;
|
|
477
489
|
/**
|
|
478
490
|
* Validates a Memcache key according to protocol requirements.
|
|
479
491
|
* @param {string} key - The key to validate
|
|
@@ -498,4 +510,4 @@ declare class Memcache extends Hookified {
|
|
|
498
510
|
private forwardNodeEvents;
|
|
499
511
|
}
|
|
500
512
|
|
|
501
|
-
export { type HashProvider, Memcache, MemcacheEvents, type MemcacheOptions, type MemcacheStats, createNode, Memcache as default };
|
|
513
|
+
export { type ExecuteOptions, type HashProvider, Memcache, MemcacheEvents, type MemcacheOptions, type MemcacheStats, createNode, Memcache as default };
|
package/dist/index.js
CHANGED
|
@@ -675,12 +675,19 @@ var Memcache = class extends Hookified2 {
|
|
|
675
675
|
constructor(options) {
|
|
676
676
|
super();
|
|
677
677
|
this._hash = new KetamaHash();
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
678
|
+
if (typeof options === "string") {
|
|
679
|
+
this._timeout = 5e3;
|
|
680
|
+
this._keepAlive = true;
|
|
681
|
+
this._keepAliveDelay = 1e3;
|
|
682
|
+
this.addNode(options);
|
|
683
|
+
} else {
|
|
684
|
+
this._timeout = options?.timeout || 5e3;
|
|
685
|
+
this._keepAlive = options?.keepAlive !== false;
|
|
686
|
+
this._keepAliveDelay = options?.keepAliveDelay || 1e3;
|
|
687
|
+
const nodeUris = options?.nodes || ["localhost:11211"];
|
|
688
|
+
for (const nodeUri of nodeUris) {
|
|
689
|
+
this.addNode(nodeUri);
|
|
690
|
+
}
|
|
684
691
|
}
|
|
685
692
|
}
|
|
686
693
|
/**
|
|
@@ -1029,16 +1036,8 @@ var Memcache = class extends Hookified2 {
|
|
|
1029
1036
|
const command = `cas ${key} ${flags} ${exptime} ${bytes} ${casToken}\r
|
|
1030
1037
|
${valueStr}`;
|
|
1031
1038
|
const nodes = await this.getNodesByKey(key);
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
const result = await node.command(command);
|
|
1035
|
-
return result === "STORED";
|
|
1036
|
-
} catch {
|
|
1037
|
-
return false;
|
|
1038
|
-
}
|
|
1039
|
-
});
|
|
1040
|
-
const results = await Promise.all(promises);
|
|
1041
|
-
const success = results.every((result) => result === true);
|
|
1039
|
+
const results = await this.execute(command, nodes);
|
|
1040
|
+
const success = results.every((result) => result === "STORED");
|
|
1042
1041
|
await this.afterHook("cas", {
|
|
1043
1042
|
key,
|
|
1044
1043
|
value,
|
|
@@ -1067,16 +1066,8 @@ ${valueStr}`;
|
|
|
1067
1066
|
const command = `set ${key} ${flags} ${exptime} ${bytes}\r
|
|
1068
1067
|
${valueStr}`;
|
|
1069
1068
|
const nodes = await this.getNodesByKey(key);
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
const result = await node.command(command);
|
|
1073
|
-
return result === "STORED";
|
|
1074
|
-
} catch {
|
|
1075
|
-
return false;
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
|
-
const results = await Promise.all(promises);
|
|
1079
|
-
const success = results.every((result) => result === true);
|
|
1069
|
+
const results = await this.execute(command, nodes);
|
|
1070
|
+
const success = results.every((result) => result === "STORED");
|
|
1080
1071
|
await this.afterHook("set", { key, value, exptime, flags, success });
|
|
1081
1072
|
return success;
|
|
1082
1073
|
}
|
|
@@ -1098,16 +1089,8 @@ ${valueStr}`;
|
|
|
1098
1089
|
const command = `add ${key} ${flags} ${exptime} ${bytes}\r
|
|
1099
1090
|
${valueStr}`;
|
|
1100
1091
|
const nodes = await this.getNodesByKey(key);
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
const result = await node.command(command);
|
|
1104
|
-
return result === "STORED";
|
|
1105
|
-
} catch {
|
|
1106
|
-
return false;
|
|
1107
|
-
}
|
|
1108
|
-
});
|
|
1109
|
-
const results = await Promise.all(promises);
|
|
1110
|
-
const success = results.every((result) => result === true);
|
|
1092
|
+
const results = await this.execute(command, nodes);
|
|
1093
|
+
const success = results.every((result) => result === "STORED");
|
|
1111
1094
|
await this.afterHook("add", { key, value, exptime, flags, success });
|
|
1112
1095
|
return success;
|
|
1113
1096
|
}
|
|
@@ -1129,16 +1112,8 @@ ${valueStr}`;
|
|
|
1129
1112
|
const command = `replace ${key} ${flags} ${exptime} ${bytes}\r
|
|
1130
1113
|
${valueStr}`;
|
|
1131
1114
|
const nodes = await this.getNodesByKey(key);
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
const result = await node.command(command);
|
|
1135
|
-
return result === "STORED";
|
|
1136
|
-
} catch {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
});
|
|
1140
|
-
const results = await Promise.all(promises);
|
|
1141
|
-
const success = results.every((result) => result === true);
|
|
1115
|
+
const results = await this.execute(command, nodes);
|
|
1116
|
+
const success = results.every((result) => result === "STORED");
|
|
1142
1117
|
await this.afterHook("replace", { key, value, exptime, flags, success });
|
|
1143
1118
|
return success;
|
|
1144
1119
|
}
|
|
@@ -1158,16 +1133,8 @@ ${valueStr}`;
|
|
|
1158
1133
|
const command = `append ${key} 0 0 ${bytes}\r
|
|
1159
1134
|
${valueStr}`;
|
|
1160
1135
|
const nodes = await this.getNodesByKey(key);
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1163
|
-
const result = await node.command(command);
|
|
1164
|
-
return result === "STORED";
|
|
1165
|
-
} catch {
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
});
|
|
1169
|
-
const results = await Promise.all(promises);
|
|
1170
|
-
const success = results.every((result) => result === true);
|
|
1136
|
+
const results = await this.execute(command, nodes);
|
|
1137
|
+
const success = results.every((result) => result === "STORED");
|
|
1171
1138
|
await this.afterHook("append", { key, value, success });
|
|
1172
1139
|
return success;
|
|
1173
1140
|
}
|
|
@@ -1187,16 +1154,8 @@ ${valueStr}`;
|
|
|
1187
1154
|
const command = `prepend ${key} 0 0 ${bytes}\r
|
|
1188
1155
|
${valueStr}`;
|
|
1189
1156
|
const nodes = await this.getNodesByKey(key);
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const result = await node.command(command);
|
|
1193
|
-
return result === "STORED";
|
|
1194
|
-
} catch {
|
|
1195
|
-
return false;
|
|
1196
|
-
}
|
|
1197
|
-
});
|
|
1198
|
-
const results = await Promise.all(promises);
|
|
1199
|
-
const success = results.every((result) => result === true);
|
|
1157
|
+
const results = await this.execute(command, nodes);
|
|
1158
|
+
const success = results.every((result) => result === "STORED");
|
|
1200
1159
|
await this.afterHook("prepend", { key, value, success });
|
|
1201
1160
|
return success;
|
|
1202
1161
|
}
|
|
@@ -1211,16 +1170,8 @@ ${valueStr}`;
|
|
|
1211
1170
|
await this.beforeHook("delete", { key });
|
|
1212
1171
|
this.validateKey(key);
|
|
1213
1172
|
const nodes = await this.getNodesByKey(key);
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
const result = await node.command(`delete ${key}`);
|
|
1217
|
-
return result === "DELETED";
|
|
1218
|
-
} catch {
|
|
1219
|
-
return false;
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1222
|
-
const results = await Promise.all(promises);
|
|
1223
|
-
const success = results.every((result) => result === true);
|
|
1173
|
+
const results = await this.execute(`delete ${key}`, nodes);
|
|
1174
|
+
const success = results.every((result) => result === "DELETED");
|
|
1224
1175
|
await this.afterHook("delete", { key, success });
|
|
1225
1176
|
return success;
|
|
1226
1177
|
}
|
|
@@ -1236,16 +1187,8 @@ ${valueStr}`;
|
|
|
1236
1187
|
await this.beforeHook("incr", { key, value });
|
|
1237
1188
|
this.validateKey(key);
|
|
1238
1189
|
const nodes = await this.getNodesByKey(key);
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
const result = await node.command(`incr ${key} ${value}`);
|
|
1242
|
-
return typeof result === "number" ? result : void 0;
|
|
1243
|
-
} catch {
|
|
1244
|
-
return void 0;
|
|
1245
|
-
}
|
|
1246
|
-
});
|
|
1247
|
-
const results = await Promise.all(promises);
|
|
1248
|
-
const newValue = results.find((v) => v !== void 0);
|
|
1190
|
+
const results = await this.execute(`incr ${key} ${value}`, nodes);
|
|
1191
|
+
const newValue = results.find((v) => typeof v === "number");
|
|
1249
1192
|
await this.afterHook("incr", { key, value, newValue });
|
|
1250
1193
|
return newValue;
|
|
1251
1194
|
}
|
|
@@ -1261,16 +1204,8 @@ ${valueStr}`;
|
|
|
1261
1204
|
await this.beforeHook("decr", { key, value });
|
|
1262
1205
|
this.validateKey(key);
|
|
1263
1206
|
const nodes = await this.getNodesByKey(key);
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1266
|
-
const result = await node.command(`decr ${key} ${value}`);
|
|
1267
|
-
return typeof result === "number" ? result : void 0;
|
|
1268
|
-
} catch {
|
|
1269
|
-
return void 0;
|
|
1270
|
-
}
|
|
1271
|
-
});
|
|
1272
|
-
const results = await Promise.all(promises);
|
|
1273
|
-
const newValue = results.find((v) => v !== void 0);
|
|
1207
|
+
const results = await this.execute(`decr ${key} ${value}`, nodes);
|
|
1208
|
+
const newValue = results.find((v) => typeof v === "number");
|
|
1274
1209
|
await this.afterHook("decr", { key, value, newValue });
|
|
1275
1210
|
return newValue;
|
|
1276
1211
|
}
|
|
@@ -1286,16 +1221,8 @@ ${valueStr}`;
|
|
|
1286
1221
|
await this.beforeHook("touch", { key, exptime });
|
|
1287
1222
|
this.validateKey(key);
|
|
1288
1223
|
const nodes = await this.getNodesByKey(key);
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
const result = await node.command(`touch ${key} ${exptime}`);
|
|
1292
|
-
return result === "TOUCHED";
|
|
1293
|
-
} catch {
|
|
1294
|
-
return false;
|
|
1295
|
-
}
|
|
1296
|
-
});
|
|
1297
|
-
const results = await Promise.all(promises);
|
|
1298
|
-
const success = results.every((result) => result === true);
|
|
1224
|
+
const results = await this.execute(`touch ${key} ${exptime}`, nodes);
|
|
1225
|
+
const success = results.every((result) => result === "TOUCHED");
|
|
1299
1226
|
await this.afterHook("touch", { key, exptime, success });
|
|
1300
1227
|
return success;
|
|
1301
1228
|
}
|
|
@@ -1411,6 +1338,23 @@ ${valueStr}`;
|
|
|
1411
1338
|
}
|
|
1412
1339
|
return nodes;
|
|
1413
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Execute a command on the specified nodes.
|
|
1343
|
+
* @param {string} command - The memcache command string to execute
|
|
1344
|
+
* @param {MemcacheNode[]} nodes - Array of MemcacheNode instances to execute on
|
|
1345
|
+
* @param {ExecuteOptions} options - Optional execution options
|
|
1346
|
+
* @returns {Promise<unknown[]>} Promise resolving to array of results from each node
|
|
1347
|
+
*/
|
|
1348
|
+
async execute(command, nodes, options) {
|
|
1349
|
+
const promises = nodes.map(async (node) => {
|
|
1350
|
+
try {
|
|
1351
|
+
return await node.command(command, options?.commandOptions);
|
|
1352
|
+
} catch {
|
|
1353
|
+
return void 0;
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
return Promise.all(promises);
|
|
1357
|
+
}
|
|
1414
1358
|
/**
|
|
1415
1359
|
* Validates a Memcache key according to protocol requirements.
|
|
1416
1360
|
* @param {string} key - The key to validate
|