@ferricstore/ferricstore 0.1.3 → 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 +1264 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +277 -6
- package/dist/index.d.ts +277 -6
- package/dist/index.js +1259 -36
- 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 +7 -0
- 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 -0
- 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 +6 -0
- 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 +12 -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 -0
- 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,
|
|
@@ -55,12 +56,15 @@ __export(index_exports, {
|
|
|
55
56
|
QueueClient: () => QueueClient,
|
|
56
57
|
QueueWorker: () => QueueWorker,
|
|
57
58
|
RawCodec: () => RawCodec,
|
|
59
|
+
ReconnectingExecutor: () => ReconnectingExecutor,
|
|
60
|
+
RoutingTopology: () => RoutingTopology,
|
|
58
61
|
SetStore: () => SetStore,
|
|
59
62
|
SortedSetStore: () => SortedSetStore,
|
|
60
63
|
StaleLeaseError: () => StaleLeaseError,
|
|
61
64
|
StreamStore: () => StreamStore,
|
|
62
65
|
TDigestStore: () => TDigestStore,
|
|
63
66
|
TopKStore: () => TopKStore,
|
|
67
|
+
TopologyNativeAdapterPool: () => TopologyNativeAdapterPool,
|
|
64
68
|
Workflow: () => Workflow,
|
|
65
69
|
WorkflowClient: () => WorkflowClient,
|
|
66
70
|
WorkflowContext: () => WorkflowContext,
|
|
@@ -69,6 +73,7 @@ __export(index_exports, {
|
|
|
69
73
|
classifyServerError: () => classifyServerError,
|
|
70
74
|
complete: () => complete,
|
|
71
75
|
fail: () => fail,
|
|
76
|
+
isReconnectableClosedConnectionError: () => isReconnectableClosedConnectionError,
|
|
72
77
|
mapException: () => mapException,
|
|
73
78
|
retry: () => retry,
|
|
74
79
|
transition: () => transition
|
|
@@ -187,24 +192,171 @@ var FLAG_CUSTOM_PAYLOAD = 2;
|
|
|
187
192
|
var FLAG_COMPRESSED = 8;
|
|
188
193
|
var FLAG_MORE_CHUNKS = 32;
|
|
189
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
|
+
};
|
|
190
335
|
var OPCODES = {
|
|
191
|
-
startup:
|
|
192
|
-
auth:
|
|
193
|
-
ping:
|
|
194
|
-
clientSetName:
|
|
195
|
-
clientInfo:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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"]
|
|
208
360
|
};
|
|
209
361
|
var COMPACT_FLOW_CLAIM_JOBS = 128;
|
|
210
362
|
var COMPACT_OK_LIST = 129;
|
|
@@ -337,12 +489,46 @@ function buildProtocolCommand(args) {
|
|
|
337
489
|
}
|
|
338
490
|
const command = asText(args[0]).toUpperCase();
|
|
339
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
|
+
}
|
|
340
502
|
if (command === "PING") {
|
|
341
503
|
return { laneId: 0, opcode: OPCODES.ping, payload: commandArgs.length === 0 ? {} : { message: commandArgs[0] } };
|
|
342
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
|
+
}
|
|
343
523
|
if (command === "CLIENT" && asText(commandArgs[0]).toUpperCase() === "SETNAME" && commandArgs.length === 2) {
|
|
344
524
|
return { laneId: 0, opcode: OPCODES.clientSetName, payload: { name: commandArgs[1] } };
|
|
345
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
|
+
}
|
|
346
532
|
if (command === "GET" && commandArgs.length >= 1) {
|
|
347
533
|
return { opcode: OPCODES.get, payload: { key: commandArgs[0] } };
|
|
348
534
|
}
|
|
@@ -361,7 +547,7 @@ function buildProtocolCommand(args) {
|
|
|
361
547
|
if (command === "DEL") {
|
|
362
548
|
return { opcode: OPCODES.del, payload: { keys: commandArgs } };
|
|
363
549
|
}
|
|
364
|
-
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(commandArgs)) {
|
|
550
|
+
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(command, commandArgs)) {
|
|
365
551
|
return commandExec(args);
|
|
366
552
|
}
|
|
367
553
|
if (command === "FLOW.CREATE") {
|
|
@@ -379,6 +565,9 @@ function buildProtocolCommand(args) {
|
|
|
379
565
|
if (command === "FLOW.COMPLETE_MANY") {
|
|
380
566
|
return flowCompleteManyPayload(commandArgs) ?? commandExec(args);
|
|
381
567
|
}
|
|
568
|
+
if (command === "FLOW.SEARCH") {
|
|
569
|
+
return flowSearchPayload(commandArgs) ?? commandExec(args);
|
|
570
|
+
}
|
|
382
571
|
return commandExec(args);
|
|
383
572
|
}
|
|
384
573
|
function commandExec(args) {
|
|
@@ -723,13 +912,13 @@ function writeOptionalBinary(out, offset, value) {
|
|
|
723
912
|
}
|
|
724
913
|
return writeBinary(out, offset, value);
|
|
725
914
|
}
|
|
726
|
-
function hasFlowCommandOnlyOption(args) {
|
|
915
|
+
function hasFlowCommandOnlyOption(command, args) {
|
|
727
916
|
return args.some((arg) => {
|
|
728
917
|
if (typeof arg !== "string" && !import_node_buffer.Buffer.isBuffer(arg)) {
|
|
729
918
|
return false;
|
|
730
919
|
}
|
|
731
920
|
const token = asText(arg).toUpperCase();
|
|
732
|
-
return token === "
|
|
921
|
+
return token === "INDEXED_STATE_META" || command !== "FLOW.SEARCH" && token === "STATE_META";
|
|
733
922
|
});
|
|
734
923
|
}
|
|
735
924
|
function flowCreatePayload(args) {
|
|
@@ -847,6 +1036,12 @@ function flowCompleteManyPayload(args) {
|
|
|
847
1036
|
if (!auto && !mixed) payload.partition_key = partition;
|
|
848
1037
|
return { opcode: OPCODES.flowCompleteMany, payload };
|
|
849
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
|
+
}
|
|
850
1045
|
function compactFlowCreateManyPayload(partition, rawItems, mixed, auto, options) {
|
|
851
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") {
|
|
852
1047
|
return void 0;
|
|
@@ -949,6 +1144,78 @@ function compactFlowCompleteManyPayload(partition, rawItems, mixed, auto, option
|
|
|
949
1144
|
}
|
|
950
1145
|
return { flags: FLAG_CUSTOM_PAYLOAD, opcode: OPCODES.flowCompleteMany, payload: out };
|
|
951
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
|
+
}
|
|
952
1219
|
function parseFlowOptions(args, start, end, config) {
|
|
953
1220
|
const payload = {};
|
|
954
1221
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1470,15 +1737,21 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1470
1737
|
maxChunkBytes;
|
|
1471
1738
|
requestId = 0n;
|
|
1472
1739
|
protocolLanes;
|
|
1740
|
+
heartbeatIntervalMs;
|
|
1741
|
+
heartbeatInFlight = false;
|
|
1742
|
+
heartbeatTimer;
|
|
1743
|
+
lastActivityMs = Date.now();
|
|
1473
1744
|
timeoutMs;
|
|
1474
|
-
constructor(socket, timeoutMs, protocolLanes, maxChunkBytes) {
|
|
1745
|
+
constructor(socket, timeoutMs, protocolLanes, maxChunkBytes, heartbeatIntervalMs) {
|
|
1475
1746
|
this.socket = socket;
|
|
1476
1747
|
this.maxChunkBytes = Math.max(1, Math.trunc(maxChunkBytes));
|
|
1477
1748
|
this.timeoutMs = timeoutMs;
|
|
1478
1749
|
this.protocolLanes = Math.max(1, Math.trunc(protocolLanes));
|
|
1750
|
+
this.heartbeatIntervalMs = normalizeHeartbeatInterval(heartbeatIntervalMs);
|
|
1479
1751
|
this.socket.on("data", (chunk) => this.onData(chunk));
|
|
1480
1752
|
this.socket.on("error", (error) => this.failAll(error));
|
|
1481
1753
|
this.socket.on("close", () => this.failAll(new FerricStoreError("FerricStore connection closed")));
|
|
1754
|
+
this.startHeartbeat();
|
|
1482
1755
|
}
|
|
1483
1756
|
static async fromUrl(url, options = {}) {
|
|
1484
1757
|
const parsed = parseFerricUrl(url);
|
|
@@ -1487,7 +1760,8 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1487
1760
|
socket,
|
|
1488
1761
|
options.timeoutMs ?? 3e4,
|
|
1489
1762
|
options.protocolLanes ?? 8,
|
|
1490
|
-
options.maxChunkBytes ?? 64 * 1024 * 1024
|
|
1763
|
+
options.maxChunkBytes ?? 64 * 1024 * 1024,
|
|
1764
|
+
options.heartbeatIntervalMs
|
|
1491
1765
|
);
|
|
1492
1766
|
await adapter.startup(options.clientName);
|
|
1493
1767
|
const password = options.password ?? parsed.password;
|
|
@@ -1500,6 +1774,9 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1500
1774
|
const command = buildProtocolCommand(args);
|
|
1501
1775
|
return await this.request(command);
|
|
1502
1776
|
}
|
|
1777
|
+
async executeProtocolCommand(command, laneId) {
|
|
1778
|
+
return await this.request(laneId == null ? command : { ...command, laneId });
|
|
1779
|
+
}
|
|
1503
1780
|
async executePipeline(commands, options = {}) {
|
|
1504
1781
|
if (commands.length === 0) {
|
|
1505
1782
|
return [];
|
|
@@ -1511,6 +1788,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1511
1788
|
return;
|
|
1512
1789
|
}
|
|
1513
1790
|
this.closed = true;
|
|
1791
|
+
this.stopHeartbeat();
|
|
1514
1792
|
this.socket.end();
|
|
1515
1793
|
}
|
|
1516
1794
|
async startup(clientName) {
|
|
@@ -1532,6 +1810,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1532
1810
|
if (this.closed) {
|
|
1533
1811
|
throw new FerricStoreError("FerricStore connection is closed");
|
|
1534
1812
|
}
|
|
1813
|
+
this.lastActivityMs = Date.now();
|
|
1535
1814
|
const requestId = this.nextRequestId();
|
|
1536
1815
|
const frame = encodeRequest(this.assignLane(command), requestId);
|
|
1537
1816
|
return await new Promise((resolve, reject) => {
|
|
@@ -1545,10 +1824,12 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1545
1824
|
opcode: command.opcode,
|
|
1546
1825
|
reject: (reason) => {
|
|
1547
1826
|
clearTimeout(timer);
|
|
1827
|
+
this.lastActivityMs = Date.now();
|
|
1548
1828
|
reject(reason instanceof Error ? reason : classifyServerError(String(reason), reason));
|
|
1549
1829
|
},
|
|
1550
1830
|
resolve: (value) => {
|
|
1551
1831
|
clearTimeout(timer);
|
|
1832
|
+
this.lastActivityMs = Date.now();
|
|
1552
1833
|
resolve(value);
|
|
1553
1834
|
}
|
|
1554
1835
|
});
|
|
@@ -1557,11 +1838,43 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1557
1838
|
this.pending.delete(requestId);
|
|
1558
1839
|
this.cleanupChunksForRequest(requestId);
|
|
1559
1840
|
clearTimeout(timer);
|
|
1841
|
+
this.lastActivityMs = Date.now();
|
|
1560
1842
|
reject(error);
|
|
1561
1843
|
}
|
|
1562
1844
|
});
|
|
1563
1845
|
});
|
|
1564
1846
|
}
|
|
1847
|
+
startHeartbeat() {
|
|
1848
|
+
if (this.heartbeatIntervalMs == null) {
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
this.heartbeatTimer = setInterval(() => {
|
|
1852
|
+
void this.sendHeartbeat();
|
|
1853
|
+
}, this.heartbeatIntervalMs);
|
|
1854
|
+
this.heartbeatTimer.unref?.();
|
|
1855
|
+
}
|
|
1856
|
+
stopHeartbeat() {
|
|
1857
|
+
if (this.heartbeatTimer == null) {
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
clearInterval(this.heartbeatTimer);
|
|
1861
|
+
this.heartbeatTimer = void 0;
|
|
1862
|
+
}
|
|
1863
|
+
async sendHeartbeat() {
|
|
1864
|
+
if (this.closed || this.heartbeatInFlight || this.pending.size > 0 || this.heartbeatIntervalMs == null) {
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
if (Date.now() - this.lastActivityMs < this.heartbeatIntervalMs) {
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
this.heartbeatInFlight = true;
|
|
1871
|
+
try {
|
|
1872
|
+
await this.request(buildProtocolCommand(["PING"]));
|
|
1873
|
+
} catch {
|
|
1874
|
+
} finally {
|
|
1875
|
+
this.heartbeatInFlight = false;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1565
1878
|
onData(chunk) {
|
|
1566
1879
|
try {
|
|
1567
1880
|
this.buffer = this.buffer.byteLength === 0 ? chunk : import_node_buffer2.Buffer.concat([this.buffer, chunk]);
|
|
@@ -1620,6 +1933,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1620
1933
|
return;
|
|
1621
1934
|
}
|
|
1622
1935
|
this.closed = true;
|
|
1936
|
+
this.stopHeartbeat();
|
|
1623
1937
|
const error = reason instanceof Error ? reason : classifyServerError(String(reason), reason);
|
|
1624
1938
|
for (const pending of this.pending.values()) {
|
|
1625
1939
|
pending.reject(error);
|
|
@@ -1652,6 +1966,88 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1652
1966
|
return { ...command, laneId: this.dataLane };
|
|
1653
1967
|
}
|
|
1654
1968
|
};
|
|
1969
|
+
var ReconnectingExecutor = class {
|
|
1970
|
+
constructor(createExecutor, options = {}) {
|
|
1971
|
+
this.createExecutor = createExecutor;
|
|
1972
|
+
this.maxRetries = Math.max(0, Math.trunc(options.maxRetries ?? 1));
|
|
1973
|
+
this.executorPromise = createExecutor();
|
|
1974
|
+
}
|
|
1975
|
+
createExecutor;
|
|
1976
|
+
closed = false;
|
|
1977
|
+
maxRetries;
|
|
1978
|
+
executorPromise;
|
|
1979
|
+
reconnectPromise;
|
|
1980
|
+
async executeCommand(...args) {
|
|
1981
|
+
return await this.withReconnect((executor) => executor.executeCommand(...args));
|
|
1982
|
+
}
|
|
1983
|
+
async executePipeline(commands, options = {}) {
|
|
1984
|
+
return await this.withReconnect(async (executor) => {
|
|
1985
|
+
if (executor.executePipeline != null) {
|
|
1986
|
+
return await executor.executePipeline(commands, options);
|
|
1987
|
+
}
|
|
1988
|
+
return await Promise.all(commands.map((command) => executor.executeCommand(...command)));
|
|
1989
|
+
});
|
|
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
|
+
}
|
|
2005
|
+
async close() {
|
|
2006
|
+
if (this.closed) {
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
this.closed = true;
|
|
2010
|
+
const executor = await this.executorPromise.catch(() => void 0);
|
|
2011
|
+
await Promise.resolve(executor?.close?.());
|
|
2012
|
+
}
|
|
2013
|
+
async withReconnect(operation) {
|
|
2014
|
+
let executor = await this.executorPromise;
|
|
2015
|
+
for (let attempt = 0; ; attempt++) {
|
|
2016
|
+
try {
|
|
2017
|
+
return await operation(executor);
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
if (this.closed || attempt >= this.maxRetries || !isReconnectableClosedConnectionError(error)) {
|
|
2020
|
+
throw error;
|
|
2021
|
+
}
|
|
2022
|
+
executor = await this.reconnect(executor);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
async reconnect(staleExecutor) {
|
|
2027
|
+
if (this.closed) {
|
|
2028
|
+
throw new FerricStoreError("FerricStore client is closed");
|
|
2029
|
+
}
|
|
2030
|
+
if (this.reconnectPromise != null) {
|
|
2031
|
+
return await this.reconnectPromise;
|
|
2032
|
+
}
|
|
2033
|
+
const currentExecutor = await this.executorPromise.catch(() => void 0);
|
|
2034
|
+
if (currentExecutor != null && currentExecutor !== staleExecutor) {
|
|
2035
|
+
return currentExecutor;
|
|
2036
|
+
}
|
|
2037
|
+
this.reconnectPromise = (async () => {
|
|
2038
|
+
await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
|
|
2039
|
+
const nextExecutor = await this.createExecutor();
|
|
2040
|
+
this.executorPromise = Promise.resolve(nextExecutor);
|
|
2041
|
+
return nextExecutor;
|
|
2042
|
+
})().finally(() => {
|
|
2043
|
+
this.reconnectPromise = void 0;
|
|
2044
|
+
});
|
|
2045
|
+
return await this.reconnectPromise;
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
function isReconnectableClosedConnectionError(error) {
|
|
2049
|
+
return error instanceof Error && error.message === "FerricStore connection is closed";
|
|
2050
|
+
}
|
|
1655
2051
|
function parseFerricUrl(value) {
|
|
1656
2052
|
const url = new URL(value);
|
|
1657
2053
|
if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
|
|
@@ -1675,6 +2071,7 @@ async function connect(parsed, options) {
|
|
|
1675
2071
|
}, timeoutMs);
|
|
1676
2072
|
timer.unref?.();
|
|
1677
2073
|
socket.setNoDelay(true);
|
|
2074
|
+
socket.setKeepAlive(options.keepAlive ?? true, normalizeKeepAliveInitialDelay(options.keepAliveInitialDelayMs));
|
|
1678
2075
|
socket.once("connect", () => {
|
|
1679
2076
|
clearTimeout(timer);
|
|
1680
2077
|
resolve(socket);
|
|
@@ -1685,6 +2082,662 @@ async function connect(parsed, options) {
|
|
|
1685
2082
|
});
|
|
1686
2083
|
});
|
|
1687
2084
|
}
|
|
2085
|
+
function normalizeHeartbeatInterval(value) {
|
|
2086
|
+
if (value != null && value <= 0) {
|
|
2087
|
+
return void 0;
|
|
2088
|
+
}
|
|
2089
|
+
return Math.trunc(value ?? 6e4);
|
|
2090
|
+
}
|
|
2091
|
+
function normalizeKeepAliveInitialDelay(value) {
|
|
2092
|
+
return Math.max(0, Math.trunc(value ?? 3e4));
|
|
2093
|
+
}
|
|
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
|
+
]);
|
|
1688
2741
|
|
|
1689
2742
|
// src/codecs.ts
|
|
1690
2743
|
var RawCodec = class {
|
|
@@ -1899,7 +2952,7 @@ function parseKvResponse(value) {
|
|
|
1899
2952
|
return { value };
|
|
1900
2953
|
}
|
|
1901
2954
|
function autoPartitionKeyForId(id) {
|
|
1902
|
-
return `${AUTO_PARTITION_PREFIX}${
|
|
2955
|
+
return `${AUTO_PARTITION_PREFIX}${crc322(Buffer.from(id)) % AUTO_PARTITION_BUCKETS}`;
|
|
1903
2956
|
}
|
|
1904
2957
|
function expandManyResponse(value, count) {
|
|
1905
2958
|
if (Array.isArray(value) && value.length === count) {
|
|
@@ -1969,7 +3022,7 @@ function coerceDiagnosticValue(value) {
|
|
|
1969
3022
|
}
|
|
1970
3023
|
return value;
|
|
1971
3024
|
}
|
|
1972
|
-
function
|
|
3025
|
+
function crc322(buffer) {
|
|
1973
3026
|
let crc = 4294967295;
|
|
1974
3027
|
for (const byte of buffer) {
|
|
1975
3028
|
crc ^= byte;
|
|
@@ -2895,14 +3948,14 @@ function claimedItemFromResp(value, codec) {
|
|
|
2895
3948
|
const partition = tuple[1] == null ? null : bytes(tuple[1]);
|
|
2896
3949
|
const leaseToken = bytes(tuple[2]);
|
|
2897
3950
|
const fencingToken = integer(tuple[3]);
|
|
2898
|
-
const attrs = tuple[5] ?? (
|
|
3951
|
+
const attrs = tuple[5] ?? (isPlainObject3(tuple[4]) ? tuple[4] : void 0);
|
|
2899
3952
|
const item = {
|
|
2900
3953
|
id: text(id),
|
|
2901
3954
|
partitionKey: optionalString(partition),
|
|
2902
3955
|
leaseToken,
|
|
2903
3956
|
fencingToken,
|
|
2904
3957
|
runState: optionalString(tuple[4]),
|
|
2905
|
-
attributes:
|
|
3958
|
+
attributes: isPlainObject3(attrs) ? attrs : void 0,
|
|
2906
3959
|
type: "",
|
|
2907
3960
|
state: "running"
|
|
2908
3961
|
};
|
|
@@ -3022,9 +4075,9 @@ function decodeValues(value, codec) {
|
|
|
3022
4075
|
}
|
|
3023
4076
|
function plainObjectField(value, key) {
|
|
3024
4077
|
const item = field(value, key);
|
|
3025
|
-
return
|
|
4078
|
+
return isPlainObject3(item) ? item : void 0;
|
|
3026
4079
|
}
|
|
3027
|
-
function
|
|
4080
|
+
function isPlainObject3(value) {
|
|
3028
4081
|
return typeof value === "object" && value != null && !Buffer.isBuffer(value) && !(value instanceof Uint8Array) && !Array.isArray(value);
|
|
3029
4082
|
}
|
|
3030
4083
|
|
|
@@ -3075,7 +4128,21 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3075
4128
|
this.zset = new SortedSetStore(this);
|
|
3076
4129
|
}
|
|
3077
4130
|
static async fromUrl(url, options = {}) {
|
|
3078
|
-
|
|
4131
|
+
const reconnectOptions = options.reconnect ?? options.nativeOptions?.autoReconnect ?? true;
|
|
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);
|
|
4144
|
+
const executor = reconnectOptions === false ? await createExecutor() : new ReconnectingExecutor(createExecutor, reconnectOptions === true ? {} : reconnectOptions);
|
|
4145
|
+
return new _FerricStoreClient(executor, options);
|
|
3079
4146
|
}
|
|
3080
4147
|
async command(...args) {
|
|
3081
4148
|
return await this.executor.executeCommand(...args);
|
|
@@ -3089,6 +4156,18 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3089
4156
|
async close() {
|
|
3090
4157
|
await this.executor.close?.();
|
|
3091
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
|
+
}
|
|
3092
4171
|
async ping(message) {
|
|
3093
4172
|
return await this.command("PING", ...message == null ? [] : [message]);
|
|
3094
4173
|
}
|
|
@@ -3151,7 +4230,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3151
4230
|
return response == null ? null : text(response);
|
|
3152
4231
|
}
|
|
3153
4232
|
async clientInfo() {
|
|
3154
|
-
return
|
|
4233
|
+
return infoText(await this.command("CLIENT", "INFO"));
|
|
3155
4234
|
}
|
|
3156
4235
|
async clientList(options = {}) {
|
|
3157
4236
|
return text(await this.command("CLIENT", "LIST", ...options.type == null ? [] : ["TYPE", options.type]));
|
|
@@ -3206,7 +4285,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3206
4285
|
return Number(await this.command("PUBSUB", "NUMPAT"));
|
|
3207
4286
|
}
|
|
3208
4287
|
async aclSetUser(username, rules) {
|
|
3209
|
-
return okResponse(await this.command("ACL", "SETUSER", username, ...rules));
|
|
4288
|
+
return okResponse(await this.command("ACL", "SETUSER", username, ...ruleArgs(rules)));
|
|
3210
4289
|
}
|
|
3211
4290
|
async aclDelUser(...usernames) {
|
|
3212
4291
|
return Number(await this.command("ACL", "DELUSER", ...usernames));
|
|
@@ -3217,6 +4296,9 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3217
4296
|
async aclList() {
|
|
3218
4297
|
return arrayResponse(await this.command("ACL", "LIST"));
|
|
3219
4298
|
}
|
|
4299
|
+
async aclListUsers() {
|
|
4300
|
+
return await this.aclList();
|
|
4301
|
+
}
|
|
3220
4302
|
async aclWhoami() {
|
|
3221
4303
|
return text(await this.command("ACL", "WHOAMI"));
|
|
3222
4304
|
}
|
|
@@ -3312,6 +4394,54 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3312
4394
|
async ferricstoreDoctor(...args) {
|
|
3313
4395
|
return await this.command("FERRICSTORE.DOCTOR", ...args);
|
|
3314
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
|
+
}
|
|
3315
4445
|
async create(id, options) {
|
|
3316
4446
|
const currentNowMs = options.nowMs ?? nowMs();
|
|
3317
4447
|
const args = [
|
|
@@ -3792,6 +4922,14 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3792
4922
|
async list(type, options = {}) {
|
|
3793
4923
|
return await this.indexQuery("FLOW.LIST", type, options);
|
|
3794
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
|
+
}
|
|
3795
4933
|
async terminals(type, options = {}) {
|
|
3796
4934
|
return await this.indexQuery("FLOW.TERMINALS", type, options);
|
|
3797
4935
|
}
|
|
@@ -3961,6 +5099,18 @@ var ErrorMappingExecutor = class {
|
|
|
3961
5099
|
throwMapped(error);
|
|
3962
5100
|
}
|
|
3963
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
|
+
}
|
|
3964
5114
|
async close() {
|
|
3965
5115
|
await this.executor.close?.();
|
|
3966
5116
|
}
|
|
@@ -3996,6 +5146,18 @@ var AutoBatchExecutor = class {
|
|
|
3996
5146
|
}
|
|
3997
5147
|
return await Promise.all(commands.map((command) => this.executor.executeCommand(...command)));
|
|
3998
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
|
+
}
|
|
3999
5161
|
async close() {
|
|
4000
5162
|
this.scheduled = false;
|
|
4001
5163
|
if (this.timer != null) {
|
|
@@ -4156,7 +5318,7 @@ var neverAutoBatchCommandPrefixes = /* @__PURE__ */ new Set([
|
|
|
4156
5318
|
]);
|
|
4157
5319
|
var neverAutoBatchCommands = /* @__PURE__ */ new Set(["FLOW.CLAIM_DUE", "FLOW.RECLAIM"]);
|
|
4158
5320
|
function autoBatchCommandAllowed(command, mode) {
|
|
4159
|
-
const name =
|
|
5321
|
+
const name = commandName2(command);
|
|
4160
5322
|
if (name == null || neverAutoBatchCommands.has(name)) {
|
|
4161
5323
|
return false;
|
|
4162
5324
|
}
|
|
@@ -4166,18 +5328,18 @@ function autoBatchCommandAllowed(command, mode) {
|
|
|
4166
5328
|
}
|
|
4167
5329
|
return mode === "all" || safeAutoBatchCommands.has(name);
|
|
4168
5330
|
}
|
|
4169
|
-
function
|
|
4170
|
-
const first =
|
|
5331
|
+
function commandName2(command) {
|
|
5332
|
+
const first = commandPart2(command[0]);
|
|
4171
5333
|
if (first == null) {
|
|
4172
5334
|
return null;
|
|
4173
5335
|
}
|
|
4174
5336
|
if (first === "FLOW" && command.length > 1) {
|
|
4175
|
-
const second =
|
|
5337
|
+
const second = commandPart2(command[1]);
|
|
4176
5338
|
return second == null ? first : `${first}.${second}`;
|
|
4177
5339
|
}
|
|
4178
5340
|
return first;
|
|
4179
5341
|
}
|
|
4180
|
-
function
|
|
5342
|
+
function commandPart2(value) {
|
|
4181
5343
|
if (typeof value === "string") {
|
|
4182
5344
|
return value.toUpperCase();
|
|
4183
5345
|
}
|
|
@@ -4202,6 +5364,67 @@ function appendStateMeta(args, stateMeta) {
|
|
|
4202
5364
|
args.push("STATE_META", name, value);
|
|
4203
5365
|
}
|
|
4204
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
|
+
}
|
|
4205
5428
|
function sharedCreateManyStateMeta(items, stateMeta) {
|
|
4206
5429
|
const itemMetas = items.map((item) => item.stateMeta).filter((meta) => meta != null);
|
|
4207
5430
|
if (itemMetas.length === 0) {
|
|
@@ -5175,6 +6398,7 @@ function valueRefToString(value) {
|
|
|
5175
6398
|
0 && (module.exports = {
|
|
5176
6399
|
BitmapStore,
|
|
5177
6400
|
BloomFilterStore,
|
|
6401
|
+
COMMAND_OPCODES,
|
|
5178
6402
|
CountMinSketchStore,
|
|
5179
6403
|
CuckooFilterStore,
|
|
5180
6404
|
FerricStoreClient,
|
|
@@ -5198,12 +6422,15 @@ function valueRefToString(value) {
|
|
|
5198
6422
|
QueueClient,
|
|
5199
6423
|
QueueWorker,
|
|
5200
6424
|
RawCodec,
|
|
6425
|
+
ReconnectingExecutor,
|
|
6426
|
+
RoutingTopology,
|
|
5201
6427
|
SetStore,
|
|
5202
6428
|
SortedSetStore,
|
|
5203
6429
|
StaleLeaseError,
|
|
5204
6430
|
StreamStore,
|
|
5205
6431
|
TDigestStore,
|
|
5206
6432
|
TopKStore,
|
|
6433
|
+
TopologyNativeAdapterPool,
|
|
5207
6434
|
Workflow,
|
|
5208
6435
|
WorkflowClient,
|
|
5209
6436
|
WorkflowContext,
|
|
@@ -5212,6 +6439,7 @@ function valueRefToString(value) {
|
|
|
5212
6439
|
classifyServerError,
|
|
5213
6440
|
complete,
|
|
5214
6441
|
fail,
|
|
6442
|
+
isReconnectableClosedConnectionError,
|
|
5215
6443
|
mapException,
|
|
5216
6444
|
retry,
|
|
5217
6445
|
transition
|