@ferricstore/ferricstore 0.2.0 → 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 (88) hide show
  1. package/README.md +20 -3
  2. package/dist/index.cjs +515 -124
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +58 -7
  5. package/dist/index.d.ts +58 -7
  6. package/dist/index.js +502 -113
  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 +1 -1
  12. package/docs/api/classes/ConnectionClosedError.html +1 -1
  13. package/docs/api/classes/FerricStoreClient.html +3 -3
  14. package/docs/api/classes/FerricStoreError.html +1 -1
  15. package/docs/api/classes/FlowAlreadyExistsError.html +2 -2
  16. package/docs/api/classes/FlowBatchError.html +1 -1
  17. package/docs/api/classes/FlowNotFoundError.html +1 -1
  18. package/docs/api/classes/FlowWrongStateError.html +1 -1
  19. package/docs/api/classes/InvalidCommandError.html +2 -2
  20. package/docs/api/classes/LeaseRenewalError.html +1 -1
  21. package/docs/api/classes/LockHeldError.html +2 -2
  22. package/docs/api/classes/LockNotOwnedError.html +2 -2
  23. package/docs/api/classes/OverloadedError.html +2 -2
  24. package/docs/api/classes/QueueCompletionError.html +1 -1
  25. package/docs/api/classes/ReconnectingExecutor.html +4 -4
  26. package/docs/api/classes/RequestTimeoutError.html +1 -1
  27. package/docs/api/classes/RerouteError.html +2 -2
  28. package/docs/api/classes/StaleLeaseError.html +1 -1
  29. package/docs/api/classes/StalePolicyGenerationError.html +39 -0
  30. package/docs/api/classes/TopologyNativeAdapterPool.html +3 -3
  31. package/docs/api/classes/Workflow.html +1 -1
  32. package/docs/api/classes/WorkflowWorker.html +2 -2
  33. package/docs/api/functions/classifyServerError.html +1 -1
  34. package/docs/api/functions/isReconnectableClosedConnectionError.html +1 -1
  35. package/docs/api/functions/mapException.html +1 -1
  36. package/docs/api/hierarchy.html +1 -1
  37. package/docs/api/index.html +8 -4
  38. package/docs/api/interfaces/AdminListOptions.html +1 -1
  39. package/docs/api/interfaces/CancelOptions.html +2 -2
  40. package/docs/api/interfaces/CompleteOptions.html +2 -2
  41. package/docs/api/interfaces/CompleteOutcome.html +1 -1
  42. package/docs/api/interfaces/CreateManyOptions.html +1 -1
  43. package/docs/api/interfaces/EffectCompensateOptions.html +1 -1
  44. package/docs/api/interfaces/EffectConfirmOptions.html +1 -1
  45. package/docs/api/interfaces/EffectFailOptions.html +1 -1
  46. package/docs/api/interfaces/EffectStatusOptions.html +1 -1
  47. package/docs/api/interfaces/ExtendLeaseOptions.html +2 -2
  48. package/docs/api/interfaces/FailOptions.html +2 -2
  49. package/docs/api/interfaces/FailOutcome.html +1 -1
  50. package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +1 -1
  51. package/docs/api/interfaces/FerricStoreClientOptions.html +1 -1
  52. package/docs/api/interfaces/FlowPolicyBackoffSnapshot.html +5 -0
  53. package/docs/api/interfaces/FlowPolicyOptions.html +8 -4
  54. package/docs/api/interfaces/FlowPolicyRetentionSnapshot.html +3 -0
  55. package/docs/api/interfaces/FlowPolicyRetrySnapshot.html +4 -0
  56. package/docs/api/interfaces/FlowPolicySnapshot.html +17 -0
  57. package/docs/api/interfaces/FlowPolicyStateSnapshot.html +6 -0
  58. package/docs/api/interfaces/HistoryOptions.html +2 -2
  59. package/docs/api/interfaces/InvocationCreateOptions.html +2 -2
  60. package/docs/api/interfaces/LeaseMutationOptions.html +2 -2
  61. package/docs/api/interfaces/LimitListOptions.html +1 -1
  62. package/docs/api/interfaces/MutateOptions.html +2 -2
  63. package/docs/api/interfaces/NamedValueMutation.html +1 -1
  64. package/docs/api/interfaces/ProtocolCommand.html +2 -2
  65. package/docs/api/interfaces/ReadOptions.html +2 -2
  66. package/docs/api/interfaces/ReclaimOptions.html +1 -1
  67. package/docs/api/interfaces/RequestContext.html +2 -2
  68. package/docs/api/interfaces/RequestContextOptions.html +2 -2
  69. package/docs/api/interfaces/RetryOptions.html +2 -2
  70. package/docs/api/interfaces/RetryOutcome.html +1 -1
  71. package/docs/api/interfaces/RunStepsItem.html +2 -2
  72. package/docs/api/interfaces/RunStepsManyOptions.html +2 -2
  73. package/docs/api/interfaces/SearchOptions.html +2 -2
  74. package/docs/api/interfaces/StepContinueOptions.html +3 -3
  75. package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
  76. package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
  77. package/docs/api/interfaces/TransitionOptions.html +2 -2
  78. package/docs/api/interfaces/TransitionOutcome.html +1 -1
  79. package/docs/api/modules.html +1 -1
  80. package/docs/api/types/ConnectionRequestDisposition.html +1 -1
  81. package/docs/api/types/FlowPolicyBackoffKind.html +1 -0
  82. package/docs/api/types/ManagementPairs.html +1 -1
  83. package/docs/api/types/SearchStateMeta.html +1 -1
  84. package/docs/api/variables/FERRICSTORE_MINIMUM_SERVER_VERSION.html +1 -1
  85. package/docs/api/variables/FERRICSTORE_NATIVE_PROTOCOL_VERSION.html +1 -1
  86. package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
  87. package/docs/api/variables/MAX_FLOW_POLICY_GENERATION.html +1 -0
  88. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -58,6 +58,9 @@ var FlowWrongStateError = class extends FerricStoreError {
58
58
  var StaleLeaseError = class extends FerricStoreError {
59
59
  code = "stale_lease";
60
60
  };
61
+ var StalePolicyGenerationError = class extends FerricStoreError {
62
+ code = "stale_policy_generation";
63
+ };
61
64
  var FlowAlreadyExistsError = class extends FerricStoreError {
62
65
  code = "flow_already_exists";
63
66
  };
@@ -123,6 +126,9 @@ function classifyServerError(message, raw, cause, status) {
123
126
  if (code === "stale_lease" || code === "stale_flow_lease" || lower.includes("stale flow lease") || lower.includes("stale lease") || lower.includes("stale token")) {
124
127
  return new StaleLeaseError(message, { cause, raw });
125
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
+ }
126
132
  if (code === "flow_not_found" || lower.includes("flow") && (lower.includes("not found") || lower.includes("does not exist"))) {
127
133
  return new FlowNotFoundError(message, { cause, raw });
128
134
  }
@@ -1176,7 +1182,7 @@ var NativeHeartbeat = class {
1176
1182
  };
1177
1183
 
1178
1184
  // src/protocol.ts
1179
- import { Buffer as Buffer22 } from "buffer";
1185
+ import { Buffer as Buffer23 } from "buffer";
1180
1186
 
1181
1187
  // src/command-grammar.ts
1182
1188
  import { Buffer as Buffer4 } from "buffer";
@@ -1998,6 +2004,8 @@ var OPCODES = {
1998
2004
  flowRetryMany: COMMAND_OPCODES["FLOW.RETRY_MANY"],
1999
2005
  flowFailMany: COMMAND_OPCODES["FLOW.FAIL_MANY"],
2000
2006
  flowReclaim: COMMAND_OPCODES["FLOW.RECLAIM"],
2007
+ flowPolicySet: COMMAND_OPCODES["FLOW.POLICY.SET"],
2008
+ flowPolicyGet: COMMAND_OPCODES["FLOW.POLICY.GET"],
2001
2009
  flowSpawnChildren: COMMAND_OPCODES["FLOW.SPAWN_CHILDREN"],
2002
2010
  flowStepContinue: COMMAND_OPCODES["FLOW.STEP_CONTINUE"],
2003
2011
  flowStartAndClaim: COMMAND_OPCODES["FLOW.START_AND_CLAIM"],
@@ -4162,6 +4170,162 @@ function flowSignalPayload(args) {
4162
4170
  return { opcode: OPCODES.flowSignal, payload: { id: args[0], ...options } };
4163
4171
  }
4164
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
+
4165
4329
  // src/protocol-connection-policy.ts
4166
4330
  function assertCommandDoesNotRequirePinnedConnection(args) {
4167
4331
  const outer = commandName(args[0]);
@@ -4209,7 +4373,7 @@ function commandRequiresOrderedPipelineExecution(args) {
4209
4373
  }
4210
4374
 
4211
4375
  // src/protocol-compact-request.ts
4212
- import { Buffer as Buffer13 } from "buffer";
4376
+ import { Buffer as Buffer14 } from "buffer";
4213
4377
  function compactMsetPayload(args, maxBodyBytes) {
4214
4378
  if (args.length % 2 !== 0) throw new FerricStoreError("MSET requires key/value pairs");
4215
4379
  const minimum = 6 + args.length * 4;
@@ -4228,7 +4392,7 @@ function compactMsetPayload(args, maxBodyBytes) {
4228
4392
  total += 4 + byteLength;
4229
4393
  }
4230
4394
  assertCompactPayloadFits(total, maxBodyBytes);
4231
- const out = Buffer13.allocUnsafe(total);
4395
+ const out = Buffer14.allocUnsafe(total);
4232
4396
  let offset = writeCompactPipelineHeader(out, 1, args.length / 2);
4233
4397
  for (let index = 0; index < args.length; index += 1) {
4234
4398
  offset = writeBinaryValue(out, offset, args[index], byteLengths[index] ?? 0);
@@ -4252,7 +4416,7 @@ function compactKeyPipelinePayload(opcode, args, mode, maxBodyBytes) {
4252
4416
  total += 4 + byteLength;
4253
4417
  }
4254
4418
  assertCompactPayloadFits(total, maxBodyBytes);
4255
- const out = Buffer13.allocUnsafe(total);
4419
+ const out = Buffer14.allocUnsafe(total);
4256
4420
  let offset = writeCompactPipelineHeader(out, mode, args.length);
4257
4421
  for (let index = 0; index < args.length; index += 1) {
4258
4422
  offset = writeBinaryValue(out, offset, args[index], byteLengths[index] ?? 0);
@@ -4262,7 +4426,7 @@ function compactKeyPipelinePayload(opcode, args, mode, maxBodyBytes) {
4262
4426
  function compactPipelinePayload(commands, maxBodyBytes) {
4263
4427
  if (commands.length === 0) {
4264
4428
  assertCompactPayloadFits(6, maxBodyBytes);
4265
- const out = Buffer13.allocUnsafe(6);
4429
+ const out = Buffer14.allocUnsafe(6);
4266
4430
  writeCompactPipelineHeader(out, 128 | 2, 0);
4267
4431
  return out;
4268
4432
  }
@@ -4304,7 +4468,7 @@ function compactSetPipelinePayload(commands, maxBodyBytes) {
4304
4468
  total += 8 + keyByteLength + valueByteLength;
4305
4469
  }
4306
4470
  assertCompactPayloadFits(total, maxBodyBytes);
4307
- const out = Buffer13.allocUnsafe(total);
4471
+ const out = Buffer14.allocUnsafe(total);
4308
4472
  let offset = writeCompactPipelineHeader(out, 128 | 1, commands.length);
4309
4473
  for (let index = 0; index < commands.length; index += 1) {
4310
4474
  offset = writeBinaryValue(out, offset, argumentValues[index * 2], argumentByteLengths[index * 2] ?? 0);
@@ -4339,7 +4503,7 @@ function compactGetPipelinePayload(commands, maxBodyBytes) {
4339
4503
  total += 4 + keyByteLength;
4340
4504
  }
4341
4505
  assertCompactPayloadFits(total, maxBodyBytes);
4342
- const out = Buffer13.allocUnsafe(total);
4506
+ const out = Buffer14.allocUnsafe(total);
4343
4507
  let offset = writeCompactPipelineHeader(out, 128 | 2, commands.length);
4344
4508
  for (let index = 0; index < commands.length; index += 1) {
4345
4509
  offset = writeBinaryValue(out, offset, keys[index], keyByteLengths[index] ?? 0);
@@ -4353,7 +4517,7 @@ function compactArgumentsEligible(args, sparseMessage) {
4353
4517
  }
4354
4518
  return true;
4355
4519
  }
4356
- function compactPipelineEligible(commands, commandName4, argumentCount) {
4520
+ function compactPipelineEligible(commands, commandName5, argumentCount) {
4357
4521
  for (let index = 0; index < commands.length; index += 1) {
4358
4522
  const command = commands[index];
4359
4523
  if (!Object.hasOwn(commands, index) || !Array.isArray(command)) {
@@ -4365,7 +4529,7 @@ function compactPipelineEligible(commands, commandName4, argumentCount) {
4365
4529
  throw new TypeError("pipeline command arguments must be dense");
4366
4530
  }
4367
4531
  }
4368
- if (!commandNameIs(command[0], commandName4)) return false;
4532
+ if (!commandNameIs(command[0], commandName5)) return false;
4369
4533
  for (let argumentIndex = 1; argumentIndex < argumentCount; argumentIndex += 1) {
4370
4534
  if (!isCompactBinaryScalar(command[argumentIndex])) return false;
4371
4535
  }
@@ -4380,7 +4544,7 @@ function writeCompactPipelineHeader(out, mode, count) {
4380
4544
  }
4381
4545
 
4382
4546
  // src/protocol-kv.ts
4383
- import { Buffer as Buffer14 } from "buffer";
4547
+ import { Buffer as Buffer15 } from "buffer";
4384
4548
  function collectionProtocolCommand(command, args) {
4385
4549
  switch (command) {
4386
4550
  case "CAS":
@@ -4627,17 +4791,17 @@ function parsedNumber(value) {
4627
4791
  }
4628
4792
  }
4629
4793
  function isBinaryCommandArgument(value) {
4630
- return typeof value === "string" || Buffer14.isBuffer(value);
4794
+ return typeof value === "string" || Buffer15.isBuffer(value);
4631
4795
  }
4632
4796
  function isDirectKvValueArgument(value) {
4633
4797
  return isBinaryCommandArgument(value) || typeof value === "object" && value != null;
4634
4798
  }
4635
4799
  function binaryToken(value) {
4636
- return typeof value === "string" ? value.toUpperCase() : Buffer14.isBuffer(value) ? value.toString("utf8").toUpperCase() : void 0;
4800
+ return typeof value === "string" ? value.toUpperCase() : Buffer15.isBuffer(value) ? value.toString("utf8").toUpperCase() : void 0;
4637
4801
  }
4638
4802
 
4639
4803
  // src/protocol-value.ts
4640
- import { Buffer as Buffer15 } from "buffer";
4804
+ import { Buffer as Buffer16 } from "buffer";
4641
4805
  function planValueWithLimit(value, maxBytes) {
4642
4806
  const budget = {
4643
4807
  maxBytes,
@@ -4692,7 +4856,7 @@ function planContainerValue(value, budget, ancestors, depth) {
4692
4856
  let byteLength = 5;
4693
4857
  let index = 0;
4694
4858
  for (const [key, item] of entries) {
4695
- const keyByteLength = Buffer15.byteLength(key);
4859
+ const keyByteLength = Buffer16.byteLength(key);
4696
4860
  consumeEncodeBytes(budget, 4 + keyByteLength);
4697
4861
  const valuePlan = planValueAt(item, budget, ancestors, depth + 1);
4698
4862
  entryPlans[index] = { key, keyByteLength, value: valuePlan };
@@ -4741,7 +4905,7 @@ function planScalarValue(value, budget) {
4741
4905
  consumeEncodeBytes(budget, 9);
4742
4906
  return { byteLength: 9, tag: 7, value };
4743
4907
  }
4744
- if (typeof value === "string" || Buffer15.isBuffer(value) || value instanceof Uint8Array) {
4908
+ if (typeof value === "string" || Buffer16.isBuffer(value) || value instanceof Uint8Array) {
4745
4909
  const valueByteLength = binaryValueByteLength(value);
4746
4910
  const byteLength = 5 + valueByteLength;
4747
4911
  consumeEncodeBytes(budget, byteLength);
@@ -4765,7 +4929,7 @@ function writeValuePlan(output, start, plan) {
4765
4929
  offset += 4;
4766
4930
  if (typeof plan.value === "string") {
4767
4931
  output.write(plan.value, offset, plan.valueByteLength, "utf8");
4768
- } else if (Buffer15.isBuffer(plan.value)) {
4932
+ } else if (Buffer16.isBuffer(plan.value)) {
4769
4933
  plan.value.copy(output, offset);
4770
4934
  } else {
4771
4935
  output.set(plan.value, offset);
@@ -4899,20 +5063,20 @@ function requireAvailable(data, offset, size) {
4899
5063
  }
4900
5064
 
4901
5065
  // src/key-slot-validation.ts
4902
- import { Buffer as Buffer17 } from "buffer";
5066
+ import { Buffer as Buffer18 } from "buffer";
4903
5067
 
4904
5068
  // src/topology-utilities.ts
4905
- import { Buffer as Buffer16 } from "buffer";
5069
+ import { Buffer as Buffer17 } from "buffer";
4906
5070
  function isPlainObject3(value) {
4907
- return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer16.isBuffer(value);
5071
+ return typeof value === "object" && value != null && !Array.isArray(value) && !Buffer17.isBuffer(value);
4908
5072
  }
4909
5073
  function getField(source, ...keys) {
4910
5074
  if (source instanceof Map) {
4911
5075
  for (const key of keys) {
4912
5076
  if (source.has(key)) return source.get(key);
4913
- const bufferKey = Buffer16.from(key);
5077
+ const bufferKey = Buffer17.from(key);
4914
5078
  for (const [itemKey, value] of source.entries()) {
4915
- if (Buffer16.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
5079
+ if (Buffer17.isBuffer(itemKey) && itemKey.equals(bufferKey)) return value;
4916
5080
  }
4917
5081
  }
4918
5082
  return void 0;
@@ -4926,7 +5090,7 @@ function getField(source, ...keys) {
4926
5090
  }
4927
5091
  function textOrUndefined(value) {
4928
5092
  if (typeof value === "string") return value;
4929
- if (Buffer16.isBuffer(value)) return value.toString("utf8");
5093
+ if (Buffer17.isBuffer(value)) return value.toString("utf8");
4930
5094
  return void 0;
4931
5095
  }
4932
5096
  function numberOrUndefined(value) {
@@ -5101,7 +5265,7 @@ function stringHashRange(key) {
5101
5265
  var validatedCommands = /* @__PURE__ */ new WeakSet();
5102
5266
  function assertAtomicKeyValueCommandSharesSlot(args) {
5103
5267
  const value = args[0];
5104
- const command = (typeof value === "string" ? value : Buffer17.isBuffer(value) ? value.toString("utf8") : "").toUpperCase();
5268
+ const command = (typeof value === "string" ? value : Buffer18.isBuffer(value) ? value.toString("utf8") : "").toUpperCase();
5105
5269
  if (command === "MSET" || command === "MSETNX") {
5106
5270
  assertKeyValueCommandSharesSlot(args, command);
5107
5271
  }
@@ -5111,7 +5275,7 @@ function assertKeyValueCommandSharesSlot(args, command) {
5111
5275
  let expectedSlot;
5112
5276
  for (let index = 1; index < args.length; index += 2) {
5113
5277
  const key = args[index];
5114
- if (typeof key !== "string" && !Buffer17.isBuffer(key)) return;
5278
+ if (typeof key !== "string" && !Buffer18.isBuffer(key)) return;
5115
5279
  const slot = routingSlotForKey(key);
5116
5280
  expectedSlot ??= slot;
5117
5281
  if (slot !== expectedSlot) {
@@ -5122,13 +5286,13 @@ function assertKeyValueCommandSharesSlot(args, command) {
5122
5286
  }
5123
5287
 
5124
5288
  // src/protocol-response.ts
5125
- import { Buffer as Buffer21 } from "buffer";
5289
+ import { Buffer as Buffer22 } from "buffer";
5126
5290
 
5127
5291
  // src/protocol-compact-response.ts
5128
- import { Buffer as Buffer20 } from "buffer";
5292
+ import { Buffer as Buffer21 } from "buffer";
5129
5293
 
5130
5294
  // src/native-negotiation.ts
5131
- import { Buffer as Buffer18 } from "buffer";
5295
+ import { Buffer as Buffer19 } from "buffer";
5132
5296
  var EMPTY_COMPACT_RESPONSE_OPCODES = /* @__PURE__ */ new Map();
5133
5297
  function nativeNegotiation(value) {
5134
5298
  const capabilities = field(value, "capabilities") ?? value;
@@ -5196,8 +5360,8 @@ function nonNegativeSafeInteger2(value) {
5196
5360
  }
5197
5361
  function text2(value) {
5198
5362
  if (typeof value === "string") return value;
5199
- if (Buffer18.isBuffer(value) || value instanceof Uint8Array) {
5200
- return Buffer18.from(value).toString("utf8");
5363
+ if (Buffer19.isBuffer(value) || value instanceof Uint8Array) {
5364
+ return Buffer19.from(value).toString("utf8");
5201
5365
  }
5202
5366
  return void 0;
5203
5367
  }
@@ -5349,7 +5513,7 @@ function decodeCompactOkList(data, budget) {
5349
5513
  const count = data.readUInt32BE(1);
5350
5514
  requireCompactContainer2(count, budget);
5351
5515
  if (data.byteLength !== 5) throw new FerricStoreError("invalid compact OK list response");
5352
- return Array.from({ length: count }, () => Buffer20.from("OK"));
5516
+ return Array.from({ length: count }, () => Buffer21.from("OK"));
5353
5517
  }
5354
5518
  function decodeCompactKvGet(data) {
5355
5519
  requireAvailable(data, 0, 2);
@@ -5395,7 +5559,7 @@ function decodeCompactMgetFixed(data, budget) {
5395
5559
  if (size === NULL_U32 || data.byteLength !== expected) {
5396
5560
  throw new FerricStoreError("invalid compact fixed MGET response");
5397
5561
  }
5398
- if (size === 0) return new Array(count).fill(Buffer20.alloc(0));
5562
+ if (size === 0) return new Array(count).fill(Buffer21.alloc(0));
5399
5563
  const values = new Array(count);
5400
5564
  for (let index = 0, offset = 9; index < count; index += 1, offset += size) {
5401
5565
  values[index] = data.subarray(offset, offset + size);
@@ -5610,7 +5774,7 @@ function requireCompactContainer2(count, budget) {
5610
5774
  }
5611
5775
  function errorMessage(status, value) {
5612
5776
  if (typeof value === "string") return value;
5613
- if (Buffer20.isBuffer(value)) return value.toString("utf8");
5777
+ if (Buffer21.isBuffer(value)) return value.toString("utf8");
5614
5778
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5615
5779
  const message = value.message;
5616
5780
  return typeof message === "string" ? message : String(message);
@@ -5690,7 +5854,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
5690
5854
  return out;
5691
5855
  }
5692
5856
  function pipelineStatus(value) {
5693
- if (typeof value !== "string" && !Buffer21.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5857
+ if (typeof value !== "string" && !Buffer22.isBuffer(value) && !(value instanceof Uint8Array)) return null;
5694
5858
  const status = asText(value).toLowerCase();
5695
5859
  return status === "ok" || status === "busy" || status === "error" ? status : null;
5696
5860
  }
@@ -5705,7 +5869,7 @@ function decodeResponseValue(opcode, body, hints) {
5705
5869
  }
5706
5870
  function errorMessage2(status, value) {
5707
5871
  if (typeof value === "string") return value;
5708
- if (Buffer21.isBuffer(value)) return value.toString("utf8");
5872
+ if (Buffer22.isBuffer(value)) return value.toString("utf8");
5709
5873
  if (typeof value === "object" && value != null && Object.hasOwn(value, "message")) {
5710
5874
  const message = value.message;
5711
5875
  return typeof message === "string" ? message : String(message);
@@ -5721,7 +5885,7 @@ function encodeRequest(command, requestId, maxBodyBytes = Number.MAX_SAFE_INTEGE
5721
5885
  const bodyLength = customPayload ? binaryValueByteLength(command.payload) : plan?.byteLength ?? 0;
5722
5886
  if (bodyLength > bodyLimit) throw requestFrameTooLarge(bodyLimit);
5723
5887
  const flags = command.flags ?? 0;
5724
- const frame = Buffer22.allocUnsafe(HEADER_SIZE + bodyLength);
5888
+ const frame = Buffer23.allocUnsafe(HEADER_SIZE + bodyLength);
5725
5889
  frame.write(MAGIC, 0, "ascii");
5726
5890
  frame.writeUInt8(REQUEST_VERSION, 4);
5727
5891
  frame.writeUInt8(flags, 5);
@@ -5846,6 +6010,12 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
5846
6010
  if (command === "FLOW.LIST") {
5847
6011
  return (allowCustomPayload ? compactFlowListPayload(commandArgs2, maxBodyBytes) : void 0) ?? commandExec(args);
5848
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
+ }
5849
6019
  const flowAdmin = FLOW_ADMIN_COMMANDS[command];
5850
6020
  if (flowAdmin != null) {
5851
6021
  return flowAdminPayload(flowAdmin.opcode, commandArgs2, flowAdmin.leadingFields) ?? commandExec(args);
@@ -6101,7 +6271,7 @@ import "net";
6101
6271
  import "tls";
6102
6272
 
6103
6273
  // src/client-native-options.ts
6104
- import { Buffer as Buffer23 } from "buffer";
6274
+ import { Buffer as Buffer24 } from "buffer";
6105
6275
 
6106
6276
  // src/string-array-snapshot.ts
6107
6277
  function snapshotOwnStringArray(values, name) {
@@ -6184,7 +6354,7 @@ function snapshotTlsOptions(options) {
6184
6354
  return Object.freeze(snapshot);
6185
6355
  }
6186
6356
  function snapshotTlsValue(value, name) {
6187
- if (Buffer23.isBuffer(value)) return Buffer23.from(value);
6357
+ if (Buffer24.isBuffer(value)) return Buffer24.from(value);
6188
6358
  if (value instanceof Uint8Array) return value.slice();
6189
6359
  if (Array.isArray(value)) {
6190
6360
  const snapshot = new Array(value.length);
@@ -7592,6 +7762,29 @@ function executeCommandArgs(executor, args) {
7592
7762
 
7593
7763
  // src/reconnecting-executor.ts
7594
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
7595
7788
  var ReconnectingExecutor = class {
7596
7789
  constructor(createExecutor, options = {}) {
7597
7790
  this.createExecutor = createExecutor;
@@ -7622,7 +7815,12 @@ var ReconnectingExecutor = class {
7622
7815
  async executeCommandArgs(args) {
7623
7816
  const snapshot = snapshotCommandArguments(args);
7624
7817
  assertCommandHasStableConnectionState(snapshot);
7625
- 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
+ );
7626
7824
  }
7627
7825
  async ready() {
7628
7826
  await this.executorPromise;
@@ -7707,7 +7905,7 @@ var ReconnectingExecutor = class {
7707
7905
  })();
7708
7906
  await this.closePromise;
7709
7907
  }
7710
- async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError) {
7908
+ async withReconnect(operation, reconnectable = isReconnectableClosedConnectionError, replayAllowed = true) {
7711
7909
  let executor = await this.executorPromise;
7712
7910
  let retries = 0;
7713
7911
  for (; ; ) {
@@ -7716,6 +7914,10 @@ var ReconnectingExecutor = class {
7716
7914
  return await operation(executor);
7717
7915
  } catch (error) {
7718
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
+ }
7719
7921
  for (; ; ) {
7720
7922
  retries += 1;
7721
7923
  try {
@@ -7768,6 +7970,9 @@ var ReconnectingExecutor = class {
7768
7970
  function isReconnectableClosedConnectionError(error) {
7769
7971
  return error instanceof ConnectionClosedError && error.requestDisposition === "unsent";
7770
7972
  }
7973
+ function isClosedConnectionError(error) {
7974
+ return error instanceof ConnectionClosedError;
7975
+ }
7771
7976
  function isReadOnlyReconnectableClosedConnectionError(error) {
7772
7977
  const pending = [error];
7773
7978
  const seen = /* @__PURE__ */ new Set();
@@ -7789,7 +7994,7 @@ function normalizeNonNegativeInteger2(value, fallback) {
7789
7994
  import "buffer";
7790
7995
 
7791
7996
  // src/topology-routing.ts
7792
- import { Buffer as Buffer28 } from "buffer";
7997
+ import { Buffer as Buffer30 } from "buffer";
7793
7998
  import { createHash } from "crypto";
7794
7999
  var FLOW_PARTITION_ROUTE_CACHE_MAX = 1024;
7795
8000
  var flowPartitionRouteCache = /* @__PURE__ */ new Map();
@@ -7809,7 +8014,7 @@ function routingKeyFromProtocolPayload(name, command) {
7809
8014
  "scope"
7810
8015
  ]) {
7811
8016
  const value = getField(command.payload, field2);
7812
- if (typeof value === "string" || Buffer28.isBuffer(value)) {
8017
+ if (typeof value === "string" || Buffer30.isBuffer(value)) {
7813
8018
  return value;
7814
8019
  }
7815
8020
  }
@@ -7856,7 +8061,7 @@ function flowRoutingData(name, args) {
7856
8061
  if (typeof partition === "string" && partition.toUpperCase() !== "AUTO" && partition.toUpperCase() !== "MIXED") {
7857
8062
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
7858
8063
  }
7859
- if (Buffer28.isBuffer(partition)) {
8064
+ if (Buffer30.isBuffer(partition)) {
7860
8065
  const text3 = partition.toString("utf8").toUpperCase();
7861
8066
  if (text3 !== "AUTO" && text3 !== "MIXED") {
7862
8067
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
@@ -7947,13 +8152,13 @@ function flowPartitionRoutingKeyFromCommand(command, claim) {
7947
8152
  return { handled: false };
7948
8153
  }
7949
8154
  function isRoutingKey(value) {
7950
- return typeof value === "string" || Buffer28.isBuffer(value);
8155
+ return typeof value === "string" || Buffer30.isBuffer(value);
7951
8156
  }
7952
8157
  function flowLogicalPartitionRoutingKey(value) {
7953
- if (typeof value !== "string" && !Buffer28.isBuffer(value)) {
8158
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) {
7954
8159
  return void 0;
7955
8160
  }
7956
- const bytes2 = Buffer28.isBuffer(value) ? value : Buffer28.from(value);
8161
+ const bytes2 = Buffer30.isBuffer(value) ? value : Buffer30.from(value);
7957
8162
  const text3 = bytes2.toString("utf8");
7958
8163
  const autoMatch = /^__flow_auto__:(0|[1-9]\d{0,2})$/u.exec(text3);
7959
8164
  if (autoMatch != null) {
@@ -7962,7 +8167,7 @@ function flowLogicalPartitionRoutingKey(value) {
7962
8167
  return `{fa:${bucket}}`;
7963
8168
  }
7964
8169
  }
7965
- const cacheKey = `${Buffer28.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
8170
+ const cacheKey = `${Buffer30.isBuffer(value) ? "b" : "s"}:${bytes2.toString("base64")}`;
7966
8171
  const cached = flowPartitionRouteCache.get(cacheKey);
7967
8172
  if (cached != null) {
7968
8173
  flowPartitionRouteCache.delete(cacheKey);
@@ -7979,14 +8184,14 @@ function flowLogicalPartitionRoutingKey(value) {
7979
8184
  return routeKey;
7980
8185
  }
7981
8186
  function flowAutoIdRoutingKey(value) {
7982
- if (typeof value !== "string" && !Buffer28.isBuffer(value)) {
8187
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) {
7983
8188
  return void 0;
7984
8189
  }
7985
- const bucket = (Buffer28.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
8190
+ const bucket = (Buffer30.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
7986
8191
  return `{fa:${bucket}}`;
7987
8192
  }
7988
8193
  function flowClaimLogicalPartitionRoutingKey(value) {
7989
- if (typeof value !== "string" && !Buffer28.isBuffer(value)) return void 0;
8194
+ if (typeof value !== "string" && !Buffer30.isBuffer(value)) return void 0;
7990
8195
  const selector = commandPart(value);
7991
8196
  if (selector === "AUTO" || selector === "ANY") return void 0;
7992
8197
  if (selector === "GLOBAL") return "{f}";
@@ -8001,7 +8206,7 @@ function singleShardFlowClaimPartitionKey(values) {
8001
8206
  return keys.some((key) => key == null) ? void 0 : singleShardKey(keys);
8002
8207
  }
8003
8208
  function singleShardKey(keys) {
8004
- if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !Buffer28.isBuffer(key))) {
8209
+ if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !Buffer30.isBuffer(key))) {
8005
8210
  return void 0;
8006
8211
  }
8007
8212
  const usable = keys;
@@ -8012,7 +8217,7 @@ function singleShardKey(keys) {
8012
8217
  const firstSlot = routingSlotForKey(first);
8013
8218
  return usable.every((key) => routingSlotForKey(key) === firstSlot) ? first : void 0;
8014
8219
  }
8015
- function commandName2(args) {
8220
+ function commandName3(args) {
8016
8221
  return commandView(args).name;
8017
8222
  }
8018
8223
  var commandPart = commandToken;
@@ -8034,7 +8239,7 @@ function routedKeyGroups(keys, routeKey) {
8034
8239
  const groups = /* @__PURE__ */ new Map();
8035
8240
  for (let index = 0; index < keys.length; index += 1) {
8036
8241
  const key = keys[index];
8037
- if (typeof key !== "string" && !Buffer28.isBuffer(key)) return void 0;
8242
+ if (typeof key !== "string" && !Buffer30.isBuffer(key)) return void 0;
8038
8243
  const route = routeKey(key);
8039
8244
  const groupKey = `${route.endpointKey}\0${route.laneId}`;
8040
8245
  const group = groups.get(groupKey);
@@ -8426,7 +8631,7 @@ var TopologyPipelineExecutor = class {
8426
8631
  commands.forEach((command, index) => {
8427
8632
  const routed = this.context.routeData(command);
8428
8633
  if (routed == null) {
8429
- if (decomposableCommandNames.has(commandName2(command) ?? "")) standalone.push({ command, index });
8634
+ if (decomposableCommandNames.has(commandName3(command) ?? "")) standalone.push({ command, index });
8430
8635
  else {
8431
8636
  controlCommands.push(command);
8432
8637
  controlIndices.push(index);
@@ -8642,7 +8847,7 @@ function topologyRouteIsCurrent(topology, route) {
8642
8847
  // src/topology-route-planner.ts
8643
8848
  function topologyRouteData(args, route) {
8644
8849
  if (args.length === 0) return void 0;
8645
- const name = commandName2(args);
8850
+ const name = commandName3(args);
8646
8851
  if (name?.startsWith("FLOW.") === true) {
8647
8852
  const routed = flowRoutingData(name, args);
8648
8853
  return routed == null ? void 0 : {
@@ -8671,7 +8876,7 @@ var TopologyScatterExecutor = class {
8671
8876
  }
8672
8877
  dependencies;
8673
8878
  async execute(args) {
8674
- const name = commandName2(args);
8879
+ const name = commandName3(args);
8675
8880
  if (name === "MGET") {
8676
8881
  const value = await this.scatterArray(name, args.slice(1));
8677
8882
  return value == null ? { handled: false } : { handled: true, value };
@@ -8753,6 +8958,18 @@ var TopologyScatterExecutor = class {
8753
8958
  }
8754
8959
  };
8755
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
+
8756
8973
  // src/topology.ts
8757
8974
  var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8758
8975
  adapterOptions;
@@ -8938,6 +9155,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8938
9155
  this.assertOpen();
8939
9156
  const snapshot = snapshotCommandArguments(args);
8940
9157
  assertCommandHasStableConnectionState(snapshot);
9158
+ const casMutation = isCasMutation(snapshot);
8941
9159
  let rerouteAttempt = 0;
8942
9160
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8943
9161
  const routed = this.routeData(snapshot);
@@ -8953,7 +9171,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8953
9171
  try {
8954
9172
  return routed.command == null ? await adapter.executeCommandOnLane(snapshot, routed.route.laneId) : await adapter.executeProtocolCommand(routed.command, routed.route.laneId);
8955
9173
  } catch (error) {
8956
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9174
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !casMutation)) {
8957
9175
  rerouteAttempt += 1;
8958
9176
  continue;
8959
9177
  }
@@ -8995,6 +9213,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
8995
9213
  assertCommandHasStableConnectionState(command);
8996
9214
  }
8997
9215
  let rerouteAttempt = 0;
9216
+ const containsCasMutation = snapshot.some(isCasMutation);
8998
9217
  for (let planningAttempt = 0; planningAttempt < MAX_TOPOLOGY_PLANNING_ATTEMPTS; planningAttempt += 1) {
8999
9218
  const route = singleRouteForCommands(snapshot, (command) => this.routeData(command));
9000
9219
  if (route == null) return void 0;
@@ -9003,7 +9222,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
9003
9222
  try {
9004
9223
  return await adapter.executeFusedPipelineOnLane(snapshot, route.laneId, snapshotOptions);
9005
9224
  } catch (error) {
9006
- if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt)) {
9225
+ if (await this.refreshAndCanRetrySafeReroute(error, rerouteAttempt, !containsCasMutation)) {
9007
9226
  rerouteAttempt += 1;
9008
9227
  continue;
9009
9228
  }
@@ -9012,15 +9231,14 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
9012
9231
  }
9013
9232
  throw new StaleTopologyRouteError();
9014
9233
  }
9015
- async refreshAndCanRetrySafeReroute(error, attempt) {
9016
- if (attempt !== 0 || !isRetryableRouteError(error)) return false;
9017
- const refreshed = await this.refreshTopology().then(
9018
- () => true,
9019
- () => 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()
9020
9241
  );
9021
- if (!refreshed || !await waitForExplicitlySafeReroute(error)) return false;
9022
- this.assertOpen();
9023
- return true;
9024
9242
  }
9025
9243
  async executePipelineOnRoute(commands, route, options) {
9026
9244
  const adapter = await this.adapterForCurrentRoute(route);
@@ -9110,7 +9328,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
9110
9328
  };
9111
9329
 
9112
9330
  // src/response-map-preservation.ts
9113
- import { Buffer as Buffer30 } from "buffer";
9331
+ import { Buffer as Buffer32 } from "buffer";
9114
9332
  function toStringKeyMapPreservingValues(value) {
9115
9333
  if (value == null) return void 0;
9116
9334
  const result = {};
@@ -9120,7 +9338,7 @@ function toStringKeyMapPreservingValues(value) {
9120
9338
  }
9121
9339
  return result;
9122
9340
  }
9123
- if (typeof value === "object" && !Array.isArray(value) && !Buffer30.isBuffer(value) && !(value instanceof Uint8Array)) {
9341
+ if (typeof value === "object" && !Array.isArray(value) && !Buffer32.isBuffer(value) && !(value instanceof Uint8Array)) {
9124
9342
  for (const [key, item] of Object.entries(value)) {
9125
9343
  setOwnValue(result, text(key), normalizeMapStructurePreservingBytes(item));
9126
9344
  }
@@ -9129,7 +9347,7 @@ function toStringKeyMapPreservingValues(value) {
9129
9347
  return void 0;
9130
9348
  }
9131
9349
  function normalizeMapStructurePreservingBytes(value) {
9132
- if (Buffer30.isBuffer(value) || value instanceof Uint8Array) return value;
9350
+ if (Buffer32.isBuffer(value) || value instanceof Uint8Array) return value;
9133
9351
  if (value instanceof Map) {
9134
9352
  const result = {};
9135
9353
  for (const [key, item] of value.entries()) {
@@ -9151,7 +9369,7 @@ function normalizeMapStructurePreservingBytes(value) {
9151
9369
  }
9152
9370
 
9153
9371
  // src/native-kv-responses.ts
9154
- import { Buffer as Buffer31 } from "buffer";
9372
+ import { Buffer as Buffer33 } from "buffer";
9155
9373
  function rateLimitResultFromResp(value) {
9156
9374
  if (!Array.isArray(value) || value.length !== 4) {
9157
9375
  throw new TypeError("RATELIMIT.ADD returned an unexpected response");
@@ -9208,13 +9426,13 @@ function fetchOrComputeResultFromResp(value, codec) {
9208
9426
  }
9209
9427
  function decodePayload(codec, value) {
9210
9428
  if (value == null) return null;
9211
- if (Buffer31.isBuffer(value)) return codec.decode(value);
9212
- if (value instanceof Uint8Array) return codec.decode(Buffer31.from(value));
9213
- if (typeof value === "string") return codec.decode(Buffer31.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));
9214
9432
  return normalizeRefMeta(value);
9215
9433
  }
9216
9434
  function requiredResponseString(value, context) {
9217
- if (typeof value !== "string" && !Buffer31.isBuffer(value) && !(value instanceof Uint8Array)) {
9435
+ if (typeof value !== "string" && !Buffer33.isBuffer(value) && !(value instanceof Uint8Array)) {
9218
9436
  throw new TypeError(`${context} returned an unexpected response`);
9219
9437
  }
9220
9438
  const result = text(value);
@@ -9231,7 +9449,7 @@ function requiredNonNegativeInteger(value, context) {
9231
9449
  return result;
9232
9450
  }
9233
9451
  function responseBytes(value, context) {
9234
- if (typeof value !== "string" && !Buffer31.isBuffer(value) && !(value instanceof Uint8Array)) {
9452
+ if (typeof value !== "string" && !Buffer33.isBuffer(value) && !(value instanceof Uint8Array)) {
9235
9453
  throw new TypeError(`${context} returned an unexpected response`);
9236
9454
  }
9237
9455
  return bytes(value);
@@ -9941,7 +10159,7 @@ function groupAutoPartitionItems(items) {
9941
10159
  }
9942
10160
 
9943
10161
  // src/client-values.ts
9944
- import { Buffer as Buffer32 } from "buffer";
10162
+ import { Buffer as Buffer34 } from "buffer";
9945
10163
  async function valueMGetEntries(client, refs, options = {}) {
9946
10164
  const refCount = refs.length;
9947
10165
  if (refCount === 0) {
@@ -9976,12 +10194,12 @@ async function valueMGetEntries(client, refs, options = {}) {
9976
10194
  const item = response[index];
9977
10195
  if (item == null) {
9978
10196
  entries[index] = { found: false };
9979
- } else if (Buffer32.isBuffer(item)) {
10197
+ } else if (Buffer34.isBuffer(item)) {
9980
10198
  entries[index] = { found: true, value: client.codec.decode(item) };
9981
10199
  } else if (item instanceof Uint8Array) {
9982
- entries[index] = { found: true, value: client.codec.decode(Buffer32.from(item)) };
10200
+ entries[index] = { found: true, value: client.codec.decode(Buffer34.from(item)) };
9983
10201
  } else if (typeof item === "string") {
9984
- entries[index] = { found: true, value: client.codec.decode(Buffer32.from(item)) };
10202
+ entries[index] = { found: true, value: client.codec.decode(Buffer34.from(item)) };
9985
10203
  } else {
9986
10204
  entries[index] = { found: true, value: item };
9987
10205
  }
@@ -9993,7 +10211,7 @@ async function valueMGetEntries(client, refs, options = {}) {
9993
10211
  import "buffer";
9994
10212
 
9995
10213
  // src/auto-batch-ordering.ts
9996
- import { Buffer as Buffer33 } from "buffer";
10214
+ import { Buffer as Buffer35 } from "buffer";
9997
10215
  function autoBatchOrderingPlan(batch) {
9998
10216
  const accesses = /* @__PURE__ */ new Map();
9999
10217
  const fallbackDependencies = [];
@@ -10108,13 +10326,13 @@ function flowManyAutoBatchIds(command, name) {
10108
10326
  return fixedFlowItemIds(
10109
10327
  command,
10110
10328
  mixed ? 4 : 3,
10111
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && Buffer33.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)])
10112
10330
  );
10113
10331
  }
10114
10332
  return fixedFlowItemIds(
10115
10333
  command,
10116
10334
  mixed ? 4 : 3,
10117
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer33.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)])
10118
10336
  );
10119
10337
  }
10120
10338
  function createManyAutoBatchIds(command) {
@@ -10127,7 +10345,7 @@ function createManyAutoBatchIds(command) {
10127
10345
  const ids = fixedFlowItemIds(
10128
10346
  command,
10129
10347
  width,
10130
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer33.isBuffer(command[itemIndex + width - 1]),
10348
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer35.isBuffer(command[itemIndex + width - 1]),
10131
10349
  markerIndex
10132
10350
  );
10133
10351
  if (ids != null) return ids;
@@ -10145,7 +10363,7 @@ function extendedCreateManyAutoBatchIds(command, markerIndex) {
10145
10363
  let cursor = markerIndex + 2;
10146
10364
  for (let itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
10147
10365
  const id = command[cursor];
10148
- if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !Buffer33.isBuffer(command[cursor + 2])) return void 0;
10366
+ if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !Buffer35.isBuffer(command[cursor + 2])) return void 0;
10149
10367
  ids.push(id);
10150
10368
  cursor += 3;
10151
10369
  const afterValues = skipExtendedNamedItems(command, cursor, true);
@@ -10164,7 +10382,7 @@ function skipExtendedNamedItems(command, countIndex, encodedValues) {
10164
10382
  for (let index = 0; index < count; index += 1) {
10165
10383
  const name = command[firstItem + index * 2];
10166
10384
  const value = command[firstItem + index * 2 + 1];
10167
- if (!isAutoBatchResourceValue(name) || (encodedValues ? !Buffer33.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
10385
+ if (!isAutoBatchResourceValue(name) || (encodedValues ? !Buffer35.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
10168
10386
  }
10169
10387
  return firstItem + count * 2;
10170
10388
  }
@@ -10179,7 +10397,7 @@ function runStepsManyAutoBatchIds(command) {
10179
10397
  ids.push(item);
10180
10398
  continue;
10181
10399
  }
10182
- if (typeof item !== "object" || item == null || Array.isArray(item) || Buffer33.isBuffer(item)) return void 0;
10400
+ if (typeof item !== "object" || item == null || Array.isArray(item) || Buffer35.isBuffer(item)) return void 0;
10183
10401
  const id = item.id;
10184
10402
  if (!isAutoBatchResourceValue(id)) return void 0;
10185
10403
  ids.push(id);
@@ -10220,7 +10438,7 @@ function flowValuePutOwner(command, start) {
10220
10438
  }
10221
10439
  var flowValuePutOptionTokens = /* @__PURE__ */ new Set(["NAME", "NOW", "OVERRIDE", "OWNER_FLOW_ID", "PARTITION", "TTL", "TTL_MS"]);
10222
10440
  function isAutoBatchResourceValue(value) {
10223
- return typeof value === "string" || Buffer33.isBuffer(value);
10441
+ return typeof value === "string" || Buffer35.isBuffer(value);
10224
10442
  }
10225
10443
  function isFencingToken(value) {
10226
10444
  return typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint";
@@ -10229,7 +10447,7 @@ function nonNegativeItemCount(value) {
10229
10447
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
10230
10448
  }
10231
10449
  function autoBatchResourceKey(namespace, value) {
10232
- return `${namespace}:${Buffer33.from(value).toString("base64")}`;
10450
+ return `${namespace}:${Buffer35.from(value).toString("base64")}`;
10233
10451
  }
10234
10452
  function autoBatchCommandName(command) {
10235
10453
  return commandView(command).name ?? null;
@@ -10644,9 +10862,9 @@ import "buffer";
10644
10862
  import "buffer";
10645
10863
 
10646
10864
  // src/client-core-helpers.ts
10647
- import { Buffer as Buffer35 } from "buffer";
10865
+ import { Buffer as Buffer37 } from "buffer";
10648
10866
  function bgsaveResponse(response) {
10649
- if ((typeof response === "string" || Buffer35.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") {
10650
10868
  return true;
10651
10869
  }
10652
10870
  return okResponse(response);
@@ -10660,7 +10878,7 @@ function fetchOrComputeCompletionToken(options) {
10660
10878
  "fetch-or-compute completion requires computeToken"
10661
10879
  );
10662
10880
  }
10663
- if (!Buffer35.isBuffer(options.computeToken)) {
10881
+ if (!Buffer37.isBuffer(options.computeToken)) {
10664
10882
  throw new TypeError("fetch-or-compute computeToken must be a Buffer");
10665
10883
  }
10666
10884
  return options.computeToken;
@@ -11020,14 +11238,14 @@ var FerricStoreAdministrationClient = class extends FerricStoreClientBase {
11020
11238
  };
11021
11239
 
11022
11240
  // src/store-utilities.ts
11023
- import { Buffer as Buffer38 } from "buffer";
11241
+ import { Buffer as Buffer40 } from "buffer";
11024
11242
  function encode(codec, value) {
11025
11243
  return codec.encode(value);
11026
11244
  }
11027
11245
  function decode(codec, value) {
11028
11246
  if (value == null) return null;
11029
- if (Buffer38.isBuffer(value)) return codec.decode(value);
11030
- if (value instanceof Uint8Array) return codec.decode(Buffer38.from(value));
11247
+ if (Buffer40.isBuffer(value)) return codec.decode(value);
11248
+ if (value instanceof Uint8Array) return codec.decode(Buffer40.from(value));
11031
11249
  return value;
11032
11250
  }
11033
11251
  function number(value) {
@@ -11088,7 +11306,7 @@ function requireNonEmpty(values, command, noun = "argument") {
11088
11306
  if (values.length === 0) throw new TypeError(`${command} requires at least one ${noun}`);
11089
11307
  return values;
11090
11308
  }
11091
- function commandName3(args) {
11309
+ function commandName4(args) {
11092
11310
  return typeof args[0] === "string" ? args[0] : "command";
11093
11311
  }
11094
11312
  function commandArgs(client, args) {
@@ -11103,7 +11321,7 @@ function arrayOrRest(values) {
11103
11321
  function appendXReadStreams(args, streams) {
11104
11322
  const offset = args.length;
11105
11323
  const streamCount = streams.length;
11106
- requireNonEmpty(streams, commandName3(args), "stream");
11324
+ requireNonEmpty(streams, commandName4(args), "stream");
11107
11325
  args.length = offset + 1 + streamCount * 2;
11108
11326
  args[offset] = "STREAMS";
11109
11327
  for (let index = 0; index < streamCount; index += 1) {
@@ -11113,7 +11331,7 @@ function appendXReadStreams(args, streams) {
11113
11331
  const stream = streams[index];
11114
11332
  if (stream == null) throw new TypeError("XREAD streams must contain only stream descriptors");
11115
11333
  if (!Object.hasOwn(stream, "key") || !Object.hasOwn(stream, "id")) {
11116
- 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`);
11117
11335
  }
11118
11336
  args[offset + 1 + index] = stream.key;
11119
11337
  args[offset + 1 + streamCount + index] = stream.id;
@@ -11135,7 +11353,7 @@ function concatArgs(...parts) {
11135
11353
  return args;
11136
11354
  }
11137
11355
  function encodedArgs(prefix, codec, values, allowEmpty = false) {
11138
- if (!allowEmpty) requireNonEmpty(values, commandName3(prefix));
11356
+ if (!allowEmpty) requireNonEmpty(values, commandName4(prefix));
11139
11357
  const args = new Array(prefix.length + values.length);
11140
11358
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
11141
11359
  for (let index = 0; index < values.length; index += 1) {
@@ -11149,7 +11367,7 @@ function encodedArgs(prefix, codec, values, allowEmpty = false) {
11149
11367
  function keyValueArgs(prefix, codec, entries) {
11150
11368
  const validateTuples = Array.isArray(entries);
11151
11369
  const pairs = validateTuples ? entries : Object.entries(entries);
11152
- requireNonEmpty(pairs, commandName3(prefix), "entry");
11370
+ requireNonEmpty(pairs, commandName4(prefix), "entry");
11153
11371
  const args = new Array(prefix.length + pairs.length * 2);
11154
11372
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
11155
11373
  for (let index = 0; index < pairs.length; index += 1) {
@@ -11163,7 +11381,7 @@ function keyValueArgs(prefix, codec, entries) {
11163
11381
  return args;
11164
11382
  }
11165
11383
  function encodedEntryArgs(prefix, codec, entries) {
11166
- requireNonEmpty(entries, commandName3(prefix), "entry");
11384
+ requireNonEmpty(entries, commandName4(prefix), "entry");
11167
11385
  const args = new Array(prefix.length + entries.length * 2);
11168
11386
  for (let index = 0; index < prefix.length; index += 1) args[index] = prefix[index];
11169
11387
  for (let index = 0; index < entries.length; index += 1) {
@@ -13262,6 +13480,165 @@ function throwIfClosed(signal) {
13262
13480
  throw new FerricStoreError("FerricStore client is closed", { raw: signal.reason });
13263
13481
  }
13264
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
+
13265
13642
  // src/client-flow-support.ts
13266
13643
  var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13267
13644
  async rewind(id, options = {}) {
@@ -13404,27 +13781,36 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13404
13781
  }
13405
13782
  async installPolicy(type, options = {}) {
13406
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);
13407
13792
  if (options.indexedAttributes != null) {
13408
13793
  args.push("INDEXED_ATTRIBUTES", JSON.stringify(options.indexedAttributes));
13409
13794
  }
13410
13795
  append(args, "INDEXED_STATE_META", options.indexedStateMeta);
13411
13796
  append(args, "MAX_ACTIVE_MS", options.maxActiveMs);
13797
+ if (options.state == null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13412
13798
  append(args, "STATE", options.state);
13413
13799
  appendPolicyMode(args, options.state, options.mode);
13414
13800
  if (options.retry != null) {
13415
13801
  appendRetryPolicy(args, options.retry);
13416
13802
  }
13803
+ if (options.state != null) append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13417
13804
  for (const [state, policy] of Object.entries(options.states ?? {})) {
13418
13805
  args.push("STATE", state);
13419
13806
  appendFlowStatePolicy(args, policy);
13420
13807
  }
13421
- append(args, "RETENTION_TTL_MS", options.retentionTtlMs);
13422
- return await this.commandArgs(args);
13808
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13423
13809
  }
13424
13810
  async policyGet(type, options = {}) {
13425
13811
  const args = ["FLOW.POLICY.GET", type];
13426
13812
  append(args, "STATE", options.state);
13427
- return parseKvResponse(await this.commandArgs(args));
13813
+ return flowPolicySnapshotFromResp(await this.commandArgs(args));
13428
13814
  }
13429
13815
  async retentionCleanup() {
13430
13816
  return parseKvResponse(await this.command("FLOW.RETENTION_CLEANUP"));
@@ -13513,7 +13899,7 @@ var FerricStoreFlowSupportClient = class extends FerricStoreManagementClient {
13513
13899
  };
13514
13900
 
13515
13901
  // src/flow-many-snapshot.ts
13516
- import { Buffer as Buffer41 } from "buffer";
13902
+ import { Buffer as Buffer44 } from "buffer";
13517
13903
  var encodedOptionFields = ["error", "payload", "reason", "result"];
13518
13904
  function snapshotCreateItem(item, codec) {
13519
13905
  const snapshot = {
@@ -13553,7 +13939,7 @@ function snapshotFlowManyOptions(options, codec) {
13553
13939
  }
13554
13940
  function snapshotClaimedItem(item) {
13555
13941
  const wire = item[CLAIMED_ITEM_WIRE];
13556
- const leaseToken = Buffer41.from(wire?.leaseToken ?? item.leaseToken);
13942
+ const leaseToken = Buffer44.from(wire?.leaseToken ?? item.leaseToken);
13557
13943
  const snapshot = {
13558
13944
  ...item,
13559
13945
  fencingToken: wire?.fencingToken ?? item.fencingToken,
@@ -13570,15 +13956,15 @@ function snapshotClaimedItem(item) {
13570
13956
  function snapshotFencedItem(item) {
13571
13957
  return Object.freeze({
13572
13958
  ...item,
13573
- ...item.leaseToken == null ? {} : { leaseToken: Buffer41.from(item.leaseToken) }
13959
+ ...item.leaseToken == null ? {} : { leaseToken: Buffer44.from(item.leaseToken) }
13574
13960
  });
13575
13961
  }
13576
13962
  function snapshotClaimedItemWire(wire, leaseToken) {
13577
13963
  return Object.freeze({
13578
13964
  fencingToken: wire.fencingToken,
13579
- id: Buffer41.from(wire.id),
13965
+ id: Buffer44.from(wire.id),
13580
13966
  leaseToken,
13581
- partitionKey: wire.partitionKey == null ? wire.partitionKey : Buffer41.from(wire.partitionKey)
13967
+ partitionKey: wire.partitionKey == null ? wire.partitionKey : Buffer44.from(wire.partitionKey)
13582
13968
  });
13583
13969
  }
13584
13970
  function snapshotArray(values) {
@@ -13589,7 +13975,7 @@ function snapshotArray(values) {
13589
13975
  return Object.freeze(snapshot);
13590
13976
  }
13591
13977
  function snapshotStateMeta(stateMeta) {
13592
- return snapshotRecord(stateMeta, (value) => Buffer41.isBuffer(value) ? Buffer41.from(value) : value);
13978
+ return snapshotRecord(stateMeta, (value) => Buffer44.isBuffer(value) ? Buffer44.from(value) : value);
13593
13979
  }
13594
13980
  function snapshotCommandRecord(values) {
13595
13981
  const seen = /* @__PURE__ */ new WeakMap();
@@ -13607,7 +13993,7 @@ function snapshotRecord(values, capture) {
13607
13993
  }
13608
13994
  function snapshotCommandArgument(value, seen) {
13609
13995
  if (typeof value !== "object" || value == null) return value;
13610
- if (Buffer41.isBuffer(value) || value instanceof Uint8Array) return Buffer41.from(value);
13996
+ if (Buffer44.isBuffer(value) || value instanceof Uint8Array) return Buffer44.from(value);
13611
13997
  const objectValue = value;
13612
13998
  const existing = seen.get(objectValue);
13613
13999
  if (existing != null) return existing;
@@ -14829,7 +15215,7 @@ async function runContinuousWorkerPool(options) {
14829
15215
  initialIdleSleepMs,
14830
15216
  nonNegativeDuration(options.maxIdleSleepMs, 5e3)
14831
15217
  );
14832
- const refillDelayMs = nonNegativeInteger(options.refillDelayMs, 0);
15218
+ const refillDelayMs = nonNegativeInteger2(options.refillDelayMs, 0);
14833
15219
  const active = /* @__PURE__ */ new Set();
14834
15220
  const pending = [];
14835
15221
  let pendingIndex = 0;
@@ -14948,7 +15334,7 @@ function normalizeWorkflowWorkerConfig(options) {
14948
15334
  function positiveInteger(value, fallback) {
14949
15335
  return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
14950
15336
  }
14951
- function nonNegativeInteger(value, fallback) {
15337
+ function nonNegativeInteger2(value, fallback) {
14952
15338
  return value == null || !Number.isFinite(value) ? fallback : Math.max(0, Math.trunc(value));
14953
15339
  }
14954
15340
  function nonNegativeDuration(value, fallback) {
@@ -16364,6 +16750,7 @@ var Workflow = class {
16364
16750
  }
16365
16751
  return await this.client.installPolicy(this.type, {
16366
16752
  ...options,
16753
+ replace: Object.hasOwn(options, "replace") ? options.replace ?? true : true,
16367
16754
  states
16368
16755
  });
16369
16756
  }
@@ -16576,8 +16963,8 @@ var WorkflowWorker = class {
16576
16963
  };
16577
16964
 
16578
16965
  // src/version.ts
16579
- var FERRICSTORE_SDK_VERSION = "0.2.0";
16580
- var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.8.0";
16966
+ var FERRICSTORE_SDK_VERSION = "0.3.0";
16967
+ var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.9.1";
16581
16968
  var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
16582
16969
  export {
16583
16970
  BitmapStore,
@@ -16606,6 +16993,7 @@ export {
16606
16993
  ListStore,
16607
16994
  LockHeldError,
16608
16995
  LockNotOwnedError,
16996
+ MAX_FLOW_POLICY_GENERATION,
16609
16997
  NativeAdapter,
16610
16998
  OverloadedError,
16611
16999
  Queue,
@@ -16620,6 +17008,7 @@ export {
16620
17008
  SetStore,
16621
17009
  SortedSetStore,
16622
17010
  StaleLeaseError,
17011
+ StalePolicyGenerationError,
16623
17012
  StreamStore,
16624
17013
  TDigestStore,
16625
17014
  TopKStore,