@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.js CHANGED
@@ -16,10 +16,16 @@ import "buffer";
16
16
  var FerricStoreError = class extends Error {
17
17
  code = "ferricstore_error";
18
18
  raw;
19
+ retryable;
20
+ safeToRetry;
21
+ retryAfterMs;
19
22
  constructor(message, options = {}) {
20
23
  super(message, { cause: options.cause });
21
24
  this.name = new.target.name;
22
25
  this.raw = options.raw;
26
+ this.retryable = options.retryable ?? structuredBooleanField(options.raw, "retryable");
27
+ this.safeToRetry = options.safeToRetry ?? structuredBooleanField(options.raw, "safe_to_retry");
28
+ this.retryAfterMs = options.retryAfterMs ?? structuredIntegerField(options.raw, "retry_after_ms");
23
29
  }
24
30
  };
25
31
  var ConnectionClosedError = class extends FerricStoreError {
@@ -52,6 +58,9 @@ var FlowWrongStateError = class extends FerricStoreError {
52
58
  var StaleLeaseError = class extends FerricStoreError {
53
59
  code = "stale_lease";
54
60
  };
61
+ var StalePolicyGenerationError = class extends FerricStoreError {
62
+ code = "stale_policy_generation";
63
+ };
55
64
  var FlowAlreadyExistsError = class extends FerricStoreError {
56
65
  code = "flow_already_exists";
57
66
  };
@@ -66,11 +75,14 @@ var InvalidCommandError = class extends FerricStoreError {
66
75
  };
67
76
  var OverloadedError = class extends FerricStoreError {
68
77
  code = "overloaded";
69
- retryAfterMs;
70
78
  reason;
71
79
  constructor(message, options = {}) {
72
- super(message, options);
73
- this.retryAfterMs = options.retryAfterMs;
80
+ const local = options.raw == null;
81
+ super(message, {
82
+ ...options,
83
+ retryable: options.retryable ?? (local ? true : void 0),
84
+ safeToRetry: options.safeToRetry ?? (local ? true : void 0)
85
+ });
74
86
  this.reason = options.reason;
75
87
  }
76
88
  };
@@ -88,15 +100,21 @@ function classifyServerError(message, raw, cause, status) {
88
100
  const lower = message.toLowerCase();
89
101
  const structuredCode = structuredStringField(raw, "code");
90
102
  const code = structuredCode?.toLowerCase();
103
+ const retry2 = {
104
+ retryable: structuredBooleanField(raw, "retryable"),
105
+ safeToRetry: structuredBooleanField(raw, "safe_to_retry"),
106
+ retryAfterMs: structuredIntegerField(raw, "retry_after_ms") ?? intField(lower, "retry_after_ms")
107
+ };
91
108
  if (isRerouteStatus(status) || code === "reroute") {
92
- return new RerouteError(message, { cause, raw });
109
+ return new RerouteError(message, { cause, raw, ...definedRetryMetadata(retry2) });
93
110
  }
94
111
  if (isBusyStatus(status) || isOverloadCode(structuredCode) || overloadMessage(lower)) {
95
112
  return new OverloadedError(message, {
96
113
  cause,
97
114
  raw,
115
+ ...definedRetryMetadata(retry2),
98
116
  reason: structuredStringField(raw, "reason") ?? structuredCode ?? stringField(lower, "reason"),
99
- retryAfterMs: structuredIntegerField(raw, "retry_after_ms") ?? intField(lower, "retry_after_ms")
117
+ retryAfterMs: retry2.retryAfterMs
100
118
  });
101
119
  }
102
120
  if (code === "flow_already_exists" || lower.includes("flow") && lower.includes("already exists")) {
@@ -108,6 +126,9 @@ function classifyServerError(message, raw, cause, status) {
108
126
  if (code === "stale_lease" || code === "stale_flow_lease" || lower.includes("stale flow lease") || lower.includes("stale lease") || lower.includes("stale token")) {
109
127
  return new StaleLeaseError(message, { cause, raw });
110
128
  }
129
+ if (code === "stale_generation" || code === "stale_policy_generation" || code === "stale_flow_policy_generation" || lower.includes("stale flow policy generation") || lower.includes("stale policy generation")) {
130
+ return new StalePolicyGenerationError(message, { cause, raw });
131
+ }
111
132
  if (code === "flow_not_found" || lower.includes("flow") && (lower.includes("not found") || lower.includes("does not exist"))) {
112
133
  return new FlowNotFoundError(message, { cause, raw });
113
134
  }
@@ -120,7 +141,7 @@ function classifyServerError(message, raw, cause, status) {
120
141
  if (code === "invalid_command" || lower.includes("wrong number of arguments") || lower.includes("syntax error")) {
121
142
  return new InvalidCommandError(message, { cause, raw });
122
143
  }
123
- return new FerricStoreError(message, { cause, raw });
144
+ return new FerricStoreError(message, { cause, raw, ...definedRetryMetadata(retry2) });
124
145
  }
125
146
  function mapException(error) {
126
147
  if (error instanceof FerricStoreError) {
@@ -160,8 +181,23 @@ function structuredIntegerField(raw, name) {
160
181
  if (typeof value === "bigint") {
161
182
  return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
162
183
  }
163
- const text2 = binaryText(value);
164
- return text2 == null ? void 0 : nonNegativeSafeIntegerText(text2);
184
+ const text3 = binaryText(value);
185
+ return text3 == null ? void 0 : nonNegativeSafeIntegerText(text3);
186
+ }
187
+ function structuredBooleanField(raw, name) {
188
+ const value = structuredField(raw, name);
189
+ if (typeof value === "boolean") return value;
190
+ const text3 = binaryText(value)?.toLowerCase();
191
+ if (text3 === "true" || text3 === "1") return true;
192
+ if (text3 === "false" || text3 === "0") return false;
193
+ return void 0;
194
+ }
195
+ function definedRetryMetadata(metadata) {
196
+ return {
197
+ ...metadata.retryable == null ? {} : { retryable: metadata.retryable },
198
+ ...metadata.safeToRetry == null ? {} : { safeToRetry: metadata.safeToRetry },
199
+ ...metadata.retryAfterMs == null ? {} : { retryAfterMs: metadata.retryAfterMs }
200
+ };
165
201
  }
166
202
  function nonNegativeSafeIntegerText(value) {
167
203
  if (!/^[0-9]+$/u.test(value)) return void 0;
@@ -334,9 +370,6 @@ function mapDenseResponseArray(value, transform) {
334
370
  return result;
335
371
  }
336
372
 
337
- // src/auto-partition.ts
338
- import { Buffer as Buffer3 } from "buffer";
339
-
340
373
  // src/crc32.ts
341
374
  import "buffer";
342
375
  var table = new Uint32Array(256);
@@ -354,12 +387,49 @@ function crc32(buffer) {
354
387
  }
355
388
  return (value ^ 4294967295) >>> 0;
356
389
  }
390
+ function crc32Utf8(text3, start = 0, end = text3.length) {
391
+ let value = 4294967295;
392
+ for (let index = start; index < end; index += 1) {
393
+ const first = text3.charCodeAt(index);
394
+ let point = first;
395
+ if (first >= 55296 && first <= 56319) {
396
+ const second = index + 1 < end ? text3.charCodeAt(index + 1) : 0;
397
+ if (second >= 56320 && second <= 57343) {
398
+ point = 65536 + (first - 55296 << 10) + second - 56320;
399
+ index += 1;
400
+ } else {
401
+ point = 65533;
402
+ }
403
+ } else if (first >= 56320 && first <= 57343) {
404
+ point = 65533;
405
+ }
406
+ if (point < 128) {
407
+ value = updateCrc32(value, point);
408
+ } else if (point < 2048) {
409
+ value = updateCrc32(value, 192 | point >>> 6);
410
+ value = updateCrc32(value, 128 | point & 63);
411
+ } else if (point < 65536) {
412
+ value = updateCrc32(value, 224 | point >>> 12);
413
+ value = updateCrc32(value, 128 | point >>> 6 & 63);
414
+ value = updateCrc32(value, 128 | point & 63);
415
+ } else {
416
+ value = updateCrc32(value, 240 | point >>> 18);
417
+ value = updateCrc32(value, 128 | point >>> 12 & 63);
418
+ value = updateCrc32(value, 128 | point >>> 6 & 63);
419
+ value = updateCrc32(value, 128 | point & 63);
420
+ }
421
+ }
422
+ return (value ^ 4294967295) >>> 0;
423
+ }
424
+ function updateCrc32(value, byte) {
425
+ return value >>> 8 ^ (table[(value ^ byte) & 255] ?? 0);
426
+ }
357
427
 
358
428
  // src/auto-partition.ts
359
429
  var AUTO_PARTITION_PREFIX = "__flow_auto__:";
360
430
  var AUTO_PARTITION_BUCKETS = 256;
361
431
  function autoPartitionKeyForId(id) {
362
- return `${AUTO_PARTITION_PREFIX}${crc32(Buffer3.from(id)) % AUTO_PARTITION_BUCKETS}`;
432
+ return `${AUTO_PARTITION_PREFIX}${crc32Utf8(id) % AUTO_PARTITION_BUCKETS}`;
363
433
  }
364
434
 
365
435
  // src/flow-argument-helpers.ts
@@ -752,7 +822,7 @@ function coerceDiagnosticValue(value) {
752
822
  // src/native-connection.ts
753
823
  import net from "net";
754
824
  import tls from "tls";
755
- import { Buffer as Buffer4 } from "buffer";
825
+ import { Buffer as Buffer3 } from "buffer";
756
826
  function parseFerricUrl(value) {
757
827
  const url = new URL(value);
758
828
  if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
@@ -878,7 +948,7 @@ function nonNegativeSafeInteger(value) {
878
948
  if (typeof value === "bigint") {
879
949
  return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
880
950
  }
881
- const source = Buffer4.isBuffer(value) || value instanceof Uint8Array ? Buffer4.from(value).toString("utf8") : value;
951
+ const source = Buffer3.isBuffer(value) || value instanceof Uint8Array ? Buffer3.from(value).toString("utf8") : value;
882
952
  if (typeof source !== "number" && (typeof source !== "string" || !/^\d+$/u.test(source))) {
883
953
  return void 0;
884
954
  }
@@ -1112,10 +1182,10 @@ var NativeHeartbeat = class {
1112
1182
  };
1113
1183
 
1114
1184
  // src/protocol.ts
1115
- import { Buffer as Buffer19 } from "buffer";
1185
+ import { Buffer as Buffer23 } from "buffer";
1116
1186
 
1117
1187
  // src/command-grammar.ts
1118
- import { Buffer as Buffer5 } from "buffer";
1188
+ import { Buffer as Buffer4 } from "buffer";
1119
1189
 
1120
1190
  // src/command-metadata-flow.ts
1121
1191
  var flowStateIdCommands = /* @__PURE__ */ new Set([
@@ -1562,8 +1632,8 @@ var secondKeySubcommands = /* @__PURE__ */ new Map([
1562
1632
  ]);
1563
1633
  function commandToken(value) {
1564
1634
  if (typeof value === "string") return value.toUpperCase();
1565
- if (Buffer5.isBuffer(value) || value instanceof Uint8Array) {
1566
- return Buffer5.from(value).toString("utf8").toUpperCase();
1635
+ if (Buffer4.isBuffer(value) || value instanceof Uint8Array) {
1636
+ return Buffer4.from(value).toString("utf8").toUpperCase();
1567
1637
  }
1568
1638
  return void 0;
1569
1639
  }
@@ -1666,7 +1736,7 @@ function safeIntegerArgument(value) {
1666
1736
  if (typeof value === "bigint") {
1667
1737
  return value >= BigInt(Number.MIN_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
1668
1738
  }
1669
- const source = typeof value === "string" ? value : Buffer5.isBuffer(value) || value instanceof Uint8Array ? Buffer5.from(value).toString("utf8") : void 0;
1739
+ const source = typeof value === "string" ? value : Buffer4.isBuffer(value) || value instanceof Uint8Array ? Buffer4.from(value).toString("utf8") : void 0;
1670
1740
  if (source == null || !/^[+-]?\d+$/u.test(source)) return void 0;
1671
1741
  const parsed = BigInt(source);
1672
1742
  return parsed >= BigInt(Number.MIN_SAFE_INTEGER) && parsed <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(parsed) : void 0;
@@ -1719,7 +1789,7 @@ function hasOwnCommandArgument(args, index, end, section) {
1719
1789
  }
1720
1790
 
1721
1791
  // src/protocol-core.ts
1722
- import { Buffer as Buffer9 } from "buffer";
1792
+ import { Buffer as Buffer8 } from "buffer";
1723
1793
 
1724
1794
  // src/protocol-constants.ts
1725
1795
  import "buffer";
@@ -1927,80 +1997,30 @@ var OPCODES = {
1927
1997
  flowFail: COMMAND_OPCODES["FLOW.FAIL"],
1928
1998
  flowValuePut: COMMAND_OPCODES["FLOW.VALUE.PUT"],
1929
1999
  flowValueMGet: COMMAND_OPCODES["FLOW.VALUE.MGET"],
2000
+ flowSignal: COMMAND_OPCODES["FLOW.SIGNAL"],
1930
2001
  flowCreateMany: COMMAND_OPCODES["FLOW.CREATE_MANY"],
1931
2002
  flowCompleteMany: COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
1932
2003
  flowTransitionMany: COMMAND_OPCODES["FLOW.TRANSITION_MANY"],
1933
2004
  flowRetryMany: COMMAND_OPCODES["FLOW.RETRY_MANY"],
1934
2005
  flowFailMany: COMMAND_OPCODES["FLOW.FAIL_MANY"],
1935
2006
  flowReclaim: COMMAND_OPCODES["FLOW.RECLAIM"],
2007
+ flowPolicySet: COMMAND_OPCODES["FLOW.POLICY.SET"],
2008
+ flowPolicyGet: COMMAND_OPCODES["FLOW.POLICY.GET"],
1936
2009
  flowSpawnChildren: COMMAND_OPCODES["FLOW.SPAWN_CHILDREN"],
1937
2010
  flowStepContinue: COMMAND_OPCODES["FLOW.STEP_CONTINUE"],
1938
2011
  flowStartAndClaim: COMMAND_OPCODES["FLOW.START_AND_CLAIM"],
1939
2012
  flowRunStepsMany: COMMAND_OPCODES["FLOW.RUN_STEPS_MANY"],
1940
2013
  flowSearch: COMMAND_OPCODES["FLOW.SEARCH"]
1941
2014
  };
1942
- var COMPACT_RESPONSE_OPCODES = {
1943
- flow_claim_jobs_v1: [COMMAND_OPCODES["FLOW.CLAIM_DUE"]],
1944
- flow_record_list_v1: [
1945
- COMMAND_OPCODES["FLOW.LIST"],
1946
- COMMAND_OPCODES["FLOW.TERMINALS"],
1947
- COMMAND_OPCODES["FLOW.FAILURES"],
1948
- COMMAND_OPCODES["FLOW.BY_PARENT"],
1949
- COMMAND_OPCODES["FLOW.BY_ROOT"],
1950
- COMMAND_OPCODES["FLOW.BY_CORRELATION"],
1951
- COMMAND_OPCODES["FLOW.STUCK"]
1952
- ],
1953
- flow_record_v1: [COMMAND_OPCODES["FLOW.GET"]],
1954
- kv_get_v1: [COMMAND_OPCODES.GET],
1955
- kv_mget_v1: [COMMAND_OPCODES.MGET, COMMAND_OPCODES["FLOW.VALUE.MGET"]],
1956
- ok_list_v1: [
1957
- COMMAND_OPCODES.SET,
1958
- COMMAND_OPCODES.MSET,
1959
- COMMAND_OPCODES["FLOW.CREATE_MANY"],
1960
- COMMAND_OPCODES["FLOW.COMPLETE_MANY"],
1961
- COMMAND_OPCODES["FLOW.RETRY_MANY"],
1962
- COMMAND_OPCODES["FLOW.FAIL_MANY"],
1963
- COMMAND_OPCODES["FLOW.CANCEL_MANY"]
1964
- ],
1965
- pipeline_v1: [COMMAND_OPCODES.PIPELINE]
1966
- };
1967
- var COMPACT_KIND_FLOW_CLAIM_JOBS = 1 << 0;
1968
- var COMPACT_KIND_FLOW_RECORD = 1 << 1;
1969
- var COMPACT_KIND_FLOW_RECORD_LIST = 1 << 2;
1970
- var COMPACT_KIND_KV_GET = 1 << 3;
1971
- var COMPACT_KIND_KV_MGET = 1 << 4;
1972
- var COMPACT_KIND_OK_LIST = 1 << 5;
1973
- var COMPACT_KIND_PIPELINE = 1 << 6;
1974
- var compactResponseOpcodeKinds = new Uint8Array(
1975
- Math.max(...Object.values(COMPACT_RESPONSE_OPCODES).flat()) + 1
1976
- );
1977
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_CLAIM_JOBS, COMPACT_RESPONSE_OPCODES.flow_claim_jobs_v1);
1978
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_RECORD, COMPACT_RESPONSE_OPCODES.flow_record_v1);
1979
- registerCompactResponseOpcodes(COMPACT_KIND_FLOW_RECORD_LIST, COMPACT_RESPONSE_OPCODES.flow_record_list_v1);
1980
- registerCompactResponseOpcodes(COMPACT_KIND_KV_GET, COMPACT_RESPONSE_OPCODES.kv_get_v1);
1981
- registerCompactResponseOpcodes(COMPACT_KIND_KV_MGET, [
1982
- ...COMPACT_RESPONSE_OPCODES.kv_mget_v1,
1983
- COMMAND_OPCODES.PIPELINE
1984
- ]);
1985
- registerCompactResponseOpcodes(COMPACT_KIND_OK_LIST, [
1986
- ...COMPACT_RESPONSE_OPCODES.ok_list_v1,
1987
- COMMAND_OPCODES.PIPELINE
1988
- ]);
1989
- registerCompactResponseOpcodes(COMPACT_KIND_PIPELINE, COMPACT_RESPONSE_OPCODES.pipeline_v1);
1990
- function registerCompactResponseOpcodes(kind, opcodes) {
1991
- for (const opcode of opcodes) {
1992
- compactResponseOpcodeKinds[opcode] = (compactResponseOpcodeKinds[opcode] ?? 0) | kind;
1993
- }
1994
- }
1995
- function compactResponseOpcodeSupports(opcode, kind) {
1996
- return ((compactResponseOpcodeKinds[opcode] ?? 0) & kind) !== 0;
1997
- }
1998
2015
  var COMPACT_FLOW_CLAIM_JOBS = 128;
1999
2016
  var COMPACT_OK_LIST = 129;
2000
2017
  var COMPACT_KV_GET = 130;
2001
2018
  var COMPACT_KV_MGET = 131;
2002
2019
  var COMPACT_FLOW_RECORD = 132;
2003
2020
  var COMPACT_FLOW_RECORD_LIST = 133;
2021
+ var COMPACT_BINARY_LIST_LIST = 134;
2022
+ var COMPACT_BINARY_MAP_LIST = 135;
2023
+ var COMPACT_INTEGER_LIST = 136;
2004
2024
  var COMPACT_KV_MGET_FIXED = 137;
2005
2025
  var COMPACT_FLOW_CREATE_MANY_REQUEST = 144;
2006
2026
  var COMPACT_FLOW_CLAIM_DUE_REQUEST = 145;
@@ -2070,16 +2090,16 @@ var FLOW_RECORD_FIELD_KEYS = [
2070
2090
  ];
2071
2091
 
2072
2092
  // src/protocol-text.ts
2073
- import { Buffer as Buffer7 } from "buffer";
2093
+ import { Buffer as Buffer6 } from "buffer";
2074
2094
  function toBuffer(value) {
2075
- if (Buffer7.isBuffer(value)) return value;
2076
- if (value instanceof Uint8Array) return Buffer7.from(value);
2077
- if (typeof value === "string") return Buffer7.from(value);
2095
+ if (Buffer6.isBuffer(value)) return value;
2096
+ if (value instanceof Uint8Array) return Buffer6.from(value);
2097
+ if (typeof value === "string") return Buffer6.from(value);
2078
2098
  if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2079
- return Buffer7.from(String(value));
2099
+ return Buffer6.from(String(value));
2080
2100
  }
2081
- if (typeof value === "symbol") return Buffer7.from(value.description ?? value.toString());
2082
- if (value == null) return Buffer7.alloc(0);
2101
+ if (typeof value === "symbol") return Buffer6.from(value.description ?? value.toString());
2102
+ if (value == null) return Buffer6.alloc(0);
2083
2103
  throw new FerricStoreError(`unsupported binary command argument type: ${typeof value}`);
2084
2104
  }
2085
2105
  function asText(value) {
@@ -2087,14 +2107,14 @@ function asText(value) {
2087
2107
  }
2088
2108
  function optionalText(value) {
2089
2109
  if (value == null || value === "") return void 0;
2090
- if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || Buffer7.isBuffer(value) || value instanceof Uint8Array) {
2110
+ if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || Buffer6.isBuffer(value) || value instanceof Uint8Array) {
2091
2111
  return asText(value);
2092
2112
  }
2093
2113
  return void 0;
2094
2114
  }
2095
2115
  function commandTokenIs(value, expected) {
2096
2116
  if (typeof value === "string") return value.toUpperCase() === expected;
2097
- if (Buffer7.isBuffer(value) || value instanceof Uint8Array) {
2117
+ if (Buffer6.isBuffer(value) || value instanceof Uint8Array) {
2098
2118
  return asText(value).toUpperCase() === expected;
2099
2119
  }
2100
2120
  return false;
@@ -2108,7 +2128,7 @@ function commandNameIs(value, expected) {
2108
2128
  }
2109
2129
 
2110
2130
  // src/request-context.ts
2111
- import { Buffer as Buffer8 } from "buffer";
2131
+ import { Buffer as Buffer7 } from "buffer";
2112
2132
  var requestContextCommands = /* @__PURE__ */ new Set([
2113
2133
  "INVOCATION.CREATE",
2114
2134
  "INVOCATION.DEFINITION.GET",
@@ -2163,9 +2183,9 @@ function requestContextCommandToken(value) {
2163
2183
  const upper = value.toUpperCase();
2164
2184
  return isRequestContextCommand(upper) ? upper : void 0;
2165
2185
  }
2166
- if (Buffer8.isBuffer(value) || value instanceof Uint8Array) {
2186
+ if (Buffer7.isBuffer(value) || value instanceof Uint8Array) {
2167
2187
  if (value[0] !== 73 && value[0] !== 105) return void 0;
2168
- const command = Buffer8.from(value).toString("utf8").toUpperCase();
2188
+ const command = Buffer7.from(value).toString("utf8").toUpperCase();
2169
2189
  return isRequestContextCommand(command) ? command : void 0;
2170
2190
  }
2171
2191
  return void 0;
@@ -2196,12 +2216,12 @@ function requestContextText(context, fieldName) {
2196
2216
  return value;
2197
2217
  }
2198
2218
  function isPlainObject(value) {
2199
- return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer8.isBuffer(value) && !(value instanceof Uint8Array);
2219
+ return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer7.isBuffer(value) && !(value instanceof Uint8Array);
2200
2220
  }
2201
2221
  function commandToken2(value) {
2202
2222
  if (typeof value === "string") return value.toUpperCase();
2203
- if (Buffer8.isBuffer(value) || value instanceof Uint8Array) {
2204
- return Buffer8.from(value).toString("utf8").toUpperCase();
2223
+ if (Buffer7.isBuffer(value) || value instanceof Uint8Array) {
2224
+ return Buffer7.from(value).toString("utf8").toUpperCase();
2205
2225
  }
2206
2226
  return "";
2207
2227
  }
@@ -2291,17 +2311,17 @@ function requestFrameTooLarge(maxBytes) {
2291
2311
  );
2292
2312
  }
2293
2313
  function binaryValueByteLength(value) {
2294
- if (Buffer9.isBuffer(value) || value instanceof Uint8Array) return value.byteLength;
2295
- if (typeof value === "string") return Buffer9.byteLength(value);
2314
+ if (Buffer8.isBuffer(value) || value instanceof Uint8Array) return value.byteLength;
2315
+ if (typeof value === "string") return Buffer8.byteLength(value);
2296
2316
  if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2297
- return Buffer9.byteLength(String(value));
2317
+ return Buffer8.byteLength(String(value));
2298
2318
  }
2299
- if (typeof value === "symbol") return Buffer9.byteLength(value.description ?? value.toString());
2319
+ if (typeof value === "symbol") return Buffer8.byteLength(value.description ?? value.toString());
2300
2320
  if (value == null) return 0;
2301
2321
  return toBuffer(value).byteLength;
2302
2322
  }
2303
2323
  function writeCustomPayload(output, offset, value, knownByteLength) {
2304
- if (Buffer9.isBuffer(value)) {
2324
+ if (Buffer8.isBuffer(value)) {
2305
2325
  value.copy(output, offset);
2306
2326
  return;
2307
2327
  }
@@ -2310,17 +2330,17 @@ function writeCustomPayload(output, offset, value, knownByteLength) {
2310
2330
  return;
2311
2331
  }
2312
2332
  if (value == null) return;
2313
- let text2;
2333
+ let text3;
2314
2334
  if (typeof value === "string") {
2315
- text2 = value;
2335
+ text3 = value;
2316
2336
  } else if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2317
- text2 = String(value);
2337
+ text3 = String(value);
2318
2338
  } else if (typeof value === "symbol") {
2319
- text2 = value.description ?? value.toString();
2339
+ text3 = value.description ?? value.toString();
2320
2340
  } else {
2321
2341
  throw new FerricStoreError(`unsupported binary command argument type: ${typeof value}`);
2322
2342
  }
2323
- output.write(text2, offset, knownByteLength ?? Buffer9.byteLength(text2), "utf8");
2343
+ output.write(text3, offset, knownByteLength ?? Buffer8.byteLength(text3), "utf8");
2324
2344
  }
2325
2345
  function compactBinaryEncodedLength(value) {
2326
2346
  return 4 + compactBinaryByteLength(value);
@@ -2333,10 +2353,10 @@ function compactBinaryByteLength(value) {
2333
2353
  return byteLength;
2334
2354
  }
2335
2355
  function isCompactBinaryArgument(value) {
2336
- return typeof value === "string" || Buffer9.isBuffer(value) || value instanceof Uint8Array;
2356
+ return typeof value === "string" || Buffer8.isBuffer(value) || value instanceof Uint8Array;
2337
2357
  }
2338
2358
  function isCompactBinaryScalar(value) {
2339
- return value == null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || typeof value === "symbol" || Buffer9.isBuffer(value) || value instanceof Uint8Array;
2359
+ return value == null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || typeof value === "symbol" || Buffer8.isBuffer(value) || value instanceof Uint8Array;
2340
2360
  }
2341
2361
  function compactOptionalBinaryEncodedLength(value) {
2342
2362
  return value == null ? 4 : compactBinaryEncodedLength(value);
@@ -2419,13 +2439,13 @@ function i64Arg(value) {
2419
2439
  }
2420
2440
  function boolArg(value) {
2421
2441
  if (typeof value === "boolean") return value;
2422
- const text2 = asText(value).toUpperCase();
2423
- return text2 === "1" || text2 === "TRUE" || text2 === "YES" || text2 === "ON";
2442
+ const text3 = asText(value).toUpperCase();
2443
+ return text3 === "1" || text3 === "TRUE" || text3 === "YES" || text3 === "ON";
2424
2444
  }
2425
2445
  function isBoolToken(value) {
2426
2446
  if (typeof value === "boolean") return true;
2427
- const text2 = asText(value).toUpperCase();
2428
- return text2 === "1" || text2 === "0" || text2 === "TRUE" || text2 === "FALSE" || text2 === "YES" || text2 === "NO" || text2 === "ON" || text2 === "OFF";
2447
+ const text3 = asText(value).toUpperCase();
2448
+ return text3 === "1" || text3 === "0" || text3 === "TRUE" || text3 === "FALSE" || text3 === "YES" || text3 === "NO" || text3 === "ON" || text3 === "OFF";
2429
2449
  }
2430
2450
  function independentMode(value) {
2431
2451
  if (value === true) return 2;
@@ -2467,7 +2487,23 @@ function setOwnValue2(target, key, value) {
2467
2487
  }
2468
2488
 
2469
2489
  // src/protocol-flow-options.ts
2470
- import { Buffer as Buffer10 } from "buffer";
2490
+ import { Buffer as Buffer9 } from "buffer";
2491
+
2492
+ // src/protocol-flow-option-value.ts
2493
+ function setFlowOptionValue(payload, field2, value, repeat) {
2494
+ if (!repeat || !Object.hasOwn(payload, field2)) {
2495
+ setOwnValue2(payload, field2, value);
2496
+ return;
2497
+ }
2498
+ const previous = payload[field2];
2499
+ setOwnValue2(
2500
+ payload,
2501
+ field2,
2502
+ Array.isArray(previous) ? [...previous, value] : [previous, value]
2503
+ );
2504
+ }
2505
+
2506
+ // src/protocol-flow-options.ts
2471
2507
  var OPTION_FIELDS = {
2472
2508
  CONSISTENT_PROJECTION: "consistent_projection",
2473
2509
  COUNT: "count",
@@ -2542,7 +2578,7 @@ function normalizeFlowSearchStateMeta(payload) {
2542
2578
  payload.state_meta = { [asText(state)]: stateMeta };
2543
2579
  }
2544
2580
  function isPlainObject2(value) {
2545
- return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer10.isBuffer(value) && !(value instanceof Uint8Array);
2581
+ return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer9.isBuffer(value) && !(value instanceof Uint8Array);
2546
2582
  }
2547
2583
  function parseFlowOptions(args, start, end, config) {
2548
2584
  const payload = {};
@@ -2636,8 +2672,9 @@ function parseFlowOptions(args, start, end, config) {
2636
2672
  case "TRANSITION_TO":
2637
2673
  case "TO_EVENT":
2638
2674
  case "EXPECT_STATE": {
2639
- const field2 = token.toLowerCase();
2640
- if (!putScalar(payload, field2, args, index + 1, end)) return void 0;
2675
+ const field2 = token === "IDEMPOTENCY" ? "idempotency_key" : token.toLowerCase();
2676
+ if (!hasOwnCommandArgument(args, index + 1, end, "Flow options")) return void 0;
2677
+ setFlowOptionValue(payload, field2, args[index + 1], token === "IF_STATE");
2641
2678
  index += 2;
2642
2679
  break;
2643
2680
  }
@@ -2927,6 +2964,7 @@ var FLOW_ADMIN_FIELDS = {
2927
2964
  LEASE_MS: "lease_ms",
2928
2965
  LEASE_TOKEN: "lease_token",
2929
2966
  LIMIT: "limit",
2967
+ MAX_ACTIVE_MS: "max_active_ms",
2930
2968
  MAX_FIRES: "max_fires",
2931
2969
  MAXBYTES: "payload_max_bytes",
2932
2970
  NOW: "now_ms",
@@ -2936,7 +2974,6 @@ var FLOW_ADMIN_FIELDS = {
2936
2974
  OVERLAP_RETRY_MS: "overlap_retry_ms",
2937
2975
  OVERWRITE: "overwrite",
2938
2976
  PARENT_FLOW_ID: "parent_flow_id",
2939
- PARENT_ID: "parent_id",
2940
2977
  PARTITION: "partition_key",
2941
2978
  PAYLOAD: "payload",
2942
2979
  PAYLOAD_MAX_BYTES: "payload_max_bytes",
@@ -2951,7 +2988,6 @@ var FLOW_ADMIN_FIELDS = {
2951
2988
  REV: "rev",
2952
2989
  RETURN: "return",
2953
2990
  ROOT_FLOW_ID: "root_flow_id",
2954
- ROOT_ID: "root_id",
2955
2991
  SCOPE: "scope",
2956
2992
  SHARD_ID: "shard_id",
2957
2993
  START_AT_MS: "start_at_ms",
@@ -3088,7 +3124,7 @@ function flowAdminPayload(opcode, args, leadingFields = []) {
3088
3124
  }
3089
3125
 
3090
3126
  // src/protocol-flow-compact.ts
3091
- import { Buffer as Buffer12 } from "buffer";
3127
+ import { Buffer as Buffer11 } from "buffer";
3092
3128
 
3093
3129
  // src/protocol-flow-compact-common.ts
3094
3130
  function compactManyRequestTag(value, regularTag, okOnSuccessTag) {
@@ -3197,7 +3233,7 @@ function parseNamedCountMap(values, start) {
3197
3233
  }
3198
3234
 
3199
3235
  // src/protocol-flow-compact-transition.ts
3200
- import { Buffer as Buffer11 } from "buffer";
3236
+ import { Buffer as Buffer10 } from "buffer";
3201
3237
  function compactFlowTransitionManyPayload(partition, fromState, toState, rawItems, mixed, options, maxBodyBytes) {
3202
3238
  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) {
3203
3239
  return void 0;
@@ -3249,7 +3285,7 @@ function compactFlowTransitionManyPayload(partition, fromState, toState, rawItem
3249
3285
  total += 4 + idLength + 4 + itemPartitionLength + 8 + 4 + leaseLength;
3250
3286
  }
3251
3287
  assertCompactPayloadFits(total, maxBodyBytes);
3252
- const out = Buffer11.allocUnsafe(total);
3288
+ const out = Buffer10.allocUnsafe(total);
3253
3289
  let offset = 0;
3254
3290
  out.writeUInt8(tag, offset);
3255
3291
  offset += 1;
@@ -3307,7 +3343,7 @@ function compactFlowCreateManyPayload(partition, rawItems, mixed, auto, options,
3307
3343
  }
3308
3344
  assertCompactPayloadFits(total, maxBodyBytes);
3309
3345
  const tag = mixed ? COMPACT_FLOW_CREATE_MANY_MIXED_REQUEST : auto ? COMPACT_FLOW_CREATE_MANY_REQUEST : COMPACT_FLOW_CREATE_MANY_PARTITION_REQUEST;
3310
- const out = Buffer12.allocUnsafe(total);
3346
+ const out = Buffer11.allocUnsafe(total);
3311
3347
  let offset = 0;
3312
3348
  out.writeUInt8(tag, offset++);
3313
3349
  offset = writeBinary(out, offset, toBuffer(options.type));
@@ -3352,7 +3388,7 @@ function compactFlowClaimDuePayload(type, options, maxBodyBytes) {
3352
3388
  total += 1;
3353
3389
  }
3354
3390
  assertCompactPayloadFits(total, maxBodyBytes);
3355
- const out = Buffer12.allocUnsafe(total);
3391
+ const out = Buffer11.allocUnsafe(total);
3356
3392
  let offset = 0;
3357
3393
  out.writeUInt8(COMPACT_FLOW_CLAIM_DUE_REQUEST, offset++);
3358
3394
  offset = writeBinary(out, offset, toBuffer(type));
@@ -3406,7 +3442,7 @@ function compactFlowCompleteManyPayload(opcode, partition, rawItems, mixed, auto
3406
3442
  total += compactBinaryEncodedLength(rawItems[rawIndex]) + compactOptionalBinaryEncodedLength(mixed ? rawItems[rawIndex + 1] : null) + compactBinaryEncodedLength(rawItems[rawIndex + width - 2]) + 8;
3407
3443
  }
3408
3444
  assertCompactPayloadFits(total, maxBodyBytes);
3409
- const out = Buffer12.allocUnsafe(total);
3445
+ const out = Buffer11.allocUnsafe(total);
3410
3446
  let offset = 0;
3411
3447
  out.writeUInt8(
3412
3448
  returnMode === "OK_ON_SUCCESS" ? COMPACT_FLOW_COMPLETE_MANY_OK_REQUEST : COMPACT_FLOW_COMPLETE_MANY_REQUEST,
@@ -3453,7 +3489,7 @@ function compactFlowRetryManyPayload(partition, rawItems, mixed, auto, options,
3453
3489
  total += compactBinaryEncodedLength(rawItems[rawIndex]) + compactOptionalBinaryEncodedLength(mixed ? rawItems[rawIndex + 1] : null) + compactBinaryEncodedLength(rawItems[rawIndex + width - 2]) + 8;
3454
3490
  }
3455
3491
  assertCompactPayloadFits(total, maxBodyBytes);
3456
- const out = Buffer12.allocUnsafe(total);
3492
+ const out = Buffer11.allocUnsafe(total);
3457
3493
  let offset = 0;
3458
3494
  out.writeUInt8(
3459
3495
  returnMode === "OK_ON_SUCCESS" ? COMPACT_FLOW_RETRY_MANY_OK_REQUEST : COMPACT_FLOW_RETRY_MANY_REQUEST,
@@ -3541,7 +3577,7 @@ function compactFlowCancelManyPayload(args, maxBodyBytes) {
3541
3577
  total += 4 + idLength + 4 + itemPartitionLength + 8;
3542
3578
  }
3543
3579
  assertCompactPayloadFits(total, maxBodyBytes);
3544
- const out = Buffer12.allocUnsafe(total);
3580
+ const out = Buffer11.allocUnsafe(total);
3545
3581
  let offset = 0;
3546
3582
  out.writeUInt8(tag, offset);
3547
3583
  offset += 1;
@@ -3577,7 +3613,7 @@ function isOptionalSafeIntegerNumber(value) {
3577
3613
  }
3578
3614
 
3579
3615
  // src/protocol-flow-compact-single.ts
3580
- import { Buffer as Buffer13 } from "buffer";
3616
+ import { Buffer as Buffer12 } from "buffer";
3581
3617
  function compactFlowValueMGetPayload(refs, maxBytes, maxBodyBytes) {
3582
3618
  if (refs.length > 4294967295 || maxBytes != null && (typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 0)) return void 0;
3583
3619
  const minimum = 13 + refs.length * 4;
@@ -3594,7 +3630,7 @@ function compactFlowValueMGetPayload(refs, maxBytes, maxBodyBytes) {
3594
3630
  total += 4 + byteLength;
3595
3631
  }
3596
3632
  assertCompactPayloadFits(total, maxBodyBytes);
3597
- const out = Buffer13.allocUnsafe(total);
3633
+ const out = Buffer12.allocUnsafe(total);
3598
3634
  let offset = 0;
3599
3635
  out.writeUInt8(COMPACT_FLOW_VALUE_MGET_REQUEST, offset);
3600
3636
  offset += 1;
@@ -3637,7 +3673,7 @@ function compactFlowListPayload(args, maxBodyBytes) {
3637
3673
  const stateLength = state == null ? 0 : compactBinaryByteLength(state);
3638
3674
  const total = 1 + 4 + typeLength + 4 + stateLength + 8 + 1;
3639
3675
  assertCompactPayloadFits(total, maxBodyBytes);
3640
- const out = Buffer13.allocUnsafe(total);
3676
+ const out = Buffer12.allocUnsafe(total);
3641
3677
  let offset = 0;
3642
3678
  out.writeUInt8(COMPACT_FLOW_LIST_REQUEST, offset);
3643
3679
  offset += 1;
@@ -4080,6 +4116,7 @@ function flowSpawnChildrenPayload(args) {
4080
4116
  "FROM_STATE",
4081
4117
  "ON_CHILD_FAILED",
4082
4118
  "ON_PARENT_CLOSED",
4119
+ "MAX_ACTIVE_MS",
4083
4120
  "VALUE",
4084
4121
  "VALUE_REF"
4085
4122
  ])
@@ -4110,6 +4147,185 @@ function flowSearchPayload(args) {
4110
4147
  return { opcode: OPCODES.flowSearch, payload: { type: args[0], ...payload } };
4111
4148
  }
4112
4149
 
4150
+ // src/protocol-flow-signal.ts
4151
+ function flowSignalPayload(args) {
4152
+ if (args.length < 3) return void 0;
4153
+ const options = parseFlowOptions(args, 1, args.length, {
4154
+ allowed: /* @__PURE__ */ new Set([
4155
+ "SIGNAL",
4156
+ "PARTITION",
4157
+ "IDEMPOTENCY",
4158
+ "IF_STATE",
4159
+ "TRANSITION_TO",
4160
+ "RUN_AT",
4161
+ "NOW",
4162
+ "VALUE",
4163
+ "VALUE_REF",
4164
+ "DROP_VALUE",
4165
+ "OVERRIDE_VALUE"
4166
+ ]),
4167
+ required: /* @__PURE__ */ new Set(["SIGNAL"])
4168
+ });
4169
+ if (options == null) return void 0;
4170
+ return { opcode: OPCODES.flowSignal, payload: { id: args[0], ...options } };
4171
+ }
4172
+
4173
+ // src/protocol-flow-policy.ts
4174
+ import { Buffer as Buffer13 } from "buffer";
4175
+ var BACKOFF_FIELDS = {
4176
+ BASE_MS: "base_ms",
4177
+ MAX_MS: "max_ms",
4178
+ JITTER_PCT: "jitter_pct"
4179
+ };
4180
+ var RETENTION_FIELDS = {
4181
+ RETENTION_TTL: "ttl_ms",
4182
+ RETENTION_TTL_MS: "ttl_ms",
4183
+ HISTORY_MAX_EVENTS: "history_max_events"
4184
+ };
4185
+ function flowPolicySetPayload(args) {
4186
+ if (!hasOwnCommandArgument(args, 0, args.length, "Flow policy arguments")) return void 0;
4187
+ if (!isBinaryArgument(args[0])) return void 0;
4188
+ const payload = { type: args[0] };
4189
+ const states = {};
4190
+ let target = payload;
4191
+ for (let index = 1; index < args.length; index += 2) {
4192
+ if (!hasOwnCommandArgument(args, index, args.length, "Flow policy arguments") || !hasOwnCommandArgument(args, index + 1, args.length, "Flow policy arguments")) return void 0;
4193
+ const token = optionalText(args[index])?.toUpperCase();
4194
+ const value = args[index + 1];
4195
+ if (token == null || value == null) return void 0;
4196
+ if (token === "STATE") {
4197
+ if (!isBinaryArgument(value)) return void 0;
4198
+ const state = asText(value);
4199
+ target = {};
4200
+ setOwnValue2(states, state, target);
4201
+ setOwnValue2(payload, "states", states);
4202
+ continue;
4203
+ }
4204
+ if (token === "MODE") {
4205
+ if (!isBinaryArgument(value)) return void 0;
4206
+ const mode = optionalText(value)?.toLowerCase();
4207
+ if (target === payload || mode !== "fifo" && mode !== "parallel") return void 0;
4208
+ setOwnValue2(target, "mode", mode);
4209
+ continue;
4210
+ }
4211
+ if (token === "EXPECTED_GENERATION") {
4212
+ if (target !== payload) return void 0;
4213
+ const generation = integerOption(value);
4214
+ if (generation == null || generation < 0) return void 0;
4215
+ setOwnValue2(payload, "expected_generation", generation);
4216
+ continue;
4217
+ }
4218
+ if (token === "REPLACE") {
4219
+ if (target !== payload || !isBoolToken(value)) return void 0;
4220
+ setOwnValue2(payload, "replace", boolArg(value));
4221
+ continue;
4222
+ }
4223
+ if (token === "MAX_ACTIVE_MS") {
4224
+ if (target !== payload) return void 0;
4225
+ const maxActiveMs = maxActiveOption(value);
4226
+ if (maxActiveMs == null) return void 0;
4227
+ setOwnValue2(payload, "max_active_ms", maxActiveMs);
4228
+ continue;
4229
+ }
4230
+ if (token === "INDEXED_STATE_META") {
4231
+ if (target !== payload || !isBinaryArgument(value)) return void 0;
4232
+ setOwnValue2(payload, "indexed_state_meta", value);
4233
+ continue;
4234
+ }
4235
+ if (token === "INDEXED_ATTRIBUTES") {
4236
+ if (target !== payload) return void 0;
4237
+ const names = indexedAttributes(value);
4238
+ if (names == null) return void 0;
4239
+ setOwnValue2(payload, "indexed_attributes", names);
4240
+ continue;
4241
+ }
4242
+ if (token === "MAX_RETRIES") {
4243
+ const parsed = integerOption(value);
4244
+ if (parsed == null) return void 0;
4245
+ setOwnValue2(objectField(target, "retry"), "max_retries", parsed);
4246
+ continue;
4247
+ }
4248
+ if (token === "EXHAUSTED_TO") {
4249
+ if (!isBinaryArgument(value)) return void 0;
4250
+ setOwnValue2(objectField(target, "retry"), "exhausted_to", value);
4251
+ continue;
4252
+ }
4253
+ const backoffField = BACKOFF_FIELDS[token];
4254
+ if (backoffField != null) {
4255
+ const parsed = integerOption(value);
4256
+ if (parsed == null) return void 0;
4257
+ setOwnValue2(objectField(objectField(target, "retry"), "backoff"), backoffField, parsed);
4258
+ continue;
4259
+ }
4260
+ if (token === "BACKOFF") {
4261
+ if (!isBinaryArgument(value)) return void 0;
4262
+ const kind = optionalText(value)?.toLowerCase();
4263
+ if (kind == null) return void 0;
4264
+ setOwnValue2(objectField(objectField(target, "retry"), "backoff"), "kind", kind);
4265
+ continue;
4266
+ }
4267
+ const retentionField = RETENTION_FIELDS[token];
4268
+ if (retentionField != null) {
4269
+ if (target === payload) return void 0;
4270
+ const parsed = integerOption(value);
4271
+ if (parsed == null) return void 0;
4272
+ setOwnValue2(objectField(target, "retention"), retentionField, parsed);
4273
+ continue;
4274
+ }
4275
+ return void 0;
4276
+ }
4277
+ return { opcode: OPCODES.flowPolicySet, payload };
4278
+ }
4279
+ function flowPolicyGetPayload(args) {
4280
+ if (!hasOwnCommandArgument(args, 0, args.length, "Flow policy arguments")) return void 0;
4281
+ if (!isBinaryArgument(args[0])) return void 0;
4282
+ if (args.length !== 1 && args.length !== 3) return void 0;
4283
+ const payload = { type: args[0] };
4284
+ if (args.length === 3) {
4285
+ 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;
4286
+ setOwnValue2(payload, "state", args[2]);
4287
+ }
4288
+ return { opcode: OPCODES.flowPolicyGet, payload };
4289
+ }
4290
+ function integerOption(value) {
4291
+ try {
4292
+ return safeIntegerNumberArg(value);
4293
+ } catch {
4294
+ return void 0;
4295
+ }
4296
+ }
4297
+ function maxActiveOption(value) {
4298
+ if (isBinaryArgument(value) && asText(value).toLowerCase() === "infinity") {
4299
+ return "infinity";
4300
+ }
4301
+ return integerOption(value);
4302
+ }
4303
+ function isBinaryArgument(value) {
4304
+ return typeof value === "string" || Buffer13.isBuffer(value) || value instanceof Uint8Array;
4305
+ }
4306
+ function objectField(target, name) {
4307
+ const existing = Object.hasOwn(target, name) ? target[name] : void 0;
4308
+ if (typeof existing === "object" && existing != null && !Array.isArray(existing)) {
4309
+ return existing;
4310
+ }
4311
+ const value = {};
4312
+ setOwnValue2(target, name, value);
4313
+ return value;
4314
+ }
4315
+ function indexedAttributes(value) {
4316
+ if (Array.isArray(value)) {
4317
+ return value.every((item) => typeof item === "string") ? value : void 0;
4318
+ }
4319
+ const source = optionalText(value);
4320
+ if (source == null) return void 0;
4321
+ try {
4322
+ const parsed = JSON.parse(source);
4323
+ return Array.isArray(parsed) && parsed.every((item) => typeof item === "string") ? parsed : void 0;
4324
+ } catch {
4325
+ return void 0;
4326
+ }
4327
+ }
4328
+
4113
4329
  // src/protocol-connection-policy.ts
4114
4330
  function assertCommandDoesNotRequirePinnedConnection(args) {
4115
4331
  const outer = commandName(args[0]);
@@ -4301,7 +4517,7 @@ function compactArgumentsEligible(args, sparseMessage) {
4301
4517
  }
4302
4518
  return true;
4303
4519
  }
4304
- function compactPipelineEligible(commands, commandName4, argumentCount) {
4520
+ function compactPipelineEligible(commands, commandName5, argumentCount) {
4305
4521
  for (let index = 0; index < commands.length; index += 1) {
4306
4522
  const command = commands[index];
4307
4523
  if (!Object.hasOwn(commands, index) || !Array.isArray(command)) {
@@ -4313,7 +4529,7 @@ function compactPipelineEligible(commands, commandName4, argumentCount) {
4313
4529
  throw new TypeError("pipeline command arguments must be dense");
4314
4530
  }
4315
4531
  }
4316
- if (!commandNameIs(command[0], commandName4)) return false;
4532
+ if (!commandNameIs(command[0], commandName5)) return false;
4317
4533
  for (let argumentIndex = 1; argumentIndex < argumentCount; argumentIndex += 1) {
4318
4534
  if (!isCompactBinaryScalar(command[argumentIndex])) return false;
4319
4535
  }
@@ -4476,15 +4692,20 @@ function fetchOrComputeCommand(args) {
4476
4692
  };
4477
4693
  }
4478
4694
  function fetchOrComputeResultCommand(args) {
4479
- if (args.length !== 4 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1])) return void 0;
4695
+ if (args.length !== 4 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1])) {
4696
+ throw new FerricStoreError("FETCH_OR_COMPUTE_RESULT requires key, ownership token, value, and ttl_ms");
4697
+ }
4480
4698
  const ttlMs = positiveSafeInteger2(args[3]);
4481
- return ttlMs == null ? void 0 : {
4699
+ if (ttlMs == null) throw new FerricStoreError("FETCH_OR_COMPUTE_RESULT ttl_ms must be positive");
4700
+ return {
4482
4701
  opcode: COMMAND_OPCODES.FETCH_OR_COMPUTE_RESULT,
4483
4702
  payload: { key: args[0], token: args[1], ttl_ms: ttlMs, value: args[2] }
4484
4703
  };
4485
4704
  }
4486
4705
  function fetchOrComputeErrorCommand(args) {
4487
- if (args.length !== 3 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1]) || !isBinaryCommandArgument(args[2])) return void 0;
4706
+ if (args.length !== 3 || !isBinaryCommandArgument(args[0]) || !isBinaryCommandArgument(args[1]) || !isBinaryCommandArgument(args[2])) {
4707
+ throw new FerricStoreError("FETCH_OR_COMPUTE_ERROR requires key, ownership token, and message");
4708
+ }
4488
4709
  return {
4489
4710
  opcode: COMMAND_OPCODES.FETCH_OR_COMPUTE_ERROR,
4490
4711
  payload: { key: args[0], message: args[2], token: args[1] }
@@ -4841,62 +5062,462 @@ function requireAvailable(data, offset, size) {
4841
5062
  }
4842
5063
  }
4843
5064
 
4844
- // src/protocol-response.ts
5065
+ // src/key-slot-validation.ts
4845
5066
  import { Buffer as Buffer18 } from "buffer";
4846
5067
 
4847
- // src/protocol-compact-response.ts
5068
+ // src/topology-utilities.ts
4848
5069
  import { Buffer as Buffer17 } from "buffer";
4849
- function tryDecodeCompactResponse(opcode, body, hints) {
4850
- if (body.byteLength === 0) return { found: false, value: void 0 };
4851
- const budget = { remainingItems: DEFAULT_MAX_VALUE_ITEMS };
4852
- const tag = body.readUInt8(0);
4853
- if (tag === COMPACT_OK_LIST && isOkListOpcode(opcode)) {
4854
- const values = decodeCompactOkList(body, budget);
4855
- return { found: true, value: opcode === OPCODES.pipeline ? values : values.length === 1 ? "OK" : values };
4856
- }
4857
- if (tag === COMPACT_KV_GET && compactResponseOpcodeSupports(opcode, COMPACT_KIND_KV_GET)) {
4858
- return { found: true, value: decodeCompactKvGet(body) };
5070
+ function isPlainObject3(value) {
5071
+ return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer17.isBuffer(value);
5072
+ }
5073
+ function getField(source, ...keys) {
5074
+ if (source instanceof Map) {
5075
+ for (const key of keys) {
5076
+ if (source.has(key)) return source.get(key);
5077
+ const bufferKey = Buffer17.from(key);
5078
+ for (const [itemKey, value] of source.entries()) {
5079
+ if (Buffer17.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
5080
+ }
5081
+ }
5082
+ return void 0;
4859
5083
  }
4860
- if ((tag === COMPACT_KV_MGET || tag === COMPACT_KV_MGET_FIXED) && compactResponseOpcodeSupports(opcode, COMPACT_KIND_KV_MGET)) {
4861
- return {
4862
- found: true,
4863
- value: tag === COMPACT_KV_MGET ? decodeCompactMget(body, budget) : decodeCompactMgetFixed(body, budget)
4864
- };
5084
+ if (isPlainObject3(source)) {
5085
+ for (const key of keys) {
5086
+ if (Object.hasOwn(source, key)) return source[key];
5087
+ }
4865
5088
  }
4866
- if (tag === COMPACT_PIPELINE_RESPONSE && compactResponseOpcodeSupports(opcode, COMPACT_KIND_PIPELINE)) {
4867
- return { found: true, value: decodeCompactPipeline(body, budget, hints.pipelineClaimModes) };
5089
+ return void 0;
5090
+ }
5091
+ function textOrUndefined(value) {
5092
+ if (typeof value === "string") return value;
5093
+ if (Buffer17.isBuffer(value)) return value.toString("utf8");
5094
+ return void 0;
5095
+ }
5096
+ function numberOrUndefined(value) {
5097
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
5098
+ }
5099
+ function nativeEventName(event) {
5100
+ return textOrUndefined(getField(event.value, "event"))?.toUpperCase();
5101
+ }
5102
+ function endpointFromRange(item) {
5103
+ const endpointValue = getField(item, "endpoint");
5104
+ if (endpointValue != null && !isPlainObject3(endpointValue) && !(endpointValue instanceof Map)) {
5105
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4868
5106
  }
4869
- if (tag === COMPACT_FLOW_CLAIM_JOBS && compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_CLAIM_JOBS)) {
4870
- return { found: true, value: decodeCompactClaimJobs(body, budget, hints.compactClaimMode) };
5107
+ const raw = endpointValue ?? item;
5108
+ const host = textOrUndefined(getField(raw, "host", "native_host"));
5109
+ const nativePort = numberOrUndefined(getField(raw, "native_port"));
5110
+ if (host == null || !validEndpointHost(host) || nativePort == null || nativePort <= 0 || nativePort > 65535) {
5111
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4871
5112
  }
4872
- if (tag === COMPACT_FLOW_RECORD && compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_RECORD)) {
4873
- const read = readCompactFlowRecord(body, 0, budget);
4874
- if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record bytes");
4875
- return { found: true, value: read.value };
5113
+ const nativeTlsPortValue = getField(raw, "native_tls_port");
5114
+ const nativeTlsPort = numberOrUndefined(nativeTlsPortValue);
5115
+ if (nativeTlsPortValue != null && (nativeTlsPort == null || nativeTlsPort <= 0 || nativeTlsPort > 65535)) {
5116
+ throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
4876
5117
  }
4877
- if (tag === COMPACT_FLOW_RECORD_LIST && isFlowRecordListOpcode(opcode)) {
4878
- const read = readCompactFlowRecordList(body, 0, budget);
4879
- if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record list bytes");
4880
- return { found: true, value: read.value };
5118
+ return {
5119
+ host,
5120
+ nativePort,
5121
+ node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
5122
+ ...nativeTlsPort == null ? {} : { nativeTlsPort }
5123
+ };
5124
+ }
5125
+ function validEndpointHost(host) {
5126
+ if (host.length === 0 || host.trim() !== host || /[\s\\/?#@]/u.test(host)) return false;
5127
+ const authorityHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
5128
+ try {
5129
+ const url = new URL(`ferric://${authorityHost}:6388`);
5130
+ return url.hostname !== "" && url.port === "6388" && url.username === "" && url.password === "";
5131
+ } catch {
5132
+ return false;
4881
5133
  }
4882
- return { found: false, value: void 0 };
4883
5134
  }
4884
- function isOkListOpcode(opcode) {
4885
- return compactResponseOpcodeSupports(opcode, COMPACT_KIND_OK_LIST);
5135
+ function sameEndpoint(left, right) {
5136
+ return normalizedEndpointHost(left.host) === normalizedEndpointHost(right.host) && left.nativePort === right.nativePort && left.nativeTlsPort === right.nativeTlsPort && left.node === right.node;
4886
5137
  }
4887
- function isFlowRecordListOpcode(opcode) {
4888
- return compactResponseOpcodeSupports(opcode, COMPACT_KIND_FLOW_RECORD_LIST);
5138
+ function parseFerricUrl2(value) {
5139
+ const url = new URL(value);
5140
+ if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
5141
+ throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
5142
+ }
5143
+ return {
5144
+ host: normalizeFerricUrlHost(url.hostname),
5145
+ ...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
5146
+ port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
5147
+ tls: url.protocol === "ferrics:",
5148
+ ...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
5149
+ };
4889
5150
  }
4890
- function decodeCompactOkList(data, budget) {
4891
- requireAvailable(data, 0, 5);
4892
- const count = data.readUInt32BE(1);
4893
- requireCompactContainer(count, budget);
4894
- if (data.byteLength !== 5) throw new FerricStoreError("invalid compact OK list response");
4895
- return Array.from({ length: count }, () => Buffer17.from("OK"));
5151
+ function endpointKeyFor(endpoint) {
5152
+ return `${normalizedEndpointHost(endpoint.host)}:${endpoint.nativePort}`;
4896
5153
  }
4897
- function decodeCompactKvGet(data) {
4898
- requireAvailable(data, 0, 2);
4899
- const present = data.readUInt8(1);
5154
+ function connectionKeyForEndpoint(endpoint, useTls) {
5155
+ const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
5156
+ return `${useTls ? "ferrics" : "ferric"}://${normalizedEndpointHost(endpoint.host)}:${port}`;
5157
+ }
5158
+ function connectionKeyFromUrl(url) {
5159
+ const parsed = parseFerricUrl2(url);
5160
+ return `${parsed.tls ? "ferrics" : "ferric"}://${normalizedEndpointHost(parsed.host)}:${parsed.port}`;
5161
+ }
5162
+ function normalizedEndpointHost(host) {
5163
+ const lower = host.toLowerCase();
5164
+ return lower.startsWith("[") && lower.endsWith("]") ? lower.slice(1, -1) : lower;
5165
+ }
5166
+ function urlFromEndpoint(endpoint, useTls) {
5167
+ const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
5168
+ const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
5169
+ return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
5170
+ }
5171
+ async function mapSettledWithConcurrency(items, concurrency, operation) {
5172
+ const results = new Array(items.length);
5173
+ let nextIndex = 0;
5174
+ const worker = async () => {
5175
+ while (nextIndex < items.length) {
5176
+ const index = nextIndex++;
5177
+ const item = items[index];
5178
+ try {
5179
+ results[index] = { status: "fulfilled", value: await operation(item, index) };
5180
+ } catch (reason) {
5181
+ results[index] = { status: "rejected", reason };
5182
+ }
5183
+ }
5184
+ };
5185
+ await Promise.all(Array.from(
5186
+ { length: Math.min(concurrency, items.length) },
5187
+ async () => await worker()
5188
+ ));
5189
+ return results;
5190
+ }
5191
+ function throwFirstRejected(results) {
5192
+ for (const result of results) {
5193
+ if (result.status === "rejected") throw result.reason;
5194
+ }
5195
+ }
5196
+ var TaskConcurrencyLimiter = class {
5197
+ constructor(concurrency) {
5198
+ this.concurrency = concurrency;
5199
+ }
5200
+ concurrency;
5201
+ active = 0;
5202
+ head = 0;
5203
+ pending = [];
5204
+ async run(operation) {
5205
+ await this.acquire();
5206
+ try {
5207
+ return await operation();
5208
+ } finally {
5209
+ this.release();
5210
+ }
5211
+ }
5212
+ acquire() {
5213
+ if (this.active < this.concurrency) {
5214
+ this.active += 1;
5215
+ return Promise.resolve();
5216
+ }
5217
+ return new Promise((resolve) => this.pending.push(resolve));
5218
+ }
5219
+ release() {
5220
+ const next = this.pending[this.head];
5221
+ if (next == null) {
5222
+ this.active -= 1;
5223
+ return;
5224
+ }
5225
+ this.head += 1;
5226
+ next();
5227
+ if (this.head === this.pending.length) {
5228
+ this.pending.length = 0;
5229
+ this.head = 0;
5230
+ } else if (this.head >= 1024 && this.head * 2 >= this.pending.length) {
5231
+ this.pending.splice(0, this.head);
5232
+ this.head = 0;
5233
+ }
5234
+ }
5235
+ };
5236
+ function normalizedHostSet(hosts) {
5237
+ return new Set(
5238
+ hosts.filter((host) => host != null && host !== "").map((host) => normalizedEndpointHost(host))
5239
+ );
5240
+ }
5241
+ var OPEN_BRACE = "{".charCodeAt(0);
5242
+ var CLOSE_BRACE = "}".charCodeAt(0);
5243
+ function hashTagOrKey(key) {
5244
+ const start = key.indexOf(OPEN_BRACE);
5245
+ if (start < 0) return key;
5246
+ const end = key.indexOf(CLOSE_BRACE, start + 1);
5247
+ return end > start + 1 ? key.subarray(start + 1, end) : key;
5248
+ }
5249
+ var ROUTE_SLOT_MASK = 1023;
5250
+ function routingSlotForKey(key) {
5251
+ if (typeof key === "string") {
5252
+ const [start, end] = stringHashRange(key);
5253
+ return crc32Utf8(key, start, end) & ROUTE_SLOT_MASK;
5254
+ }
5255
+ const keyBytes = key;
5256
+ return crc32(hashTagOrKey(keyBytes)) & ROUTE_SLOT_MASK;
5257
+ }
5258
+ function stringHashRange(key) {
5259
+ const startBrace = key.indexOf("{");
5260
+ const endBrace = startBrace < 0 ? -1 : key.indexOf("}", startBrace + 1);
5261
+ return endBrace > startBrace + 1 ? [startBrace + 1, endBrace] : [0, key.length];
5262
+ }
5263
+
5264
+ // src/key-slot-validation.ts
5265
+ var validatedCommands = /* @__PURE__ */ new WeakSet();
5266
+ function assertAtomicKeyValueCommandSharesSlot(args) {
5267
+ const value = args[0];
5268
+ const command = (typeof value === "string" ? value : Buffer18.isBuffer(value) ? value.toString("utf8") : "").toUpperCase();
5269
+ if (command === "MSET" || command === "MSETNX") {
5270
+ assertKeyValueCommandSharesSlot(args, command);
5271
+ }
5272
+ }
5273
+ function assertKeyValueCommandSharesSlot(args, command) {
5274
+ if (validatedCommands.has(args) || args.length < 3 || args.length % 2 === 0) return;
5275
+ let expectedSlot;
5276
+ for (let index = 1; index < args.length; index += 2) {
5277
+ const key = args[index];
5278
+ if (typeof key !== "string" && !Buffer18.isBuffer(key)) return;
5279
+ const slot = routingSlotForKey(key);
5280
+ expectedSlot ??= slot;
5281
+ if (slot !== expectedSlot) {
5282
+ throw new TypeError(`${command} keys must share a slot`);
5283
+ }
5284
+ }
5285
+ validatedCommands.add(args);
5286
+ }
5287
+
5288
+ // src/protocol-response.ts
5289
+ import { Buffer as Buffer22 } from "buffer";
5290
+
5291
+ // src/protocol-compact-response.ts
5292
+ import { Buffer as Buffer21 } from "buffer";
5293
+
5294
+ // src/native-negotiation.ts
5295
+ import { Buffer as Buffer19 } from "buffer";
5296
+ var EMPTY_COMPACT_RESPONSE_OPCODES = /* @__PURE__ */ new Map();
5297
+ function nativeNegotiation(value) {
5298
+ const capabilities = field(value, "capabilities") ?? value;
5299
+ const limits = field(capabilities, "limits");
5300
+ const responseCodecs = field(capabilities, "response_codecs");
5301
+ const maxResponseBytes = positiveSafeInteger3(field(limits, "max_response_bytes"));
5302
+ return {
5303
+ compactResponseOpcodes: parseCompactResponseOpcodes(
5304
+ field(responseCodecs, "compact_response_opcodes")
5305
+ ),
5306
+ ...maxResponseBytes == null ? {} : { maxResponseBytes }
5307
+ };
5308
+ }
5309
+ function compactResponseOpcodeSupports(capabilities, codec, opcode) {
5310
+ return capabilities?.get(codec)?.has(opcode) === true;
5311
+ }
5312
+ function parseCompactResponseOpcodes(value) {
5313
+ const entries = mapEntries(value);
5314
+ if (entries == null) return EMPTY_COMPACT_RESPONSE_OPCODES;
5315
+ const result = /* @__PURE__ */ new Map();
5316
+ for (const [rawName, rawOpcodes] of entries) {
5317
+ const name = text2(rawName);
5318
+ if (name == null || name.length === 0 || !Array.isArray(rawOpcodes)) continue;
5319
+ const opcodes = /* @__PURE__ */ new Set();
5320
+ let valid = true;
5321
+ for (let index = 0; index < rawOpcodes.length; index += 1) {
5322
+ if (!Object.hasOwn(rawOpcodes, index)) {
5323
+ valid = false;
5324
+ break;
5325
+ }
5326
+ const opcode = unsigned16(rawOpcodes[index]);
5327
+ if (opcode == null) {
5328
+ valid = false;
5329
+ break;
5330
+ }
5331
+ opcodes.add(opcode);
5332
+ }
5333
+ if (valid) result.set(name, opcodes);
5334
+ }
5335
+ return result;
5336
+ }
5337
+ function mapEntries(value) {
5338
+ if (value instanceof Map) return [...value.entries()];
5339
+ if (typeof value !== "object" || value == null || Array.isArray(value)) return void 0;
5340
+ return Object.entries(value);
5341
+ }
5342
+ function unsigned16(value) {
5343
+ const parsed = nonNegativeSafeInteger2(value);
5344
+ return parsed != null && parsed <= 65535 ? parsed : void 0;
5345
+ }
5346
+ function positiveSafeInteger3(value) {
5347
+ const parsed = nonNegativeSafeInteger2(value);
5348
+ return parsed != null && parsed > 0 ? parsed : void 0;
5349
+ }
5350
+ function nonNegativeSafeInteger2(value) {
5351
+ if (typeof value === "bigint") {
5352
+ return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : void 0;
5353
+ }
5354
+ const source = text2(value) ?? value;
5355
+ if (typeof source !== "number" && (typeof source !== "string" || !/^\d+$/u.test(source))) {
5356
+ return void 0;
5357
+ }
5358
+ const parsed = Number(source);
5359
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
5360
+ }
5361
+ function text2(value) {
5362
+ if (typeof value === "string") return value;
5363
+ if (Buffer19.isBuffer(value) || value instanceof Uint8Array) {
5364
+ return Buffer19.from(value).toString("utf8");
5365
+ }
5366
+ return void 0;
5367
+ }
5368
+
5369
+ // src/protocol-compact-collections.ts
5370
+ import "buffer";
5371
+ function decodeCompactIntegerList(data, budget) {
5372
+ requireAvailable(data, 0, 5);
5373
+ const count = data.readUInt32BE(1);
5374
+ requireCompactContainer(count, budget);
5375
+ if (data.byteLength !== 5 + count * 8) {
5376
+ throw new FerricStoreError("invalid compact integer list response");
5377
+ }
5378
+ const values = new Array(count);
5379
+ for (let index = 0, offset = 5; index < count; index += 1, offset += 8) {
5380
+ const value = data.readBigInt64BE(offset);
5381
+ values[index] = value <= MAX_SAFE_INTEGER_BIGINT && value >= MIN_SAFE_INTEGER_BIGINT ? Number(value) : value;
5382
+ }
5383
+ return values;
5384
+ }
5385
+ function decodeCompactBinaryListList(data, budget) {
5386
+ requireAvailable(data, 0, 5);
5387
+ const count = data.readUInt32BE(1);
5388
+ requireCompactContainer(count, budget);
5389
+ let offset = 5;
5390
+ const values = new Array(count);
5391
+ for (let index = 0; index < count; index += 1) {
5392
+ const read = readCompactBinaryList(data, offset, budget);
5393
+ values[index] = read.value;
5394
+ offset = read.offset;
5395
+ }
5396
+ if (offset !== data.byteLength) throw new FerricStoreError("trailing compact binary list-list bytes");
5397
+ return values;
5398
+ }
5399
+ function decodeCompactBinaryMapList(data, budget) {
5400
+ requireAvailable(data, 0, 5);
5401
+ const count = data.readUInt32BE(1);
5402
+ requireCompactContainer(count, budget);
5403
+ let offset = 5;
5404
+ const values = new Array(count);
5405
+ for (let index = 0; index < count; index += 1) {
5406
+ const read = readCompactBinaryMap(data, offset, budget);
5407
+ values[index] = read.value;
5408
+ offset = read.offset;
5409
+ }
5410
+ if (offset !== data.byteLength) throw new FerricStoreError("trailing compact binary map-list bytes");
5411
+ return values;
5412
+ }
5413
+ function readCompactBinaryList(data, offset, budget) {
5414
+ requireAvailable(data, offset, 4);
5415
+ const count = data.readUInt32BE(offset);
5416
+ requireCompactContainer(count, budget);
5417
+ offset += 4;
5418
+ const values = new Array(count);
5419
+ for (let index = 0; index < count; index += 1) {
5420
+ const read = readBinary(data, offset);
5421
+ values[index] = read.value;
5422
+ offset = read.offset;
5423
+ }
5424
+ return { value: values, offset };
5425
+ }
5426
+ function readCompactBinaryMap(data, offset, budget) {
5427
+ requireAvailable(data, offset, 4);
5428
+ const count = data.readUInt32BE(offset);
5429
+ requireCompactContainer(count, budget);
5430
+ offset += 4;
5431
+ const values = {};
5432
+ for (let index = 0; index < count; index += 1) {
5433
+ const key = readBinary(data, offset);
5434
+ const value = readBinary(data, key.offset);
5435
+ setOwnValue2(values, key.value.toString("utf8"), value.value);
5436
+ offset = value.offset;
5437
+ }
5438
+ return { value: values, offset };
5439
+ }
5440
+ function markCompactPipelineDecoded(values) {
5441
+ Object.defineProperty(values, COMPACT_PIPELINE_DECODED, { value: true });
5442
+ return values;
5443
+ }
5444
+ function requireCompactContainer(count, budget) {
5445
+ if (count > DEFAULT_MAX_VALUE_ITEMS) {
5446
+ throw new FerricStoreError("native compact response container exceeds max items");
5447
+ }
5448
+ consumeDecodeItems(count, budget);
5449
+ }
5450
+
5451
+ // src/protocol-compact-response.ts
5452
+ function tryDecodeCompactResponse(opcode, body, hints) {
5453
+ if (body.byteLength === 0) return { found: false, value: void 0 };
5454
+ const budget = { remainingItems: DEFAULT_MAX_VALUE_ITEMS };
5455
+ const tag = body.readUInt8(0);
5456
+ const pipelineValues = opcode === OPCODES.pipeline && supports(hints, "pipeline_v1", opcode);
5457
+ if (tag === COMPACT_OK_LIST && (supports(hints, "ok_list_v1", opcode) || pipelineValues)) {
5458
+ const values = decodeCompactOkList(body, budget);
5459
+ return {
5460
+ found: true,
5461
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values.length === 1 ? "OK" : values
5462
+ };
5463
+ }
5464
+ if (tag === COMPACT_KV_GET && supports(hints, "kv_get_v1", opcode)) {
5465
+ return { found: true, value: decodeCompactKvGet(body) };
5466
+ }
5467
+ if ((tag === COMPACT_KV_MGET || tag === COMPACT_KV_MGET_FIXED) && (supports(hints, "kv_mget_v1", opcode) || pipelineValues)) {
5468
+ const values = tag === COMPACT_KV_MGET ? decodeCompactMget(body, budget) : decodeCompactMgetFixed(body, budget);
5469
+ return {
5470
+ found: true,
5471
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values
5472
+ };
5473
+ }
5474
+ if (tag === COMPACT_INTEGER_LIST && pipelineValues) {
5475
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactIntegerList(body, budget)) };
5476
+ }
5477
+ if (tag === COMPACT_BINARY_LIST_LIST && pipelineValues) {
5478
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactBinaryListList(body, budget)) };
5479
+ }
5480
+ if (tag === COMPACT_BINARY_MAP_LIST && pipelineValues) {
5481
+ return { found: true, value: markCompactPipelineDecoded(decodeCompactBinaryMapList(body, budget)) };
5482
+ }
5483
+ if (tag === COMPACT_PIPELINE_RESPONSE && pipelineValues) {
5484
+ return { found: true, value: decodeCompactPipeline(body, budget, hints.pipelineClaimModes) };
5485
+ }
5486
+ if (tag === COMPACT_FLOW_CLAIM_JOBS && (supports(hints, "flow_claim_jobs_v1", opcode) || pipelineValues)) {
5487
+ const values = decodeCompactClaimJobs(body, budget, hints.compactClaimMode);
5488
+ return {
5489
+ found: true,
5490
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(values) : values
5491
+ };
5492
+ }
5493
+ if (tag === COMPACT_FLOW_RECORD && supports(hints, "flow_record_v1", opcode)) {
5494
+ const read = readCompactFlowRecord(body, 0, budget);
5495
+ if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record bytes");
5496
+ return { found: true, value: read.value };
5497
+ }
5498
+ if (tag === COMPACT_FLOW_RECORD_LIST && (supports(hints, "flow_record_list_v1", opcode) || pipelineValues)) {
5499
+ const read = readCompactFlowRecordList(body, 0, budget);
5500
+ if (read.offset !== body.byteLength) throw new FerricStoreError("trailing compact Flow record list bytes");
5501
+ return {
5502
+ found: true,
5503
+ value: opcode === OPCODES.pipeline ? markCompactPipelineDecoded(read.value) : read.value
5504
+ };
5505
+ }
5506
+ return { found: false, value: void 0 };
5507
+ }
5508
+ function supports(hints, codec, opcode) {
5509
+ return compactResponseOpcodeSupports(hints.compactResponseOpcodes, codec, opcode);
5510
+ }
5511
+ function decodeCompactOkList(data, budget) {
5512
+ requireAvailable(data, 0, 5);
5513
+ const count = data.readUInt32BE(1);
5514
+ requireCompactContainer2(count, budget);
5515
+ if (data.byteLength !== 5) throw new FerricStoreError("invalid compact OK list response");
5516
+ return Array.from({ length: count }, () => Buffer21.from("OK"));
5517
+ }
5518
+ function decodeCompactKvGet(data) {
5519
+ requireAvailable(data, 0, 2);
5520
+ const present = data.readUInt8(1);
4900
5521
  if (present === 0) {
4901
5522
  if (data.byteLength !== 2) throw new FerricStoreError("trailing compact GET bytes");
4902
5523
  return null;
@@ -4909,7 +5530,7 @@ function decodeCompactKvGet(data) {
4909
5530
  function decodeCompactMget(data, budget) {
4910
5531
  requireAvailable(data, 0, 5);
4911
5532
  const count = data.readUInt32BE(1);
4912
- requireCompactContainer(count, budget);
5533
+ requireCompactContainer2(count, budget);
4913
5534
  let offset = 5;
4914
5535
  const values = [];
4915
5536
  for (let index = 0; index < count; index += 1) {
@@ -4933,12 +5554,12 @@ function decodeCompactMgetFixed(data, budget) {
4933
5554
  requireAvailable(data, 0, 9);
4934
5555
  const count = data.readUInt32BE(1);
4935
5556
  const size = data.readUInt32BE(5);
4936
- requireCompactContainer(count, budget);
5557
+ requireCompactContainer2(count, budget);
4937
5558
  const expected = 9 + count * size;
4938
5559
  if (size === NULL_U32 || data.byteLength !== expected) {
4939
5560
  throw new FerricStoreError("invalid compact fixed MGET response");
4940
5561
  }
4941
- if (size === 0) return new Array(count).fill(Buffer17.alloc(0));
5562
+ if (size === 0) return new Array(count).fill(Buffer21.alloc(0));
4942
5563
  const values = new Array(count);
4943
5564
  for (let index = 0, offset = 9; index < count; index += 1, offset += size) {
4944
5565
  values[index] = data.subarray(offset, offset + size);
@@ -4948,7 +5569,7 @@ function decodeCompactMgetFixed(data, budget) {
4948
5569
  function decodeCompactPipeline(data, budget, claimModes) {
4949
5570
  requireAvailable(data, 0, 5);
4950
5571
  const count = data.readUInt32BE(1);
4951
- requireCompactContainer(count, budget);
5572
+ requireCompactContainer2(count, budget);
4952
5573
  if (count > Math.floor((data.byteLength - 5) / 2)) {
4953
5574
  throw new FerricStoreError("compact pipeline item count exceeds response bytes");
4954
5575
  }
@@ -5010,13 +5631,12 @@ function decodeCompactPipeline(data, budget, claimModes) {
5010
5631
  }
5011
5632
  }
5012
5633
  if (offset !== data.byteLength) throw new FerricStoreError("trailing compact pipeline bytes");
5013
- Object.defineProperty(values, COMPACT_PIPELINE_DECODED, { value: true });
5014
- return values;
5634
+ return markCompactPipelineDecoded(values);
5015
5635
  }
5016
5636
  function decodeCompactClaimJobs(data, budget, mode) {
5017
5637
  requireAvailable(data, 0, 5);
5018
5638
  const count = data.readUInt32BE(1);
5019
- requireCompactContainer(count, budget);
5639
+ requireCompactContainer2(count, budget);
5020
5640
  if (mode != null) {
5021
5641
  const values = [];
5022
5642
  let offset = 5;
@@ -5100,7 +5720,7 @@ function readCompactFlowRecord(data, offset, budget = { remainingItems: DEFAULT_
5100
5720
  if (data.readUInt8(offset) !== COMPACT_FLOW_RECORD) throw new FerricStoreError("expected compact Flow record");
5101
5721
  offset += 1;
5102
5722
  const count = data.readUInt32BE(offset);
5103
- requireCompactContainer(count, budget);
5723
+ requireCompactContainer2(count, budget);
5104
5724
  offset += 4;
5105
5725
  const record = {};
5106
5726
  for (let index = 0; index < count; index += 1) {
@@ -5128,7 +5748,7 @@ function readCompactFlowRecordList(data, offset, budget = { remainingItems: DEFA
5128
5748
  }
5129
5749
  offset += 1;
5130
5750
  const count = data.readUInt32BE(offset);
5131
- requireCompactContainer(count, budget);
5751
+ requireCompactContainer2(count, budget);
5132
5752
  offset += 4;
5133
5753
  const records = [];
5134
5754
  for (let index = 0; index < count; index += 1) {
@@ -5138,33 +5758,6 @@ function readCompactFlowRecordList(data, offset, budget = { remainingItems: DEFA
5138
5758
  }
5139
5759
  return { value: records, offset };
5140
5760
  }
5141
- function readCompactBinaryList(data, offset, budget) {
5142
- requireAvailable(data, offset, 4);
5143
- const count = data.readUInt32BE(offset);
5144
- requireCompactContainer(count, budget);
5145
- offset += 4;
5146
- const values = [];
5147
- for (let index = 0; index < count; index += 1) {
5148
- const read = readBinary(data, offset);
5149
- values.push(read.value);
5150
- offset = read.offset;
5151
- }
5152
- return { value: values, offset };
5153
- }
5154
- function readCompactBinaryMap(data, offset, budget) {
5155
- requireAvailable(data, offset, 4);
5156
- const count = data.readUInt32BE(offset);
5157
- requireCompactContainer(count, budget);
5158
- offset += 4;
5159
- const values = {};
5160
- for (let index = 0; index < count; index += 1) {
5161
- const key = readBinary(data, offset);
5162
- const value = readBinary(data, key.offset);
5163
- setOwnValue2(values, key.value.toString("utf8"), value.value);
5164
- offset = value.offset;
5165
- }
5166
- return { value: values, offset };
5167
- }
5168
5761
  function readOptionalBinary(data, offset) {
5169
5762
  requireAvailable(data, offset, 4);
5170
5763
  const size = data.readUInt32BE(offset);
@@ -5173,7 +5766,7 @@ function readOptionalBinary(data, offset) {
5173
5766
  requireAvailable(data, offset, size);
5174
5767
  return { value: data.subarray(offset, offset + size), offset: offset + size };
5175
5768
  }
5176
- function requireCompactContainer(count, budget) {
5769
+ function requireCompactContainer2(count, budget) {
5177
5770
  if (count > DEFAULT_MAX_VALUE_ITEMS) {
5178
5771
  throw new FerricStoreError("native compact response container exceeds max items");
5179
5772
  }
@@ -5181,7 +5774,7 @@ function requireCompactContainer(count, budget) {
5181
5774
  }
5182
5775
  function errorMessage(status, value) {
5183
5776
  if (typeof value === "string") return value;
5184
- if (Buffer17.isBuffer(value)) return value.toString("utf8");
5777
+ if (Buffer21.isBuffer(value)) return value.toString("utf8");
5185
5778
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5186
5779
  const message = value.message;
5187
5780
  return typeof message === "string" ? message : String(message);
@@ -5261,7 +5854,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
5261
5854
  return out;
5262
5855
  }
5263
5856
  function pipelineStatus(value) {
5264
- if (typeof value !== "string" && !Buffer18.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5857
+ if (typeof value !== "string" && !Buffer22.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5265
5858
  const status = asText(value).toLowerCase();
5266
5859
  return status === "ok" || status === "busy" || status === "error" ? status : null;
5267
5860
  }
@@ -5276,7 +5869,7 @@ function decodeResponseValue(opcode, body, hints) {
5276
5869
  }
5277
5870
  function errorMessage2(status, value) {
5278
5871
  if (typeof value === "string") return value;
5279
- if (Buffer18.isBuffer(value)) return value.toString("utf8");
5872
+ if (Buffer22.isBuffer(value)) return value.toString("utf8");
5280
5873
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5281
5874
  const message = value.message;
5282
5875
  return typeof message === "string" ? message : String(message);
@@ -5292,7 +5885,7 @@ function encodeRequest(command, requestId, maxBodyBytes = Number.MAX_SAFE_INTEGE
5292
5885
  const bodyLength = customPayload ? binaryValueByteLength(command.payload) : plan?.byteLength ?? 0;
5293
5886
  if (bodyLength > bodyLimit) throw requestFrameTooLarge(bodyLimit);
5294
5887
  const flags = command.flags ?? 0;
5295
- const frame = Buffer19.allocUnsafe(HEADER_SIZE + bodyLength);
5888
+ const frame = Buffer23.allocUnsafe(HEADER_SIZE + bodyLength);
5296
5889
  frame.write(MAGIC, 0, "ascii");
5297
5890
  frame.writeUInt8(REQUEST_VERSION, 4);
5298
5891
  frame.writeUInt8(flags, 5);
@@ -5343,6 +5936,9 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5343
5936
  }
5344
5937
  const command = asText(args[0]).toUpperCase();
5345
5938
  assertNormalizedCommandDoesNotRequirePinnedConnection(args, command);
5939
+ if (command === "MSET" || command === "MSETNX") {
5940
+ assertKeyValueCommandSharesSlot(args, command);
5941
+ }
5346
5942
  const commandArgs2 = args.slice(1);
5347
5943
  if (command === "COMMAND_EXEC" && commandArgs2.length >= 1) {
5348
5944
  return commandExec(commandArgs2);
@@ -5414,6 +6010,12 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5414
6010
  if (command === "FLOW.LIST") {
5415
6011
  return (allowCustomPayload ? compactFlowListPayload(commandArgs2, maxBodyBytes) : void 0) ?? commandExec(args);
5416
6012
  }
6013
+ if (command === "FLOW.POLICY.SET") {
6014
+ return flowPolicySetPayload(commandArgs2) ?? commandExec(args);
6015
+ }
6016
+ if (command === "FLOW.POLICY.GET") {
6017
+ return flowPolicyGetPayload(commandArgs2) ?? commandExec(args);
6018
+ }
5417
6019
  const flowAdmin = FLOW_ADMIN_COMMANDS[command];
5418
6020
  if (flowAdmin != null) {
5419
6021
  return flowAdminPayload(flowAdmin.opcode, commandArgs2, flowAdmin.leadingFields) ?? commandExec(args);
@@ -5480,20 +6082,7 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5480
6082
  ])) ?? commandExec(args);
5481
6083
  }
5482
6084
  if (command === "FLOW.SIGNAL") {
5483
- return flowCommandExecWithRouting(command, commandArgs2, 1, /* @__PURE__ */ new Set([
5484
- "SIGNAL",
5485
- "PARTITION",
5486
- "IDEMPOTENCY",
5487
- "IF_STATE",
5488
- "TRANSITION_TO",
5489
- "RUN_AT",
5490
- "NOW",
5491
- "PRIORITY",
5492
- "VALUE",
5493
- "VALUE_REF",
5494
- "DROP_VALUE",
5495
- "OVERRIDE_VALUE"
5496
- ]));
6085
+ return flowSignalPayload(commandArgs2) ?? commandExec(args);
5497
6086
  }
5498
6087
  if (command === "FLOW.CANCEL") {
5499
6088
  return flowCommandExecWithRouting(command, commandArgs2, 1, /* @__PURE__ */ new Set([
@@ -5662,6 +6251,7 @@ var DEFAULT_MAX_PENDING_CONTROL_REQUESTS = 4096;
5662
6251
  var DEFAULT_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
5663
6252
  var DEFAULT_MAX_QUEUED_REQUESTS = 65536;
5664
6253
  var DEFAULT_MAX_QUEUED_WRITE_BYTES = 64 * 1024 * 1024;
6254
+ var UNAUTHENTICATED_MAX_FRAME_BYTES = 64 * 1024;
5665
6255
  function assertNativeAdapterOptions(options) {
5666
6256
  const supplied = options;
5667
6257
  for (const name of ["autoReconnect", "haRouting", "seeds"]) {
@@ -5681,7 +6271,7 @@ import "net";
5681
6271
  import "tls";
5682
6272
 
5683
6273
  // src/client-native-options.ts
5684
- import { Buffer as Buffer20 } from "buffer";
6274
+ import { Buffer as Buffer24 } from "buffer";
5685
6275
 
5686
6276
  // src/string-array-snapshot.ts
5687
6277
  function snapshotOwnStringArray(values, name) {
@@ -5764,7 +6354,7 @@ function snapshotTlsOptions(options) {
5764
6354
  return Object.freeze(snapshot);
5765
6355
  }
5766
6356
  function snapshotTlsValue(value, name) {
5767
- if (Buffer20.isBuffer(value)) return Buffer20.from(value);
6357
+ if (Buffer24.isBuffer(value)) return Buffer24.from(value);
5768
6358
  if (value instanceof Uint8Array) return value.slice();
5769
6359
  if (Array.isArray(value)) {
5770
6360
  const snapshot = new Array(value.length);
@@ -6234,6 +6824,9 @@ var NativeChunkAssembler = class {
6234
6824
  framesByKey = /* @__PURE__ */ new Map();
6235
6825
  totalBytes = 0;
6236
6826
  totalFrames = 0;
6827
+ updateMaxResponseBytes(limit) {
6828
+ this.maxResponseBytes = Math.min(this.maxResponseBytes, limit);
6829
+ }
6237
6830
  assemble(frame) {
6238
6831
  const key = `${frame.requestId}:${frame.opcode}:${frame.laneId}`;
6239
6832
  const previous = this.chunks.get(key);
@@ -6478,12 +7071,18 @@ var NativeReceiveBuffer = class {
6478
7071
  var NativeResponseHandler = class {
6479
7072
  constructor(options) {
6480
7073
  this.options = options;
7074
+ this.maxResponseBytes = options.maxResponseBytes;
6481
7075
  }
6482
7076
  options;
6483
7077
  receiveBuffer = new NativeReceiveBuffer();
6484
7078
  drainScheduled = false;
6485
7079
  paused = false;
6486
7080
  stopped = false;
7081
+ maxResponseBytes;
7082
+ updateMaxResponseBytes(limit) {
7083
+ this.maxResponseBytes = Math.min(this.maxResponseBytes, limit);
7084
+ this.options.chunkAssembler.updateMaxResponseBytes(this.maxResponseBytes);
7085
+ }
6487
7086
  onData(chunk) {
6488
7087
  if (this.stopped) return;
6489
7088
  try {
@@ -6565,15 +7164,18 @@ var NativeResponseHandler = class {
6565
7164
  }
6566
7165
  const completeFrame = this.options.chunkAssembler.assemble(frame);
6567
7166
  if (completeFrame == null) return;
6568
- if (completeFrame.body.byteLength > this.options.maxResponseBytes) {
7167
+ if (completeFrame.body.byteLength > this.maxResponseBytes) {
6569
7168
  throw new FerricStoreError(
6570
- `native protocol response exceeded ${this.options.maxResponseBytes} bytes`
7169
+ `native protocol response exceeded ${this.maxResponseBytes} bytes`
6571
7170
  );
6572
7171
  }
6573
7172
  let value;
6574
7173
  let responseError;
6575
7174
  try {
6576
- value = decodeResponse(completeFrame, pending.opcode, pending);
7175
+ value = decodeResponse(completeFrame, pending.opcode, {
7176
+ ...pending,
7177
+ compactResponseOpcodes: this.options.compactResponseOpcodes?.() ?? EMPTY_COMPACT_RESPONSE_OPCODES
7178
+ });
6577
7179
  if (pending.opcode === OPCODES.windowUpdate) this.options.applyFlowControlLimits(value);
6578
7180
  } catch (error) {
6579
7181
  responseError = error;
@@ -6589,7 +7191,7 @@ var NativeResponseHandler = class {
6589
7191
  pending.discardedResponseBytes = discardedResponseBytes;
6590
7192
  pending.discardedResponseFrames = discardedResponseFrames;
6591
7193
  const chunked = discardedResponseFrames > 1 || (frame.flags & FLAG_MORE_CHUNKS) !== 0;
6592
- if (discardedResponseBytes > this.options.maxResponseBytes || chunked && discardedResponseBytes > this.options.maxChunkBytes) {
7194
+ if (discardedResponseBytes > this.maxResponseBytes || chunked && discardedResponseBytes > this.options.maxChunkBytes) {
6593
7195
  throw new FerricStoreError("native protocol discarded response exceeded configured byte limits");
6594
7196
  }
6595
7197
  if (discardedResponseFrames > this.options.maxChunkFrames) {
@@ -6598,12 +7200,14 @@ var NativeResponseHandler = class {
6598
7200
  if ((frame.flags & FLAG_MORE_CHUNKS) === 0) this.options.takePending(frame.requestId);
6599
7201
  }
6600
7202
  handleManagementFrame(frame) {
6601
- if (frame.body.byteLength > this.options.maxResponseBytes) {
7203
+ if (frame.body.byteLength > this.maxResponseBytes) {
6602
7204
  throw new FerricStoreError(
6603
- `native protocol response exceeded ${this.options.maxResponseBytes} bytes`
7205
+ `native protocol response exceeded ${this.maxResponseBytes} bytes`
6604
7206
  );
6605
7207
  }
6606
- const value = decodeResponse(frame, frame.opcode);
7208
+ const value = decodeResponse(frame, frame.opcode, {
7209
+ compactResponseOpcodes: this.options.compactResponseOpcodes?.() ?? EMPTY_COMPACT_RESPONSE_OPCODES
7210
+ });
6607
7211
  if (frame.opcode === OPCODES.windowUpdate) this.options.applyFlowControlLimits(value);
6608
7212
  const event = {
6609
7213
  flags: frame.flags,
@@ -6768,6 +7372,40 @@ var NativeWriteQueue = class {
6768
7372
  }
6769
7373
  };
6770
7374
 
7375
+ // src/native-connection-capabilities.ts
7376
+ var NativeConnectionCapabilities = class {
7377
+ constructor(configuredRequestFrameBytes) {
7378
+ this.configuredRequestFrameBytes = configuredRequestFrameBytes;
7379
+ this.negotiatedRequestFrameBytes = configuredRequestFrameBytes;
7380
+ this.requestFrameBytes = Math.min(configuredRequestFrameBytes, UNAUTHENTICATED_MAX_FRAME_BYTES);
7381
+ }
7382
+ configuredRequestFrameBytes;
7383
+ compactResponseOpcodes = EMPTY_COMPACT_RESPONSE_OPCODES;
7384
+ requestFrameBytes;
7385
+ negotiatedRequestFrameBytes;
7386
+ apply(value) {
7387
+ const limits = startupLimits(value);
7388
+ const negotiation = nativeNegotiation(value);
7389
+ this.compactResponseOpcodes = negotiation.compactResponseOpcodes;
7390
+ if (limits.frameBytes != null) {
7391
+ this.negotiatedRequestFrameBytes = Math.min(
7392
+ this.configuredRequestFrameBytes,
7393
+ limits.frameBytes
7394
+ );
7395
+ this.requestFrameBytes = Math.min(this.requestFrameBytes, this.negotiatedRequestFrameBytes);
7396
+ }
7397
+ return {
7398
+ ...limits.laneQueue == null ? {} : { laneQueue: limits.laneQueue },
7399
+ ...limits.lanes == null ? {} : { lanes: limits.lanes },
7400
+ ...negotiation.maxResponseBytes == null ? {} : { maxResponseBytes: negotiation.maxResponseBytes },
7401
+ ...limits.pipelineCommands == null ? {} : { pipelineCommands: limits.pipelineCommands }
7402
+ };
7403
+ }
7404
+ activateAuthenticated() {
7405
+ this.requestFrameBytes = this.negotiatedRequestFrameBytes;
7406
+ }
7407
+ };
7408
+
6771
7409
  // src/native-adapter.ts
6772
7410
  var NativeAdapter = class _NativeAdapter {
6773
7411
  socket;
@@ -6776,9 +7414,9 @@ var NativeAdapter = class _NativeAdapter {
6776
7414
  closedWaiters = /* @__PURE__ */ new Set();
6777
7415
  chunkAssembler;
6778
7416
  closed = false;
7417
+ capabilities;
6779
7418
  protocolLanes;
6780
7419
  maxPipelineCommands = Number.MAX_SAFE_INTEGER;
6781
- maxRequestFrameBytes;
6782
7420
  pendingControlRequests = 0;
6783
7421
  maxPendingControlRequests;
6784
7422
  heartbeat;
@@ -6793,7 +7431,7 @@ var NativeAdapter = class _NativeAdapter {
6793
7431
  const normalizedMaxChunkBytes = normalizePositiveLimit(maxChunkBytes, DEFAULT_MAX_CHUNK_BYTES);
6794
7432
  const normalizedMaxChunkFrames = normalizePositiveLimit(maxChunkFrames, DEFAULT_MAX_CHUNK_FRAMES);
6795
7433
  const normalizedMaxFrameBytes = normalizePositiveLimit(maxFrameBytes, DEFAULT_MAX_FRAME_BYTES);
6796
- this.maxRequestFrameBytes = normalizedMaxFrameBytes;
7434
+ this.capabilities = new NativeConnectionCapabilities(normalizedMaxFrameBytes);
6797
7435
  const normalizedMaxResponseBytes = normalizePositiveLimit(maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES);
6798
7436
  this.chunkAssembler = new NativeChunkAssembler(
6799
7437
  normalizedMaxChunkBytes,
@@ -6820,7 +7458,7 @@ var NativeAdapter = class _NativeAdapter {
6820
7458
  this.heartbeat = new NativeHeartbeat(
6821
7459
  this.heartbeatIntervalMs,
6822
7460
  async () => {
6823
- await this.request(buildProtocolCommand(["PING"], this.maxRequestFrameBytes), true);
7461
+ await this.request(buildProtocolCommand(["PING"], this.capabilities.requestFrameBytes), true);
6824
7462
  },
6825
7463
  (error) => {
6826
7464
  this.failAll(new FerricStoreError("FerricStore protocol heartbeat failed", { cause: error }), true);
@@ -6831,6 +7469,7 @@ var NativeAdapter = class _NativeAdapter {
6831
7469
  applyFlowControlLimits: (value) => this.applyFlowControlLimits(value),
6832
7470
  beginDraining: () => this.beginDraining(),
6833
7471
  chunkAssembler: this.chunkAssembler,
7472
+ compactResponseOpcodes: () => this.capabilities.compactResponseOpcodes,
6834
7473
  destroy: (error) => this.socket.destroy(error),
6835
7474
  failAll: (reason, connectionClosed, message) => this.failAll(reason, connectionClosed, message),
6836
7475
  heartbeat: this.heartbeat,
@@ -6856,6 +7495,7 @@ var NativeAdapter = class _NativeAdapter {
6856
7495
  adapter,
6857
7496
  auth: async (username, password) => {
6858
7497
  await adapter.request({ laneId: 0, opcode: OPCODES.auth, payload: { password, username } });
7498
+ adapter.capabilities.activateAuthenticated();
6859
7499
  },
6860
7500
  close: async () => {
6861
7501
  await adapter.close();
@@ -6873,13 +7513,13 @@ var NativeAdapter = class _NativeAdapter {
6873
7513
  return await this.executeCommandArgs(args);
6874
7514
  }
6875
7515
  async executeCommandArgs(args) {
6876
- const command = buildProtocolCommand(args, this.maxRequestFrameBytes);
7516
+ const command = buildProtocolCommand(args, this.capabilities.requestFrameBytes);
6877
7517
  return await this.request(command);
6878
7518
  }
6879
7519
  /** @internal Execute raw command arguments on a topology-selected lane. */
6880
7520
  async executeCommandOnLane(args, laneId) {
6881
7521
  return await this.executeProtocolCommand(
6882
- buildProtocolCommand(args, this.maxRequestFrameBytes),
7522
+ buildProtocolCommand(args, this.capabilities.requestFrameBytes),
6883
7523
  laneId
6884
7524
  );
6885
7525
  }
@@ -6901,7 +7541,7 @@ var NativeAdapter = class _NativeAdapter {
6901
7541
  laneId,
6902
7542
  options,
6903
7543
  this.maxPipelineCommands,
6904
- this.maxRequestFrameBytes
7544
+ this.capabilities.requestFrameBytes
6905
7545
  );
6906
7546
  }
6907
7547
  /** @internal Execute an ordered pipeline on a topology-selected lane. */
@@ -6912,13 +7552,11 @@ var NativeAdapter = class _NativeAdapter {
6912
7552
  laneId,
6913
7553
  options,
6914
7554
  this.maxPipelineCommands,
6915
- this.maxRequestFrameBytes
7555
+ this.capabilities.requestFrameBytes
6916
7556
  );
6917
7557
  }
6918
7558
  async close() {
6919
- if (this.closed) {
6920
- return;
6921
- }
7559
+ if (this.closed) return;
6922
7560
  this.failAll(new FerricStoreError("FerricStore connection closed"), true);
6923
7561
  this.socket.destroy();
6924
7562
  }
@@ -6936,7 +7574,9 @@ var NativeAdapter = class _NativeAdapter {
6936
7574
  });
6937
7575
  this.applyStartupLimits(response);
6938
7576
  const authRequired = field(response, "auth_required");
6939
- return authRequired == null ? false : booleanResponse(authRequired);
7577
+ const required = authRequired == null ? false : booleanResponse(authRequired);
7578
+ if (authRequired != null && !required) this.capabilities.activateAuthenticated();
7579
+ return required;
6940
7580
  }
6941
7581
  request(command, allowWhileDraining = false) {
6942
7582
  if (this.closed || this.draining && !allowWhileDraining) {
@@ -6977,7 +7617,7 @@ var NativeAdapter = class _NativeAdapter {
6977
7617
  let frame;
6978
7618
  try {
6979
7619
  requestId = this.requestScheduler.nextRequestId();
6980
- frame = encodeRequest(command, requestId, this.maxRequestFrameBytes);
7620
+ frame = encodeRequest(command, requestId, this.capabilities.requestFrameBytes);
6981
7621
  } catch (error) {
6982
7622
  if (hasFlowControlCredit) this.flowControl.release(laneId);
6983
7623
  return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
@@ -7031,9 +7671,7 @@ var NativeAdapter = class _NativeAdapter {
7031
7671
  this.resolveClosedWaiters();
7032
7672
  }
7033
7673
  failAll(reason, connectionClosed = false, dispatchedMessage) {
7034
- if (this.closed) {
7035
- return;
7036
- }
7674
+ if (this.closed) return;
7037
7675
  this.closed = true;
7038
7676
  this.responseHandler.stop();
7039
7677
  this.resolveClosedWaiters();
@@ -7099,18 +7737,18 @@ var NativeAdapter = class _NativeAdapter {
7099
7737
  }
7100
7738
  applyStartupLimits(value) {
7101
7739
  this.applyFlowControlLimits(value);
7102
- const limits = startupLimits(value);
7103
- if (limits.laneQueue != null) {
7104
- this.flowControl.updateLaneQueueLimit(limits.laneQueue);
7740
+ const capabilities = this.capabilities.apply(value);
7741
+ if (capabilities.maxResponseBytes != null) {
7742
+ this.responseHandler.updateMaxResponseBytes(capabilities.maxResponseBytes);
7105
7743
  }
7106
- if (limits.lanes != null) {
7107
- this.protocolLanes = Math.min(this.protocolLanes, limits.lanes);
7744
+ if (capabilities.laneQueue != null) {
7745
+ this.flowControl.updateLaneQueueLimit(capabilities.laneQueue);
7108
7746
  }
7109
- if (limits.pipelineCommands != null) {
7110
- this.maxPipelineCommands = limits.pipelineCommands;
7747
+ if (capabilities.lanes != null) {
7748
+ this.protocolLanes = Math.min(this.protocolLanes, capabilities.lanes);
7111
7749
  }
7112
- if (limits.frameBytes != null) {
7113
- this.maxRequestFrameBytes = Math.min(this.maxRequestFrameBytes, limits.frameBytes);
7750
+ if (capabilities.pipelineCommands != null) {
7751
+ this.maxPipelineCommands = capabilities.pipelineCommands;
7114
7752
  }
7115
7753
  }
7116
7754
  };
@@ -7124,6 +7762,29 @@ function executeCommandArgs(executor, args) {
7124
7762
 
7125
7763
  // src/reconnecting-executor.ts
7126
7764
  import "buffer";
7765
+
7766
+ // src/command-retry-policy.ts
7767
+ import { Buffer as Buffer28 } from "buffer";
7768
+ function isCasMutation(args) {
7769
+ const offset = commandName2(args[0]) === "COMMAND_EXEC" ? 1 : 0;
7770
+ const name = commandName2(args[offset]);
7771
+ if (name === "CAS") return true;
7772
+ if (name !== "FLOW.POLICY.SET") return false;
7773
+ for (let index = offset + 2; index < args.length; index += 2) {
7774
+ if (!Object.hasOwn(args, index)) return false;
7775
+ if (commandName2(args[index]) === "EXPECTED_GENERATION") return true;
7776
+ }
7777
+ return false;
7778
+ }
7779
+ function commandName2(value) {
7780
+ if (typeof value === "string") return value.toUpperCase();
7781
+ if (Buffer28.isBuffer(value) || value instanceof Uint8Array) {
7782
+ return Buffer28.from(value).toString("utf8").toUpperCase();
7783
+ }
7784
+ return void 0;
7785
+ }
7786
+
7787
+ // src/reconnecting-executor.ts
7127
7788
  var ReconnectingExecutor = class {
7128
7789
  constructor(createExecutor, options = {}) {
7129
7790
  this.createExecutor = createExecutor;
@@ -7154,7 +7815,12 @@ var ReconnectingExecutor = class {
7154
7815
  async executeCommandArgs(args) {
7155
7816
  const snapshot = snapshotCommandArguments(args);
7156
7817
  assertCommandHasStableConnectionState(snapshot);
7157
- return await this.withReconnect((executor) => executeCommandArgs(executor, snapshot));
7818
+ const casMutation = isCasMutation(snapshot);
7819
+ return await this.withReconnect(
7820
+ (executor) => executeCommandArgs(executor, snapshot),
7821
+ casMutation ? isClosedConnectionError : isReconnectableClosedConnectionError,
7822
+ !casMutation
7823
+ );
7158
7824
  }
7159
7825
  async ready() {
7160
7826
  await this.executorPromise;
@@ -7202,333 +7868,134 @@ var ReconnectingExecutor = class {
7202
7868
  async refreshTopology() {
7203
7869
  return await this.withReconnect(async (executor) => {
7204
7870
  if (executor.refreshTopology == null) {
7205
- throw new FerricStoreError("topology refresh requires a topology-aware native executor");
7206
- }
7207
- return await executor.refreshTopology();
7208
- }, isReadOnlyReconnectableClosedConnectionError);
7209
- }
7210
- async route(key) {
7211
- return await this.withReconnect(async (executor) => {
7212
- if (executor.route == null) {
7213
- throw new FerricStoreError("route lookup requires a topology-aware native executor");
7214
- }
7215
- return await executor.route(key);
7216
- }, isReadOnlyReconnectableClosedConnectionError);
7217
- }
7218
- async close() {
7219
- if (this.closePromise != null) {
7220
- await this.closePromise;
7221
- return;
7222
- }
7223
- this.closed = true;
7224
- this.retryAbortController.abort(new FerricStoreError("FerricStore client is closed"));
7225
- const executorPromise = this.executorPromise;
7226
- const reconnectPromise = this.reconnectPromise;
7227
- this.closePromise = (async () => {
7228
- let closeError;
7229
- let closeFailed = false;
7230
- const executor = await executorPromise.catch(() => void 0);
7231
- try {
7232
- await Promise.resolve(executor?.close?.());
7233
- } catch (error) {
7234
- closeFailed = true;
7235
- closeError = error;
7236
- }
7237
- await reconnectPromise?.catch(() => void 0);
7238
- if (closeFailed) throw closeError;
7239
- })();
7240
- await this.closePromise;
7241
- }
7242
- async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError) {
7243
- let executor = await this.executorPromise;
7244
- let retries = 0;
7245
- for (; ; ) {
7246
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7247
- try {
7248
- return await operation(executor);
7249
- } catch (error) {
7250
- if (this.closed || retries >= this.maxRetries || !reconnectable(error)) throw error;
7251
- for (; ; ) {
7252
- retries += 1;
7253
- try {
7254
- executor = await this.reconnect(executor);
7255
- break;
7256
- } catch (reconnectError) {
7257
- if (this.closed || retries >= this.maxRetries) throw reconnectError;
7258
- await this.waitBeforeReconnect(retries);
7259
- }
7260
- }
7261
- }
7262
- }
7263
- }
7264
- async waitBeforeReconnect(failedAttempts) {
7265
- const exponent = Math.min(30, Math.max(0, failedAttempts - 1));
7266
- const base = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent);
7267
- const jitter = base * (this.jitterPct / 100) * Math.random();
7268
- try {
7269
- await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
7270
- } catch (error) {
7271
- if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
7272
- throw error;
7273
- }
7274
- }
7275
- async reconnect(staleExecutor) {
7276
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7277
- const competingReconnect = this.reconnectPromise;
7278
- if (competingReconnect != null) return await competingReconnect;
7279
- const observedExecutorPromise = this.executorPromise;
7280
- const currentExecutor = await observedExecutorPromise.catch(() => void 0);
7281
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7282
- if (this.reconnectPromise != null) return await this.reconnectPromise;
7283
- if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
7284
- if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
7285
- this.reconnectPromise = (async () => {
7286
- await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
7287
- const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
7288
- if (this.closed) {
7289
- await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
7290
- throw new FerricStoreError("FerricStore client is closed");
7291
- }
7292
- this.executorPromise = Promise.resolve(nextExecutor);
7293
- return nextExecutor;
7294
- })().finally(() => {
7295
- this.reconnectPromise = void 0;
7296
- });
7297
- return await this.reconnectPromise;
7298
- }
7299
- };
7300
- function isReconnectableClosedConnectionError(error) {
7301
- return error instanceof ConnectionClosedError && error.requestDisposition === "unsent";
7302
- }
7303
- function isReadOnlyReconnectableClosedConnectionError(error) {
7304
- const pending = [error];
7305
- const seen = /* @__PURE__ */ new Set();
7306
- for (let visited = 0; visited < 16 && pending.length > 0; visited += 1) {
7307
- const current = pending.pop();
7308
- if (isReconnectableClosedConnectionError(current)) return true;
7309
- if (current == null || typeof current !== "object" && typeof current !== "function" || seen.has(current)) continue;
7310
- seen.add(current);
7311
- if (current instanceof Error && current.cause != null) pending.push(current.cause);
7312
- if (current instanceof FerricStoreError && current.raw != null) pending.push(current.raw);
7313
- }
7314
- return false;
7315
- }
7316
- function normalizeNonNegativeInteger2(value, fallback) {
7317
- return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
7318
- }
7319
-
7320
- // src/topology.ts
7321
- import "buffer";
7322
-
7323
- // src/topology-routing.ts
7324
- import { Buffer as Buffer26 } from "buffer";
7325
- import { createHash } from "crypto";
7326
-
7327
- // src/topology-utilities.ts
7328
- import { Buffer as Buffer25 } from "buffer";
7329
- function isPlainObject3(value) {
7330
- return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer25.isBuffer(value);
7331
- }
7332
- function getField(source, ...keys) {
7333
- if (source instanceof Map) {
7334
- for (const key of keys) {
7335
- if (source.has(key)) return source.get(key);
7336
- const bufferKey = Buffer25.from(key);
7337
- for (const [itemKey, value] of source.entries()) {
7338
- if (Buffer25.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
7339
- }
7340
- }
7341
- return void 0;
7342
- }
7343
- if (isPlainObject3(source)) {
7344
- for (const key of keys) {
7345
- if (Object.hasOwn(source, key)) return source[key];
7346
- }
7347
- }
7348
- return void 0;
7349
- }
7350
- function textOrUndefined(value) {
7351
- if (typeof value === "string") return value;
7352
- if (Buffer25.isBuffer(value)) return value.toString("utf8");
7353
- return void 0;
7354
- }
7355
- function numberOrUndefined(value) {
7356
- return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
7357
- }
7358
- function nativeEventName(event) {
7359
- return textOrUndefined(getField(event.value, "event"))?.toUpperCase();
7360
- }
7361
- function endpointFromRange(item) {
7362
- const endpointValue = getField(item, "endpoint");
7363
- if (endpointValue != null && !isPlainObject3(endpointValue) && !(endpointValue instanceof Map)) {
7364
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7365
- }
7366
- const raw = endpointValue ?? item;
7367
- const host = textOrUndefined(getField(raw, "host", "native_host"));
7368
- const nativePort = numberOrUndefined(getField(raw, "native_port"));
7369
- if (host == null || !validEndpointHost(host) || nativePort == null || nativePort <= 0 || nativePort > 65535) {
7370
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7371
- }
7372
- const nativeTlsPortValue = getField(raw, "native_tls_port");
7373
- const nativeTlsPort = numberOrUndefined(nativeTlsPortValue);
7374
- if (nativeTlsPortValue != null && (nativeTlsPort == null || nativeTlsPort <= 0 || nativeTlsPort > 65535)) {
7375
- throw new FerricStoreError("invalid SHARDS endpoint", { raw: item });
7376
- }
7377
- return {
7378
- host,
7379
- nativePort,
7380
- node: textOrUndefined(getField(raw, "node", "leader_node", "owner_node")) ?? host,
7381
- ...nativeTlsPort == null ? {} : { nativeTlsPort }
7382
- };
7383
- }
7384
- function validEndpointHost(host) {
7385
- if (host.length === 0 || host.trim() !== host || /[\s\\/?#@]/u.test(host)) return false;
7386
- const authorityHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
7387
- try {
7388
- const url = new URL(`ferric://${authorityHost}:6388`);
7389
- return url.hostname !== "" && url.port === "6388" && url.username === "" && url.password === "";
7390
- } catch {
7391
- return false;
7392
- }
7393
- }
7394
- function sameEndpoint(left, right) {
7395
- return normalizedEndpointHost(left.host) === normalizedEndpointHost(right.host) && left.nativePort === right.nativePort && left.nativeTlsPort === right.nativeTlsPort && left.node === right.node;
7396
- }
7397
- function parseFerricUrl2(value) {
7398
- const url = new URL(value);
7399
- if (url.protocol !== "ferric:" && url.protocol !== "ferrics:") {
7400
- throw new FerricStoreError(`unsupported FerricStore URL scheme: ${url.protocol}`);
7401
- }
7402
- return {
7403
- host: normalizeFerricUrlHost(url.hostname),
7404
- ...url.password === "" ? {} : { password: decodeURIComponent(url.password) },
7405
- port: Number(url.port || (url.protocol === "ferrics:" ? 6389 : 6388)),
7406
- tls: url.protocol === "ferrics:",
7407
- ...url.username === "" ? {} : { username: decodeURIComponent(url.username) }
7408
- };
7409
- }
7410
- function endpointKeyFor(endpoint) {
7411
- return `${normalizedEndpointHost(endpoint.host)}:${endpoint.nativePort}`;
7412
- }
7413
- function connectionKeyForEndpoint(endpoint, useTls) {
7414
- const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
7415
- return `${useTls ? "ferrics" : "ferric"}://${normalizedEndpointHost(endpoint.host)}:${port}`;
7416
- }
7417
- function connectionKeyFromUrl(url) {
7418
- const parsed = parseFerricUrl2(url);
7419
- return `${parsed.tls ? "ferrics" : "ferric"}://${normalizedEndpointHost(parsed.host)}:${parsed.port}`;
7420
- }
7421
- function normalizedEndpointHost(host) {
7422
- const lower = host.toLowerCase();
7423
- return lower.startsWith("[") && lower.endsWith("]") ? lower.slice(1, -1) : lower;
7424
- }
7425
- function urlFromEndpoint(endpoint, useTls) {
7426
- const port = useTls && endpoint.nativeTlsPort != null ? endpoint.nativeTlsPort : endpoint.nativePort;
7427
- const host = endpoint.host.includes(":") && !endpoint.host.startsWith("[") ? `[${endpoint.host}]` : endpoint.host;
7428
- return `${useTls ? "ferrics" : "ferric"}://${host}:${port}`;
7429
- }
7430
- async function mapSettledWithConcurrency(items, concurrency, operation) {
7431
- const results = new Array(items.length);
7432
- let nextIndex = 0;
7433
- const worker = async () => {
7434
- while (nextIndex < items.length) {
7435
- const index = nextIndex++;
7436
- const item = items[index];
7437
- try {
7438
- results[index] = { status: "fulfilled", value: await operation(item, index) };
7439
- } catch (reason) {
7440
- results[index] = { status: "rejected", reason };
7871
+ throw new FerricStoreError("topology refresh requires a topology-aware native executor");
7441
7872
  }
7442
- }
7443
- };
7444
- await Promise.all(Array.from(
7445
- { length: Math.min(concurrency, items.length) },
7446
- async () => await worker()
7447
- ));
7448
- return results;
7449
- }
7450
- function throwFirstRejected(results) {
7451
- for (const result of results) {
7452
- if (result.status === "rejected") throw result.reason;
7873
+ return await executor.refreshTopology();
7874
+ }, isReadOnlyReconnectableClosedConnectionError);
7453
7875
  }
7454
- }
7455
- var TaskConcurrencyLimiter = class {
7456
- constructor(concurrency) {
7457
- this.concurrency = concurrency;
7876
+ async route(key) {
7877
+ return await this.withReconnect(async (executor) => {
7878
+ if (executor.route == null) {
7879
+ throw new FerricStoreError("route lookup requires a topology-aware native executor");
7880
+ }
7881
+ return await executor.route(key);
7882
+ }, isReadOnlyReconnectableClosedConnectionError);
7458
7883
  }
7459
- concurrency;
7460
- active = 0;
7461
- head = 0;
7462
- pending = [];
7463
- async run(operation) {
7464
- await this.acquire();
7465
- try {
7466
- return await operation();
7467
- } finally {
7468
- this.release();
7884
+ async close() {
7885
+ if (this.closePromise != null) {
7886
+ await this.closePromise;
7887
+ return;
7469
7888
  }
7889
+ this.closed = true;
7890
+ this.retryAbortController.abort(new FerricStoreError("FerricStore client is closed"));
7891
+ const executorPromise = this.executorPromise;
7892
+ const reconnectPromise = this.reconnectPromise;
7893
+ this.closePromise = (async () => {
7894
+ let closeError;
7895
+ let closeFailed = false;
7896
+ const executor = await executorPromise.catch(() => void 0);
7897
+ try {
7898
+ await Promise.resolve(executor?.close?.());
7899
+ } catch (error) {
7900
+ closeFailed = true;
7901
+ closeError = error;
7902
+ }
7903
+ await reconnectPromise?.catch(() => void 0);
7904
+ if (closeFailed) throw closeError;
7905
+ })();
7906
+ await this.closePromise;
7470
7907
  }
7471
- acquire() {
7472
- if (this.active < this.concurrency) {
7473
- this.active += 1;
7474
- return Promise.resolve();
7908
+ async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError, replayAllowed = true) {
7909
+ let executor = await this.executorPromise;
7910
+ let retries = 0;
7911
+ for (; ; ) {
7912
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7913
+ try {
7914
+ return await operation(executor);
7915
+ } catch (error) {
7916
+ if (this.closed || retries >= this.maxRetries || !reconnectable(error)) throw error;
7917
+ if (!replayAllowed) {
7918
+ await this.reconnect(executor).catch(() => void 0);
7919
+ throw error;
7920
+ }
7921
+ for (; ; ) {
7922
+ retries += 1;
7923
+ try {
7924
+ executor = await this.reconnect(executor);
7925
+ break;
7926
+ } catch (reconnectError) {
7927
+ if (this.closed || retries >= this.maxRetries) throw reconnectError;
7928
+ await this.waitBeforeReconnect(retries);
7929
+ }
7930
+ }
7931
+ }
7475
7932
  }
7476
- return new Promise((resolve) => this.pending.push(resolve));
7477
7933
  }
7478
- release() {
7479
- const next = this.pending[this.head];
7480
- if (next == null) {
7481
- this.active -= 1;
7482
- return;
7483
- }
7484
- this.head += 1;
7485
- next();
7486
- if (this.head === this.pending.length) {
7487
- this.pending.length = 0;
7488
- this.head = 0;
7489
- } else if (this.head >= 1024 && this.head * 2 >= this.pending.length) {
7490
- this.pending.splice(0, this.head);
7491
- this.head = 0;
7934
+ async waitBeforeReconnect(failedAttempts) {
7935
+ const exponent = Math.min(30, Math.max(0, failedAttempts - 1));
7936
+ const base = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent);
7937
+ const jitter = base * (this.jitterPct / 100) * Math.random();
7938
+ try {
7939
+ await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
7940
+ } catch (error) {
7941
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
7942
+ throw error;
7492
7943
  }
7493
7944
  }
7945
+ async reconnect(staleExecutor) {
7946
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7947
+ const competingReconnect = this.reconnectPromise;
7948
+ if (competingReconnect != null) return await competingReconnect;
7949
+ const observedExecutorPromise = this.executorPromise;
7950
+ const currentExecutor = await observedExecutorPromise.catch(() => void 0);
7951
+ if (this.closed) throw new FerricStoreError("FerricStore client is closed");
7952
+ if (this.reconnectPromise != null) return await this.reconnectPromise;
7953
+ if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
7954
+ if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
7955
+ this.reconnectPromise = (async () => {
7956
+ await Promise.resolve(staleExecutor.close?.()).catch(() => void 0);
7957
+ const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
7958
+ if (this.closed) {
7959
+ await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
7960
+ throw new FerricStoreError("FerricStore client is closed");
7961
+ }
7962
+ this.executorPromise = Promise.resolve(nextExecutor);
7963
+ return nextExecutor;
7964
+ })().finally(() => {
7965
+ this.reconnectPromise = void 0;
7966
+ });
7967
+ return await this.reconnectPromise;
7968
+ }
7494
7969
  };
7495
- function normalizedHostSet(hosts) {
7496
- return new Set(
7497
- hosts.filter((host) => host != null && host !== "").map((host) => normalizedEndpointHost(host))
7498
- );
7499
- }
7500
- function bufferStartsWith(value, prefix) {
7501
- return value.byteLength >= prefix.byteLength && value.subarray(0, prefix.byteLength).equals(prefix);
7502
- }
7503
- var OPEN_BRACE = "{".charCodeAt(0);
7504
- var CLOSE_BRACE = "}".charCodeAt(0);
7505
- function hashTagOrKey(key) {
7506
- const start = key.indexOf(OPEN_BRACE);
7507
- if (start < 0) return key;
7508
- const end = key.indexOf(CLOSE_BRACE, start + 1);
7509
- return end > start + 1 ? key.subarray(start + 1, end) : key;
7970
+ function isReconnectableClosedConnectionError(error) {
7971
+ return error instanceof ConnectionClosedError && error.requestDisposition === "unsent";
7510
7972
  }
7511
- function flowHashTag(rest, fallbackKey) {
7512
- const end = rest.indexOf(CLOSE_BRACE);
7513
- return end > 0 ? rest.subarray(0, end) : hashTagOrKey(fallbackKey);
7973
+ function isClosedConnectionError(error) {
7974
+ return error instanceof ConnectionClosedError;
7514
7975
  }
7515
- var ROUTE_SLOT_MASK = 1023;
7516
- var FLOW_KEY_PREFIX = Buffer25.from("f:{");
7517
- var VALUE_REF_FLOW_KEY_PREFIX = Buffer25.from("X:f:{");
7518
- function routingSlotForKey(key) {
7519
- const keyBytes = Buffer25.isBuffer(key) ? key : Buffer25.from(key);
7520
- let hashInput;
7521
- if (bufferStartsWith(keyBytes, FLOW_KEY_PREFIX)) {
7522
- hashInput = flowHashTag(keyBytes.subarray(FLOW_KEY_PREFIX.byteLength), keyBytes);
7523
- } else if (bufferStartsWith(keyBytes, VALUE_REF_FLOW_KEY_PREFIX)) {
7524
- hashInput = flowHashTag(keyBytes.subarray(VALUE_REF_FLOW_KEY_PREFIX.byteLength), keyBytes);
7525
- } else {
7526
- hashInput = hashTagOrKey(keyBytes);
7976
+ function isReadOnlyReconnectableClosedConnectionError(error) {
7977
+ const pending = [error];
7978
+ const seen = /* @__PURE__ */ new Set();
7979
+ for (let visited = 0; visited < 16 && pending.length > 0; visited += 1) {
7980
+ const current = pending.pop();
7981
+ if (isReconnectableClosedConnectionError(current)) return true;
7982
+ if (current == null || typeof current !== "object" && typeof current !== "function" || seen.has(current)) continue;
7983
+ seen.add(current);
7984
+ if (current instanceof Error && current.cause != null) pending.push(current.cause);
7985
+ if (current instanceof FerricStoreError && current.raw != null) pending.push(current.raw);
7527
7986
  }
7528
- return crc32(hashInput) & ROUTE_SLOT_MASK;
7987
+ return false;
7988
+ }
7989
+ function normalizeNonNegativeInteger2(value, fallback) {
7990
+ return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
7529
7991
  }
7530
7992
 
7993
+ // src/topology.ts
7994
+ import "buffer";
7995
+
7531
7996
  // src/topology-routing.ts
7997
+ import { Buffer as Buffer30 } from "buffer";
7998
+ import { createHash } from "crypto";
7532
7999
  var FLOW_PARTITION_ROUTE_CACHE_MAX = 1024;
7533
8000
  var flowPartitionRouteCache = /* @__PURE__ */ new Map();
7534
8001
  function routingKeyFromProtocolPayload(name, command) {
@@ -7536,9 +8003,18 @@ function routingKeyFromProtocolPayload(name, command) {
7536
8003
  return void 0;
7537
8004
  }
7538
8005
  if (isPlainObject3(command.payload)) {
7539
- for (const field2 of ["key", "partition_key", "id", "owner_flow_id", "parent_id", "root_id", "correlation_id", "scope"]) {
8006
+ for (const field2 of [
8007
+ "key",
8008
+ "partition_key",
8009
+ "id",
8010
+ "owner_flow_id",
8011
+ "parent_flow_id",
8012
+ "root_flow_id",
8013
+ "correlation_id",
8014
+ "scope"
8015
+ ]) {
7540
8016
  const value = getField(command.payload, field2);
7541
- if (typeof value === "string" || Buffer26.isBuffer(value)) {
8017
+ if (typeof value === "string" || Buffer30.isBuffer(value)) {
7542
8018
  return value;
7543
8019
  }
7544
8020
  }
@@ -7585,9 +8061,9 @@ function flowRoutingData(name, args) {
7585
8061
  if (typeof partition === "string" && partition.toUpperCase() !== "AUTO" && partition.toUpperCase() !== "MIXED") {
7586
8062
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
7587
8063
  }
7588
- if (Buffer26.isBuffer(partition)) {
7589
- const text2 = partition.toString("utf8").toUpperCase();
7590
- if (text2 !== "AUTO" && text2 !== "MIXED") {
8064
+ if (Buffer30.isBuffer(partition)) {
8065
+ const text3 = partition.toString("utf8").toUpperCase();
8066
+ if (text3 !== "AUTO" && text3 !== "MIXED") {
7591
8067
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
7592
8068
  }
7593
8069
  }
@@ -7676,22 +8152,22 @@ function flowPartitionRoutingKeyFromCommand(command, claim) {
7676
8152
  return { handled: false };
7677
8153
  }
7678
8154
  function isRoutingKey(value) {
7679
- return typeof value === "string" || Buffer26.isBuffer(value);
8155
+ return typeof value === "string" || Buffer30.isBuffer(value);
7680
8156
  }
7681
8157
  function flowLogicalPartitionRoutingKey(value) {
7682
- if (typeof value !== "string" && !Buffer26.isBuffer(value)) {
8158
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) {
7683
8159
  return void 0;
7684
8160
  }
7685
- const bytes2 = Buffer26.isBuffer(value) ? value : Buffer26.from(value);
7686
- const text2 = bytes2.toString("utf8");
7687
- const autoMatch = /^__flow_auto__:(0|[1-9]\d{0,2})$/u.exec(text2);
8161
+ const bytes2 = Buffer30.isBuffer(value) ? value : Buffer30.from(value);
8162
+ const text3 = bytes2.toString("utf8");
8163
+ const autoMatch = /^__flow_auto__:(0|[1-9]\d{0,2})$/u.exec(text3);
7688
8164
  if (autoMatch != null) {
7689
8165
  const bucket = Number(autoMatch[1]);
7690
8166
  if (bucket < 256) {
7691
- return `f:{fa:${bucket}}:route`;
8167
+ return `{fa:${bucket}}`;
7692
8168
  }
7693
8169
  }
7694
- const cacheKey = `${Buffer26.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
8170
+ const cacheKey = `${Buffer30.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
7695
8171
  const cached = flowPartitionRouteCache.get(cacheKey);
7696
8172
  if (cached != null) {
7697
8173
  flowPartitionRouteCache.delete(cacheKey);
@@ -7699,7 +8175,7 @@ function flowLogicalPartitionRoutingKey(value) {
7699
8175
  return cached;
7700
8176
  }
7701
8177
  const digest = createHash("sha256").update(bytes2).digest("base64url");
7702
- const routeKey = `f:{f:${digest}}:route`;
8178
+ const routeKey = `{f:${digest}}`;
7703
8179
  if (flowPartitionRouteCache.size >= FLOW_PARTITION_ROUTE_CACHE_MAX) {
7704
8180
  const oldest = flowPartitionRouteCache.keys().next().value;
7705
8181
  if (oldest != null) flowPartitionRouteCache.delete(oldest);
@@ -7708,18 +8184,17 @@ function flowLogicalPartitionRoutingKey(value) {
7708
8184
  return routeKey;
7709
8185
  }
7710
8186
  function flowAutoIdRoutingKey(value) {
7711
- if (typeof value !== "string" && !Buffer26.isBuffer(value)) {
8187
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) {
7712
8188
  return void 0;
7713
8189
  }
7714
- const bytes2 = Buffer26.isBuffer(value) ? value : Buffer26.from(value);
7715
- const bucket = crc32(bytes2) & 255;
7716
- return `f:{fa:${bucket}}:route`;
8190
+ const bucket = (Buffer30.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
8191
+ return `{fa:${bucket}}`;
7717
8192
  }
7718
8193
  function flowClaimLogicalPartitionRoutingKey(value) {
7719
- if (typeof value !== "string" && !Buffer26.isBuffer(value)) return void 0;
8194
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) return void 0;
7720
8195
  const selector = commandPart(value);
7721
8196
  if (selector === "AUTO" || selector === "ANY") return void 0;
7722
- if (selector === "GLOBAL") return "f:{f}:route";
8197
+ if (selector === "GLOBAL") return "{f}";
7723
8198
  return flowLogicalPartitionRoutingKey(value);
7724
8199
  }
7725
8200
  function singleShardFlowPartitionKey(values) {
@@ -7731,7 +8206,7 @@ function singleShardFlowClaimPartitionKey(values) {
7731
8206
  return keys.some((key) => key == null) ? void 0 : singleShardKey(keys);
7732
8207
  }
7733
8208
  function singleShardKey(keys) {
7734
- if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !Buffer26.isBuffer(key))) {
8209
+ if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !Buffer30.isBuffer(key))) {
7735
8210
  return void 0;
7736
8211
  }
7737
8212
  const usable = keys;
@@ -7742,7 +8217,7 @@ function singleShardKey(keys) {
7742
8217
  const firstSlot = routingSlotForKey(first);
7743
8218
  return usable.every((key) => routingSlotForKey(key) === firstSlot) ? first : void 0;
7744
8219
  }
7745
- function commandName2(args) {
8220
+ function commandName3(args) {
7746
8221
  return commandView(args).name;
7747
8222
  }
7748
8223
  var commandPart = commandToken;
@@ -7764,7 +8239,7 @@ function routedKeyGroups(keys, routeKey) {
7764
8239
  const groups = /* @__PURE__ */ new Map();
7765
8240
  for (let index = 0; index < keys.length; index += 1) {
7766
8241
  const key = keys[index];
7767
- if (typeof key !== "string" && !Buffer26.isBuffer(key)) return void 0;
8242
+ if (typeof key !== "string" && !Buffer30.isBuffer(key)) return void 0;
7768
8243
  const route = routeKey(key);
7769
8244
  const groupKey = `${route.endpointKey}\0${route.laneId}`;
7770
8245
  const group = groups.get(groupKey);
@@ -8005,7 +8480,16 @@ function isExplicitlySafeReroute(error) {
8005
8480
  if (error instanceof ConnectionClosedError) {
8006
8481
  return error.requestDisposition === "unsent";
8007
8482
  }
8008
- return error instanceof RerouteError && getField(error.raw, "safe_to_retry") === true;
8483
+ return error instanceof RerouteError && error.retryable === true && error.safeToRetry === true;
8484
+ }
8485
+ function retryAfterMs(error) {
8486
+ return error instanceof FerricStoreError ? error.retryAfterMs ?? 0 : 0;
8487
+ }
8488
+ async function waitForExplicitlySafeReroute(error) {
8489
+ if (!isExplicitlySafeReroute(error)) return false;
8490
+ const delayMs = retryAfterMs(error);
8491
+ if (delayMs > 0) await sleep(delayMs);
8492
+ return true;
8009
8493
  }
8010
8494
 
8011
8495
  // src/topology-endpoint-trust.ts
@@ -8147,7 +8631,7 @@ var TopologyPipelineExecutor = class {
8147
8631
  commands.forEach((command, index) => {
8148
8632
  const routed = this.context.routeData(command);
8149
8633
  if (routed == null) {
8150
- if (decomposableCommandNames.has(commandName2(command) ?? "")) standalone.push({ command, index });
8634
+ if (decomposableCommandNames.has(commandName3(command) ?? "")) standalone.push({ command, index });
8151
8635
  else {
8152
8636
  controlCommands.push(command);
8153
8637
  controlIndices.push(index);
@@ -8363,7 +8847,7 @@ function topologyRouteIsCurrent(topology, route) {
8363
8847
  // src/topology-route-planner.ts
8364
8848
  function topologyRouteData(args, route) {
8365
8849
  if (args.length === 0) return void 0;
8366
- const name = commandName2(args);
8850
+ const name = commandName3(args);
8367
8851
  if (name?.startsWith("FLOW.") === true) {
8368
8852
  const routed = flowRoutingData(name, args);
8369
8853
  return routed == null ? void 0 : {
@@ -8392,7 +8876,7 @@ var TopologyScatterExecutor = class {
8392
8876
  }
8393
8877
  dependencies;
8394
8878
  async execute(args) {
8395
- const name = commandName2(args);
8879
+ const name = commandName3(args);
8396
8880
  if (name === "MGET") {
8397
8881
  const value = await this.scatterArray(name, args.slice(1));
8398
8882
  return value == null ? { handled: false } : { handled: true, value };
@@ -8474,6 +8958,18 @@ var TopologyScatterExecutor = class {
8474
8958
  }
8475
8959
  };
8476
8960
 
8961
+ // src/topology-reroute-retry.ts
8962
+ async function canRetrySafeReroute(error, attempt, retryAllowed, refresh, assertOpen) {
8963
+ if (attempt !== 0 || !isRetryableRouteError(error)) return false;
8964
+ const refreshed = await refresh().then(
8965
+ () => true,
8966
+ () => false
8967
+ );
8968
+ if (!refreshed || !retryAllowed || !await waitForExplicitlySafeReroute(error)) return false;
8969
+ assertOpen();
8970
+ return true;
8971
+ }
8972
+
8477
8973
  // src/topology.ts
8478
8974
  var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8479
8975
  adapterOptions;
@@ -8659,6 +9155,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8659
9155
  this.assertOpen();
8660
9156
  const snapshot = snapshotCommandArguments(args);
8661
9157
  assertCommandHasStableConnectionState(snapshot);
9158
+ const casMutation = isCasMutation(snapshot);
8662
9159
  let rerouteAttempt = 0;
8663
9160
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8664
9161
  const routed = this.routeData(snapshot);
@@ -8674,7 +9171,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8674
9171
  try {
8675
9172
  return routed.command == null ? await adapter.executeCommandOnLane(snapshot, routed.route.laneId) : await adapter.executeProtocolCommand(routed.command, routed.route.laneId);
8676
9173
  } catch (error) {
8677
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9174
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !casMutation)) {
8678
9175
  rerouteAttempt += 1;
8679
9176
  continue;
8680
9177
  }
@@ -8716,6 +9213,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8716
9213
  assertCommandHasStableConnectionState(command);
8717
9214
  }
8718
9215
  let rerouteAttempt = 0;
9216
+ const containsCasMutation = snapshot.some(isCasMutation);
8719
9217
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8720
9218
  const route = singleRouteForCommands(snapshot, (command) => this.routeData(command));
8721
9219
  if (route == null) return void 0;
@@ -8724,7 +9222,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8724
9222
  try {
8725
9223
  return await adapter.executeFusedPipelineOnLane(snapshot, route.laneId, snapshotOptions);
8726
9224
  } catch (error) {
8727
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9225
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !containsCasMutation)) {
8728
9226
  rerouteAttempt += 1;
8729
9227
  continue;
8730
9228
  }
@@ -8733,13 +9231,14 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8733
9231
  }
8734
9232
  throw new StaleTopologyRouteError();
8735
9233
  }
8736
- async refreshAndCanRetrySafeReroute(error, attempt) {
8737
- if (attempt !== 0 || !isRetryableRouteError(error)) return false;
8738
- const refreshed = await this.refreshTopology().then(
8739
- () => true,
8740
- () => false
9234
+ async refreshAndCanRetrySafeReroute(error, attempt, retryAllowed = true) {
9235
+ return await canRetrySafeReroute(
9236
+ error,
9237
+ attempt,
9238
+ retryAllowed,
9239
+ async () => await this.refreshTopology(),
9240
+ () => this.assertOpen()
8741
9241
  );
8742
- return refreshed && isExplicitlySafeReroute(error);
8743
9242
  }
8744
9243
  async executePipelineOnRoute(commands, route, options) {
8745
9244
  const adapter = await this.adapterForCurrentRoute(route);
@@ -8829,7 +9328,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8829
9328
  };
8830
9329
 
8831
9330
  // src/response-map-preservation.ts
8832
- import { Buffer as Buffer28 } from "buffer";
9331
+ import { Buffer as Buffer32 } from "buffer";
8833
9332
  function toStringKeyMapPreservingValues(value) {
8834
9333
  if (value == null) return void 0;
8835
9334
  const result = {};
@@ -8839,7 +9338,7 @@ function toStringKeyMapPreservingValues(value) {
8839
9338
  }
8840
9339
  return result;
8841
9340
  }
8842
- if (typeof value === "object" && !Array.isArray(value) && !Buffer28.isBuffer(value) && !(value instanceof Uint8Array)) {
9341
+ if (typeof value === "object" && !Array.isArray(value) && !Buffer32.isBuffer(value) && !(value instanceof Uint8Array)) {
8843
9342
  for (const [key, item] of Object.entries(value)) {
8844
9343
  setOwnValue(result, text(key), normalizeMapStructurePreservingBytes(item));
8845
9344
  }
@@ -8848,7 +9347,7 @@ function toStringKeyMapPreservingValues(value) {
8848
9347
  return void 0;
8849
9348
  }
8850
9349
  function normalizeMapStructurePreservingBytes(value) {
8851
- if (Buffer28.isBuffer(value) || value instanceof Uint8Array) return value;
9350
+ if (Buffer32.isBuffer(value) || value instanceof Uint8Array) return value;
8852
9351
  if (value instanceof Map) {
8853
9352
  const result = {};
8854
9353
  for (const [key, item] of value.entries()) {
@@ -8870,7 +9369,7 @@ function normalizeMapStructurePreservingBytes(value) {
8870
9369
  }
8871
9370
 
8872
9371
  // src/native-kv-responses.ts
8873
- import { Buffer as Buffer29 } from "buffer";
9372
+ import { Buffer as Buffer33 } from "buffer";
8874
9373
  function rateLimitResultFromResp(value) {
8875
9374
  if (!Array.isArray(value) || value.length !== 4) {
8876
9375
  throw new TypeError("RATELIMIT.ADD returned an unexpected response");
@@ -8913,30 +9412,27 @@ function fetchOrComputeResultFromResp(value, codec) {
8913
9412
  shouldCompute: false
8914
9413
  };
8915
9414
  }
8916
- if (status !== "compute") throw new TypeError("FETCH_OR_COMPUTE returned an unexpected response");
9415
+ if (status !== "compute" || value.length !== 3) {
9416
+ throw new TypeError("FETCH_OR_COMPUTE returned an unexpected response");
9417
+ }
8917
9418
  return {
8918
9419
  status,
8919
9420
  computeHint: responseBytes(value[1], "FETCH_OR_COMPUTE"),
8920
- ...value.length === 3 ? {
8921
- computeMode: "fenced",
8922
- computeToken: responseBytes(value[2], "FETCH_OR_COMPUTE")
8923
- } : {
8924
- computeMode: "legacy",
8925
- computeToken: null
8926
- },
9421
+ computeMode: "fenced",
9422
+ computeToken: responseBytes(value[2], "FETCH_OR_COMPUTE"),
8927
9423
  hit: false,
8928
9424
  shouldCompute: true
8929
9425
  };
8930
9426
  }
8931
9427
  function decodePayload(codec, value) {
8932
9428
  if (value == null) return null;
8933
- if (Buffer29.isBuffer(value)) return codec.decode(value);
8934
- if (value instanceof Uint8Array) return codec.decode(Buffer29.from(value));
8935
- if (typeof value === "string") return codec.decode(Buffer29.from(value));
9429
+ if (Buffer33.isBuffer(value)) return codec.decode(value);
9430
+ if (value instanceof Uint8Array) return codec.decode(Buffer33.from(value));
9431
+ if (typeof value === "string") return codec.decode(Buffer33.from(value));
8936
9432
  return normalizeRefMeta(value);
8937
9433
  }
8938
9434
  function requiredResponseString(value, context) {
8939
- if (typeof value !== "string" && !Buffer29.isBuffer(value) && !(value instanceof Uint8Array)) {
9435
+ if (typeof value !== "string" && !Buffer33.isBuffer(value) && !(value instanceof Uint8Array)) {
8940
9436
  throw new TypeError(`${context} returned an unexpected response`);
8941
9437
  }
8942
9438
  const result = text(value);
@@ -8953,7 +9449,7 @@ function requiredNonNegativeInteger(value, context) {
8953
9449
  return result;
8954
9450
  }
8955
9451
  function responseBytes(value, context) {
8956
- if (typeof value !== "string" && !Buffer29.isBuffer(value) && !(value instanceof Uint8Array)) {
9452
+ if (typeof value !== "string" && !Buffer33.isBuffer(value) && !(value instanceof Uint8Array)) {
8957
9453
  throw new TypeError(`${context} returned an unexpected response`);
8958
9454
  }
8959
9455
  return bytes(value);
@@ -9038,6 +9534,9 @@ function flowRecordFromResp(value, codec) {
9038
9534
  field(value, "max_active_ms"),
9039
9535
  "FLOW record max_active_ms"
9040
9536
  );
9537
+ const rawError = field(value, "error");
9538
+ const error = flowError(rawError, codec);
9539
+ const failureReason = optionalResponseString(field(rawError, "reason"), "FLOW record error reason");
9041
9540
  return {
9042
9541
  id: requiredTextField(value, "id", "FLOW record"),
9043
9542
  type: requiredTextField(value, "type", "FLOW record"),
@@ -9052,6 +9551,8 @@ function flowRecordFromResp(value, codec) {
9052
9551
  rootFlowId: optionalResponseString(field(value, "root_flow_id"), "FLOW record root_flow_id"),
9053
9552
  correlationId: optionalResponseString(field(value, "correlation_id"), "FLOW record correlation_id"),
9054
9553
  ...maxActiveMs == null ? {} : { maxActiveMs },
9554
+ ...error == null ? {} : { error },
9555
+ ...failureReason == null ? {} : { failureReason },
9055
9556
  valueRefs: optionalMapField(value, "value_refs", "FLOW record"),
9056
9557
  values,
9057
9558
  valueSizes: optionalMapField(value, "value_sizes", "FLOW record"),
@@ -9063,6 +9564,21 @@ function flowRecordFromResp(value, codec) {
9063
9564
  raw: value
9064
9565
  };
9065
9566
  }
9567
+ function flowError(value, codec) {
9568
+ if (value == null) return void 0;
9569
+ const reason = optionalResponseString(field(value, "reason"), "FLOW record error reason");
9570
+ if (reason === "max_active_ms") {
9571
+ const maxActiveMs = optionalPositiveResponseInteger(
9572
+ field(value, "max_active_ms"),
9573
+ "FLOW record error max_active_ms"
9574
+ );
9575
+ if (maxActiveMs == null) {
9576
+ throw new TypeError("FLOW record error max_active_ms returned an unexpected response");
9577
+ }
9578
+ return { maxActiveMs, reason };
9579
+ }
9580
+ return decodePayload2(codec, value);
9581
+ }
9066
9582
  function decodePayload2(codec, value) {
9067
9583
  if (value == null) {
9068
9584
  return null;
@@ -9643,7 +10159,7 @@ function groupAutoPartitionItems(items) {
9643
10159
  }
9644
10160
 
9645
10161
  // src/client-values.ts
9646
- import { Buffer as Buffer30 } from "buffer";
10162
+ import { Buffer as Buffer34 } from "buffer";
9647
10163
  async function valueMGetEntries(client, refs, options = {}) {
9648
10164
  const refCount = refs.length;
9649
10165
  if (refCount === 0) {
@@ -9678,12 +10194,12 @@ async function valueMGetEntries(client, refs, options = {}) {
9678
10194
  const item = response[index];
9679
10195
  if (item == null) {
9680
10196
  entries[index] = { found: false };
9681
- } else if (Buffer30.isBuffer(item)) {
10197
+ } else if (Buffer34.isBuffer(item)) {
9682
10198
  entries[index] = { found: true, value: client.codec.decode(item) };
9683
10199
  } else if (item instanceof Uint8Array) {
9684
- entries[index] = { found: true, value: client.codec.decode(Buffer30.from(item)) };
10200
+ entries[index] = { found: true, value: client.codec.decode(Buffer34.from(item)) };
9685
10201
  } else if (typeof item === "string") {
9686
- entries[index] = { found: true, value: client.codec.decode(Buffer30.from(item)) };
10202
+ entries[index] = { found: true, value: client.codec.decode(Buffer34.from(item)) };
9687
10203
  } else {
9688
10204
  entries[index] = { found: true, value: item };
9689
10205
  }
@@ -9695,7 +10211,7 @@ async function valueMGetEntries(client, refs, options = {}) {
9695
10211
  import "buffer";
9696
10212
 
9697
10213
  // src/auto-batch-ordering.ts
9698
- import { Buffer as Buffer31 } from "buffer";
10214
+ import { Buffer as Buffer35 } from "buffer";
9699
10215
  function autoBatchOrderingPlan(batch) {
9700
10216
  const accesses = /* @__PURE__ */ new Map();
9701
10217
  const fallbackDependencies = [];
@@ -9810,13 +10326,13 @@ function flowManyAutoBatchIds(command, name) {
9810
10326
  return fixedFlowItemIds(
9811
10327
  command,
9812
10328
  mixed ? 4 : 3,
9813
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && Buffer31.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
10329
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && Buffer35.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
9814
10330
  );
9815
10331
  }
9816
10332
  return fixedFlowItemIds(
9817
10333
  command,
9818
10334
  mixed ? 4 : 3,
9819
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer31.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
10335
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer35.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
9820
10336
  );
9821
10337
  }
9822
10338
  function createManyAutoBatchIds(command) {
@@ -9829,7 +10345,7 @@ function createManyAutoBatchIds(command) {
9829
10345
  const ids = fixedFlowItemIds(
9830
10346
  command,
9831
10347
  width,
9832
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer31.isBuffer(command[itemIndex + width - 1]),
10348
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer35.isBuffer(command[itemIndex + width - 1]),
9833
10349
  markerIndex
9834
10350
  );
9835
10351
  if (ids != null) return ids;
@@ -9847,7 +10363,7 @@ function extendedCreateManyAutoBatchIds(command, markerIndex) {
9847
10363
  let cursor = markerIndex + 2;
9848
10364
  for (let itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
9849
10365
  const id = command[cursor];
9850
- if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !Buffer31.isBuffer(command[cursor + 2])) return void 0;
10366
+ if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !Buffer35.isBuffer(command[cursor + 2])) return void 0;
9851
10367
  ids.push(id);
9852
10368
  cursor += 3;
9853
10369
  const afterValues = skipExtendedNamedItems(command, cursor, true);
@@ -9866,7 +10382,7 @@ function skipExtendedNamedItems(command, countIndex, encodedValues) {
9866
10382
  for (let index = 0; index < count; index += 1) {
9867
10383
  const name = command[firstItem + index * 2];
9868
10384
  const value = command[firstItem + index * 2 + 1];
9869
- if (!isAutoBatchResourceValue(name) || (encodedValues ? !Buffer31.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
10385
+ if (!isAutoBatchResourceValue(name) || (encodedValues ? !Buffer35.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
9870
10386
  }
9871
10387
  return firstItem + count * 2;
9872
10388
  }
@@ -9881,7 +10397,7 @@ function runStepsManyAutoBatchIds(command) {
9881
10397
  ids.push(item);
9882
10398
  continue;
9883
10399
  }
9884
- if (typeof item !== "object" || item == null || Array.isArray(item) || Buffer31.isBuffer(item)) return void 0;
10400
+ if (typeof item !== "object" || item == null || Array.isArray(item) || Buffer35.isBuffer(item)) return void 0;
9885
10401
  const id = item.id;
9886
10402
  if (!isAutoBatchResourceValue(id)) return void 0;
9887
10403
  ids.push(id);
@@ -9922,7 +10438,7 @@ function flowValuePutOwner(command, start) {
9922
10438
  }
9923
10439
  var flowValuePutOptionTokens = /* @__PURE__ */ new Set(["NAME", "NOW", "OVERRIDE", "OWNER_FLOW_ID", "PARTITION", "TTL", "TTL_MS"]);
9924
10440
  function isAutoBatchResourceValue(value) {
9925
- return typeof value === "string" || Buffer31.isBuffer(value);
10441
+ return typeof value === "string" || Buffer35.isBuffer(value);
9926
10442
  }
9927
10443
  function isFencingToken(value) {
9928
10444
  return typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint";
@@ -9931,7 +10447,7 @@ function nonNegativeItemCount(value) {
9931
10447
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
9932
10448
  }
9933
10449
  function autoBatchResourceKey(namespace, value) {
9934
- return `${namespace}:${Buffer31.from(value).toString("base64")}`;
10450
+ return `${namespace}:${Buffer35.from(value).toString("base64")}`;
9935
10451
  }
9936
10452
  function autoBatchCommandName(command) {
9937
10453
  return commandView(command).name ?? null;
@@ -10346,9 +10862,9 @@ import "buffer";
10346
10862
  import "buffer";
10347
10863
 
10348
10864
  // src/client-core-helpers.ts
10349
- import { Buffer as Buffer33 } from "buffer";
10865
+ import { Buffer as Buffer37 } from "buffer";
10350
10866
  function bgsaveResponse(response) {
10351
- if ((typeof response === "string" || Buffer33.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
10867
+ if ((typeof response === "string" || Buffer37.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
10352
10868
  return true;
10353
10869
  }
10354
10870
  return okResponse(response);
@@ -10359,11 +10875,11 @@ function clientNameResponse(response) {
10359
10875
  function fetchOrComputeCompletionToken(options) {
10360
10876
  if (options == null || !Object.hasOwn(options, "computeToken") || options.computeToken === void 0) {
10361
10877
  throw new TypeError(
10362
- "fetch-or-compute completion requires computeToken; pass null only for a legacy tokenless lease"
10878
+ "fetch-or-compute completion requires computeToken"
10363
10879
  );
10364
10880
  }
10365
- if (options.computeToken !== null && !Buffer33.isBuffer(options.computeToken)) {
10366
- throw new TypeError("fetch-or-compute computeToken must be a Buffer or explicit null for a legacy lease");
10881
+ if (!Buffer37.isBuffer(options.computeToken)) {
10882
+ throw new TypeError("fetch-or-compute computeToken must be a Buffer");
10367
10883
  }
10368
10884
  return options.computeToken;
10369
10885
  }
@@ -10485,14 +11001,17 @@ var FerricStoreClientBase = class {
10485
11001
  };
10486
11002
  }
10487
11003
  async command(...args) {
11004
+ assertAtomicKeyValueCommandSharesSlot(args);
10488
11005
  return await executeCommandArgs(this.executor, args);
10489
11006
  }
10490
11007
  /** Execute an already-built command without a JavaScript variadic-call limit. */
10491
11008
  async commandArgs(args) {
11009
+ assertAtomicKeyValueCommandSharesSlot(args);
10492
11010
  return await executeCommandArgs(this.executor, args);
10493
11011
  }
10494
11012
  async pipeline(commands, options) {
10495
11013
  const snapshot = snapshotPipelineCommands(commands);
11014
+ for (const command of snapshot) assertAtomicKeyValueCommandSharesSlot(command);
10496
11015
  const snapshotOptions = snapshotPipelineOptions(options);
10497
11016
  const results = this.executor.executePipeline != null ? await this.executor.executePipeline(snapshot, snapshotOptions) : await executeCommandArraysIndividually(
10498
11017
  async (command) => await this.commandArgs(command),
@@ -10719,14 +11238,14 @@ var FerricStoreAdministrationClient = class extends FerricStoreClientBase {
10719
11238
  };
10720
11239
 
10721
11240
  // src/store-utilities.ts
10722
- import { Buffer as Buffer36 } from "buffer";
11241
+ import { Buffer as Buffer40 } from "buffer";
10723
11242
  function encode(codec, value) {
10724
11243
  return codec.encode(value);
10725
11244
  }
10726
11245
  function decode(codec, value) {
10727
11246
  if (value == null) return null;
10728
- if (Buffer36.isBuffer(value)) return codec.decode(value);
10729
- if (value instanceof Uint8Array) return codec.decode(Buffer36.from(value));
11247
+ if (Buffer40.isBuffer(value)) return codec.decode(value);
11248
+ if (value instanceof Uint8Array) return codec.decode(Buffer40.from(value));
10730
11249
  return value;
10731
11250
  }
10732
11251
  function number(value) {
@@ -10787,7 +11306,7 @@ function requireNonEmpty(values, command, noun = "argument") {
10787
11306
  if (values.length === 0) throw new TypeError(`${command} requires at least one ${noun}`);
10788
11307
  return values;
10789
11308
  }
10790
- function commandName3(args) {
11309
+ function commandName4(args) {
10791
11310
  return typeof args[0] === "string" ? args[0] : "command";
10792
11311
  }
10793
11312
  function commandArgs(client, args) {
@@ -10802,7 +11321,7 @@ function arrayOrRest(values) {
10802
11321
  function appendXReadStreams(args, streams) {
10803
11322
  const offset = args.length;
10804
11323
  const streamCount = streams.length;
10805
- requireNonEmpty(streams, commandName3(args), "stream");
11324
+ requireNonEmpty(streams, commandName4(args), "stream");
10806
11325
  args.length = offset + 1 + streamCount * 2;
10807
11326
  args[offset] = "STREAMS";
10808
11327
  for (let index = 0; index < streamCount; index += 1) {
@@ -10812,7 +11331,7 @@ function appendXReadStreams(args, streams) {
10812
11331
  const stream = streams[index];
10813
11332
  if (stream == null) throw new TypeError("XREAD streams must contain only stream descriptors");
10814
11333
  if (!Object.hasOwn(stream, "key") || !Object.hasOwn(stream, "id")) {
10815
- throw new TypeError(`${commandName3(args)} streams require own key and id fields`);
11334
+ throw new TypeError(`${commandName4(args)} streams require own key and id fields`);
10816
11335
  }
10817
11336
  args[offset + 1 + index] = stream.key;
10818
11337
  args[offset + 1 + streamCount + index] = stream.id;
@@ -10834,7 +11353,7 @@ function concatArgs(...parts) {
10834
11353
  return args;
10835
11354
  }
10836
11355
  function encodedArgs(prefix, codec, values, allowEmpty = false) {
10837
- if (!allowEmpty) requireNonEmpty(values, commandName3(prefix));
11356
+ if (!allowEmpty) requireNonEmpty(values, commandName4(prefix));
10838
11357
  const args = new Array(prefix.length + values.length);
10839
11358
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10840
11359
  for (let index = 0; index < values.length; index += 1) {
@@ -10848,7 +11367,7 @@ function encodedArgs(prefix, codec, values, allowEmpty = false) {
10848
11367
  function keyValueArgs(prefix, codec, entries) {
10849
11368
  const validateTuples = Array.isArray(entries);
10850
11369
  const pairs = validateTuples ? entries : Object.entries(entries);
10851
- requireNonEmpty(pairs, commandName3(prefix), "entry");
11370
+ requireNonEmpty(pairs, commandName4(prefix), "entry");
10852
11371
  const args = new Array(prefix.length + pairs.length * 2);
10853
11372
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10854
11373
  for (let index = 0; index < pairs.length; index += 1) {
@@ -10862,7 +11381,7 @@ function keyValueArgs(prefix, codec, entries) {
10862
11381
  return args;
10863
11382
  }
10864
11383
  function encodedEntryArgs(prefix, codec, entries) {
10865
- requireNonEmpty(entries, commandName3(prefix), "entry");
11384
+ requireNonEmpty(entries, commandName4(prefix), "entry");
10866
11385
  const args = new Array(prefix.length + entries.length * 2);
10867
11386
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
10868
11387
  for (let index = 0; index < entries.length; index += 1) {
@@ -11132,12 +11651,15 @@ var TopKStore = class {
11132
11651
  const width = ownOption(options, "width");
11133
11652
  const depth = ownOption(options, "depth");
11134
11653
  const decay = ownOption(options, "decay");
11135
- const tuningCount = [width, depth, decay].filter((value) => value != null).length;
11136
- if (tuningCount !== 0 && tuningCount !== 3) {
11137
- throw new TypeError("TOPK.RESERVE width, depth, and decay must be provided together");
11654
+ if (decay != null || Object.hasOwn(options, "decay")) {
11655
+ throw new TypeError("TOPK.RESERVE decay is not supported by FerricStore 0.8");
11138
11656
  }
11139
- if (tuningCount === 3) {
11140
- args.push(width, depth, decay);
11657
+ const tuningCount = [width, depth].filter((value) => value != null).length;
11658
+ if (tuningCount !== 0 && tuningCount !== 2) {
11659
+ throw new TypeError("TOPK.RESERVE width and depth must be provided together");
11660
+ }
11661
+ if (tuningCount === 2) {
11662
+ args.push(width, depth);
11141
11663
  }
11142
11664
  return okResponse(await commandArgs(this.client, args));
11143
11665
  }
@@ -11543,10 +12065,14 @@ var KeyValueStore = class {
11543
12065
  );
11544
12066
  }
11545
12067
  async mset(entries) {
11546
- return okResponse(await commandArgs(this.client, keyValueArgs(["MSET"], this.client.codec, entries)));
12068
+ const args = keyValueArgs(["MSET"], this.client.codec, entries);
12069
+ assertKeyValueCommandSharesSlot(args, "MSET");
12070
+ return okResponse(await commandArgs(this.client, args));
11547
12071
  }
11548
12072
  async msetnx(entries) {
11549
- return binaryBooleanResponse(await commandArgs(this.client, keyValueArgs(["MSETNX"], this.client.codec, entries)));
12073
+ const args = keyValueArgs(["MSETNX"], this.client.codec, entries);
12074
+ assertKeyValueCommandSharesSlot(args, "MSETNX");
12075
+ return binaryBooleanResponse(await commandArgs(this.client, args));
11550
12076
  }
11551
12077
  async incr(key) {
11552
12078
  return integerReply(await this.client.command("INCR", key));
@@ -12616,7 +13142,8 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
12616
13142
  return okResponse(await this.command(
12617
13143
  "FETCH_OR_COMPUTE_RESULT",
12618
13144
  key,
12619
- ...computeToken === null ? [encoded] : [computeToken, encoded],
13145
+ computeToken,
13146
+ encoded,
12620
13147
  options.ttlMs
12621
13148
  ));
12622
13149
  }
@@ -12625,7 +13152,7 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
12625
13152
  return okResponse(await this.command(
12626
13153
  "FETCH_OR_COMPUTE_ERROR",
12627
13154
  key,
12628
- ...computeToken === null ? [] : [computeToken],
13155
+ computeToken,
12629
13156
  message
12630
13157
  ));
12631
13158
  }
@@ -12751,7 +13278,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
12751
13278
  }
12752
13279
  async effectGet(id, effectKey, options = {}) {
12753
13280
  const args = ["FLOW.EFFECT.GET", id, "EFFECT_KEY", effectKey];
12754
- append(args, "PARTITION", options.partitionKey ?? autoPartitionKeyForId(id));
13281
+ append(args, "PARTITION", options.partitionKey);
12755
13282
  return optionalAdminRecord(await this.commandArgs(args), "FLOW.EFFECT.GET");
12756
13283
  }
12757
13284
  async effectStatus(command, id, effectKey, options) {
@@ -12923,7 +13450,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
12923
13450
  }
12924
13451
  async governanceLedger(id, options = {}) {
12925
13452
  const args = ["FLOW.GOVERNANCE.LEDGER", id];
12926
- append(args, "PARTITION", options.partitionKey ?? autoPartitionKeyForId(id));
13453
+ append(args, "PARTITION", options.partitionKey);
12927
13454
  append(args, "LIMIT", options.limit);
12928
13455
  append(args, "FROM_MS", options.fromMs);
12929
13456
  append(args, "TO_MS", options.toMs);
@@ -12939,7 +13466,7 @@ async function executeProducerWriteWithBackpressure(execute, policy, signal) {
12939
13466
  try {
12940
13467
  return await execute();
12941
13468
  } catch (error) {
12942
- if (!(error instanceof OverloadedError) || attempt >= policy.maxRetries) throw error;
13469
+ if (!(error instanceof OverloadedError) || error.retryable !== true || error.safeToRetry !== true || attempt >= policy.maxRetries) throw error;
12943
13470
  const exponential = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** attempt);
12944
13471
  const base = Math.min(policy.maxDelayMs, error.retryAfterMs ?? exponential);
12945
13472
  const jitter = base * (policy.jitterPct / 100) * Math.random();
@@ -12953,6 +13480,165 @@ function throwIfClosed(signal) {
12953
13480
  throw new FerricStoreError("FerricStore client is closed", { raw: signal.reason });
12954
13481
  }
12955
13482
 
13483
+ // src/flow-policy.ts
13484
+ import { Buffer as Buffer42 } from "buffer";
13485
+ var MAX_FLOW_POLICY_GENERATION = Number.MAX_SAFE_INTEGER;
13486
+ function assertFlowPolicyGeneration(value) {
13487
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
13488
+ throw new TypeError("expectedGeneration must be a nonnegative safe integer");
13489
+ }
13490
+ }
13491
+ function flowPolicySnapshotFromResp(value) {
13492
+ const policy = requiredRecord(value, "FLOW policy snapshot");
13493
+ const maxActiveMs = optionalPositiveInteger(field(policy, "max_active_ms"), "max_active_ms");
13494
+ const state = optionalText2(field(policy, "state"), "state");
13495
+ const mode = optionalMode(field(policy, "mode"), "mode");
13496
+ const version = optionalVersion(field(policy, "version"));
13497
+ const indexedStateMeta = optionalText2(field(policy, "indexed_state_meta"), "indexed_state_meta");
13498
+ const governance = optionalRecord(field(policy, "governance"), "governance");
13499
+ const states = optionalStates(field(policy, "states"));
13500
+ return {
13501
+ type: requiredText(field(policy, "type"), "type"),
13502
+ generation: nonNegativeInteger(field(policy, "generation"), "generation"),
13503
+ ...state == null ? {} : { state },
13504
+ ...version == null ? {} : { version },
13505
+ ...mode == null ? {} : { mode },
13506
+ ...maxActiveMs == null ? {} : { maxActiveMs },
13507
+ retry: retrySnapshot(field(policy, "retry"), "retry"),
13508
+ retention: retentionSnapshot(field(policy, "retention"), "retention"),
13509
+ indexedAttributes: stringArray(field(policy, "indexed_attributes"), "indexed_attributes"),
13510
+ ...indexedStateMeta == null ? {} : { indexedStateMeta },
13511
+ ...governance == null ? {} : { governance },
13512
+ ...states == null ? {} : { states },
13513
+ raw: value
13514
+ };
13515
+ }
13516
+ function retrySnapshot(value, context) {
13517
+ const retry2 = requiredRecord(value, `FLOW policy ${context}`);
13518
+ const backoff = requiredRecord(field(retry2, "backoff"), `FLOW policy ${context}.backoff`);
13519
+ return {
13520
+ maxRetries: nonNegativeInteger(field(retry2, "max_retries"), `${context}.max_retries`),
13521
+ backoff: {
13522
+ kind: backoffKind(field(backoff, "kind"), `${context}.backoff.kind`),
13523
+ baseMs: nonNegativeInteger(field(backoff, "base_ms"), `${context}.backoff.base_ms`),
13524
+ maxMs: nonNegativeInteger(field(backoff, "max_ms"), `${context}.backoff.max_ms`),
13525
+ jitterPct: nonNegativeInteger(field(backoff, "jitter_pct"), `${context}.backoff.jitter_pct`)
13526
+ },
13527
+ exhaustedTo: requiredText(field(retry2, "exhausted_to"), `${context}.exhausted_to`)
13528
+ };
13529
+ }
13530
+ function retentionSnapshot(value, context) {
13531
+ const retention = requiredRecord(value, `FLOW policy ${context}`);
13532
+ return {
13533
+ ttlMs: nonNegativeInteger(field(retention, "ttl_ms"), `${context}.ttl_ms`),
13534
+ historyMaxEvents: nonNegativeInteger(
13535
+ field(retention, "history_max_events"),
13536
+ `${context}.history_max_events`
13537
+ )
13538
+ };
13539
+ }
13540
+ function optionalStates(value) {
13541
+ if (value == null) return void 0;
13542
+ const source = requiredRecord(value, "FLOW policy states");
13543
+ const result = {};
13544
+ for (const [state, raw] of Object.entries(source)) {
13545
+ const policy = requiredRecord(raw, `FLOW policy state ${state}`);
13546
+ const maxActiveMs = optionalPositiveInteger(
13547
+ field(policy, "max_active_ms"),
13548
+ `states.${state}.max_active_ms`
13549
+ );
13550
+ const governance = optionalRecord(field(policy, "governance"), `states.${state}.governance`);
13551
+ Object.defineProperty(result, state, {
13552
+ configurable: true,
13553
+ enumerable: true,
13554
+ value: {
13555
+ mode: requiredMode(field(policy, "mode"), `states.${state}.mode`),
13556
+ ...maxActiveMs == null ? {} : { maxActiveMs },
13557
+ retry: retrySnapshot(field(policy, "retry"), `states.${state}.retry`),
13558
+ retention: retentionSnapshot(field(policy, "retention"), `states.${state}.retention`),
13559
+ ...governance == null ? {} : { governance }
13560
+ },
13561
+ writable: true
13562
+ });
13563
+ }
13564
+ return result;
13565
+ }
13566
+ function requiredRecord(value, context) {
13567
+ if (value instanceof Map) {
13568
+ const result = {};
13569
+ for (const [key, item] of value.entries()) {
13570
+ setOwnValue(result, requiredText(key, `${context} key`), item);
13571
+ }
13572
+ return result;
13573
+ }
13574
+ if (typeof value === "object" && value != null && !Array.isArray(value) && !Buffer42.isBuffer(value) && !(value instanceof Uint8Array)) return value;
13575
+ throw new TypeError(`${context} returned an unexpected response`);
13576
+ }
13577
+ function optionalRecord(value, context) {
13578
+ return value == null ? void 0 : requiredRecord(value, `FLOW policy ${context}`);
13579
+ }
13580
+ function stringArray(value, context) {
13581
+ if (!Array.isArray(value)) {
13582
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13583
+ }
13584
+ const result = new Array(value.length);
13585
+ for (let index = 0; index < value.length; index += 1) {
13586
+ if (!Object.hasOwn(value, index)) {
13587
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13588
+ }
13589
+ result[index] = requiredText(value[index], `${context}[${index}]`);
13590
+ }
13591
+ return result;
13592
+ }
13593
+ function requiredText(value, context) {
13594
+ if (typeof value === "string") return value;
13595
+ if (Buffer42.isBuffer(value) || value instanceof Uint8Array) {
13596
+ return Buffer42.from(value).toString("utf8");
13597
+ }
13598
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13599
+ }
13600
+ function optionalText2(value, context) {
13601
+ return value == null ? void 0 : requiredText(value, context);
13602
+ }
13603
+ function nonNegativeInteger(value, context) {
13604
+ let parsed;
13605
+ try {
13606
+ parsed = integer(value);
13607
+ } catch {
13608
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13609
+ }
13610
+ if (parsed < 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13611
+ return parsed;
13612
+ }
13613
+ function optionalPositiveInteger(value, context) {
13614
+ if (value == null) return void 0;
13615
+ const parsed = nonNegativeInteger(value, context);
13616
+ if (parsed === 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13617
+ return parsed;
13618
+ }
13619
+ function optionalVersion(value) {
13620
+ if (value == null) return void 0;
13621
+ if (typeof value === "number" || typeof value === "bigint") {
13622
+ return nonNegativeInteger(value, "version");
13623
+ }
13624
+ return requiredText(value, "version");
13625
+ }
13626
+ function backoffKind(value, context) {
13627
+ const kind = requiredText(value, context).toLowerCase();
13628
+ if (kind === "none" || kind === "fixed" || kind === "linear" || kind === "exponential") {
13629
+ return kind;
13630
+ }
13631
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13632
+ }
13633
+ function requiredMode(value, context) {
13634
+ const mode = requiredText(value, context).toLowerCase();
13635
+ if (mode === "fifo" || mode === "parallel") return mode;
13636
+ throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
13637
+ }
13638
+ function optionalMode(value, context) {
13639
+ return value == null ? void 0 : requiredMode(value, context);
13640
+ }
13641
+
12956
13642
  // src/client-flow-support.ts
12957
13643
  var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
12958
13644
  async rewind(id, options = {}) {
@@ -13095,27 +13781,36 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13095
13781
  }
13096
13782
  async installPolicy(type, options = {}) {
13097
13783
  const args = ["FLOW.POLICY.SET", type];
13784
+ const expectedGeneration = Object.hasOwn(options, "expectedGeneration") ? options.expectedGeneration : void 0;
13785
+ const replace = Object.hasOwn(options, "replace") ? options.replace : void 0;
13786
+ if (expectedGeneration != null) assertFlowPolicyGeneration(expectedGeneration);
13787
+ if (replace != null && typeof replace !== "boolean") {
13788
+ throw new TypeError("replace must be a boolean");
13789
+ }
13790
+ append(args, "EXPECTED_GENERATION", expectedGeneration);
13791
+ appendBool(args, "REPLACE", replace);
13098
13792
  if (options.indexedAttributes != null) {
13099
13793
  args.push("INDEXED_ATTRIBUTES", JSON.stringify(options.indexedAttributes));
13100
13794
  }
13101
13795
  append(args, "INDEXED_STATE_META", options.indexedStateMeta);
13102
13796
  append(args, "MAX_ACTIVE_MS", options.maxActiveMs);
13797
+ if (options.state == null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13103
13798
  append(args, "STATE", options.state);
13104
13799
  appendPolicyMode(args, options.state, options.mode);
13105
13800
  if (options.retry != null) {
13106
13801
  appendRetryPolicy(args, options.retry);
13107
13802
  }
13803
+ if (options.state != null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13108
13804
  for (const [state, policy] of Object.entries(options.states ?? {})) {
13109
13805
  args.push("STATE", state);
13110
13806
  appendFlowStatePolicy(args, policy);
13111
13807
  }
13112
- append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13113
- return await this.commandArgs(args);
13808
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13114
13809
  }
13115
13810
  async policyGet(type, options = {}) {
13116
13811
  const args = ["FLOW.POLICY.GET", type];
13117
13812
  append(args, "STATE", options.state);
13118
- return parseKvResponse(await this.commandArgs(args));
13813
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13119
13814
  }
13120
13815
  async retentionCleanup() {
13121
13816
  return parseKvResponse(await this.command("FLOW.RETENTION_CLEANUP"));
@@ -13204,7 +13899,7 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13204
13899
  };
13205
13900
 
13206
13901
  // src/flow-many-snapshot.ts
13207
- import { Buffer as Buffer39 } from "buffer";
13902
+ import { Buffer as Buffer44 } from "buffer";
13208
13903
  var encodedOptionFields = ["error", "payload", "reason", "result"];
13209
13904
  function snapshotCreateItem(item, codec) {
13210
13905
  const snapshot = {
@@ -13244,7 +13939,7 @@ function snapshotFlowManyOptions(options, codec) {
13244
13939
  }
13245
13940
  function snapshotClaimedItem(item) {
13246
13941
  const wire = item[CLAIMED_ITEM_WIRE];
13247
- const leaseToken = Buffer39.from(wire?.leaseToken ?? item.leaseToken);
13942
+ const leaseToken = Buffer44.from(wire?.leaseToken ?? item.leaseToken);
13248
13943
  const snapshot = {
13249
13944
  ...item,
13250
13945
  fencingToken: wire?.fencingToken ?? item.fencingToken,
@@ -13261,15 +13956,15 @@ function snapshotClaimedItem(item) {
13261
13956
  function snapshotFencedItem(item) {
13262
13957
  return Object.freeze({
13263
13958
  ...item,
13264
- ...item.leaseToken == null ? {} : { leaseToken: Buffer39.from(item.leaseToken) }
13959
+ ...item.leaseToken == null ? {} : { leaseToken: Buffer44.from(item.leaseToken) }
13265
13960
  });
13266
13961
  }
13267
13962
  function snapshotClaimedItemWire(wire, leaseToken) {
13268
13963
  return Object.freeze({
13269
13964
  fencingToken: wire.fencingToken,
13270
- id: Buffer39.from(wire.id),
13965
+ id: Buffer44.from(wire.id),
13271
13966
  leaseToken,
13272
- partitionKey: wire.partitionKey == null ? wire.partitionKey : Buffer39.from(wire.partitionKey)
13967
+ partitionKey: wire.partitionKey == null ? wire.partitionKey : Buffer44.from(wire.partitionKey)
13273
13968
  });
13274
13969
  }
13275
13970
  function snapshotArray(values) {
@@ -13280,7 +13975,7 @@ function snapshotArray(values) {
13280
13975
  return Object.freeze(snapshot);
13281
13976
  }
13282
13977
  function snapshotStateMeta(stateMeta) {
13283
- return snapshotRecord(stateMeta, (value) => Buffer39.isBuffer(value) ? Buffer39.from(value) : value);
13978
+ return snapshotRecord(stateMeta, (value) => Buffer44.isBuffer(value) ? Buffer44.from(value) : value);
13284
13979
  }
13285
13980
  function snapshotCommandRecord(values) {
13286
13981
  const seen = /* @__PURE__ */ new WeakMap();
@@ -13298,7 +13993,7 @@ function snapshotRecord(values, capture) {
13298
13993
  }
13299
13994
  function snapshotCommandArgument(value, seen) {
13300
13995
  if (typeof value !== "object" || value == null) return value;
13301
- if (Buffer39.isBuffer(value) || value instanceof Uint8Array) return Buffer39.from(value);
13996
+ if (Buffer44.isBuffer(value) || value instanceof Uint8Array) return Buffer44.from(value);
13302
13997
  const objectValue = value;
13303
13998
  const existing = seen.get(objectValue);
13304
13999
  if (existing != null) return existing;
@@ -14096,6 +14791,9 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14096
14791
  return new _FerricStoreClient(executor, clientOptions);
14097
14792
  }
14098
14793
  async valuePut(value, options = {}) {
14794
+ if (options.ownerFlowId != null && options.name != null && options.ttlMs != null) {
14795
+ throw new TypeError("named Flow values cannot have a TTL");
14796
+ }
14099
14797
  const args = ["FLOW.VALUE.PUT", this.codec.encode(value), "NOW", options.nowMs ?? nowMs()];
14100
14798
  append(args, "PARTITION", options.partitionKey);
14101
14799
  append(args, "OWNER_FLOW_ID", options.ownerFlowId);
@@ -14127,7 +14825,6 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14127
14825
  append(args, "TRANSITION_TO", options.transitionTo);
14128
14826
  append(args, "RUN_AT", options.runAtMs);
14129
14827
  append(args, "NOW", options.nowMs ?? nowMs());
14130
- append(args, "PRIORITY", options.priority);
14131
14828
  appendNamedValues(args, this.codec, options);
14132
14829
  return await this.commandArgs(args);
14133
14830
  }
@@ -14157,6 +14854,7 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
14157
14854
  append(args, "CORRELATION_ID", options.correlationId);
14158
14855
  append(args, "PRIORITY", options.priority);
14159
14856
  append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
14857
+ append(args, "MAX_ACTIVE_MS", options.maxActiveMs);
14160
14858
  appendAttributes(args, options.attributes);
14161
14859
  appendStateMeta(args, options.stateMeta);
14162
14860
  appendNamedValues(args, this.codec, options);
@@ -14517,7 +15215,7 @@ async function runContinuousWorkerPool(options) {
14517
15215
  initialIdleSleepMs,
14518
15216
  nonNegativeDuration(options.maxIdleSleepMs, 5e3)
14519
15217
  );
14520
- const refillDelayMs = nonNegativeInteger(options.refillDelayMs, 0);
15218
+ const refillDelayMs = nonNegativeInteger2(options.refillDelayMs, 0);
14521
15219
  const active = /* @__PURE__ */ new Set();
14522
15220
  const pending = [];
14523
15221
  let pendingIndex = 0;
@@ -14636,7 +15334,7 @@ function normalizeWorkflowWorkerConfig(options) {
14636
15334
  function positiveInteger(value, fallback) {
14637
15335
  return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
14638
15336
  }
14639
- function nonNegativeInteger(value, fallback) {
15337
+ function nonNegativeInteger2(value, fallback) {
14640
15338
  return value == null || !Number.isFinite(value) ? fallback : Math.max(0, Math.trunc(value));
14641
15339
  }
14642
15340
  function nonNegativeDuration(value, fallback) {
@@ -16052,6 +16750,7 @@ var Workflow = class {
16052
16750
  }
16053
16751
  return await this.client.installPolicy(this.type, {
16054
16752
  ...options,
16753
+ replace: Object.hasOwn(options, "replace") ? options.replace ?? true : true,
16055
16754
  states
16056
16755
  });
16057
16756
  }
@@ -16262,15 +16961,22 @@ var WorkflowWorker = class {
16262
16961
  );
16263
16962
  }
16264
16963
  };
16964
+
16965
+ // src/version.ts
16966
+ var FERRICSTORE_SDK_VERSION = "0.3.0";
16967
+ var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.9.1";
16968
+ var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
16265
16969
  export {
16266
16970
  BitmapStore,
16267
16971
  BloomFilterStore,
16268
16972
  COMMAND_OPCODES,
16269
- COMPACT_RESPONSE_OPCODES,
16270
16973
  ClaimHydrationError,
16271
16974
  ConnectionClosedError,
16272
16975
  CountMinSketchStore,
16273
16976
  CuckooFilterStore,
16977
+ FERRICSTORE_MINIMUM_SERVER_VERSION,
16978
+ FERRICSTORE_NATIVE_PROTOCOL_VERSION,
16979
+ FERRICSTORE_SDK_VERSION,
16274
16980
  FerricStoreClient,
16275
16981
  FerricStoreError,
16276
16982
  FlowAlreadyExistsError,
@@ -16287,6 +16993,7 @@ export {
16287
16993
  ListStore,
16288
16994
  LockHeldError,
16289
16995
  LockNotOwnedError,
16996
+ MAX_FLOW_POLICY_GENERATION,
16290
16997
  NativeAdapter,
16291
16998
  OverloadedError,
16292
16999
  Queue,
@@ -16301,6 +17008,7 @@ export {
16301
17008
  SetStore,
16302
17009
  SortedSetStore,
16303
17010
  StaleLeaseError,
17011
+ StalePolicyGenerationError,
16304
17012
  StreamStore,
16305
17013
  TDigestStore,
16306
17014
  TopKStore,