@ferricstore/ferricstore 0.1.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +35 -6
  2. package/dist/index.cjs +1379 -667
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +83 -33
  5. package/dist/index.d.ts +83 -33
  6. package/dist/index.js +1384 -676
  7. package/dist/index.js.map +1 -1
  8. package/docs/api/assets/hierarchy.js +1 -1
  9. package/docs/api/assets/navigation.js +1 -1
  10. package/docs/api/assets/search.js +1 -1
  11. package/docs/api/classes/ClaimHydrationError.html +6 -3
  12. package/docs/api/classes/ConnectionClosedError.html +6 -3
  13. package/docs/api/classes/FerricStoreClient.html +4 -4
  14. package/docs/api/classes/FerricStoreError.html +6 -3
  15. package/docs/api/classes/FlowAlreadyExistsError.html +6 -3
  16. package/docs/api/classes/FlowBatchError.html +6 -3
  17. package/docs/api/classes/FlowNotFoundError.html +6 -3
  18. package/docs/api/classes/FlowWrongStateError.html +6 -3
  19. package/docs/api/classes/InvalidCommandError.html +6 -3
  20. package/docs/api/classes/KeyValueStore.html +2 -2
  21. package/docs/api/classes/LeaseRenewalError.html +1 -1
  22. package/docs/api/classes/LockHeldError.html +6 -3
  23. package/docs/api/classes/LockNotOwnedError.html +6 -3
  24. package/docs/api/classes/NativeAdapter.html +2 -2
  25. package/docs/api/classes/OverloadedError.html +6 -4
  26. package/docs/api/classes/QueueCompletionError.html +1 -1
  27. package/docs/api/classes/ReconnectingExecutor.html +4 -4
  28. package/docs/api/classes/RequestTimeoutError.html +6 -3
  29. package/docs/api/classes/RerouteError.html +6 -3
  30. package/docs/api/classes/StaleLeaseError.html +6 -3
  31. package/docs/api/classes/StalePolicyGenerationError.html +39 -0
  32. package/docs/api/classes/TDigestStore.html +2 -2
  33. package/docs/api/classes/TopKStore.html +1 -1
  34. package/docs/api/classes/TopologyNativeAdapterPool.html +3 -3
  35. package/docs/api/classes/Workflow.html +1 -1
  36. package/docs/api/classes/WorkflowWorker.html +2 -2
  37. package/docs/api/functions/classifyServerError.html +1 -1
  38. package/docs/api/functions/isReconnectableClosedConnectionError.html +1 -1
  39. package/docs/api/functions/mapException.html +1 -1
  40. package/docs/api/hierarchy.html +1 -1
  41. package/docs/api/index.html +22 -8
  42. package/docs/api/interfaces/AdminListOptions.html +1 -1
  43. package/docs/api/interfaces/CancelOptions.html +2 -2
  44. package/docs/api/interfaces/ClaimDueOptions.html +3 -3
  45. package/docs/api/interfaces/CompleteJobsAndClaimJobsResult.html +6 -6
  46. package/docs/api/interfaces/CompleteManyOptions.html +2 -2
  47. package/docs/api/interfaces/CompleteOptions.html +2 -2
  48. package/docs/api/interfaces/CompleteOutcome.html +1 -1
  49. package/docs/api/interfaces/CreateManyOptions.html +1 -1
  50. package/docs/api/interfaces/EffectCompensateOptions.html +1 -1
  51. package/docs/api/interfaces/EffectConfirmOptions.html +1 -1
  52. package/docs/api/interfaces/EffectFailOptions.html +1 -1
  53. package/docs/api/interfaces/EffectStatusOptions.html +1 -1
  54. package/docs/api/interfaces/ExtendLeaseOptions.html +2 -2
  55. package/docs/api/interfaces/FailOptions.html +2 -2
  56. package/docs/api/interfaces/FailOutcome.html +1 -1
  57. package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +1 -1
  58. package/docs/api/interfaces/FerricStoreClientOptions.html +1 -1
  59. package/docs/api/interfaces/{FetchOrComputeLegacyResult.html → FetchOrComputeComputeResult.html} +2 -2
  60. package/docs/api/interfaces/FlowMaxActiveFailure.html +3 -0
  61. package/docs/api/interfaces/FlowPolicyBackoffSnapshot.html +5 -0
  62. package/docs/api/interfaces/FlowPolicyOptions.html +8 -4
  63. package/docs/api/interfaces/FlowPolicyRetentionSnapshot.html +3 -0
  64. package/docs/api/interfaces/FlowPolicyRetrySnapshot.html +4 -0
  65. package/docs/api/interfaces/FlowPolicySnapshot.html +17 -0
  66. package/docs/api/interfaces/FlowPolicyStateSnapshot.html +6 -0
  67. package/docs/api/interfaces/FlowRecord.html +4 -2
  68. package/docs/api/interfaces/FlowStatePolicy.html +2 -2
  69. package/docs/api/interfaces/HistoryOptions.html +2 -2
  70. package/docs/api/interfaces/InvocationCreateOptions.html +2 -2
  71. package/docs/api/interfaces/LeaseMutationOptions.html +2 -2
  72. package/docs/api/interfaces/LimitListOptions.html +1 -1
  73. package/docs/api/interfaces/MutateOptions.html +2 -2
  74. package/docs/api/interfaces/NamedValueMutation.html +1 -1
  75. package/docs/api/interfaces/ProtocolCommand.html +2 -2
  76. package/docs/api/interfaces/ReadOptions.html +2 -2
  77. package/docs/api/interfaces/ReclaimOptions.html +2 -2
  78. package/docs/api/interfaces/RequestContext.html +2 -2
  79. package/docs/api/interfaces/RequestContextOptions.html +2 -2
  80. package/docs/api/interfaces/RetryOptions.html +2 -2
  81. package/docs/api/interfaces/RetryOutcome.html +1 -1
  82. package/docs/api/interfaces/RunStepsItem.html +2 -2
  83. package/docs/api/interfaces/RunStepsManyOptions.html +2 -2
  84. package/docs/api/interfaces/SearchOptions.html +2 -2
  85. package/docs/api/interfaces/StartAndClaimOptions.html +4 -2
  86. package/docs/api/interfaces/StepContinueOptions.html +3 -3
  87. package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
  88. package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
  89. package/docs/api/interfaces/TransitionOptions.html +2 -2
  90. package/docs/api/interfaces/TransitionOutcome.html +1 -1
  91. package/docs/api/modules.html +1 -1
  92. package/docs/api/types/ConnectionRequestDisposition.html +1 -1
  93. package/docs/api/types/FetchOrComputeFencedResult.html +1 -0
  94. package/docs/api/types/FetchOrComputeResult.html +1 -1
  95. package/docs/api/types/FlowPolicyBackoffKind.html +1 -0
  96. package/docs/api/types/FlowStateMode.html +1 -1
  97. package/docs/api/types/FlowStatePolicyLike.html +1 -1
  98. package/docs/api/types/ManagementPairs.html +1 -1
  99. package/docs/api/types/RequestDisposition.html +1 -1
  100. package/docs/api/types/SearchStateMeta.html +1 -1
  101. package/docs/api/types/TopKReserveOptions.html +1 -1
  102. package/docs/api/variables/COMMAND_OPCODES.html +1 -1
  103. package/docs/api/variables/FERRICSTORE_MINIMUM_SERVER_VERSION.html +2 -0
  104. package/docs/api/variables/FERRICSTORE_NATIVE_PROTOCOL_VERSION.html +2 -0
  105. package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +2 -0
  106. package/docs/api/variables/MAX_FLOW_POLICY_GENERATION.html +1 -0
  107. package/package.json +6 -1
  108. package/docs/api/interfaces/FetchOrComputeFencedResult.html +0 -9
  109. package/docs/api/types/FetchOrComputeComputeResult.html +0 -1
  110. package/docs/api/variables/COMPACT_RESPONSE_OPCODES.html +0 -2
package/dist/index.cjs CHANGED
@@ -33,11 +33,13 @@ __export(index_exports, {
33
33
  BitmapStore: () => BitmapStore,
34
34
  BloomFilterStore: () => BloomFilterStore,
35
35
  COMMAND_OPCODES: () => COMMAND_OPCODES,
36
- COMPACT_RESPONSE_OPCODES: () => COMPACT_RESPONSE_OPCODES,
37
36
  ClaimHydrationError: () => ClaimHydrationError,
38
37
  ConnectionClosedError: () => ConnectionClosedError,
39
38
  CountMinSketchStore: () => CountMinSketchStore,
40
39
  CuckooFilterStore: () => CuckooFilterStore,
40
+ FERRICSTORE_MINIMUM_SERVER_VERSION: () => FERRICSTORE_MINIMUM_SERVER_VERSION,
41
+ FERRICSTORE_NATIVE_PROTOCOL_VERSION: () => FERRICSTORE_NATIVE_PROTOCOL_VERSION,
42
+ FERRICSTORE_SDK_VERSION: () => FERRICSTORE_SDK_VERSION,
41
43
  FerricStoreClient: () => FerricStoreClient,
42
44
  FerricStoreError: () => FerricStoreError,
43
45
  FlowAlreadyExistsError: () => FlowAlreadyExistsError,
@@ -54,6 +56,7 @@ __export(index_exports, {
54
56
  ListStore: () => ListStore,
55
57
  LockHeldError: () => LockHeldError,
56
58
  LockNotOwnedError: () => LockNotOwnedError,
59
+ MAX_FLOW_POLICY_GENERATION: () => MAX_FLOW_POLICY_GENERATION,
57
60
  NativeAdapter: () => NativeAdapter,
58
61
  OverloadedError: () => OverloadedError,
59
62
  Queue: () => Queue,
@@ -68,6 +71,7 @@ __export(index_exports, {
68
71
  SetStore: () => SetStore,
69
72
  SortedSetStore: () => SortedSetStore,
70
73
  StaleLeaseError: () => StaleLeaseError,
74
+ StalePolicyGenerationError: () => StalePolicyGenerationError,
71
75
  StreamStore: () => StreamStore,
72
76
  TDigestStore: () => TDigestStore,
73
77
  TopKStore: () => TopKStore,
@@ -100,16 +104,22 @@ function assertSafeVariadicDispatch(argumentCount, arrayCapability) {
100
104
  // src/native-adapter.ts
101
105
  var import_node_net3 = require("net");
102
106
  var import_node_tls3 = require("tls");
103
- var import_node_buffer22 = require("buffer");
107
+ var import_node_buffer26 = require("buffer");
104
108
 
105
109
  // src/errors.ts
106
110
  var FerricStoreError = class extends Error {
107
111
  code = "ferricstore_error";
108
112
  raw;
113
+ retryable;
114
+ safeToRetry;
115
+ retryAfterMs;
109
116
  constructor(message, options = {}) {
110
117
  super(message, { cause: options.cause });
111
118
  this.name = new.target.name;
112
119
  this.raw = options.raw;
120
+ this.retryable = options.retryable ?? structuredBooleanField(options.raw, "retryable");
121
+ this.safeToRetry = options.safeToRetry ?? structuredBooleanField(options.raw, "safe_to_retry");
122
+ this.retryAfterMs = options.retryAfterMs ?? structuredIntegerField(options.raw, "retry_after_ms");
113
123
  }
114
124
  };
115
125
  var ConnectionClosedError = class extends FerricStoreError {
@@ -142,6 +152,9 @@ var FlowWrongStateError = class extends FerricStoreError {
142
152
  var StaleLeaseError = class extends FerricStoreError {
143
153
  code = "stale_lease";
144
154
  };
155
+ var StalePolicyGenerationError = class extends FerricStoreError {
156
+ code = "stale_policy_generation";
157
+ };
145
158
  var FlowAlreadyExistsError = class extends FerricStoreError {
146
159
  code = "flow_already_exists";
147
160
  };
@@ -156,11 +169,14 @@ var InvalidCommandError = class extends FerricStoreError {
156
169
  };
157
170
  var OverloadedError = class extends FerricStoreError {
158
171
  code = "overloaded";
159
- retryAfterMs;
160
172
  reason;
161
173
  constructor(message, options = {}) {
162
- super(message, options);
163
- this.retryAfterMs = options.retryAfterMs;
174
+ const local = options.raw == null;
175
+ super(message, {
176
+ ...options,
177
+ retryable: options.retryable ?? (local ? true : void 0),
178
+ safeToRetry: options.safeToRetry ?? (local ? true : void 0)
179
+ });
164
180
  this.reason = options.reason;
165
181
  }
166
182
  };
@@ -178,15 +194,21 @@ function classifyServerError(message, raw, cause, status) {
178
194
  const lower = message.toLowerCase();
179
195
  const structuredCode = structuredStringField(raw, "code");
180
196
  const code = structuredCode?.toLowerCase();
197
+ const retry2 = {
198
+ retryable: structuredBooleanField(raw, "retryable"),
199
+ safeToRetry: structuredBooleanField(raw, "safe_to_retry"),
200
+ retryAfterMs: structuredIntegerField(raw, "retry_after_ms") ?? intField(lower, "retry_after_ms")
201
+ };
181
202
  if (isRerouteStatus(status) || code === "reroute") {
182
- return new RerouteError(message, { cause, raw });
203
+ return new RerouteError(message, { cause, raw, ...definedRetryMetadata(retry2) });
183
204
  }
184
205
  if (isBusyStatus(status) || isOverloadCode(structuredCode) || overloadMessage(lower)) {
185
206
  return new OverloadedError(message, {
186
207
  cause,
187
208
  raw,
209
+ ...definedRetryMetadata(retry2),
188
210
  reason: structuredStringField(raw, "reason") ?? structuredCode ?? stringField(lower, "reason"),
189
- retryAfterMs: structuredIntegerField(raw, "retry_after_ms") ?? intField(lower, "retry_after_ms")
211
+ retryAfterMs: retry2.retryAfterMs
190
212
  });
191
213
  }
192
214
  if (code === "flow_already_exists" || lower.includes("flow") && lower.includes("already exists")) {
@@ -198,6 +220,9 @@ function classifyServerError(message, raw, cause, status) {
198
220
  if (code === "stale_lease" || code === "stale_flow_lease" || lower.includes("stale flow lease") || lower.includes("stale lease") || lower.includes("stale token")) {
199
221
  return new StaleLeaseError(message, { cause, raw });
200
222
  }
223
+ if (code === "stale_generation" || code === "stale_policy_generation" || code === "stale_flow_policy_generation" || lower.includes("stale flow policy generation") || lower.includes("stale policy generation")) {
224
+ return new StalePolicyGenerationError(message, { cause, raw });
225
+ }
201
226
  if (code === "flow_not_found" || lower.includes("flow") && (lower.includes("not found") || lower.includes("does not exist"))) {
202
227
  return new FlowNotFoundError(message, { cause, raw });
203
228
  }
@@ -210,7 +235,7 @@ function classifyServerError(message, raw, cause, status) {
210
235
  if (code === "invalid_command" || lower.includes("wrong number of arguments") || lower.includes("syntax error")) {
211
236
  return new InvalidCommandError(message, { cause, raw });
212
237
  }
213
- return new FerricStoreError(message, { cause, raw });
238
+ return new FerricStoreError(message, { cause, raw, ...definedRetryMetadata(retry2) });
214
239
  }
215
240
  function mapException(error) {
216
241
  if (error instanceof FerricStoreError) {
@@ -250,8 +275,23 @@ function structuredIntegerField(raw, name) {
250
275
  if (typeof value === "bigint") {
251
276
  return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
252
277
  }
253
- const text2 = binaryText(value);
254
- return text2 == null ? void 0 : nonNegativeSafeIntegerText(text2);
278
+ const text3 = binaryText(value);
279
+ return text3 == null ? void 0 : nonNegativeSafeIntegerText(text3);
280
+ }
281
+ function structuredBooleanField(raw, name) {
282
+ const value = structuredField(raw, name);
283
+ if (typeof value === "boolean") return value;
284
+ const text3 = binaryText(value)?.toLowerCase();
285
+ if (text3 === "true" || text3 === "1") return true;
286
+ if (text3 === "false" || text3 === "0") return false;
287
+ return void 0;
288
+ }
289
+ function definedRetryMetadata(metadata) {
290
+ return {
291
+ ...metadata.retryable == null ? {} : { retryable: metadata.retryable },
292
+ ...metadata.safeToRetry == null ? {} : { safeToRetry: metadata.safeToRetry },
293
+ ...metadata.retryAfterMs == null ? {} : { retryAfterMs: metadata.retryAfterMs }
294
+ };
255
295
  }
256
296
  function nonNegativeSafeIntegerText(value) {
257
297
  if (!/^[0-9]+$/u.test(value)) return void 0;
@@ -424,9 +464,6 @@ function mapDenseResponseArray(value, transform) {
424
464
  return result;
425
465
  }
426
466
 
427
- // src/auto-partition.ts
428
- var import_node_buffer2 = require("buffer");
429
-
430
467
  // src/crc32.ts
431
468
  var import_node_buffer = require("buffer");
432
469
  var table = new Uint32Array(256);
@@ -444,12 +481,49 @@ function crc32(buffer) {
444
481
  }
445
482
  return (value ^ 4294967295) >>> 0;
446
483
  }
484
+ function crc32Utf8(text3, start = 0, end = text3.length) {
485
+ let value = 4294967295;
486
+ for (let index = start; index < end; index += 1) {
487
+ const first = text3.charCodeAt(index);
488
+ let point = first;
489
+ if (first >= 55296 && first <= 56319) {
490
+ const second = index + 1 < end ? text3.charCodeAt(index + 1) : 0;
491
+ if (second >= 56320 && second <= 57343) {
492
+ point = 65536 + (first - 55296 << 10) + second - 56320;
493
+ index += 1;
494
+ } else {
495
+ point = 65533;
496
+ }
497
+ } else if (first >= 56320 && first <= 57343) {
498
+ point = 65533;
499
+ }
500
+ if (point < 128) {
501
+ value = updateCrc32(value, point);
502
+ } else if (point < 2048) {
503
+ value = updateCrc32(value, 192 | point >>> 6);
504
+ value = updateCrc32(value, 128 | point & 63);
505
+ } else if (point < 65536) {
506
+ value = updateCrc32(value, 224 | point >>> 12);
507
+ value = updateCrc32(value, 128 | point >>> 6 & 63);
508
+ value = updateCrc32(value, 128 | point & 63);
509
+ } else {
510
+ value = updateCrc32(value, 240 | point >>> 18);
511
+ value = updateCrc32(value, 128 | point >>> 12 & 63);
512
+ value = updateCrc32(value, 128 | point >>> 6 & 63);
513
+ value = updateCrc32(value, 128 | point & 63);
514
+ }
515
+ }
516
+ return (value ^ 4294967295) >>> 0;
517
+ }
518
+ function updateCrc32(value, byte) {
519
+ return value >>> 8 ^ (table[(value ^ byte) & 255] ?? 0);
520
+ }
447
521
 
448
522
  // src/auto-partition.ts
449
523
  var AUTO_PARTITION_PREFIX = "__flow_auto__:";
450
524
  var AUTO_PARTITION_BUCKETS = 256;
451
525
  function autoPartitionKeyForId(id) {
452
- return `${AUTO_PARTITION_PREFIX}${crc32(import_node_buffer2.Buffer.from(id)) % AUTO_PARTITION_BUCKETS}`;
526
+ return `${AUTO_PARTITION_PREFIX}${crc32Utf8(id) % AUTO_PARTITION_BUCKETS}`;
453
527
  }
454
528
 
455
529
  // src/flow-argument-helpers.ts
@@ -842,7 +916,7 @@ function coerceDiagnosticValue(value) {
842
916
  // src/native-connection.ts
843
917
  var import_node_net = __toESM(require("net"), 1);
844
918
  var import_node_tls = __toESM(require("tls"), 1);
845
- var import_node_buffer3 = require("buffer");
919
+ var import_node_buffer2 = require("buffer");
846
920
  function parseFerricUrl(value) {
847
921
  const url = new URL(value);
848
922
  if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
@@ -968,7 +1042,7 @@ function nonNegativeSafeInteger(value) {
968
1042
  if (typeof value === "bigint") {
969
1043
  return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
970
1044
  }
971
- const source = import_node_buffer3.Buffer.isBuffer(value) || value instanceof Uint8Array ? import_node_buffer3.Buffer.from(value).toString("utf8") : value;
1045
+ const source = import_node_buffer2.Buffer.isBuffer(value) || value instanceof Uint8Array ? import_node_buffer2.Buffer.from(value).toString("utf8") : value;
972
1046
  if (typeof source !== "number" && (typeof source !== "string" || !/^\d+$/u.test(source))) {
973
1047
  return void 0;
974
1048
  }
@@ -1202,10 +1276,10 @@ var NativeHeartbeat = class {
1202
1276
  };
1203
1277
 
1204
1278
  // src/protocol.ts
1205
- var import_node_buffer18 = require("buffer");
1279
+ var import_node_buffer22 = require("buffer");
1206
1280
 
1207
1281
  // src/command-grammar.ts
1208
- var import_node_buffer4 = require("buffer");
1282
+ var import_node_buffer3 = require("buffer");
1209
1283
 
1210
1284
  // src/command-metadata-flow.ts
1211
1285
  var flowStateIdCommands = /* @__PURE__ */ new Set([
@@ -1652,8 +1726,8 @@ var secondKeySubcommands = /* @__PURE__ */ new Map([
1652
1726
  ]);
1653
1727
  function commandToken(value) {
1654
1728
  if (typeof value === "string") return value.toUpperCase();
1655
- if (import_node_buffer4.Buffer.isBuffer(value) || value instanceof Uint8Array) {
1656
- return import_node_buffer4.Buffer.from(value).toString("utf8").toUpperCase();
1729
+ if (import_node_buffer3.Buffer.isBuffer(value) || value instanceof Uint8Array) {
1730
+ return import_node_buffer3.Buffer.from(value).toString("utf8").toUpperCase();
1657
1731
  }
1658
1732
  return void 0;
1659
1733
  }
@@ -1756,7 +1830,7 @@ function safeIntegerArgument(value) {
1756
1830
  if (typeof value === "bigint") {
1757
1831
  return value >= BigInt(Number.MIN_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
1758
1832
  }
1759
- const source = typeof value === "string" ? value : import_node_buffer4.Buffer.isBuffer(value) || value instanceof Uint8Array ? import_node_buffer4.Buffer.from(value).toString("utf8") : void 0;
1833
+ const source = typeof value === "string" ? value : import_node_buffer3.Buffer.isBuffer(value) || value instanceof Uint8Array ? import_node_buffer3.Buffer.from(value).toString("utf8") : void 0;
1760
1834
  if (source == null || !/^[+-]?\d+$/u.test(source)) return void 0;
1761
1835
  const parsed = BigInt(source);
1762
1836
  return parsed >= BigInt(Number.MIN_SAFE_INTEGER) && parsed <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(parsed) : void 0;
@@ -1809,10 +1883,10 @@ function hasOwnCommandArgument(args, index, end, section) {
1809
1883
  }
1810
1884
 
1811
1885
  // src/protocol-core.ts
1812
- var import_node_buffer8 = require("buffer");
1886
+ var import_node_buffer7 = require("buffer");
1813
1887
 
1814
1888
  // src/protocol-constants.ts
1815
- var import_node_buffer5 = require("buffer");
1889
+ var import_node_buffer4 = require("buffer");
1816
1890
 
1817
1891
  // src/native-protocol-manifest.json
1818
1892
  var native_protocol_manifest_default = {
@@ -2017,80 +2091,30 @@ var OPCODES = {
2017
2091
  flowFail: COMMAND_OPCODES["FLOW.FAIL"],
2018
2092
  flowValuePut: COMMAND_OPCODES["FLOW.VALUE.PUT"],
2019
2093
  flowValueMGet: COMMAND_OPCODES["FLOW.VALUE.MGET"],
2094
+ flowSignal: COMMAND_OPCODES["FLOW.SIGNAL"],
2020
2095
  flowCreateMany: COMMAND_OPCODES["FLOW.CREATE_MANY"],
2021
2096
  flowCompleteMany: COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
2022
2097
  flowTransitionMany: COMMAND_OPCODES["FLOW.TRANSITION_MANY"],
2023
2098
  flowRetryMany: COMMAND_OPCODES["FLOW.RETRY_MANY"],
2024
2099
  flowFailMany: COMMAND_OPCODES["FLOW.FAIL_MANY"],
2025
2100
  flowReclaim: COMMAND_OPCODES["FLOW.RECLAIM"],
2101
+ flowPolicySet: COMMAND_OPCODES["FLOW.POLICY.SET"],
2102
+ flowPolicyGet: COMMAND_OPCODES["FLOW.POLICY.GET"],
2026
2103
  flowSpawnChildren: COMMAND_OPCODES["FLOW.SPAWN_CHILDREN"],
2027
2104
  flowStepContinue: COMMAND_OPCODES["FLOW.STEP_CONTINUE"],
2028
2105
  flowStartAndClaim: COMMAND_OPCODES["FLOW.START_AND_CLAIM"],
2029
2106
  flowRunStepsMany: COMMAND_OPCODES["FLOW.RUN_STEPS_MANY"],
2030
2107
  flowSearch: COMMAND_OPCODES["FLOW.SEARCH"]
2031
2108
  };
2032
- var COMPACT_RESPONSE_OPCODES = {
2033
- flow_claim_jobs_v1: [COMMAND_OPCODES["FLOW.CLAIM_DUE"]],
2034
- flow_record_list_v1: [
2035
- COMMAND_OPCODES["FLOW.LIST"],
2036
- COMMAND_OPCODES["FLOW.TERMINALS"],
2037
- COMMAND_OPCODES["FLOW.FAILURES"],
2038
- COMMAND_OPCODES["FLOW.BY_PARENT"],
2039
- COMMAND_OPCODES["FLOW.BY_ROOT"],
2040
- COMMAND_OPCODES["FLOW.BY_CORRELATION"],
2041
- COMMAND_OPCODES["FLOW.STUCK"]
2042
- ],
2043
- flow_record_v1: [COMMAND_OPCODES["FLOW.GET"]],
2044
- kv_get_v1: [COMMAND_OPCODES.GET],
2045
- kv_mget_v1: [COMMAND_OPCODES.MGET, COMMAND_OPCODES["FLOW.VALUE.MGET"]],
2046
- ok_list_v1: [
2047
- COMMAND_OPCODES.SET,
2048
- COMMAND_OPCODES.MSET,
2049
- COMMAND_OPCODES["FLOW.CREATE_MANY"],
2050
- COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
2051
- COMMAND_OPCODES["FLOW.RETRY_MANY"],
2052
- COMMAND_OPCODES["FLOW.FAIL_MANY"],
2053
- COMMAND_OPCODES["FLOW.CANCEL_MANY"]
2054
- ],
2055
- pipeline_v1: [COMMAND_OPCODES.PIPELINE]
2056
- };
2057
- var COMPACT_KIND_FLOW_CLAIM_JOBS = 1 << 0;
2058
- var COMPACT_KIND_FLOW_RECORD = 1 << 1;
2059
- var COMPACT_KIND_FLOW_RECORD_LIST = 1 << 2;
2060
- var COMPACT_KIND_KV_GET = 1 << 3;
2061
- var COMPACT_KIND_KV_MGET = 1 << 4;
2062
- var COMPACT_KIND_OK_LIST = 1 << 5;
2063
- var COMPACT_KIND_PIPELINE = 1 << 6;
2064
- var compactResponseOpcodeKinds = new Uint8Array(
2065
- Math.max(...Object.values(COMPACT_RESPONSE_OPCODES).flat()) + 1
2066
- );
2067
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_CLAIM_JOBS, COMPACT_RESPONSE_OPCODES.flow_claim_jobs_v1);
2068
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_RECORD, COMPACT_RESPONSE_OPCODES.flow_record_v1);
2069
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_RECORD_LIST, COMPACT_RESPONSE_OPCODES.flow_record_list_v1);
2070
- registerCompactResponseOpcodes(COMPACT_KIND_KV_GET, COMPACT_RESPONSE_OPCODES.kv_get_v1);
2071
- registerCompactResponseOpcodes(COMPACT_KIND_KV_MGET, [
2072
- ...COMPACT_RESPONSE_OPCODES.kv_mget_v1,
2073
- COMMAND_OPCODES.PIPELINE
2074
- ]);
2075
- registerCompactResponseOpcodes(COMPACT_KIND_OK_LIST, [
2076
- ...COMPACT_RESPONSE_OPCODES.ok_list_v1,
2077
- COMMAND_OPCODES.PIPELINE
2078
- ]);
2079
- registerCompactResponseOpcodes(COMPACT_KIND_PIPELINE, COMPACT_RESPONSE_OPCODES.pipeline_v1);
2080
- function registerCompactResponseOpcodes(kind, opcodes) {
2081
- for (const opcode of opcodes) {
2082
- compactResponseOpcodeKinds[opcode] = (compactResponseOpcodeKinds[opcode] ?? 0) | kind;
2083
- }
2084
- }
2085
- function compactResponseOpcodeSupports(opcode, kind) {
2086
- return ((compactResponseOpcodeKinds[opcode] ?? 0) & kind) !== 0;
2087
- }
2088
2109
  var COMPACT_FLOW_CLAIM_JOBS = 128;
2089
2110
  var COMPACT_OK_LIST = 129;
2090
2111
  var COMPACT_KV_GET = 130;
2091
2112
  var COMPACT_KV_MGET = 131;
2092
2113
  var COMPACT_FLOW_RECORD = 132;
2093
2114
  var COMPACT_FLOW_RECORD_LIST = 133;
2115
+ var COMPACT_BINARY_LIST_LIST = 134;
2116
+ var COMPACT_BINARY_MAP_LIST = 135;
2117
+ var COMPACT_INTEGER_LIST = 136;
2094
2118
  var COMPACT_KV_MGET_FIXED = 137;
2095
2119
  var COMPACT_FLOW_CREATE_MANY_REQUEST = 144;
2096
2120
  var COMPACT_FLOW_CLAIM_DUE_REQUEST = 145;
@@ -2160,16 +2184,16 @@ var FLOW_RECORD_FIELD_KEYS = [
2160
2184
  ];
2161
2185
 
2162
2186
  // src/protocol-text.ts
2163
- var import_node_buffer6 = require("buffer");
2187
+ var import_node_buffer5 = require("buffer");
2164
2188
  function toBuffer(value) {
2165
- if (import_node_buffer6.Buffer.isBuffer(value)) return value;
2166
- if (value instanceof Uint8Array) return import_node_buffer6.Buffer.from(value);
2167
- if (typeof value === "string") return import_node_buffer6.Buffer.from(value);
2189
+ if (import_node_buffer5.Buffer.isBuffer(value)) return value;
2190
+ if (value instanceof Uint8Array) return import_node_buffer5.Buffer.from(value);
2191
+ if (typeof value === "string") return import_node_buffer5.Buffer.from(value);
2168
2192
  if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2169
- return import_node_buffer6.Buffer.from(String(value));
2193
+ return import_node_buffer5.Buffer.from(String(value));
2170
2194
  }
2171
- if (typeof value === "symbol") return import_node_buffer6.Buffer.from(value.description ?? value.toString());
2172
- if (value == null) return import_node_buffer6.Buffer.alloc(0);
2195
+ if (typeof value === "symbol") return import_node_buffer5.Buffer.from(value.description ?? value.toString());
2196
+ if (value == null) return import_node_buffer5.Buffer.alloc(0);
2173
2197
  throw new FerricStoreError(`unsupported binary command argument type: ${typeof value}`);
2174
2198
  }
2175
2199
  function asText(value) {
@@ -2177,14 +2201,14 @@ function asText(value) {
2177
2201
  }
2178
2202
  function optionalText(value) {
2179
2203
  if (value == null || value === "") return void 0;
2180
- if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || import_node_buffer6.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2204
+ if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || import_node_buffer5.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2181
2205
  return asText(value);
2182
2206
  }
2183
2207
  return void 0;
2184
2208
  }
2185
2209
  function commandTokenIs(value, expected) {
2186
2210
  if (typeof value === "string") return value.toUpperCase() === expected;
2187
- if (import_node_buffer6.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2211
+ if (import_node_buffer5.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2188
2212
  return asText(value).toUpperCase() === expected;
2189
2213
  }
2190
2214
  return false;
@@ -2198,7 +2222,7 @@ function commandNameIs(value, expected) {
2198
2222
  }
2199
2223
 
2200
2224
  // src/request-context.ts
2201
- var import_node_buffer7 = require("buffer");
2225
+ var import_node_buffer6 = require("buffer");
2202
2226
  var requestContextCommands = /* @__PURE__ */ new Set([
2203
2227
  "INVOCATION.CREATE",
2204
2228
  "INVOCATION.DEFINITION.GET",
@@ -2253,9 +2277,9 @@ function requestContextCommandToken(value) {
2253
2277
  const upper = value.toUpperCase();
2254
2278
  return isRequestContextCommand(upper) ? upper : void 0;
2255
2279
  }
2256
- if (import_node_buffer7.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2280
+ if (import_node_buffer6.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2257
2281
  if (value[0] !== 73 && value[0] !== 105) return void 0;
2258
- const command = import_node_buffer7.Buffer.from(value).toString("utf8").toUpperCase();
2282
+ const command = import_node_buffer6.Buffer.from(value).toString("utf8").toUpperCase();
2259
2283
  return isRequestContextCommand(command) ? command : void 0;
2260
2284
  }
2261
2285
  return void 0;
@@ -2286,12 +2310,12 @@ function requestContextText(context, fieldName) {
2286
2310
  return value;
2287
2311
  }
2288
2312
  function isPlainObject(value) {
2289
- return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer7.Buffer.isBuffer(value) && !(value instanceof Uint8Array);
2313
+ return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer6.Buffer.isBuffer(value) && !(value instanceof Uint8Array);
2290
2314
  }
2291
2315
  function commandToken2(value) {
2292
2316
  if (typeof value === "string") return value.toUpperCase();
2293
- if (import_node_buffer7.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2294
- return import_node_buffer7.Buffer.from(value).toString("utf8").toUpperCase();
2317
+ if (import_node_buffer6.Buffer.isBuffer(value) || value instanceof Uint8Array) {
2318
+ return import_node_buffer6.Buffer.from(value).toString("utf8").toUpperCase();
2295
2319
  }
2296
2320
  return "";
2297
2321
  }
@@ -2381,17 +2405,17 @@ function requestFrameTooLarge(maxBytes) {
2381
2405
  );
2382
2406
  }
2383
2407
  function binaryValueByteLength(value) {
2384
- if (import_node_buffer8.Buffer.isBuffer(value) || value instanceof Uint8Array) return value.byteLength;
2385
- if (typeof value === "string") return import_node_buffer8.Buffer.byteLength(value);
2408
+ if (import_node_buffer7.Buffer.isBuffer(value) || value instanceof Uint8Array) return value.byteLength;
2409
+ if (typeof value === "string") return import_node_buffer7.Buffer.byteLength(value);
2386
2410
  if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2387
- return import_node_buffer8.Buffer.byteLength(String(value));
2411
+ return import_node_buffer7.Buffer.byteLength(String(value));
2388
2412
  }
2389
- if (typeof value === "symbol") return import_node_buffer8.Buffer.byteLength(value.description ?? value.toString());
2413
+ if (typeof value === "symbol") return import_node_buffer7.Buffer.byteLength(value.description ?? value.toString());
2390
2414
  if (value == null) return 0;
2391
2415
  return toBuffer(value).byteLength;
2392
2416
  }
2393
2417
  function writeCustomPayload(output, offset, value, knownByteLength) {
2394
- if (import_node_buffer8.Buffer.isBuffer(value)) {
2418
+ if (import_node_buffer7.Buffer.isBuffer(value)) {
2395
2419
  value.copy(output, offset);
2396
2420
  return;
2397
2421
  }
@@ -2400,17 +2424,17 @@ function writeCustomPayload(output, offset, value, knownByteLength) {
2400
2424
  return;
2401
2425
  }
2402
2426
  if (value == null) return;
2403
- let text2;
2427
+ let text3;
2404
2428
  if (typeof value === "string") {
2405
- text2 = value;
2429
+ text3 = value;
2406
2430
  } else if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2407
- text2 = String(value);
2431
+ text3 = String(value);
2408
2432
  } else if (typeof value === "symbol") {
2409
- text2 = value.description ?? value.toString();
2433
+ text3 = value.description ?? value.toString();
2410
2434
  } else {
2411
2435
  throw new FerricStoreError(`unsupported binary command argument type: ${typeof value}`);
2412
2436
  }
2413
- output.write(text2, offset, knownByteLength ?? import_node_buffer8.Buffer.byteLength(text2), "utf8");
2437
+ output.write(text3, offset, knownByteLength ?? import_node_buffer7.Buffer.byteLength(text3), "utf8");
2414
2438
  }
2415
2439
  function compactBinaryEncodedLength(value) {
2416
2440
  return 4 + compactBinaryByteLength(value);
@@ -2423,10 +2447,10 @@ function compactBinaryByteLength(value) {
2423
2447
  return byteLength;
2424
2448
  }
2425
2449
  function isCompactBinaryArgument(value) {
2426
- return typeof value === "string" || import_node_buffer8.Buffer.isBuffer(value) || value instanceof Uint8Array;
2450
+ return typeof value === "string" || import_node_buffer7.Buffer.isBuffer(value) || value instanceof Uint8Array;
2427
2451
  }
2428
2452
  function isCompactBinaryScalar(value) {
2429
- return value == null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || typeof value === "symbol" || import_node_buffer8.Buffer.isBuffer(value) || value instanceof Uint8Array;
2453
+ return value == null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || typeof value === "symbol" || import_node_buffer7.Buffer.isBuffer(value) || value instanceof Uint8Array;
2430
2454
  }
2431
2455
  function compactOptionalBinaryEncodedLength(value) {
2432
2456
  return value == null ? 4 : compactBinaryEncodedLength(value);
@@ -2509,13 +2533,13 @@ function i64Arg(value) {
2509
2533
  }
2510
2534
  function boolArg(value) {
2511
2535
  if (typeof value === "boolean") return value;
2512
- const text2 = asText(value).toUpperCase();
2513
- return text2 === "1" || text2 === "TRUE" || text2 === "YES" || text2 === "ON";
2536
+ const text3 = asText(value).toUpperCase();
2537
+ return text3 === "1" || text3 === "TRUE" || text3 === "YES" || text3 === "ON";
2514
2538
  }
2515
2539
  function isBoolToken(value) {
2516
2540
  if (typeof value === "boolean") return true;
2517
- const text2 = asText(value).toUpperCase();
2518
- return text2 === "1" || text2 === "0" || text2 === "TRUE" || text2 === "FALSE" || text2 === "YES" || text2 === "NO" || text2 === "ON" || text2 === "OFF";
2541
+ const text3 = asText(value).toUpperCase();
2542
+ return text3 === "1" || text3 === "0" || text3 === "TRUE" || text3 === "FALSE" || text3 === "YES" || text3 === "NO" || text3 === "ON" || text3 === "OFF";
2519
2543
  }
2520
2544
  function independentMode(value) {
2521
2545
  if (value === true) return 2;
@@ -2557,7 +2581,23 @@ function setOwnValue2(target, key, value) {
2557
2581
  }
2558
2582
 
2559
2583
  // src/protocol-flow-options.ts
2560
- var import_node_buffer9 = require("buffer");
2584
+ var import_node_buffer8 = require("buffer");
2585
+
2586
+ // src/protocol-flow-option-value.ts
2587
+ function setFlowOptionValue(payload, field2, value, repeat) {
2588
+ if (!repeat || !Object.hasOwn(payload, field2)) {
2589
+ setOwnValue2(payload, field2, value);
2590
+ return;
2591
+ }
2592
+ const previous = payload[field2];
2593
+ setOwnValue2(
2594
+ payload,
2595
+ field2,
2596
+ Array.isArray(previous) ? [...previous, value] : [previous, value]
2597
+ );
2598
+ }
2599
+
2600
+ // src/protocol-flow-options.ts
2561
2601
  var OPTION_FIELDS = {
2562
2602
  CONSISTENT_PROJECTION: "consistent_projection",
2563
2603
  COUNT: "count",
@@ -2632,7 +2672,7 @@ function normalizeFlowSearchStateMeta(payload) {
2632
2672
  payload.state_meta = { [asText(state)]: stateMeta };
2633
2673
  }
2634
2674
  function isPlainObject2(value) {
2635
- return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer9.Buffer.isBuffer(value) && !(value instanceof Uint8Array);
2675
+ return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer8.Buffer.isBuffer(value) && !(value instanceof Uint8Array);
2636
2676
  }
2637
2677
  function parseFlowOptions(args, start, end, config) {
2638
2678
  const payload = {};
@@ -2726,8 +2766,9 @@ function parseFlowOptions(args, start, end, config) {
2726
2766
  case "TRANSITION_TO":
2727
2767
  case "TO_EVENT":
2728
2768
  case "EXPECT_STATE": {
2729
- const field2 = token.toLowerCase();
2730
- if (!putScalar(payload, field2, args, index + 1, end)) return void 0;
2769
+ const field2 = token === "IDEMPOTENCY" ? "idempotency_key" : token.toLowerCase();
2770
+ if (!hasOwnCommandArgument(args, index + 1, end, "Flow options")) return void 0;
2771
+ setFlowOptionValue(payload, field2, args[index + 1], token === "IF_STATE");
2731
2772
  index += 2;
2732
2773
  break;
2733
2774
  }
@@ -3017,6 +3058,7 @@ var FLOW_ADMIN_FIELDS = {
3017
3058
  LEASE_MS: "lease_ms",
3018
3059
  LEASE_TOKEN: "lease_token",
3019
3060
  LIMIT: "limit",
3061
+ MAX_ACTIVE_MS: "max_active_ms",
3020
3062
  MAX_FIRES: "max_fires",
3021
3063
  MAXBYTES: "payload_max_bytes",
3022
3064
  NOW: "now_ms",
@@ -3026,7 +3068,6 @@ var FLOW_ADMIN_FIELDS = {
3026
3068
  OVERLAP_RETRY_MS: "overlap_retry_ms",
3027
3069
  OVERWRITE: "overwrite",
3028
3070
  PARENT_FLOW_ID: "parent_flow_id",
3029
- PARENT_ID: "parent_id",
3030
3071
  PARTITION: "partition_key",
3031
3072
  PAYLOAD: "payload",
3032
3073
  PAYLOAD_MAX_BYTES: "payload_max_bytes",
@@ -3041,7 +3082,6 @@ var FLOW_ADMIN_FIELDS = {
3041
3082
  REV: "rev",
3042
3083
  RETURN: "return",
3043
3084
  ROOT_FLOW_ID: "root_flow_id",
3044
- ROOT_ID: "root_id",
3045
3085
  SCOPE: "scope",
3046
3086
  SHARD_ID: "shard_id",
3047
3087
  START_AT_MS: "start_at_ms",
@@ -3178,7 +3218,7 @@ function flowAdminPayload(opcode, args, leadingFields = []) {
3178
3218
  }
3179
3219
 
3180
3220
  // src/protocol-flow-compact.ts
3181
- var import_node_buffer11 = require("buffer");
3221
+ var import_node_buffer10 = require("buffer");
3182
3222
 
3183
3223
  // src/protocol-flow-compact-common.ts
3184
3224
  function compactManyRequestTag(value, regularTag, okOnSuccessTag) {
@@ -3287,7 +3327,7 @@ function parseNamedCountMap(values, start) {
3287
3327
  }
3288
3328
 
3289
3329
  // src/protocol-flow-compact-transition.ts
3290
- var import_node_buffer10 = require("buffer");
3330
+ var import_node_buffer9 = require("buffer");
3291
3331
  function compactFlowTransitionManyPayload(partition, fromState, toState, rawItems, mixed, options, maxBodyBytes) {
3292
3332
  if ("payload" in options || "priority" in options || "state_meta" in options || "values" in options || "value_refs" in options || "drop_values" in options || "override_values" in options || "attributes_merge" in options || "attributes_delete" in options || typeof options.now_ms !== "number" || !Number.isSafeInteger(options.now_ms) || options.now_ms < 0) {
3293
3333
  return void 0;
@@ -3339,7 +3379,7 @@ function compactFlowTransitionManyPayload(partition, fromState, toState, rawItem
3339
3379
  total += 4 + idLength + 4 + itemPartitionLength + 8 + 4 + leaseLength;
3340
3380
  }
3341
3381
  assertCompactPayloadFits(total, maxBodyBytes);
3342
- const out = import_node_buffer10.Buffer.allocUnsafe(total);
3382
+ const out = import_node_buffer9.Buffer.allocUnsafe(total);
3343
3383
  let offset = 0;
3344
3384
  out.writeUInt8(tag, offset);
3345
3385
  offset += 1;
@@ -3397,7 +3437,7 @@ function compactFlowCreateManyPayload(partition, rawItems, mixed, auto, options,
3397
3437
  }
3398
3438
  assertCompactPayloadFits(total, maxBodyBytes);
3399
3439
  const tag = mixed ? COMPACT_FLOW_CREATE_MANY_MIXED_REQUEST : auto ? COMPACT_FLOW_CREATE_MANY_REQUEST : COMPACT_FLOW_CREATE_MANY_PARTITION_REQUEST;
3400
- const out = import_node_buffer11.Buffer.allocUnsafe(total);
3440
+ const out = import_node_buffer10.Buffer.allocUnsafe(total);
3401
3441
  let offset = 0;
3402
3442
  out.writeUInt8(tag, offset++);
3403
3443
  offset = writeBinary(out, offset, toBuffer(options.type));
@@ -3442,7 +3482,7 @@ function compactFlowClaimDuePayload(type, options, maxBodyBytes) {
3442
3482
  total += 1;
3443
3483
  }
3444
3484
  assertCompactPayloadFits(total, maxBodyBytes);
3445
- const out = import_node_buffer11.Buffer.allocUnsafe(total);
3485
+ const out = import_node_buffer10.Buffer.allocUnsafe(total);
3446
3486
  let offset = 0;
3447
3487
  out.writeUInt8(COMPACT_FLOW_CLAIM_DUE_REQUEST, offset++);
3448
3488
  offset = writeBinary(out, offset, toBuffer(type));
@@ -3496,7 +3536,7 @@ function compactFlowCompleteManyPayload(opcode, partition, rawItems, mixed, auto
3496
3536
  total += compactBinaryEncodedLength(rawItems[rawIndex]) + compactOptionalBinaryEncodedLength(mixed ? rawItems[rawIndex + 1] : null) + compactBinaryEncodedLength(rawItems[rawIndex + width - 2]) + 8;
3497
3537
  }
3498
3538
  assertCompactPayloadFits(total, maxBodyBytes);
3499
- const out = import_node_buffer11.Buffer.allocUnsafe(total);
3539
+ const out = import_node_buffer10.Buffer.allocUnsafe(total);
3500
3540
  let offset = 0;
3501
3541
  out.writeUInt8(
3502
3542
  returnMode === "OK_ON_SUCCESS" ? COMPACT_FLOW_COMPLETE_MANY_OK_REQUEST : COMPACT_FLOW_COMPLETE_MANY_REQUEST,
@@ -3543,7 +3583,7 @@ function compactFlowRetryManyPayload(partition, rawItems, mixed, auto, options,
3543
3583
  total += compactBinaryEncodedLength(rawItems[rawIndex]) + compactOptionalBinaryEncodedLength(mixed ? rawItems[rawIndex + 1] : null) + compactBinaryEncodedLength(rawItems[rawIndex + width - 2]) + 8;
3544
3584
  }
3545
3585
  assertCompactPayloadFits(total, maxBodyBytes);
3546
- const out = import_node_buffer11.Buffer.allocUnsafe(total);
3586
+ const out = import_node_buffer10.Buffer.allocUnsafe(total);
3547
3587
  let offset = 0;
3548
3588
  out.writeUInt8(
3549
3589
  returnMode === "OK_ON_SUCCESS" ? COMPACT_FLOW_RETRY_MANY_OK_REQUEST : COMPACT_FLOW_RETRY_MANY_REQUEST,
@@ -3631,7 +3671,7 @@ function compactFlowCancelManyPayload(args, maxBodyBytes) {
3631
3671
  total += 4 + idLength + 4 + itemPartitionLength + 8;
3632
3672
  }
3633
3673
  assertCompactPayloadFits(total, maxBodyBytes);
3634
- const out = import_node_buffer11.Buffer.allocUnsafe(total);
3674
+ const out = import_node_buffer10.Buffer.allocUnsafe(total);
3635
3675
  let offset = 0;
3636
3676
  out.writeUInt8(tag, offset);
3637
3677
  offset += 1;
@@ -3667,7 +3707,7 @@ function isOptionalSafeIntegerNumber(value) {
3667
3707
  }
3668
3708
 
3669
3709
  // src/protocol-flow-compact-single.ts
3670
- var import_node_buffer12 = require("buffer");
3710
+ var import_node_buffer11 = require("buffer");
3671
3711
  function compactFlowValueMGetPayload(refs, maxBytes, maxBodyBytes) {
3672
3712
  if (refs.length > 4294967295 || maxBytes != null && (typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 0)) return void 0;
3673
3713
  const minimum = 13 + refs.length * 4;
@@ -3684,7 +3724,7 @@ function compactFlowValueMGetPayload(refs, maxBytes, maxBodyBytes) {
3684
3724
  total += 4 + byteLength;
3685
3725
  }
3686
3726
  assertCompactPayloadFits(total, maxBodyBytes);
3687
- const out = import_node_buffer12.Buffer.allocUnsafe(total);
3727
+ const out = import_node_buffer11.Buffer.allocUnsafe(total);
3688
3728
  let offset = 0;
3689
3729
  out.writeUInt8(COMPACT_FLOW_VALUE_MGET_REQUEST, offset);
3690
3730
  offset += 1;
@@ -3727,7 +3767,7 @@ function compactFlowListPayload(args, maxBodyBytes) {
3727
3767
  const stateLength = state == null ? 0 : compactBinaryByteLength(state);
3728
3768
  const total = 1 + 4 + typeLength + 4 + stateLength + 8 + 1;
3729
3769
  assertCompactPayloadFits(total, maxBodyBytes);
3730
- const out = import_node_buffer12.Buffer.allocUnsafe(total);
3770
+ const out = import_node_buffer11.Buffer.allocUnsafe(total);
3731
3771
  let offset = 0;
3732
3772
  out.writeUInt8(COMPACT_FLOW_LIST_REQUEST, offset);
3733
3773
  offset += 1;
@@ -4170,6 +4210,7 @@ function flowSpawnChildrenPayload(args) {
4170
4210
  "FROM_STATE",
4171
4211
  "ON_CHILD_FAILED",
4172
4212
  "ON_PARENT_CLOSED",
4213
+ "MAX_ACTIVE_MS",
4173
4214
  "VALUE",
4174
4215
  "VALUE_REF"
4175
4216
  ])
@@ -4200,6 +4241,185 @@ function flowSearchPayload(args) {
4200
4241
  return { opcode: OPCODES.flowSearch, payload: { type: args[0], ...payload } };
4201
4242
  }
4202
4243
 
4244
+ // src/protocol-flow-signal.ts
4245
+ function flowSignalPayload(args) {
4246
+ if (args.length < 3) return void 0;
4247
+ const options = parseFlowOptions(args, 1, args.length, {
4248
+ allowed: /* @__PURE__ */ new Set([
4249
+ "SIGNAL",
4250
+ "PARTITION",
4251
+ "IDEMPOTENCY",
4252
+ "IF_STATE",
4253
+ "TRANSITION_TO",
4254
+ "RUN_AT",
4255
+ "NOW",
4256
+ "VALUE",
4257
+ "VALUE_REF",
4258
+ "DROP_VALUE",
4259
+ "OVERRIDE_VALUE"
4260
+ ]),
4261
+ required: /* @__PURE__ */ new Set(["SIGNAL"])
4262
+ });
4263
+ if (options == null) return void 0;
4264
+ return { opcode: OPCODES.flowSignal, payload: { id: args[0], ...options } };
4265
+ }
4266
+
4267
+ // src/protocol-flow-policy.ts
4268
+ var import_node_buffer12 = require("buffer");
4269
+ var BACKOFF_FIELDS = {
4270
+ BASE_MS: "base_ms",
4271
+ MAX_MS: "max_ms",
4272
+ JITTER_PCT: "jitter_pct"
4273
+ };
4274
+ var RETENTION_FIELDS = {
4275
+ RETENTION_TTL: "ttl_ms",
4276
+ RETENTION_TTL_MS: "ttl_ms",
4277
+ HISTORY_MAX_EVENTS: "history_max_events"
4278
+ };
4279
+ function flowPolicySetPayload(args) {
4280
+ if (!hasOwnCommandArgument(args, 0, args.length, "Flow policy arguments")) return void 0;
4281
+ if (!isBinaryArgument(args[0])) return void 0;
4282
+ const payload = { type: args[0] };
4283
+ const states = {};
4284
+ let target = payload;
4285
+ for (let index = 1; index < args.length; index += 2) {
4286
+ if (!hasOwnCommandArgument(args, index, args.length, "Flow policy arguments") || !hasOwnCommandArgument(args, index + 1, args.length, "Flow policy arguments")) return void 0;
4287
+ const token = optionalText(args[index])?.toUpperCase();
4288
+ const value = args[index + 1];
4289
+ if (token == null || value == null) return void 0;
4290
+ if (token === "STATE") {
4291
+ if (!isBinaryArgument(value)) return void 0;
4292
+ const state = asText(value);
4293
+ target = {};
4294
+ setOwnValue2(states, state, target);
4295
+ setOwnValue2(payload, "states", states);
4296
+ continue;
4297
+ }
4298
+ if (token === "MODE") {
4299
+ if (!isBinaryArgument(value)) return void 0;
4300
+ const mode = optionalText(value)?.toLowerCase();
4301
+ if (target === payload || mode !== "fifo" && mode !== "parallel") return void 0;
4302
+ setOwnValue2(target, "mode", mode);
4303
+ continue;
4304
+ }
4305
+ if (token === "EXPECTED_GENERATION") {
4306
+ if (target !== payload) return void 0;
4307
+ const generation = integerOption(value);
4308
+ if (generation == null || generation < 0) return void 0;
4309
+ setOwnValue2(payload, "expected_generation", generation);
4310
+ continue;
4311
+ }
4312
+ if (token === "REPLACE") {
4313
+ if (target !== payload || !isBoolToken(value)) return void 0;
4314
+ setOwnValue2(payload, "replace", boolArg(value));
4315
+ continue;
4316
+ }
4317
+ if (token === "MAX_ACTIVE_MS") {
4318
+ if (target !== payload) return void 0;
4319
+ const maxActiveMs = maxActiveOption(value);
4320
+ if (maxActiveMs == null) return void 0;
4321
+ setOwnValue2(payload, "max_active_ms", maxActiveMs);
4322
+ continue;
4323
+ }
4324
+ if (token === "INDEXED_STATE_META") {
4325
+ if (target !== payload || !isBinaryArgument(value)) return void 0;
4326
+ setOwnValue2(payload, "indexed_state_meta", value);
4327
+ continue;
4328
+ }
4329
+ if (token === "INDEXED_ATTRIBUTES") {
4330
+ if (target !== payload) return void 0;
4331
+ const names = indexedAttributes(value);
4332
+ if (names == null) return void 0;
4333
+ setOwnValue2(payload, "indexed_attributes", names);
4334
+ continue;
4335
+ }
4336
+ if (token === "MAX_RETRIES") {
4337
+ const parsed = integerOption(value);
4338
+ if (parsed == null) return void 0;
4339
+ setOwnValue2(objectField(target, "retry"), "max_retries", parsed);
4340
+ continue;
4341
+ }
4342
+ if (token === "EXHAUSTED_TO") {
4343
+ if (!isBinaryArgument(value)) return void 0;
4344
+ setOwnValue2(objectField(target, "retry"), "exhausted_to", value);
4345
+ continue;
4346
+ }
4347
+ const backoffField = BACKOFF_FIELDS[token];
4348
+ if (backoffField != null) {
4349
+ const parsed = integerOption(value);
4350
+ if (parsed == null) return void 0;
4351
+ setOwnValue2(objectField(objectField(target, "retry"), "backoff"), backoffField, parsed);
4352
+ continue;
4353
+ }
4354
+ if (token === "BACKOFF") {
4355
+ if (!isBinaryArgument(value)) return void 0;
4356
+ const kind = optionalText(value)?.toLowerCase();
4357
+ if (kind == null) return void 0;
4358
+ setOwnValue2(objectField(objectField(target, "retry"), "backoff"), "kind", kind);
4359
+ continue;
4360
+ }
4361
+ const retentionField = RETENTION_FIELDS[token];
4362
+ if (retentionField != null) {
4363
+ if (target === payload) return void 0;
4364
+ const parsed = integerOption(value);
4365
+ if (parsed == null) return void 0;
4366
+ setOwnValue2(objectField(target, "retention"), retentionField, parsed);
4367
+ continue;
4368
+ }
4369
+ return void 0;
4370
+ }
4371
+ return { opcode: OPCODES.flowPolicySet, payload };
4372
+ }
4373
+ function flowPolicyGetPayload(args) {
4374
+ if (!hasOwnCommandArgument(args, 0, args.length, "Flow policy arguments")) return void 0;
4375
+ if (!isBinaryArgument(args[0])) return void 0;
4376
+ if (args.length !== 1 && args.length !== 3) return void 0;
4377
+ const payload = { type: args[0] };
4378
+ if (args.length === 3) {
4379
+ if (!hasOwnCommandArgument(args, 1, args.length, "Flow policy arguments") || !hasOwnCommandArgument(args, 2, args.length, "Flow policy arguments") || optionalText(args[1])?.toUpperCase() !== "STATE" || !isBinaryArgument(args[2])) return void 0;
4380
+ setOwnValue2(payload, "state", args[2]);
4381
+ }
4382
+ return { opcode: OPCODES.flowPolicyGet, payload };
4383
+ }
4384
+ function integerOption(value) {
4385
+ try {
4386
+ return safeIntegerNumberArg(value);
4387
+ } catch {
4388
+ return void 0;
4389
+ }
4390
+ }
4391
+ function maxActiveOption(value) {
4392
+ if (isBinaryArgument(value) && asText(value).toLowerCase() === "infinity") {
4393
+ return "infinity";
4394
+ }
4395
+ return integerOption(value);
4396
+ }
4397
+ function isBinaryArgument(value) {
4398
+ return typeof value === "string" || import_node_buffer12.Buffer.isBuffer(value) || value instanceof Uint8Array;
4399
+ }
4400
+ function objectField(target, name) {
4401
+ const existing = Object.hasOwn(target, name) ? target[name] : void 0;
4402
+ if (typeof existing === "object" && existing != null && !Array.isArray(existing)) {
4403
+ return existing;
4404
+ }
4405
+ const value = {};
4406
+ setOwnValue2(target, name, value);
4407
+ return value;
4408
+ }
4409
+ function indexedAttributes(value) {
4410
+ if (Array.isArray(value)) {
4411
+ return value.every((item) => typeof item === "string") ? value : void 0;
4412
+ }
4413
+ const source = optionalText(value);
4414
+ if (source == null) return void 0;
4415
+ try {
4416
+ const parsed = JSON.parse(source);
4417
+ return Array.isArray(parsed) && parsed.every((item) => typeof item === "string") ? parsed : void 0;
4418
+ } catch {
4419
+ return void 0;
4420
+ }
4421
+ }
4422
+
4203
4423
  // src/protocol-connection-policy.ts
4204
4424
  function assertCommandDoesNotRequirePinnedConnection(args) {
4205
4425
  const outer = commandName(args[0]);
@@ -4391,7 +4611,7 @@ function compactArgumentsEligible(args, sparseMessage) {
4391
4611
  }
4392
4612
  return true;
4393
4613
  }
4394
- function compactPipelineEligible(commands, commandName4, argumentCount) {
4614
+ function compactPipelineEligible(commands, commandName5, argumentCount) {
4395
4615
  for (let index = 0; index < commands.length; index += 1) {
4396
4616
  const command = commands[index];
4397
4617
  if (!Object.hasOwn(commands, index) || !Array.isArray(command)) {
@@ -4403,7 +4623,7 @@ function compactPipelineEligible(commands, commandName4, argumentCount) {
4403
4623
  throw new TypeError("pipeline command arguments must be dense");
4404
4624
  }
4405
4625
  }
4406
- if (!commandNameIs(command[0], commandName4)) return false;
4626
+ if (!commandNameIs(command[0], commandName5)) return false;
4407
4627
  for (let argumentIndex = 1; argumentIndex < argumentCount; argumentIndex += 1) {
4408
4628
  if (!isCompactBinaryScalar(command[argumentIndex])) return false;
4409
4629
  }
@@ -4566,15 +4786,20 @@ function fetchOrComputeCommand(args) {
4566
4786
  };
4567
4787
  }
4568
4788
  function fetchOrComputeResultCommand(args) {
4569
- if (args.length !== 4 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1])) return void 0;
4789
+ if (args.length !== 4 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1])) {
4790
+ throw new FerricStoreError("FETCH_OR_COMPUTE_RESULT requires key, ownership token, value, and ttl_ms");
4791
+ }
4570
4792
  const ttlMs = positiveSafeInteger2(args[3]);
4571
- return ttlMs == null ? void 0 : {
4793
+ if (ttlMs == null) throw new FerricStoreError("FETCH_OR_COMPUTE_RESULT ttl_ms must be positive");
4794
+ return {
4572
4795
  opcode: COMMAND_OPCODES.FETCH_OR_COMPUTE_RESULT,
4573
4796
  payload: { key: args[0], token: args[1], ttl_ms: ttlMs, value: args[2] }
4574
4797
  };
4575
4798
  }
4576
4799
  function fetchOrComputeErrorCommand(args) {
4577
- if (args.length !== 3 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1]) || !isBinaryCommandArgument(args[2])) return void 0;
4800
+ if (args.length !== 3 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1]) || !isBinaryCommandArgument(args[2])) {
4801
+ throw new FerricStoreError("FETCH_OR_COMPUTE_ERROR requires key, ownership token, and message");
4802
+ }
4578
4803
  return {
4579
4804
  opcode: COMMAND_OPCODES.FETCH_OR_COMPUTE_ERROR,
4580
4805
  payload: { key: args[0], message: args[2], token: args[1] }
@@ -4931,58 +5156,458 @@ function requireAvailable(data, offset, size) {
4931
5156
  }
4932
5157
  }
4933
5158
 
4934
- // src/protocol-response.ts
5159
+ // src/key-slot-validation.ts
4935
5160
  var import_node_buffer17 = require("buffer");
4936
5161
 
4937
- // src/protocol-compact-response.ts
5162
+ // src/topology-utilities.ts
4938
5163
  var import_node_buffer16 = require("buffer");
4939
- function tryDecodeCompactResponse(opcode, body, hints) {
4940
- if (body.byteLength === 0) return { found: false, value: void 0 };
4941
- const budget = { remainingItems: DEFAULT_MAX_VALUE_ITEMS };
4942
- const tag = body.readUInt8(0);
4943
- if (tag === COMPACT_OK_LIST && isOkListOpcode(opcode)) {
4944
- const values = decodeCompactOkList(body, budget);
4945
- return { found: true, value: opcode === OPCODES.pipeline ? values : values.length === 1 ? "OK" : values };
4946
- }
4947
- if (tag === COMPACT_KV_GET && compactResponseOpcodeSupports(opcode, COMPACT_KIND_KV_GET)) {
4948
- return { found: true, value: decodeCompactKvGet(body) };
5164
+ function isPlainObject3(value) {
5165
+ return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer16.Buffer.isBuffer(value);
5166
+ }
5167
+ function getField(source, ...keys) {
5168
+ if (source instanceof Map) {
5169
+ for (const key of keys) {
5170
+ if (source.has(key)) return source.get(key);
5171
+ const bufferKey = import_node_buffer16.Buffer.from(key);
5172
+ for (const [itemKey, value] of source.entries()) {
5173
+ if (import_node_buffer16.Buffer.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
5174
+ }
5175
+ }
5176
+ return void 0;
4949
5177
  }
4950
- if ((tag === COMPACT_KV_MGET || tag === COMPACT_KV_MGET_FIXED) && compactResponseOpcodeSupports(opcode, COMPACT_KIND_KV_MGET)) {
4951
- return {
4952
- found: true,
4953
- value: tag === COMPACT_KV_MGET ? decodeCompactMget(body, budget) : decodeCompactMgetFixed(body, budget)
4954
- };
5178
+ if (isPlainObject3(source)) {
5179
+ for (const key of keys) {
5180
+ if (Object.hasOwn(source, key)) return source[key];
5181
+ }
4955
5182
  }
4956
- if (tag === COMPACT_PIPELINE_RESPONSE && compactResponseOpcodeSupports(opcode, COMPACT_KIND_PIPELINE)) {
4957
- return { found: true, value: decodeCompactPipeline(body, budget, hints.pipelineClaimModes) };
5183
+ return void 0;
5184
+ }
5185
+ function textOrUndefined(value) {
5186
+ if (typeof value === "string") return value;
5187
+ if (import_node_buffer16.Buffer.isBuffer(value)) return value.toString("utf8");
5188
+ return void 0;
5189
+ }
5190
+ function numberOrUndefined(value) {
5191
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
5192
+ }
5193
+ function nativeEventName(event) {
5194
+ return textOrUndefined(getField(event.value, "event"))?.toUpperCase();
5195
+ }
5196
+ function endpointFromRange(item) {
5197
+ const endpointValue = getField(item, "endpoint");
5198
+ if (endpointValue != null && !isPlainObject3(endpointValue) && !(endpointValue instanceof Map)) {
5199
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4958
5200
  }
4959
- if (tag === COMPACT_FLOW_CLAIM_JOBS && compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_CLAIM_JOBS)) {
4960
- return { found: true, value: decodeCompactClaimJobs(body, budget, hints.compactClaimMode) };
5201
+ const raw = endpointValue ?? item;
5202
+ const host = textOrUndefined(getField(raw, "host", "native_host"));
5203
+ const nativePort = numberOrUndefined(getField(raw, "native_port"));
5204
+ if (host == null || !validEndpointHost(host) || nativePort == null || nativePort <= 0 || nativePort > 65535) {
5205
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4961
5206
  }
4962
- if (tag === COMPACT_FLOW_RECORD && compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_RECORD)) {
4963
- const read = readCompactFlowRecord(body, 0, budget);
4964
- if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record bytes");
4965
- return { found: true, value: read.value };
5207
+ const nativeTlsPortValue = getField(raw, "native_tls_port");
5208
+ const nativeTlsPort = numberOrUndefined(nativeTlsPortValue);
5209
+ if (nativeTlsPortValue != null && (nativeTlsPort == null || nativeTlsPort <= 0 || nativeTlsPort > 65535)) {
5210
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4966
5211
  }
4967
- if (tag === COMPACT_FLOW_RECORD_LIST && isFlowRecordListOpcode(opcode)) {
4968
- const read = readCompactFlowRecordList(body, 0, budget);
4969
- if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record list bytes");
4970
- return { found: true, value: read.value };
5212
+ return {
5213
+ host,
5214
+ nativePort,
5215
+ node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
5216
+ ...nativeTlsPort == null ? {} : { nativeTlsPort }
5217
+ };
5218
+ }
5219
+ function validEndpointHost(host) {
5220
+ if (host.length === 0 || host.trim() !== host || /[\s\\/?#@]/u.test(host)) return false;
5221
+ const authorityHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
5222
+ try {
5223
+ const url = new URL(`ferric://${authorityHost}:6388`);
5224
+ return url.hostname !== "" && url.port === "6388" && url.username === "" && url.password === "";
5225
+ } catch {
5226
+ return false;
4971
5227
  }
4972
- return { found: false, value: void 0 };
4973
5228
  }
4974
- function isOkListOpcode(opcode) {
4975
- return compactResponseOpcodeSupports(opcode, COMPACT_KIND_OK_LIST);
5229
+ function sameEndpoint(left, right) {
5230
+ return normalizedEndpointHost(left.host) === normalizedEndpointHost(right.host) && left.nativePort === right.nativePort && left.nativeTlsPort === right.nativeTlsPort && left.node === right.node;
4976
5231
  }
4977
- function isFlowRecordListOpcode(opcode) {
4978
- return compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_RECORD_LIST);
5232
+ function parseFerricUrl2(value) {
5233
+ const url = new URL(value);
5234
+ if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
5235
+ throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
5236
+ }
5237
+ return {
5238
+ host: normalizeFerricUrlHost(url.hostname),
5239
+ ...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
5240
+ port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
5241
+ tls: url.protocol === "ferrics:",
5242
+ ...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
5243
+ };
4979
5244
  }
4980
- function decodeCompactOkList(data, budget) {
4981
- requireAvailable(data, 0, 5);
4982
- const count = data.readUInt32BE(1);
5245
+ function endpointKeyFor(endpoint) {
5246
+ return `${normalizedEndpointHost(endpoint.host)}:${endpoint.nativePort}`;
5247
+ }
5248
+ function connectionKeyForEndpoint(endpoint, useTls) {
5249
+ const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
5250
+ return `${useTls ? "ferrics" : "ferric"}://${normalizedEndpointHost(endpoint.host)}:${port}`;
5251
+ }
5252
+ function connectionKeyFromUrl(url) {
5253
+ const parsed = parseFerricUrl2(url);
5254
+ return `${parsed.tls ? "ferrics" : "ferric"}://${normalizedEndpointHost(parsed.host)}:${parsed.port}`;
5255
+ }
5256
+ function normalizedEndpointHost(host) {
5257
+ const lower = host.toLowerCase();
5258
+ return lower.startsWith("[") && lower.endsWith("]") ? lower.slice(1, -1) : lower;
5259
+ }
5260
+ function urlFromEndpoint(endpoint, useTls) {
5261
+ const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
5262
+ const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
5263
+ return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
5264
+ }
5265
+ async function mapSettledWithConcurrency(items, concurrency, operation) {
5266
+ const results = new Array(items.length);
5267
+ let nextIndex = 0;
5268
+ const worker = async () => {
5269
+ while (nextIndex < items.length) {
5270
+ const index = nextIndex++;
5271
+ const item = items[index];
5272
+ try {
5273
+ results[index] = { status: "fulfilled", value: await operation(item, index) };
5274
+ } catch (reason) {
5275
+ results[index] = { status: "rejected", reason };
5276
+ }
5277
+ }
5278
+ };
5279
+ await Promise.all(Array.from(
5280
+ { length: Math.min(concurrency, items.length) },
5281
+ async () => await worker()
5282
+ ));
5283
+ return results;
5284
+ }
5285
+ function throwFirstRejected(results) {
5286
+ for (const result of results) {
5287
+ if (result.status === "rejected") throw result.reason;
5288
+ }
5289
+ }
5290
+ var TaskConcurrencyLimiter = class {
5291
+ constructor(concurrency) {
5292
+ this.concurrency = concurrency;
5293
+ }
5294
+ concurrency;
5295
+ active = 0;
5296
+ head = 0;
5297
+ pending = [];
5298
+ async run(operation) {
5299
+ await this.acquire();
5300
+ try {
5301
+ return await operation();
5302
+ } finally {
5303
+ this.release();
5304
+ }
5305
+ }
5306
+ acquire() {
5307
+ if (this.active < this.concurrency) {
5308
+ this.active += 1;
5309
+ return Promise.resolve();
5310
+ }
5311
+ return new Promise((resolve) => this.pending.push(resolve));
5312
+ }
5313
+ release() {
5314
+ const next = this.pending[this.head];
5315
+ if (next == null) {
5316
+ this.active -= 1;
5317
+ return;
5318
+ }
5319
+ this.head += 1;
5320
+ next();
5321
+ if (this.head === this.pending.length) {
5322
+ this.pending.length = 0;
5323
+ this.head = 0;
5324
+ } else if (this.head >= 1024 && this.head * 2 >= this.pending.length) {
5325
+ this.pending.splice(0, this.head);
5326
+ this.head = 0;
5327
+ }
5328
+ }
5329
+ };
5330
+ function normalizedHostSet(hosts) {
5331
+ return new Set(
5332
+ hosts.filter((host) => host != null && host !== "").map((host) => normalizedEndpointHost(host))
5333
+ );
5334
+ }
5335
+ var OPEN_BRACE = "{".charCodeAt(0);
5336
+ var CLOSE_BRACE = "}".charCodeAt(0);
5337
+ function hashTagOrKey(key) {
5338
+ const start = key.indexOf(OPEN_BRACE);
5339
+ if (start < 0) return key;
5340
+ const end = key.indexOf(CLOSE_BRACE, start + 1);
5341
+ return end > start + 1 ? key.subarray(start + 1, end) : key;
5342
+ }
5343
+ var ROUTE_SLOT_MASK = 1023;
5344
+ function routingSlotForKey(key) {
5345
+ if (typeof key === "string") {
5346
+ const [start, end] = stringHashRange(key);
5347
+ return crc32Utf8(key, start, end) & ROUTE_SLOT_MASK;
5348
+ }
5349
+ const keyBytes = key;
5350
+ return crc32(hashTagOrKey(keyBytes)) & ROUTE_SLOT_MASK;
5351
+ }
5352
+ function stringHashRange(key) {
5353
+ const startBrace = key.indexOf("{");
5354
+ const endBrace = startBrace < 0 ? -1 : key.indexOf("}", startBrace + 1);
5355
+ return endBrace > startBrace + 1 ? [startBrace + 1, endBrace] : [0, key.length];
5356
+ }
5357
+
5358
+ // src/key-slot-validation.ts
5359
+ var validatedCommands = /* @__PURE__ */ new WeakSet();
5360
+ function assertAtomicKeyValueCommandSharesSlot(args) {
5361
+ const value = args[0];
5362
+ const command = (typeof value === "string" ? value : import_node_buffer17.Buffer.isBuffer(value) ? value.toString("utf8") : "").toUpperCase();
5363
+ if (command === "MSET" || command === "MSETNX") {
5364
+ assertKeyValueCommandSharesSlot(args, command);
5365
+ }
5366
+ }
5367
+ function assertKeyValueCommandSharesSlot(args, command) {
5368
+ if (validatedCommands.has(args) || args.length < 3 || args.length % 2 === 0) return;
5369
+ let expectedSlot;
5370
+ for (let index = 1; index < args.length; index += 2) {
5371
+ const key = args[index];
5372
+ if (typeof key !== "string" && !import_node_buffer17.Buffer.isBuffer(key)) return;
5373
+ const slot = routingSlotForKey(key);
5374
+ expectedSlot ??= slot;
5375
+ if (slot !== expectedSlot) {
5376
+ throw new TypeError(`${command} keys must share a slot`);
5377
+ }
5378
+ }
5379
+ validatedCommands.add(args);
5380
+ }
5381
+
5382
+ // src/protocol-response.ts
5383
+ var import_node_buffer21 = require("buffer");
5384
+
5385
+ // src/protocol-compact-response.ts
5386
+ var import_node_buffer20 = require("buffer");
5387
+
5388
+ // src/native-negotiation.ts
5389
+ var import_node_buffer18 = require("buffer");
5390
+ var EMPTY_COMPACT_RESPONSE_OPCODES = /* @__PURE__ */ new Map();
5391
+ function nativeNegotiation(value) {
5392
+ const capabilities = field(value, "capabilities") ?? value;
5393
+ const limits = field(capabilities, "limits");
5394
+ const responseCodecs = field(capabilities, "response_codecs");
5395
+ const maxResponseBytes = positiveSafeInteger3(field(limits, "max_response_bytes"));
5396
+ return {
5397
+ compactResponseOpcodes: parseCompactResponseOpcodes(
5398
+ field(responseCodecs, "compact_response_opcodes")
5399
+ ),
5400
+ ...maxResponseBytes == null ? {} : { maxResponseBytes }
5401
+ };
5402
+ }
5403
+ function compactResponseOpcodeSupports(capabilities, codec, opcode) {
5404
+ return capabilities?.get(codec)?.has(opcode) === true;
5405
+ }
5406
+ function parseCompactResponseOpcodes(value) {
5407
+ const entries = mapEntries(value);
5408
+ if (entries == null) return EMPTY_COMPACT_RESPONSE_OPCODES;
5409
+ const result = /* @__PURE__ */ new Map();
5410
+ for (const [rawName, rawOpcodes] of entries) {
5411
+ const name = text2(rawName);
5412
+ if (name == null || name.length === 0 || !Array.isArray(rawOpcodes)) continue;
5413
+ const opcodes = /* @__PURE__ */ new Set();
5414
+ let valid = true;
5415
+ for (let index = 0; index < rawOpcodes.length; index += 1) {
5416
+ if (!Object.hasOwn(rawOpcodes, index)) {
5417
+ valid = false;
5418
+ break;
5419
+ }
5420
+ const opcode = unsigned16(rawOpcodes[index]);
5421
+ if (opcode == null) {
5422
+ valid = false;
5423
+ break;
5424
+ }
5425
+ opcodes.add(opcode);
5426
+ }
5427
+ if (valid) result.set(name, opcodes);
5428
+ }
5429
+ return result;
5430
+ }
5431
+ function mapEntries(value) {
5432
+ if (value instanceof Map) return [...value.entries()];
5433
+ if (typeof value !== "object" || value == null || Array.isArray(value)) return void 0;
5434
+ return Object.entries(value);
5435
+ }
5436
+ function unsigned16(value) {
5437
+ const parsed = nonNegativeSafeInteger2(value);
5438
+ return parsed != null && parsed <= 65535 ? parsed : void 0;
5439
+ }
5440
+ function positiveSafeInteger3(value) {
5441
+ const parsed = nonNegativeSafeInteger2(value);
5442
+ return parsed != null && parsed > 0 ? parsed : void 0;
5443
+ }
5444
+ function nonNegativeSafeInteger2(value) {
5445
+ if (typeof value === "bigint") {
5446
+ return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
5447
+ }
5448
+ const source = text2(value) ?? value;
5449
+ if (typeof source !== "number" && (typeof source !== "string" || !/^\d+$/u.test(source))) {
5450
+ return void 0;
5451
+ }
5452
+ const parsed = Number(source);
5453
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
5454
+ }
5455
+ function text2(value) {
5456
+ if (typeof value === "string") return value;
5457
+ if (import_node_buffer18.Buffer.isBuffer(value) || value instanceof Uint8Array) {
5458
+ return import_node_buffer18.Buffer.from(value).toString("utf8");
5459
+ }
5460
+ return void 0;
5461
+ }
5462
+
5463
+ // src/protocol-compact-collections.ts
5464
+ var import_node_buffer19 = require("buffer");
5465
+ function decodeCompactIntegerList(data, budget) {
5466
+ requireAvailable(data, 0, 5);
5467
+ const count = data.readUInt32BE(1);
4983
5468
  requireCompactContainer(count, budget);
5469
+ if (data.byteLength !== 5 + count * 8) {
5470
+ throw new FerricStoreError("invalid compact integer list response");
5471
+ }
5472
+ const values = new Array(count);
5473
+ for (let index = 0, offset = 5; index < count; index += 1, offset += 8) {
5474
+ const value = data.readBigInt64BE(offset);
5475
+ values[index] = value <= MAX_SAFE_INTEGER_BIGINT && value >= MIN_SAFE_INTEGER_BIGINT ? Number(value) : value;
5476
+ }
5477
+ return values;
5478
+ }
5479
+ function decodeCompactBinaryListList(data, budget) {
5480
+ requireAvailable(data, 0, 5);
5481
+ const count = data.readUInt32BE(1);
5482
+ requireCompactContainer(count, budget);
5483
+ let offset = 5;
5484
+ const values = new Array(count);
5485
+ for (let index = 0; index < count; index += 1) {
5486
+ const read = readCompactBinaryList(data, offset, budget);
5487
+ values[index] = read.value;
5488
+ offset = read.offset;
5489
+ }
5490
+ if (offset !== data.byteLength) throw new FerricStoreError("trailing compact binary list-list bytes");
5491
+ return values;
5492
+ }
5493
+ function decodeCompactBinaryMapList(data, budget) {
5494
+ requireAvailable(data, 0, 5);
5495
+ const count = data.readUInt32BE(1);
5496
+ requireCompactContainer(count, budget);
5497
+ let offset = 5;
5498
+ const values = new Array(count);
5499
+ for (let index = 0; index < count; index += 1) {
5500
+ const read = readCompactBinaryMap(data, offset, budget);
5501
+ values[index] = read.value;
5502
+ offset = read.offset;
5503
+ }
5504
+ if (offset !== data.byteLength) throw new FerricStoreError("trailing compact binary map-list bytes");
5505
+ return values;
5506
+ }
5507
+ function readCompactBinaryList(data, offset, budget) {
5508
+ requireAvailable(data, offset, 4);
5509
+ const count = data.readUInt32BE(offset);
5510
+ requireCompactContainer(count, budget);
5511
+ offset += 4;
5512
+ const values = new Array(count);
5513
+ for (let index = 0; index < count; index += 1) {
5514
+ const read = readBinary(data, offset);
5515
+ values[index] = read.value;
5516
+ offset = read.offset;
5517
+ }
5518
+ return { value: values, offset };
5519
+ }
5520
+ function readCompactBinaryMap(data, offset, budget) {
5521
+ requireAvailable(data, offset, 4);
5522
+ const count = data.readUInt32BE(offset);
5523
+ requireCompactContainer(count, budget);
5524
+ offset += 4;
5525
+ const values = {};
5526
+ for (let index = 0; index < count; index += 1) {
5527
+ const key = readBinary(data, offset);
5528
+ const value = readBinary(data, key.offset);
5529
+ setOwnValue2(values, key.value.toString("utf8"), value.value);
5530
+ offset = value.offset;
5531
+ }
5532
+ return { value: values, offset };
5533
+ }
5534
+ function markCompactPipelineDecoded(values) {
5535
+ Object.defineProperty(values, COMPACT_PIPELINE_DECODED, { value: true });
5536
+ return values;
5537
+ }
5538
+ function requireCompactContainer(count, budget) {
5539
+ if (count > DEFAULT_MAX_VALUE_ITEMS) {
5540
+ throw new FerricStoreError("native compact response container exceeds max items");
5541
+ }
5542
+ consumeDecodeItems(count, budget);
5543
+ }
5544
+
5545
+ // src/protocol-compact-response.ts
5546
+ function tryDecodeCompactResponse(opcode, body, hints) {
5547
+ if (body.byteLength === 0) return { found: false, value: void 0 };
5548
+ const budget = { remainingItems: DEFAULT_MAX_VALUE_ITEMS };
5549
+ const tag = body.readUInt8(0);
5550
+ const pipelineValues = opcode === OPCODES.pipeline && supports(hints, "pipeline_v1", opcode);
5551
+ if (tag === COMPACT_OK_LIST && (supports(hints, "ok_list_v1", opcode) || pipelineValues)) {
5552
+ const values = decodeCompactOkList(body, budget);
5553
+ return {
5554
+ found: true,
5555
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values.length === 1 ? "OK" : values
5556
+ };
5557
+ }
5558
+ if (tag === COMPACT_KV_GET && supports(hints, "kv_get_v1", opcode)) {
5559
+ return { found: true, value: decodeCompactKvGet(body) };
5560
+ }
5561
+ if ((tag === COMPACT_KV_MGET || tag === COMPACT_KV_MGET_FIXED) && (supports(hints, "kv_mget_v1", opcode) || pipelineValues)) {
5562
+ const values = tag === COMPACT_KV_MGET ? decodeCompactMget(body, budget) : decodeCompactMgetFixed(body, budget);
5563
+ return {
5564
+ found: true,
5565
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values
5566
+ };
5567
+ }
5568
+ if (tag === COMPACT_INTEGER_LIST && pipelineValues) {
5569
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactIntegerList(body, budget)) };
5570
+ }
5571
+ if (tag === COMPACT_BINARY_LIST_LIST && pipelineValues) {
5572
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactBinaryListList(body, budget)) };
5573
+ }
5574
+ if (tag === COMPACT_BINARY_MAP_LIST && pipelineValues) {
5575
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactBinaryMapList(body, budget)) };
5576
+ }
5577
+ if (tag === COMPACT_PIPELINE_RESPONSE && pipelineValues) {
5578
+ return { found: true, value: decodeCompactPipeline(body, budget, hints.pipelineClaimModes) };
5579
+ }
5580
+ if (tag === COMPACT_FLOW_CLAIM_JOBS && (supports(hints, "flow_claim_jobs_v1", opcode) || pipelineValues)) {
5581
+ const values = decodeCompactClaimJobs(body, budget, hints.compactClaimMode);
5582
+ return {
5583
+ found: true,
5584
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values
5585
+ };
5586
+ }
5587
+ if (tag === COMPACT_FLOW_RECORD && supports(hints, "flow_record_v1", opcode)) {
5588
+ const read = readCompactFlowRecord(body, 0, budget);
5589
+ if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record bytes");
5590
+ return { found: true, value: read.value };
5591
+ }
5592
+ if (tag === COMPACT_FLOW_RECORD_LIST && (supports(hints, "flow_record_list_v1", opcode) || pipelineValues)) {
5593
+ const read = readCompactFlowRecordList(body, 0, budget);
5594
+ if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record list bytes");
5595
+ return {
5596
+ found: true,
5597
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(read.value) : read.value
5598
+ };
5599
+ }
5600
+ return { found: false, value: void 0 };
5601
+ }
5602
+ function supports(hints, codec, opcode) {
5603
+ return compactResponseOpcodeSupports(hints.compactResponseOpcodes, codec, opcode);
5604
+ }
5605
+ function decodeCompactOkList(data, budget) {
5606
+ requireAvailable(data, 0, 5);
5607
+ const count = data.readUInt32BE(1);
5608
+ requireCompactContainer2(count, budget);
4984
5609
  if (data.byteLength !== 5) throw new FerricStoreError("invalid compact OK list response");
4985
- return Array.from({ length: count }, () => import_node_buffer16.Buffer.from("OK"));
5610
+ return Array.from({ length: count }, () => import_node_buffer20.Buffer.from("OK"));
4986
5611
  }
4987
5612
  function decodeCompactKvGet(data) {
4988
5613
  requireAvailable(data, 0, 2);
@@ -4999,7 +5624,7 @@ function decodeCompactKvGet(data) {
4999
5624
  function decodeCompactMget(data, budget) {
5000
5625
  requireAvailable(data, 0, 5);
5001
5626
  const count = data.readUInt32BE(1);
5002
- requireCompactContainer(count, budget);
5627
+ requireCompactContainer2(count, budget);
5003
5628
  let offset = 5;
5004
5629
  const values = [];
5005
5630
  for (let index = 0; index < count; index += 1) {
@@ -5023,12 +5648,12 @@ function decodeCompactMgetFixed(data, budget) {
5023
5648
  requireAvailable(data, 0, 9);
5024
5649
  const count = data.readUInt32BE(1);
5025
5650
  const size = data.readUInt32BE(5);
5026
- requireCompactContainer(count, budget);
5651
+ requireCompactContainer2(count, budget);
5027
5652
  const expected = 9 + count * size;
5028
5653
  if (size === NULL_U32 || data.byteLength !== expected) {
5029
5654
  throw new FerricStoreError("invalid compact fixed MGET response");
5030
5655
  }
5031
- if (size === 0) return new Array(count).fill(import_node_buffer16.Buffer.alloc(0));
5656
+ if (size === 0) return new Array(count).fill(import_node_buffer20.Buffer.alloc(0));
5032
5657
  const values = new Array(count);
5033
5658
  for (let index = 0, offset = 9; index < count; index += 1, offset += size) {
5034
5659
  values[index] = data.subarray(offset, offset + size);
@@ -5038,7 +5663,7 @@ function decodeCompactMgetFixed(data, budget) {
5038
5663
  function decodeCompactPipeline(data, budget, claimModes) {
5039
5664
  requireAvailable(data, 0, 5);
5040
5665
  const count = data.readUInt32BE(1);
5041
- requireCompactContainer(count, budget);
5666
+ requireCompactContainer2(count, budget);
5042
5667
  if (count > Math.floor((data.byteLength - 5) / 2)) {
5043
5668
  throw new FerricStoreError("compact pipeline item count exceeds response bytes");
5044
5669
  }
@@ -5100,13 +5725,12 @@ function decodeCompactPipeline(data, budget, claimModes) {
5100
5725
  }
5101
5726
  }
5102
5727
  if (offset !== data.byteLength) throw new FerricStoreError("trailing compact pipeline bytes");
5103
- Object.defineProperty(values, COMPACT_PIPELINE_DECODED, { value: true });
5104
- return values;
5728
+ return markCompactPipelineDecoded(values);
5105
5729
  }
5106
5730
  function decodeCompactClaimJobs(data, budget, mode) {
5107
5731
  requireAvailable(data, 0, 5);
5108
5732
  const count = data.readUInt32BE(1);
5109
- requireCompactContainer(count, budget);
5733
+ requireCompactContainer2(count, budget);
5110
5734
  if (mode != null) {
5111
5735
  const values = [];
5112
5736
  let offset = 5;
@@ -5190,7 +5814,7 @@ function readCompactFlowRecord(data, offset, budget = { remainingItems: DEFAULT_
5190
5814
  if (data.readUInt8(offset) !== COMPACT_FLOW_RECORD) throw new FerricStoreError("expected compact Flow record");
5191
5815
  offset += 1;
5192
5816
  const count = data.readUInt32BE(offset);
5193
- requireCompactContainer(count, budget);
5817
+ requireCompactContainer2(count, budget);
5194
5818
  offset += 4;
5195
5819
  const record = {};
5196
5820
  for (let index = 0; index < count; index += 1) {
@@ -5218,7 +5842,7 @@ function readCompactFlowRecordList(data, offset, budget = { remainingItems: DEFA
5218
5842
  }
5219
5843
  offset += 1;
5220
5844
  const count = data.readUInt32BE(offset);
5221
- requireCompactContainer(count, budget);
5845
+ requireCompactContainer2(count, budget);
5222
5846
  offset += 4;
5223
5847
  const records = [];
5224
5848
  for (let index = 0; index < count; index += 1) {
@@ -5228,33 +5852,6 @@ function readCompactFlowRecordList(data, offset, budget = { remainingItems: DEFA
5228
5852
  }
5229
5853
  return { value: records, offset };
5230
5854
  }
5231
- function readCompactBinaryList(data, offset, budget) {
5232
- requireAvailable(data, offset, 4);
5233
- const count = data.readUInt32BE(offset);
5234
- requireCompactContainer(count, budget);
5235
- offset += 4;
5236
- const values = [];
5237
- for (let index = 0; index < count; index += 1) {
5238
- const read = readBinary(data, offset);
5239
- values.push(read.value);
5240
- offset = read.offset;
5241
- }
5242
- return { value: values, offset };
5243
- }
5244
- function readCompactBinaryMap(data, offset, budget) {
5245
- requireAvailable(data, offset, 4);
5246
- const count = data.readUInt32BE(offset);
5247
- requireCompactContainer(count, budget);
5248
- offset += 4;
5249
- const values = {};
5250
- for (let index = 0; index < count; index += 1) {
5251
- const key = readBinary(data, offset);
5252
- const value = readBinary(data, key.offset);
5253
- setOwnValue2(values, key.value.toString("utf8"), value.value);
5254
- offset = value.offset;
5255
- }
5256
- return { value: values, offset };
5257
- }
5258
5855
  function readOptionalBinary(data, offset) {
5259
5856
  requireAvailable(data, offset, 4);
5260
5857
  const size = data.readUInt32BE(offset);
@@ -5263,7 +5860,7 @@ function readOptionalBinary(data, offset) {
5263
5860
  requireAvailable(data, offset, size);
5264
5861
  return { value: data.subarray(offset, offset + size), offset: offset + size };
5265
5862
  }
5266
- function requireCompactContainer(count, budget) {
5863
+ function requireCompactContainer2(count, budget) {
5267
5864
  if (count > DEFAULT_MAX_VALUE_ITEMS) {
5268
5865
  throw new FerricStoreError("native compact response container exceeds max items");
5269
5866
  }
@@ -5271,7 +5868,7 @@ function requireCompactContainer(count, budget) {
5271
5868
  }
5272
5869
  function errorMessage(status, value) {
5273
5870
  if (typeof value === "string") return value;
5274
- if (import_node_buffer16.Buffer.isBuffer(value)) return value.toString("utf8");
5871
+ if (import_node_buffer20.Buffer.isBuffer(value)) return value.toString("utf8");
5275
5872
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5276
5873
  const message = value.message;
5277
5874
  return typeof message === "string" ? message : String(message);
@@ -5351,7 +5948,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
5351
5948
  return out;
5352
5949
  }
5353
5950
  function pipelineStatus(value) {
5354
- if (typeof value !== "string" && !import_node_buffer17.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5951
+ if (typeof value !== "string" && !import_node_buffer21.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5355
5952
  const status = asText(value).toLowerCase();
5356
5953
  return status === "ok" || status === "busy" || status === "error" ? status : null;
5357
5954
  }
@@ -5366,7 +5963,7 @@ function decodeResponseValue(opcode, body, hints) {
5366
5963
  }
5367
5964
  function errorMessage2(status, value) {
5368
5965
  if (typeof value === "string") return value;
5369
- if (import_node_buffer17.Buffer.isBuffer(value)) return value.toString("utf8");
5966
+ if (import_node_buffer21.Buffer.isBuffer(value)) return value.toString("utf8");
5370
5967
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5371
5968
  const message = value.message;
5372
5969
  return typeof message === "string" ? message : String(message);
@@ -5382,7 +5979,7 @@ function encodeRequest(command, requestId, maxBodyBytes = Number.MAX_SAFE_INTEGE
5382
5979
  const bodyLength = customPayload ? binaryValueByteLength(command.payload) : plan?.byteLength ?? 0;
5383
5980
  if (bodyLength > bodyLimit) throw requestFrameTooLarge(bodyLimit);
5384
5981
  const flags = command.flags ?? 0;
5385
- const frame = import_node_buffer18.Buffer.allocUnsafe(HEADER_SIZE + bodyLength);
5982
+ const frame = import_node_buffer22.Buffer.allocUnsafe(HEADER_SIZE + bodyLength);
5386
5983
  frame.write(MAGIC, 0, "ascii");
5387
5984
  frame.writeUInt8(REQUEST_VERSION, 4);
5388
5985
  frame.writeUInt8(flags, 5);
@@ -5433,6 +6030,9 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5433
6030
  }
5434
6031
  const command = asText(args[0]).toUpperCase();
5435
6032
  assertNormalizedCommandDoesNotRequirePinnedConnection(args, command);
6033
+ if (command === "MSET" || command === "MSETNX") {
6034
+ assertKeyValueCommandSharesSlot(args, command);
6035
+ }
5436
6036
  const commandArgs2 = args.slice(1);
5437
6037
  if (command === "COMMAND_EXEC" && commandArgs2.length >= 1) {
5438
6038
  return commandExec(commandArgs2);
@@ -5504,6 +6104,12 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5504
6104
  if (command === "FLOW.LIST") {
5505
6105
  return (allowCustomPayload ? compactFlowListPayload(commandArgs2, maxBodyBytes) : void 0) ?? commandExec(args);
5506
6106
  }
6107
+ if (command === "FLOW.POLICY.SET") {
6108
+ return flowPolicySetPayload(commandArgs2) ?? commandExec(args);
6109
+ }
6110
+ if (command === "FLOW.POLICY.GET") {
6111
+ return flowPolicyGetPayload(commandArgs2) ?? commandExec(args);
6112
+ }
5507
6113
  const flowAdmin = FLOW_ADMIN_COMMANDS[command];
5508
6114
  if (flowAdmin != null) {
5509
6115
  return flowAdminPayload(flowAdmin.opcode, commandArgs2, flowAdmin.leadingFields) ?? commandExec(args);
@@ -5564,26 +6170,13 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5564
6170
  "VALUE",
5565
6171
  "VALUE_REF",
5566
6172
  "DROP_VALUE",
5567
- "OVERRIDE_VALUE",
5568
- "ATTRIBUTE_MERGE",
5569
- "ATTRIBUTE_DELETE"
5570
- ])) ?? commandExec(args);
5571
- }
5572
- if (command === "FLOW.SIGNAL") {
5573
- return flowCommandExecWithRouting(command, commandArgs2, 1, /* @__PURE__ */ new Set([
5574
- "SIGNAL",
5575
- "PARTITION",
5576
- "IDEMPOTENCY",
5577
- "IF_STATE",
5578
- "TRANSITION_TO",
5579
- "RUN_AT",
5580
- "NOW",
5581
- "PRIORITY",
5582
- "VALUE",
5583
- "VALUE_REF",
5584
- "DROP_VALUE",
5585
- "OVERRIDE_VALUE"
5586
- ]));
6173
+ "OVERRIDE_VALUE",
6174
+ "ATTRIBUTE_MERGE",
6175
+ "ATTRIBUTE_DELETE"
6176
+ ])) ?? commandExec(args);
6177
+ }
6178
+ if (command === "FLOW.SIGNAL") {
6179
+ return flowSignalPayload(commandArgs2) ?? commandExec(args);
5587
6180
  }
5588
6181
  if (command === "FLOW.CANCEL") {
5589
6182
  return flowCommandExecWithRouting(command, commandArgs2, 1, /* @__PURE__ */ new Set([
@@ -5752,6 +6345,7 @@ var DEFAULT_MAX_PENDING_CONTROL_REQUESTS = 4096;
5752
6345
  var DEFAULT_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
5753
6346
  var DEFAULT_MAX_QUEUED_REQUESTS = 65536;
5754
6347
  var DEFAULT_MAX_QUEUED_WRITE_BYTES = 64 * 1024 * 1024;
6348
+ var UNAUTHENTICATED_MAX_FRAME_BYTES = 64 * 1024;
5755
6349
  function assertNativeAdapterOptions(options) {
5756
6350
  const supplied = options;
5757
6351
  for (const name of ["autoReconnect", "haRouting", "seeds"]) {
@@ -5771,7 +6365,7 @@ var import_node_net2 = require("net");
5771
6365
  var import_node_tls2 = require("tls");
5772
6366
 
5773
6367
  // src/client-native-options.ts
5774
- var import_node_buffer19 = require("buffer");
6368
+ var import_node_buffer23 = require("buffer");
5775
6369
 
5776
6370
  // src/string-array-snapshot.ts
5777
6371
  function snapshotOwnStringArray(values, name) {
@@ -5854,7 +6448,7 @@ function snapshotTlsOptions(options) {
5854
6448
  return Object.freeze(snapshot);
5855
6449
  }
5856
6450
  function snapshotTlsValue(value, name) {
5857
- if (import_node_buffer19.Buffer.isBuffer(value)) return import_node_buffer19.Buffer.from(value);
6451
+ if (import_node_buffer23.Buffer.isBuffer(value)) return import_node_buffer23.Buffer.from(value);
5858
6452
  if (value instanceof Uint8Array) return value.slice();
5859
6453
  if (Array.isArray(value)) {
5860
6454
  const snapshot = new Array(value.length);
@@ -6324,6 +6918,9 @@ var NativeChunkAssembler = class {
6324
6918
  framesByKey = /* @__PURE__ */ new Map();
6325
6919
  totalBytes = 0;
6326
6920
  totalFrames = 0;
6921
+ updateMaxResponseBytes(limit) {
6922
+ this.maxResponseBytes = Math.min(this.maxResponseBytes, limit);
6923
+ }
6327
6924
  assemble(frame) {
6328
6925
  const key = `${frame.requestId}:${frame.opcode}:${frame.laneId}`;
6329
6926
  const previous = this.chunks.get(key);
@@ -6406,7 +7003,7 @@ var NativeChunkAssembler = class {
6406
7003
  };
6407
7004
 
6408
7005
  // src/native-response-handler.ts
6409
- var import_node_buffer20 = require("buffer");
7006
+ var import_node_buffer24 = require("buffer");
6410
7007
 
6411
7008
  // src/native-receive-buffer.ts
6412
7009
  var FRAME_ASSEMBLY_PAGE_BYTES = 64 * 1024;
@@ -6568,12 +7165,18 @@ var NativeReceiveBuffer = class {
6568
7165
  var NativeResponseHandler = class {
6569
7166
  constructor(options) {
6570
7167
  this.options = options;
7168
+ this.maxResponseBytes = options.maxResponseBytes;
6571
7169
  }
6572
7170
  options;
6573
7171
  receiveBuffer = new NativeReceiveBuffer();
6574
7172
  drainScheduled = false;
6575
7173
  paused = false;
6576
7174
  stopped = false;
7175
+ maxResponseBytes;
7176
+ updateMaxResponseBytes(limit) {
7177
+ this.maxResponseBytes = Math.min(this.maxResponseBytes, limit);
7178
+ this.options.chunkAssembler.updateMaxResponseBytes(this.maxResponseBytes);
7179
+ }
6577
7180
  onData(chunk) {
6578
7181
  if (this.stopped) return;
6579
7182
  try {
@@ -6655,15 +7258,18 @@ var NativeResponseHandler = class {
6655
7258
  }
6656
7259
  const completeFrame = this.options.chunkAssembler.assemble(frame);
6657
7260
  if (completeFrame == null) return;
6658
- if (completeFrame.body.byteLength > this.options.maxResponseBytes) {
7261
+ if (completeFrame.body.byteLength > this.maxResponseBytes) {
6659
7262
  throw new FerricStoreError(
6660
- `native protocol response exceeded ${this.options.maxResponseBytes} bytes`
7263
+ `native protocol response exceeded ${this.maxResponseBytes} bytes`
6661
7264
  );
6662
7265
  }
6663
7266
  let value;
6664
7267
  let responseError;
6665
7268
  try {
6666
- value = decodeResponse(completeFrame, pending.opcode, pending);
7269
+ value = decodeResponse(completeFrame, pending.opcode, {
7270
+ ...pending,
7271
+ compactResponseOpcodes: this.options.compactResponseOpcodes?.() ?? EMPTY_COMPACT_RESPONSE_OPCODES
7272
+ });
6667
7273
  if (pending.opcode === OPCODES.windowUpdate) this.options.applyFlowControlLimits(value);
6668
7274
  } catch (error) {
6669
7275
  responseError = error;
@@ -6679,7 +7285,7 @@ var NativeResponseHandler = class {
6679
7285
  pending.discardedResponseBytes = discardedResponseBytes;
6680
7286
  pending.discardedResponseFrames = discardedResponseFrames;
6681
7287
  const chunked = discardedResponseFrames > 1 || (frame.flags & FLAG_MORE_CHUNKS) !== 0;
6682
- if (discardedResponseBytes > this.options.maxResponseBytes || chunked && discardedResponseBytes > this.options.maxChunkBytes) {
7288
+ if (discardedResponseBytes > this.maxResponseBytes || chunked && discardedResponseBytes > this.options.maxChunkBytes) {
6683
7289
  throw new FerricStoreError("native protocol discarded response exceeded configured byte limits");
6684
7290
  }
6685
7291
  if (discardedResponseFrames > this.options.maxChunkFrames) {
@@ -6688,12 +7294,14 @@ var NativeResponseHandler = class {
6688
7294
  if ((frame.flags & FLAG_MORE_CHUNKS) === 0) this.options.takePending(frame.requestId);
6689
7295
  }
6690
7296
  handleManagementFrame(frame) {
6691
- if (frame.body.byteLength > this.options.maxResponseBytes) {
7297
+ if (frame.body.byteLength > this.maxResponseBytes) {
6692
7298
  throw new FerricStoreError(
6693
- `native protocol response exceeded ${this.options.maxResponseBytes} bytes`
7299
+ `native protocol response exceeded ${this.maxResponseBytes} bytes`
6694
7300
  );
6695
7301
  }
6696
- const value = decodeResponse(frame, frame.opcode);
7302
+ const value = decodeResponse(frame, frame.opcode, {
7303
+ compactResponseOpcodes: this.options.compactResponseOpcodes?.() ?? EMPTY_COMPACT_RESPONSE_OPCODES
7304
+ });
6697
7305
  if (frame.opcode === OPCODES.windowUpdate) this.options.applyFlowControlLimits(value);
6698
7306
  const event = {
6699
7307
  flags: frame.flags,
@@ -6730,7 +7338,7 @@ var NativeRequestScheduler = class {
6730
7338
  };
6731
7339
 
6732
7340
  // src/native-write-queue.ts
6733
- var import_node_buffer21 = require("buffer");
7341
+ var import_node_buffer25 = require("buffer");
6734
7342
  var NativeWriteQueue = class {
6735
7343
  constructor(socket, maxQueuedWriteBytes, isClosed, hasPending, rejectPending) {
6736
7344
  this.socket = socket;
@@ -6858,6 +7466,40 @@ var NativeWriteQueue = class {
6858
7466
  }
6859
7467
  };
6860
7468
 
7469
+ // src/native-connection-capabilities.ts
7470
+ var NativeConnectionCapabilities = class {
7471
+ constructor(configuredRequestFrameBytes) {
7472
+ this.configuredRequestFrameBytes = configuredRequestFrameBytes;
7473
+ this.negotiatedRequestFrameBytes = configuredRequestFrameBytes;
7474
+ this.requestFrameBytes = Math.min(configuredRequestFrameBytes, UNAUTHENTICATED_MAX_FRAME_BYTES);
7475
+ }
7476
+ configuredRequestFrameBytes;
7477
+ compactResponseOpcodes = EMPTY_COMPACT_RESPONSE_OPCODES;
7478
+ requestFrameBytes;
7479
+ negotiatedRequestFrameBytes;
7480
+ apply(value) {
7481
+ const limits = startupLimits(value);
7482
+ const negotiation = nativeNegotiation(value);
7483
+ this.compactResponseOpcodes = negotiation.compactResponseOpcodes;
7484
+ if (limits.frameBytes != null) {
7485
+ this.negotiatedRequestFrameBytes = Math.min(
7486
+ this.configuredRequestFrameBytes,
7487
+ limits.frameBytes
7488
+ );
7489
+ this.requestFrameBytes = Math.min(this.requestFrameBytes, this.negotiatedRequestFrameBytes);
7490
+ }
7491
+ return {
7492
+ ...limits.laneQueue == null ? {} : { laneQueue: limits.laneQueue },
7493
+ ...limits.lanes == null ? {} : { lanes: limits.lanes },
7494
+ ...negotiation.maxResponseBytes == null ? {} : { maxResponseBytes: negotiation.maxResponseBytes },
7495
+ ...limits.pipelineCommands == null ? {} : { pipelineCommands: limits.pipelineCommands }
7496
+ };
7497
+ }
7498
+ activateAuthenticated() {
7499
+ this.requestFrameBytes = this.negotiatedRequestFrameBytes;
7500
+ }
7501
+ };
7502
+
6861
7503
  // src/native-adapter.ts
6862
7504
  var NativeAdapter = class _NativeAdapter {
6863
7505
  socket;
@@ -6866,9 +7508,9 @@ var NativeAdapter = class _NativeAdapter {
6866
7508
  closedWaiters = /* @__PURE__ */ new Set();
6867
7509
  chunkAssembler;
6868
7510
  closed = false;
7511
+ capabilities;
6869
7512
  protocolLanes;
6870
7513
  maxPipelineCommands = Number.MAX_SAFE_INTEGER;
6871
- maxRequestFrameBytes;
6872
7514
  pendingControlRequests = 0;
6873
7515
  maxPendingControlRequests;
6874
7516
  heartbeat;
@@ -6883,7 +7525,7 @@ var NativeAdapter = class _NativeAdapter {
6883
7525
  const normalizedMaxChunkBytes = normalizePositiveLimit(maxChunkBytes, DEFAULT_MAX_CHUNK_BYTES);
6884
7526
  const normalizedMaxChunkFrames = normalizePositiveLimit(maxChunkFrames, DEFAULT_MAX_CHUNK_FRAMES);
6885
7527
  const normalizedMaxFrameBytes = normalizePositiveLimit(maxFrameBytes, DEFAULT_MAX_FRAME_BYTES);
6886
- this.maxRequestFrameBytes = normalizedMaxFrameBytes;
7528
+ this.capabilities = new NativeConnectionCapabilities(normalizedMaxFrameBytes);
6887
7529
  const normalizedMaxResponseBytes = normalizePositiveLimit(maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES);
6888
7530
  this.chunkAssembler = new NativeChunkAssembler(
6889
7531
  normalizedMaxChunkBytes,
@@ -6910,7 +7552,7 @@ var NativeAdapter = class _NativeAdapter {
6910
7552
  this.heartbeat = new NativeHeartbeat(
6911
7553
  this.heartbeatIntervalMs,
6912
7554
  async () => {
6913
- await this.request(buildProtocolCommand(["PING"], this.maxRequestFrameBytes), true);
7555
+ await this.request(buildProtocolCommand(["PING"], this.capabilities.requestFrameBytes), true);
6914
7556
  },
6915
7557
  (error) => {
6916
7558
  this.failAll(new FerricStoreError("FerricStore protocol heartbeat failed", { cause: error }), true);
@@ -6921,6 +7563,7 @@ var NativeAdapter = class _NativeAdapter {
6921
7563
  applyFlowControlLimits: (value) => this.applyFlowControlLimits(value),
6922
7564
  beginDraining: () => this.beginDraining(),
6923
7565
  chunkAssembler: this.chunkAssembler,
7566
+ compactResponseOpcodes: () => this.capabilities.compactResponseOpcodes,
6924
7567
  destroy: (error) => this.socket.destroy(error),
6925
7568
  failAll: (reason, connectionClosed, message) => this.failAll(reason, connectionClosed, message),
6926
7569
  heartbeat: this.heartbeat,
@@ -6946,6 +7589,7 @@ var NativeAdapter = class _NativeAdapter {
6946
7589
  adapter,
6947
7590
  auth: async (username, password) => {
6948
7591
  await adapter.request({ laneId: 0, opcode: OPCODES.auth, payload: { password, username } });
7592
+ adapter.capabilities.activateAuthenticated();
6949
7593
  },
6950
7594
  close: async () => {
6951
7595
  await adapter.close();
@@ -6963,13 +7607,13 @@ var NativeAdapter = class _NativeAdapter {
6963
7607
  return await this.executeCommandArgs(args);
6964
7608
  }
6965
7609
  async executeCommandArgs(args) {
6966
- const command = buildProtocolCommand(args, this.maxRequestFrameBytes);
7610
+ const command = buildProtocolCommand(args, this.capabilities.requestFrameBytes);
6967
7611
  return await this.request(command);
6968
7612
  }
6969
7613
  /** @internal Execute raw command arguments on a topology-selected lane. */
6970
7614
  async executeCommandOnLane(args, laneId) {
6971
7615
  return await this.executeProtocolCommand(
6972
- buildProtocolCommand(args, this.maxRequestFrameBytes),
7616
+ buildProtocolCommand(args, this.capabilities.requestFrameBytes),
6973
7617
  laneId
6974
7618
  );
6975
7619
  }
@@ -6991,7 +7635,7 @@ var NativeAdapter = class _NativeAdapter {
6991
7635
  laneId,
6992
7636
  options,
6993
7637
  this.maxPipelineCommands,
6994
- this.maxRequestFrameBytes
7638
+ this.capabilities.requestFrameBytes
6995
7639
  );
6996
7640
  }
6997
7641
  /** @internal Execute an ordered pipeline on a topology-selected lane. */
@@ -7002,13 +7646,11 @@ var NativeAdapter = class _NativeAdapter {
7002
7646
  laneId,
7003
7647
  options,
7004
7648
  this.maxPipelineCommands,
7005
- this.maxRequestFrameBytes
7649
+ this.capabilities.requestFrameBytes
7006
7650
  );
7007
7651
  }
7008
7652
  async close() {
7009
- if (this.closed) {
7010
- return;
7011
- }
7653
+ if (this.closed) return;
7012
7654
  this.failAll(new FerricStoreError("FerricStore connection closed"), true);
7013
7655
  this.socket.destroy();
7014
7656
  }
@@ -7026,7 +7668,9 @@ var NativeAdapter = class _NativeAdapter {
7026
7668
  });
7027
7669
  this.applyStartupLimits(response);
7028
7670
  const authRequired = field(response, "auth_required");
7029
- return authRequired == null ? false : booleanResponse(authRequired);
7671
+ const required = authRequired == null ? false : booleanResponse(authRequired);
7672
+ if (authRequired != null && !required) this.capabilities.activateAuthenticated();
7673
+ return required;
7030
7674
  }
7031
7675
  request(command, allowWhileDraining = false) {
7032
7676
  if (this.closed || this.draining && !allowWhileDraining) {
@@ -7067,7 +7711,7 @@ var NativeAdapter = class _NativeAdapter {
7067
7711
  let frame;
7068
7712
  try {
7069
7713
  requestId = this.requestScheduler.nextRequestId();
7070
- frame = encodeRequest(command, requestId, this.maxRequestFrameBytes);
7714
+ frame = encodeRequest(command, requestId, this.capabilities.requestFrameBytes);
7071
7715
  } catch (error) {
7072
7716
  if (hasFlowControlCredit) this.flowControl.release(laneId);
7073
7717
  return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
@@ -7121,9 +7765,7 @@ var NativeAdapter = class _NativeAdapter {
7121
7765
  this.resolveClosedWaiters();
7122
7766
  }
7123
7767
  failAll(reason, connectionClosed = false, dispatchedMessage) {
7124
- if (this.closed) {
7125
- return;
7126
- }
7768
+ if (this.closed) return;
7127
7769
  this.closed = true;
7128
7770
  this.responseHandler.stop();
7129
7771
  this.resolveClosedWaiters();
@@ -7189,18 +7831,18 @@ var NativeAdapter = class _NativeAdapter {
7189
7831
  }
7190
7832
  applyStartupLimits(value) {
7191
7833
  this.applyFlowControlLimits(value);
7192
- const limits = startupLimits(value);
7193
- if (limits.laneQueue != null) {
7194
- this.flowControl.updateLaneQueueLimit(limits.laneQueue);
7834
+ const capabilities = this.capabilities.apply(value);
7835
+ if (capabilities.maxResponseBytes != null) {
7836
+ this.responseHandler.updateMaxResponseBytes(capabilities.maxResponseBytes);
7195
7837
  }
7196
- if (limits.lanes != null) {
7197
- this.protocolLanes = Math.min(this.protocolLanes, limits.lanes);
7838
+ if (capabilities.laneQueue != null) {
7839
+ this.flowControl.updateLaneQueueLimit(capabilities.laneQueue);
7198
7840
  }
7199
- if (limits.pipelineCommands != null) {
7200
- this.maxPipelineCommands = limits.pipelineCommands;
7841
+ if (capabilities.lanes != null) {
7842
+ this.protocolLanes = Math.min(this.protocolLanes, capabilities.lanes);
7201
7843
  }
7202
- if (limits.frameBytes != null) {
7203
- this.maxRequestFrameBytes = Math.min(this.maxRequestFrameBytes, limits.frameBytes);
7844
+ if (capabilities.pipelineCommands != null) {
7845
+ this.maxPipelineCommands = capabilities.pipelineCommands;
7204
7846
  }
7205
7847
  }
7206
7848
  };
@@ -7213,7 +7855,30 @@ function executeCommandArgs(executor, args) {
7213
7855
  }
7214
7856
 
7215
7857
  // src/reconnecting-executor.ts
7216
- var import_node_buffer23 = require("buffer");
7858
+ var import_node_buffer28 = require("buffer");
7859
+
7860
+ // src/command-retry-policy.ts
7861
+ var import_node_buffer27 = require("buffer");
7862
+ function isCasMutation(args) {
7863
+ const offset = commandName2(args[0]) === "COMMAND_EXEC" ? 1 : 0;
7864
+ const name = commandName2(args[offset]);
7865
+ if (name === "CAS") return true;
7866
+ if (name !== "FLOW.POLICY.SET") return false;
7867
+ for (let index = offset + 2; index < args.length; index += 2) {
7868
+ if (!Object.hasOwn(args, index)) return false;
7869
+ if (commandName2(args[index]) === "EXPECTED_GENERATION") return true;
7870
+ }
7871
+ return false;
7872
+ }
7873
+ function commandName2(value) {
7874
+ if (typeof value === "string") return value.toUpperCase();
7875
+ if (import_node_buffer27.Buffer.isBuffer(value) || value instanceof Uint8Array) {
7876
+ return import_node_buffer27.Buffer.from(value).toString("utf8").toUpperCase();
7877
+ }
7878
+ return void 0;
7879
+ }
7880
+
7881
+ // src/reconnecting-executor.ts
7217
7882
  var ReconnectingExecutor = class {
7218
7883
  constructor(createExecutor, options = {}) {
7219
7884
  this.createExecutor = createExecutor;
@@ -7244,7 +7909,12 @@ var ReconnectingExecutor = class {
7244
7909
  async executeCommandArgs(args) {
7245
7910
  const snapshot = snapshotCommandArguments(args);
7246
7911
  assertCommandHasStableConnectionState(snapshot);
7247
- return await this.withReconnect((executor) => executeCommandArgs(executor, snapshot));
7912
+ const casMutation = isCasMutation(snapshot);
7913
+ return await this.withReconnect(
7914
+ (executor) => executeCommandArgs(executor, snapshot),
7915
+ casMutation ? isClosedConnectionError : isReconnectableClosedConnectionError,
7916
+ !casMutation
7917
+ );
7248
7918
  }
7249
7919
  async ready() {
7250
7920
  await this.executorPromise;
@@ -7314,311 +7984,112 @@ var ReconnectingExecutor = class {
7314
7984
  this.retryAbortController.abort(new FerricStoreError("FerricStore client is closed"));
7315
7985
  const executorPromise = this.executorPromise;
7316
7986
  const reconnectPromise = this.reconnectPromise;
7317
- this.closePromise = (async () => {
7318
- let closeError;
7319
- let closeFailed = false;
7320
- const executor = await executorPromise.catch(() => void 0);
7321
- try {
7322
- await Promise.resolve(executor?.close?.());
7323
- } catch (error) {
7324
- closeFailed = true;
7325
- closeError = error;
7326
- }
7327
- await reconnectPromise?.catch(() => void 0);
7328
- if (closeFailed) throw closeError;
7329
- })();
7330
- await this.closePromise;
7331
- }
7332
- async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError) {
7333
- let executor = await this.executorPromise;
7334
- let retries = 0;
7335
- for (; ; ) {
7336
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7337
- try {
7338
- return await operation(executor);
7339
- } catch (error) {
7340
- if (this.closed || retries >= this.maxRetries || !reconnectable(error)) throw error;
7341
- for (; ; ) {
7342
- retries += 1;
7343
- try {
7344
- executor = await this.reconnect(executor);
7345
- break;
7346
- } catch (reconnectError) {
7347
- if (this.closed || retries >= this.maxRetries) throw reconnectError;
7348
- await this.waitBeforeReconnect(retries);
7349
- }
7350
- }
7351
- }
7352
- }
7353
- }
7354
- async waitBeforeReconnect(failedAttempts) {
7355
- const exponent = Math.min(30, Math.max(0, failedAttempts - 1));
7356
- const base = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent);
7357
- const jitter = base * (this.jitterPct / 100) * Math.random();
7358
- try {
7359
- await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
7360
- } catch (error) {
7361
- if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
7362
- throw error;
7363
- }
7364
- }
7365
- async reconnect(staleExecutor) {
7366
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7367
- const competingReconnect = this.reconnectPromise;
7368
- if (competingReconnect != null) return await competingReconnect;
7369
- const observedExecutorPromise = this.executorPromise;
7370
- const currentExecutor = await observedExecutorPromise.catch(() => void 0);
7371
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7372
- if (this.reconnectPromise != null) return await this.reconnectPromise;
7373
- if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
7374
- if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
7375
- this.reconnectPromise = (async () => {
7376
- await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
7377
- const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
7378
- if (this.closed) {
7379
- await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
7380
- throw new FerricStoreError("FerricStore client is closed");
7381
- }
7382
- this.executorPromise = Promise.resolve(nextExecutor);
7383
- return nextExecutor;
7384
- })().finally(() => {
7385
- this.reconnectPromise = void 0;
7386
- });
7387
- return await this.reconnectPromise;
7388
- }
7389
- };
7390
- function isReconnectableClosedConnectionError(error) {
7391
- return error instanceof ConnectionClosedError && error.requestDisposition === "unsent";
7392
- }
7393
- function isReadOnlyReconnectableClosedConnectionError(error) {
7394
- const pending = [error];
7395
- const seen = /* @__PURE__ */ new Set();
7396
- for (let visited = 0; visited < 16 && pending.length > 0; visited += 1) {
7397
- const current = pending.pop();
7398
- if (isReconnectableClosedConnectionError(current)) return true;
7399
- if (current == null || typeof current !== "object" && typeof current !== "function" || seen.has(current)) continue;
7400
- seen.add(current);
7401
- if (current instanceof Error && current.cause != null) pending.push(current.cause);
7402
- if (current instanceof FerricStoreError && current.raw != null) pending.push(current.raw);
7403
- }
7404
- return false;
7405
- }
7406
- function normalizeNonNegativeInteger2(value, fallback) {
7407
- return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
7408
- }
7409
-
7410
- // src/topology.ts
7411
- var import_node_buffer26 = require("buffer");
7412
-
7413
- // src/topology-routing.ts
7414
- var import_node_buffer25 = require("buffer");
7415
- var import_node_crypto = require("crypto");
7416
-
7417
- // src/topology-utilities.ts
7418
- var import_node_buffer24 = require("buffer");
7419
- function isPlainObject3(value) {
7420
- return typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer24.Buffer.isBuffer(value);
7421
- }
7422
- function getField(source, ...keys) {
7423
- if (source instanceof Map) {
7424
- for (const key of keys) {
7425
- if (source.has(key)) return source.get(key);
7426
- const bufferKey = import_node_buffer24.Buffer.from(key);
7427
- for (const [itemKey, value] of source.entries()) {
7428
- if (import_node_buffer24.Buffer.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
7429
- }
7430
- }
7431
- return void 0;
7432
- }
7433
- if (isPlainObject3(source)) {
7434
- for (const key of keys) {
7435
- if (Object.hasOwn(source, key)) return source[key];
7436
- }
7437
- }
7438
- return void 0;
7439
- }
7440
- function textOrUndefined(value) {
7441
- if (typeof value === "string") return value;
7442
- if (import_node_buffer24.Buffer.isBuffer(value)) return value.toString("utf8");
7443
- return void 0;
7444
- }
7445
- function numberOrUndefined(value) {
7446
- return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
7447
- }
7448
- function nativeEventName(event) {
7449
- return textOrUndefined(getField(event.value, "event"))?.toUpperCase();
7450
- }
7451
- function endpointFromRange(item) {
7452
- const endpointValue = getField(item, "endpoint");
7453
- if (endpointValue != null && !isPlainObject3(endpointValue) && !(endpointValue instanceof Map)) {
7454
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7455
- }
7456
- const raw = endpointValue ?? item;
7457
- const host = textOrUndefined(getField(raw, "host", "native_host"));
7458
- const nativePort = numberOrUndefined(getField(raw, "native_port"));
7459
- if (host == null || !validEndpointHost(host) || nativePort == null || nativePort <= 0 || nativePort > 65535) {
7460
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7461
- }
7462
- const nativeTlsPortValue = getField(raw, "native_tls_port");
7463
- const nativeTlsPort = numberOrUndefined(nativeTlsPortValue);
7464
- if (nativeTlsPortValue != null && (nativeTlsPort == null || nativeTlsPort <= 0 || nativeTlsPort > 65535)) {
7465
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7466
- }
7467
- return {
7468
- host,
7469
- nativePort,
7470
- node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
7471
- ...nativeTlsPort == null ? {} : { nativeTlsPort }
7472
- };
7473
- }
7474
- function validEndpointHost(host) {
7475
- if (host.length === 0 || host.trim() !== host || /[\s\\/?#@]/u.test(host)) return false;
7476
- const authorityHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
7477
- try {
7478
- const url = new URL(`ferric://${authorityHost}:6388`);
7479
- return url.hostname !== "" && url.port === "6388" && url.username === "" && url.password === "";
7480
- } catch {
7481
- return false;
7482
- }
7483
- }
7484
- function sameEndpoint(left, right) {
7485
- return normalizedEndpointHost(left.host) === normalizedEndpointHost(right.host) && left.nativePort === right.nativePort && left.nativeTlsPort === right.nativeTlsPort && left.node === right.node;
7486
- }
7487
- function parseFerricUrl2(value) {
7488
- const url = new URL(value);
7489
- if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
7490
- throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
7491
- }
7492
- return {
7493
- host: normalizeFerricUrlHost(url.hostname),
7494
- ...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
7495
- port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
7496
- tls: url.protocol === "ferrics:",
7497
- ...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
7498
- };
7499
- }
7500
- function endpointKeyFor(endpoint) {
7501
- return `${normalizedEndpointHost(endpoint.host)}:${endpoint.nativePort}`;
7502
- }
7503
- function connectionKeyForEndpoint(endpoint, useTls) {
7504
- const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
7505
- return `${useTls ? "ferrics" : "ferric"}://${normalizedEndpointHost(endpoint.host)}:${port}`;
7506
- }
7507
- function connectionKeyFromUrl(url) {
7508
- const parsed = parseFerricUrl2(url);
7509
- return `${parsed.tls ? "ferrics" : "ferric"}://${normalizedEndpointHost(parsed.host)}:${parsed.port}`;
7510
- }
7511
- function normalizedEndpointHost(host) {
7512
- const lower = host.toLowerCase();
7513
- return lower.startsWith("[") && lower.endsWith("]") ? lower.slice(1, -1) : lower;
7514
- }
7515
- function urlFromEndpoint(endpoint, useTls) {
7516
- const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
7517
- const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
7518
- return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
7519
- }
7520
- async function mapSettledWithConcurrency(items, concurrency, operation) {
7521
- const results = new Array(items.length);
7522
- let nextIndex = 0;
7523
- const worker = async () => {
7524
- while (nextIndex < items.length) {
7525
- const index = nextIndex++;
7526
- const item = items[index];
7987
+ this.closePromise = (async () => {
7988
+ let closeError;
7989
+ let closeFailed = false;
7990
+ const executor = await executorPromise.catch(() => void 0);
7527
7991
  try {
7528
- results[index] = { status: "fulfilled", value: await operation(item, index) };
7529
- } catch (reason) {
7530
- results[index] = { status: "rejected", reason };
7992
+ await Promise.resolve(executor?.close?.());
7993
+ } catch (error) {
7994
+ closeFailed = true;
7995
+ closeError = error;
7531
7996
  }
7532
- }
7533
- };
7534
- await Promise.all(Array.from(
7535
- { length: Math.min(concurrency, items.length) },
7536
- async () => await worker()
7537
- ));
7538
- return results;
7539
- }
7540
- function throwFirstRejected(results) {
7541
- for (const result of results) {
7542
- if (result.status === "rejected") throw result.reason;
7543
- }
7544
- }
7545
- var TaskConcurrencyLimiter = class {
7546
- constructor(concurrency) {
7547
- this.concurrency = concurrency;
7997
+ await reconnectPromise?.catch(() => void 0);
7998
+ if (closeFailed) throw closeError;
7999
+ })();
8000
+ await this.closePromise;
7548
8001
  }
7549
- concurrency;
7550
- active = 0;
7551
- head = 0;
7552
- pending = [];
7553
- async run(operation) {
7554
- await this.acquire();
7555
- try {
7556
- return await operation();
7557
- } finally {
7558
- this.release();
8002
+ async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError, replayAllowed = true) {
8003
+ let executor = await this.executorPromise;
8004
+ let retries = 0;
8005
+ for (; ; ) {
8006
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
8007
+ try {
8008
+ return await operation(executor);
8009
+ } catch (error) {
8010
+ if (this.closed || retries >= this.maxRetries || !reconnectable(error)) throw error;
8011
+ if (!replayAllowed) {
8012
+ await this.reconnect(executor).catch(() => void 0);
8013
+ throw error;
8014
+ }
8015
+ for (; ; ) {
8016
+ retries += 1;
8017
+ try {
8018
+ executor = await this.reconnect(executor);
8019
+ break;
8020
+ } catch (reconnectError) {
8021
+ if (this.closed || retries >= this.maxRetries) throw reconnectError;
8022
+ await this.waitBeforeReconnect(retries);
8023
+ }
8024
+ }
8025
+ }
7559
8026
  }
7560
8027
  }
7561
- acquire() {
7562
- if (this.active < this.concurrency) {
7563
- this.active += 1;
7564
- return Promise.resolve();
8028
+ async waitBeforeReconnect(failedAttempts) {
8029
+ const exponent = Math.min(30, Math.max(0, failedAttempts - 1));
8030
+ const base = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent);
8031
+ const jitter = base * (this.jitterPct / 100) * Math.random();
8032
+ try {
8033
+ await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
8034
+ } catch (error) {
8035
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
8036
+ throw error;
7565
8037
  }
7566
- return new Promise((resolve) => this.pending.push(resolve));
7567
8038
  }
7568
- release() {
7569
- const next = this.pending[this.head];
7570
- if (next == null) {
7571
- this.active -= 1;
7572
- return;
7573
- }
7574
- this.head += 1;
7575
- next();
7576
- if (this.head === this.pending.length) {
7577
- this.pending.length = 0;
7578
- this.head = 0;
7579
- } else if (this.head >= 1024 && this.head * 2 >= this.pending.length) {
7580
- this.pending.splice(0, this.head);
7581
- this.head = 0;
7582
- }
8039
+ async reconnect(staleExecutor) {
8040
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
8041
+ const competingReconnect = this.reconnectPromise;
8042
+ if (competingReconnect != null) return await competingReconnect;
8043
+ const observedExecutorPromise = this.executorPromise;
8044
+ const currentExecutor = await observedExecutorPromise.catch(() => void 0);
8045
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
8046
+ if (this.reconnectPromise != null) return await this.reconnectPromise;
8047
+ if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
8048
+ if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
8049
+ this.reconnectPromise = (async () => {
8050
+ await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
8051
+ const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
8052
+ if (this.closed) {
8053
+ await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
8054
+ throw new FerricStoreError("FerricStore client is closed");
8055
+ }
8056
+ this.executorPromise = Promise.resolve(nextExecutor);
8057
+ return nextExecutor;
8058
+ })().finally(() => {
8059
+ this.reconnectPromise = void 0;
8060
+ });
8061
+ return await this.reconnectPromise;
7583
8062
  }
7584
8063
  };
7585
- function normalizedHostSet(hosts) {
7586
- return new Set(
7587
- hosts.filter((host) => host != null && host !== "").map((host) => normalizedEndpointHost(host))
7588
- );
7589
- }
7590
- function bufferStartsWith(value, prefix) {
7591
- return value.byteLength >= prefix.byteLength && value.subarray(0, prefix.byteLength).equals(prefix);
7592
- }
7593
- var OPEN_BRACE = "{".charCodeAt(0);
7594
- var CLOSE_BRACE = "}".charCodeAt(0);
7595
- function hashTagOrKey(key) {
7596
- const start = key.indexOf(OPEN_BRACE);
7597
- if (start < 0) return key;
7598
- const end = key.indexOf(CLOSE_BRACE, start + 1);
7599
- return end > start + 1 ? key.subarray(start + 1, end) : key;
8064
+ function isReconnectableClosedConnectionError(error) {
8065
+ return error instanceof ConnectionClosedError && error.requestDisposition === "unsent";
7600
8066
  }
7601
- function flowHashTag(rest, fallbackKey) {
7602
- const end = rest.indexOf(CLOSE_BRACE);
7603
- return end > 0 ? rest.subarray(0, end) : hashTagOrKey(fallbackKey);
8067
+ function isClosedConnectionError(error) {
8068
+ return error instanceof ConnectionClosedError;
7604
8069
  }
7605
- var ROUTE_SLOT_MASK = 1023;
7606
- var FLOW_KEY_PREFIX = import_node_buffer24.Buffer.from("f:{");
7607
- var VALUE_REF_FLOW_KEY_PREFIX = import_node_buffer24.Buffer.from("X:f:{");
7608
- function routingSlotForKey(key) {
7609
- const keyBytes = import_node_buffer24.Buffer.isBuffer(key) ? key : import_node_buffer24.Buffer.from(key);
7610
- let hashInput;
7611
- if (bufferStartsWith(keyBytes, FLOW_KEY_PREFIX)) {
7612
- hashInput = flowHashTag(keyBytes.subarray(FLOW_KEY_PREFIX.byteLength), keyBytes);
7613
- } else if (bufferStartsWith(keyBytes, VALUE_REF_FLOW_KEY_PREFIX)) {
7614
- hashInput = flowHashTag(keyBytes.subarray(VALUE_REF_FLOW_KEY_PREFIX.byteLength), keyBytes);
7615
- } else {
7616
- hashInput = hashTagOrKey(keyBytes);
8070
+ function isReadOnlyReconnectableClosedConnectionError(error) {
8071
+ const pending = [error];
8072
+ const seen = /* @__PURE__ */ new Set();
8073
+ for (let visited = 0; visited < 16 && pending.length > 0; visited += 1) {
8074
+ const current = pending.pop();
8075
+ if (isReconnectableClosedConnectionError(current)) return true;
8076
+ if (current == null || typeof current !== "object" && typeof current !== "function" || seen.has(current)) continue;
8077
+ seen.add(current);
8078
+ if (current instanceof Error && current.cause != null) pending.push(current.cause);
8079
+ if (current instanceof FerricStoreError && current.raw != null) pending.push(current.raw);
7617
8080
  }
7618
- return crc32(hashInput) & ROUTE_SLOT_MASK;
8081
+ return false;
8082
+ }
8083
+ function normalizeNonNegativeInteger2(value, fallback) {
8084
+ return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
7619
8085
  }
7620
8086
 
8087
+ // src/topology.ts
8088
+ var import_node_buffer30 = require("buffer");
8089
+
7621
8090
  // src/topology-routing.ts
8091
+ var import_node_buffer29 = require("buffer");
8092
+ var import_node_crypto = require("crypto");
7622
8093
  var FLOW_PARTITION_ROUTE_CACHE_MAX = 1024;
7623
8094
  var flowPartitionRouteCache = /* @__PURE__ */ new Map();
7624
8095
  function routingKeyFromProtocolPayload(name, command) {
@@ -7626,9 +8097,18 @@ function routingKeyFromProtocolPayload(name, command) {
7626
8097
  return void 0;
7627
8098
  }
7628
8099
  if (isPlainObject3(command.payload)) {
7629
- for (const field2 of ["key", "partition_key", "id", "owner_flow_id", "parent_id", "root_id", "correlation_id", "scope"]) {
8100
+ for (const field2 of [
8101
+ "key",
8102
+ "partition_key",
8103
+ "id",
8104
+ "owner_flow_id",
8105
+ "parent_flow_id",
8106
+ "root_flow_id",
8107
+ "correlation_id",
8108
+ "scope"
8109
+ ]) {
7630
8110
  const value = getField(command.payload, field2);
7631
- if (typeof value === "string" || import_node_buffer25.Buffer.isBuffer(value)) {
8111
+ if (typeof value === "string" || import_node_buffer29.Buffer.isBuffer(value)) {
7632
8112
  return value;
7633
8113
  }
7634
8114
  }
@@ -7675,9 +8155,9 @@ function flowRoutingData(name, args) {
7675
8155
  if (typeof partition === "string" && partition.toUpperCase() !== "AUTO" && partition.toUpperCase() !== "MIXED") {
7676
8156
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
7677
8157
  }
7678
- if (import_node_buffer25.Buffer.isBuffer(partition)) {
7679
- const text2 = partition.toString("utf8").toUpperCase();
7680
- if (text2 !== "AUTO" && text2 !== "MIXED") {
8158
+ if (import_node_buffer29.Buffer.isBuffer(partition)) {
8159
+ const text3 = partition.toString("utf8").toUpperCase();
8160
+ if (text3 !== "AUTO" && text3 !== "MIXED") {
7681
8161
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
7682
8162
  }
7683
8163
  }
@@ -7766,22 +8246,22 @@ function flowPartitionRoutingKeyFromCommand(command, claim) {
7766
8246
  return { handled: false };
7767
8247
  }
7768
8248
  function isRoutingKey(value) {
7769
- return typeof value === "string" || import_node_buffer25.Buffer.isBuffer(value);
8249
+ return typeof value === "string" || import_node_buffer29.Buffer.isBuffer(value);
7770
8250
  }
7771
8251
  function flowLogicalPartitionRoutingKey(value) {
7772
- if (typeof value !== "string" && !import_node_buffer25.Buffer.isBuffer(value)) {
8252
+ if (typeof value !== "string" && !import_node_buffer29.Buffer.isBuffer(value)) {
7773
8253
  return void 0;
7774
8254
  }
7775
- const bytes2 = import_node_buffer25.Buffer.isBuffer(value) ? value : import_node_buffer25.Buffer.from(value);
7776
- const text2 = bytes2.toString("utf8");
7777
- const autoMatch = /^__flow_auto__:(0|[1-9]\d{0,2})$/u.exec(text2);
8255
+ const bytes2 = import_node_buffer29.Buffer.isBuffer(value) ? value : import_node_buffer29.Buffer.from(value);
8256
+ const text3 = bytes2.toString("utf8");
8257
+ const autoMatch = /^__flow_auto__:(0|[1-9]\d{0,2})$/u.exec(text3);
7778
8258
  if (autoMatch != null) {
7779
8259
  const bucket = Number(autoMatch[1]);
7780
8260
  if (bucket < 256) {
7781
- return `f:{fa:${bucket}}:route`;
8261
+ return `{fa:${bucket}}`;
7782
8262
  }
7783
8263
  }
7784
- const cacheKey = `${import_node_buffer25.Buffer.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
8264
+ const cacheKey = `${import_node_buffer29.Buffer.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
7785
8265
  const cached = flowPartitionRouteCache.get(cacheKey);
7786
8266
  if (cached != null) {
7787
8267
  flowPartitionRouteCache.delete(cacheKey);
@@ -7789,7 +8269,7 @@ function flowLogicalPartitionRoutingKey(value) {
7789
8269
  return cached;
7790
8270
  }
7791
8271
  const digest = (0, import_node_crypto.createHash)("sha256").update(bytes2).digest("base64url");
7792
- const routeKey = `f:{f:${digest}}:route`;
8272
+ const routeKey = `{f:${digest}}`;
7793
8273
  if (flowPartitionRouteCache.size >= FLOW_PARTITION_ROUTE_CACHE_MAX) {
7794
8274
  const oldest = flowPartitionRouteCache.keys().next().value;
7795
8275
  if (oldest != null) flowPartitionRouteCache.delete(oldest);
@@ -7798,18 +8278,17 @@ function flowLogicalPartitionRoutingKey(value) {
7798
8278
  return routeKey;
7799
8279
  }
7800
8280
  function flowAutoIdRoutingKey(value) {
7801
- if (typeof value !== "string" && !import_node_buffer25.Buffer.isBuffer(value)) {
8281
+ if (typeof value !== "string" && !import_node_buffer29.Buffer.isBuffer(value)) {
7802
8282
  return void 0;
7803
8283
  }
7804
- const bytes2 = import_node_buffer25.Buffer.isBuffer(value) ? value : import_node_buffer25.Buffer.from(value);
7805
- const bucket = crc32(bytes2) & 255;
7806
- return `f:{fa:${bucket}}:route`;
8284
+ const bucket = (import_node_buffer29.Buffer.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
8285
+ return `{fa:${bucket}}`;
7807
8286
  }
7808
8287
  function flowClaimLogicalPartitionRoutingKey(value) {
7809
- if (typeof value !== "string" && !import_node_buffer25.Buffer.isBuffer(value)) return void 0;
8288
+ if (typeof value !== "string" && !import_node_buffer29.Buffer.isBuffer(value)) return void 0;
7810
8289
  const selector = commandPart(value);
7811
8290
  if (selector === "AUTO" || selector === "ANY") return void 0;
7812
- if (selector === "GLOBAL") return "f:{f}:route";
8291
+ if (selector === "GLOBAL") return "{f}";
7813
8292
  return flowLogicalPartitionRoutingKey(value);
7814
8293
  }
7815
8294
  function singleShardFlowPartitionKey(values) {
@@ -7821,7 +8300,7 @@ function singleShardFlowClaimPartitionKey(values) {
7821
8300
  return keys.some((key) => key == null) ? void 0 : singleShardKey(keys);
7822
8301
  }
7823
8302
  function singleShardKey(keys) {
7824
- if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !import_node_buffer25.Buffer.isBuffer(key))) {
8303
+ if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !import_node_buffer29.Buffer.isBuffer(key))) {
7825
8304
  return void 0;
7826
8305
  }
7827
8306
  const usable = keys;
@@ -7832,7 +8311,7 @@ function singleShardKey(keys) {
7832
8311
  const firstSlot = routingSlotForKey(first);
7833
8312
  return usable.every((key) => routingSlotForKey(key) === firstSlot) ? first : void 0;
7834
8313
  }
7835
- function commandName2(args) {
8314
+ function commandName3(args) {
7836
8315
  return commandView(args).name;
7837
8316
  }
7838
8317
  var commandPart = commandToken;
@@ -7854,7 +8333,7 @@ function routedKeyGroups(keys, routeKey) {
7854
8333
  const groups = /* @__PURE__ */ new Map();
7855
8334
  for (let index = 0; index < keys.length; index += 1) {
7856
8335
  const key = keys[index];
7857
- if (typeof key !== "string" && !import_node_buffer25.Buffer.isBuffer(key)) return void 0;
8336
+ if (typeof key !== "string" && !import_node_buffer29.Buffer.isBuffer(key)) return void 0;
7858
8337
  const route = routeKey(key);
7859
8338
  const groupKey = `${route.endpointKey}\0${route.laneId}`;
7860
8339
  const group = groups.get(groupKey);
@@ -8095,7 +8574,16 @@ function isExplicitlySafeReroute(error) {
8095
8574
  if (error instanceof ConnectionClosedError) {
8096
8575
  return error.requestDisposition === "unsent";
8097
8576
  }
8098
- return error instanceof RerouteError && getField(error.raw, "safe_to_retry") === true;
8577
+ return error instanceof RerouteError && error.retryable === true && error.safeToRetry === true;
8578
+ }
8579
+ function retryAfterMs(error) {
8580
+ return error instanceof FerricStoreError ? error.retryAfterMs ?? 0 : 0;
8581
+ }
8582
+ async function waitForExplicitlySafeReroute(error) {
8583
+ if (!isExplicitlySafeReroute(error)) return false;
8584
+ const delayMs = retryAfterMs(error);
8585
+ if (delayMs > 0) await sleep(delayMs);
8586
+ return true;
8099
8587
  }
8100
8588
 
8101
8589
  // src/topology-endpoint-trust.ts
@@ -8237,7 +8725,7 @@ var TopologyPipelineExecutor = class {
8237
8725
  commands.forEach((command, index) => {
8238
8726
  const routed = this.context.routeData(command);
8239
8727
  if (routed == null) {
8240
- if (decomposableCommandNames.has(commandName2(command) ?? "")) standalone.push({ command, index });
8728
+ if (decomposableCommandNames.has(commandName3(command) ?? "")) standalone.push({ command, index });
8241
8729
  else {
8242
8730
  controlCommands.push(command);
8243
8731
  controlIndices.push(index);
@@ -8453,7 +8941,7 @@ function topologyRouteIsCurrent(topology, route) {
8453
8941
  // src/topology-route-planner.ts
8454
8942
  function topologyRouteData(args, route) {
8455
8943
  if (args.length === 0) return void 0;
8456
- const name = commandName2(args);
8944
+ const name = commandName3(args);
8457
8945
  if (name?.startsWith("FLOW.") === true) {
8458
8946
  const routed = flowRoutingData(name, args);
8459
8947
  return routed == null ? void 0 : {
@@ -8482,7 +8970,7 @@ var TopologyScatterExecutor = class {
8482
8970
  }
8483
8971
  dependencies;
8484
8972
  async execute(args) {
8485
- const name = commandName2(args);
8973
+ const name = commandName3(args);
8486
8974
  if (name === "MGET") {
8487
8975
  const value = await this.scatterArray(name, args.slice(1));
8488
8976
  return value == null ? { handled: false } : { handled: true, value };
@@ -8564,6 +9052,18 @@ var TopologyScatterExecutor = class {
8564
9052
  }
8565
9053
  };
8566
9054
 
9055
+ // src/topology-reroute-retry.ts
9056
+ async function canRetrySafeReroute(error, attempt, retryAllowed, refresh, assertOpen) {
9057
+ if (attempt !== 0 || !isRetryableRouteError(error)) return false;
9058
+ const refreshed = await refresh().then(
9059
+ () => true,
9060
+ () => false
9061
+ );
9062
+ if (!refreshed || !retryAllowed || !await waitForExplicitlySafeReroute(error)) return false;
9063
+ assertOpen();
9064
+ return true;
9065
+ }
9066
+
8567
9067
  // src/topology.ts
8568
9068
  var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8569
9069
  adapterOptions;
@@ -8749,6 +9249,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8749
9249
  this.assertOpen();
8750
9250
  const snapshot = snapshotCommandArguments(args);
8751
9251
  assertCommandHasStableConnectionState(snapshot);
9252
+ const casMutation = isCasMutation(snapshot);
8752
9253
  let rerouteAttempt = 0;
8753
9254
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8754
9255
  const routed = this.routeData(snapshot);
@@ -8764,7 +9265,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8764
9265
  try {
8765
9266
  return routed.command == null ? await adapter.executeCommandOnLane(snapshot, routed.route.laneId) : await adapter.executeProtocolCommand(routed.command, routed.route.laneId);
8766
9267
  } catch (error) {
8767
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9268
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !casMutation)) {
8768
9269
  rerouteAttempt += 1;
8769
9270
  continue;
8770
9271
  }
@@ -8806,6 +9307,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8806
9307
  assertCommandHasStableConnectionState(command);
8807
9308
  }
8808
9309
  let rerouteAttempt = 0;
9310
+ const containsCasMutation = snapshot.some(isCasMutation);
8809
9311
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8810
9312
  const route = singleRouteForCommands(snapshot, (command) => this.routeData(command));
8811
9313
  if (route == null) return void 0;
@@ -8814,7 +9316,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8814
9316
  try {
8815
9317
  return await adapter.executeFusedPipelineOnLane(snapshot, route.laneId, snapshotOptions);
8816
9318
  } catch (error) {
8817
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9319
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !containsCasMutation)) {
8818
9320
  rerouteAttempt += 1;
8819
9321
  continue;
8820
9322
  }
@@ -8823,13 +9325,14 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8823
9325
  }
8824
9326
  throw new StaleTopologyRouteError();
8825
9327
  }
8826
- async refreshAndCanRetrySafeReroute(error, attempt) {
8827
- if (attempt !== 0 || !isRetryableRouteError(error)) return false;
8828
- const refreshed = await this.refreshTopology().then(
8829
- () => true,
8830
- () => false
9328
+ async refreshAndCanRetrySafeReroute(error, attempt, retryAllowed = true) {
9329
+ return await canRetrySafeReroute(
9330
+ error,
9331
+ attempt,
9332
+ retryAllowed,
9333
+ async () => await this.refreshTopology(),
9334
+ () => this.assertOpen()
8831
9335
  );
8832
- return refreshed && isExplicitlySafeReroute(error);
8833
9336
  }
8834
9337
  async executePipelineOnRoute(commands, route, options) {
8835
9338
  const adapter = await this.adapterForCurrentRoute(route);
@@ -8919,7 +9422,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8919
9422
  };
8920
9423
 
8921
9424
  // src/response-map-preservation.ts
8922
- var import_node_buffer27 = require("buffer");
9425
+ var import_node_buffer31 = require("buffer");
8923
9426
  function toStringKeyMapPreservingValues(value) {
8924
9427
  if (value == null) return void 0;
8925
9428
  const result = {};
@@ -8929,7 +9432,7 @@ function toStringKeyMapPreservingValues(value) {
8929
9432
  }
8930
9433
  return result;
8931
9434
  }
8932
- if (typeof value === "object" && !Array.isArray(value) && !import_node_buffer27.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9435
+ if (typeof value === "object" && !Array.isArray(value) && !import_node_buffer31.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
8933
9436
  for (const [key, item] of Object.entries(value)) {
8934
9437
  setOwnValue(result, text(key), normalizeMapStructurePreservingBytes(item));
8935
9438
  }
@@ -8938,7 +9441,7 @@ function toStringKeyMapPreservingValues(value) {
8938
9441
  return void 0;
8939
9442
  }
8940
9443
  function normalizeMapStructurePreservingBytes(value) {
8941
- if (import_node_buffer27.Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
9444
+ if (import_node_buffer31.Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
8942
9445
  if (value instanceof Map) {
8943
9446
  const result = {};
8944
9447
  for (const [key, item] of value.entries()) {
@@ -8960,7 +9463,7 @@ function normalizeMapStructurePreservingBytes(value) {
8960
9463
  }
8961
9464
 
8962
9465
  // src/native-kv-responses.ts
8963
- var import_node_buffer28 = require("buffer");
9466
+ var import_node_buffer32 = require("buffer");
8964
9467
  function rateLimitResultFromResp(value) {
8965
9468
  if (!Array.isArray(value) || value.length !== 4) {
8966
9469
  throw new TypeError("RATELIMIT.ADD returned an unexpected response");
@@ -9003,30 +9506,27 @@ function fetchOrComputeResultFromResp(value, codec) {
9003
9506
  shouldCompute: false
9004
9507
  };
9005
9508
  }
9006
- if (status !== "compute") throw new TypeError("FETCH_OR_COMPUTE returned an unexpected response");
9509
+ if (status !== "compute" || value.length !== 3) {
9510
+ throw new TypeError("FETCH_OR_COMPUTE returned an unexpected response");
9511
+ }
9007
9512
  return {
9008
9513
  status,
9009
9514
  computeHint: responseBytes(value[1], "FETCH_OR_COMPUTE"),
9010
- ...value.length === 3 ? {
9011
- computeMode: "fenced",
9012
- computeToken: responseBytes(value[2], "FETCH_OR_COMPUTE")
9013
- } : {
9014
- computeMode: "legacy",
9015
- computeToken: null
9016
- },
9515
+ computeMode: "fenced",
9516
+ computeToken: responseBytes(value[2], "FETCH_OR_COMPUTE"),
9017
9517
  hit: false,
9018
9518
  shouldCompute: true
9019
9519
  };
9020
9520
  }
9021
9521
  function decodePayload(codec, value) {
9022
9522
  if (value == null) return null;
9023
- if (import_node_buffer28.Buffer.isBuffer(value)) return codec.decode(value);
9024
- if (value instanceof Uint8Array) return codec.decode(import_node_buffer28.Buffer.from(value));
9025
- if (typeof value === "string") return codec.decode(import_node_buffer28.Buffer.from(value));
9523
+ if (import_node_buffer32.Buffer.isBuffer(value)) return codec.decode(value);
9524
+ if (value instanceof Uint8Array) return codec.decode(import_node_buffer32.Buffer.from(value));
9525
+ if (typeof value === "string") return codec.decode(import_node_buffer32.Buffer.from(value));
9026
9526
  return normalizeRefMeta(value);
9027
9527
  }
9028
9528
  function requiredResponseString(value, context) {
9029
- if (typeof value !== "string" && !import_node_buffer28.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9529
+ if (typeof value !== "string" && !import_node_buffer32.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9030
9530
  throw new TypeError(`${context} returned an unexpected response`);
9031
9531
  }
9032
9532
  const result = text(value);
@@ -9043,7 +9543,7 @@ function requiredNonNegativeInteger(value, context) {
9043
9543
  return result;
9044
9544
  }
9045
9545
  function responseBytes(value, context) {
9046
- if (typeof value !== "string" && !import_node_buffer28.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9546
+ if (typeof value !== "string" && !import_node_buffer32.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9047
9547
  throw new TypeError(`${context} returned an unexpected response`);
9048
9548
  }
9049
9549
  return bytes(value);
@@ -9128,6 +9628,9 @@ function flowRecordFromResp(value, codec) {
9128
9628
  field(value, "max_active_ms"),
9129
9629
  "FLOW record max_active_ms"
9130
9630
  );
9631
+ const rawError = field(value, "error");
9632
+ const error = flowError(rawError, codec);
9633
+ const failureReason = optionalResponseString(field(rawError, "reason"), "FLOW record error reason");
9131
9634
  return {
9132
9635
  id: requiredTextField(value, "id", "FLOW record"),
9133
9636
  type: requiredTextField(value, "type", "FLOW record"),
@@ -9142,6 +9645,8 @@ function flowRecordFromResp(value, codec) {
9142
9645
  rootFlowId: optionalResponseString(field(value, "root_flow_id"), "FLOW record root_flow_id"),
9143
9646
  correlationId: optionalResponseString(field(value, "correlation_id"), "FLOW record correlation_id"),
9144
9647
  ...maxActiveMs == null ? {} : { maxActiveMs },
9648
+ ...error == null ? {} : { error },
9649
+ ...failureReason == null ? {} : { failureReason },
9145
9650
  valueRefs: optionalMapField(value, "value_refs", "FLOW record"),
9146
9651
  values,
9147
9652
  valueSizes: optionalMapField(value, "value_sizes", "FLOW record"),
@@ -9153,6 +9658,21 @@ function flowRecordFromResp(value, codec) {
9153
9658
  raw: value
9154
9659
  };
9155
9660
  }
9661
+ function flowError(value, codec) {
9662
+ if (value == null) return void 0;
9663
+ const reason = optionalResponseString(field(value, "reason"), "FLOW record error reason");
9664
+ if (reason === "max_active_ms") {
9665
+ const maxActiveMs = optionalPositiveResponseInteger(
9666
+ field(value, "max_active_ms"),
9667
+ "FLOW record error max_active_ms"
9668
+ );
9669
+ if (maxActiveMs == null) {
9670
+ throw new TypeError("FLOW record error max_active_ms returned an unexpected response");
9671
+ }
9672
+ return { maxActiveMs, reason };
9673
+ }
9674
+ return decodePayload2(codec, value);
9675
+ }
9156
9676
  function decodePayload2(codec, value) {
9157
9677
  if (value == null) {
9158
9678
  return null;
@@ -9733,7 +10253,7 @@ function groupAutoPartitionItems(items) {
9733
10253
  }
9734
10254
 
9735
10255
  // src/client-values.ts
9736
- var import_node_buffer29 = require("buffer");
10256
+ var import_node_buffer33 = require("buffer");
9737
10257
  async function valueMGetEntries(client, refs, options = {}) {
9738
10258
  const refCount = refs.length;
9739
10259
  if (refCount === 0) {
@@ -9768,12 +10288,12 @@ async function valueMGetEntries(client, refs, options = {}) {
9768
10288
  const item = response[index];
9769
10289
  if (item == null) {
9770
10290
  entries[index] = { found: false };
9771
- } else if (import_node_buffer29.Buffer.isBuffer(item)) {
10291
+ } else if (import_node_buffer33.Buffer.isBuffer(item)) {
9772
10292
  entries[index] = { found: true, value: client.codec.decode(item) };
9773
10293
  } else if (item instanceof Uint8Array) {
9774
- entries[index] = { found: true, value: client.codec.decode(import_node_buffer29.Buffer.from(item)) };
10294
+ entries[index] = { found: true, value: client.codec.decode(import_node_buffer33.Buffer.from(item)) };
9775
10295
  } else if (typeof item === "string") {
9776
- entries[index] = { found: true, value: client.codec.decode(import_node_buffer29.Buffer.from(item)) };
10296
+ entries[index] = { found: true, value: client.codec.decode(import_node_buffer33.Buffer.from(item)) };
9777
10297
  } else {
9778
10298
  entries[index] = { found: true, value: item };
9779
10299
  }
@@ -9782,10 +10302,10 @@ async function valueMGetEntries(client, refs, options = {}) {
9782
10302
  }
9783
10303
 
9784
10304
  // src/auto-batch.ts
9785
- var import_node_buffer31 = require("buffer");
10305
+ var import_node_buffer35 = require("buffer");
9786
10306
 
9787
10307
  // src/auto-batch-ordering.ts
9788
- var import_node_buffer30 = require("buffer");
10308
+ var import_node_buffer34 = require("buffer");
9789
10309
  function autoBatchOrderingPlan(batch) {
9790
10310
  const accesses = /* @__PURE__ */ new Map();
9791
10311
  const fallbackDependencies = [];
@@ -9900,13 +10420,13 @@ function flowManyAutoBatchIds(command, name) {
9900
10420
  return fixedFlowItemIds(
9901
10421
  command,
9902
10422
  mixed ? 4 : 3,
9903
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && import_node_buffer30.Buffer.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
10423
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && import_node_buffer34.Buffer.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
9904
10424
  );
9905
10425
  }
9906
10426
  return fixedFlowItemIds(
9907
10427
  command,
9908
10428
  mixed ? 4 : 3,
9909
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer30.Buffer.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
10429
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer34.Buffer.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
9910
10430
  );
9911
10431
  }
9912
10432
  function createManyAutoBatchIds(command) {
@@ -9919,7 +10439,7 @@ function createManyAutoBatchIds(command) {
9919
10439
  const ids = fixedFlowItemIds(
9920
10440
  command,
9921
10441
  width,
9922
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer30.Buffer.isBuffer(command[itemIndex + width - 1]),
10442
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer34.Buffer.isBuffer(command[itemIndex + width - 1]),
9923
10443
  markerIndex
9924
10444
  );
9925
10445
  if (ids != null) return ids;
@@ -9937,7 +10457,7 @@ function extendedCreateManyAutoBatchIds(command, markerIndex) {
9937
10457
  let cursor = markerIndex + 2;
9938
10458
  for (let itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
9939
10459
  const id = command[cursor];
9940
- if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !import_node_buffer30.Buffer.isBuffer(command[cursor + 2])) return void 0;
10460
+ if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !import_node_buffer34.Buffer.isBuffer(command[cursor + 2])) return void 0;
9941
10461
  ids.push(id);
9942
10462
  cursor += 3;
9943
10463
  const afterValues = skipExtendedNamedItems(command, cursor, true);
@@ -9956,7 +10476,7 @@ function skipExtendedNamedItems(command, countIndex, encodedValues) {
9956
10476
  for (let index = 0; index < count; index += 1) {
9957
10477
  const name = command[firstItem + index * 2];
9958
10478
  const value = command[firstItem + index * 2 + 1];
9959
- if (!isAutoBatchResourceValue(name) || (encodedValues ? !import_node_buffer30.Buffer.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
10479
+ if (!isAutoBatchResourceValue(name) || (encodedValues ? !import_node_buffer34.Buffer.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
9960
10480
  }
9961
10481
  return firstItem + count * 2;
9962
10482
  }
@@ -9971,7 +10491,7 @@ function runStepsManyAutoBatchIds(command) {
9971
10491
  ids.push(item);
9972
10492
  continue;
9973
10493
  }
9974
- if (typeof item !== "object" || item == null || Array.isArray(item) || import_node_buffer30.Buffer.isBuffer(item)) return void 0;
10494
+ if (typeof item !== "object" || item == null || Array.isArray(item) || import_node_buffer34.Buffer.isBuffer(item)) return void 0;
9975
10495
  const id = item.id;
9976
10496
  if (!isAutoBatchResourceValue(id)) return void 0;
9977
10497
  ids.push(id);
@@ -10012,7 +10532,7 @@ function flowValuePutOwner(command, start) {
10012
10532
  }
10013
10533
  var flowValuePutOptionTokens = /* @__PURE__ */ new Set(["NAME", "NOW", "OVERRIDE", "OWNER_FLOW_ID", "PARTITION", "TTL", "TTL_MS"]);
10014
10534
  function isAutoBatchResourceValue(value) {
10015
- return typeof value === "string" || import_node_buffer30.Buffer.isBuffer(value);
10535
+ return typeof value === "string" || import_node_buffer34.Buffer.isBuffer(value);
10016
10536
  }
10017
10537
  function isFencingToken(value) {
10018
10538
  return typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint";
@@ -10021,7 +10541,7 @@ function nonNegativeItemCount(value) {
10021
10541
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
10022
10542
  }
10023
10543
  function autoBatchResourceKey(namespace, value) {
10024
- return `${namespace}:${import_node_buffer30.Buffer.from(value).toString("base64")}`;
10544
+ return `${namespace}:${import_node_buffer34.Buffer.from(value).toString("base64")}`;
10025
10545
  }
10026
10546
  function autoBatchCommandName(command) {
10027
10547
  return commandView(command).name ?? null;
@@ -10430,15 +10950,15 @@ function claimFailure(error) {
10430
10950
  }
10431
10951
 
10432
10952
  // src/client-flow-support.ts
10433
- var import_node_buffer37 = require("buffer");
10953
+ var import_node_buffer42 = require("buffer");
10434
10954
 
10435
10955
  // src/client-core.ts
10436
- var import_node_buffer36 = require("buffer");
10956
+ var import_node_buffer40 = require("buffer");
10437
10957
 
10438
10958
  // src/client-core-helpers.ts
10439
- var import_node_buffer32 = require("buffer");
10959
+ var import_node_buffer36 = require("buffer");
10440
10960
  function bgsaveResponse(response) {
10441
- if ((typeof response === "string" || import_node_buffer32.Buffer.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
10961
+ if ((typeof response === "string" || import_node_buffer36.Buffer.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
10442
10962
  return true;
10443
10963
  }
10444
10964
  return okResponse(response);
@@ -10449,11 +10969,11 @@ function clientNameResponse(response) {
10449
10969
  function fetchOrComputeCompletionToken(options) {
10450
10970
  if (options == null || !Object.hasOwn(options, "computeToken") || options.computeToken === void 0) {
10451
10971
  throw new TypeError(
10452
- "fetch-or-compute completion requires computeToken; pass null only for a legacy tokenless lease"
10972
+ "fetch-or-compute completion requires computeToken"
10453
10973
  );
10454
10974
  }
10455
- if (options.computeToken !== null && !import_node_buffer32.Buffer.isBuffer(options.computeToken)) {
10456
- throw new TypeError("fetch-or-compute computeToken must be a Buffer or explicit null for a legacy lease");
10975
+ if (!import_node_buffer36.Buffer.isBuffer(options.computeToken)) {
10976
+ throw new TypeError("fetch-or-compute computeToken must be a Buffer");
10457
10977
  }
10458
10978
  return options.computeToken;
10459
10979
  }
@@ -10475,10 +10995,10 @@ function unsupportedClientCaching() {
10475
10995
  }
10476
10996
 
10477
10997
  // src/client-base.ts
10478
- var import_node_buffer34 = require("buffer");
10998
+ var import_node_buffer38 = require("buffer");
10479
10999
 
10480
11000
  // src/client-executor.ts
10481
- var import_node_buffer33 = require("buffer");
11001
+ var import_node_buffer37 = require("buffer");
10482
11002
  var ErrorMappingExecutor = class {
10483
11003
  constructor(executor) {
10484
11004
  this.executor = executor;
@@ -10575,14 +11095,17 @@ var FerricStoreClientBase = class {
10575
11095
  };
10576
11096
  }
10577
11097
  async command(...args) {
11098
+ assertAtomicKeyValueCommandSharesSlot(args);
10578
11099
  return await executeCommandArgs(this.executor, args);
10579
11100
  }
10580
11101
  /** Execute an already-built command without a JavaScript variadic-call limit. */
10581
11102
  async commandArgs(args) {
11103
+ assertAtomicKeyValueCommandSharesSlot(args);
10582
11104
  return await executeCommandArgs(this.executor, args);
10583
11105
  }
10584
11106
  async pipeline(commands, options) {
10585
11107
  const snapshot = snapshotPipelineCommands(commands);
11108
+ for (const command of snapshot) assertAtomicKeyValueCommandSharesSlot(command);
10586
11109
  const snapshotOptions = snapshotPipelineOptions(options);
10587
11110
  const results = this.executor.executePipeline != null ? await this.executor.executePipeline(snapshot, snapshotOptions) : await executeCommandArraysIndividually(
10588
11111
  async (command) => await this.commandArgs(command),
@@ -10809,14 +11332,14 @@ var FerricStoreAdministrationClient = class extends FerricStoreClientBase {
10809
11332
  };
10810
11333
 
10811
11334
  // src/store-utilities.ts
10812
- var import_node_buffer35 = require("buffer");
11335
+ var import_node_buffer39 = require("buffer");
10813
11336
  function encode(codec, value) {
10814
11337
  return codec.encode(value);
10815
11338
  }
10816
11339
  function decode(codec, value) {
10817
11340
  if (value == null) return null;
10818
- if (import_node_buffer35.Buffer.isBuffer(value)) return codec.decode(value);
10819
- if (value instanceof Uint8Array) return codec.decode(import_node_buffer35.Buffer.from(value));
11341
+ if (import_node_buffer39.Buffer.isBuffer(value)) return codec.decode(value);
11342
+ if (value instanceof Uint8Array) return codec.decode(import_node_buffer39.Buffer.from(value));
10820
11343
  return value;
10821
11344
  }
10822
11345
  function number(value) {
@@ -10877,7 +11400,7 @@ function requireNonEmpty(values, command, noun = "argument") {
10877
11400
  if (values.length === 0) throw new TypeError(`${command} requires at least one ${noun}`);
10878
11401
  return values;
10879
11402
  }
10880
- function commandName3(args) {
11403
+ function commandName4(args) {
10881
11404
  return typeof args[0] === "string" ? args[0] : "command";
10882
11405
  }
10883
11406
  function commandArgs(client, args) {
@@ -10892,7 +11415,7 @@ function arrayOrRest(values) {
10892
11415
  function appendXReadStreams(args, streams) {
10893
11416
  const offset = args.length;
10894
11417
  const streamCount = streams.length;
10895
- requireNonEmpty(streams, commandName3(args), "stream");
11418
+ requireNonEmpty(streams, commandName4(args), "stream");
10896
11419
  args.length = offset + 1 + streamCount * 2;
10897
11420
  args[offset] = "STREAMS";
10898
11421
  for (let index = 0; index < streamCount; index += 1) {
@@ -10902,7 +11425,7 @@ function appendXReadStreams(args, streams) {
10902
11425
  const stream = streams[index];
10903
11426
  if (stream == null) throw new TypeError("XREAD streams must contain only stream descriptors");
10904
11427
  if (!Object.hasOwn(stream, "key") || !Object.hasOwn(stream, "id")) {
10905
- throw new TypeError(`${commandName3(args)} streams require own key and id fields`);
11428
+ throw new TypeError(`${commandName4(args)} streams require own key and id fields`);
10906
11429
  }
10907
11430
  args[offset + 1 + index] = stream.key;
10908
11431
  args[offset + 1 + streamCount + index] = stream.id;
@@ -10924,7 +11447,7 @@ function concatArgs(...parts) {
10924
11447
  return args;
10925
11448
  }
10926
11449
  function encodedArgs(prefix, codec, values, allowEmpty = false) {
10927
- if (!allowEmpty) requireNonEmpty(values, commandName3(prefix));
11450
+ if (!allowEmpty) requireNonEmpty(values, commandName4(prefix));
10928
11451
  const args = new Array(prefix.length + values.length);
10929
11452
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10930
11453
  for (let index = 0; index < values.length; index += 1) {
@@ -10938,7 +11461,7 @@ function encodedArgs(prefix, codec, values, allowEmpty = false) {
10938
11461
  function keyValueArgs(prefix, codec, entries) {
10939
11462
  const validateTuples = Array.isArray(entries);
10940
11463
  const pairs = validateTuples ? entries : Object.entries(entries);
10941
- requireNonEmpty(pairs, commandName3(prefix), "entry");
11464
+ requireNonEmpty(pairs, commandName4(prefix), "entry");
10942
11465
  const args = new Array(prefix.length + pairs.length * 2);
10943
11466
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10944
11467
  for (let index = 0; index < pairs.length; index += 1) {
@@ -10952,7 +11475,7 @@ function keyValueArgs(prefix, codec, entries) {
10952
11475
  return args;
10953
11476
  }
10954
11477
  function encodedEntryArgs(prefix, codec, entries) {
10955
- requireNonEmpty(entries, commandName3(prefix), "entry");
11478
+ requireNonEmpty(entries, commandName4(prefix), "entry");
10956
11479
  const args = new Array(prefix.length + entries.length * 2);
10957
11480
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10958
11481
  for (let index = 0; index < entries.length; index += 1) {
@@ -11222,12 +11745,15 @@ var TopKStore = class {
11222
11745
  const width = ownOption(options, "width");
11223
11746
  const depth = ownOption(options, "depth");
11224
11747
  const decay = ownOption(options, "decay");
11225
- const tuningCount = [width, depth, decay].filter((value) => value != null).length;
11226
- if (tuningCount !== 0 && tuningCount !== 3) {
11227
- throw new TypeError("TOPK.RESERVE width, depth, and decay must be provided together");
11748
+ if (decay != null || Object.hasOwn(options, "decay")) {
11749
+ throw new TypeError("TOPK.RESERVE decay is not supported by FerricStore 0.8");
11750
+ }
11751
+ const tuningCount = [width, depth].filter((value) => value != null).length;
11752
+ if (tuningCount !== 0 && tuningCount !== 2) {
11753
+ throw new TypeError("TOPK.RESERVE width and depth must be provided together");
11228
11754
  }
11229
- if (tuningCount === 3) {
11230
- args.push(width, depth, decay);
11755
+ if (tuningCount === 2) {
11756
+ args.push(width, depth);
11231
11757
  }
11232
11758
  return okResponse(await commandArgs(this.client, args));
11233
11759
  }
@@ -11633,10 +12159,14 @@ var KeyValueStore = class {
11633
12159
  );
11634
12160
  }
11635
12161
  async mset(entries) {
11636
- return okResponse(await commandArgs(this.client, keyValueArgs(["MSET"], this.client.codec, entries)));
12162
+ const args = keyValueArgs(["MSET"], this.client.codec, entries);
12163
+ assertKeyValueCommandSharesSlot(args, "MSET");
12164
+ return okResponse(await commandArgs(this.client, args));
11637
12165
  }
11638
12166
  async msetnx(entries) {
11639
- return binaryBooleanResponse(await commandArgs(this.client, keyValueArgs(["MSETNX"], this.client.codec, entries)));
12167
+ const args = keyValueArgs(["MSETNX"], this.client.codec, entries);
12168
+ assertKeyValueCommandSharesSlot(args, "MSETNX");
12169
+ return binaryBooleanResponse(await commandArgs(this.client, args));
11640
12170
  }
11641
12171
  async incr(key) {
11642
12172
  return integerReply(await this.client.command("INCR", key));
@@ -12706,7 +13236,8 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
12706
13236
  return okResponse(await this.command(
12707
13237
  "FETCH_OR_COMPUTE_RESULT",
12708
13238
  key,
12709
- ...computeToken === null ? [encoded] : [computeToken, encoded],
13239
+ computeToken,
13240
+ encoded,
12710
13241
  options.ttlMs
12711
13242
  ));
12712
13243
  }
@@ -12715,7 +13246,7 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
12715
13246
  return okResponse(await this.command(
12716
13247
  "FETCH_OR_COMPUTE_ERROR",
12717
13248
  key,
12718
- ...computeToken === null ? [] : [computeToken],
13249
+ computeToken,
12719
13250
  message
12720
13251
  ));
12721
13252
  }
@@ -12841,7 +13372,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
12841
13372
  }
12842
13373
  async effectGet(id, effectKey, options = {}) {
12843
13374
  const args = ["FLOW.EFFECT.GET", id, "EFFECT_KEY", effectKey];
12844
- append(args, "PARTITION", options.partitionKey ?? autoPartitionKeyForId(id));
13375
+ append(args, "PARTITION", options.partitionKey);
12845
13376
  return optionalAdminRecord(await this.commandArgs(args), "FLOW.EFFECT.GET");
12846
13377
  }
12847
13378
  async effectStatus(command, id, effectKey, options) {
@@ -13013,7 +13544,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
13013
13544
  }
13014
13545
  async governanceLedger(id, options = {}) {
13015
13546
  const args = ["FLOW.GOVERNANCE.LEDGER", id];
13016
- append(args, "PARTITION", options.partitionKey ?? autoPartitionKeyForId(id));
13547
+ append(args, "PARTITION", options.partitionKey);
13017
13548
  append(args, "LIMIT", options.limit);
13018
13549
  append(args, "FROM_MS", options.fromMs);
13019
13550
  append(args, "TO_MS", options.toMs);
@@ -13029,7 +13560,7 @@ async function executeProducerWriteWithBackpressure(execute, policy, signal) {
13029
13560
  try {
13030
13561
  return await execute();
13031
13562
  } catch (error) {
13032
- if (!(error instanceof OverloadedError) || attempt >= policy.maxRetries) throw error;
13563
+ if (!(error instanceof OverloadedError) || error.retryable !== true || error.safeToRetry !== true || attempt >= policy.maxRetries) throw error;
13033
13564
  const exponential = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** attempt);
13034
13565
  const base = Math.min(policy.maxDelayMs, error.retryAfterMs ?? exponential);
13035
13566
  const jitter = base * (policy.jitterPct / 100) * Math.random();
@@ -13043,6 +13574,165 @@ function throwIfClosed(signal) {
13043
13574
  throw new FerricStoreError("FerricStore client is closed", { raw: signal.reason });
13044
13575
  }
13045
13576
 
13577
+ // src/flow-policy.ts
13578
+ var import_node_buffer41 = require("buffer");
13579
+ var MAX_FLOW_POLICY_GENERATION = Number.MAX_SAFE_INTEGER;
13580
+ function assertFlowPolicyGeneration(value) {
13581
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
13582
+ throw new TypeError("expectedGeneration must be a nonnegative safe integer");
13583
+ }
13584
+ }
13585
+ function flowPolicySnapshotFromResp(value) {
13586
+ const policy = requiredRecord(value, "FLOW policy snapshot");
13587
+ const maxActiveMs = optionalPositiveInteger(field(policy, "max_active_ms"), "max_active_ms");
13588
+ const state = optionalText2(field(policy, "state"), "state");
13589
+ const mode = optionalMode(field(policy, "mode"), "mode");
13590
+ const version = optionalVersion(field(policy, "version"));
13591
+ const indexedStateMeta = optionalText2(field(policy, "indexed_state_meta"), "indexed_state_meta");
13592
+ const governance = optionalRecord(field(policy, "governance"), "governance");
13593
+ const states = optionalStates(field(policy, "states"));
13594
+ return {
13595
+ type: requiredText(field(policy, "type"), "type"),
13596
+ generation: nonNegativeInteger(field(policy, "generation"), "generation"),
13597
+ ...state == null ? {} : { state },
13598
+ ...version == null ? {} : { version },
13599
+ ...mode == null ? {} : { mode },
13600
+ ...maxActiveMs == null ? {} : { maxActiveMs },
13601
+ retry: retrySnapshot(field(policy, "retry"), "retry"),
13602
+ retention: retentionSnapshot(field(policy, "retention"), "retention"),
13603
+ indexedAttributes: stringArray(field(policy, "indexed_attributes"), "indexed_attributes"),
13604
+ ...indexedStateMeta == null ? {} : { indexedStateMeta },
13605
+ ...governance == null ? {} : { governance },
13606
+ ...states == null ? {} : { states },
13607
+ raw: value
13608
+ };
13609
+ }
13610
+ function retrySnapshot(value, context) {
13611
+ const retry2 = requiredRecord(value, `FLOW policy ${context}`);
13612
+ const backoff = requiredRecord(field(retry2, "backoff"), `FLOW policy ${context}.backoff`);
13613
+ return {
13614
+ maxRetries: nonNegativeInteger(field(retry2, "max_retries"), `${context}.max_retries`),
13615
+ backoff: {
13616
+ kind: backoffKind(field(backoff, "kind"), `${context}.backoff.kind`),
13617
+ baseMs: nonNegativeInteger(field(backoff, "base_ms"), `${context}.backoff.base_ms`),
13618
+ maxMs: nonNegativeInteger(field(backoff, "max_ms"), `${context}.backoff.max_ms`),
13619
+ jitterPct: nonNegativeInteger(field(backoff, "jitter_pct"), `${context}.backoff.jitter_pct`)
13620
+ },
13621
+ exhaustedTo: requiredText(field(retry2, "exhausted_to"), `${context}.exhausted_to`)
13622
+ };
13623
+ }
13624
+ function retentionSnapshot(value, context) {
13625
+ const retention = requiredRecord(value, `FLOW policy ${context}`);
13626
+ return {
13627
+ ttlMs: nonNegativeInteger(field(retention, "ttl_ms"), `${context}.ttl_ms`),
13628
+ historyMaxEvents: nonNegativeInteger(
13629
+ field(retention, "history_max_events"),
13630
+ `${context}.history_max_events`
13631
+ )
13632
+ };
13633
+ }
13634
+ function optionalStates(value) {
13635
+ if (value == null) return void 0;
13636
+ const source = requiredRecord(value, "FLOW policy states");
13637
+ const result = {};
13638
+ for (const [state, raw] of Object.entries(source)) {
13639
+ const policy = requiredRecord(raw, `FLOW policy state ${state}`);
13640
+ const maxActiveMs = optionalPositiveInteger(
13641
+ field(policy, "max_active_ms"),
13642
+ `states.${state}.max_active_ms`
13643
+ );
13644
+ const governance = optionalRecord(field(policy, "governance"), `states.${state}.governance`);
13645
+ Object.defineProperty(result, state, {
13646
+ configurable: true,
13647
+ enumerable: true,
13648
+ value: {
13649
+ mode: requiredMode(field(policy, "mode"), `states.${state}.mode`),
13650
+ ...maxActiveMs == null ? {} : { maxActiveMs },
13651
+ retry: retrySnapshot(field(policy, "retry"), `states.${state}.retry`),
13652
+ retention: retentionSnapshot(field(policy, "retention"), `states.${state}.retention`),
13653
+ ...governance == null ? {} : { governance }
13654
+ },
13655
+ writable: true
13656
+ });
13657
+ }
13658
+ return result;
13659
+ }
13660
+ function requiredRecord(value, context) {
13661
+ if (value instanceof Map) {
13662
+ const result = {};
13663
+ for (const [key, item] of value.entries()) {
13664
+ setOwnValue(result, requiredText(key, `${context} key`), item);
13665
+ }
13666
+ return result;
13667
+ }
13668
+ if (typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer41.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return value;
13669
+ throw new TypeError(`${context} returned an unexpected response`);
13670
+ }
13671
+ function optionalRecord(value, context) {
13672
+ return value == null ? void 0 : requiredRecord(value, `FLOW policy ${context}`);
13673
+ }
13674
+ function stringArray(value, context) {
13675
+ if (!Array.isArray(value)) {
13676
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13677
+ }
13678
+ const result = new Array(value.length);
13679
+ for (let index = 0; index < value.length; index += 1) {
13680
+ if (!Object.hasOwn(value, index)) {
13681
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13682
+ }
13683
+ result[index] = requiredText(value[index], `${context}[${index}]`);
13684
+ }
13685
+ return result;
13686
+ }
13687
+ function requiredText(value, context) {
13688
+ if (typeof value === "string") return value;
13689
+ if (import_node_buffer41.Buffer.isBuffer(value) || value instanceof Uint8Array) {
13690
+ return import_node_buffer41.Buffer.from(value).toString("utf8");
13691
+ }
13692
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13693
+ }
13694
+ function optionalText2(value, context) {
13695
+ return value == null ? void 0 : requiredText(value, context);
13696
+ }
13697
+ function nonNegativeInteger(value, context) {
13698
+ let parsed;
13699
+ try {
13700
+ parsed = integer(value);
13701
+ } catch {
13702
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13703
+ }
13704
+ if (parsed < 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13705
+ return parsed;
13706
+ }
13707
+ function optionalPositiveInteger(value, context) {
13708
+ if (value == null) return void 0;
13709
+ const parsed = nonNegativeInteger(value, context);
13710
+ if (parsed === 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13711
+ return parsed;
13712
+ }
13713
+ function optionalVersion(value) {
13714
+ if (value == null) return void 0;
13715
+ if (typeof value === "number" || typeof value === "bigint") {
13716
+ return nonNegativeInteger(value, "version");
13717
+ }
13718
+ return requiredText(value, "version");
13719
+ }
13720
+ function backoffKind(value, context) {
13721
+ const kind = requiredText(value, context).toLowerCase();
13722
+ if (kind === "none" || kind === "fixed" || kind === "linear" || kind === "exponential") {
13723
+ return kind;
13724
+ }
13725
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13726
+ }
13727
+ function requiredMode(value, context) {
13728
+ const mode = requiredText(value, context).toLowerCase();
13729
+ if (mode === "fifo" || mode === "parallel") return mode;
13730
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13731
+ }
13732
+ function optionalMode(value, context) {
13733
+ return value == null ? void 0 : requiredMode(value, context);
13734
+ }
13735
+
13046
13736
  // src/client-flow-support.ts
13047
13737
  var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13048
13738
  async rewind(id, options = {}) {
@@ -13185,27 +13875,36 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13185
13875
  }
13186
13876
  async installPolicy(type, options = {}) {
13187
13877
  const args = ["FLOW.POLICY.SET", type];
13878
+ const expectedGeneration = Object.hasOwn(options, "expectedGeneration") ? options.expectedGeneration : void 0;
13879
+ const replace = Object.hasOwn(options, "replace") ? options.replace : void 0;
13880
+ if (expectedGeneration != null) assertFlowPolicyGeneration(expectedGeneration);
13881
+ if (replace != null && typeof replace !== "boolean") {
13882
+ throw new TypeError("replace must be a boolean");
13883
+ }
13884
+ append(args, "EXPECTED_GENERATION", expectedGeneration);
13885
+ appendBool(args, "REPLACE", replace);
13188
13886
  if (options.indexedAttributes != null) {
13189
13887
  args.push("INDEXED_ATTRIBUTES", JSON.stringify(options.indexedAttributes));
13190
13888
  }
13191
13889
  append(args, "INDEXED_STATE_META", options.indexedStateMeta);
13192
13890
  append(args, "MAX_ACTIVE_MS", options.maxActiveMs);
13891
+ if (options.state == null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13193
13892
  append(args, "STATE", options.state);
13194
13893
  appendPolicyMode(args, options.state, options.mode);
13195
13894
  if (options.retry != null) {
13196
13895
  appendRetryPolicy(args, options.retry);
13197
13896
  }
13897
+ if (options.state != null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13198
13898
  for (const [state, policy] of Object.entries(options.states ?? {})) {
13199
13899
  args.push("STATE", state);
13200
13900
  appendFlowStatePolicy(args, policy);
13201
13901
  }
13202
- append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13203
- return await this.commandArgs(args);
13902
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13204
13903
  }
13205
13904
  async policyGet(type, options = {}) {
13206
13905
  const args = ["FLOW.POLICY.GET", type];
13207
13906
  append(args, "STATE", options.state);
13208
- return parseKvResponse(await this.commandArgs(args));
13907
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13209
13908
  }
13210
13909
  async retentionCleanup() {
13211
13910
  return parseKvResponse(await this.command("FLOW.RETENTION_CLEANUP"));
@@ -13294,7 +13993,7 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13294
13993
  };
13295
13994
 
13296
13995
  // src/flow-many-snapshot.ts
13297
- var import_node_buffer38 = require("buffer");
13996
+ var import_node_buffer43 = require("buffer");
13298
13997
  var encodedOptionFields = ["error", "payload", "reason", "result"];
13299
13998
  function snapshotCreateItem(item, codec) {
13300
13999
  const snapshot = {
@@ -13334,7 +14033,7 @@ function snapshotFlowManyOptions(options, codec) {
13334
14033
  }
13335
14034
  function snapshotClaimedItem(item) {
13336
14035
  const wire = item[CLAIMED_ITEM_WIRE];
13337
- const leaseToken = import_node_buffer38.Buffer.from(wire?.leaseToken ?? item.leaseToken);
14036
+ const leaseToken = import_node_buffer43.Buffer.from(wire?.leaseToken ?? item.leaseToken);
13338
14037
  const snapshot = {
13339
14038
  ...item,
13340
14039
  fencingToken: wire?.fencingToken ?? item.fencingToken,
@@ -13351,15 +14050,15 @@ function snapshotClaimedItem(item) {
13351
14050
  function snapshotFencedItem(item) {
13352
14051
  return Object.freeze({
13353
14052
  ...item,
13354
- ...item.leaseToken == null ? {} : { leaseToken: import_node_buffer38.Buffer.from(item.leaseToken) }
14053
+ ...item.leaseToken == null ? {} : { leaseToken: import_node_buffer43.Buffer.from(item.leaseToken) }
13355
14054
  });
13356
14055
  }
13357
14056
  function snapshotClaimedItemWire(wire, leaseToken) {
13358
14057
  return Object.freeze({
13359
14058
  fencingToken: wire.fencingToken,
13360
- id: import_node_buffer38.Buffer.from(wire.id),
14059
+ id: import_node_buffer43.Buffer.from(wire.id),
13361
14060
  leaseToken,
13362
- partitionKey: wire.partitionKey == null ? wire.partitionKey : import_node_buffer38.Buffer.from(wire.partitionKey)
14061
+ partitionKey: wire.partitionKey == null ? wire.partitionKey : import_node_buffer43.Buffer.from(wire.partitionKey)
13363
14062
  });
13364
14063
  }
13365
14064
  function snapshotArray(values) {
@@ -13370,7 +14069,7 @@ function snapshotArray(values) {
13370
14069
  return Object.freeze(snapshot);
13371
14070
  }
13372
14071
  function snapshotStateMeta(stateMeta) {
13373
- return snapshotRecord(stateMeta, (value) => import_node_buffer38.Buffer.isBuffer(value) ? import_node_buffer38.Buffer.from(value) : value);
14072
+ return snapshotRecord(stateMeta, (value) => import_node_buffer43.Buffer.isBuffer(value) ? import_node_buffer43.Buffer.from(value) : value);
13374
14073
  }
13375
14074
  function snapshotCommandRecord(values) {
13376
14075
  const seen = /* @__PURE__ */ new WeakMap();
@@ -13388,7 +14087,7 @@ function snapshotRecord(values, capture) {
13388
14087
  }
13389
14088
  function snapshotCommandArgument(value, seen) {
13390
14089
  if (typeof value !== "object" || value == null) return value;
13391
- if (import_node_buffer38.Buffer.isBuffer(value) || value instanceof Uint8Array) return import_node_buffer38.Buffer.from(value);
14090
+ if (import_node_buffer43.Buffer.isBuffer(value) || value instanceof Uint8Array) return import_node_buffer43.Buffer.from(value);
13392
14091
  const objectValue = value;
13393
14092
  const existing = seen.get(objectValue);
13394
14093
  if (existing != null) return existing;
@@ -14186,6 +14885,9 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14186
14885
  return new _FerricStoreClient(executor, clientOptions);
14187
14886
  }
14188
14887
  async valuePut(value, options = {}) {
14888
+ if (options.ownerFlowId != null && options.name != null && options.ttlMs != null) {
14889
+ throw new TypeError("named Flow values cannot have a TTL");
14890
+ }
14189
14891
  const args = ["FLOW.VALUE.PUT", this.codec.encode(value), "NOW", options.nowMs ?? nowMs()];
14190
14892
  append(args, "PARTITION", options.partitionKey);
14191
14893
  append(args, "OWNER_FLOW_ID", options.ownerFlowId);
@@ -14217,7 +14919,6 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14217
14919
  append(args, "TRANSITION_TO", options.transitionTo);
14218
14920
  append(args, "RUN_AT", options.runAtMs);
14219
14921
  append(args, "NOW", options.nowMs ?? nowMs());
14220
- append(args, "PRIORITY", options.priority);
14221
14922
  appendNamedValues(args, this.codec, options);
14222
14923
  return await this.commandArgs(args);
14223
14924
  }
@@ -14247,6 +14948,7 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14247
14948
  append(args, "CORRELATION_ID", options.correlationId);
14248
14949
  append(args, "PRIORITY", options.priority);
14249
14950
  append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
14951
+ append(args, "MAX_ACTIVE_MS", options.maxActiveMs);
14250
14952
  appendAttributes(args, options.attributes);
14251
14953
  appendStateMeta(args, options.stateMeta);
14252
14954
  appendNamedValues(args, this.codec, options);
@@ -14607,7 +15309,7 @@ async function runContinuousWorkerPool(options) {
14607
15309
  initialIdleSleepMs,
14608
15310
  nonNegativeDuration(options.maxIdleSleepMs, 5e3)
14609
15311
  );
14610
- const refillDelayMs = nonNegativeInteger(options.refillDelayMs, 0);
15312
+ const refillDelayMs = nonNegativeInteger2(options.refillDelayMs, 0);
14611
15313
  const active = /* @__PURE__ */ new Set();
14612
15314
  const pending = [];
14613
15315
  let pendingIndex = 0;
@@ -14726,7 +15428,7 @@ function normalizeWorkflowWorkerConfig(options) {
14726
15428
  function positiveInteger(value, fallback) {
14727
15429
  return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
14728
15430
  }
14729
- function nonNegativeInteger(value, fallback) {
15431
+ function nonNegativeInteger2(value, fallback) {
14730
15432
  return value == null || !Number.isFinite(value) ? fallback : Math.max(0, Math.trunc(value));
14731
15433
  }
14732
15434
  function nonNegativeDuration(value, fallback) {
@@ -16142,6 +16844,7 @@ var Workflow = class {
16142
16844
  }
16143
16845
  return await this.client.installPolicy(this.type, {
16144
16846
  ...options,
16847
+ replace: Object.hasOwn(options, "replace") ? options.replace ?? true : true,
16145
16848
  states
16146
16849
  });
16147
16850
  }
@@ -16352,16 +17055,23 @@ var WorkflowWorker = class {
16352
17055
  );
16353
17056
  }
16354
17057
  };
17058
+
17059
+ // src/version.ts
17060
+ var FERRICSTORE_SDK_VERSION = "0.3.0";
17061
+ var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.9.1";
17062
+ var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
16355
17063
  // Annotate the CommonJS export names for ESM import in node:
16356
17064
  0 && (module.exports = {
16357
17065
  BitmapStore,
16358
17066
  BloomFilterStore,
16359
17067
  COMMAND_OPCODES,
16360
- COMPACT_RESPONSE_OPCODES,
16361
17068
  ClaimHydrationError,
16362
17069
  ConnectionClosedError,
16363
17070
  CountMinSketchStore,
16364
17071
  CuckooFilterStore,
17072
+ FERRICSTORE_MINIMUM_SERVER_VERSION,
17073
+ FERRICSTORE_NATIVE_PROTOCOL_VERSION,
17074
+ FERRICSTORE_SDK_VERSION,
16365
17075
  FerricStoreClient,
16366
17076
  FerricStoreError,
16367
17077
  FlowAlreadyExistsError,
@@ -16378,6 +17088,7 @@ var WorkflowWorker = class {
16378
17088
  ListStore,
16379
17089
  LockHeldError,
16380
17090
  LockNotOwnedError,
17091
+ MAX_FLOW_POLICY_GENERATION,
16381
17092
  NativeAdapter,
16382
17093
  OverloadedError,
16383
17094
  Queue,
@@ -16392,6 +17103,7 @@ var WorkflowWorker = class {
16392
17103
  SetStore,
16393
17104
  SortedSetStore,
16394
17105
  StaleLeaseError,
17106
+ StalePolicyGenerationError,
16395
17107
  StreamStore,
16396
17108
  TDigestStore,
16397
17109
  TopKStore,