@ferricstore/ferricstore 0.1.4 → 0.1.5
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 +37 -1
- package/dist/index.cjs +1133 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +244 -3
- package/dist/index.d.ts +244 -3
- package/dist/index.js +1130 -34
- package/dist/index.js.map +1 -1
- package/docs/api/assets/hierarchy.js +1 -1
- package/docs/api/assets/highlight.css +6 -6
- package/docs/api/assets/navigation.js +1 -1
- package/docs/api/assets/search.js +1 -1
- package/docs/api/classes/FerricStoreClient.html +19 -2
- package/docs/api/classes/FerricStoreError.html +3 -3
- package/docs/api/classes/FlowAlreadyExistsError.html +3 -3
- package/docs/api/classes/FlowNotFoundError.html +3 -3
- package/docs/api/classes/FlowWrongStateError.html +3 -3
- package/docs/api/classes/InvalidCommandError.html +3 -3
- package/docs/api/classes/LockHeldError.html +3 -3
- package/docs/api/classes/LockNotOwnedError.html +3 -3
- package/docs/api/classes/NativeAdapter.html +3 -2
- package/docs/api/classes/OverloadedError.html +3 -3
- package/docs/api/classes/ReconnectingExecutor.html +4 -2
- package/docs/api/classes/RoutingTopology.html +9 -0
- package/docs/api/classes/StaleLeaseError.html +3 -3
- package/docs/api/classes/TopologyNativeAdapterPool.html +8 -0
- package/docs/api/functions/isReconnectableClosedConnectionError.html +1 -1
- package/docs/api/hierarchy.html +1 -1
- package/docs/api/index.html +16 -7
- package/docs/api/interfaces/AutoBatchOptions.html +2 -2
- package/docs/api/interfaces/CancelOptions.html +2 -2
- package/docs/api/interfaces/ClaimDueOptions.html +2 -2
- package/docs/api/interfaces/ClaimedItem.html +2 -3
- package/docs/api/interfaces/CommandExecutor.html +4 -2
- package/docs/api/interfaces/CompleteOptions.html +2 -2
- package/docs/api/interfaces/CompleteOutcome.html +1 -1
- package/docs/api/interfaces/CreateManyOptions.html +2 -2
- package/docs/api/interfaces/CreateOptions.html +2 -2
- package/docs/api/interfaces/ExecutePipelineOptions.html +2 -2
- package/docs/api/interfaces/FailOptions.html +2 -2
- package/docs/api/interfaces/FailOutcome.html +1 -1
- package/docs/api/interfaces/FencedItem.html +2 -2
- package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +2 -2
- package/docs/api/interfaces/FerricStoreClientOptions.html +2 -2
- package/docs/api/interfaces/FetchOrComputeResult.html +2 -2
- package/docs/api/interfaces/FlowRecord.html +2 -2
- package/docs/api/interfaces/KeyInfo.html +2 -2
- package/docs/api/interfaces/MutateOptions.html +2 -2
- package/docs/api/interfaces/NamedValueMutation.html +1 -1
- package/docs/api/interfaces/NativeAdapterOptions.html +8 -2
- package/docs/api/interfaces/ProtocolCommand.html +5 -0
- package/docs/api/interfaces/RateLimitResult.html +2 -2
- package/docs/api/interfaces/ReadOptions.html +2 -2
- package/docs/api/interfaces/ReclaimOptions.html +2 -2
- package/docs/api/interfaces/ReconnectOptions.html +2 -2
- package/docs/api/interfaces/RetryOptions.html +2 -2
- package/docs/api/interfaces/RetryOutcome.html +1 -1
- package/docs/api/interfaces/RoutingEndpoint.html +5 -0
- package/docs/api/interfaces/RoutingRoute.html +7 -0
- package/docs/api/interfaces/SearchOptions.html +12 -0
- package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
- package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
- package/docs/api/interfaces/TransitionOptions.html +2 -2
- package/docs/api/interfaces/TransitionOutcome.html +1 -1
- package/docs/api/modules.html +1 -1
- package/docs/api/types/CommandArgument.html +1 -1
- package/docs/api/types/EndpointPolicy.html +1 -0
- package/docs/api/types/ManagementPairs.html +1 -0
- package/docs/api/types/QueueBatchHandler.html +1 -1
- package/docs/api/types/QueueHandler.html +1 -1
- package/docs/api/types/QueueJob.html +1 -0
- package/docs/api/types/SearchStateMeta.html +1 -0
- package/docs/api/variables/COMMAND_OPCODES.html +1 -0
- package/docs/python-parity.md +4 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BitmapStore: () => BitmapStore,
|
|
34
34
|
BloomFilterStore: () => BloomFilterStore,
|
|
35
|
+
COMMAND_OPCODES: () => COMMAND_OPCODES,
|
|
35
36
|
CountMinSketchStore: () => CountMinSketchStore,
|
|
36
37
|
CuckooFilterStore: () => CuckooFilterStore,
|
|
37
38
|
FerricStoreClient: () => FerricStoreClient,
|
|
@@ -56,12 +57,14 @@ __export(index_exports, {
|
|
|
56
57
|
QueueWorker: () => QueueWorker,
|
|
57
58
|
RawCodec: () => RawCodec,
|
|
58
59
|
ReconnectingExecutor: () => ReconnectingExecutor,
|
|
60
|
+
RoutingTopology: () => RoutingTopology,
|
|
59
61
|
SetStore: () => SetStore,
|
|
60
62
|
SortedSetStore: () => SortedSetStore,
|
|
61
63
|
StaleLeaseError: () => StaleLeaseError,
|
|
62
64
|
StreamStore: () => StreamStore,
|
|
63
65
|
TDigestStore: () => TDigestStore,
|
|
64
66
|
TopKStore: () => TopKStore,
|
|
67
|
+
TopologyNativeAdapterPool: () => TopologyNativeAdapterPool,
|
|
65
68
|
Workflow: () => Workflow,
|
|
66
69
|
WorkflowClient: () => WorkflowClient,
|
|
67
70
|
WorkflowContext: () => WorkflowContext,
|
|
@@ -189,24 +192,171 @@ var FLAG_CUSTOM_PAYLOAD = 2;
|
|
|
189
192
|
var FLAG_COMPRESSED = 8;
|
|
190
193
|
var FLAG_MORE_CHUNKS = 32;
|
|
191
194
|
var STATUS_OK = 0;
|
|
195
|
+
var COMMAND_OPCODES = {
|
|
196
|
+
"HELLO": 1,
|
|
197
|
+
"AUTH": 2,
|
|
198
|
+
"PING": 3,
|
|
199
|
+
"CLIENT.SETNAME": 4,
|
|
200
|
+
"CLIENT.INFO": 5,
|
|
201
|
+
"ROUTE": 6,
|
|
202
|
+
"SHARDS": 7,
|
|
203
|
+
"BACKPRESSURE": 8,
|
|
204
|
+
"QUIT": 9,
|
|
205
|
+
"GOAWAY": 10,
|
|
206
|
+
"OPTIONS": 11,
|
|
207
|
+
"STARTUP": 12,
|
|
208
|
+
"WINDOW_UPDATE": 13,
|
|
209
|
+
"PIPELINE": 14,
|
|
210
|
+
"ROUTE_BATCH": 15,
|
|
211
|
+
"EVENT": 16,
|
|
212
|
+
"SUBSCRIBE_EVENTS": 17,
|
|
213
|
+
"UNSUBSCRIBE_EVENTS": 18,
|
|
214
|
+
"COMMAND_EXEC": 256,
|
|
215
|
+
"GET": 257,
|
|
216
|
+
"SET": 258,
|
|
217
|
+
"DEL": 259,
|
|
218
|
+
"MGET": 260,
|
|
219
|
+
"MSET": 261,
|
|
220
|
+
"CAS": 262,
|
|
221
|
+
"LOCK": 263,
|
|
222
|
+
"UNLOCK": 264,
|
|
223
|
+
"EXTEND": 265,
|
|
224
|
+
"RATELIMIT.ADD": 266,
|
|
225
|
+
"FETCH_OR_COMPUTE": 267,
|
|
226
|
+
"FETCH_OR_COMPUTE_RESULT": 268,
|
|
227
|
+
"FETCH_OR_COMPUTE_ERROR": 269,
|
|
228
|
+
"HSET": 272,
|
|
229
|
+
"HGET": 273,
|
|
230
|
+
"HMGET": 274,
|
|
231
|
+
"HGETALL": 275,
|
|
232
|
+
"LPUSH": 288,
|
|
233
|
+
"RPUSH": 289,
|
|
234
|
+
"LPOP": 290,
|
|
235
|
+
"RPOP": 291,
|
|
236
|
+
"LRANGE": 292,
|
|
237
|
+
"SADD": 304,
|
|
238
|
+
"SREM": 305,
|
|
239
|
+
"SMEMBERS": 306,
|
|
240
|
+
"SISMEMBER": 307,
|
|
241
|
+
"ZADD": 320,
|
|
242
|
+
"ZREM": 321,
|
|
243
|
+
"ZRANGE": 322,
|
|
244
|
+
"ZSCORE": 323,
|
|
245
|
+
"FLOW.CREATE": 513,
|
|
246
|
+
"FLOW.GET": 514,
|
|
247
|
+
"FLOW.CLAIM_DUE": 515,
|
|
248
|
+
"FLOW.COMPLETE": 516,
|
|
249
|
+
"FLOW.TRANSITION": 517,
|
|
250
|
+
"FLOW.RETRY": 518,
|
|
251
|
+
"FLOW.FAIL": 519,
|
|
252
|
+
"FLOW.CANCEL": 520,
|
|
253
|
+
"FLOW.EXTEND_LEASE": 521,
|
|
254
|
+
"FLOW.HISTORY": 522,
|
|
255
|
+
"FLOW.VALUE.PUT": 523,
|
|
256
|
+
"FLOW.VALUE.MGET": 524,
|
|
257
|
+
"FLOW.SIGNAL": 525,
|
|
258
|
+
"FLOW.LIST": 526,
|
|
259
|
+
"FLOW.CREATE_MANY": 527,
|
|
260
|
+
"FLOW.COMPLETE_MANY": 528,
|
|
261
|
+
"FLOW.TRANSITION_MANY": 529,
|
|
262
|
+
"FLOW.RETRY_MANY": 530,
|
|
263
|
+
"FLOW.FAIL_MANY": 531,
|
|
264
|
+
"FLOW.CANCEL_MANY": 532,
|
|
265
|
+
"FLOW.RECLAIM": 533,
|
|
266
|
+
"FLOW.REWIND": 534,
|
|
267
|
+
"FLOW.TERMINALS": 535,
|
|
268
|
+
"FLOW.FAILURES": 536,
|
|
269
|
+
"FLOW.BY_PARENT": 537,
|
|
270
|
+
"FLOW.BY_ROOT": 538,
|
|
271
|
+
"FLOW.BY_CORRELATION": 539,
|
|
272
|
+
"FLOW.INFO": 540,
|
|
273
|
+
"FLOW.STUCK": 541,
|
|
274
|
+
"FLOW.POLICY.SET": 542,
|
|
275
|
+
"FLOW.POLICY.GET": 543,
|
|
276
|
+
"FLOW.SPAWN_CHILDREN": 544,
|
|
277
|
+
"FLOW.RETENTION_CLEANUP": 545,
|
|
278
|
+
"FLOW.STEP_CONTINUE": 546,
|
|
279
|
+
"FLOW.START_AND_CLAIM": 547,
|
|
280
|
+
"FLOW.RUN_STEPS_MANY": 548,
|
|
281
|
+
"FLOW.SCHEDULE.CREATE": 549,
|
|
282
|
+
"FLOW.SCHEDULE.GET": 550,
|
|
283
|
+
"FLOW.SCHEDULE.DELETE": 551,
|
|
284
|
+
"FLOW.SCHEDULE.FIRE_DUE": 552,
|
|
285
|
+
"FLOW.SCHEDULE.LIST": 553,
|
|
286
|
+
"FLOW.SCHEDULE.FIRE": 554,
|
|
287
|
+
"FLOW.SCHEDULE.PAUSE": 555,
|
|
288
|
+
"FLOW.SCHEDULE.RESUME": 556,
|
|
289
|
+
"FLOW.STATS": 557,
|
|
290
|
+
"FLOW.ATTRIBUTES": 558,
|
|
291
|
+
"FLOW.ATTRIBUTE_VALUES": 559,
|
|
292
|
+
"FLOW.SEARCH": 560,
|
|
293
|
+
"FLOW.EFFECT.RESERVE": 576,
|
|
294
|
+
"FLOW.EFFECT.CONFIRM": 577,
|
|
295
|
+
"FLOW.EFFECT.FAIL": 578,
|
|
296
|
+
"FLOW.EFFECT.COMPENSATE": 579,
|
|
297
|
+
"FLOW.EFFECT.GET": 580,
|
|
298
|
+
"FLOW.GOVERNANCE.LEDGER": 581,
|
|
299
|
+
"FLOW.APPROVAL.REQUEST": 582,
|
|
300
|
+
"FLOW.APPROVAL.APPROVE": 583,
|
|
301
|
+
"FLOW.APPROVAL.REJECT": 584,
|
|
302
|
+
"FLOW.APPROVAL.GET": 585,
|
|
303
|
+
"FLOW.CIRCUIT.OPEN": 586,
|
|
304
|
+
"FLOW.CIRCUIT.CLOSE": 587,
|
|
305
|
+
"FLOW.CIRCUIT.GET": 588,
|
|
306
|
+
"FLOW.BUDGET.RESERVE": 589,
|
|
307
|
+
"FLOW.BUDGET.GET": 590,
|
|
308
|
+
"FLOW.LIMIT.LEASE": 591,
|
|
309
|
+
"FLOW.LIMIT.SPEND": 592,
|
|
310
|
+
"FLOW.LIMIT.RELEASE": 593,
|
|
311
|
+
"FLOW.LIMIT.GET": 594,
|
|
312
|
+
"FLOW.APPROVAL.LIST": 595,
|
|
313
|
+
"FLOW.GOVERNANCE.OVERVIEW": 596,
|
|
314
|
+
"FLOW.BUDGET.LIST": 597,
|
|
315
|
+
"FLOW.LIMIT.LIST": 598,
|
|
316
|
+
"FLOW.BUDGET.COMMIT": 599,
|
|
317
|
+
"FLOW.BUDGET.RELEASE": 600,
|
|
318
|
+
"CLUSTER.HEALTH": 769,
|
|
319
|
+
"CLUSTER.STATS": 770,
|
|
320
|
+
"CLUSTER.KEYSLOT": 771,
|
|
321
|
+
"CLUSTER.SLOTS": 772,
|
|
322
|
+
"CLUSTER.STATUS": 773,
|
|
323
|
+
"CLUSTER.JOIN": 774,
|
|
324
|
+
"CLUSTER.LEAVE": 775,
|
|
325
|
+
"CLUSTER.FAILOVER": 776,
|
|
326
|
+
"CLUSTER.PROMOTE": 777,
|
|
327
|
+
"CLUSTER.DEMOTE": 778,
|
|
328
|
+
"CLUSTER.ROLE": 779,
|
|
329
|
+
"FERRICSTORE.KEY_INFO": 780,
|
|
330
|
+
"FERRICSTORE.CONFIG": 781,
|
|
331
|
+
"FERRICSTORE.HOTNESS": 782,
|
|
332
|
+
"FERRICSTORE.METRICS": 783,
|
|
333
|
+
"FERRICSTORE.BLOBGC": 784
|
|
334
|
+
};
|
|
192
335
|
var OPCODES = {
|
|
193
|
-
startup:
|
|
194
|
-
auth:
|
|
195
|
-
ping:
|
|
196
|
-
clientSetName:
|
|
197
|
-
clientInfo:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
336
|
+
startup: COMMAND_OPCODES.STARTUP,
|
|
337
|
+
auth: COMMAND_OPCODES.AUTH,
|
|
338
|
+
ping: COMMAND_OPCODES.PING,
|
|
339
|
+
clientSetName: COMMAND_OPCODES["CLIENT.SETNAME"],
|
|
340
|
+
clientInfo: COMMAND_OPCODES["CLIENT.INFO"],
|
|
341
|
+
route: COMMAND_OPCODES.ROUTE,
|
|
342
|
+
routeBatch: COMMAND_OPCODES.ROUTE_BATCH,
|
|
343
|
+
shards: COMMAND_OPCODES.SHARDS,
|
|
344
|
+
backpressure: COMMAND_OPCODES.BACKPRESSURE,
|
|
345
|
+
options: COMMAND_OPCODES.OPTIONS,
|
|
346
|
+
quit: COMMAND_OPCODES.QUIT,
|
|
347
|
+
commandExec: COMMAND_OPCODES.COMMAND_EXEC,
|
|
348
|
+
get: COMMAND_OPCODES.GET,
|
|
349
|
+
set: COMMAND_OPCODES.SET,
|
|
350
|
+
del: COMMAND_OPCODES.DEL,
|
|
351
|
+
mget: COMMAND_OPCODES.MGET,
|
|
352
|
+
mset: COMMAND_OPCODES.MSET,
|
|
353
|
+
pipeline: COMMAND_OPCODES.PIPELINE,
|
|
354
|
+
flowCreate: COMMAND_OPCODES["FLOW.CREATE"],
|
|
355
|
+
flowClaimDue: COMMAND_OPCODES["FLOW.CLAIM_DUE"],
|
|
356
|
+
flowComplete: COMMAND_OPCODES["FLOW.COMPLETE"],
|
|
357
|
+
flowCreateMany: COMMAND_OPCODES["FLOW.CREATE_MANY"],
|
|
358
|
+
flowCompleteMany: COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
|
|
359
|
+
flowSearch: COMMAND_OPCODES["FLOW.SEARCH"]
|
|
210
360
|
};
|
|
211
361
|
var COMPACT_FLOW_CLAIM_JOBS = 128;
|
|
212
362
|
var COMPACT_OK_LIST = 129;
|
|
@@ -339,12 +489,46 @@ function buildProtocolCommand(args) {
|
|
|
339
489
|
}
|
|
340
490
|
const command = asText(args[0]).toUpperCase();
|
|
341
491
|
const commandArgs = args.slice(1);
|
|
492
|
+
if (command === "COMMAND_EXEC" && commandArgs.length >= 1) {
|
|
493
|
+
return commandExec(commandArgs);
|
|
494
|
+
}
|
|
495
|
+
if (command === "HELLO" || command === "STARTUP" || command === "WINDOW_UPDATE") {
|
|
496
|
+
return { laneId: 0, opcode: COMMAND_OPCODES[command], payload: optionMap(commandArgs) };
|
|
497
|
+
}
|
|
498
|
+
if (command === "AUTH") {
|
|
499
|
+
const payload = commandArgs.length === 1 ? { password: commandArgs[0], username: "default" } : { password: commandArgs[1], username: commandArgs[0] };
|
|
500
|
+
return { laneId: 0, opcode: OPCODES.auth, payload };
|
|
501
|
+
}
|
|
342
502
|
if (command === "PING") {
|
|
343
503
|
return { laneId: 0, opcode: OPCODES.ping, payload: commandArgs.length === 0 ? {} : { message: commandArgs[0] } };
|
|
344
504
|
}
|
|
505
|
+
if (command === "OPTIONS") {
|
|
506
|
+
return { laneId: 0, opcode: OPCODES.options, payload: {} };
|
|
507
|
+
}
|
|
508
|
+
if (command === "BACKPRESSURE") {
|
|
509
|
+
return { laneId: 0, opcode: OPCODES.backpressure, payload: {} };
|
|
510
|
+
}
|
|
511
|
+
if (command === "QUIT") {
|
|
512
|
+
return { laneId: 0, opcode: OPCODES.quit, payload: {} };
|
|
513
|
+
}
|
|
514
|
+
if (command === "ROUTE" && commandArgs.length >= 1) {
|
|
515
|
+
return { laneId: 0, opcode: OPCODES.route, payload: { key: commandArgs[0] } };
|
|
516
|
+
}
|
|
517
|
+
if (command === "ROUTE_BATCH") {
|
|
518
|
+
return { laneId: 0, opcode: OPCODES.routeBatch, payload: { keys: commandArgs } };
|
|
519
|
+
}
|
|
520
|
+
if (command === "SHARDS") {
|
|
521
|
+
return { laneId: 0, opcode: OPCODES.shards, payload: {} };
|
|
522
|
+
}
|
|
345
523
|
if (command === "CLIENT" && asText(commandArgs[0]).toUpperCase() === "SETNAME" && commandArgs.length === 2) {
|
|
346
524
|
return { laneId: 0, opcode: OPCODES.clientSetName, payload: { name: commandArgs[1] } };
|
|
347
525
|
}
|
|
526
|
+
if (command === "CLIENT.SETNAME" && commandArgs.length === 1) {
|
|
527
|
+
return { laneId: 0, opcode: OPCODES.clientSetName, payload: { name: commandArgs[0] } };
|
|
528
|
+
}
|
|
529
|
+
if (command === "CLIENT" && asText(commandArgs[0]).toUpperCase() === "INFO" && commandArgs.length === 1 || command === "CLIENT.INFO" && commandArgs.length === 0) {
|
|
530
|
+
return { laneId: 0, opcode: OPCODES.clientInfo, payload: {} };
|
|
531
|
+
}
|
|
348
532
|
if (command === "GET" && commandArgs.length >= 1) {
|
|
349
533
|
return { opcode: OPCODES.get, payload: { key: commandArgs[0] } };
|
|
350
534
|
}
|
|
@@ -363,7 +547,7 @@ function buildProtocolCommand(args) {
|
|
|
363
547
|
if (command === "DEL") {
|
|
364
548
|
return { opcode: OPCODES.del, payload: { keys: commandArgs } };
|
|
365
549
|
}
|
|
366
|
-
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(commandArgs)) {
|
|
550
|
+
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(command, commandArgs)) {
|
|
367
551
|
return commandExec(args);
|
|
368
552
|
}
|
|
369
553
|
if (command === "FLOW.CREATE") {
|
|
@@ -381,6 +565,9 @@ function buildProtocolCommand(args) {
|
|
|
381
565
|
if (command === "FLOW.COMPLETE_MANY") {
|
|
382
566
|
return flowCompleteManyPayload(commandArgs) ?? commandExec(args);
|
|
383
567
|
}
|
|
568
|
+
if (command === "FLOW.SEARCH") {
|
|
569
|
+
return flowSearchPayload(commandArgs) ?? commandExec(args);
|
|
570
|
+
}
|
|
384
571
|
return commandExec(args);
|
|
385
572
|
}
|
|
386
573
|
function commandExec(args) {
|
|
@@ -725,13 +912,13 @@ function writeOptionalBinary(out, offset, value) {
|
|
|
725
912
|
}
|
|
726
913
|
return writeBinary(out, offset, value);
|
|
727
914
|
}
|
|
728
|
-
function hasFlowCommandOnlyOption(args) {
|
|
915
|
+
function hasFlowCommandOnlyOption(command, args) {
|
|
729
916
|
return args.some((arg) => {
|
|
730
917
|
if (typeof arg !== "string" && !import_node_buffer.Buffer.isBuffer(arg)) {
|
|
731
918
|
return false;
|
|
732
919
|
}
|
|
733
920
|
const token = asText(arg).toUpperCase();
|
|
734
|
-
return token === "
|
|
921
|
+
return token === "INDEXED_STATE_META" || command !== "FLOW.SEARCH" && token === "STATE_META";
|
|
735
922
|
});
|
|
736
923
|
}
|
|
737
924
|
function flowCreatePayload(args) {
|
|
@@ -849,6 +1036,12 @@ function flowCompleteManyPayload(args) {
|
|
|
849
1036
|
if (!auto && !mixed) payload.partition_key = partition;
|
|
850
1037
|
return { opcode: OPCODES.flowCompleteMany, payload };
|
|
851
1038
|
}
|
|
1039
|
+
function flowSearchPayload(args) {
|
|
1040
|
+
if (args.length < 1) return void 0;
|
|
1041
|
+
const payload = optionMap(args.slice(1));
|
|
1042
|
+
normalizeFlowSearchStateMeta(payload);
|
|
1043
|
+
return { opcode: OPCODES.flowSearch, payload: { type: args[0], ...payload } };
|
|
1044
|
+
}
|
|
852
1045
|
function compactFlowCreateManyPayload(partition, rawItems, mixed, auto, options) {
|
|
853
1046
|
if ("priority" in options || "idempotent" in options || "retention_ttl_ms" in options || typeof options.type === "undefined" || typeof options.state === "undefined" || typeof options.now_ms !== "number") {
|
|
854
1047
|
return void 0;
|
|
@@ -951,6 +1144,78 @@ function compactFlowCompleteManyPayload(partition, rawItems, mixed, auto, option
|
|
|
951
1144
|
}
|
|
952
1145
|
return { flags: FLAG_CUSTOM_PAYLOAD, opcode: OPCODES.flowCompleteMany, payload: out };
|
|
953
1146
|
}
|
|
1147
|
+
var OPTION_FIELDS = {
|
|
1148
|
+
CONSISTENT_PROJECTION: "consistent_projection",
|
|
1149
|
+
COUNT: "count",
|
|
1150
|
+
FROM_MS: "from_ms",
|
|
1151
|
+
INCLUDE_COLD: "include_cold",
|
|
1152
|
+
PARTITION: "partition_key",
|
|
1153
|
+
REV: "rev",
|
|
1154
|
+
STATE: "state",
|
|
1155
|
+
TERMINAL_ONLY: "terminal_only",
|
|
1156
|
+
TO_MS: "to_ms"
|
|
1157
|
+
};
|
|
1158
|
+
var BOOL_OPTION_FIELDS = /* @__PURE__ */ new Set(["consistent_projection", "include_cold", "rev", "terminal_only"]);
|
|
1159
|
+
function optionMap(args) {
|
|
1160
|
+
const payload = {};
|
|
1161
|
+
for (let index = 0; index < args.length; ) {
|
|
1162
|
+
const token = asText(args[index]).toUpperCase();
|
|
1163
|
+
if (token === "ATTRIBUTE") {
|
|
1164
|
+
if (index + 2 >= args.length) {
|
|
1165
|
+
throw new FerricStoreError("ATTRIBUTE requires name and value");
|
|
1166
|
+
}
|
|
1167
|
+
const name = asText(args[index + 1]);
|
|
1168
|
+
const attributes = ensureObjectField(payload, "attributes");
|
|
1169
|
+
attributes[name] = args[index + 2];
|
|
1170
|
+
index += 3;
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
if (token === "STATE_META") {
|
|
1174
|
+
if (index + 2 >= args.length) {
|
|
1175
|
+
throw new FerricStoreError("STATE_META requires name and value");
|
|
1176
|
+
}
|
|
1177
|
+
const name = asText(args[index + 1]);
|
|
1178
|
+
const stateMeta = ensureObjectField(payload, "state_meta");
|
|
1179
|
+
stateMeta[name] = args[index + 2];
|
|
1180
|
+
index += 3;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
const field2 = OPTION_FIELDS[token] ?? token.toLowerCase();
|
|
1184
|
+
if (index + 1 >= args.length) {
|
|
1185
|
+
throw new FerricStoreError(`${token} requires a value`);
|
|
1186
|
+
}
|
|
1187
|
+
const value = args[index + 1];
|
|
1188
|
+
payload[field2] = BOOL_OPTION_FIELDS.has(field2) ? boolArg(value) : value;
|
|
1189
|
+
index += 2;
|
|
1190
|
+
}
|
|
1191
|
+
return payload;
|
|
1192
|
+
}
|
|
1193
|
+
function ensureObjectField(payload, field2) {
|
|
1194
|
+
const value = payload[field2];
|
|
1195
|
+
if (isPlainObject(value)) {
|
|
1196
|
+
return value;
|
|
1197
|
+
}
|
|
1198
|
+
const next = {};
|
|
1199
|
+
payload[field2] = next;
|
|
1200
|
+
return next;
|
|
1201
|
+
}
|
|
1202
|
+
function normalizeFlowSearchStateMeta(payload) {
|
|
1203
|
+
const stateMeta = payload.state_meta;
|
|
1204
|
+
if (!isPlainObject(stateMeta) || Object.keys(stateMeta).length === 0) {
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
if (Object.values(stateMeta).every(isPlainObject)) {
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const state = payload.state;
|
|
1211
|
+
if (state == null) {
|
|
1212
|
+
throw new FerricStoreError("FLOW.SEARCH STATE_META filters require STATE or nested state metadata");
|
|
1213
|
+
}
|
|
1214
|
+
payload.state_meta = { [asText(state)]: stateMeta };
|
|
1215
|
+
}
|
|
1216
|
+
function isPlainObject(value) {
|
|
1217
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer.Buffer.isBuffer(value) && !(value instanceof Uint8Array);
|
|
1218
|
+
}
|
|
954
1219
|
function parseFlowOptions(args, start, end, config) {
|
|
955
1220
|
const payload = {};
|
|
956
1221
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1509,6 +1774,9 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1509
1774
|
const command = buildProtocolCommand(args);
|
|
1510
1775
|
return await this.request(command);
|
|
1511
1776
|
}
|
|
1777
|
+
async executeProtocolCommand(command, laneId) {
|
|
1778
|
+
return await this.request(laneId == null ? command : { ...command, laneId });
|
|
1779
|
+
}
|
|
1512
1780
|
async executePipeline(commands, options = {}) {
|
|
1513
1781
|
if (commands.length === 0) {
|
|
1514
1782
|
return [];
|
|
@@ -1720,6 +1988,20 @@ var ReconnectingExecutor = class {
|
|
|
1720
1988
|
return await Promise.all(commands.map((command) => executor.executeCommand(...command)));
|
|
1721
1989
|
});
|
|
1722
1990
|
}
|
|
1991
|
+
async refreshTopology() {
|
|
1992
|
+
const executor = await this.executorPromise;
|
|
1993
|
+
if (executor.refreshTopology == null) {
|
|
1994
|
+
throw new FerricStoreError("topology refresh requires a topology-aware native executor");
|
|
1995
|
+
}
|
|
1996
|
+
return await executor.refreshTopology();
|
|
1997
|
+
}
|
|
1998
|
+
async route(key) {
|
|
1999
|
+
const executor = await this.executorPromise;
|
|
2000
|
+
if (executor.route == null) {
|
|
2001
|
+
throw new FerricStoreError("route lookup requires a topology-aware native executor");
|
|
2002
|
+
}
|
|
2003
|
+
return await executor.route(key);
|
|
2004
|
+
}
|
|
1723
2005
|
async close() {
|
|
1724
2006
|
if (this.closed) {
|
|
1725
2007
|
return;
|
|
@@ -1810,6 +2092,653 @@ function normalizeKeepAliveInitialDelay(value) {
|
|
|
1810
2092
|
return Math.max(0, Math.trunc(value ?? 3e4));
|
|
1811
2093
|
}
|
|
1812
2094
|
|
|
2095
|
+
// src/topology.ts
|
|
2096
|
+
var import_node_buffer3 = require("buffer");
|
|
2097
|
+
var ROUTE_SLOT_COUNT = 1024;
|
|
2098
|
+
var ROUTE_SLOT_MASK = ROUTE_SLOT_COUNT - 1;
|
|
2099
|
+
var RoutingTopology = class _RoutingTopology {
|
|
2100
|
+
routeEpoch;
|
|
2101
|
+
shardCount;
|
|
2102
|
+
slots;
|
|
2103
|
+
endpoints;
|
|
2104
|
+
constructor(routeEpoch, shardCount, slots, endpoints) {
|
|
2105
|
+
this.routeEpoch = routeEpoch;
|
|
2106
|
+
this.shardCount = shardCount;
|
|
2107
|
+
this.slots = slots;
|
|
2108
|
+
this.endpoints = endpoints;
|
|
2109
|
+
}
|
|
2110
|
+
static empty() {
|
|
2111
|
+
return new _RoutingTopology(0, 0, Array.from({ length: ROUTE_SLOT_COUNT }), /* @__PURE__ */ new Map());
|
|
2112
|
+
}
|
|
2113
|
+
static build(payload) {
|
|
2114
|
+
const ranges = getField(payload, "ranges");
|
|
2115
|
+
if (!Array.isArray(ranges)) {
|
|
2116
|
+
throw new FerricStoreError("invalid SHARDS topology payload", { raw: payload });
|
|
2117
|
+
}
|
|
2118
|
+
const slots = Array.from({ length: ROUTE_SLOT_COUNT });
|
|
2119
|
+
const endpoints = /* @__PURE__ */ new Map();
|
|
2120
|
+
for (const item of ranges) {
|
|
2121
|
+
if (textOrUndefined(getField(item, "hint")) === "leader_unknown") {
|
|
2122
|
+
throw new FerricStoreError("SHARDS range has no leader", { raw: item });
|
|
2123
|
+
}
|
|
2124
|
+
const first = numberOrUndefined(getField(item, "first_slot"));
|
|
2125
|
+
const last = numberOrUndefined(getField(item, "last_slot"));
|
|
2126
|
+
const shard = numberOrUndefined(getField(item, "shard"));
|
|
2127
|
+
const laneId = numberOrUndefined(getField(item, "lane_id"));
|
|
2128
|
+
const endpoint = endpointFromRange(item);
|
|
2129
|
+
if (first == null || last == null || shard == null || laneId == null || first < 0 || last < first || last >= ROUTE_SLOT_COUNT) {
|
|
2130
|
+
throw new FerricStoreError("invalid SHARDS range", { raw: item });
|
|
2131
|
+
}
|
|
2132
|
+
const endpointKey = endpointKeyFor(endpoint);
|
|
2133
|
+
const route = {
|
|
2134
|
+
endpoint,
|
|
2135
|
+
endpointKey,
|
|
2136
|
+
laneId,
|
|
2137
|
+
leaderNode: endpoint.node,
|
|
2138
|
+
shard
|
|
2139
|
+
};
|
|
2140
|
+
for (let slot = first; slot <= last; slot += 1) {
|
|
2141
|
+
slots[slot] = route;
|
|
2142
|
+
}
|
|
2143
|
+
endpoints.set(endpointKey, endpoint);
|
|
2144
|
+
}
|
|
2145
|
+
return new _RoutingTopology(
|
|
2146
|
+
numberOrUndefined(getField(payload, "route_epoch")) ?? 0,
|
|
2147
|
+
numberOrUndefined(getField(payload, "shard_count")) ?? 0,
|
|
2148
|
+
slots,
|
|
2149
|
+
endpoints
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
static slotForKey(key) {
|
|
2153
|
+
const textKey = import_node_buffer3.Buffer.isBuffer(key) ? key.toString("utf8") : key;
|
|
2154
|
+
let hashInput;
|
|
2155
|
+
if (textKey.startsWith("f:{")) {
|
|
2156
|
+
hashInput = flowHashTag(textKey.slice(3), textKey);
|
|
2157
|
+
} else if (textKey.startsWith("X:f:{")) {
|
|
2158
|
+
hashInput = flowHashTag(textKey.slice(5), textKey);
|
|
2159
|
+
} else {
|
|
2160
|
+
hashInput = hashTagOrKey(textKey);
|
|
2161
|
+
}
|
|
2162
|
+
return crc32(import_node_buffer3.Buffer.from(hashInput)) & ROUTE_SLOT_MASK;
|
|
2163
|
+
}
|
|
2164
|
+
routeKey(key) {
|
|
2165
|
+
const slot = _RoutingTopology.slotForKey(key);
|
|
2166
|
+
const route = this.slots[slot];
|
|
2167
|
+
if (route == null) {
|
|
2168
|
+
throw new FerricStoreError(`no route for slot ${slot}`);
|
|
2169
|
+
}
|
|
2170
|
+
return { ...route, slot };
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
|
|
2174
|
+
adapterOptions;
|
|
2175
|
+
adapters = /* @__PURE__ */ new Map();
|
|
2176
|
+
endpointPolicy;
|
|
2177
|
+
endpointValidator;
|
|
2178
|
+
seedEndpointKeys;
|
|
2179
|
+
seedUrls;
|
|
2180
|
+
tls;
|
|
2181
|
+
trustedHosts;
|
|
2182
|
+
warmConnections;
|
|
2183
|
+
topologyValue = RoutingTopology.empty();
|
|
2184
|
+
constructor(urls, options = {}) {
|
|
2185
|
+
if (urls.length === 0) {
|
|
2186
|
+
throw new FerricStoreError("TopologyNativeAdapterPool requires at least one seed URL");
|
|
2187
|
+
}
|
|
2188
|
+
this.seedUrls = [...urls];
|
|
2189
|
+
this.endpointPolicy = options.endpointPolicy ?? "seed_hosts";
|
|
2190
|
+
this.endpointValidator = options.endpointValidator;
|
|
2191
|
+
this.warmConnections = options.warmConnections ?? false;
|
|
2192
|
+
this.tls = options.tlsOptions != null || urls.some((url) => parseFerricUrl2(url).tls);
|
|
2193
|
+
this.adapterOptions = nativeOnlyOptions(withSeedAuthDefaults(urls, options));
|
|
2194
|
+
this.seedEndpointKeys = new Set(urls.map((url) => endpointKeyFor(endpointFromUrl(url))));
|
|
2195
|
+
this.trustedHosts = normalizedHostSet(options.trustedHosts ?? []);
|
|
2196
|
+
}
|
|
2197
|
+
static async fromUrls(urls, options = {}) {
|
|
2198
|
+
const pool = new _TopologyNativeAdapterPool(urls, options);
|
|
2199
|
+
await pool.refreshTopology();
|
|
2200
|
+
return pool;
|
|
2201
|
+
}
|
|
2202
|
+
get topology() {
|
|
2203
|
+
return this.topologyValue;
|
|
2204
|
+
}
|
|
2205
|
+
async refreshTopology() {
|
|
2206
|
+
let lastError;
|
|
2207
|
+
for (const url of this.refreshCandidateUrls()) {
|
|
2208
|
+
try {
|
|
2209
|
+
const adapter = await this.adapterForUrl(url);
|
|
2210
|
+
const topology = RoutingTopology.build(await adapter.executeCommand("SHARDS"));
|
|
2211
|
+
this.topologyValue = topology;
|
|
2212
|
+
if (this.warmConnections) {
|
|
2213
|
+
await Promise.allSettled([...topology.endpoints.values()].map(async (endpoint) => await this.adapterForEndpoint(endpoint)));
|
|
2214
|
+
}
|
|
2215
|
+
return topology;
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
lastError = error;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
throw new FerricStoreError("no FerricStore topology endpoint reachable", { raw: lastError });
|
|
2221
|
+
}
|
|
2222
|
+
route(key) {
|
|
2223
|
+
const route = this.topologyValue.routeKey(key);
|
|
2224
|
+
this.validateEndpoint(route.endpoint);
|
|
2225
|
+
return route;
|
|
2226
|
+
}
|
|
2227
|
+
async executeCommand(...args) {
|
|
2228
|
+
const routed = this.routeData(args);
|
|
2229
|
+
if (routed == null) {
|
|
2230
|
+
return await (await this.controlAdapter()).executeCommand(...args);
|
|
2231
|
+
}
|
|
2232
|
+
const adapter = await this.adapterForEndpoint(routed.route.endpoint);
|
|
2233
|
+
try {
|
|
2234
|
+
return await adapter.executeProtocolCommand(routed.command, routed.route.laneId);
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
if (isRetryableRouteError(error)) {
|
|
2237
|
+
await this.refreshTopology().catch(() => void 0);
|
|
2238
|
+
}
|
|
2239
|
+
throw error;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
async executePipeline(commands, options = {}) {
|
|
2243
|
+
if (commands.length === 0) {
|
|
2244
|
+
return [];
|
|
2245
|
+
}
|
|
2246
|
+
const route = this.singleRouteForCommands(commands);
|
|
2247
|
+
if (route == null) {
|
|
2248
|
+
return await (await this.controlAdapter()).executePipeline(commands, options);
|
|
2249
|
+
}
|
|
2250
|
+
const adapter = await this.adapterForEndpoint(route.endpoint);
|
|
2251
|
+
try {
|
|
2252
|
+
const response = await adapter.executeProtocolCommand(pipelineCommand(commands), route.laneId);
|
|
2253
|
+
return unwrapPipelineResponse(response, options);
|
|
2254
|
+
} catch (error) {
|
|
2255
|
+
if (isRetryableRouteError(error)) {
|
|
2256
|
+
await this.refreshTopology().catch(() => void 0);
|
|
2257
|
+
}
|
|
2258
|
+
throw error;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
async close() {
|
|
2262
|
+
await Promise.allSettled([...this.adapters.values()].map(async (adapter) => await adapter.close()));
|
|
2263
|
+
this.adapters.clear();
|
|
2264
|
+
}
|
|
2265
|
+
routeData(args) {
|
|
2266
|
+
if (args.length === 0) {
|
|
2267
|
+
return void 0;
|
|
2268
|
+
}
|
|
2269
|
+
let command;
|
|
2270
|
+
try {
|
|
2271
|
+
command = buildProtocolCommand(args);
|
|
2272
|
+
} catch {
|
|
2273
|
+
return void 0;
|
|
2274
|
+
}
|
|
2275
|
+
const key = routingKey(args, command);
|
|
2276
|
+
if (key == null) {
|
|
2277
|
+
return void 0;
|
|
2278
|
+
}
|
|
2279
|
+
return { command, route: this.route(key) };
|
|
2280
|
+
}
|
|
2281
|
+
singleRouteForCommands(commands) {
|
|
2282
|
+
let route;
|
|
2283
|
+
for (const command of commands) {
|
|
2284
|
+
const routed = this.routeData(command);
|
|
2285
|
+
if (routed == null) {
|
|
2286
|
+
return void 0;
|
|
2287
|
+
}
|
|
2288
|
+
const current = routed.route;
|
|
2289
|
+
if (route == null) {
|
|
2290
|
+
route = current;
|
|
2291
|
+
} else if (route.endpointKey !== current.endpointKey || route.laneId !== current.laneId) {
|
|
2292
|
+
return void 0;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return route;
|
|
2296
|
+
}
|
|
2297
|
+
async controlAdapter() {
|
|
2298
|
+
for (const url of this.refreshCandidateUrls()) {
|
|
2299
|
+
try {
|
|
2300
|
+
return await this.adapterForUrl(url);
|
|
2301
|
+
} catch {
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return await this.adapterForUrl(this.seedUrls[0] ?? "ferric://127.0.0.1:6388");
|
|
2305
|
+
}
|
|
2306
|
+
async adapterForUrl(url) {
|
|
2307
|
+
const endpoint = endpointFromUrl(url);
|
|
2308
|
+
const key = endpointKeyFor(endpoint);
|
|
2309
|
+
const existing = this.adapters.get(key);
|
|
2310
|
+
if (existing != null) {
|
|
2311
|
+
return existing;
|
|
2312
|
+
}
|
|
2313
|
+
const adapter = await NativeAdapter.fromUrl(url, this.adapterOptions);
|
|
2314
|
+
this.adapters.set(key, adapter);
|
|
2315
|
+
return adapter;
|
|
2316
|
+
}
|
|
2317
|
+
async adapterForEndpoint(endpoint) {
|
|
2318
|
+
this.validateEndpoint(endpoint);
|
|
2319
|
+
const key = endpointKeyFor(endpoint);
|
|
2320
|
+
const existing = this.adapters.get(key);
|
|
2321
|
+
if (existing != null) {
|
|
2322
|
+
return existing;
|
|
2323
|
+
}
|
|
2324
|
+
const adapter = await NativeAdapter.fromUrl(urlFromEndpoint(endpoint, this.tls), this.adapterOptions);
|
|
2325
|
+
this.adapters.set(key, adapter);
|
|
2326
|
+
return adapter;
|
|
2327
|
+
}
|
|
2328
|
+
validateEndpoint(endpoint) {
|
|
2329
|
+
let allowed;
|
|
2330
|
+
if (this.endpointPolicy === "any" || this.endpointPolicy === "none") {
|
|
2331
|
+
allowed = true;
|
|
2332
|
+
} else if (this.endpointPolicy === "seed_hosts") {
|
|
2333
|
+
allowed = this.seedEndpointKeys.has(endpointKeyFor(endpoint)) || this.trustedHosts.has(endpoint.host.toLowerCase());
|
|
2334
|
+
} else if ("allowHosts" in this.endpointPolicy) {
|
|
2335
|
+
allowed = normalizedHostSet(this.endpointPolicy.allowHosts).has(endpoint.host.toLowerCase());
|
|
2336
|
+
} else {
|
|
2337
|
+
throw new FerricStoreError("invalid endpoint policy", { raw: this.endpointPolicy });
|
|
2338
|
+
}
|
|
2339
|
+
if (!allowed) {
|
|
2340
|
+
throw new FerricStoreError("unsafe learned endpoint", { raw: endpoint });
|
|
2341
|
+
}
|
|
2342
|
+
if (this.endpointValidator != null && !this.endpointValidator(endpoint)) {
|
|
2343
|
+
throw new FerricStoreError("unsafe learned endpoint", { raw: endpoint });
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
refreshCandidateUrls() {
|
|
2347
|
+
const urls = [
|
|
2348
|
+
...this.seedUrls,
|
|
2349
|
+
...[...this.topologyValue.endpoints.values()].map((endpoint) => urlFromEndpoint(endpoint, this.tls))
|
|
2350
|
+
];
|
|
2351
|
+
return [...new Set(urls)];
|
|
2352
|
+
}
|
|
2353
|
+
};
|
|
2354
|
+
function routingKey(args, command) {
|
|
2355
|
+
const name = commandName(args);
|
|
2356
|
+
if (command.opcode < 256 || name === "CLUSTER.KEYSLOT" || name === "SHARDS" || name === "ROUTE") {
|
|
2357
|
+
return void 0;
|
|
2358
|
+
}
|
|
2359
|
+
const keyFromArgs = routingKeyFromArgs(name, args);
|
|
2360
|
+
if (keyFromArgs != null) {
|
|
2361
|
+
return keyFromArgs;
|
|
2362
|
+
}
|
|
2363
|
+
if (!isPlainObject2(command.payload)) {
|
|
2364
|
+
return void 0;
|
|
2365
|
+
}
|
|
2366
|
+
for (const field2 of ["key", "partition_key", "id", "owner_flow_id", "parent_id", "root_id", "correlation_id", "scope"]) {
|
|
2367
|
+
const value = getField(command.payload, field2);
|
|
2368
|
+
if (typeof value === "string" || import_node_buffer3.Buffer.isBuffer(value)) {
|
|
2369
|
+
return value;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
const keys = getField(command.payload, "keys");
|
|
2373
|
+
if (Array.isArray(keys)) {
|
|
2374
|
+
return singleShardKey(keys);
|
|
2375
|
+
}
|
|
2376
|
+
const pairsValue = getField(command.payload, "pairs");
|
|
2377
|
+
if (Array.isArray(pairsValue)) {
|
|
2378
|
+
return singleShardKey(
|
|
2379
|
+
pairsValue.filter((pair) => Array.isArray(pair) && pair.length > 0).map((pair) => pair[0])
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
return void 0;
|
|
2383
|
+
}
|
|
2384
|
+
function routingKeyFromArgs(name, args) {
|
|
2385
|
+
if (name == null) {
|
|
2386
|
+
return void 0;
|
|
2387
|
+
}
|
|
2388
|
+
if (name === "MGET" || name === "DEL") {
|
|
2389
|
+
return singleShardKey(args.slice(1));
|
|
2390
|
+
}
|
|
2391
|
+
if (name === "MSET") {
|
|
2392
|
+
const keys = [];
|
|
2393
|
+
for (let index = 1; index < args.length; index += 2) {
|
|
2394
|
+
keys.push(args[index]);
|
|
2395
|
+
}
|
|
2396
|
+
return singleShardKey(keys);
|
|
2397
|
+
}
|
|
2398
|
+
if (name === "BITOP") {
|
|
2399
|
+
return bitopRoutingKey(args);
|
|
2400
|
+
}
|
|
2401
|
+
if (name === "RENAME" || name === "RENAMENX") {
|
|
2402
|
+
return singleShardKey(args.slice(1, 3));
|
|
2403
|
+
}
|
|
2404
|
+
if (name === "XREAD" || name === "XREADGROUP") {
|
|
2405
|
+
return streamReadRoutingKey(args);
|
|
2406
|
+
}
|
|
2407
|
+
if (name.startsWith("FLOW.")) {
|
|
2408
|
+
return flowRoutingKey(name, args);
|
|
2409
|
+
}
|
|
2410
|
+
if (firstKeyCommands.has(name)) {
|
|
2411
|
+
const key = args[1];
|
|
2412
|
+
return typeof key === "string" || import_node_buffer3.Buffer.isBuffer(key) ? key : void 0;
|
|
2413
|
+
}
|
|
2414
|
+
return void 0;
|
|
2415
|
+
}
|
|
2416
|
+
function flowRoutingKey(name, args) {
|
|
2417
|
+
const flowArgs = args.slice(1);
|
|
2418
|
+
if (flowArgs.length === 0) {
|
|
2419
|
+
return void 0;
|
|
2420
|
+
}
|
|
2421
|
+
if (name === "FLOW.CLAIM_DUE" || name === "FLOW.RECLAIM") {
|
|
2422
|
+
return flowPartitionRoutingKey(flowArgs, 1);
|
|
2423
|
+
}
|
|
2424
|
+
if (name === "FLOW.CREATE_MANY" || name === "FLOW.COMPLETE_MANY" || name === "FLOW.TRANSITION_MANY" || name === "FLOW.RETRY_MANY" || name === "FLOW.FAIL_MANY" || name === "FLOW.CANCEL_MANY") {
|
|
2425
|
+
const partition2 = flowArgs[0];
|
|
2426
|
+
if (typeof partition2 === "string" && partition2.toUpperCase() !== "AUTO" && partition2.toUpperCase() !== "MIXED") {
|
|
2427
|
+
return partition2;
|
|
2428
|
+
}
|
|
2429
|
+
if (import_node_buffer3.Buffer.isBuffer(partition2)) {
|
|
2430
|
+
const text2 = partition2.toString("utf8").toUpperCase();
|
|
2431
|
+
if (text2 !== "AUTO" && text2 !== "MIXED") {
|
|
2432
|
+
return partition2;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return void 0;
|
|
2436
|
+
}
|
|
2437
|
+
const partition = flowPartitionRoutingKey(flowArgs, 1);
|
|
2438
|
+
if (partition != null) {
|
|
2439
|
+
return partition;
|
|
2440
|
+
}
|
|
2441
|
+
if (typeScopedFlowCommands.has(name)) {
|
|
2442
|
+
return void 0;
|
|
2443
|
+
}
|
|
2444
|
+
const id = flowArgs[0];
|
|
2445
|
+
return typeof id === "string" || import_node_buffer3.Buffer.isBuffer(id) ? id : void 0;
|
|
2446
|
+
}
|
|
2447
|
+
function bitopRoutingKey(args) {
|
|
2448
|
+
if (args.length < 4) {
|
|
2449
|
+
return void 0;
|
|
2450
|
+
}
|
|
2451
|
+
return singleShardKey(args.slice(2));
|
|
2452
|
+
}
|
|
2453
|
+
function streamReadRoutingKey(args) {
|
|
2454
|
+
const streamsIndex = args.findIndex((arg) => commandPart(arg) === "STREAMS");
|
|
2455
|
+
if (streamsIndex < 0) {
|
|
2456
|
+
return void 0;
|
|
2457
|
+
}
|
|
2458
|
+
const streamArgs = args.slice(streamsIndex + 1);
|
|
2459
|
+
if (streamArgs.length === 0 || streamArgs.length % 2 !== 0) {
|
|
2460
|
+
return void 0;
|
|
2461
|
+
}
|
|
2462
|
+
return singleShardKey(streamArgs.slice(0, streamArgs.length / 2));
|
|
2463
|
+
}
|
|
2464
|
+
function flowPartitionRoutingKey(args, start) {
|
|
2465
|
+
for (let index = start; index < args.length; index += 1) {
|
|
2466
|
+
const token = commandPart(args[index]);
|
|
2467
|
+
if (token === "PARTITION") {
|
|
2468
|
+
const key = args[index + 1];
|
|
2469
|
+
return typeof key === "string" || import_node_buffer3.Buffer.isBuffer(key) ? key : void 0;
|
|
2470
|
+
}
|
|
2471
|
+
if (token === "PARTITIONS") {
|
|
2472
|
+
const count = Number(args[index + 1]);
|
|
2473
|
+
if (!Number.isInteger(count) || count < 0) {
|
|
2474
|
+
return void 0;
|
|
2475
|
+
}
|
|
2476
|
+
return singleShardKey(args.slice(index + 2, index + 2 + count));
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
return void 0;
|
|
2480
|
+
}
|
|
2481
|
+
function singleShardKey(keys) {
|
|
2482
|
+
const usable = keys.filter((key) => typeof key === "string" || import_node_buffer3.Buffer.isBuffer(key));
|
|
2483
|
+
if (usable.length === 0) {
|
|
2484
|
+
return void 0;
|
|
2485
|
+
}
|
|
2486
|
+
const first = usable[0];
|
|
2487
|
+
if (first == null) {
|
|
2488
|
+
return void 0;
|
|
2489
|
+
}
|
|
2490
|
+
const firstSlot = RoutingTopology.slotForKey(first);
|
|
2491
|
+
return usable.every((key) => RoutingTopology.slotForKey(key) === firstSlot) ? first : void 0;
|
|
2492
|
+
}
|
|
2493
|
+
function commandName(args) {
|
|
2494
|
+
const first = commandPart(args[0]);
|
|
2495
|
+
if (first == null) {
|
|
2496
|
+
return void 0;
|
|
2497
|
+
}
|
|
2498
|
+
if (first === "FLOW" && args.length > 1) {
|
|
2499
|
+
const second = commandPart(args[1]);
|
|
2500
|
+
return second == null ? first : `${first}.${second}`;
|
|
2501
|
+
}
|
|
2502
|
+
if (first === "CLIENT" && args.length > 1) {
|
|
2503
|
+
const second = commandPart(args[1]);
|
|
2504
|
+
return second == null ? first : `${first}.${second}`;
|
|
2505
|
+
}
|
|
2506
|
+
if (first === "CLUSTER" && args.length > 1) {
|
|
2507
|
+
const second = commandPart(args[1]);
|
|
2508
|
+
return second == null ? first : `${first}.${second}`;
|
|
2509
|
+
}
|
|
2510
|
+
return first;
|
|
2511
|
+
}
|
|
2512
|
+
function commandPart(value) {
|
|
2513
|
+
if (typeof value === "string") {
|
|
2514
|
+
return value.toUpperCase();
|
|
2515
|
+
}
|
|
2516
|
+
if (import_node_buffer3.Buffer.isBuffer(value)) {
|
|
2517
|
+
return value.toString("utf8").toUpperCase();
|
|
2518
|
+
}
|
|
2519
|
+
return void 0;
|
|
2520
|
+
}
|
|
2521
|
+
function isPlainObject2(value) {
|
|
2522
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer3.Buffer.isBuffer(value);
|
|
2523
|
+
}
|
|
2524
|
+
function getField(source, ...keys) {
|
|
2525
|
+
if (source instanceof Map) {
|
|
2526
|
+
for (const key of keys) {
|
|
2527
|
+
if (source.has(key)) return source.get(key);
|
|
2528
|
+
const bufferKey = import_node_buffer3.Buffer.from(key);
|
|
2529
|
+
for (const [itemKey, value] of source.entries()) {
|
|
2530
|
+
if (import_node_buffer3.Buffer.isBuffer(itemKey) && itemKey.equals(bufferKey)) {
|
|
2531
|
+
return value;
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
return void 0;
|
|
2536
|
+
}
|
|
2537
|
+
if (isPlainObject2(source)) {
|
|
2538
|
+
for (const key of keys) {
|
|
2539
|
+
if (key in source) return source[key];
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return void 0;
|
|
2543
|
+
}
|
|
2544
|
+
function textOrUndefined(value) {
|
|
2545
|
+
if (typeof value === "string") {
|
|
2546
|
+
return value;
|
|
2547
|
+
}
|
|
2548
|
+
if (import_node_buffer3.Buffer.isBuffer(value)) {
|
|
2549
|
+
return value.toString("utf8");
|
|
2550
|
+
}
|
|
2551
|
+
return void 0;
|
|
2552
|
+
}
|
|
2553
|
+
function numberOrUndefined(value) {
|
|
2554
|
+
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
2555
|
+
}
|
|
2556
|
+
function endpointFromRange(item) {
|
|
2557
|
+
const endpointValue = getField(item, "endpoint");
|
|
2558
|
+
const raw = isPlainObject2(endpointValue) || endpointValue instanceof Map ? endpointValue : item;
|
|
2559
|
+
const host = textOrUndefined(getField(raw, "host", "native_host"));
|
|
2560
|
+
const nativePort = numberOrUndefined(getField(raw, "native_port"));
|
|
2561
|
+
if (host == null || nativePort == null) {
|
|
2562
|
+
throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
|
|
2563
|
+
}
|
|
2564
|
+
const nativeTlsPort = numberOrUndefined(getField(raw, "native_tls_port"));
|
|
2565
|
+
return {
|
|
2566
|
+
host,
|
|
2567
|
+
nativePort,
|
|
2568
|
+
node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
|
|
2569
|
+
...nativeTlsPort == null ? {} : { nativeTlsPort }
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function endpointFromUrl(url) {
|
|
2573
|
+
const parsed = parseFerricUrl2(url);
|
|
2574
|
+
return {
|
|
2575
|
+
host: parsed.host,
|
|
2576
|
+
nativePort: parsed.port,
|
|
2577
|
+
node: parsed.host,
|
|
2578
|
+
...parsed.tls ? { nativeTlsPort: parsed.port } : {}
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
function parseFerricUrl2(value) {
|
|
2582
|
+
const url = new URL(value);
|
|
2583
|
+
if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
|
|
2584
|
+
throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
|
|
2585
|
+
}
|
|
2586
|
+
return {
|
|
2587
|
+
host: url.hostname || "127.0.0.1",
|
|
2588
|
+
...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
|
|
2589
|
+
port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
|
|
2590
|
+
tls: url.protocol === "ferrics:",
|
|
2591
|
+
...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
function endpointKeyFor(endpoint) {
|
|
2595
|
+
return `${endpoint.host.toLowerCase()}:${endpoint.nativePort}`;
|
|
2596
|
+
}
|
|
2597
|
+
function urlFromEndpoint(endpoint, useTls) {
|
|
2598
|
+
const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
|
|
2599
|
+
const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
|
|
2600
|
+
return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
|
|
2601
|
+
}
|
|
2602
|
+
function nativeOnlyOptions(options) {
|
|
2603
|
+
const nativeOptions = { ...options };
|
|
2604
|
+
delete nativeOptions.endpointPolicy;
|
|
2605
|
+
delete nativeOptions.endpointValidator;
|
|
2606
|
+
delete nativeOptions.haRouting;
|
|
2607
|
+
delete nativeOptions.seeds;
|
|
2608
|
+
delete nativeOptions.trustedHosts;
|
|
2609
|
+
delete nativeOptions.warmConnections;
|
|
2610
|
+
return nativeOptions;
|
|
2611
|
+
}
|
|
2612
|
+
function withSeedAuthDefaults(urls, options) {
|
|
2613
|
+
const next = { ...options };
|
|
2614
|
+
for (const url of urls) {
|
|
2615
|
+
const parsed = parseFerricUrl2(url);
|
|
2616
|
+
if (next.username == null && parsed.username != null) {
|
|
2617
|
+
next.username = parsed.username;
|
|
2618
|
+
}
|
|
2619
|
+
if (next.password == null && parsed.password != null) {
|
|
2620
|
+
next.password = parsed.password;
|
|
2621
|
+
}
|
|
2622
|
+
if (next.username != null && next.password != null) {
|
|
2623
|
+
break;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
return next;
|
|
2627
|
+
}
|
|
2628
|
+
function isRetryableRouteError(error) {
|
|
2629
|
+
if (!(error instanceof Error)) {
|
|
2630
|
+
return false;
|
|
2631
|
+
}
|
|
2632
|
+
const message = error.message.toLowerCase();
|
|
2633
|
+
return message.includes("connection closed") || message.includes("connection is closed") || message.includes("shard not available") || message.includes("leader") || message.includes("route");
|
|
2634
|
+
}
|
|
2635
|
+
function normalizedHostSet(hosts) {
|
|
2636
|
+
return new Set(hosts.filter((host) => host != null && host !== "").map((host) => host.toLowerCase()));
|
|
2637
|
+
}
|
|
2638
|
+
function hashTagOrKey(key) {
|
|
2639
|
+
const start = key.indexOf("{");
|
|
2640
|
+
if (start < 0) {
|
|
2641
|
+
return key;
|
|
2642
|
+
}
|
|
2643
|
+
const end = key.indexOf("}", start + 1);
|
|
2644
|
+
return end > start + 1 ? key.slice(start + 1, end) : key;
|
|
2645
|
+
}
|
|
2646
|
+
function flowHashTag(rest, fallbackKey) {
|
|
2647
|
+
const end = rest.indexOf("}");
|
|
2648
|
+
return end > 0 ? rest.slice(0, end) : hashTagOrKey(fallbackKey);
|
|
2649
|
+
}
|
|
2650
|
+
var crc32Table = new Uint32Array(256);
|
|
2651
|
+
for (let index = 0; index < 256; index += 1) {
|
|
2652
|
+
let value = index;
|
|
2653
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
2654
|
+
value = (value & 1) !== 0 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
2655
|
+
}
|
|
2656
|
+
crc32Table[index] = value >>> 0;
|
|
2657
|
+
}
|
|
2658
|
+
function crc32(buffer) {
|
|
2659
|
+
let value = 4294967295;
|
|
2660
|
+
for (const byte of buffer) {
|
|
2661
|
+
value = value >>> 8 ^ (crc32Table[(value ^ byte) & 255] ?? 0);
|
|
2662
|
+
}
|
|
2663
|
+
return (value ^ 4294967295) >>> 0;
|
|
2664
|
+
}
|
|
2665
|
+
var firstKeyCommands = /* @__PURE__ */ new Set([
|
|
2666
|
+
"BITCOUNT",
|
|
2667
|
+
"BITFIELD",
|
|
2668
|
+
"BITPOS",
|
|
2669
|
+
"CAS",
|
|
2670
|
+
"EXISTS",
|
|
2671
|
+
"EXPIRE",
|
|
2672
|
+
"EXPIREAT",
|
|
2673
|
+
"FETCH_OR_COMPUTE",
|
|
2674
|
+
"FETCH_OR_COMPUTE_ERROR",
|
|
2675
|
+
"FETCH_OR_COMPUTE_RESULT",
|
|
2676
|
+
"FERRICSTORE.KEY_INFO",
|
|
2677
|
+
"GET",
|
|
2678
|
+
"GETBIT",
|
|
2679
|
+
"GETDEL",
|
|
2680
|
+
"GETEX",
|
|
2681
|
+
"HDEL",
|
|
2682
|
+
"HEXISTS",
|
|
2683
|
+
"HGET",
|
|
2684
|
+
"HGETALL",
|
|
2685
|
+
"HINCRBY",
|
|
2686
|
+
"HKEYS",
|
|
2687
|
+
"HLEN",
|
|
2688
|
+
"HMGET",
|
|
2689
|
+
"HMSET",
|
|
2690
|
+
"HSET",
|
|
2691
|
+
"HVALS",
|
|
2692
|
+
"LOCK",
|
|
2693
|
+
"LPOP",
|
|
2694
|
+
"LPUSH",
|
|
2695
|
+
"LRANGE",
|
|
2696
|
+
"LREM",
|
|
2697
|
+
"RATELIMIT.ADD",
|
|
2698
|
+
"RPOP",
|
|
2699
|
+
"RPUSH",
|
|
2700
|
+
"SADD",
|
|
2701
|
+
"SCARD",
|
|
2702
|
+
"SISMEMBER",
|
|
2703
|
+
"SMEMBERS",
|
|
2704
|
+
"SREM",
|
|
2705
|
+
"SET",
|
|
2706
|
+
"SETBIT",
|
|
2707
|
+
"STRLEN",
|
|
2708
|
+
"TTL",
|
|
2709
|
+
"TYPE",
|
|
2710
|
+
"UNLINK",
|
|
2711
|
+
"UNLOCK",
|
|
2712
|
+
"XADD",
|
|
2713
|
+
"XLEN",
|
|
2714
|
+
"XRANGE",
|
|
2715
|
+
"ZADD",
|
|
2716
|
+
"ZCARD",
|
|
2717
|
+
"ZRANGE",
|
|
2718
|
+
"ZREM",
|
|
2719
|
+
"ZSCORE"
|
|
2720
|
+
]);
|
|
2721
|
+
var typeScopedFlowCommands = /* @__PURE__ */ new Set([
|
|
2722
|
+
"FLOW.APPROVAL.LIST",
|
|
2723
|
+
"FLOW.ATTRIBUTE_VALUES",
|
|
2724
|
+
"FLOW.ATTRIBUTES",
|
|
2725
|
+
"FLOW.BUDGET.LIST",
|
|
2726
|
+
"FLOW.FAILURES",
|
|
2727
|
+
"FLOW.GOVERNANCE.OVERVIEW",
|
|
2728
|
+
"FLOW.INFO",
|
|
2729
|
+
"FLOW.LIMIT.LIST",
|
|
2730
|
+
"FLOW.LIST",
|
|
2731
|
+
"FLOW.POLICY.GET",
|
|
2732
|
+
"FLOW.POLICY.SET",
|
|
2733
|
+
"FLOW.RETENTION_CLEANUP",
|
|
2734
|
+
"FLOW.SCHEDULE.FIRE_DUE",
|
|
2735
|
+
"FLOW.SCHEDULE.LIST",
|
|
2736
|
+
"FLOW.SEARCH",
|
|
2737
|
+
"FLOW.STATS",
|
|
2738
|
+
"FLOW.STUCK",
|
|
2739
|
+
"FLOW.TERMINALS"
|
|
2740
|
+
]);
|
|
2741
|
+
|
|
1813
2742
|
// src/codecs.ts
|
|
1814
2743
|
var RawCodec = class {
|
|
1815
2744
|
encode(value) {
|
|
@@ -2023,7 +2952,7 @@ function parseKvResponse(value) {
|
|
|
2023
2952
|
return { value };
|
|
2024
2953
|
}
|
|
2025
2954
|
function autoPartitionKeyForId(id) {
|
|
2026
|
-
return `${AUTO_PARTITION_PREFIX}${
|
|
2955
|
+
return `${AUTO_PARTITION_PREFIX}${crc322(Buffer.from(id)) % AUTO_PARTITION_BUCKETS}`;
|
|
2027
2956
|
}
|
|
2028
2957
|
function expandManyResponse(value, count) {
|
|
2029
2958
|
if (Array.isArray(value) && value.length === count) {
|
|
@@ -2093,7 +3022,7 @@ function coerceDiagnosticValue(value) {
|
|
|
2093
3022
|
}
|
|
2094
3023
|
return value;
|
|
2095
3024
|
}
|
|
2096
|
-
function
|
|
3025
|
+
function crc322(buffer) {
|
|
2097
3026
|
let crc = 4294967295;
|
|
2098
3027
|
for (const byte of buffer) {
|
|
2099
3028
|
crc ^= byte;
|
|
@@ -3019,14 +3948,14 @@ function claimedItemFromResp(value, codec) {
|
|
|
3019
3948
|
const partition = tuple[1] == null ? null : bytes(tuple[1]);
|
|
3020
3949
|
const leaseToken = bytes(tuple[2]);
|
|
3021
3950
|
const fencingToken = integer(tuple[3]);
|
|
3022
|
-
const attrs = tuple[5] ?? (
|
|
3951
|
+
const attrs = tuple[5] ?? (isPlainObject3(tuple[4]) ? tuple[4] : void 0);
|
|
3023
3952
|
const item = {
|
|
3024
3953
|
id: text(id),
|
|
3025
3954
|
partitionKey: optionalString(partition),
|
|
3026
3955
|
leaseToken,
|
|
3027
3956
|
fencingToken,
|
|
3028
3957
|
runState: optionalString(tuple[4]),
|
|
3029
|
-
attributes:
|
|
3958
|
+
attributes: isPlainObject3(attrs) ? attrs : void 0,
|
|
3030
3959
|
type: "",
|
|
3031
3960
|
state: "running"
|
|
3032
3961
|
};
|
|
@@ -3146,9 +4075,9 @@ function decodeValues(value, codec) {
|
|
|
3146
4075
|
}
|
|
3147
4076
|
function plainObjectField(value, key) {
|
|
3148
4077
|
const item = field(value, key);
|
|
3149
|
-
return
|
|
4078
|
+
return isPlainObject3(item) ? item : void 0;
|
|
3150
4079
|
}
|
|
3151
|
-
function
|
|
4080
|
+
function isPlainObject3(value) {
|
|
3152
4081
|
return typeof value === "object" && value != null && !Buffer.isBuffer(value) && !(value instanceof Uint8Array) && !Array.isArray(value);
|
|
3153
4082
|
}
|
|
3154
4083
|
|
|
@@ -3200,7 +4129,18 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3200
4129
|
}
|
|
3201
4130
|
static async fromUrl(url, options = {}) {
|
|
3202
4131
|
const reconnectOptions = options.reconnect ?? options.nativeOptions?.autoReconnect ?? true;
|
|
3203
|
-
const
|
|
4132
|
+
const nativeOptions = options.nativeOptions ?? {};
|
|
4133
|
+
const seeds = nativeOptions.seeds ?? [];
|
|
4134
|
+
const createExecutor = async () => nativeOptions.haRouting === true || seeds.length > 0 ? await TopologyNativeAdapterPool.fromUrls([url, ...seeds], nativeOptions) : await NativeAdapter.fromUrl(url, nativeOptions);
|
|
4135
|
+
const executor = reconnectOptions === false ? await createExecutor() : new ReconnectingExecutor(createExecutor, reconnectOptions === true ? {} : reconnectOptions);
|
|
4136
|
+
return new _FerricStoreClient(executor, options);
|
|
4137
|
+
}
|
|
4138
|
+
static async fromUrls(urls, options = {}) {
|
|
4139
|
+
if (urls.length === 0) {
|
|
4140
|
+
throw new Error("FerricStoreClient.fromUrls requires at least one URL");
|
|
4141
|
+
}
|
|
4142
|
+
const reconnectOptions = options.reconnect ?? options.nativeOptions?.autoReconnect ?? true;
|
|
4143
|
+
const createExecutor = async () => await TopologyNativeAdapterPool.fromUrls(urls, options.nativeOptions);
|
|
3204
4144
|
const executor = reconnectOptions === false ? await createExecutor() : new ReconnectingExecutor(createExecutor, reconnectOptions === true ? {} : reconnectOptions);
|
|
3205
4145
|
return new _FerricStoreClient(executor, options);
|
|
3206
4146
|
}
|
|
@@ -3216,6 +4156,18 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3216
4156
|
async close() {
|
|
3217
4157
|
await this.executor.close?.();
|
|
3218
4158
|
}
|
|
4159
|
+
async refreshTopology() {
|
|
4160
|
+
if (this.executor.refreshTopology == null) {
|
|
4161
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
4162
|
+
}
|
|
4163
|
+
return await this.executor.refreshTopology();
|
|
4164
|
+
}
|
|
4165
|
+
async route(key) {
|
|
4166
|
+
if (this.executor.route == null) {
|
|
4167
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
4168
|
+
}
|
|
4169
|
+
return await this.executor.route(key);
|
|
4170
|
+
}
|
|
3219
4171
|
async ping(message) {
|
|
3220
4172
|
return await this.command("PING", ...message == null ? [] : [message]);
|
|
3221
4173
|
}
|
|
@@ -3278,7 +4230,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3278
4230
|
return response == null ? null : text(response);
|
|
3279
4231
|
}
|
|
3280
4232
|
async clientInfo() {
|
|
3281
|
-
return
|
|
4233
|
+
return infoText(await this.command("CLIENT", "INFO"));
|
|
3282
4234
|
}
|
|
3283
4235
|
async clientList(options = {}) {
|
|
3284
4236
|
return text(await this.command("CLIENT", "LIST", ...options.type == null ? [] : ["TYPE", options.type]));
|
|
@@ -3333,7 +4285,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3333
4285
|
return Number(await this.command("PUBSUB", "NUMPAT"));
|
|
3334
4286
|
}
|
|
3335
4287
|
async aclSetUser(username, rules) {
|
|
3336
|
-
return okResponse(await this.command("ACL", "SETUSER", username, ...rules));
|
|
4288
|
+
return okResponse(await this.command("ACL", "SETUSER", username, ...ruleArgs(rules)));
|
|
3337
4289
|
}
|
|
3338
4290
|
async aclDelUser(...usernames) {
|
|
3339
4291
|
return Number(await this.command("ACL", "DELUSER", ...usernames));
|
|
@@ -3344,6 +4296,9 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3344
4296
|
async aclList() {
|
|
3345
4297
|
return arrayResponse(await this.command("ACL", "LIST"));
|
|
3346
4298
|
}
|
|
4299
|
+
async aclListUsers() {
|
|
4300
|
+
return await this.aclList();
|
|
4301
|
+
}
|
|
3347
4302
|
async aclWhoami() {
|
|
3348
4303
|
return text(await this.command("ACL", "WHOAMI"));
|
|
3349
4304
|
}
|
|
@@ -3439,6 +4394,54 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3439
4394
|
async ferricstoreDoctor(...args) {
|
|
3440
4395
|
return await this.command("FERRICSTORE.DOCTOR", ...args);
|
|
3441
4396
|
}
|
|
4397
|
+
async capabilities() {
|
|
4398
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.CAPABILITIES"));
|
|
4399
|
+
}
|
|
4400
|
+
async ensureNamespace(prefix, attrs = {}) {
|
|
4401
|
+
return normalizeAdminResponse(
|
|
4402
|
+
await this.command("FERRICSTORE.NAMESPACE", "ENSURE", prefix, ...managementPairArgs(attrs))
|
|
4403
|
+
);
|
|
4404
|
+
}
|
|
4405
|
+
async getNamespace(prefix) {
|
|
4406
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "GET", prefix));
|
|
4407
|
+
}
|
|
4408
|
+
async listNamespaces() {
|
|
4409
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "LIST"));
|
|
4410
|
+
}
|
|
4411
|
+
async deleteNamespace(prefix) {
|
|
4412
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "DELETE", prefix));
|
|
4413
|
+
}
|
|
4414
|
+
async setQuota(namespace, quotaSpec = {}) {
|
|
4415
|
+
return normalizeAdminResponse(
|
|
4416
|
+
await this.command("FERRICSTORE.QUOTA", "SET", namespace, ...managementPairArgs(quotaSpec))
|
|
4417
|
+
);
|
|
4418
|
+
}
|
|
4419
|
+
async getQuota(namespace) {
|
|
4420
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.QUOTA", "GET", namespace));
|
|
4421
|
+
}
|
|
4422
|
+
async quotaUsage(namespace) {
|
|
4423
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.QUOTA", "USAGE", namespace));
|
|
4424
|
+
}
|
|
4425
|
+
async clusterInfo() {
|
|
4426
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.TELEMETRY", "CLUSTER_INFO"));
|
|
4427
|
+
}
|
|
4428
|
+
async namespaceUsage(prefix) {
|
|
4429
|
+
return normalizeAdminResponse(
|
|
4430
|
+
await this.command("FERRICSTORE.TELEMETRY", "NAMESPACE_USAGE", prefix)
|
|
4431
|
+
);
|
|
4432
|
+
}
|
|
4433
|
+
async flowQuery(attrs = {}) {
|
|
4434
|
+
const response = normalizeAdminResponse(
|
|
4435
|
+
await this.command("FERRICSTORE.TELEMETRY", "FLOW_QUERY", ...managementPairArgs(attrs))
|
|
4436
|
+
);
|
|
4437
|
+
return Array.isArray(response) ? [...response] : [];
|
|
4438
|
+
}
|
|
4439
|
+
async flowHistory(id, attrs = {}) {
|
|
4440
|
+
const response = normalizeAdminResponse(
|
|
4441
|
+
await this.command("FERRICSTORE.TELEMETRY", "FLOW_HISTORY", id, ...managementPairArgs(attrs))
|
|
4442
|
+
);
|
|
4443
|
+
return Array.isArray(response) ? [...response] : [];
|
|
4444
|
+
}
|
|
3442
4445
|
async create(id, options) {
|
|
3443
4446
|
const currentNowMs = options.nowMs ?? nowMs();
|
|
3444
4447
|
const args = [
|
|
@@ -3919,6 +4922,14 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3919
4922
|
async list(type, options = {}) {
|
|
3920
4923
|
return await this.indexQuery("FLOW.LIST", type, options);
|
|
3921
4924
|
}
|
|
4925
|
+
async search(type, options = {}) {
|
|
4926
|
+
const args = ["FLOW.SEARCH", type];
|
|
4927
|
+
appendReadOptions(args, options);
|
|
4928
|
+
appendAttributes(args, options.attributes);
|
|
4929
|
+
appendSearchStateMeta(args, options.state, options.stateMeta);
|
|
4930
|
+
const response = await this.command(...args);
|
|
4931
|
+
return Array.isArray(response) ? this.records(response) : [];
|
|
4932
|
+
}
|
|
3922
4933
|
async terminals(type, options = {}) {
|
|
3923
4934
|
return await this.indexQuery("FLOW.TERMINALS", type, options);
|
|
3924
4935
|
}
|
|
@@ -4088,6 +5099,18 @@ var ErrorMappingExecutor = class {
|
|
|
4088
5099
|
throwMapped(error);
|
|
4089
5100
|
}
|
|
4090
5101
|
}
|
|
5102
|
+
async refreshTopology() {
|
|
5103
|
+
if (this.executor.refreshTopology == null) {
|
|
5104
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
5105
|
+
}
|
|
5106
|
+
return await this.executor.refreshTopology();
|
|
5107
|
+
}
|
|
5108
|
+
async route(key) {
|
|
5109
|
+
if (this.executor.route == null) {
|
|
5110
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
5111
|
+
}
|
|
5112
|
+
return await this.executor.route(key);
|
|
5113
|
+
}
|
|
4091
5114
|
async close() {
|
|
4092
5115
|
await this.executor.close?.();
|
|
4093
5116
|
}
|
|
@@ -4123,6 +5146,18 @@ var AutoBatchExecutor = class {
|
|
|
4123
5146
|
}
|
|
4124
5147
|
return await Promise.all(commands.map((command) => this.executor.executeCommand(...command)));
|
|
4125
5148
|
}
|
|
5149
|
+
async refreshTopology() {
|
|
5150
|
+
if (this.executor.refreshTopology == null) {
|
|
5151
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
5152
|
+
}
|
|
5153
|
+
return await this.executor.refreshTopology();
|
|
5154
|
+
}
|
|
5155
|
+
async route(key) {
|
|
5156
|
+
if (this.executor.route == null) {
|
|
5157
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
5158
|
+
}
|
|
5159
|
+
return await this.executor.route(key);
|
|
5160
|
+
}
|
|
4126
5161
|
async close() {
|
|
4127
5162
|
this.scheduled = false;
|
|
4128
5163
|
if (this.timer != null) {
|
|
@@ -4283,7 +5318,7 @@ var neverAutoBatchCommandPrefixes = /* @__PURE__ */ new Set([
|
|
|
4283
5318
|
]);
|
|
4284
5319
|
var neverAutoBatchCommands = /* @__PURE__ */ new Set(["FLOW.CLAIM_DUE", "FLOW.RECLAIM"]);
|
|
4285
5320
|
function autoBatchCommandAllowed(command, mode) {
|
|
4286
|
-
const name =
|
|
5321
|
+
const name = commandName2(command);
|
|
4287
5322
|
if (name == null || neverAutoBatchCommands.has(name)) {
|
|
4288
5323
|
return false;
|
|
4289
5324
|
}
|
|
@@ -4293,18 +5328,18 @@ function autoBatchCommandAllowed(command, mode) {
|
|
|
4293
5328
|
}
|
|
4294
5329
|
return mode === "all" || safeAutoBatchCommands.has(name);
|
|
4295
5330
|
}
|
|
4296
|
-
function
|
|
4297
|
-
const first =
|
|
5331
|
+
function commandName2(command) {
|
|
5332
|
+
const first = commandPart2(command[0]);
|
|
4298
5333
|
if (first == null) {
|
|
4299
5334
|
return null;
|
|
4300
5335
|
}
|
|
4301
5336
|
if (first === "FLOW" && command.length > 1) {
|
|
4302
|
-
const second =
|
|
5337
|
+
const second = commandPart2(command[1]);
|
|
4303
5338
|
return second == null ? first : `${first}.${second}`;
|
|
4304
5339
|
}
|
|
4305
5340
|
return first;
|
|
4306
5341
|
}
|
|
4307
|
-
function
|
|
5342
|
+
function commandPart2(value) {
|
|
4308
5343
|
if (typeof value === "string") {
|
|
4309
5344
|
return value.toUpperCase();
|
|
4310
5345
|
}
|
|
@@ -4329,6 +5364,67 @@ function appendStateMeta(args, stateMeta) {
|
|
|
4329
5364
|
args.push("STATE_META", name, value);
|
|
4330
5365
|
}
|
|
4331
5366
|
}
|
|
5367
|
+
function appendAttributes(args, attributes) {
|
|
5368
|
+
for (const [name, value] of Object.entries(attributes ?? {})) {
|
|
5369
|
+
args.push("ATTRIBUTE", name, value);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
function appendSearchStateMeta(args, state, stateMeta) {
|
|
5373
|
+
const entries = Object.entries(stateMeta ?? {});
|
|
5374
|
+
if (entries.length === 0) {
|
|
5375
|
+
return;
|
|
5376
|
+
}
|
|
5377
|
+
if (entries.every(([, value]) => isPlainObject4(value))) {
|
|
5378
|
+
for (const [metaState, values] of entries) {
|
|
5379
|
+
args.push("STATE_META", metaState, values);
|
|
5380
|
+
}
|
|
5381
|
+
return;
|
|
5382
|
+
}
|
|
5383
|
+
if (state == null) {
|
|
5384
|
+
throw new Error("search stateMeta filters require state or nested state metadata");
|
|
5385
|
+
}
|
|
5386
|
+
args.push("STATE_META", state, stateMeta);
|
|
5387
|
+
}
|
|
5388
|
+
function normalizeAdminResponse(value) {
|
|
5389
|
+
return normalizeRefMeta(value);
|
|
5390
|
+
}
|
|
5391
|
+
function infoText(value) {
|
|
5392
|
+
if (typeof value === "string" || Buffer.isBuffer(value)) {
|
|
5393
|
+
return text(value);
|
|
5394
|
+
}
|
|
5395
|
+
const normalized = normalizeRefMeta(value);
|
|
5396
|
+
if (isPlainObject4(normalized)) {
|
|
5397
|
+
return Object.entries(normalized).map(([key, item]) => `${key}=${diagnosticValueText(item)}`).join(" ");
|
|
5398
|
+
}
|
|
5399
|
+
return diagnosticValueText(normalized);
|
|
5400
|
+
}
|
|
5401
|
+
function diagnosticValueText(value) {
|
|
5402
|
+
if (value == null) {
|
|
5403
|
+
return "";
|
|
5404
|
+
}
|
|
5405
|
+
if (Buffer.isBuffer(value)) {
|
|
5406
|
+
return value.toString("utf8");
|
|
5407
|
+
}
|
|
5408
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
5409
|
+
return String(value);
|
|
5410
|
+
}
|
|
5411
|
+
return JSON.stringify(value);
|
|
5412
|
+
}
|
|
5413
|
+
function managementPairArgs(pairs2) {
|
|
5414
|
+
const args = [];
|
|
5415
|
+
for (const [key, value] of Object.entries(pairs2)) {
|
|
5416
|
+
if (value != null) {
|
|
5417
|
+
args.push(key.toUpperCase(), value);
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
return args;
|
|
5421
|
+
}
|
|
5422
|
+
function ruleArgs(rules) {
|
|
5423
|
+
return typeof rules === "string" ? [rules] : [...rules];
|
|
5424
|
+
}
|
|
5425
|
+
function isPlainObject4(value) {
|
|
5426
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer.isBuffer(value);
|
|
5427
|
+
}
|
|
4332
5428
|
function sharedCreateManyStateMeta(items, stateMeta) {
|
|
4333
5429
|
const itemMetas = items.map((item) => item.stateMeta).filter((meta) => meta != null);
|
|
4334
5430
|
if (itemMetas.length === 0) {
|
|
@@ -5302,6 +6398,7 @@ function valueRefToString(value) {
|
|
|
5302
6398
|
0 && (module.exports = {
|
|
5303
6399
|
BitmapStore,
|
|
5304
6400
|
BloomFilterStore,
|
|
6401
|
+
COMMAND_OPCODES,
|
|
5305
6402
|
CountMinSketchStore,
|
|
5306
6403
|
CuckooFilterStore,
|
|
5307
6404
|
FerricStoreClient,
|
|
@@ -5326,12 +6423,14 @@ function valueRefToString(value) {
|
|
|
5326
6423
|
QueueWorker,
|
|
5327
6424
|
RawCodec,
|
|
5328
6425
|
ReconnectingExecutor,
|
|
6426
|
+
RoutingTopology,
|
|
5329
6427
|
SetStore,
|
|
5330
6428
|
SortedSetStore,
|
|
5331
6429
|
StaleLeaseError,
|
|
5332
6430
|
StreamStore,
|
|
5333
6431
|
TDigestStore,
|
|
5334
6432
|
TopKStore,
|
|
6433
|
+
TopologyNativeAdapterPool,
|
|
5335
6434
|
Workflow,
|
|
5336
6435
|
WorkflowClient,
|
|
5337
6436
|
WorkflowContext,
|