@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.js
CHANGED
|
@@ -110,24 +110,171 @@ var FLAG_CUSTOM_PAYLOAD = 2;
|
|
|
110
110
|
var FLAG_COMPRESSED = 8;
|
|
111
111
|
var FLAG_MORE_CHUNKS = 32;
|
|
112
112
|
var STATUS_OK = 0;
|
|
113
|
+
var COMMAND_OPCODES = {
|
|
114
|
+
"HELLO": 1,
|
|
115
|
+
"AUTH": 2,
|
|
116
|
+
"PING": 3,
|
|
117
|
+
"CLIENT.SETNAME": 4,
|
|
118
|
+
"CLIENT.INFO": 5,
|
|
119
|
+
"ROUTE": 6,
|
|
120
|
+
"SHARDS": 7,
|
|
121
|
+
"BACKPRESSURE": 8,
|
|
122
|
+
"QUIT": 9,
|
|
123
|
+
"GOAWAY": 10,
|
|
124
|
+
"OPTIONS": 11,
|
|
125
|
+
"STARTUP": 12,
|
|
126
|
+
"WINDOW_UPDATE": 13,
|
|
127
|
+
"PIPELINE": 14,
|
|
128
|
+
"ROUTE_BATCH": 15,
|
|
129
|
+
"EVENT": 16,
|
|
130
|
+
"SUBSCRIBE_EVENTS": 17,
|
|
131
|
+
"UNSUBSCRIBE_EVENTS": 18,
|
|
132
|
+
"COMMAND_EXEC": 256,
|
|
133
|
+
"GET": 257,
|
|
134
|
+
"SET": 258,
|
|
135
|
+
"DEL": 259,
|
|
136
|
+
"MGET": 260,
|
|
137
|
+
"MSET": 261,
|
|
138
|
+
"CAS": 262,
|
|
139
|
+
"LOCK": 263,
|
|
140
|
+
"UNLOCK": 264,
|
|
141
|
+
"EXTEND": 265,
|
|
142
|
+
"RATELIMIT.ADD": 266,
|
|
143
|
+
"FETCH_OR_COMPUTE": 267,
|
|
144
|
+
"FETCH_OR_COMPUTE_RESULT": 268,
|
|
145
|
+
"FETCH_OR_COMPUTE_ERROR": 269,
|
|
146
|
+
"HSET": 272,
|
|
147
|
+
"HGET": 273,
|
|
148
|
+
"HMGET": 274,
|
|
149
|
+
"HGETALL": 275,
|
|
150
|
+
"LPUSH": 288,
|
|
151
|
+
"RPUSH": 289,
|
|
152
|
+
"LPOP": 290,
|
|
153
|
+
"RPOP": 291,
|
|
154
|
+
"LRANGE": 292,
|
|
155
|
+
"SADD": 304,
|
|
156
|
+
"SREM": 305,
|
|
157
|
+
"SMEMBERS": 306,
|
|
158
|
+
"SISMEMBER": 307,
|
|
159
|
+
"ZADD": 320,
|
|
160
|
+
"ZREM": 321,
|
|
161
|
+
"ZRANGE": 322,
|
|
162
|
+
"ZSCORE": 323,
|
|
163
|
+
"FLOW.CREATE": 513,
|
|
164
|
+
"FLOW.GET": 514,
|
|
165
|
+
"FLOW.CLAIM_DUE": 515,
|
|
166
|
+
"FLOW.COMPLETE": 516,
|
|
167
|
+
"FLOW.TRANSITION": 517,
|
|
168
|
+
"FLOW.RETRY": 518,
|
|
169
|
+
"FLOW.FAIL": 519,
|
|
170
|
+
"FLOW.CANCEL": 520,
|
|
171
|
+
"FLOW.EXTEND_LEASE": 521,
|
|
172
|
+
"FLOW.HISTORY": 522,
|
|
173
|
+
"FLOW.VALUE.PUT": 523,
|
|
174
|
+
"FLOW.VALUE.MGET": 524,
|
|
175
|
+
"FLOW.SIGNAL": 525,
|
|
176
|
+
"FLOW.LIST": 526,
|
|
177
|
+
"FLOW.CREATE_MANY": 527,
|
|
178
|
+
"FLOW.COMPLETE_MANY": 528,
|
|
179
|
+
"FLOW.TRANSITION_MANY": 529,
|
|
180
|
+
"FLOW.RETRY_MANY": 530,
|
|
181
|
+
"FLOW.FAIL_MANY": 531,
|
|
182
|
+
"FLOW.CANCEL_MANY": 532,
|
|
183
|
+
"FLOW.RECLAIM": 533,
|
|
184
|
+
"FLOW.REWIND": 534,
|
|
185
|
+
"FLOW.TERMINALS": 535,
|
|
186
|
+
"FLOW.FAILURES": 536,
|
|
187
|
+
"FLOW.BY_PARENT": 537,
|
|
188
|
+
"FLOW.BY_ROOT": 538,
|
|
189
|
+
"FLOW.BY_CORRELATION": 539,
|
|
190
|
+
"FLOW.INFO": 540,
|
|
191
|
+
"FLOW.STUCK": 541,
|
|
192
|
+
"FLOW.POLICY.SET": 542,
|
|
193
|
+
"FLOW.POLICY.GET": 543,
|
|
194
|
+
"FLOW.SPAWN_CHILDREN": 544,
|
|
195
|
+
"FLOW.RETENTION_CLEANUP": 545,
|
|
196
|
+
"FLOW.STEP_CONTINUE": 546,
|
|
197
|
+
"FLOW.START_AND_CLAIM": 547,
|
|
198
|
+
"FLOW.RUN_STEPS_MANY": 548,
|
|
199
|
+
"FLOW.SCHEDULE.CREATE": 549,
|
|
200
|
+
"FLOW.SCHEDULE.GET": 550,
|
|
201
|
+
"FLOW.SCHEDULE.DELETE": 551,
|
|
202
|
+
"FLOW.SCHEDULE.FIRE_DUE": 552,
|
|
203
|
+
"FLOW.SCHEDULE.LIST": 553,
|
|
204
|
+
"FLOW.SCHEDULE.FIRE": 554,
|
|
205
|
+
"FLOW.SCHEDULE.PAUSE": 555,
|
|
206
|
+
"FLOW.SCHEDULE.RESUME": 556,
|
|
207
|
+
"FLOW.STATS": 557,
|
|
208
|
+
"FLOW.ATTRIBUTES": 558,
|
|
209
|
+
"FLOW.ATTRIBUTE_VALUES": 559,
|
|
210
|
+
"FLOW.SEARCH": 560,
|
|
211
|
+
"FLOW.EFFECT.RESERVE": 576,
|
|
212
|
+
"FLOW.EFFECT.CONFIRM": 577,
|
|
213
|
+
"FLOW.EFFECT.FAIL": 578,
|
|
214
|
+
"FLOW.EFFECT.COMPENSATE": 579,
|
|
215
|
+
"FLOW.EFFECT.GET": 580,
|
|
216
|
+
"FLOW.GOVERNANCE.LEDGER": 581,
|
|
217
|
+
"FLOW.APPROVAL.REQUEST": 582,
|
|
218
|
+
"FLOW.APPROVAL.APPROVE": 583,
|
|
219
|
+
"FLOW.APPROVAL.REJECT": 584,
|
|
220
|
+
"FLOW.APPROVAL.GET": 585,
|
|
221
|
+
"FLOW.CIRCUIT.OPEN": 586,
|
|
222
|
+
"FLOW.CIRCUIT.CLOSE": 587,
|
|
223
|
+
"FLOW.CIRCUIT.GET": 588,
|
|
224
|
+
"FLOW.BUDGET.RESERVE": 589,
|
|
225
|
+
"FLOW.BUDGET.GET": 590,
|
|
226
|
+
"FLOW.LIMIT.LEASE": 591,
|
|
227
|
+
"FLOW.LIMIT.SPEND": 592,
|
|
228
|
+
"FLOW.LIMIT.RELEASE": 593,
|
|
229
|
+
"FLOW.LIMIT.GET": 594,
|
|
230
|
+
"FLOW.APPROVAL.LIST": 595,
|
|
231
|
+
"FLOW.GOVERNANCE.OVERVIEW": 596,
|
|
232
|
+
"FLOW.BUDGET.LIST": 597,
|
|
233
|
+
"FLOW.LIMIT.LIST": 598,
|
|
234
|
+
"FLOW.BUDGET.COMMIT": 599,
|
|
235
|
+
"FLOW.BUDGET.RELEASE": 600,
|
|
236
|
+
"CLUSTER.HEALTH": 769,
|
|
237
|
+
"CLUSTER.STATS": 770,
|
|
238
|
+
"CLUSTER.KEYSLOT": 771,
|
|
239
|
+
"CLUSTER.SLOTS": 772,
|
|
240
|
+
"CLUSTER.STATUS": 773,
|
|
241
|
+
"CLUSTER.JOIN": 774,
|
|
242
|
+
"CLUSTER.LEAVE": 775,
|
|
243
|
+
"CLUSTER.FAILOVER": 776,
|
|
244
|
+
"CLUSTER.PROMOTE": 777,
|
|
245
|
+
"CLUSTER.DEMOTE": 778,
|
|
246
|
+
"CLUSTER.ROLE": 779,
|
|
247
|
+
"FERRICSTORE.KEY_INFO": 780,
|
|
248
|
+
"FERRICSTORE.CONFIG": 781,
|
|
249
|
+
"FERRICSTORE.HOTNESS": 782,
|
|
250
|
+
"FERRICSTORE.METRICS": 783,
|
|
251
|
+
"FERRICSTORE.BLOBGC": 784
|
|
252
|
+
};
|
|
113
253
|
var OPCODES = {
|
|
114
|
-
startup:
|
|
115
|
-
auth:
|
|
116
|
-
ping:
|
|
117
|
-
clientSetName:
|
|
118
|
-
clientInfo:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
254
|
+
startup: COMMAND_OPCODES.STARTUP,
|
|
255
|
+
auth: COMMAND_OPCODES.AUTH,
|
|
256
|
+
ping: COMMAND_OPCODES.PING,
|
|
257
|
+
clientSetName: COMMAND_OPCODES["CLIENT.SETNAME"],
|
|
258
|
+
clientInfo: COMMAND_OPCODES["CLIENT.INFO"],
|
|
259
|
+
route: COMMAND_OPCODES.ROUTE,
|
|
260
|
+
routeBatch: COMMAND_OPCODES.ROUTE_BATCH,
|
|
261
|
+
shards: COMMAND_OPCODES.SHARDS,
|
|
262
|
+
backpressure: COMMAND_OPCODES.BACKPRESSURE,
|
|
263
|
+
options: COMMAND_OPCODES.OPTIONS,
|
|
264
|
+
quit: COMMAND_OPCODES.QUIT,
|
|
265
|
+
commandExec: COMMAND_OPCODES.COMMAND_EXEC,
|
|
266
|
+
get: COMMAND_OPCODES.GET,
|
|
267
|
+
set: COMMAND_OPCODES.SET,
|
|
268
|
+
del: COMMAND_OPCODES.DEL,
|
|
269
|
+
mget: COMMAND_OPCODES.MGET,
|
|
270
|
+
mset: COMMAND_OPCODES.MSET,
|
|
271
|
+
pipeline: COMMAND_OPCODES.PIPELINE,
|
|
272
|
+
flowCreate: COMMAND_OPCODES["FLOW.CREATE"],
|
|
273
|
+
flowClaimDue: COMMAND_OPCODES["FLOW.CLAIM_DUE"],
|
|
274
|
+
flowComplete: COMMAND_OPCODES["FLOW.COMPLETE"],
|
|
275
|
+
flowCreateMany: COMMAND_OPCODES["FLOW.CREATE_MANY"],
|
|
276
|
+
flowCompleteMany: COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
|
|
277
|
+
flowSearch: COMMAND_OPCODES["FLOW.SEARCH"]
|
|
131
278
|
};
|
|
132
279
|
var COMPACT_FLOW_CLAIM_JOBS = 128;
|
|
133
280
|
var COMPACT_OK_LIST = 129;
|
|
@@ -260,12 +407,46 @@ function buildProtocolCommand(args) {
|
|
|
260
407
|
}
|
|
261
408
|
const command = asText(args[0]).toUpperCase();
|
|
262
409
|
const commandArgs = args.slice(1);
|
|
410
|
+
if (command === "COMMAND_EXEC" && commandArgs.length >= 1) {
|
|
411
|
+
return commandExec(commandArgs);
|
|
412
|
+
}
|
|
413
|
+
if (command === "HELLO" || command === "STARTUP" || command === "WINDOW_UPDATE") {
|
|
414
|
+
return { laneId: 0, opcode: COMMAND_OPCODES[command], payload: optionMap(commandArgs) };
|
|
415
|
+
}
|
|
416
|
+
if (command === "AUTH") {
|
|
417
|
+
const payload = commandArgs.length === 1 ? { password: commandArgs[0], username: "default" } : { password: commandArgs[1], username: commandArgs[0] };
|
|
418
|
+
return { laneId: 0, opcode: OPCODES.auth, payload };
|
|
419
|
+
}
|
|
263
420
|
if (command === "PING") {
|
|
264
421
|
return { laneId: 0, opcode: OPCODES.ping, payload: commandArgs.length === 0 ? {} : { message: commandArgs[0] } };
|
|
265
422
|
}
|
|
423
|
+
if (command === "OPTIONS") {
|
|
424
|
+
return { laneId: 0, opcode: OPCODES.options, payload: {} };
|
|
425
|
+
}
|
|
426
|
+
if (command === "BACKPRESSURE") {
|
|
427
|
+
return { laneId: 0, opcode: OPCODES.backpressure, payload: {} };
|
|
428
|
+
}
|
|
429
|
+
if (command === "QUIT") {
|
|
430
|
+
return { laneId: 0, opcode: OPCODES.quit, payload: {} };
|
|
431
|
+
}
|
|
432
|
+
if (command === "ROUTE" && commandArgs.length >= 1) {
|
|
433
|
+
return { laneId: 0, opcode: OPCODES.route, payload: { key: commandArgs[0] } };
|
|
434
|
+
}
|
|
435
|
+
if (command === "ROUTE_BATCH") {
|
|
436
|
+
return { laneId: 0, opcode: OPCODES.routeBatch, payload: { keys: commandArgs } };
|
|
437
|
+
}
|
|
438
|
+
if (command === "SHARDS") {
|
|
439
|
+
return { laneId: 0, opcode: OPCODES.shards, payload: {} };
|
|
440
|
+
}
|
|
266
441
|
if (command === "CLIENT" && asText(commandArgs[0]).toUpperCase() === "SETNAME" && commandArgs.length === 2) {
|
|
267
442
|
return { laneId: 0, opcode: OPCODES.clientSetName, payload: { name: commandArgs[1] } };
|
|
268
443
|
}
|
|
444
|
+
if (command === "CLIENT.SETNAME" && commandArgs.length === 1) {
|
|
445
|
+
return { laneId: 0, opcode: OPCODES.clientSetName, payload: { name: commandArgs[0] } };
|
|
446
|
+
}
|
|
447
|
+
if (command === "CLIENT" && asText(commandArgs[0]).toUpperCase() === "INFO" && commandArgs.length === 1 || command === "CLIENT.INFO" && commandArgs.length === 0) {
|
|
448
|
+
return { laneId: 0, opcode: OPCODES.clientInfo, payload: {} };
|
|
449
|
+
}
|
|
269
450
|
if (command === "GET" && commandArgs.length >= 1) {
|
|
270
451
|
return { opcode: OPCODES.get, payload: { key: commandArgs[0] } };
|
|
271
452
|
}
|
|
@@ -284,7 +465,7 @@ function buildProtocolCommand(args) {
|
|
|
284
465
|
if (command === "DEL") {
|
|
285
466
|
return { opcode: OPCODES.del, payload: { keys: commandArgs } };
|
|
286
467
|
}
|
|
287
|
-
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(commandArgs)) {
|
|
468
|
+
if (command.startsWith("FLOW.") && hasFlowCommandOnlyOption(command, commandArgs)) {
|
|
288
469
|
return commandExec(args);
|
|
289
470
|
}
|
|
290
471
|
if (command === "FLOW.CREATE") {
|
|
@@ -302,6 +483,9 @@ function buildProtocolCommand(args) {
|
|
|
302
483
|
if (command === "FLOW.COMPLETE_MANY") {
|
|
303
484
|
return flowCompleteManyPayload(commandArgs) ?? commandExec(args);
|
|
304
485
|
}
|
|
486
|
+
if (command === "FLOW.SEARCH") {
|
|
487
|
+
return flowSearchPayload(commandArgs) ?? commandExec(args);
|
|
488
|
+
}
|
|
305
489
|
return commandExec(args);
|
|
306
490
|
}
|
|
307
491
|
function commandExec(args) {
|
|
@@ -646,13 +830,13 @@ function writeOptionalBinary(out, offset, value) {
|
|
|
646
830
|
}
|
|
647
831
|
return writeBinary(out, offset, value);
|
|
648
832
|
}
|
|
649
|
-
function hasFlowCommandOnlyOption(args) {
|
|
833
|
+
function hasFlowCommandOnlyOption(command, args) {
|
|
650
834
|
return args.some((arg) => {
|
|
651
835
|
if (typeof arg !== "string" && !Buffer2.isBuffer(arg)) {
|
|
652
836
|
return false;
|
|
653
837
|
}
|
|
654
838
|
const token = asText(arg).toUpperCase();
|
|
655
|
-
return token === "
|
|
839
|
+
return token === "INDEXED_STATE_META" || command !== "FLOW.SEARCH" && token === "STATE_META";
|
|
656
840
|
});
|
|
657
841
|
}
|
|
658
842
|
function flowCreatePayload(args) {
|
|
@@ -770,6 +954,12 @@ function flowCompleteManyPayload(args) {
|
|
|
770
954
|
if (!auto && !mixed) payload.partition_key = partition;
|
|
771
955
|
return { opcode: OPCODES.flowCompleteMany, payload };
|
|
772
956
|
}
|
|
957
|
+
function flowSearchPayload(args) {
|
|
958
|
+
if (args.length < 1) return void 0;
|
|
959
|
+
const payload = optionMap(args.slice(1));
|
|
960
|
+
normalizeFlowSearchStateMeta(payload);
|
|
961
|
+
return { opcode: OPCODES.flowSearch, payload: { type: args[0], ...payload } };
|
|
962
|
+
}
|
|
773
963
|
function compactFlowCreateManyPayload(partition, rawItems, mixed, auto, options) {
|
|
774
964
|
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") {
|
|
775
965
|
return void 0;
|
|
@@ -872,6 +1062,78 @@ function compactFlowCompleteManyPayload(partition, rawItems, mixed, auto, option
|
|
|
872
1062
|
}
|
|
873
1063
|
return { flags: FLAG_CUSTOM_PAYLOAD, opcode: OPCODES.flowCompleteMany, payload: out };
|
|
874
1064
|
}
|
|
1065
|
+
var OPTION_FIELDS = {
|
|
1066
|
+
CONSISTENT_PROJECTION: "consistent_projection",
|
|
1067
|
+
COUNT: "count",
|
|
1068
|
+
FROM_MS: "from_ms",
|
|
1069
|
+
INCLUDE_COLD: "include_cold",
|
|
1070
|
+
PARTITION: "partition_key",
|
|
1071
|
+
REV: "rev",
|
|
1072
|
+
STATE: "state",
|
|
1073
|
+
TERMINAL_ONLY: "terminal_only",
|
|
1074
|
+
TO_MS: "to_ms"
|
|
1075
|
+
};
|
|
1076
|
+
var BOOL_OPTION_FIELDS = /* @__PURE__ */ new Set(["consistent_projection", "include_cold", "rev", "terminal_only"]);
|
|
1077
|
+
function optionMap(args) {
|
|
1078
|
+
const payload = {};
|
|
1079
|
+
for (let index = 0; index < args.length; ) {
|
|
1080
|
+
const token = asText(args[index]).toUpperCase();
|
|
1081
|
+
if (token === "ATTRIBUTE") {
|
|
1082
|
+
if (index + 2 >= args.length) {
|
|
1083
|
+
throw new FerricStoreError("ATTRIBUTE requires name and value");
|
|
1084
|
+
}
|
|
1085
|
+
const name = asText(args[index + 1]);
|
|
1086
|
+
const attributes = ensureObjectField(payload, "attributes");
|
|
1087
|
+
attributes[name] = args[index + 2];
|
|
1088
|
+
index += 3;
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
if (token === "STATE_META") {
|
|
1092
|
+
if (index + 2 >= args.length) {
|
|
1093
|
+
throw new FerricStoreError("STATE_META requires name and value");
|
|
1094
|
+
}
|
|
1095
|
+
const name = asText(args[index + 1]);
|
|
1096
|
+
const stateMeta = ensureObjectField(payload, "state_meta");
|
|
1097
|
+
stateMeta[name] = args[index + 2];
|
|
1098
|
+
index += 3;
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
const field2 = OPTION_FIELDS[token] ?? token.toLowerCase();
|
|
1102
|
+
if (index + 1 >= args.length) {
|
|
1103
|
+
throw new FerricStoreError(`${token} requires a value`);
|
|
1104
|
+
}
|
|
1105
|
+
const value = args[index + 1];
|
|
1106
|
+
payload[field2] = BOOL_OPTION_FIELDS.has(field2) ? boolArg(value) : value;
|
|
1107
|
+
index += 2;
|
|
1108
|
+
}
|
|
1109
|
+
return payload;
|
|
1110
|
+
}
|
|
1111
|
+
function ensureObjectField(payload, field2) {
|
|
1112
|
+
const value = payload[field2];
|
|
1113
|
+
if (isPlainObject(value)) {
|
|
1114
|
+
return value;
|
|
1115
|
+
}
|
|
1116
|
+
const next = {};
|
|
1117
|
+
payload[field2] = next;
|
|
1118
|
+
return next;
|
|
1119
|
+
}
|
|
1120
|
+
function normalizeFlowSearchStateMeta(payload) {
|
|
1121
|
+
const stateMeta = payload.state_meta;
|
|
1122
|
+
if (!isPlainObject(stateMeta) || Object.keys(stateMeta).length === 0) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
if (Object.values(stateMeta).every(isPlainObject)) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
const state = payload.state;
|
|
1129
|
+
if (state == null) {
|
|
1130
|
+
throw new FerricStoreError("FLOW.SEARCH STATE_META filters require STATE or nested state metadata");
|
|
1131
|
+
}
|
|
1132
|
+
payload.state_meta = { [asText(state)]: stateMeta };
|
|
1133
|
+
}
|
|
1134
|
+
function isPlainObject(value) {
|
|
1135
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer2.isBuffer(value) && !(value instanceof Uint8Array);
|
|
1136
|
+
}
|
|
875
1137
|
function parseFlowOptions(args, start, end, config) {
|
|
876
1138
|
const payload = {};
|
|
877
1139
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1393,15 +1655,21 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1393
1655
|
maxChunkBytes;
|
|
1394
1656
|
requestId = 0n;
|
|
1395
1657
|
protocolLanes;
|
|
1658
|
+
heartbeatIntervalMs;
|
|
1659
|
+
heartbeatInFlight = false;
|
|
1660
|
+
heartbeatTimer;
|
|
1661
|
+
lastActivityMs = Date.now();
|
|
1396
1662
|
timeoutMs;
|
|
1397
|
-
constructor(socket, timeoutMs, protocolLanes, maxChunkBytes) {
|
|
1663
|
+
constructor(socket, timeoutMs, protocolLanes, maxChunkBytes, heartbeatIntervalMs) {
|
|
1398
1664
|
this.socket = socket;
|
|
1399
1665
|
this.maxChunkBytes = Math.max(1, Math.trunc(maxChunkBytes));
|
|
1400
1666
|
this.timeoutMs = timeoutMs;
|
|
1401
1667
|
this.protocolLanes = Math.max(1, Math.trunc(protocolLanes));
|
|
1668
|
+
this.heartbeatIntervalMs = normalizeHeartbeatInterval(heartbeatIntervalMs);
|
|
1402
1669
|
this.socket.on("data", (chunk) => this.onData(chunk));
|
|
1403
1670
|
this.socket.on("error", (error) => this.failAll(error));
|
|
1404
1671
|
this.socket.on("close", () => this.failAll(new FerricStoreError("FerricStore connection closed")));
|
|
1672
|
+
this.startHeartbeat();
|
|
1405
1673
|
}
|
|
1406
1674
|
static async fromUrl(url, options = {}) {
|
|
1407
1675
|
const parsed = parseFerricUrl(url);
|
|
@@ -1410,7 +1678,8 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1410
1678
|
socket,
|
|
1411
1679
|
options.timeoutMs ?? 3e4,
|
|
1412
1680
|
options.protocolLanes ?? 8,
|
|
1413
|
-
options.maxChunkBytes ?? 64 * 1024 * 1024
|
|
1681
|
+
options.maxChunkBytes ?? 64 * 1024 * 1024,
|
|
1682
|
+
options.heartbeatIntervalMs
|
|
1414
1683
|
);
|
|
1415
1684
|
await adapter.startup(options.clientName);
|
|
1416
1685
|
const password = options.password ?? parsed.password;
|
|
@@ -1423,6 +1692,9 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1423
1692
|
const command = buildProtocolCommand(args);
|
|
1424
1693
|
return await this.request(command);
|
|
1425
1694
|
}
|
|
1695
|
+
async executeProtocolCommand(command, laneId) {
|
|
1696
|
+
return await this.request(laneId == null ? command : { ...command, laneId });
|
|
1697
|
+
}
|
|
1426
1698
|
async executePipeline(commands, options = {}) {
|
|
1427
1699
|
if (commands.length === 0) {
|
|
1428
1700
|
return [];
|
|
@@ -1434,6 +1706,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1434
1706
|
return;
|
|
1435
1707
|
}
|
|
1436
1708
|
this.closed = true;
|
|
1709
|
+
this.stopHeartbeat();
|
|
1437
1710
|
this.socket.end();
|
|
1438
1711
|
}
|
|
1439
1712
|
async startup(clientName) {
|
|
@@ -1455,6 +1728,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1455
1728
|
if (this.closed) {
|
|
1456
1729
|
throw new FerricStoreError("FerricStore connection is closed");
|
|
1457
1730
|
}
|
|
1731
|
+
this.lastActivityMs = Date.now();
|
|
1458
1732
|
const requestId = this.nextRequestId();
|
|
1459
1733
|
const frame = encodeRequest(this.assignLane(command), requestId);
|
|
1460
1734
|
return await new Promise((resolve, reject) => {
|
|
@@ -1468,10 +1742,12 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1468
1742
|
opcode: command.opcode,
|
|
1469
1743
|
reject: (reason) => {
|
|
1470
1744
|
clearTimeout(timer);
|
|
1745
|
+
this.lastActivityMs = Date.now();
|
|
1471
1746
|
reject(reason instanceof Error ? reason : classifyServerError(String(reason), reason));
|
|
1472
1747
|
},
|
|
1473
1748
|
resolve: (value) => {
|
|
1474
1749
|
clearTimeout(timer);
|
|
1750
|
+
this.lastActivityMs = Date.now();
|
|
1475
1751
|
resolve(value);
|
|
1476
1752
|
}
|
|
1477
1753
|
});
|
|
@@ -1480,11 +1756,43 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1480
1756
|
this.pending.delete(requestId);
|
|
1481
1757
|
this.cleanupChunksForRequest(requestId);
|
|
1482
1758
|
clearTimeout(timer);
|
|
1759
|
+
this.lastActivityMs = Date.now();
|
|
1483
1760
|
reject(error);
|
|
1484
1761
|
}
|
|
1485
1762
|
});
|
|
1486
1763
|
});
|
|
1487
1764
|
}
|
|
1765
|
+
startHeartbeat() {
|
|
1766
|
+
if (this.heartbeatIntervalMs == null) {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
this.heartbeatTimer = setInterval(() => {
|
|
1770
|
+
void this.sendHeartbeat();
|
|
1771
|
+
}, this.heartbeatIntervalMs);
|
|
1772
|
+
this.heartbeatTimer.unref?.();
|
|
1773
|
+
}
|
|
1774
|
+
stopHeartbeat() {
|
|
1775
|
+
if (this.heartbeatTimer == null) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
clearInterval(this.heartbeatTimer);
|
|
1779
|
+
this.heartbeatTimer = void 0;
|
|
1780
|
+
}
|
|
1781
|
+
async sendHeartbeat() {
|
|
1782
|
+
if (this.closed || this.heartbeatInFlight || this.pending.size > 0 || this.heartbeatIntervalMs == null) {
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
if (Date.now() - this.lastActivityMs < this.heartbeatIntervalMs) {
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
this.heartbeatInFlight = true;
|
|
1789
|
+
try {
|
|
1790
|
+
await this.request(buildProtocolCommand(["PING"]));
|
|
1791
|
+
} catch {
|
|
1792
|
+
} finally {
|
|
1793
|
+
this.heartbeatInFlight = false;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1488
1796
|
onData(chunk) {
|
|
1489
1797
|
try {
|
|
1490
1798
|
this.buffer = this.buffer.byteLength === 0 ? chunk : Buffer3.concat([this.buffer, chunk]);
|
|
@@ -1543,6 +1851,7 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1543
1851
|
return;
|
|
1544
1852
|
}
|
|
1545
1853
|
this.closed = true;
|
|
1854
|
+
this.stopHeartbeat();
|
|
1546
1855
|
const error = reason instanceof Error ? reason : classifyServerError(String(reason), reason);
|
|
1547
1856
|
for (const pending of this.pending.values()) {
|
|
1548
1857
|
pending.reject(error);
|
|
@@ -1575,6 +1884,88 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
1575
1884
|
return { ...command, laneId: this.dataLane };
|
|
1576
1885
|
}
|
|
1577
1886
|
};
|
|
1887
|
+
var ReconnectingExecutor = class {
|
|
1888
|
+
constructor(createExecutor, options = {}) {
|
|
1889
|
+
this.createExecutor = createExecutor;
|
|
1890
|
+
this.maxRetries = Math.max(0, Math.trunc(options.maxRetries ?? 1));
|
|
1891
|
+
this.executorPromise = createExecutor();
|
|
1892
|
+
}
|
|
1893
|
+
createExecutor;
|
|
1894
|
+
closed = false;
|
|
1895
|
+
maxRetries;
|
|
1896
|
+
executorPromise;
|
|
1897
|
+
reconnectPromise;
|
|
1898
|
+
async executeCommand(...args) {
|
|
1899
|
+
return await this.withReconnect((executor) => executor.executeCommand(...args));
|
|
1900
|
+
}
|
|
1901
|
+
async executePipeline(commands, options = {}) {
|
|
1902
|
+
return await this.withReconnect(async (executor) => {
|
|
1903
|
+
if (executor.executePipeline != null) {
|
|
1904
|
+
return await executor.executePipeline(commands, options);
|
|
1905
|
+
}
|
|
1906
|
+
return await Promise.all(commands.map((command) => executor.executeCommand(...command)));
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
async refreshTopology() {
|
|
1910
|
+
const executor = await this.executorPromise;
|
|
1911
|
+
if (executor.refreshTopology == null) {
|
|
1912
|
+
throw new FerricStoreError("topology refresh requires a topology-aware native executor");
|
|
1913
|
+
}
|
|
1914
|
+
return await executor.refreshTopology();
|
|
1915
|
+
}
|
|
1916
|
+
async route(key) {
|
|
1917
|
+
const executor = await this.executorPromise;
|
|
1918
|
+
if (executor.route == null) {
|
|
1919
|
+
throw new FerricStoreError("route lookup requires a topology-aware native executor");
|
|
1920
|
+
}
|
|
1921
|
+
return await executor.route(key);
|
|
1922
|
+
}
|
|
1923
|
+
async close() {
|
|
1924
|
+
if (this.closed) {
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
this.closed = true;
|
|
1928
|
+
const executor = await this.executorPromise.catch(() => void 0);
|
|
1929
|
+
await Promise.resolve(executor?.close?.());
|
|
1930
|
+
}
|
|
1931
|
+
async withReconnect(operation) {
|
|
1932
|
+
let executor = await this.executorPromise;
|
|
1933
|
+
for (let attempt = 0; ; attempt++) {
|
|
1934
|
+
try {
|
|
1935
|
+
return await operation(executor);
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
if (this.closed || attempt >= this.maxRetries || !isReconnectableClosedConnectionError(error)) {
|
|
1938
|
+
throw error;
|
|
1939
|
+
}
|
|
1940
|
+
executor = await this.reconnect(executor);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
async reconnect(staleExecutor) {
|
|
1945
|
+
if (this.closed) {
|
|
1946
|
+
throw new FerricStoreError("FerricStore client is closed");
|
|
1947
|
+
}
|
|
1948
|
+
if (this.reconnectPromise != null) {
|
|
1949
|
+
return await this.reconnectPromise;
|
|
1950
|
+
}
|
|
1951
|
+
const currentExecutor = await this.executorPromise.catch(() => void 0);
|
|
1952
|
+
if (currentExecutor != null && currentExecutor !== staleExecutor) {
|
|
1953
|
+
return currentExecutor;
|
|
1954
|
+
}
|
|
1955
|
+
this.reconnectPromise = (async () => {
|
|
1956
|
+
await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
|
|
1957
|
+
const nextExecutor = await this.createExecutor();
|
|
1958
|
+
this.executorPromise = Promise.resolve(nextExecutor);
|
|
1959
|
+
return nextExecutor;
|
|
1960
|
+
})().finally(() => {
|
|
1961
|
+
this.reconnectPromise = void 0;
|
|
1962
|
+
});
|
|
1963
|
+
return await this.reconnectPromise;
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
function isReconnectableClosedConnectionError(error) {
|
|
1967
|
+
return error instanceof Error && error.message === "FerricStore connection is closed";
|
|
1968
|
+
}
|
|
1578
1969
|
function parseFerricUrl(value) {
|
|
1579
1970
|
const url = new URL(value);
|
|
1580
1971
|
if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
|
|
@@ -1598,6 +1989,7 @@ async function connect(parsed, options) {
|
|
|
1598
1989
|
}, timeoutMs);
|
|
1599
1990
|
timer.unref?.();
|
|
1600
1991
|
socket.setNoDelay(true);
|
|
1992
|
+
socket.setKeepAlive(options.keepAlive ?? true, normalizeKeepAliveInitialDelay(options.keepAliveInitialDelayMs));
|
|
1601
1993
|
socket.once("connect", () => {
|
|
1602
1994
|
clearTimeout(timer);
|
|
1603
1995
|
resolve(socket);
|
|
@@ -1608,6 +2000,662 @@ async function connect(parsed, options) {
|
|
|
1608
2000
|
});
|
|
1609
2001
|
});
|
|
1610
2002
|
}
|
|
2003
|
+
function normalizeHeartbeatInterval(value) {
|
|
2004
|
+
if (value != null && value <= 0) {
|
|
2005
|
+
return void 0;
|
|
2006
|
+
}
|
|
2007
|
+
return Math.trunc(value ?? 6e4);
|
|
2008
|
+
}
|
|
2009
|
+
function normalizeKeepAliveInitialDelay(value) {
|
|
2010
|
+
return Math.max(0, Math.trunc(value ?? 3e4));
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// src/topology.ts
|
|
2014
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
2015
|
+
var ROUTE_SLOT_COUNT = 1024;
|
|
2016
|
+
var ROUTE_SLOT_MASK = ROUTE_SLOT_COUNT - 1;
|
|
2017
|
+
var RoutingTopology = class _RoutingTopology {
|
|
2018
|
+
routeEpoch;
|
|
2019
|
+
shardCount;
|
|
2020
|
+
slots;
|
|
2021
|
+
endpoints;
|
|
2022
|
+
constructor(routeEpoch, shardCount, slots, endpoints) {
|
|
2023
|
+
this.routeEpoch = routeEpoch;
|
|
2024
|
+
this.shardCount = shardCount;
|
|
2025
|
+
this.slots = slots;
|
|
2026
|
+
this.endpoints = endpoints;
|
|
2027
|
+
}
|
|
2028
|
+
static empty() {
|
|
2029
|
+
return new _RoutingTopology(0, 0, Array.from({ length: ROUTE_SLOT_COUNT }), /* @__PURE__ */ new Map());
|
|
2030
|
+
}
|
|
2031
|
+
static build(payload) {
|
|
2032
|
+
const ranges = getField(payload, "ranges");
|
|
2033
|
+
if (!Array.isArray(ranges)) {
|
|
2034
|
+
throw new FerricStoreError("invalid SHARDS topology payload", { raw: payload });
|
|
2035
|
+
}
|
|
2036
|
+
const slots = Array.from({ length: ROUTE_SLOT_COUNT });
|
|
2037
|
+
const endpoints = /* @__PURE__ */ new Map();
|
|
2038
|
+
for (const item of ranges) {
|
|
2039
|
+
if (textOrUndefined(getField(item, "hint")) === "leader_unknown") {
|
|
2040
|
+
throw new FerricStoreError("SHARDS range has no leader", { raw: item });
|
|
2041
|
+
}
|
|
2042
|
+
const first = numberOrUndefined(getField(item, "first_slot"));
|
|
2043
|
+
const last = numberOrUndefined(getField(item, "last_slot"));
|
|
2044
|
+
const shard = numberOrUndefined(getField(item, "shard"));
|
|
2045
|
+
const laneId = numberOrUndefined(getField(item, "lane_id"));
|
|
2046
|
+
const endpoint = endpointFromRange(item);
|
|
2047
|
+
if (first == null || last == null || shard == null || laneId == null || first < 0 || last < first || last >= ROUTE_SLOT_COUNT) {
|
|
2048
|
+
throw new FerricStoreError("invalid SHARDS range", { raw: item });
|
|
2049
|
+
}
|
|
2050
|
+
const endpointKey = endpointKeyFor(endpoint);
|
|
2051
|
+
const route = {
|
|
2052
|
+
endpoint,
|
|
2053
|
+
endpointKey,
|
|
2054
|
+
laneId,
|
|
2055
|
+
leaderNode: endpoint.node,
|
|
2056
|
+
shard
|
|
2057
|
+
};
|
|
2058
|
+
for (let slot = first; slot <= last; slot += 1) {
|
|
2059
|
+
slots[slot] = route;
|
|
2060
|
+
}
|
|
2061
|
+
endpoints.set(endpointKey, endpoint);
|
|
2062
|
+
}
|
|
2063
|
+
return new _RoutingTopology(
|
|
2064
|
+
numberOrUndefined(getField(payload, "route_epoch")) ?? 0,
|
|
2065
|
+
numberOrUndefined(getField(payload, "shard_count")) ?? 0,
|
|
2066
|
+
slots,
|
|
2067
|
+
endpoints
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
static slotForKey(key) {
|
|
2071
|
+
const textKey = Buffer4.isBuffer(key) ? key.toString("utf8") : key;
|
|
2072
|
+
let hashInput;
|
|
2073
|
+
if (textKey.startsWith("f:{")) {
|
|
2074
|
+
hashInput = flowHashTag(textKey.slice(3), textKey);
|
|
2075
|
+
} else if (textKey.startsWith("X:f:{")) {
|
|
2076
|
+
hashInput = flowHashTag(textKey.slice(5), textKey);
|
|
2077
|
+
} else {
|
|
2078
|
+
hashInput = hashTagOrKey(textKey);
|
|
2079
|
+
}
|
|
2080
|
+
return crc32(Buffer4.from(hashInput)) & ROUTE_SLOT_MASK;
|
|
2081
|
+
}
|
|
2082
|
+
routeKey(key) {
|
|
2083
|
+
const slot = _RoutingTopology.slotForKey(key);
|
|
2084
|
+
const route = this.slots[slot];
|
|
2085
|
+
if (route == null) {
|
|
2086
|
+
throw new FerricStoreError(`no route for slot ${slot}`);
|
|
2087
|
+
}
|
|
2088
|
+
return { ...route, slot };
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
|
|
2092
|
+
adapterOptions;
|
|
2093
|
+
adapters = /* @__PURE__ */ new Map();
|
|
2094
|
+
endpointPolicy;
|
|
2095
|
+
endpointValidator;
|
|
2096
|
+
seedEndpointKeys;
|
|
2097
|
+
seedUrls;
|
|
2098
|
+
tls;
|
|
2099
|
+
trustedHosts;
|
|
2100
|
+
warmConnections;
|
|
2101
|
+
topologyValue = RoutingTopology.empty();
|
|
2102
|
+
constructor(urls, options = {}) {
|
|
2103
|
+
if (urls.length === 0) {
|
|
2104
|
+
throw new FerricStoreError("TopologyNativeAdapterPool requires at least one seed URL");
|
|
2105
|
+
}
|
|
2106
|
+
this.seedUrls = [...urls];
|
|
2107
|
+
this.endpointPolicy = options.endpointPolicy ?? "seed_hosts";
|
|
2108
|
+
this.endpointValidator = options.endpointValidator;
|
|
2109
|
+
this.warmConnections = options.warmConnections ?? false;
|
|
2110
|
+
this.tls = options.tlsOptions != null || urls.some((url) => parseFerricUrl2(url).tls);
|
|
2111
|
+
this.adapterOptions = nativeOnlyOptions(withSeedAuthDefaults(urls, options));
|
|
2112
|
+
this.seedEndpointKeys = new Set(urls.map((url) => endpointKeyFor(endpointFromUrl(url))));
|
|
2113
|
+
this.trustedHosts = normalizedHostSet(options.trustedHosts ?? []);
|
|
2114
|
+
}
|
|
2115
|
+
static async fromUrls(urls, options = {}) {
|
|
2116
|
+
const pool = new _TopologyNativeAdapterPool(urls, options);
|
|
2117
|
+
await pool.refreshTopology();
|
|
2118
|
+
return pool;
|
|
2119
|
+
}
|
|
2120
|
+
get topology() {
|
|
2121
|
+
return this.topologyValue;
|
|
2122
|
+
}
|
|
2123
|
+
async refreshTopology() {
|
|
2124
|
+
let lastError;
|
|
2125
|
+
for (const url of this.refreshCandidateUrls()) {
|
|
2126
|
+
try {
|
|
2127
|
+
const adapter = await this.adapterForUrl(url);
|
|
2128
|
+
const topology = RoutingTopology.build(await adapter.executeCommand("SHARDS"));
|
|
2129
|
+
this.topologyValue = topology;
|
|
2130
|
+
if (this.warmConnections) {
|
|
2131
|
+
await Promise.allSettled([...topology.endpoints.values()].map(async (endpoint) => await this.adapterForEndpoint(endpoint)));
|
|
2132
|
+
}
|
|
2133
|
+
return topology;
|
|
2134
|
+
} catch (error) {
|
|
2135
|
+
lastError = error;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
throw new FerricStoreError("no FerricStore topology endpoint reachable", { raw: lastError });
|
|
2139
|
+
}
|
|
2140
|
+
route(key) {
|
|
2141
|
+
const route = this.topologyValue.routeKey(key);
|
|
2142
|
+
this.validateEndpoint(route.endpoint);
|
|
2143
|
+
return route;
|
|
2144
|
+
}
|
|
2145
|
+
async executeCommand(...args) {
|
|
2146
|
+
const routed = this.routeData(args);
|
|
2147
|
+
if (routed == null) {
|
|
2148
|
+
return await (await this.controlAdapter()).executeCommand(...args);
|
|
2149
|
+
}
|
|
2150
|
+
const adapter = await this.adapterForEndpoint(routed.route.endpoint);
|
|
2151
|
+
try {
|
|
2152
|
+
return await adapter.executeProtocolCommand(routed.command, routed.route.laneId);
|
|
2153
|
+
} catch (error) {
|
|
2154
|
+
if (isRetryableRouteError(error)) {
|
|
2155
|
+
await this.refreshTopology().catch(() => void 0);
|
|
2156
|
+
}
|
|
2157
|
+
throw error;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
async executePipeline(commands, options = {}) {
|
|
2161
|
+
if (commands.length === 0) {
|
|
2162
|
+
return [];
|
|
2163
|
+
}
|
|
2164
|
+
const route = this.singleRouteForCommands(commands);
|
|
2165
|
+
if (route == null) {
|
|
2166
|
+
return await (await this.controlAdapter()).executePipeline(commands, options);
|
|
2167
|
+
}
|
|
2168
|
+
const adapter = await this.adapterForEndpoint(route.endpoint);
|
|
2169
|
+
try {
|
|
2170
|
+
const response = await adapter.executeProtocolCommand(pipelineCommand(commands), route.laneId);
|
|
2171
|
+
return unwrapPipelineResponse(response, options);
|
|
2172
|
+
} catch (error) {
|
|
2173
|
+
if (isRetryableRouteError(error)) {
|
|
2174
|
+
await this.refreshTopology().catch(() => void 0);
|
|
2175
|
+
}
|
|
2176
|
+
throw error;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
async close() {
|
|
2180
|
+
await Promise.allSettled([...this.adapters.values()].map(async (adapter) => await adapter.close()));
|
|
2181
|
+
this.adapters.clear();
|
|
2182
|
+
}
|
|
2183
|
+
routeData(args) {
|
|
2184
|
+
if (args.length === 0) {
|
|
2185
|
+
return void 0;
|
|
2186
|
+
}
|
|
2187
|
+
let command;
|
|
2188
|
+
try {
|
|
2189
|
+
command = buildProtocolCommand(args);
|
|
2190
|
+
} catch {
|
|
2191
|
+
return void 0;
|
|
2192
|
+
}
|
|
2193
|
+
const key = routingKey(args, command);
|
|
2194
|
+
if (key == null) {
|
|
2195
|
+
return void 0;
|
|
2196
|
+
}
|
|
2197
|
+
return { command, route: this.route(key) };
|
|
2198
|
+
}
|
|
2199
|
+
singleRouteForCommands(commands) {
|
|
2200
|
+
let route;
|
|
2201
|
+
for (const command of commands) {
|
|
2202
|
+
const routed = this.routeData(command);
|
|
2203
|
+
if (routed == null) {
|
|
2204
|
+
return void 0;
|
|
2205
|
+
}
|
|
2206
|
+
const current = routed.route;
|
|
2207
|
+
if (route == null) {
|
|
2208
|
+
route = current;
|
|
2209
|
+
} else if (route.endpointKey !== current.endpointKey || route.laneId !== current.laneId) {
|
|
2210
|
+
return void 0;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
return route;
|
|
2214
|
+
}
|
|
2215
|
+
async controlAdapter() {
|
|
2216
|
+
for (const url of this.refreshCandidateUrls()) {
|
|
2217
|
+
try {
|
|
2218
|
+
return await this.adapterForUrl(url);
|
|
2219
|
+
} catch {
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
return await this.adapterForUrl(this.seedUrls[0] ?? "ferric://127.0.0.1:6388");
|
|
2223
|
+
}
|
|
2224
|
+
async adapterForUrl(url) {
|
|
2225
|
+
const endpoint = endpointFromUrl(url);
|
|
2226
|
+
const key = endpointKeyFor(endpoint);
|
|
2227
|
+
const existing = this.adapters.get(key);
|
|
2228
|
+
if (existing != null) {
|
|
2229
|
+
return existing;
|
|
2230
|
+
}
|
|
2231
|
+
const adapter = await NativeAdapter.fromUrl(url, this.adapterOptions);
|
|
2232
|
+
this.adapters.set(key, adapter);
|
|
2233
|
+
return adapter;
|
|
2234
|
+
}
|
|
2235
|
+
async adapterForEndpoint(endpoint) {
|
|
2236
|
+
this.validateEndpoint(endpoint);
|
|
2237
|
+
const key = endpointKeyFor(endpoint);
|
|
2238
|
+
const existing = this.adapters.get(key);
|
|
2239
|
+
if (existing != null) {
|
|
2240
|
+
return existing;
|
|
2241
|
+
}
|
|
2242
|
+
const adapter = await NativeAdapter.fromUrl(urlFromEndpoint(endpoint, this.tls), this.adapterOptions);
|
|
2243
|
+
this.adapters.set(key, adapter);
|
|
2244
|
+
return adapter;
|
|
2245
|
+
}
|
|
2246
|
+
validateEndpoint(endpoint) {
|
|
2247
|
+
let allowed;
|
|
2248
|
+
if (this.endpointPolicy === "any" || this.endpointPolicy === "none") {
|
|
2249
|
+
allowed = true;
|
|
2250
|
+
} else if (this.endpointPolicy === "seed_hosts") {
|
|
2251
|
+
allowed = this.seedEndpointKeys.has(endpointKeyFor(endpoint)) || this.trustedHosts.has(endpoint.host.toLowerCase());
|
|
2252
|
+
} else if ("allowHosts" in this.endpointPolicy) {
|
|
2253
|
+
allowed = normalizedHostSet(this.endpointPolicy.allowHosts).has(endpoint.host.toLowerCase());
|
|
2254
|
+
} else {
|
|
2255
|
+
throw new FerricStoreError("invalid endpoint policy", { raw: this.endpointPolicy });
|
|
2256
|
+
}
|
|
2257
|
+
if (!allowed) {
|
|
2258
|
+
throw new FerricStoreError("unsafe learned endpoint", { raw: endpoint });
|
|
2259
|
+
}
|
|
2260
|
+
if (this.endpointValidator != null && !this.endpointValidator(endpoint)) {
|
|
2261
|
+
throw new FerricStoreError("unsafe learned endpoint", { raw: endpoint });
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
refreshCandidateUrls() {
|
|
2265
|
+
const urls = [
|
|
2266
|
+
...this.seedUrls,
|
|
2267
|
+
...[...this.topologyValue.endpoints.values()].map((endpoint) => urlFromEndpoint(endpoint, this.tls))
|
|
2268
|
+
];
|
|
2269
|
+
return [...new Set(urls)];
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
function routingKey(args, command) {
|
|
2273
|
+
const name = commandName(args);
|
|
2274
|
+
if (command.opcode < 256 || name === "CLUSTER.KEYSLOT" || name === "SHARDS" || name === "ROUTE") {
|
|
2275
|
+
return void 0;
|
|
2276
|
+
}
|
|
2277
|
+
const keyFromArgs = routingKeyFromArgs(name, args);
|
|
2278
|
+
if (keyFromArgs != null) {
|
|
2279
|
+
return keyFromArgs;
|
|
2280
|
+
}
|
|
2281
|
+
if (!isPlainObject2(command.payload)) {
|
|
2282
|
+
return void 0;
|
|
2283
|
+
}
|
|
2284
|
+
for (const field2 of ["key", "partition_key", "id", "owner_flow_id", "parent_id", "root_id", "correlation_id", "scope"]) {
|
|
2285
|
+
const value = getField(command.payload, field2);
|
|
2286
|
+
if (typeof value === "string" || Buffer4.isBuffer(value)) {
|
|
2287
|
+
return value;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
const keys = getField(command.payload, "keys");
|
|
2291
|
+
if (Array.isArray(keys)) {
|
|
2292
|
+
return singleShardKey(keys);
|
|
2293
|
+
}
|
|
2294
|
+
const pairsValue = getField(command.payload, "pairs");
|
|
2295
|
+
if (Array.isArray(pairsValue)) {
|
|
2296
|
+
return singleShardKey(
|
|
2297
|
+
pairsValue.filter((pair) => Array.isArray(pair) && pair.length > 0).map((pair) => pair[0])
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
return void 0;
|
|
2301
|
+
}
|
|
2302
|
+
function routingKeyFromArgs(name, args) {
|
|
2303
|
+
if (name == null) {
|
|
2304
|
+
return void 0;
|
|
2305
|
+
}
|
|
2306
|
+
if (name === "MGET" || name === "DEL") {
|
|
2307
|
+
return singleShardKey(args.slice(1));
|
|
2308
|
+
}
|
|
2309
|
+
if (name === "MSET") {
|
|
2310
|
+
const keys = [];
|
|
2311
|
+
for (let index = 1; index < args.length; index += 2) {
|
|
2312
|
+
keys.push(args[index]);
|
|
2313
|
+
}
|
|
2314
|
+
return singleShardKey(keys);
|
|
2315
|
+
}
|
|
2316
|
+
if (name === "BITOP") {
|
|
2317
|
+
return bitopRoutingKey(args);
|
|
2318
|
+
}
|
|
2319
|
+
if (name === "RENAME" || name === "RENAMENX") {
|
|
2320
|
+
return singleShardKey(args.slice(1, 3));
|
|
2321
|
+
}
|
|
2322
|
+
if (name === "XREAD" || name === "XREADGROUP") {
|
|
2323
|
+
return streamReadRoutingKey(args);
|
|
2324
|
+
}
|
|
2325
|
+
if (name.startsWith("FLOW.")) {
|
|
2326
|
+
return flowRoutingKey(name, args);
|
|
2327
|
+
}
|
|
2328
|
+
if (firstKeyCommands.has(name)) {
|
|
2329
|
+
const key = args[1];
|
|
2330
|
+
return typeof key === "string" || Buffer4.isBuffer(key) ? key : void 0;
|
|
2331
|
+
}
|
|
2332
|
+
return void 0;
|
|
2333
|
+
}
|
|
2334
|
+
function flowRoutingKey(name, args) {
|
|
2335
|
+
const flowArgs = args.slice(1);
|
|
2336
|
+
if (flowArgs.length === 0) {
|
|
2337
|
+
return void 0;
|
|
2338
|
+
}
|
|
2339
|
+
if (name === "FLOW.CLAIM_DUE" || name === "FLOW.RECLAIM") {
|
|
2340
|
+
return flowPartitionRoutingKey(flowArgs, 1);
|
|
2341
|
+
}
|
|
2342
|
+
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") {
|
|
2343
|
+
const partition2 = flowArgs[0];
|
|
2344
|
+
if (typeof partition2 === "string" && partition2.toUpperCase() !== "AUTO" && partition2.toUpperCase() !== "MIXED") {
|
|
2345
|
+
return partition2;
|
|
2346
|
+
}
|
|
2347
|
+
if (Buffer4.isBuffer(partition2)) {
|
|
2348
|
+
const text2 = partition2.toString("utf8").toUpperCase();
|
|
2349
|
+
if (text2 !== "AUTO" && text2 !== "MIXED") {
|
|
2350
|
+
return partition2;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return void 0;
|
|
2354
|
+
}
|
|
2355
|
+
const partition = flowPartitionRoutingKey(flowArgs, 1);
|
|
2356
|
+
if (partition != null) {
|
|
2357
|
+
return partition;
|
|
2358
|
+
}
|
|
2359
|
+
if (typeScopedFlowCommands.has(name)) {
|
|
2360
|
+
return void 0;
|
|
2361
|
+
}
|
|
2362
|
+
const id = flowArgs[0];
|
|
2363
|
+
return typeof id === "string" || Buffer4.isBuffer(id) ? id : void 0;
|
|
2364
|
+
}
|
|
2365
|
+
function bitopRoutingKey(args) {
|
|
2366
|
+
if (args.length < 4) {
|
|
2367
|
+
return void 0;
|
|
2368
|
+
}
|
|
2369
|
+
return singleShardKey(args.slice(2));
|
|
2370
|
+
}
|
|
2371
|
+
function streamReadRoutingKey(args) {
|
|
2372
|
+
const streamsIndex = args.findIndex((arg) => commandPart(arg) === "STREAMS");
|
|
2373
|
+
if (streamsIndex < 0) {
|
|
2374
|
+
return void 0;
|
|
2375
|
+
}
|
|
2376
|
+
const streamArgs = args.slice(streamsIndex + 1);
|
|
2377
|
+
if (streamArgs.length === 0 || streamArgs.length % 2 !== 0) {
|
|
2378
|
+
return void 0;
|
|
2379
|
+
}
|
|
2380
|
+
return singleShardKey(streamArgs.slice(0, streamArgs.length / 2));
|
|
2381
|
+
}
|
|
2382
|
+
function flowPartitionRoutingKey(args, start) {
|
|
2383
|
+
for (let index = start; index < args.length; index += 1) {
|
|
2384
|
+
const token = commandPart(args[index]);
|
|
2385
|
+
if (token === "PARTITION") {
|
|
2386
|
+
const key = args[index + 1];
|
|
2387
|
+
return typeof key === "string" || Buffer4.isBuffer(key) ? key : void 0;
|
|
2388
|
+
}
|
|
2389
|
+
if (token === "PARTITIONS") {
|
|
2390
|
+
const count = Number(args[index + 1]);
|
|
2391
|
+
if (!Number.isInteger(count) || count < 0) {
|
|
2392
|
+
return void 0;
|
|
2393
|
+
}
|
|
2394
|
+
return singleShardKey(args.slice(index + 2, index + 2 + count));
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
return void 0;
|
|
2398
|
+
}
|
|
2399
|
+
function singleShardKey(keys) {
|
|
2400
|
+
const usable = keys.filter((key) => typeof key === "string" || Buffer4.isBuffer(key));
|
|
2401
|
+
if (usable.length === 0) {
|
|
2402
|
+
return void 0;
|
|
2403
|
+
}
|
|
2404
|
+
const first = usable[0];
|
|
2405
|
+
if (first == null) {
|
|
2406
|
+
return void 0;
|
|
2407
|
+
}
|
|
2408
|
+
const firstSlot = RoutingTopology.slotForKey(first);
|
|
2409
|
+
return usable.every((key) => RoutingTopology.slotForKey(key) === firstSlot) ? first : void 0;
|
|
2410
|
+
}
|
|
2411
|
+
function commandName(args) {
|
|
2412
|
+
const first = commandPart(args[0]);
|
|
2413
|
+
if (first == null) {
|
|
2414
|
+
return void 0;
|
|
2415
|
+
}
|
|
2416
|
+
if (first === "FLOW" && args.length > 1) {
|
|
2417
|
+
const second = commandPart(args[1]);
|
|
2418
|
+
return second == null ? first : `${first}.${second}`;
|
|
2419
|
+
}
|
|
2420
|
+
if (first === "CLIENT" && args.length > 1) {
|
|
2421
|
+
const second = commandPart(args[1]);
|
|
2422
|
+
return second == null ? first : `${first}.${second}`;
|
|
2423
|
+
}
|
|
2424
|
+
if (first === "CLUSTER" && args.length > 1) {
|
|
2425
|
+
const second = commandPart(args[1]);
|
|
2426
|
+
return second == null ? first : `${first}.${second}`;
|
|
2427
|
+
}
|
|
2428
|
+
return first;
|
|
2429
|
+
}
|
|
2430
|
+
function commandPart(value) {
|
|
2431
|
+
if (typeof value === "string") {
|
|
2432
|
+
return value.toUpperCase();
|
|
2433
|
+
}
|
|
2434
|
+
if (Buffer4.isBuffer(value)) {
|
|
2435
|
+
return value.toString("utf8").toUpperCase();
|
|
2436
|
+
}
|
|
2437
|
+
return void 0;
|
|
2438
|
+
}
|
|
2439
|
+
function isPlainObject2(value) {
|
|
2440
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer4.isBuffer(value);
|
|
2441
|
+
}
|
|
2442
|
+
function getField(source, ...keys) {
|
|
2443
|
+
if (source instanceof Map) {
|
|
2444
|
+
for (const key of keys) {
|
|
2445
|
+
if (source.has(key)) return source.get(key);
|
|
2446
|
+
const bufferKey = Buffer4.from(key);
|
|
2447
|
+
for (const [itemKey, value] of source.entries()) {
|
|
2448
|
+
if (Buffer4.isBuffer(itemKey) && itemKey.equals(bufferKey)) {
|
|
2449
|
+
return value;
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
return void 0;
|
|
2454
|
+
}
|
|
2455
|
+
if (isPlainObject2(source)) {
|
|
2456
|
+
for (const key of keys) {
|
|
2457
|
+
if (key in source) return source[key];
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
return void 0;
|
|
2461
|
+
}
|
|
2462
|
+
function textOrUndefined(value) {
|
|
2463
|
+
if (typeof value === "string") {
|
|
2464
|
+
return value;
|
|
2465
|
+
}
|
|
2466
|
+
if (Buffer4.isBuffer(value)) {
|
|
2467
|
+
return value.toString("utf8");
|
|
2468
|
+
}
|
|
2469
|
+
return void 0;
|
|
2470
|
+
}
|
|
2471
|
+
function numberOrUndefined(value) {
|
|
2472
|
+
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
2473
|
+
}
|
|
2474
|
+
function endpointFromRange(item) {
|
|
2475
|
+
const endpointValue = getField(item, "endpoint");
|
|
2476
|
+
const raw = isPlainObject2(endpointValue) || endpointValue instanceof Map ? endpointValue : item;
|
|
2477
|
+
const host = textOrUndefined(getField(raw, "host", "native_host"));
|
|
2478
|
+
const nativePort = numberOrUndefined(getField(raw, "native_port"));
|
|
2479
|
+
if (host == null || nativePort == null) {
|
|
2480
|
+
throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
|
|
2481
|
+
}
|
|
2482
|
+
const nativeTlsPort = numberOrUndefined(getField(raw, "native_tls_port"));
|
|
2483
|
+
return {
|
|
2484
|
+
host,
|
|
2485
|
+
nativePort,
|
|
2486
|
+
node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
|
|
2487
|
+
...nativeTlsPort == null ? {} : { nativeTlsPort }
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function endpointFromUrl(url) {
|
|
2491
|
+
const parsed = parseFerricUrl2(url);
|
|
2492
|
+
return {
|
|
2493
|
+
host: parsed.host,
|
|
2494
|
+
nativePort: parsed.port,
|
|
2495
|
+
node: parsed.host,
|
|
2496
|
+
...parsed.tls ? { nativeTlsPort: parsed.port } : {}
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
function parseFerricUrl2(value) {
|
|
2500
|
+
const url = new URL(value);
|
|
2501
|
+
if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
|
|
2502
|
+
throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
|
|
2503
|
+
}
|
|
2504
|
+
return {
|
|
2505
|
+
host: url.hostname || "127.0.0.1",
|
|
2506
|
+
...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
|
|
2507
|
+
port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
|
|
2508
|
+
tls: url.protocol === "ferrics:",
|
|
2509
|
+
...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2512
|
+
function endpointKeyFor(endpoint) {
|
|
2513
|
+
return `${endpoint.host.toLowerCase()}:${endpoint.nativePort}`;
|
|
2514
|
+
}
|
|
2515
|
+
function urlFromEndpoint(endpoint, useTls) {
|
|
2516
|
+
const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
|
|
2517
|
+
const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
|
|
2518
|
+
return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
|
|
2519
|
+
}
|
|
2520
|
+
function nativeOnlyOptions(options) {
|
|
2521
|
+
const nativeOptions = { ...options };
|
|
2522
|
+
delete nativeOptions.endpointPolicy;
|
|
2523
|
+
delete nativeOptions.endpointValidator;
|
|
2524
|
+
delete nativeOptions.haRouting;
|
|
2525
|
+
delete nativeOptions.seeds;
|
|
2526
|
+
delete nativeOptions.trustedHosts;
|
|
2527
|
+
delete nativeOptions.warmConnections;
|
|
2528
|
+
return nativeOptions;
|
|
2529
|
+
}
|
|
2530
|
+
function withSeedAuthDefaults(urls, options) {
|
|
2531
|
+
const next = { ...options };
|
|
2532
|
+
for (const url of urls) {
|
|
2533
|
+
const parsed = parseFerricUrl2(url);
|
|
2534
|
+
if (next.username == null && parsed.username != null) {
|
|
2535
|
+
next.username = parsed.username;
|
|
2536
|
+
}
|
|
2537
|
+
if (next.password == null && parsed.password != null) {
|
|
2538
|
+
next.password = parsed.password;
|
|
2539
|
+
}
|
|
2540
|
+
if (next.username != null && next.password != null) {
|
|
2541
|
+
break;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
return next;
|
|
2545
|
+
}
|
|
2546
|
+
function isRetryableRouteError(error) {
|
|
2547
|
+
if (!(error instanceof Error)) {
|
|
2548
|
+
return false;
|
|
2549
|
+
}
|
|
2550
|
+
const message = error.message.toLowerCase();
|
|
2551
|
+
return message.includes("connection closed") || message.includes("connection is closed") || message.includes("shard not available") || message.includes("leader") || message.includes("route");
|
|
2552
|
+
}
|
|
2553
|
+
function normalizedHostSet(hosts) {
|
|
2554
|
+
return new Set(hosts.filter((host) => host != null && host !== "").map((host) => host.toLowerCase()));
|
|
2555
|
+
}
|
|
2556
|
+
function hashTagOrKey(key) {
|
|
2557
|
+
const start = key.indexOf("{");
|
|
2558
|
+
if (start < 0) {
|
|
2559
|
+
return key;
|
|
2560
|
+
}
|
|
2561
|
+
const end = key.indexOf("}", start + 1);
|
|
2562
|
+
return end > start + 1 ? key.slice(start + 1, end) : key;
|
|
2563
|
+
}
|
|
2564
|
+
function flowHashTag(rest, fallbackKey) {
|
|
2565
|
+
const end = rest.indexOf("}");
|
|
2566
|
+
return end > 0 ? rest.slice(0, end) : hashTagOrKey(fallbackKey);
|
|
2567
|
+
}
|
|
2568
|
+
var crc32Table = new Uint32Array(256);
|
|
2569
|
+
for (let index = 0; index < 256; index += 1) {
|
|
2570
|
+
let value = index;
|
|
2571
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
2572
|
+
value = (value & 1) !== 0 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
2573
|
+
}
|
|
2574
|
+
crc32Table[index] = value >>> 0;
|
|
2575
|
+
}
|
|
2576
|
+
function crc32(buffer) {
|
|
2577
|
+
let value = 4294967295;
|
|
2578
|
+
for (const byte of buffer) {
|
|
2579
|
+
value = value >>> 8 ^ (crc32Table[(value ^ byte) & 255] ?? 0);
|
|
2580
|
+
}
|
|
2581
|
+
return (value ^ 4294967295) >>> 0;
|
|
2582
|
+
}
|
|
2583
|
+
var firstKeyCommands = /* @__PURE__ */ new Set([
|
|
2584
|
+
"BITCOUNT",
|
|
2585
|
+
"BITFIELD",
|
|
2586
|
+
"BITPOS",
|
|
2587
|
+
"CAS",
|
|
2588
|
+
"EXISTS",
|
|
2589
|
+
"EXPIRE",
|
|
2590
|
+
"EXPIREAT",
|
|
2591
|
+
"FETCH_OR_COMPUTE",
|
|
2592
|
+
"FETCH_OR_COMPUTE_ERROR",
|
|
2593
|
+
"FETCH_OR_COMPUTE_RESULT",
|
|
2594
|
+
"FERRICSTORE.KEY_INFO",
|
|
2595
|
+
"GET",
|
|
2596
|
+
"GETBIT",
|
|
2597
|
+
"GETDEL",
|
|
2598
|
+
"GETEX",
|
|
2599
|
+
"HDEL",
|
|
2600
|
+
"HEXISTS",
|
|
2601
|
+
"HGET",
|
|
2602
|
+
"HGETALL",
|
|
2603
|
+
"HINCRBY",
|
|
2604
|
+
"HKEYS",
|
|
2605
|
+
"HLEN",
|
|
2606
|
+
"HMGET",
|
|
2607
|
+
"HMSET",
|
|
2608
|
+
"HSET",
|
|
2609
|
+
"HVALS",
|
|
2610
|
+
"LOCK",
|
|
2611
|
+
"LPOP",
|
|
2612
|
+
"LPUSH",
|
|
2613
|
+
"LRANGE",
|
|
2614
|
+
"LREM",
|
|
2615
|
+
"RATELIMIT.ADD",
|
|
2616
|
+
"RPOP",
|
|
2617
|
+
"RPUSH",
|
|
2618
|
+
"SADD",
|
|
2619
|
+
"SCARD",
|
|
2620
|
+
"SISMEMBER",
|
|
2621
|
+
"SMEMBERS",
|
|
2622
|
+
"SREM",
|
|
2623
|
+
"SET",
|
|
2624
|
+
"SETBIT",
|
|
2625
|
+
"STRLEN",
|
|
2626
|
+
"TTL",
|
|
2627
|
+
"TYPE",
|
|
2628
|
+
"UNLINK",
|
|
2629
|
+
"UNLOCK",
|
|
2630
|
+
"XADD",
|
|
2631
|
+
"XLEN",
|
|
2632
|
+
"XRANGE",
|
|
2633
|
+
"ZADD",
|
|
2634
|
+
"ZCARD",
|
|
2635
|
+
"ZRANGE",
|
|
2636
|
+
"ZREM",
|
|
2637
|
+
"ZSCORE"
|
|
2638
|
+
]);
|
|
2639
|
+
var typeScopedFlowCommands = /* @__PURE__ */ new Set([
|
|
2640
|
+
"FLOW.APPROVAL.LIST",
|
|
2641
|
+
"FLOW.ATTRIBUTE_VALUES",
|
|
2642
|
+
"FLOW.ATTRIBUTES",
|
|
2643
|
+
"FLOW.BUDGET.LIST",
|
|
2644
|
+
"FLOW.FAILURES",
|
|
2645
|
+
"FLOW.GOVERNANCE.OVERVIEW",
|
|
2646
|
+
"FLOW.INFO",
|
|
2647
|
+
"FLOW.LIMIT.LIST",
|
|
2648
|
+
"FLOW.LIST",
|
|
2649
|
+
"FLOW.POLICY.GET",
|
|
2650
|
+
"FLOW.POLICY.SET",
|
|
2651
|
+
"FLOW.RETENTION_CLEANUP",
|
|
2652
|
+
"FLOW.SCHEDULE.FIRE_DUE",
|
|
2653
|
+
"FLOW.SCHEDULE.LIST",
|
|
2654
|
+
"FLOW.SEARCH",
|
|
2655
|
+
"FLOW.STATS",
|
|
2656
|
+
"FLOW.STUCK",
|
|
2657
|
+
"FLOW.TERMINALS"
|
|
2658
|
+
]);
|
|
1611
2659
|
|
|
1612
2660
|
// src/codecs.ts
|
|
1613
2661
|
var RawCodec = class {
|
|
@@ -1822,7 +2870,7 @@ function parseKvResponse(value) {
|
|
|
1822
2870
|
return { value };
|
|
1823
2871
|
}
|
|
1824
2872
|
function autoPartitionKeyForId(id) {
|
|
1825
|
-
return `${AUTO_PARTITION_PREFIX}${
|
|
2873
|
+
return `${AUTO_PARTITION_PREFIX}${crc322(Buffer.from(id)) % AUTO_PARTITION_BUCKETS}`;
|
|
1826
2874
|
}
|
|
1827
2875
|
function expandManyResponse(value, count) {
|
|
1828
2876
|
if (Array.isArray(value) && value.length === count) {
|
|
@@ -1892,7 +2940,7 @@ function coerceDiagnosticValue(value) {
|
|
|
1892
2940
|
}
|
|
1893
2941
|
return value;
|
|
1894
2942
|
}
|
|
1895
|
-
function
|
|
2943
|
+
function crc322(buffer) {
|
|
1896
2944
|
let crc = 4294967295;
|
|
1897
2945
|
for (const byte of buffer) {
|
|
1898
2946
|
crc ^= byte;
|
|
@@ -2818,14 +3866,14 @@ function claimedItemFromResp(value, codec) {
|
|
|
2818
3866
|
const partition = tuple[1] == null ? null : bytes(tuple[1]);
|
|
2819
3867
|
const leaseToken = bytes(tuple[2]);
|
|
2820
3868
|
const fencingToken = integer(tuple[3]);
|
|
2821
|
-
const attrs = tuple[5] ?? (
|
|
3869
|
+
const attrs = tuple[5] ?? (isPlainObject3(tuple[4]) ? tuple[4] : void 0);
|
|
2822
3870
|
const item = {
|
|
2823
3871
|
id: text(id),
|
|
2824
3872
|
partitionKey: optionalString(partition),
|
|
2825
3873
|
leaseToken,
|
|
2826
3874
|
fencingToken,
|
|
2827
3875
|
runState: optionalString(tuple[4]),
|
|
2828
|
-
attributes:
|
|
3876
|
+
attributes: isPlainObject3(attrs) ? attrs : void 0,
|
|
2829
3877
|
type: "",
|
|
2830
3878
|
state: "running"
|
|
2831
3879
|
};
|
|
@@ -2945,9 +3993,9 @@ function decodeValues(value, codec) {
|
|
|
2945
3993
|
}
|
|
2946
3994
|
function plainObjectField(value, key) {
|
|
2947
3995
|
const item = field(value, key);
|
|
2948
|
-
return
|
|
3996
|
+
return isPlainObject3(item) ? item : void 0;
|
|
2949
3997
|
}
|
|
2950
|
-
function
|
|
3998
|
+
function isPlainObject3(value) {
|
|
2951
3999
|
return typeof value === "object" && value != null && !Buffer.isBuffer(value) && !(value instanceof Uint8Array) && !Array.isArray(value);
|
|
2952
4000
|
}
|
|
2953
4001
|
|
|
@@ -2998,7 +4046,21 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
2998
4046
|
this.zset = new SortedSetStore(this);
|
|
2999
4047
|
}
|
|
3000
4048
|
static async fromUrl(url, options = {}) {
|
|
3001
|
-
|
|
4049
|
+
const reconnectOptions = options.reconnect ?? options.nativeOptions?.autoReconnect ?? true;
|
|
4050
|
+
const nativeOptions = options.nativeOptions ?? {};
|
|
4051
|
+
const seeds = nativeOptions.seeds ?? [];
|
|
4052
|
+
const createExecutor = async () => nativeOptions.haRouting === true || seeds.length > 0 ? await TopologyNativeAdapterPool.fromUrls([url, ...seeds], nativeOptions) : await NativeAdapter.fromUrl(url, nativeOptions);
|
|
4053
|
+
const executor = reconnectOptions === false ? await createExecutor() : new ReconnectingExecutor(createExecutor, reconnectOptions === true ? {} : reconnectOptions);
|
|
4054
|
+
return new _FerricStoreClient(executor, options);
|
|
4055
|
+
}
|
|
4056
|
+
static async fromUrls(urls, options = {}) {
|
|
4057
|
+
if (urls.length === 0) {
|
|
4058
|
+
throw new Error("FerricStoreClient.fromUrls requires at least one URL");
|
|
4059
|
+
}
|
|
4060
|
+
const reconnectOptions = options.reconnect ?? options.nativeOptions?.autoReconnect ?? true;
|
|
4061
|
+
const createExecutor = async () => await TopologyNativeAdapterPool.fromUrls(urls, options.nativeOptions);
|
|
4062
|
+
const executor = reconnectOptions === false ? await createExecutor() : new ReconnectingExecutor(createExecutor, reconnectOptions === true ? {} : reconnectOptions);
|
|
4063
|
+
return new _FerricStoreClient(executor, options);
|
|
3002
4064
|
}
|
|
3003
4065
|
async command(...args) {
|
|
3004
4066
|
return await this.executor.executeCommand(...args);
|
|
@@ -3012,6 +4074,18 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3012
4074
|
async close() {
|
|
3013
4075
|
await this.executor.close?.();
|
|
3014
4076
|
}
|
|
4077
|
+
async refreshTopology() {
|
|
4078
|
+
if (this.executor.refreshTopology == null) {
|
|
4079
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
4080
|
+
}
|
|
4081
|
+
return await this.executor.refreshTopology();
|
|
4082
|
+
}
|
|
4083
|
+
async route(key) {
|
|
4084
|
+
if (this.executor.route == null) {
|
|
4085
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
4086
|
+
}
|
|
4087
|
+
return await this.executor.route(key);
|
|
4088
|
+
}
|
|
3015
4089
|
async ping(message) {
|
|
3016
4090
|
return await this.command("PING", ...message == null ? [] : [message]);
|
|
3017
4091
|
}
|
|
@@ -3074,7 +4148,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3074
4148
|
return response == null ? null : text(response);
|
|
3075
4149
|
}
|
|
3076
4150
|
async clientInfo() {
|
|
3077
|
-
return
|
|
4151
|
+
return infoText(await this.command("CLIENT", "INFO"));
|
|
3078
4152
|
}
|
|
3079
4153
|
async clientList(options = {}) {
|
|
3080
4154
|
return text(await this.command("CLIENT", "LIST", ...options.type == null ? [] : ["TYPE", options.type]));
|
|
@@ -3129,7 +4203,7 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3129
4203
|
return Number(await this.command("PUBSUB", "NUMPAT"));
|
|
3130
4204
|
}
|
|
3131
4205
|
async aclSetUser(username, rules) {
|
|
3132
|
-
return okResponse(await this.command("ACL", "SETUSER", username, ...rules));
|
|
4206
|
+
return okResponse(await this.command("ACL", "SETUSER", username, ...ruleArgs(rules)));
|
|
3133
4207
|
}
|
|
3134
4208
|
async aclDelUser(...usernames) {
|
|
3135
4209
|
return Number(await this.command("ACL", "DELUSER", ...usernames));
|
|
@@ -3140,6 +4214,9 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3140
4214
|
async aclList() {
|
|
3141
4215
|
return arrayResponse(await this.command("ACL", "LIST"));
|
|
3142
4216
|
}
|
|
4217
|
+
async aclListUsers() {
|
|
4218
|
+
return await this.aclList();
|
|
4219
|
+
}
|
|
3143
4220
|
async aclWhoami() {
|
|
3144
4221
|
return text(await this.command("ACL", "WHOAMI"));
|
|
3145
4222
|
}
|
|
@@ -3235,6 +4312,54 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3235
4312
|
async ferricstoreDoctor(...args) {
|
|
3236
4313
|
return await this.command("FERRICSTORE.DOCTOR", ...args);
|
|
3237
4314
|
}
|
|
4315
|
+
async capabilities() {
|
|
4316
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.CAPABILITIES"));
|
|
4317
|
+
}
|
|
4318
|
+
async ensureNamespace(prefix, attrs = {}) {
|
|
4319
|
+
return normalizeAdminResponse(
|
|
4320
|
+
await this.command("FERRICSTORE.NAMESPACE", "ENSURE", prefix, ...managementPairArgs(attrs))
|
|
4321
|
+
);
|
|
4322
|
+
}
|
|
4323
|
+
async getNamespace(prefix) {
|
|
4324
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "GET", prefix));
|
|
4325
|
+
}
|
|
4326
|
+
async listNamespaces() {
|
|
4327
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "LIST"));
|
|
4328
|
+
}
|
|
4329
|
+
async deleteNamespace(prefix) {
|
|
4330
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.NAMESPACE", "DELETE", prefix));
|
|
4331
|
+
}
|
|
4332
|
+
async setQuota(namespace, quotaSpec = {}) {
|
|
4333
|
+
return normalizeAdminResponse(
|
|
4334
|
+
await this.command("FERRICSTORE.QUOTA", "SET", namespace, ...managementPairArgs(quotaSpec))
|
|
4335
|
+
);
|
|
4336
|
+
}
|
|
4337
|
+
async getQuota(namespace) {
|
|
4338
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.QUOTA", "GET", namespace));
|
|
4339
|
+
}
|
|
4340
|
+
async quotaUsage(namespace) {
|
|
4341
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.QUOTA", "USAGE", namespace));
|
|
4342
|
+
}
|
|
4343
|
+
async clusterInfo() {
|
|
4344
|
+
return normalizeAdminResponse(await this.command("FERRICSTORE.TELEMETRY", "CLUSTER_INFO"));
|
|
4345
|
+
}
|
|
4346
|
+
async namespaceUsage(prefix) {
|
|
4347
|
+
return normalizeAdminResponse(
|
|
4348
|
+
await this.command("FERRICSTORE.TELEMETRY", "NAMESPACE_USAGE", prefix)
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
async flowQuery(attrs = {}) {
|
|
4352
|
+
const response = normalizeAdminResponse(
|
|
4353
|
+
await this.command("FERRICSTORE.TELEMETRY", "FLOW_QUERY", ...managementPairArgs(attrs))
|
|
4354
|
+
);
|
|
4355
|
+
return Array.isArray(response) ? [...response] : [];
|
|
4356
|
+
}
|
|
4357
|
+
async flowHistory(id, attrs = {}) {
|
|
4358
|
+
const response = normalizeAdminResponse(
|
|
4359
|
+
await this.command("FERRICSTORE.TELEMETRY", "FLOW_HISTORY", id, ...managementPairArgs(attrs))
|
|
4360
|
+
);
|
|
4361
|
+
return Array.isArray(response) ? [...response] : [];
|
|
4362
|
+
}
|
|
3238
4363
|
async create(id, options) {
|
|
3239
4364
|
const currentNowMs = options.nowMs ?? nowMs();
|
|
3240
4365
|
const args = [
|
|
@@ -3715,6 +4840,14 @@ var FerricStoreClient = class _FerricStoreClient {
|
|
|
3715
4840
|
async list(type, options = {}) {
|
|
3716
4841
|
return await this.indexQuery("FLOW.LIST", type, options);
|
|
3717
4842
|
}
|
|
4843
|
+
async search(type, options = {}) {
|
|
4844
|
+
const args = ["FLOW.SEARCH", type];
|
|
4845
|
+
appendReadOptions(args, options);
|
|
4846
|
+
appendAttributes(args, options.attributes);
|
|
4847
|
+
appendSearchStateMeta(args, options.state, options.stateMeta);
|
|
4848
|
+
const response = await this.command(...args);
|
|
4849
|
+
return Array.isArray(response) ? this.records(response) : [];
|
|
4850
|
+
}
|
|
3718
4851
|
async terminals(type, options = {}) {
|
|
3719
4852
|
return await this.indexQuery("FLOW.TERMINALS", type, options);
|
|
3720
4853
|
}
|
|
@@ -3884,6 +5017,18 @@ var ErrorMappingExecutor = class {
|
|
|
3884
5017
|
throwMapped(error);
|
|
3885
5018
|
}
|
|
3886
5019
|
}
|
|
5020
|
+
async refreshTopology() {
|
|
5021
|
+
if (this.executor.refreshTopology == null) {
|
|
5022
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
5023
|
+
}
|
|
5024
|
+
return await this.executor.refreshTopology();
|
|
5025
|
+
}
|
|
5026
|
+
async route(key) {
|
|
5027
|
+
if (this.executor.route == null) {
|
|
5028
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
5029
|
+
}
|
|
5030
|
+
return await this.executor.route(key);
|
|
5031
|
+
}
|
|
3887
5032
|
async close() {
|
|
3888
5033
|
await this.executor.close?.();
|
|
3889
5034
|
}
|
|
@@ -3919,6 +5064,18 @@ var AutoBatchExecutor = class {
|
|
|
3919
5064
|
}
|
|
3920
5065
|
return await Promise.all(commands.map((command) => this.executor.executeCommand(...command)));
|
|
3921
5066
|
}
|
|
5067
|
+
async refreshTopology() {
|
|
5068
|
+
if (this.executor.refreshTopology == null) {
|
|
5069
|
+
throw new Error("topology refresh requires a topology-aware native executor");
|
|
5070
|
+
}
|
|
5071
|
+
return await this.executor.refreshTopology();
|
|
5072
|
+
}
|
|
5073
|
+
async route(key) {
|
|
5074
|
+
if (this.executor.route == null) {
|
|
5075
|
+
throw new Error("route lookup requires a topology-aware native executor");
|
|
5076
|
+
}
|
|
5077
|
+
return await this.executor.route(key);
|
|
5078
|
+
}
|
|
3922
5079
|
async close() {
|
|
3923
5080
|
this.scheduled = false;
|
|
3924
5081
|
if (this.timer != null) {
|
|
@@ -4079,7 +5236,7 @@ var neverAutoBatchCommandPrefixes = /* @__PURE__ */ new Set([
|
|
|
4079
5236
|
]);
|
|
4080
5237
|
var neverAutoBatchCommands = /* @__PURE__ */ new Set(["FLOW.CLAIM_DUE", "FLOW.RECLAIM"]);
|
|
4081
5238
|
function autoBatchCommandAllowed(command, mode) {
|
|
4082
|
-
const name =
|
|
5239
|
+
const name = commandName2(command);
|
|
4083
5240
|
if (name == null || neverAutoBatchCommands.has(name)) {
|
|
4084
5241
|
return false;
|
|
4085
5242
|
}
|
|
@@ -4089,18 +5246,18 @@ function autoBatchCommandAllowed(command, mode) {
|
|
|
4089
5246
|
}
|
|
4090
5247
|
return mode === "all" || safeAutoBatchCommands.has(name);
|
|
4091
5248
|
}
|
|
4092
|
-
function
|
|
4093
|
-
const first =
|
|
5249
|
+
function commandName2(command) {
|
|
5250
|
+
const first = commandPart2(command[0]);
|
|
4094
5251
|
if (first == null) {
|
|
4095
5252
|
return null;
|
|
4096
5253
|
}
|
|
4097
5254
|
if (first === "FLOW" && command.length > 1) {
|
|
4098
|
-
const second =
|
|
5255
|
+
const second = commandPart2(command[1]);
|
|
4099
5256
|
return second == null ? first : `${first}.${second}`;
|
|
4100
5257
|
}
|
|
4101
5258
|
return first;
|
|
4102
5259
|
}
|
|
4103
|
-
function
|
|
5260
|
+
function commandPart2(value) {
|
|
4104
5261
|
if (typeof value === "string") {
|
|
4105
5262
|
return value.toUpperCase();
|
|
4106
5263
|
}
|
|
@@ -4125,6 +5282,67 @@ function appendStateMeta(args, stateMeta) {
|
|
|
4125
5282
|
args.push("STATE_META", name, value);
|
|
4126
5283
|
}
|
|
4127
5284
|
}
|
|
5285
|
+
function appendAttributes(args, attributes) {
|
|
5286
|
+
for (const [name, value] of Object.entries(attributes ?? {})) {
|
|
5287
|
+
args.push("ATTRIBUTE", name, value);
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
function appendSearchStateMeta(args, state, stateMeta) {
|
|
5291
|
+
const entries = Object.entries(stateMeta ?? {});
|
|
5292
|
+
if (entries.length === 0) {
|
|
5293
|
+
return;
|
|
5294
|
+
}
|
|
5295
|
+
if (entries.every(([, value]) => isPlainObject4(value))) {
|
|
5296
|
+
for (const [metaState, values] of entries) {
|
|
5297
|
+
args.push("STATE_META", metaState, values);
|
|
5298
|
+
}
|
|
5299
|
+
return;
|
|
5300
|
+
}
|
|
5301
|
+
if (state == null) {
|
|
5302
|
+
throw new Error("search stateMeta filters require state or nested state metadata");
|
|
5303
|
+
}
|
|
5304
|
+
args.push("STATE_META", state, stateMeta);
|
|
5305
|
+
}
|
|
5306
|
+
function normalizeAdminResponse(value) {
|
|
5307
|
+
return normalizeRefMeta(value);
|
|
5308
|
+
}
|
|
5309
|
+
function infoText(value) {
|
|
5310
|
+
if (typeof value === "string" || Buffer.isBuffer(value)) {
|
|
5311
|
+
return text(value);
|
|
5312
|
+
}
|
|
5313
|
+
const normalized = normalizeRefMeta(value);
|
|
5314
|
+
if (isPlainObject4(normalized)) {
|
|
5315
|
+
return Object.entries(normalized).map(([key, item]) => `${key}=${diagnosticValueText(item)}`).join(" ");
|
|
5316
|
+
}
|
|
5317
|
+
return diagnosticValueText(normalized);
|
|
5318
|
+
}
|
|
5319
|
+
function diagnosticValueText(value) {
|
|
5320
|
+
if (value == null) {
|
|
5321
|
+
return "";
|
|
5322
|
+
}
|
|
5323
|
+
if (Buffer.isBuffer(value)) {
|
|
5324
|
+
return value.toString("utf8");
|
|
5325
|
+
}
|
|
5326
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
5327
|
+
return String(value);
|
|
5328
|
+
}
|
|
5329
|
+
return JSON.stringify(value);
|
|
5330
|
+
}
|
|
5331
|
+
function managementPairArgs(pairs2) {
|
|
5332
|
+
const args = [];
|
|
5333
|
+
for (const [key, value] of Object.entries(pairs2)) {
|
|
5334
|
+
if (value != null) {
|
|
5335
|
+
args.push(key.toUpperCase(), value);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
return args;
|
|
5339
|
+
}
|
|
5340
|
+
function ruleArgs(rules) {
|
|
5341
|
+
return typeof rules === "string" ? [rules] : [...rules];
|
|
5342
|
+
}
|
|
5343
|
+
function isPlainObject4(value) {
|
|
5344
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer.isBuffer(value);
|
|
5345
|
+
}
|
|
4128
5346
|
function sharedCreateManyStateMeta(items, stateMeta) {
|
|
4129
5347
|
const itemMetas = items.map((item) => item.stateMeta).filter((meta) => meta != null);
|
|
4130
5348
|
if (itemMetas.length === 0) {
|
|
@@ -5097,6 +6315,7 @@ function valueRefToString(value) {
|
|
|
5097
6315
|
export {
|
|
5098
6316
|
BitmapStore,
|
|
5099
6317
|
BloomFilterStore,
|
|
6318
|
+
COMMAND_OPCODES,
|
|
5100
6319
|
CountMinSketchStore,
|
|
5101
6320
|
CuckooFilterStore,
|
|
5102
6321
|
FerricStoreClient,
|
|
@@ -5120,12 +6339,15 @@ export {
|
|
|
5120
6339
|
QueueClient,
|
|
5121
6340
|
QueueWorker,
|
|
5122
6341
|
RawCodec,
|
|
6342
|
+
ReconnectingExecutor,
|
|
6343
|
+
RoutingTopology,
|
|
5123
6344
|
SetStore,
|
|
5124
6345
|
SortedSetStore,
|
|
5125
6346
|
StaleLeaseError,
|
|
5126
6347
|
StreamStore,
|
|
5127
6348
|
TDigestStore,
|
|
5128
6349
|
TopKStore,
|
|
6350
|
+
TopologyNativeAdapterPool,
|
|
5129
6351
|
Workflow,
|
|
5130
6352
|
WorkflowClient,
|
|
5131
6353
|
WorkflowContext,
|
|
@@ -5134,6 +6356,7 @@ export {
|
|
|
5134
6356
|
classifyServerError,
|
|
5135
6357
|
complete,
|
|
5136
6358
|
fail,
|
|
6359
|
+
isReconnectableClosedConnectionError,
|
|
5137
6360
|
mapException,
|
|
5138
6361
|
retry,
|
|
5139
6362
|
transition
|