@ferricstore/ferricstore 0.5.1 → 0.11.4

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 (131) hide show
  1. package/README.md +44 -5
  2. package/dist/index.cjs +2162 -518
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +183 -34
  5. package/dist/index.d.ts +183 -34
  6. package/dist/index.js +2134 -490
  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/ConnectionClosedError.html +1 -1
  12. package/docs/api/classes/FerricStoreClient.html +3 -3
  13. package/docs/api/classes/FerricStoreError.html +1 -1
  14. package/docs/api/classes/FlowAlreadyExistsError.html +1 -1
  15. package/docs/api/classes/FlowBatchError.html +1 -1
  16. package/docs/api/classes/FlowNotFoundError.html +1 -1
  17. package/docs/api/classes/FlowQueryError.html +1 -1
  18. package/docs/api/classes/FlowWrongStateError.html +1 -1
  19. package/docs/api/classes/InvalidCommandError.html +1 -1
  20. package/docs/api/classes/LeaseRenewalError.html +1 -1
  21. package/docs/api/classes/LockHeldError.html +1 -1
  22. package/docs/api/classes/LockNotOwnedError.html +1 -1
  23. package/docs/api/classes/NativeAdapter.html +1 -1
  24. package/docs/api/classes/OverloadedError.html +1 -1
  25. package/docs/api/classes/QueueCompletionError.html +1 -1
  26. package/docs/api/classes/RequestTimeoutError.html +1 -1
  27. package/docs/api/classes/RerouteError.html +1 -1
  28. package/docs/api/classes/StaleLeaseError.html +1 -1
  29. package/docs/api/classes/StalePolicyGenerationError.html +1 -1
  30. package/docs/api/functions/projectFlowQuery.html +1 -1
  31. package/docs/api/hierarchy.html +1 -1
  32. package/docs/api/index.html +34 -5
  33. package/docs/api/interfaces/AdminListOptions.html +2 -2
  34. package/docs/api/interfaces/ApprovalListOptions.html +2 -2
  35. package/docs/api/interfaces/ApprovalRequestOptions.html +2 -2
  36. package/docs/api/interfaces/AttributeQueryOptions.html +2 -2
  37. package/docs/api/interfaces/AutoBatchOptions.html +2 -2
  38. package/docs/api/interfaces/BudgetCommitOptions.html +2 -2
  39. package/docs/api/interfaces/BudgetReserveOptions.html +2 -2
  40. package/docs/api/interfaces/CancelOptions.html +2 -2
  41. package/docs/api/interfaces/CircuitOpenOptions.html +2 -2
  42. package/docs/api/interfaces/ClaimDueOptions.html +3 -3
  43. package/docs/api/interfaces/ClaimHydrationItem.html +2 -2
  44. package/docs/api/interfaces/CompleteJobsAndClaimJobsResult.html +6 -6
  45. package/docs/api/interfaces/CompleteManyOptions.html +2 -2
  46. package/docs/api/interfaces/CompleteOptions.html +2 -2
  47. package/docs/api/interfaces/CompleteOutcome.html +1 -1
  48. package/docs/api/interfaces/CreateManyOptions.html +5 -5
  49. package/docs/api/interfaces/CreateOptions.html +3 -3
  50. package/docs/api/interfaces/EffectCompensateOptions.html +2 -2
  51. package/docs/api/interfaces/EffectConfirmOptions.html +2 -2
  52. package/docs/api/interfaces/EffectFailOptions.html +2 -2
  53. package/docs/api/interfaces/EffectReserveOptions.html +2 -2
  54. package/docs/api/interfaces/EffectStatusOptions.html +2 -2
  55. package/docs/api/interfaces/ExtendLeaseOptions.html +2 -2
  56. package/docs/api/interfaces/FailOptions.html +2 -2
  57. package/docs/api/interfaces/FailOutcome.html +1 -1
  58. package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +4 -4
  59. package/docs/api/interfaces/FerricStoreClientOptions.html +4 -4
  60. package/docs/api/interfaces/FlowBatchCompletedItem.html +2 -2
  61. package/docs/api/interfaces/FlowEventProjectionField.html +2 -2
  62. package/docs/api/interfaces/FlowExplainCapabilities.html +5 -0
  63. package/docs/api/interfaces/FlowExplainResult.html +8 -2
  64. package/docs/api/interfaces/FlowPolicyOptions.html +5 -5
  65. package/docs/api/interfaces/FlowQueryCountResult.html +1 -1
  66. package/docs/api/interfaces/FlowQueryIndex.html +11 -2
  67. package/docs/api/interfaces/FlowQueryIndexBuild.html +10 -0
  68. package/docs/api/interfaces/FlowQueryIndexCoverage.html +5 -0
  69. package/docs/api/interfaces/FlowQueryIndexField.html +5 -0
  70. package/docs/api/interfaces/FlowQueryIndexFormat.html +2 -2
  71. package/docs/api/interfaces/FlowQueryIndexProgress.html +7 -0
  72. package/docs/api/interfaces/FlowQueryIndexRegistry.html +2 -2
  73. package/docs/api/interfaces/FlowQueryIndexRetirement.html +10 -0
  74. package/docs/api/interfaces/FlowQueryIndexServices.html +6 -0
  75. package/docs/api/interfaces/FlowQueryIndexStatistics.html +11 -0
  76. package/docs/api/interfaces/FlowQueryIndexStatus.html +2 -2
  77. package/docs/api/interfaces/FlowQueryIndexValidation.html +13 -0
  78. package/docs/api/interfaces/FlowQueryQuality.html +2 -2
  79. package/docs/api/interfaces/FlowQueryRecordsResult.html +1 -1
  80. package/docs/api/interfaces/FlowRunProjectionField.html +2 -2
  81. package/docs/api/interfaces/FlowStatePolicy.html +2 -2
  82. package/docs/api/interfaces/FlowStatsOptions.html +2 -2
  83. package/docs/api/interfaces/GovernanceLedgerOptions.html +2 -2
  84. package/docs/api/interfaces/HistoryOptions.html +2 -2
  85. package/docs/api/interfaces/InvocationCreateOptions.html +2 -2
  86. package/docs/api/interfaces/LeaseMutationOptions.html +2 -2
  87. package/docs/api/interfaces/LimitAmountOptions.html +2 -2
  88. package/docs/api/interfaces/LimitLeaseOptions.html +2 -2
  89. package/docs/api/interfaces/LimitListOptions.html +2 -2
  90. package/docs/api/interfaces/LimitReleaseOptions.html +2 -2
  91. package/docs/api/interfaces/MutateOptions.html +2 -2
  92. package/docs/api/interfaces/NamedValueMutation.html +1 -1
  93. package/docs/api/interfaces/ReadOptions.html +3 -3
  94. package/docs/api/interfaces/ReclaimOptions.html +2 -2
  95. package/docs/api/interfaces/RequestContext.html +2 -2
  96. package/docs/api/interfaces/RequestContextOptions.html +2 -2
  97. package/docs/api/interfaces/RetryOptions.html +2 -2
  98. package/docs/api/interfaces/RetryOutcome.html +1 -1
  99. package/docs/api/interfaces/RunStepsItem.html +2 -2
  100. package/docs/api/interfaces/RunStepsManyOptions.html +2 -2
  101. package/docs/api/interfaces/ScheduleFireDueOptions.html +3 -2
  102. package/docs/api/interfaces/ScheduleFireOptions.html +3 -0
  103. package/docs/api/interfaces/ScheduleListOptions.html +2 -2
  104. package/docs/api/interfaces/ScheduleOptions.html +4 -2
  105. package/docs/api/interfaces/SearchOptions.html +3 -3
  106. package/docs/api/interfaces/StartAndClaimOptions.html +3 -3
  107. package/docs/api/interfaces/StepContinueOptions.html +3 -3
  108. package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
  109. package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
  110. package/docs/api/interfaces/TransitionOptions.html +2 -2
  111. package/docs/api/interfaces/TransitionOutcome.html +1 -1
  112. package/docs/api/modules.html +1 -1
  113. package/docs/api/types/ConnectionRequestDisposition.html +1 -1
  114. package/docs/api/types/FlowProjectionField.html +1 -1
  115. package/docs/api/types/FlowProjectionShape.html +1 -1
  116. package/docs/api/types/FlowQueryIndexServiceState.html +1 -0
  117. package/docs/api/types/FlowStateMode.html +1 -1
  118. package/docs/api/types/FlowStatePolicyLike.html +1 -1
  119. package/docs/api/types/ManagementPairs.html +1 -1
  120. package/docs/api/types/ScheduleCatchupPolicy.html +2 -0
  121. package/docs/api/types/ScheduleFireDueResult.html +3 -0
  122. package/docs/api/types/ScheduleFireResult.html +2 -0
  123. package/docs/api/types/ScheduleKind.html +1 -0
  124. package/docs/api/types/ScheduleOverlapPolicy.html +1 -0
  125. package/docs/api/types/ScheduleRecord.html +12 -0
  126. package/docs/api/types/ScheduleState.html +1 -0
  127. package/docs/api/types/SearchStateMeta.html +1 -1
  128. package/docs/api/variables/FERRICSTORE_MINIMUM_SERVER_VERSION.html +1 -1
  129. package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
  130. package/docs/api/variables/FlowProjection.html +1 -1
  131. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -85,7 +85,7 @@ __export(index_exports, {
85
85
  WorkflowWorker: () => WorkflowWorker,
86
86
  classifyServerError: () => classifyServerError,
87
87
  complete: () => complete,
88
- fail: () => fail,
88
+ fail: () => fail2,
89
89
  isOutcome: () => isOutcome,
90
90
  isReconnectableClosedConnectionError: () => isReconnectableClosedConnectionError,
91
91
  mapException: () => mapException,
@@ -107,7 +107,7 @@ function assertSafeVariadicDispatch(argumentCount, arrayCapability) {
107
107
  // src/native-adapter.ts
108
108
  var import_node_net3 = require("net");
109
109
  var import_node_tls3 = require("tls");
110
- var import_node_buffer31 = require("buffer");
110
+ var import_node_buffer32 = require("buffer");
111
111
 
112
112
  // src/errors.ts
113
113
  var FerricStoreError = class extends Error {
@@ -1237,10 +1237,10 @@ var NativeFlowControl = class {
1237
1237
 
1238
1238
  // src/native-heartbeat.ts
1239
1239
  var NativeHeartbeat = class {
1240
- constructor(intervalMs, send, fail2) {
1240
+ constructor(intervalMs, send, fail3) {
1241
1241
  this.intervalMs = intervalMs;
1242
1242
  this.send = send;
1243
- this.fail = fail2;
1243
+ this.fail = fail3;
1244
1244
  }
1245
1245
  intervalMs;
1246
1246
  send;
@@ -1287,9 +1287,11 @@ var FLOW_QUERY_LANGUAGE_VERSION = "FQL1";
1287
1287
  var FLOW_QUERY_MAX_BYTES = 16 * 1024;
1288
1288
  var FLOW_QUERY_MAX_PARAMETERS = 64;
1289
1289
  var FLOW_QUERY_MAX_PARAMETER_NAME_BYTES = 128;
1290
+ var FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES = 65535;
1290
1291
  var MIN_I64 = -(1n << 63n);
1291
1292
  var MAX_I64 = (1n << 63n) - 1n;
1292
1293
  var INDEX_ID = /^[A-Za-z0-9_.:-]{1,64}$/u;
1294
+ var PARAMETER_NAME = /^[A-Za-z0-9_.-]+$/u;
1293
1295
  function flowQueryArgs(query, params = {}) {
1294
1296
  validateFlowQueryText(query);
1295
1297
  const entries = flowQueryParameterEntries(params);
@@ -1343,9 +1345,34 @@ function validateFlowQueryIndexId(indexId) {
1343
1345
  }
1344
1346
  }
1345
1347
  function hasFlowExplainPrefix(query) {
1346
- const source = query.trimStart();
1347
- const prefix = source.slice(0, 7);
1348
- return prefix.toUpperCase() === "EXPLAIN" && (source.length === 7 || /[\t\n\r ]/u.test(source[7] ?? ""));
1348
+ const source = trimFlowQueryAsciiWhitespaceStart(query);
1349
+ return matchesFlowQueryAsciiKeyword(source, 0, "EXPLAIN") && (source.length === 7 || isFlowQueryAsciiWhitespaceCode(source.charCodeAt(7)));
1350
+ }
1351
+ function matchesFlowQueryAsciiKeyword(value, offset, keyword) {
1352
+ if (offset < 0 || offset + keyword.length > value.length) return false;
1353
+ for (let index = 0; index < keyword.length; index += 1) {
1354
+ let actual = value.charCodeAt(offset + index);
1355
+ if (actual >= 97 && actual <= 122) actual -= 32;
1356
+ if (actual !== keyword.charCodeAt(index)) return false;
1357
+ }
1358
+ return true;
1359
+ }
1360
+ function trimFlowQueryAsciiWhitespaceStart(value) {
1361
+ let start = 0;
1362
+ while (start < value.length && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(start))) {
1363
+ start += 1;
1364
+ }
1365
+ return start === 0 ? value : value.slice(start);
1366
+ }
1367
+ function trimFlowQueryAsciiWhitespace(value) {
1368
+ let start = 0;
1369
+ let end = value.length;
1370
+ while (start < end && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(start))) start += 1;
1371
+ while (end > start && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(end - 1))) end -= 1;
1372
+ return start === 0 && end === value.length ? value : value.slice(start, end);
1373
+ }
1374
+ function isFlowQueryAsciiWhitespaceCode(code) {
1375
+ return code === 32 || code === 9 || code === 10 || code === 13;
1349
1376
  }
1350
1377
  function flowQueryParameterEntries(params) {
1351
1378
  if (typeof params !== "object" || params == null || Array.isArray(params)) {
@@ -1371,19 +1398,26 @@ function flowQueryParameterEntries(params) {
1371
1398
  function validateFlowQueryParameterName(name) {
1372
1399
  validateUnicodeScalarText(name, "FLOW.QUERY parameter name");
1373
1400
  const size = import_node_buffer3.Buffer.byteLength(name, "utf8");
1374
- if (size === 0 || size > FLOW_QUERY_MAX_PARAMETER_NAME_BYTES) {
1401
+ if (size === 0 || size > FLOW_QUERY_MAX_PARAMETER_NAME_BYTES || !PARAMETER_NAME.test(name)) {
1375
1402
  throw new TypeError(
1376
- `FLOW.QUERY parameter names must be 1..${FLOW_QUERY_MAX_PARAMETER_NAME_BYTES} bytes`
1403
+ `FLOW.QUERY parameter names must be 1..${FLOW_QUERY_MAX_PARAMETER_NAME_BYTES} ASCII letters, digits, '_', '.', or '-'`
1377
1404
  );
1378
1405
  }
1379
1406
  }
1380
1407
  function normalizeFlowQueryParameter(value, name) {
1381
1408
  if (typeof value === "string") {
1382
1409
  validateUnicodeScalarText(value, `FLOW.QUERY parameter ${JSON.stringify(name)}`);
1410
+ validateParameterValueSize(import_node_buffer3.Buffer.byteLength(value, "utf8"), name);
1411
+ return value;
1412
+ }
1413
+ if (import_node_buffer3.Buffer.isBuffer(value)) {
1414
+ validateParameterValueSize(value.byteLength, name);
1383
1415
  return value;
1384
1416
  }
1385
- if (import_node_buffer3.Buffer.isBuffer(value)) return value;
1386
- if (value instanceof Uint8Array) return import_node_buffer3.Buffer.from(value);
1417
+ if (value instanceof Uint8Array) {
1418
+ validateParameterValueSize(value.byteLength, name);
1419
+ return import_node_buffer3.Buffer.from(value);
1420
+ }
1387
1421
  if (typeof value === "boolean") return value;
1388
1422
  if (typeof value === "bigint" && value >= MIN_I64 && value <= MAX_I64) return value;
1389
1423
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -1393,6 +1427,13 @@ function normalizeFlowQueryParameter(value, name) {
1393
1427
  `FLOW.QUERY parameter ${JSON.stringify(name)} must be text, bytes, boolean, a finite float, or a signed 64-bit integer`
1394
1428
  );
1395
1429
  }
1430
+ function validateParameterValueSize(size, name) {
1431
+ if (size > FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES) {
1432
+ throw new TypeError(
1433
+ `FLOW.QUERY parameter ${JSON.stringify(name)} exceeds ${FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES} bytes`
1434
+ );
1435
+ }
1436
+ }
1396
1437
  function commandText(value, context) {
1397
1438
  if (typeof value === "string") {
1398
1439
  validateUnicodeScalarText(value, context);
@@ -3146,6 +3187,7 @@ var FLOW_ADMIN_FIELDS = {
3146
3187
  AT_MS: "at_ms",
3147
3188
  BLOCK: "block_ms",
3148
3189
  BLOCK_MS: "block_ms",
3190
+ CATCHUP_POLICY: "catchup_policy",
3149
3191
  CONSISTENT_PROJECTION: "consistent_projection",
3150
3192
  CORRELATION_ID: "correlation_id",
3151
3193
  COUNT: "count",
@@ -5579,15 +5621,15 @@ function requiredMap(value, context) {
5579
5621
  }
5580
5622
  throw incompatibleServer(`${context} is missing or invalid`);
5581
5623
  }
5582
- function requiredBoundedText(mapping, name) {
5583
- const value = strictText(field(mapping, name));
5624
+ function requiredBoundedText(mapping2, name) {
5625
+ const value = strictText(field(mapping2, name));
5584
5626
  if (value == null || value.length === 0 || import_node_buffer20.Buffer.byteLength(value) > 256) {
5585
5627
  throw incompatibleServer(`HELLO flow_query ${name} must be bounded non-empty text`);
5586
5628
  }
5587
5629
  return value;
5588
5630
  }
5589
- function boundedTextSet(mapping, name, maximum) {
5590
- const raw = field(mapping, name);
5631
+ function boundedTextSet(mapping2, name, maximum) {
5632
+ const raw = field(mapping2, name);
5591
5633
  if (!Array.isArray(raw) || raw.length < 1 || raw.length > maximum) {
5592
5634
  throw incompatibleServer(`HELLO ${name} must contain 1..${maximum} entries`);
5593
5635
  }
@@ -5864,7 +5906,10 @@ function isDecodedRecord(value) {
5864
5906
  // src/protocol-flow-query-result.ts
5865
5907
  var CONTRACT = "ferric.flow.query.result/v1";
5866
5908
  var MAX_RECORDS = 100;
5909
+ var MIN_CURSOR_BYTES = 16;
5867
5910
  var MAX_CURSOR_BYTES = 4096;
5911
+ var CURSOR_PREFIX = import_node_buffer24.Buffer.from("fqc1_");
5912
+ var MAX_SIGNED_64 = (1n << 63n) - 1n;
5868
5913
  var RECORD_FIELDS = [
5869
5914
  "id",
5870
5915
  "type",
@@ -5928,9 +5973,13 @@ function decodeCompactFlowQueryResult(data, budget) {
5928
5973
  const usage = {};
5929
5974
  for (const field3 of USAGE_FIELDS) {
5930
5975
  const read = readU64(data, offset);
5976
+ if (BigInt(read.value) > MAX_SIGNED_64) {
5977
+ throw new FerricStoreError("compact FLOW.QUERY usage exceeds signed 64-bit range");
5978
+ }
5931
5979
  usage[field3] = read.value;
5932
5980
  offset = read.offset;
5933
5981
  }
5982
+ validateCompactUsage(usage);
5934
5983
  let result;
5935
5984
  if (kind === 0) {
5936
5985
  const page = readPage(data, offset);
@@ -5939,6 +5988,7 @@ function decodeCompactFlowQueryResult(data, budget) {
5939
5988
  const count = data.readUInt32BE(offset);
5940
5989
  offset += 4;
5941
5990
  if (count > MAX_RECORDS) throw new FerricStoreError("compact FLOW.QUERY page exceeds 100 records");
5991
+ validateCompactRecordUsage(usage, count);
5942
5992
  consumeDecodeItems(count, budget);
5943
5993
  const records = new Array(count);
5944
5994
  for (let index = 0; index < count; index += 1) {
@@ -5948,7 +5998,13 @@ function decodeCompactFlowQueryResult(data, budget) {
5948
5998
  }
5949
5999
  result = { version: import_node_buffer24.Buffer.from(CONTRACT), records, page: page.value, quality, usage };
5950
6000
  } else if (kind === 1) {
6001
+ if (usageInteger(usage, "result_records") !== 1n) {
6002
+ throw new FerricStoreError("compact FLOW.QUERY count usage is inconsistent");
6003
+ }
5951
6004
  const count = readU64(data, offset);
6005
+ if (BigInt(count.value) > MAX_SIGNED_64) {
6006
+ throw new FerricStoreError("compact FLOW.QUERY count exceeds signed 64-bit range");
6007
+ }
5952
6008
  offset = count.offset;
5953
6009
  result = {
5954
6010
  version: import_node_buffer24.Buffer.from(CONTRACT),
@@ -5973,18 +6029,37 @@ function readPage(data, offset) {
5973
6029
  if (hasMoreCode === 0 && size === NULL_U32) {
5974
6030
  return { value: { has_more: false, cursor: null }, offset };
5975
6031
  }
5976
- if (hasMoreCode !== 1 || size === 0 || size === NULL_U32 || size > MAX_CURSOR_BYTES) {
6032
+ if (hasMoreCode !== 1 || size < MIN_CURSOR_BYTES || size === NULL_U32 || size > MAX_CURSOR_BYTES) {
5977
6033
  throw new FerricStoreError("invalid compact FLOW.QUERY page cursor");
5978
6034
  }
5979
6035
  requireAvailable(data, offset, size);
6036
+ const cursor = data.subarray(offset, offset + size);
6037
+ if (!cursor.subarray(0, CURSOR_PREFIX.byteLength).equals(CURSOR_PREFIX) || !(0, import_node_buffer24.isUtf8)(cursor)) {
6038
+ throw new FerricStoreError("invalid compact FLOW.QUERY page cursor");
6039
+ }
5980
6040
  return {
5981
6041
  value: {
5982
6042
  has_more: true,
5983
- cursor: detachDecodedBinary(data.subarray(offset, offset + size), data.byteLength)
6043
+ cursor: detachDecodedBinary(cursor, data.byteLength)
5984
6044
  },
5985
6045
  offset: offset + size
5986
6046
  };
5987
6047
  }
6048
+ function validateCompactUsage(usage) {
6049
+ const scanned = usageInteger(usage, "scanned_entries");
6050
+ if (usageInteger(usage, "hydrated_records") > scanned || usageInteger(usage, "duplicate_entries") > scanned || usageInteger(usage, "range_pages") > scanned + usageInteger(usage, "range_seeks") || usageInteger(usage, "residual_checks") > scanned * 12n) {
6051
+ throw new FerricStoreError("compact FLOW.QUERY usage counters are inconsistent");
6052
+ }
6053
+ }
6054
+ function validateCompactRecordUsage(usage, count) {
6055
+ const resultRecords = usageInteger(usage, "result_records");
6056
+ if (resultRecords !== BigInt(count) || resultRecords > usageInteger(usage, "scanned_entries")) {
6057
+ throw new FerricStoreError("compact FLOW.QUERY record usage is inconsistent");
6058
+ }
6059
+ }
6060
+ function usageInteger(usage, field3) {
6061
+ return BigInt(usage[field3]);
6062
+ }
5988
6063
  function readRecord(data, offset, budget) {
5989
6064
  requireAvailable(data, offset, 4);
5990
6065
  const bitmap = data.readUInt32BE(offset);
@@ -6877,15 +6952,15 @@ function snapshotOwnStringArray(values, name) {
6877
6952
 
6878
6953
  // src/client-native-options.ts
6879
6954
  function snapshotNativeClientOptions(options) {
6880
- const own = { ...options };
6955
+ const own2 = { ...options };
6881
6956
  const snapshot = {
6882
- ...own,
6883
- ...typeof own.autoReconnect === "object" && own.autoReconnect != null ? { autoReconnect: Object.freeze({ ...own.autoReconnect }) } : {},
6884
- ...own.endpointPolicy == null ? {} : { endpointPolicy: snapshotEndpointPolicy(own.endpointPolicy) },
6885
- ...own.events == null ? {} : { events: snapshotOwnStringArray(own.events, "events") },
6886
- ...own.seeds == null ? {} : { seeds: snapshotOwnStringArray(own.seeds, "seeds") },
6887
- ...own.tlsOptions == null ? {} : { tlsOptions: snapshotTlsOptions(own.tlsOptions) },
6888
- ...own.trustedHosts == null ? {} : { trustedHosts: snapshotOwnStringArray(own.trustedHosts, "trustedHosts") }
6957
+ ...own2,
6958
+ ...typeof own2.autoReconnect === "object" && own2.autoReconnect != null ? { autoReconnect: Object.freeze({ ...own2.autoReconnect }) } : {},
6959
+ ...own2.endpointPolicy == null ? {} : { endpointPolicy: snapshotEndpointPolicy(own2.endpointPolicy) },
6960
+ ...own2.events == null ? {} : { events: snapshotOwnStringArray(own2.events, "events") },
6961
+ ...own2.seeds == null ? {} : { seeds: snapshotOwnStringArray(own2.seeds, "seeds") },
6962
+ ...own2.tlsOptions == null ? {} : { tlsOptions: snapshotTlsOptions(own2.tlsOptions) },
6963
+ ...own2.trustedHosts == null ? {} : { trustedHosts: snapshotOwnStringArray(own2.trustedHosts, "trustedHosts") }
6889
6964
  };
6890
6965
  return Object.freeze(snapshot);
6891
6966
  }
@@ -6926,12 +7001,12 @@ function topologyNativeOptions(options) {
6926
7001
  }
6927
7002
  function snapshotEndpointPolicy(policy) {
6928
7003
  if (typeof policy !== "object") return policy;
6929
- const own = { ...policy };
6930
- if (!Object.hasOwn(own, "allowHosts") || !Array.isArray(own.allowHosts)) {
7004
+ const own2 = { ...policy };
7005
+ if (!Object.hasOwn(own2, "allowHosts") || !Array.isArray(own2.allowHosts)) {
6931
7006
  throw new TypeError("endpointPolicy.allowHosts must be an own array of strings");
6932
7007
  }
6933
7008
  return Object.freeze({
6934
- allowHosts: snapshotOwnStringArray(own.allowHosts, "endpointPolicy.allowHosts")
7009
+ allowHosts: snapshotOwnStringArray(own2.allowHosts, "endpointPolicy.allowHosts")
6935
7010
  });
6936
7011
  }
6937
7012
  function snapshotTlsOptions(options) {
@@ -8010,6 +8085,63 @@ function nativeStartupPayload(clientName, events) {
8010
8085
  };
8011
8086
  }
8012
8087
 
8088
+ // src/flow-query-deadline.ts
8089
+ var import_node_buffer31 = require("buffer");
8090
+ function withFlowQueryDeadline(command, timeoutMs, nowMs2) {
8091
+ if (timeoutMs == null) return command;
8092
+ if (command.opcode === OPCODES.pipeline) {
8093
+ return withPipelineQueryDeadlines(command, timeoutMs, nowMs2);
8094
+ }
8095
+ if (command.opcode !== OPCODES.flowQuery) return command;
8096
+ const payload = command.payload;
8097
+ if (payload instanceof Map) {
8098
+ const nextPayload = new Map(payload);
8099
+ nextPayload.set("deadline_ms", flowQueryDeadlineMs(timeoutMs, nowMs2));
8100
+ return { ...command, payload: nextPayload };
8101
+ }
8102
+ if (typeof payload !== "object" || payload == null || Array.isArray(payload) || import_node_buffer31.Buffer.isBuffer(payload) || payload instanceof Uint8Array) {
8103
+ return command;
8104
+ }
8105
+ return {
8106
+ ...command,
8107
+ payload: {
8108
+ ...payload,
8109
+ deadline_ms: flowQueryDeadlineMs(timeoutMs, nowMs2)
8110
+ }
8111
+ };
8112
+ }
8113
+ function withPipelineQueryDeadlines(command, timeoutMs, nowMs2) {
8114
+ const payload = plainRecord(command.payload);
8115
+ if (payload == null) return command;
8116
+ const rawCommands = payload.commands;
8117
+ if (!Array.isArray(rawCommands)) return command;
8118
+ let commands;
8119
+ let deadlineMs;
8120
+ for (let index = 0; index < rawCommands.length; index += 1) {
8121
+ const rawItem = rawCommands[index];
8122
+ const item = plainRecord(rawItem);
8123
+ if (item?.opcode !== OPCODES.flowQuery) continue;
8124
+ const body = plainRecord(item.body);
8125
+ if (body == null) continue;
8126
+ commands ??= rawCommands.slice();
8127
+ deadlineMs ??= flowQueryDeadlineMs(timeoutMs, nowMs2);
8128
+ commands[index] = { ...item, body: { ...body, deadline_ms: deadlineMs } };
8129
+ }
8130
+ return commands == null ? command : { ...command, payload: { ...payload, commands } };
8131
+ }
8132
+ function flowQueryDeadlineMs(timeoutMs, nowMs2) {
8133
+ return Math.min(
8134
+ Number.MAX_SAFE_INTEGER,
8135
+ (nowMs2 ?? Date.now()) + Math.max(1, Math.ceil(timeoutMs))
8136
+ );
8137
+ }
8138
+ function plainRecord(value) {
8139
+ if (typeof value !== "object" || value == null || Array.isArray(value) || import_node_buffer31.Buffer.isBuffer(value) || value instanceof Uint8Array) {
8140
+ return void 0;
8141
+ }
8142
+ return value;
8143
+ }
8144
+
8013
8145
  // src/native-adapter.ts
8014
8146
  var NativeAdapter = class _NativeAdapter {
8015
8147
  socket;
@@ -8215,7 +8347,11 @@ var NativeAdapter = class _NativeAdapter {
8215
8347
  let frame;
8216
8348
  try {
8217
8349
  requestId = this.requestScheduler.nextRequestId();
8218
- frame = encodeRequest(command, requestId, this.capabilities.requestFrameBytes);
8350
+ frame = encodeRequest(
8351
+ withFlowQueryDeadline(command, timeoutMs),
8352
+ requestId,
8353
+ this.capabilities.requestFrameBytes
8354
+ );
8219
8355
  } catch (error) {
8220
8356
  if (hasFlowControlCredit) this.flowControl.release(laneId);
8221
8357
  return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
@@ -8360,10 +8496,10 @@ function executeCommandArgs(executor, args) {
8360
8496
  }
8361
8497
 
8362
8498
  // src/reconnecting-executor.ts
8363
- var import_node_buffer33 = require("buffer");
8499
+ var import_node_buffer34 = require("buffer");
8364
8500
 
8365
8501
  // src/command-retry-policy.ts
8366
- var import_node_buffer32 = require("buffer");
8502
+ var import_node_buffer33 = require("buffer");
8367
8503
  function isCasMutation(args) {
8368
8504
  const offset = commandName2(args[0]) === "COMMAND_EXEC" ? 1 : 0;
8369
8505
  const name = commandName2(args[offset]);
@@ -8377,8 +8513,8 @@ function isCasMutation(args) {
8377
8513
  }
8378
8514
  function commandName2(value) {
8379
8515
  if (typeof value === "string") return value.toUpperCase();
8380
- if (import_node_buffer32.Buffer.isBuffer(value) || value instanceof Uint8Array) {
8381
- return import_node_buffer32.Buffer.from(value).toString("utf8").toUpperCase();
8516
+ if (import_node_buffer33.Buffer.isBuffer(value) || value instanceof Uint8Array) {
8517
+ return import_node_buffer33.Buffer.from(value).toString("utf8").toUpperCase();
8382
8518
  }
8383
8519
  return void 0;
8384
8520
  }
@@ -8590,15 +8726,15 @@ function normalizeNonNegativeInteger2(value, fallback) {
8590
8726
  }
8591
8727
 
8592
8728
  // src/topology.ts
8593
- var import_node_buffer36 = require("buffer");
8729
+ var import_node_buffer37 = require("buffer");
8594
8730
 
8595
8731
  // src/topology-routing.ts
8596
- var import_node_buffer35 = require("buffer");
8732
+ var import_node_buffer36 = require("buffer");
8597
8733
 
8598
8734
  // src/flow-partition-route-cache.ts
8599
- var import_node_buffer34 = require("buffer");
8735
+ var import_node_buffer35 = require("buffer");
8600
8736
  var import_node_crypto = require("crypto");
8601
- var AUTO_PREFIX = import_node_buffer34.Buffer.from("__flow_auto__:", "ascii");
8737
+ var AUTO_PREFIX = import_node_buffer35.Buffer.from("__flow_auto__:", "ascii");
8602
8738
  var MAX_CACHEABLE_PARTITION_BYTES = 4 * 1024;
8603
8739
  var MAX_CACHE_BYTES = 1 * 1024 * 1024;
8604
8740
  var MAX_CACHE_ENTRIES = 1024;
@@ -8608,10 +8744,10 @@ var cacheBytes = 0;
8608
8744
  var cacheHits = 0;
8609
8745
  var cacheMisses = 0;
8610
8746
  function flowLogicalPartitionRoutingKey(value) {
8611
- if (typeof value !== "string" && !import_node_buffer34.Buffer.isBuffer(value)) return void 0;
8747
+ if (typeof value !== "string" && !import_node_buffer35.Buffer.isBuffer(value)) return void 0;
8612
8748
  const autoBucket = flowAutoBucket(value);
8613
8749
  if (autoBucket != null) return `{fa:${autoBucket}}`;
8614
- const bytes2 = import_node_buffer34.Buffer.isBuffer(value) ? value : import_node_buffer34.Buffer.from(value);
8750
+ const bytes2 = import_node_buffer35.Buffer.isBuffer(value) ? value : import_node_buffer35.Buffer.from(value);
8615
8751
  if (bytes2.byteLength > MAX_CACHEABLE_PARTITION_BYTES) return hashRoute(bytes2);
8616
8752
  const cacheKey = bytes2.toString("base64");
8617
8753
  const cached = routeCache.get(cacheKey);
@@ -8685,7 +8821,7 @@ function routingKeyFromProtocolPayload(name, command) {
8685
8821
  "scope"
8686
8822
  ]) {
8687
8823
  const value = getField(command.payload, field3);
8688
- if (typeof value === "string" || import_node_buffer35.Buffer.isBuffer(value)) {
8824
+ if (typeof value === "string" || import_node_buffer36.Buffer.isBuffer(value)) {
8689
8825
  return value;
8690
8826
  }
8691
8827
  }
@@ -8733,7 +8869,7 @@ function flowRoutingData(name, args) {
8733
8869
  if (typeof partition === "string" && partition.toUpperCase() !== "AUTO" && partition.toUpperCase() !== "MIXED") {
8734
8870
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
8735
8871
  }
8736
- if (import_node_buffer35.Buffer.isBuffer(partition)) {
8872
+ if (import_node_buffer36.Buffer.isBuffer(partition)) {
8737
8873
  const text3 = partition.toString("utf8").toUpperCase();
8738
8874
  if (text3 !== "AUTO" && text3 !== "MIXED") {
8739
8875
  return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
@@ -8824,17 +8960,17 @@ function flowPartitionRoutingKeyFromCommand(command, claim) {
8824
8960
  return { handled: false };
8825
8961
  }
8826
8962
  function isRoutingKey(value) {
8827
- return typeof value === "string" || import_node_buffer35.Buffer.isBuffer(value);
8963
+ return typeof value === "string" || import_node_buffer36.Buffer.isBuffer(value);
8828
8964
  }
8829
8965
  function flowAutoIdRoutingKey(value) {
8830
- if (typeof value !== "string" && !import_node_buffer35.Buffer.isBuffer(value)) {
8966
+ if (typeof value !== "string" && !import_node_buffer36.Buffer.isBuffer(value)) {
8831
8967
  return void 0;
8832
8968
  }
8833
- const bucket = (import_node_buffer35.Buffer.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
8969
+ const bucket = (import_node_buffer36.Buffer.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
8834
8970
  return `{fa:${bucket}}`;
8835
8971
  }
8836
8972
  function flowClaimLogicalPartitionRoutingKey(value) {
8837
- if (typeof value !== "string" && !import_node_buffer35.Buffer.isBuffer(value)) return void 0;
8973
+ if (typeof value !== "string" && !import_node_buffer36.Buffer.isBuffer(value)) return void 0;
8838
8974
  const selector = commandPart(value);
8839
8975
  if (selector === "AUTO" || selector === "ANY") return void 0;
8840
8976
  if (selector === "GLOBAL") return "{f}";
@@ -8849,7 +8985,7 @@ function singleShardFlowClaimPartitionKey(values) {
8849
8985
  return keys.some((key) => key == null) ? void 0 : singleShardKey(keys);
8850
8986
  }
8851
8987
  function singleShardKey(keys) {
8852
- if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !import_node_buffer35.Buffer.isBuffer(key))) {
8988
+ if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !import_node_buffer36.Buffer.isBuffer(key))) {
8853
8989
  return void 0;
8854
8990
  }
8855
8991
  const usable = keys;
@@ -8882,7 +9018,7 @@ function routedKeyGroups(keys, routeKey) {
8882
9018
  const groups = /* @__PURE__ */ new Map();
8883
9019
  for (let index = 0; index < keys.length; index += 1) {
8884
9020
  const key = keys[index];
8885
- if (typeof key !== "string" && !import_node_buffer35.Buffer.isBuffer(key)) return void 0;
9021
+ if (typeof key !== "string" && !import_node_buffer36.Buffer.isBuffer(key)) return void 0;
8886
9022
  const route = routeKey(key);
8887
9023
  const groupKey = `${route.endpointKey}\0${route.laneId}`;
8888
9024
  const group = groups.get(groupKey);
@@ -9971,7 +10107,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
9971
10107
  };
9972
10108
 
9973
10109
  // src/response-map-preservation.ts
9974
- var import_node_buffer37 = require("buffer");
10110
+ var import_node_buffer38 = require("buffer");
9975
10111
  function toStringKeyMapPreservingValues(value) {
9976
10112
  if (value == null) return void 0;
9977
10113
  const result = {};
@@ -9981,7 +10117,7 @@ function toStringKeyMapPreservingValues(value) {
9981
10117
  }
9982
10118
  return result;
9983
10119
  }
9984
- if (typeof value === "object" && !Array.isArray(value) && !import_node_buffer37.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
10120
+ if (typeof value === "object" && !Array.isArray(value) && !import_node_buffer38.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
9985
10121
  for (const [key, item] of Object.entries(value)) {
9986
10122
  setOwnValue(result, text(key), normalizeMapStructurePreservingBytes(item));
9987
10123
  }
@@ -9990,7 +10126,7 @@ function toStringKeyMapPreservingValues(value) {
9990
10126
  return void 0;
9991
10127
  }
9992
10128
  function normalizeMapStructurePreservingBytes(value) {
9993
- if (import_node_buffer37.Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
10129
+ if (import_node_buffer38.Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
9994
10130
  if (value instanceof Map) {
9995
10131
  const result = {};
9996
10132
  for (const [key, item] of value.entries()) {
@@ -10012,7 +10148,7 @@ function normalizeMapStructurePreservingBytes(value) {
10012
10148
  }
10013
10149
 
10014
10150
  // src/native-kv-responses.ts
10015
- var import_node_buffer38 = require("buffer");
10151
+ var import_node_buffer39 = require("buffer");
10016
10152
  function rateLimitResultFromResp(value) {
10017
10153
  if (!Array.isArray(value) || value.length !== 4) {
10018
10154
  throw new TypeError("RATELIMIT.ADD returned an unexpected response");
@@ -10069,13 +10205,13 @@ function fetchOrComputeResultFromResp(value, codec) {
10069
10205
  }
10070
10206
  function decodePayload(codec, value) {
10071
10207
  if (value == null) return null;
10072
- if (import_node_buffer38.Buffer.isBuffer(value)) return codec.decode(value);
10073
- if (value instanceof Uint8Array) return codec.decode(import_node_buffer38.Buffer.from(value));
10074
- if (typeof value === "string") return codec.decode(import_node_buffer38.Buffer.from(value));
10208
+ if (import_node_buffer39.Buffer.isBuffer(value)) return codec.decode(value);
10209
+ if (value instanceof Uint8Array) return codec.decode(import_node_buffer39.Buffer.from(value));
10210
+ if (typeof value === "string") return codec.decode(import_node_buffer39.Buffer.from(value));
10075
10211
  return normalizeRefMeta(value);
10076
10212
  }
10077
10213
  function requiredResponseString(value, context) {
10078
- if (typeof value !== "string" && !import_node_buffer38.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
10214
+ if (typeof value !== "string" && !import_node_buffer39.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
10079
10215
  throw new TypeError(`${context} returned an unexpected response`);
10080
10216
  }
10081
10217
  const result = text(value);
@@ -10092,7 +10228,7 @@ function requiredNonNegativeInteger(value, context) {
10092
10228
  return result;
10093
10229
  }
10094
10230
  function responseBytes(value, context) {
10095
- if (typeof value !== "string" && !import_node_buffer38.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
10231
+ if (typeof value !== "string" && !import_node_buffer39.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) {
10096
10232
  throw new TypeError(`${context} returned an unexpected response`);
10097
10233
  }
10098
10234
  return bytes(value);
@@ -10775,7 +10911,7 @@ function groupAutoPartitionItems(items) {
10775
10911
  }
10776
10912
 
10777
10913
  // src/client-values.ts
10778
- var import_node_buffer39 = require("buffer");
10914
+ var import_node_buffer40 = require("buffer");
10779
10915
  async function valueMGetEntries(client, refs, options = {}) {
10780
10916
  const refCount = refs.length;
10781
10917
  if (refCount === 0) {
@@ -10810,12 +10946,12 @@ async function valueMGetEntries(client, refs, options = {}) {
10810
10946
  const item = response[index];
10811
10947
  if (item == null) {
10812
10948
  entries[index] = { found: false };
10813
- } else if (import_node_buffer39.Buffer.isBuffer(item)) {
10949
+ } else if (import_node_buffer40.Buffer.isBuffer(item)) {
10814
10950
  entries[index] = { found: true, value: client.codec.decode(item) };
10815
10951
  } else if (item instanceof Uint8Array) {
10816
- entries[index] = { found: true, value: client.codec.decode(import_node_buffer39.Buffer.from(item)) };
10952
+ entries[index] = { found: true, value: client.codec.decode(import_node_buffer40.Buffer.from(item)) };
10817
10953
  } else if (typeof item === "string") {
10818
- entries[index] = { found: true, value: client.codec.decode(import_node_buffer39.Buffer.from(item)) };
10954
+ entries[index] = { found: true, value: client.codec.decode(import_node_buffer40.Buffer.from(item)) };
10819
10955
  } else {
10820
10956
  entries[index] = { found: true, value: item };
10821
10957
  }
@@ -10824,10 +10960,10 @@ async function valueMGetEntries(client, refs, options = {}) {
10824
10960
  }
10825
10961
 
10826
10962
  // src/auto-batch.ts
10827
- var import_node_buffer41 = require("buffer");
10963
+ var import_node_buffer42 = require("buffer");
10828
10964
 
10829
10965
  // src/auto-batch-ordering.ts
10830
- var import_node_buffer40 = require("buffer");
10966
+ var import_node_buffer41 = require("buffer");
10831
10967
  function autoBatchOrderingPlan(batch) {
10832
10968
  const accesses = /* @__PURE__ */ new Map();
10833
10969
  const fallbackDependencies = [];
@@ -10942,13 +11078,13 @@ function flowManyAutoBatchIds(command, name) {
10942
11078
  return fixedFlowItemIds(
10943
11079
  command,
10944
11080
  mixed ? 4 : 3,
10945
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && import_node_buffer40.Buffer.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
11081
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && import_node_buffer41.Buffer.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
10946
11082
  );
10947
11083
  }
10948
11084
  return fixedFlowItemIds(
10949
11085
  command,
10950
11086
  mixed ? 4 : 3,
10951
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer40.Buffer.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
11087
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer41.Buffer.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
10952
11088
  );
10953
11089
  }
10954
11090
  function createManyAutoBatchIds(command) {
@@ -10961,7 +11097,7 @@ function createManyAutoBatchIds(command) {
10961
11097
  const ids = fixedFlowItemIds(
10962
11098
  command,
10963
11099
  width,
10964
- (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer40.Buffer.isBuffer(command[itemIndex + width - 1]),
11100
+ (itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && import_node_buffer41.Buffer.isBuffer(command[itemIndex + width - 1]),
10965
11101
  markerIndex
10966
11102
  );
10967
11103
  if (ids != null) return ids;
@@ -10979,7 +11115,7 @@ function extendedCreateManyAutoBatchIds(command, markerIndex) {
10979
11115
  let cursor = markerIndex + 2;
10980
11116
  for (let itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
10981
11117
  const id = command[cursor];
10982
- if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !import_node_buffer40.Buffer.isBuffer(command[cursor + 2])) return void 0;
11118
+ if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !import_node_buffer41.Buffer.isBuffer(command[cursor + 2])) return void 0;
10983
11119
  ids.push(id);
10984
11120
  cursor += 3;
10985
11121
  const afterValues = skipExtendedNamedItems(command, cursor, true);
@@ -10998,7 +11134,7 @@ function skipExtendedNamedItems(command, countIndex, encodedValues) {
10998
11134
  for (let index = 0; index < count; index += 1) {
10999
11135
  const name = command[firstItem + index * 2];
11000
11136
  const value = command[firstItem + index * 2 + 1];
11001
- if (!isAutoBatchResourceValue(name) || (encodedValues ? !import_node_buffer40.Buffer.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
11137
+ if (!isAutoBatchResourceValue(name) || (encodedValues ? !import_node_buffer41.Buffer.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
11002
11138
  }
11003
11139
  return firstItem + count * 2;
11004
11140
  }
@@ -11013,7 +11149,7 @@ function runStepsManyAutoBatchIds(command) {
11013
11149
  ids.push(item);
11014
11150
  continue;
11015
11151
  }
11016
- if (typeof item !== "object" || item == null || Array.isArray(item) || import_node_buffer40.Buffer.isBuffer(item)) return void 0;
11152
+ if (typeof item !== "object" || item == null || Array.isArray(item) || import_node_buffer41.Buffer.isBuffer(item)) return void 0;
11017
11153
  const id = item.id;
11018
11154
  if (!isAutoBatchResourceValue(id)) return void 0;
11019
11155
  ids.push(id);
@@ -11054,7 +11190,7 @@ function flowValuePutOwner(command, start) {
11054
11190
  }
11055
11191
  var flowValuePutOptionTokens = /* @__PURE__ */ new Set(["NAME", "NOW", "OVERRIDE", "OWNER_FLOW_ID", "PARTITION", "TTL", "TTL_MS"]);
11056
11192
  function isAutoBatchResourceValue(value) {
11057
- return typeof value === "string" || import_node_buffer40.Buffer.isBuffer(value);
11193
+ return typeof value === "string" || import_node_buffer41.Buffer.isBuffer(value);
11058
11194
  }
11059
11195
  function isFencingToken(value) {
11060
11196
  return typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint";
@@ -11063,7 +11199,7 @@ function nonNegativeItemCount(value) {
11063
11199
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
11064
11200
  }
11065
11201
  function autoBatchResourceKey(namespace, value) {
11066
- return `${namespace}:${import_node_buffer40.Buffer.from(value).toString("base64")}`;
11202
+ return `${namespace}:${import_node_buffer41.Buffer.from(value).toString("base64")}`;
11067
11203
  }
11068
11204
  function autoBatchCommandName(command) {
11069
11205
  return commandView(command).name ?? null;
@@ -11472,13 +11608,13 @@ function claimFailure(error) {
11472
11608
  }
11473
11609
 
11474
11610
  // src/client-flow-support.ts
11475
- var import_node_buffer52 = require("buffer");
11611
+ var import_node_buffer55 = require("buffer");
11476
11612
 
11477
11613
  // src/flow-query-builder.ts
11478
- var import_node_buffer43 = require("buffer");
11614
+ var import_node_buffer44 = require("buffer");
11479
11615
 
11480
11616
  // src/flow-query-metadata.ts
11481
- var import_node_buffer42 = require("buffer");
11617
+ var import_node_buffer43 = require("buffer");
11482
11618
  var MAX_FLOW_QUERY_METADATA_KEY_BYTES = 64;
11483
11619
  function normalizeStateMeta(value, state) {
11484
11620
  if (value == null) return {};
@@ -11503,7 +11639,7 @@ function normalizeStateMeta(value, state) {
11503
11639
  function metadataEntries(value, context) {
11504
11640
  const entries = objectEntries(value ?? {}, context).map(([rawName, item]) => {
11505
11641
  const name = rawName.trim();
11506
- const size = import_node_buffer42.Buffer.byteLength(name, "utf8");
11642
+ const size = import_node_buffer43.Buffer.byteLength(name, "utf8");
11507
11643
  if (size === 0 || size > MAX_FLOW_QUERY_METADATA_KEY_BYTES || name.startsWith("__")) {
11508
11644
  throw new TypeError(`${context} key is invalid or reserved`);
11509
11645
  }
@@ -11527,7 +11663,7 @@ function objectEntries(value, context) {
11527
11663
  return keys.map((key) => [key, value[key]]);
11528
11664
  }
11529
11665
  function isPlainRecord(value) {
11530
- if (typeof value !== "object" || value == null || Array.isArray(value) || import_node_buffer42.Buffer.isBuffer(value)) {
11666
+ if (typeof value !== "object" || value == null || Array.isArray(value) || import_node_buffer43.Buffer.isBuffer(value)) {
11531
11667
  return false;
11532
11668
  }
11533
11669
  const prototype = Object.getPrototypeOf(value);
@@ -11597,7 +11733,7 @@ var FlowCollectionQuery = class {
11597
11733
  const states = /* @__PURE__ */ new Set();
11598
11734
  for (const [rawState, metadata] of objectEntries(values, "stateMeta")) {
11599
11735
  const state = requiredText(rawState, "stateMeta state").trim();
11600
- const stateBytes = import_node_buffer43.Buffer.byteLength(state, "utf8");
11736
+ const stateBytes = import_node_buffer44.Buffer.byteLength(state, "utf8");
11601
11737
  if (stateBytes === 0 || stateBytes > MAX_FLOW_QUERY_STATE_BYTES) {
11602
11738
  throw new TypeError(
11603
11739
  `stateMeta state names must be 1..${MAX_FLOW_QUERY_STATE_BYTES} bytes`
@@ -11776,7 +11912,7 @@ function requiredPartition(value) {
11776
11912
  "FLOW.QUERY convenience methods require a partition key"
11777
11913
  );
11778
11914
  }
11779
- const size = import_node_buffer43.Buffer.byteLength(value, "utf8");
11915
+ const size = import_node_buffer44.Buffer.byteLength(value, "utf8");
11780
11916
  if (size === 0 || size > MAX_FLOW_QUERY_PARTITION_BYTES) {
11781
11917
  throw new TypeError(
11782
11918
  `FLOW.QUERY partition key must be 1..${MAX_FLOW_QUERY_PARTITION_BYTES} bytes`
@@ -11824,20 +11960,23 @@ function requiredText(value, context) {
11824
11960
  return value;
11825
11961
  }
11826
11962
  function queryParameter(value, context) {
11827
- if (typeof value === "string" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "number" || import_node_buffer43.Buffer.isBuffer(value)) {
11963
+ if (typeof value === "string" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "number" || import_node_buffer44.Buffer.isBuffer(value)) {
11828
11964
  return value;
11829
11965
  }
11830
11966
  throw new TypeError(`${context} must be a scalar FLOW.QUERY parameter`);
11831
11967
  }
11832
11968
 
11833
11969
  // src/flow-query-response.ts
11834
- var import_node_buffer45 = require("buffer");
11970
+ var import_node_buffer48 = require("buffer");
11971
+
11972
+ // src/flow-query-diagnostic-response.ts
11973
+ var import_node_buffer46 = require("buffer");
11835
11974
 
11836
11975
  // src/flow-query-response-validation.ts
11837
- var import_node_buffer44 = require("buffer");
11976
+ var import_node_buffer45 = require("buffer");
11838
11977
  function requiredMap2(value, context) {
11839
11978
  if (value instanceof Map) return value;
11840
- if (typeof value !== "object" || value == null || Array.isArray(value) || import_node_buffer44.Buffer.isBuffer(value) || value instanceof Uint8Array) {
11979
+ if (typeof value !== "object" || value == null || Array.isArray(value) || import_node_buffer45.Buffer.isBuffer(value) || value instanceof Uint8Array) {
11841
11980
  throw decodeError(`${context} must be a map`, value);
11842
11981
  }
11843
11982
  return value;
@@ -11894,7 +12033,7 @@ function normalizeMetadataValue(value, context, budget, ancestors, depth) {
11894
12033
  if (!value.isWellFormed()) throw decodeError(`${context} contains invalid text`, value);
11895
12034
  return value;
11896
12035
  }
11897
- if (import_node_buffer44.Buffer.isBuffer(value) || value instanceof Uint8Array) {
12036
+ if (import_node_buffer45.Buffer.isBuffer(value) || value instanceof Uint8Array) {
11898
12037
  const decoded = strictText2(value);
11899
12038
  if (decoded == null) throw decodeError(`${context} contains invalid UTF-8`, value);
11900
12039
  return decoded;
@@ -11947,36 +12086,36 @@ function isPlainObject6(value) {
11947
12086
  const prototype = Object.getPrototypeOf(value);
11948
12087
  return prototype === Object.prototype || prototype === null;
11949
12088
  }
11950
- function requireContract(mapping, name, expected, context) {
11951
- const actual = requiredText2(mapping, name, context);
12089
+ function requireContract(mapping2, name, expected, context) {
12090
+ const actual = requiredText2(mapping2, name, context);
11952
12091
  if (actual !== expected) {
11953
12092
  throw decodeError(
11954
12093
  `${context} has unsupported contract ${JSON.stringify(actual)}`,
11955
- mapping
12094
+ mapping2
11956
12095
  );
11957
12096
  }
11958
12097
  }
11959
- function requiredText2(mapping, name, context) {
11960
- const value = strictText2(field(mapping, name));
12098
+ function requiredText2(mapping2, name, context) {
12099
+ const value = strictText2(field(mapping2, name));
11961
12100
  if (value == null || value.length === 0) {
11962
- throw decodeError(`${context} ${name} must be non-empty text`, mapping);
12101
+ throw decodeError(`${context} ${name} must be non-empty text`, mapping2);
11963
12102
  }
11964
12103
  return value;
11965
12104
  }
11966
- function optionalText2(mapping, name, context) {
11967
- const raw = field(mapping, name);
12105
+ function optionalText2(mapping2, name, context) {
12106
+ const raw = field(mapping2, name);
11968
12107
  if (raw == null) return void 0;
11969
12108
  const value = strictText2(raw);
11970
12109
  if (value == null)
11971
- throw decodeError(`${context} ${name} must be text`, mapping);
12110
+ throw decodeError(`${context} ${name} must be text`, mapping2);
11972
12111
  return value;
11973
12112
  }
11974
- function requiredBoundedText2(mapping, name, context, maximumBytes) {
11975
- const value = requiredText2(mapping, name, context);
11976
- if (import_node_buffer44.Buffer.byteLength(value, "utf8") > maximumBytes) {
12113
+ function requiredBoundedText2(mapping2, name, context, maximumBytes) {
12114
+ const value = requiredText2(mapping2, name, context);
12115
+ if (import_node_buffer45.Buffer.byteLength(value, "utf8") > maximumBytes) {
11977
12116
  throw decodeError(
11978
12117
  `${context} ${name} exceeds ${maximumBytes} bytes`,
11979
- mapping
12118
+ mapping2
11980
12119
  );
11981
12120
  }
11982
12121
  return value;
@@ -11986,15 +12125,15 @@ function boundedText(value, context, maximumBytes) {
11986
12125
  if (decoded == null || decoded.length === 0) {
11987
12126
  throw decodeError(`${context} must be non-empty text`, value);
11988
12127
  }
11989
- if (import_node_buffer44.Buffer.byteLength(decoded, "utf8") > maximumBytes) {
12128
+ if (import_node_buffer45.Buffer.byteLength(decoded, "utf8") > maximumBytes) {
11990
12129
  throw decodeError(`${context} exceeds ${maximumBytes} bytes`, value);
11991
12130
  }
11992
12131
  return decoded;
11993
12132
  }
11994
- function requiredBoolean(mapping, name, context) {
11995
- const value = field(mapping, name);
12133
+ function requiredBoolean(mapping2, name, context) {
12134
+ const value = field(mapping2, name);
11996
12135
  if (typeof value !== "boolean") {
11997
- throw decodeError(`${context} ${name} must be boolean`, mapping);
12136
+ throw decodeError(`${context} ${name} must be boolean`, mapping2);
11998
12137
  }
11999
12138
  return value;
12000
12139
  }
@@ -12037,13 +12176,13 @@ function positiveBoundedInteger(value, maximum, context) {
12037
12176
  if (parsed === 0) throw decodeError(`${context} must be positive`, value);
12038
12177
  return parsed;
12039
12178
  }
12040
- function hasKey(mapping, name) {
12041
- if (!(mapping instanceof Map)) return Object.hasOwn(mapping, name);
12042
- if (mapping.has(name)) return true;
12043
- const binaryName = import_node_buffer44.Buffer.from(name);
12044
- for (const key of mapping.keys()) {
12045
- if (import_node_buffer44.Buffer.isBuffer(key) && key.equals(binaryName)) return true;
12046
- if (key instanceof Uint8Array && import_node_buffer44.Buffer.from(key).equals(binaryName))
12179
+ function hasKey(mapping2, name) {
12180
+ if (!(mapping2 instanceof Map)) return Object.hasOwn(mapping2, name);
12181
+ if (mapping2.has(name)) return true;
12182
+ const binaryName = import_node_buffer45.Buffer.from(name);
12183
+ for (const key of mapping2.keys()) {
12184
+ if (import_node_buffer45.Buffer.isBuffer(key) && key.equals(binaryName)) return true;
12185
+ if (key instanceof Uint8Array && import_node_buffer45.Buffer.from(key).equals(binaryName))
12047
12186
  return true;
12048
12187
  }
12049
12188
  return false;
@@ -12054,7 +12193,7 @@ function decodeError(message, raw) {
12054
12193
  function strictText2(value) {
12055
12194
  if (typeof value === "string")
12056
12195
  return value.isWellFormed() ? value : void 0;
12057
- if (import_node_buffer44.Buffer.isBuffer(value) || value instanceof Uint8Array) {
12196
+ if (import_node_buffer45.Buffer.isBuffer(value) || value instanceof Uint8Array) {
12058
12197
  try {
12059
12198
  return new TextDecoder("utf-8", { fatal: true }).decode(value);
12060
12199
  } catch {
@@ -12087,193 +12226,39 @@ var FlowQueryError = class extends FerricStoreError {
12087
12226
  }
12088
12227
  };
12089
12228
 
12090
- // src/flow-query-response.ts
12091
- var FLOW_QUERY_RESULT_CONTRACT = "ferric.flow.query.result/v1";
12092
- var FLOW_EXPLAIN_CONTRACT = "ferric.flow.explain/v1";
12093
- var FLOW_QUERY_INDEXES_CONTRACT = "ferric.flow.query.indexes/v1";
12094
- var USAGE_FIELDS2 = [
12095
- ["range_seeks", "rangeSeeks"],
12096
- ["range_pages", "rangePages"],
12097
- ["scanned_entries", "scannedEntries"],
12098
- ["scanned_bytes", "scannedBytes"],
12099
- ["hydrated_records", "hydratedRecords"],
12100
- ["residual_checks", "residualChecks"],
12101
- ["duplicate_entries", "duplicateEntries"],
12102
- ["result_records", "resultRecords"],
12103
- ["response_bytes", "responseBytes"],
12104
- ["memory_high_water_bytes", "memoryHighWaterBytes"],
12105
- ["wall_time_us", "wallTimeUs"]
12106
- ];
12107
- var MAX_SIGNED_64 = (1n << 63n) - 1n;
12108
- var MAX_UNSIGNED_64 = (1n << 64n) - 1n;
12109
- function decodeFlowQueryResult(value) {
12110
- const result = decodeFlowQueryResponse(value, (mapping) => freezeMap(mapping));
12111
- if (result.kind === "records") {
12112
- return Object.freeze({
12113
- kind: "records",
12114
- version: FLOW_QUERY_RESULT_CONTRACT,
12115
- records: Object.freeze(result.records),
12116
- page: result.page,
12117
- quality: result.quality,
12118
- usage: result.usage,
12119
- raw: result.raw
12120
- });
12121
- }
12122
- return Object.freeze({
12123
- kind: "count",
12124
- version: FLOW_QUERY_RESULT_CONTRACT,
12125
- count: result.count,
12126
- quality: result.quality,
12127
- usage: result.usage,
12128
- raw: result.raw
12129
- });
12130
- }
12131
- function decodeFlowQueryRecords(value, decodeRecord) {
12132
- const result = decodeFlowQueryResponse(
12133
- value,
12134
- (mapping, index) => decodeRecord(mapping instanceof Map ? freezeMap(mapping) : mapping, index)
12135
- );
12136
- if (result.kind === "count") {
12137
- throw new FerricStoreError("Flow record convenience query returned a count result", {
12138
- raw: result.raw
12139
- });
12140
- }
12141
- return result.records;
12142
- }
12143
- function decodeFlowQueryResponse(value, decodeRecord) {
12144
- const mapping = requiredMap2(value, "FLOW.QUERY result");
12145
- requireContract(mapping, "version", FLOW_QUERY_RESULT_CONTRACT, "FLOW.QUERY result");
12146
- const quality = decodeQuality(field(mapping, "quality"));
12147
- const usage = decodeUsage(field(mapping, "usage"));
12148
- const hasRecords = hasKey(mapping, "records");
12149
- const hasCount = hasKey(mapping, "result");
12150
- if (hasRecords === hasCount) {
12151
- throw decodeError("FLOW.QUERY result must contain exactly one records or count shape", value);
12152
- }
12153
- const raw = freezeMap(mapping);
12154
- if (hasRecords) {
12155
- const rawRecords = field(mapping, "records");
12156
- if (!Array.isArray(rawRecords) || rawRecords.length > 100) {
12157
- throw decodeError("FLOW.QUERY records must be an array of at most 100 maps", value);
12158
- }
12159
- const records = new Array(rawRecords.length);
12160
- for (let index = 0; index < rawRecords.length; index += 1) {
12161
- if (!Object.hasOwn(rawRecords, index)) {
12162
- throw decodeError("FLOW.QUERY records must be a dense array", value);
12163
- }
12164
- records[index] = decodeRecord(
12165
- requiredMap2(rawRecords[index], `FLOW.QUERY record ${index}`),
12166
- index
12167
- );
12168
- }
12169
- if (usage.resultRecords !== records.length) {
12170
- throw decodeError("FLOW.QUERY usage result_records does not match records", value);
12171
- }
12172
- return {
12173
- kind: "records",
12174
- records,
12175
- page: decodePage(field(mapping, "page")),
12176
- quality,
12177
- usage,
12178
- raw
12179
- };
12180
- }
12181
- if (hasKey(mapping, "page")) {
12182
- throw decodeError("FLOW.QUERY count result contains an unexpected page", value);
12183
- }
12184
- const countResult = requiredMap2(field(mapping, "result"), "FLOW.QUERY count result");
12185
- if (requiredText2(countResult, "kind", "FLOW.QUERY count result") !== "count") {
12186
- throw decodeError("FLOW.QUERY count result kind must be count", value);
12187
- }
12188
- const count = boundedInteger(
12189
- field(countResult, "value"),
12190
- MAX_SIGNED_64,
12191
- "FLOW.QUERY count value"
12192
- );
12193
- if (usage.resultRecords !== 1) {
12194
- throw decodeError("FLOW.QUERY count usage result_records must be 1", value);
12195
- }
12196
- return {
12197
- kind: "count",
12198
- count,
12199
- quality,
12200
- usage,
12201
- raw
12202
- };
12203
- }
12204
- function decodeFlowExplainResult(value) {
12205
- const mapping = requiredMap2(value, "FLOW.QUERY explain");
12206
- requireContract(mapping, "version", FLOW_EXPLAIN_CONTRACT, "FLOW.QUERY explain");
12207
- const queryFingerprint = requiredText2(mapping, "query_fingerprint", "FLOW.QUERY explain");
12208
- if (!/^[0-9a-f]{64}$/iu.test(queryFingerprint)) {
12209
- throw decodeError("FLOW.QUERY explain query_fingerprint is invalid", value);
12210
- }
12211
- const status = requiredText2(mapping, "status", "FLOW.QUERY explain");
12212
- if (status !== "planned" && status !== "rejected" && status !== "executed") {
12213
- throw decodeError(`FLOW.QUERY explain status ${JSON.stringify(status)} is unsupported`, value);
12214
- }
12215
- const plan = freezeMetadataMap(
12216
- requiredMap2(field(mapping, "plan"), "FLOW.QUERY explain plan"),
12217
- "FLOW.QUERY explain plan"
12218
- );
12219
- const estimate = freezeMetadataMap(
12220
- requiredMap2(field(mapping, "estimate"), "FLOW.QUERY explain estimate"),
12221
- "FLOW.QUERY explain estimate"
12222
- );
12223
- const bounds = freezeMetadataMap(
12224
- requiredMap2(field(mapping, "bounds"), "FLOW.QUERY explain bounds"),
12225
- "FLOW.QUERY explain bounds"
12226
- );
12227
- const actualValue = field(mapping, "actual");
12228
- let actual;
12229
- if (status === "executed") {
12230
- if (actualValue == null) {
12231
- throw decodeError("FLOW.QUERY executed explain is missing actual usage", value);
12232
- }
12233
- actual = decodeUsage(actualValue);
12234
- } else if (actualValue != null) {
12235
- throw decodeError("FLOW.QUERY non-executed explain contains actual usage", value);
12236
- }
12237
- const diagnosticValue = field(mapping, "diagnostic");
12238
- let diagnostic;
12239
- if (status === "rejected") {
12240
- diagnostic = tryDecodeFlowQueryError(diagnosticValue);
12241
- if (diagnostic == null) {
12242
- throw decodeError("FLOW.QUERY rejected explain has an invalid diagnostic", value);
12243
- }
12244
- } else if (diagnosticValue != null) {
12245
- throw decodeError("FLOW.QUERY non-rejected explain contains a diagnostic", value);
12246
- }
12247
- return Object.freeze({
12248
- version: FLOW_EXPLAIN_CONTRACT,
12249
- queryFingerprint,
12250
- status,
12251
- plan,
12252
- estimate,
12253
- bounds,
12254
- actual,
12255
- diagnostic,
12256
- raw: freezeMap(mapping)
12257
- });
12258
- }
12229
+ // src/flow-query-diagnostic-response.ts
12230
+ var MIN_SIGNED_64 = -(1n << 63n);
12231
+ var MAX_SIGNED_642 = (1n << 63n) - 1n;
12232
+ var DIAGNOSTIC_TEXT_BYTES = 1024;
12233
+ var DIAGNOSTIC_CONTEXT_ENTRIES = 16;
12234
+ var DIAGNOSTIC_CONTEXT_LIST_ITEMS = 32;
12235
+ var DIAGNOSTIC_CONTEXT_KEY_BYTES = 128;
12236
+ var DIAGNOSTIC_CONTEXT_DEPTH = 6;
12237
+ var DIAGNOSTIC_CONTEXT_NODES = 512;
12259
12238
  function tryDecodeFlowQueryError(value, cause) {
12260
12239
  try {
12261
- const mapping = requiredMap2(value, "FLOW.QUERY diagnostic");
12262
- const contextValue = field(mapping, "context");
12263
- const context = contextValue == null ? void 0 : freezeMetadataMap(
12264
- requiredMap2(contextValue, "FLOW.QUERY diagnostic context"),
12265
- "FLOW.QUERY diagnostic context"
12266
- );
12267
- const position = decodePosition(field(mapping, "position"));
12240
+ const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic");
12241
+ const context = decodeDiagnosticContext(field(mapping2, "context"));
12242
+ const position = decodePosition(field(mapping2, "position"));
12268
12243
  return new FlowQueryError({
12269
- code: requiredText2(mapping, "code", "FLOW.QUERY diagnostic"),
12270
- message: requiredText2(mapping, "message", "FLOW.QUERY diagnostic"),
12271
- detail: optionalText2(mapping, "detail", "FLOW.QUERY diagnostic"),
12272
- hint: optionalText2(mapping, "hint", "FLOW.QUERY diagnostic"),
12273
- retryable: requiredBoolean(mapping, "retryable", "FLOW.QUERY diagnostic"),
12274
- safeToRetry: requiredBoolean(mapping, "safe_to_retry", "FLOW.QUERY diagnostic"),
12244
+ code: requiredBoundedText2(
12245
+ mapping2,
12246
+ "code",
12247
+ "FLOW.QUERY diagnostic",
12248
+ DIAGNOSTIC_TEXT_BYTES
12249
+ ),
12250
+ message: requiredBoundedText2(
12251
+ mapping2,
12252
+ "message",
12253
+ "FLOW.QUERY diagnostic",
12254
+ DIAGNOSTIC_TEXT_BYTES
12255
+ ),
12256
+ detail: optionalDiagnosticText(mapping2, "detail"),
12257
+ hint: optionalDiagnosticText(mapping2, "hint"),
12258
+ retryable: requiredBoolean(mapping2, "retryable", "FLOW.QUERY diagnostic"),
12259
+ safeToRetry: requiredBoolean(mapping2, "safe_to_retry", "FLOW.QUERY diagnostic"),
12275
12260
  retryAfterMs: nonNegativeInteger(
12276
- field(mapping, "retry_after_ms"),
12261
+ field(mapping2, "retry_after_ms"),
12277
12262
  "FLOW.QUERY diagnostic retry_after_ms"
12278
12263
  ),
12279
12264
  position,
@@ -12286,142 +12271,1269 @@ function tryDecodeFlowQueryError(value, cause) {
12286
12271
  throw error;
12287
12272
  }
12288
12273
  }
12289
- function decodeFlowQueryIndexStatus(value) {
12290
- const mapping = requiredMap2(value, "FLOW.QUERY.INDEXES");
12291
- requireContract(
12292
- mapping,
12293
- "contract_version",
12294
- FLOW_QUERY_INDEXES_CONTRACT,
12295
- "FLOW.QUERY.INDEXES"
12296
- );
12297
- const registry = requiredMap2(field(mapping, "registry"), "FLOW.QUERY.INDEXES registry");
12298
- const services = freezeMetadataMap(
12299
- requiredMap2(field(mapping, "services"), "FLOW.QUERY.INDEXES services"),
12300
- "FLOW.QUERY.INDEXES services"
12274
+ function optionalDiagnosticText(mapping2, name) {
12275
+ const value = optionalText2(mapping2, name, "FLOW.QUERY diagnostic");
12276
+ if (value != null && import_node_buffer46.Buffer.byteLength(value, "utf8") > DIAGNOSTIC_TEXT_BYTES) {
12277
+ throw decodeError(
12278
+ `FLOW.QUERY diagnostic ${name} exceeds ${DIAGNOSTIC_TEXT_BYTES} bytes`,
12279
+ mapping2
12280
+ );
12281
+ }
12282
+ return value;
12283
+ }
12284
+ function decodeDiagnosticContext(value) {
12285
+ if (value == null) return void 0;
12286
+ const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic context");
12287
+ const size = mapping2 instanceof Map ? mapping2.size : Object.keys(mapping2).length;
12288
+ if (size > DIAGNOSTIC_CONTEXT_ENTRIES) {
12289
+ throw decodeError("FLOW.QUERY diagnostic context contains too many entries", value);
12290
+ }
12291
+ validateDiagnosticContextValue(
12292
+ mapping2,
12293
+ DIAGNOSTIC_CONTEXT_DEPTH,
12294
+ { remaining: DIAGNOSTIC_CONTEXT_NODES }
12301
12295
  );
12302
- const rawIndexes = field(mapping, "indexes");
12303
- if (!Array.isArray(rawIndexes) || rawIndexes.length > 32) {
12304
- throw decodeError("FLOW.QUERY.INDEXES indexes must contain at most 32 entries", value);
12296
+ return freezeMetadataMap(mapping2, "FLOW.QUERY diagnostic context");
12297
+ }
12298
+ function validateDiagnosticContextValue(value, depth, budget) {
12299
+ consumeDiagnosticContextNode(value, budget);
12300
+ if (value == null || typeof value === "boolean") return;
12301
+ if (typeof value === "number") {
12302
+ if (Number.isSafeInteger(value)) return;
12303
+ throw decodeError("FLOW.QUERY diagnostic context contains an invalid integer", value);
12305
12304
  }
12306
- const indexes = new Array(rawIndexes.length);
12307
- for (let index = 0; index < rawIndexes.length; index += 1) {
12308
- if (!Object.hasOwn(rawIndexes, index)) {
12309
- throw decodeError("FLOW.QUERY.INDEXES indexes must be a dense array", value);
12305
+ if (typeof value === "bigint") {
12306
+ if (value >= MIN_SIGNED_64 && value <= MAX_SIGNED_642) return;
12307
+ throw decodeError("FLOW.QUERY diagnostic context contains an invalid integer", value);
12308
+ }
12309
+ const text3 = diagnosticContextText(value);
12310
+ if (text3 != null) {
12311
+ if (import_node_buffer46.Buffer.byteLength(text3, "utf8") <= DIAGNOSTIC_TEXT_BYTES) return;
12312
+ throw decodeError("FLOW.QUERY diagnostic context contains oversized text", value);
12313
+ }
12314
+ if (depth <= 0) {
12315
+ throw decodeError("FLOW.QUERY diagnostic context exceeds its maximum depth", value);
12316
+ }
12317
+ if (Array.isArray(value)) {
12318
+ if (value.length > DIAGNOSTIC_CONTEXT_LIST_ITEMS) {
12319
+ throw decodeError("FLOW.QUERY diagnostic context list is too large", value);
12310
12320
  }
12311
- indexes[index] = decodeIndex(rawIndexes[index], index);
12321
+ for (let index = 0; index < value.length; index += 1) {
12322
+ if (!Object.hasOwn(value, index)) {
12323
+ throw decodeError("FLOW.QUERY diagnostic context list must be dense", value);
12324
+ }
12325
+ validateDiagnosticContextValue(value[index], depth - 1, budget);
12326
+ }
12327
+ return;
12312
12328
  }
12313
- return Object.freeze({
12314
- contractVersion: FLOW_QUERY_INDEXES_CONTRACT,
12315
- observedAtMs: nonNegativeInteger(
12316
- field(mapping, "observed_at_ms"),
12317
- "FLOW.QUERY.INDEXES observed_at_ms"
12329
+ if (value instanceof Map || isDiagnosticContextRecord(value)) {
12330
+ const entries = value instanceof Map ? value.entries() : Object.entries(value);
12331
+ const size = value instanceof Map ? value.size : Object.keys(value).length;
12332
+ if (size > DIAGNOSTIC_CONTEXT_ENTRIES) {
12333
+ throw decodeError("FLOW.QUERY diagnostic context map is too large", value);
12334
+ }
12335
+ for (const [rawKey, item] of entries) {
12336
+ const key = diagnosticContextText(rawKey);
12337
+ if (key == null || key.length === 0 || import_node_buffer46.Buffer.byteLength(key, "utf8") > DIAGNOSTIC_CONTEXT_KEY_BYTES) {
12338
+ throw decodeError("FLOW.QUERY diagnostic context contains an invalid key", value);
12339
+ }
12340
+ validateDiagnosticContextValue(item, depth - 1, budget);
12341
+ }
12342
+ return;
12343
+ }
12344
+ throw decodeError("FLOW.QUERY diagnostic context contains an unsupported value", value);
12345
+ }
12346
+ function consumeDiagnosticContextNode(value, budget) {
12347
+ if (budget.remaining <= 0) {
12348
+ throw decodeError("FLOW.QUERY diagnostic context exceeds its node limit", value);
12349
+ }
12350
+ budget.remaining -= 1;
12351
+ }
12352
+ function diagnosticContextText(value) {
12353
+ if (typeof value === "string") return value.isWellFormed() ? value : void 0;
12354
+ if (!import_node_buffer46.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return void 0;
12355
+ try {
12356
+ return new TextDecoder("utf-8", { fatal: true }).decode(value);
12357
+ } catch {
12358
+ return void 0;
12359
+ }
12360
+ }
12361
+ function isDiagnosticContextRecord(value) {
12362
+ if (typeof value !== "object" || value == null) return false;
12363
+ const prototype = Object.getPrototypeOf(value);
12364
+ return prototype === Object.prototype || prototype === null;
12365
+ }
12366
+ function decodePosition(value) {
12367
+ if (value == null) return void 0;
12368
+ const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic position");
12369
+ return Object.freeze({
12370
+ byte: positiveInteger(field(mapping2, "byte"), "FLOW.QUERY diagnostic position byte"),
12371
+ line: positiveInteger(field(mapping2, "line"), "FLOW.QUERY diagnostic position line"),
12372
+ column: positiveInteger(
12373
+ field(mapping2, "column"),
12374
+ "FLOW.QUERY diagnostic position column"
12375
+ )
12376
+ });
12377
+ }
12378
+
12379
+ // src/flow-query-index-contract.ts
12380
+ var import_node_buffer47 = require("buffer");
12381
+ var FLOW_QUERY_BUILD_PHASES = ["pending", "snapshot", "backfill", "done"];
12382
+ var FLOW_QUERY_VALIDATION_PHASES = [
12383
+ "pending",
12384
+ "source",
12385
+ "index",
12386
+ "counter",
12387
+ "cleanup",
12388
+ "done"
12389
+ ];
12390
+ var FLOW_QUERY_RETIREMENT_PHASES = [
12391
+ "pending",
12392
+ "fence",
12393
+ "index",
12394
+ "counter",
12395
+ "reverse",
12396
+ "cleanup",
12397
+ "done"
12398
+ ];
12399
+ var IDENTIFIER = /^[A-Za-z0-9_.:-]+$/u;
12400
+ var UNQUOTED_METADATA = /^[A-Za-z0-9_-]+$/u;
12401
+ var ATTRIBUTE_SELECTOR = /^attribute\['((?:[^']|'')*)'\]$/u;
12402
+ var STATE_META_SELECTOR = /^state_meta\['((?:[^']|'')*)'\]\['((?:[^']|'')*)'\]$/u;
12403
+ var INTEGER_FIELDS = /* @__PURE__ */ new Set([
12404
+ "version",
12405
+ "priority",
12406
+ "created_at_ms",
12407
+ "updated_at_ms",
12408
+ "next_run_at_ms",
12409
+ "lease_deadline_ms",
12410
+ "attempts",
12411
+ "max_active_ms"
12412
+ ]);
12413
+ var BUILTIN_FIELDS = /* @__PURE__ */ new Set([
12414
+ ...INTEGER_FIELDS,
12415
+ "partition_key",
12416
+ "run_id",
12417
+ "event_id",
12418
+ "type",
12419
+ "state",
12420
+ "run_state",
12421
+ "parent_flow_id",
12422
+ "root_flow_id",
12423
+ "correlation_id"
12424
+ ]);
12425
+ var RETIREMENT_PROGRESS_FIELDS = [
12426
+ "phase_counts",
12427
+ "current_phases",
12428
+ "completed_shards",
12429
+ "total_shards",
12430
+ "deleted_entries",
12431
+ "deleted_bytes",
12432
+ "rewritten_reverse_rows"
12433
+ ];
12434
+ function validateFlowQueryIndexContract(status, expectedId) {
12435
+ let previousId;
12436
+ let previousVersion;
12437
+ for (const index of status.indexes) {
12438
+ const version = BigInt(index.version);
12439
+ if (previousId != null && (index.id < previousId || index.id === previousId && previousVersion != null && version <= previousVersion)) {
12440
+ fail("indexes must be uniquely sorted by id and version", status.raw);
12441
+ }
12442
+ if (expectedId != null && index.id !== expectedId) {
12443
+ fail("filtered indexes do not match the requested id", status.raw);
12444
+ }
12445
+ previousId = index.id;
12446
+ previousVersion = version;
12447
+ validateIndex(index, status);
12448
+ }
12449
+ if (expectedId != null && status.indexes.length === 0) {
12450
+ fail("filtered indexes do not match the requested id", status.raw);
12451
+ }
12452
+ const hasUnavailableStatistics = status.indexes.some(
12453
+ (index) => index.statistics.status === "unavailable"
12454
+ );
12455
+ if (status.services.statisticsStore === "unavailable") {
12456
+ if (status.indexes.some((index) => index.statistics.status !== "unavailable")) {
12457
+ fail("statistics must be unavailable when the service is unavailable", status.raw);
12458
+ }
12459
+ } else if (hasUnavailableStatistics) {
12460
+ fail("statistics cannot be unavailable while the service is ready", status.raw);
12461
+ }
12462
+ }
12463
+ function validateIndex(index, status) {
12464
+ if (!IDENTIFIER.test(index.id) || index.workloads.some((workload) => !IDENTIFIER.test(workload))) {
12465
+ fail("index identity contains invalid characters", index.raw);
12466
+ }
12467
+ const first = index.fields[0];
12468
+ if (first?.name !== "partition_key" || first.direction !== "asc" || first.encoding !== "hashed") {
12469
+ fail("index must begin with partition_key asc hashed", index.raw);
12470
+ }
12471
+ let attributeFields = 0;
12472
+ for (const indexField of index.fields) {
12473
+ if (indexField.encoding === "hashed" && indexField.direction !== "asc") {
12474
+ fail("hashed index fields must be ascending", index.raw);
12475
+ }
12476
+ const kind = fieldKind(indexField.name);
12477
+ if (kind == null) fail("index contains an unsupported field selector", index.raw);
12478
+ if (indexField.encoding === "ordered" && kind !== "integer") {
12479
+ fail("ordered index fields must be integers", index.raw);
12480
+ }
12481
+ if (kind === "attribute") attributeFields += 1;
12482
+ }
12483
+ if (attributeFields > 1) fail("index may contain at most one attribute field", index.raw);
12484
+ for (const prefix of index.countPrefixes) {
12485
+ if (index.fields.slice(0, prefix).some((field3) => field3.encoding !== "hashed")) {
12486
+ fail("count prefixes may cover only hashed fields", index.raw);
12487
+ }
12488
+ }
12489
+ if (index.coveringFields.length > 0) {
12490
+ if (index.coveringFields.some((field3) => fieldKind(field3) == null)) {
12491
+ fail("index contains an unsupported covering field selector", index.raw);
12492
+ }
12493
+ const required = /* @__PURE__ */ new Set(["run_id", "version", ...index.fields.map((field3) => field3.name)]);
12494
+ if ([...required].some((field3) => !index.coveringFields.includes(field3))) {
12495
+ fail("index covering fields omit an identity or index field", index.raw);
12496
+ }
12497
+ }
12498
+ if (index.countPrefixes.length > 0 !== (index.format.counter != null)) {
12499
+ fail("index counter format is inconsistent with count prefixes", index.raw);
12500
+ }
12501
+ validateProgress(index.build, FLOW_QUERY_BUILD_PHASES, "build");
12502
+ validateProgress(index.validation, FLOW_QUERY_VALIDATION_PHASES, "validation");
12503
+ if (index.retirement.status === "not_applicable") {
12504
+ if (RETIREMENT_PROGRESS_FIELDS.some((name) => hasKey(index.retirement.raw, name))) {
12505
+ fail("not_applicable retirement must not contain progress", index.retirement.raw);
12506
+ }
12507
+ } else {
12508
+ const retirement = index.retirement;
12509
+ if (retirement.phaseCounts == null || retirement.currentPhases == null || retirement.completedShards == null || retirement.totalShards == null) {
12510
+ fail("retirement progress is incomplete", retirement.raw);
12511
+ }
12512
+ validateProgress(
12513
+ {
12514
+ phaseCounts: retirement.phaseCounts,
12515
+ currentPhases: retirement.currentPhases,
12516
+ completedShards: retirement.completedShards,
12517
+ totalShards: retirement.totalShards,
12518
+ raw: retirement.raw
12519
+ },
12520
+ FLOW_QUERY_RETIREMENT_PHASES,
12521
+ "retirement"
12522
+ );
12523
+ }
12524
+ const totals = new Set(
12525
+ [
12526
+ index.coverage.totalShards,
12527
+ index.build.totalShards,
12528
+ index.validation.totalShards,
12529
+ ...index.retirement.totalShards == null ? [] : [index.retirement.totalShards]
12530
+ ].map(indexInteger)
12531
+ );
12532
+ if (totals.size !== 1) fail("index shard totals are inconsistent", index.raw);
12533
+ if (!sameIndexInteger(index.coverage.completeShards, index.build.completedShards)) {
12534
+ fail("coverage and build completion are inconsistent", index.raw);
12535
+ }
12536
+ if (index.coverage.validation !== index.validation.status) {
12537
+ fail("coverage and validation status are inconsistent", index.raw);
12538
+ }
12539
+ const queryable = index.state === "active" && sameIndexInteger(
12540
+ index.coverage.completeShards,
12541
+ index.coverage.totalShards
12542
+ ) && index.coverage.validation === "passed";
12543
+ if (index.queryable !== queryable) fail("index queryable flag is inconsistent", index.raw);
12544
+ validateLifecycle(index);
12545
+ validateValidation(index);
12546
+ validateStatistics(index, status);
12547
+ }
12548
+ function validateProgress(progress, phases, section) {
12549
+ const entries = Object.entries(progress.phaseCounts);
12550
+ if (entries.length === 0 || entries.some(
12551
+ ([phase, count]) => !phases.includes(phase) || indexInteger(count) <= 0n
12552
+ ) || entries.reduce((sum, [, count]) => sum + indexInteger(count), 0n) !== indexInteger(progress.totalShards)) {
12553
+ fail(`${section} phase_counts are invalid`, progress.raw);
12554
+ }
12555
+ const current = phases.filter((phase) => Object.hasOwn(progress.phaseCounts, phase));
12556
+ if (current.length !== progress.currentPhases.length || current.some((phase, position) => phase !== progress.currentPhases[position])) {
12557
+ fail(`${section} current_phases are inconsistent`, progress.raw);
12558
+ }
12559
+ if (!sameIndexInteger(
12560
+ progress.completedShards,
12561
+ progress.phaseCounts.done ?? 0
12562
+ )) {
12563
+ fail(`${section} completed_shards is inconsistent`, progress.raw);
12564
+ }
12565
+ }
12566
+ function validateLifecycle(index) {
12567
+ const built = sameIndexInteger(
12568
+ index.build.completedShards,
12569
+ index.build.totalShards
12570
+ );
12571
+ const validation = index.validation.status;
12572
+ const retirement = index.retirement.status;
12573
+ const valid = index.state === "building" && !built && validation === "pending" && retirement === "not_applicable" || index.state === "validating" && built && (validation === "pending" || validation === "passed") && retirement === "not_applicable" || index.state === "active" && built && validation === "passed" && retirement === "not_applicable" || index.state === "retiring" && built && validation !== "pending" && retirement !== "not_applicable" || index.state === "failed" && validation !== "pending" && retirement !== "not_applicable";
12574
+ if (!valid) fail("index lifecycle fields are inconsistent", index.raw);
12575
+ }
12576
+ function validateValidation(index) {
12577
+ const validation = index.validation;
12578
+ const valid = validation.status === "pending" && indexInteger(validation.mismatches) === 0n && validation.failureReason == null && validation.validatedAtMs == null || validation.status === "passed" && indexInteger(validation.mismatches) === 0n && validation.failureReason == null && validation.validatedAtMs != null || validation.status === "failed" && indexInteger(validation.mismatches) > 0n && Boolean(validation.failureReason) && validation.validatedAtMs != null;
12579
+ if (!valid) fail("validation status fields are inconsistent", validation.raw);
12580
+ }
12581
+ function validateStatistics(index, status) {
12582
+ const statistics = index.statistics;
12583
+ const times = [
12584
+ statistics.oldestCollectedAtMs,
12585
+ statistics.newestCollectedAtMs,
12586
+ statistics.oldestAgeMs,
12587
+ statistics.newestAgeMs
12588
+ ];
12589
+ if (indexInteger(statistics.samples) === 0n) {
12590
+ if (statistics.status !== "missing" && statistics.status !== "unavailable" || times.some((value) => value != null)) {
12591
+ fail("empty statistics fields are inconsistent", statistics.raw);
12592
+ }
12593
+ return;
12594
+ }
12595
+ if (times.some((value) => value == null)) {
12596
+ fail("sampled statistics require timestamps and ages", statistics.raw);
12597
+ }
12598
+ const [oldest, newest, oldestAge, newestAge] = times;
12599
+ if (indexInteger(oldest) > indexInteger(newest) || indexInteger(oldestAge) !== indexAge(status.observedAtMs, oldest) || indexInteger(newestAge) !== indexAge(status.observedAtMs, newest)) {
12600
+ fail("statistics timestamps or ages are inconsistent", statistics.raw);
12601
+ }
12602
+ const expected = sameIndexInteger(statistics.freshSamples, statistics.samples) ? ["fresh"] : indexInteger(statistics.freshSamples) === 0n ? indexInteger(statistics.futureSamples) > 0n ? ["stale", "future"] : ["stale"] : ["mixed"];
12603
+ if (!expected.includes(statistics.status)) {
12604
+ fail("statistics status does not match sample counters", statistics.raw);
12605
+ }
12606
+ }
12607
+ function fieldKind(name) {
12608
+ if (BUILTIN_FIELDS.has(name)) return INTEGER_FIELDS.has(name) ? "integer" : "keyword";
12609
+ const parts = name.split(".");
12610
+ if (parts.length === 2 && parts[0] === "attribute" && validUnquoted(parts[1] ?? "")) return "attribute";
12611
+ if (parts.length === 3 && parts[0] === "state_meta" && validUnquoted(parts[1] ?? "") && validUnquoted(parts[2] ?? "")) return "state_meta";
12612
+ const attribute = ATTRIBUTE_SELECTOR.exec(name);
12613
+ if (attribute != null) {
12614
+ const metadata = (attribute[1] ?? "").replaceAll("''", "'");
12615
+ if (validMetadata(metadata, true) && name === externalSelector("attribute", metadata)) return "attribute";
12616
+ }
12617
+ const stateMeta = STATE_META_SELECTOR.exec(name);
12618
+ if (stateMeta != null) {
12619
+ const state = (stateMeta[1] ?? "").replaceAll("''", "'");
12620
+ const metadata = (stateMeta[2] ?? "").replaceAll("''", "'");
12621
+ if (validMetadata(state, false) && validMetadata(metadata, true) && name === externalSelector("state_meta", state, metadata)) return "state_meta";
12622
+ }
12623
+ return void 0;
12624
+ }
12625
+ function validUnquoted(value) {
12626
+ return !value.startsWith("__") && UNQUOTED_METADATA.test(value) && import_node_buffer47.Buffer.byteLength(value, "ascii") <= 64;
12627
+ }
12628
+ function validMetadata(value, rejectReserved) {
12629
+ return value.length > 0 && import_node_buffer47.Buffer.byteLength(value, "utf8") <= 64 && (!rejectReserved || !value.startsWith("__"));
12630
+ }
12631
+ function externalSelector(root, ...segments) {
12632
+ return segments.every(validUnquoted) ? [root, ...segments].join(".") : root + segments.map((segment) => `['${segment.replaceAll("'", "''")}']`).join("");
12633
+ }
12634
+ function sameIndexInteger(left, right) {
12635
+ return indexInteger(left) === indexInteger(right);
12636
+ }
12637
+ function indexAge(observedAtMs, collectedAtMs) {
12638
+ const age = indexInteger(observedAtMs) - indexInteger(collectedAtMs);
12639
+ return age < 0n ? 0n : age;
12640
+ }
12641
+ function indexInteger(value) {
12642
+ return BigInt(value);
12643
+ }
12644
+ function fail(message, raw) {
12645
+ throw decodeError(`FLOW.QUERY.INDEXES ${message}`, raw);
12646
+ }
12647
+
12648
+ // src/flow-query-index-decode.ts
12649
+ var MAX_UNSIGNED_64 = (1n << 64n) - 1n;
12650
+ function decodeUniqueTextArray(value, context, maximum, maximumBytes) {
12651
+ if (!Array.isArray(value) || value.length > maximum) {
12652
+ throw decodeError(`${context} must be a bounded array`, value);
12653
+ }
12654
+ const result = new Array(value.length);
12655
+ const seen = /* @__PURE__ */ new Set();
12656
+ for (let index = 0; index < value.length; index += 1) {
12657
+ if (!Object.hasOwn(value, index)) {
12658
+ throw decodeError(`${context} must be dense`, value);
12659
+ }
12660
+ const text3 = boundedText(
12661
+ value[index],
12662
+ `${context} entry ${index}`,
12663
+ maximumBytes
12664
+ );
12665
+ if (seen.has(text3)) {
12666
+ throw decodeError(`${context} contains duplicates`, value);
12667
+ }
12668
+ seen.add(text3);
12669
+ result[index] = text3;
12670
+ }
12671
+ return Object.freeze(result);
12672
+ }
12673
+ function requiredChoice(mapping2, name, context, choices) {
12674
+ const value = requiredBoundedText2(mapping2, name, context, 64);
12675
+ if (!choices.includes(value)) {
12676
+ throw decodeError(`${context} ${name} is unsupported`, mapping2);
12677
+ }
12678
+ return value;
12679
+ }
12680
+ function optionalBoundedText(mapping2, name, context, maximumBytes) {
12681
+ const value = field(mapping2, name);
12682
+ if (value == null) return void 0;
12683
+ return boundedText(value, `${context} ${name}`, maximumBytes);
12684
+ }
12685
+ function optionalIndexInteger(mapping2, name, context) {
12686
+ const value = field(mapping2, name);
12687
+ return value == null ? void 0 : unsignedIndexInteger(value, `${context} ${name}`);
12688
+ }
12689
+ function indexCounter(mapping2, name, section) {
12690
+ return unsignedIndexInteger(
12691
+ field(mapping2, name),
12692
+ `FLOW.QUERY.INDEXES index ${section} ${name}`
12693
+ );
12694
+ }
12695
+ function unsignedIndexInteger(value, context) {
12696
+ return boundedInteger(value, MAX_UNSIGNED_64, context);
12697
+ }
12698
+ function positiveIndexInteger(value, context) {
12699
+ return positiveBoundedInteger(value, MAX_UNSIGNED_64, context);
12700
+ }
12701
+ function indexInteger2(value) {
12702
+ return BigInt(value);
12703
+ }
12704
+
12705
+ // src/flow-query-index-lifecycle-response.ts
12706
+ function decodeIndexCoverage(value) {
12707
+ const context = "FLOW.QUERY.INDEXES index coverage";
12708
+ const mapping2 = requiredMap2(value, context);
12709
+ const completeShards = unsignedIndexInteger(
12710
+ field(mapping2, "complete_shards"),
12711
+ `${context} complete_shards`
12712
+ );
12713
+ const totalShards = positiveIndexInteger(
12714
+ field(mapping2, "total_shards"),
12715
+ `${context} total_shards`
12716
+ );
12717
+ if (indexInteger2(completeShards) > indexInteger2(totalShards)) {
12718
+ throw decodeError(`${context} complete_shards exceeds total_shards`, value);
12719
+ }
12720
+ return Object.freeze({
12721
+ completeShards,
12722
+ totalShards,
12723
+ validation: requiredChoice(mapping2, "validation", context, [
12724
+ "pending",
12725
+ "passed",
12726
+ "failed"
12727
+ ]),
12728
+ raw: freezeMap(mapping2)
12729
+ });
12730
+ }
12731
+ function decodeIndexBuild(value) {
12732
+ const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES index build");
12733
+ const progress = decodeIndexProgress(
12734
+ mapping2,
12735
+ "build",
12736
+ FLOW_QUERY_BUILD_PHASES
12737
+ );
12738
+ return Object.freeze({
12739
+ ...progress,
12740
+ scannedRecords: indexCounter(mapping2, "scanned_records", "build"),
12741
+ writtenEntries: indexCounter(mapping2, "written_entries", "build"),
12742
+ writtenBytes: indexCounter(mapping2, "written_bytes", "build")
12743
+ });
12744
+ }
12745
+ function decodeIndexValidation(value) {
12746
+ const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES index validation");
12747
+ if (!hasKey(mapping2, "failure_reason") || !hasKey(mapping2, "validated_at_ms")) {
12748
+ throw decodeError(
12749
+ "FLOW.QUERY.INDEXES index validation is missing nullable fields",
12750
+ value
12751
+ );
12752
+ }
12753
+ const progress = decodeIndexProgress(
12754
+ mapping2,
12755
+ "validation",
12756
+ FLOW_QUERY_VALIDATION_PHASES
12757
+ );
12758
+ return Object.freeze({
12759
+ ...progress,
12760
+ status: requiredChoice(
12761
+ mapping2,
12762
+ "status",
12763
+ "FLOW.QUERY.INDEXES index validation",
12764
+ ["pending", "passed", "failed"]
12765
+ ),
12766
+ checkedRecords: indexCounter(mapping2, "checked_records", "validation"),
12767
+ checkedEntries: indexCounter(mapping2, "checked_entries", "validation"),
12768
+ mismatches: indexCounter(mapping2, "mismatches", "validation"),
12769
+ failureReason: optionalBoundedText(
12770
+ mapping2,
12771
+ "failure_reason",
12772
+ "FLOW.QUERY.INDEXES index validation",
12773
+ 128
12774
+ ),
12775
+ validatedAtMs: optionalIndexInteger(
12776
+ mapping2,
12777
+ "validated_at_ms",
12778
+ "FLOW.QUERY.INDEXES index validation"
12779
+ )
12780
+ });
12781
+ }
12782
+ function decodeIndexRetirement(value) {
12783
+ const context = "FLOW.QUERY.INDEXES index retirement";
12784
+ const mapping2 = requiredMap2(value, context);
12785
+ const status = requiredChoice(mapping2, "status", context, [
12786
+ "not_applicable",
12787
+ "pending",
12788
+ "complete"
12789
+ ]);
12790
+ if (status === "not_applicable") {
12791
+ return Object.freeze({ status, raw: freezeMap(mapping2) });
12792
+ }
12793
+ const completedShards = unsignedIndexInteger(
12794
+ field(mapping2, "completed_shards"),
12795
+ `${context} completed_shards`
12796
+ );
12797
+ const totalShards = positiveIndexInteger(
12798
+ field(mapping2, "total_shards"),
12799
+ `${context} total_shards`
12800
+ );
12801
+ if (indexInteger2(completedShards) > indexInteger2(totalShards)) {
12802
+ throw decodeError(
12803
+ `${context} completed_shards exceeds total_shards`,
12804
+ mapping2
12805
+ );
12806
+ }
12807
+ return Object.freeze({
12808
+ status,
12809
+ phaseCounts: decodePhaseCounts(
12810
+ field(mapping2, "phase_counts"),
12811
+ "retirement"
12812
+ ),
12813
+ currentPhases: decodeAllowedPhases(
12814
+ field(mapping2, "current_phases"),
12815
+ "retirement",
12816
+ FLOW_QUERY_RETIREMENT_PHASES
12817
+ ),
12818
+ completedShards,
12819
+ totalShards,
12820
+ deletedEntries: indexCounter(mapping2, "deleted_entries", "retirement"),
12821
+ deletedBytes: indexCounter(mapping2, "deleted_bytes", "retirement"),
12822
+ rewrittenReverseRows: indexCounter(
12823
+ mapping2,
12824
+ "rewritten_reverse_rows",
12825
+ "retirement"
12826
+ ),
12827
+ raw: freezeMap(mapping2)
12828
+ });
12829
+ }
12830
+ function decodeIndexStatistics(value) {
12831
+ const context = "FLOW.QUERY.INDEXES index statistics";
12832
+ const mapping2 = requiredMap2(value, context);
12833
+ for (const name of [
12834
+ "oldest_collected_at_ms",
12835
+ "newest_collected_at_ms",
12836
+ "oldest_age_ms",
12837
+ "newest_age_ms"
12838
+ ]) {
12839
+ if (!hasKey(mapping2, name)) {
12840
+ throw decodeError(
12841
+ `${context} is missing required nullable fields`,
12842
+ value
12843
+ );
12844
+ }
12845
+ }
12846
+ const samples = indexCounter(mapping2, "samples", "statistics");
12847
+ const freshSamples = indexCounter(mapping2, "fresh_samples", "statistics");
12848
+ const staleSamples = indexCounter(mapping2, "stale_samples", "statistics");
12849
+ const futureSamples = indexCounter(mapping2, "future_samples", "statistics");
12850
+ if (indexInteger2(freshSamples) + indexInteger2(staleSamples) !== indexInteger2(samples) || indexInteger2(futureSamples) > indexInteger2(staleSamples)) {
12851
+ throw decodeError(`${context} counters are inconsistent`, value);
12852
+ }
12853
+ return Object.freeze({
12854
+ status: requiredChoice(mapping2, "status", context, [
12855
+ "fresh",
12856
+ "stale",
12857
+ "future",
12858
+ "mixed",
12859
+ "missing",
12860
+ "unavailable"
12861
+ ]),
12862
+ samples,
12863
+ freshSamples,
12864
+ staleSamples,
12865
+ futureSamples,
12866
+ oldestCollectedAtMs: optionalIndexInteger(
12867
+ mapping2,
12868
+ "oldest_collected_at_ms",
12869
+ context
12870
+ ),
12871
+ newestCollectedAtMs: optionalIndexInteger(
12872
+ mapping2,
12873
+ "newest_collected_at_ms",
12874
+ context
12875
+ ),
12876
+ oldestAgeMs: optionalIndexInteger(mapping2, "oldest_age_ms", context),
12877
+ newestAgeMs: optionalIndexInteger(mapping2, "newest_age_ms", context),
12878
+ raw: freezeMap(mapping2)
12879
+ });
12880
+ }
12881
+ function decodeIndexProgress(mapping2, section, allowedPhases) {
12882
+ const context = `FLOW.QUERY.INDEXES index ${section}`;
12883
+ const completedShards = unsignedIndexInteger(
12884
+ field(mapping2, "completed_shards"),
12885
+ `${context} completed_shards`
12886
+ );
12887
+ const totalShards = positiveIndexInteger(
12888
+ field(mapping2, "total_shards"),
12889
+ `${context} total_shards`
12890
+ );
12891
+ if (indexInteger2(completedShards) > indexInteger2(totalShards)) {
12892
+ throw decodeError(
12893
+ `${context} completed_shards exceeds total_shards`,
12894
+ mapping2
12895
+ );
12896
+ }
12897
+ return Object.freeze({
12898
+ scope: requiredChoice(mapping2, "scope", context, ["catalog_build"]),
12899
+ phaseCounts: decodePhaseCounts(field(mapping2, "phase_counts"), section),
12900
+ currentPhases: decodeAllowedPhases(
12901
+ field(mapping2, "current_phases"),
12902
+ section,
12903
+ allowedPhases
12318
12904
  ),
12319
- statisticsMaxAgeMs: nonNegativeInteger(
12320
- field(mapping, "statistics_max_age_ms"),
12905
+ completedShards,
12906
+ totalShards,
12907
+ raw: freezeMap(mapping2)
12908
+ });
12909
+ }
12910
+ function decodePhaseCounts(value, section) {
12911
+ const context = `FLOW.QUERY.INDEXES index ${section} phase_counts`;
12912
+ const mapping2 = requiredMap2(value, context);
12913
+ const entries = mapping2 instanceof Map ? [...mapping2.entries()] : Object.entries(mapping2);
12914
+ if (entries.length > 16) {
12915
+ throw decodeError(`${context} contains too many entries`, value);
12916
+ }
12917
+ const result = /* @__PURE__ */ Object.create(null);
12918
+ for (const [rawPhase, rawCount] of entries) {
12919
+ const phase = boundedText(rawPhase, `${context} phase`, 64);
12920
+ if (Object.hasOwn(result, phase)) {
12921
+ throw decodeError(`${context} contains duplicate phases`, value);
12922
+ }
12923
+ result[phase] = unsignedIndexInteger(rawCount, `${context} ${phase}`);
12924
+ }
12925
+ return Object.freeze(result);
12926
+ }
12927
+ function decodeAllowedPhases(value, section, allowed) {
12928
+ const phases = decodeUniqueTextArray(
12929
+ value,
12930
+ `FLOW.QUERY.INDEXES index ${section} current_phases`,
12931
+ allowed.length,
12932
+ 64
12933
+ );
12934
+ if (phases.some((phase) => !allowed.includes(phase))) {
12935
+ throw decodeError(
12936
+ `FLOW.QUERY.INDEXES index ${section} current_phases are invalid`,
12937
+ value
12938
+ );
12939
+ }
12940
+ return phases;
12941
+ }
12942
+
12943
+ // src/flow-query-index-response.ts
12944
+ var FLOW_QUERY_INDEXES_CONTRACT = "ferric.flow.query.indexes/v1";
12945
+ function decodeFlowQueryIndexStatus(value, expectedId) {
12946
+ const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES");
12947
+ requireContract(
12948
+ mapping2,
12949
+ "contract_version",
12950
+ FLOW_QUERY_INDEXES_CONTRACT,
12951
+ "FLOW.QUERY.INDEXES"
12952
+ );
12953
+ const registry = requiredMap2(
12954
+ field(mapping2, "registry"),
12955
+ "FLOW.QUERY.INDEXES registry"
12956
+ );
12957
+ const services = decodeIndexServices(field(mapping2, "services"));
12958
+ const rawIndexes = field(mapping2, "indexes");
12959
+ if (!Array.isArray(rawIndexes) || rawIndexes.length > 32) {
12960
+ throw decodeError(
12961
+ "FLOW.QUERY.INDEXES indexes must contain at most 32 entries",
12962
+ value
12963
+ );
12964
+ }
12965
+ const indexes = new Array(rawIndexes.length);
12966
+ for (let index = 0; index < rawIndexes.length; index += 1) {
12967
+ if (!Object.hasOwn(rawIndexes, index)) {
12968
+ throw decodeError(
12969
+ "FLOW.QUERY.INDEXES indexes must be a dense array",
12970
+ value
12971
+ );
12972
+ }
12973
+ indexes[index] = decodeIndex(rawIndexes[index], index);
12974
+ }
12975
+ const status = Object.freeze({
12976
+ contractVersion: FLOW_QUERY_INDEXES_CONTRACT,
12977
+ observedAtMs: unsignedIndexInteger(
12978
+ field(mapping2, "observed_at_ms"),
12979
+ "FLOW.QUERY.INDEXES observed_at_ms"
12980
+ ),
12981
+ statisticsMaxAgeMs: unsignedIndexInteger(
12982
+ field(mapping2, "statistics_max_age_ms"),
12321
12983
  "FLOW.QUERY.INDEXES statistics_max_age_ms"
12322
12984
  ),
12323
12985
  registry: Object.freeze({
12324
- epoch: boundedInteger(
12986
+ epoch: unsignedIndexInteger(
12325
12987
  field(registry, "epoch"),
12326
- MAX_UNSIGNED_64,
12327
12988
  "FLOW.QUERY.INDEXES epoch"
12328
12989
  ),
12329
- catalogVersion: positiveBoundedInteger(
12990
+ catalogVersion: positiveIndexInteger(
12330
12991
  field(registry, "catalog_version"),
12331
- MAX_UNSIGNED_64,
12332
12992
  "FLOW.QUERY.INDEXES catalog_version"
12333
12993
  )
12334
12994
  }),
12335
12995
  services,
12336
12996
  indexes: Object.freeze(indexes),
12337
- raw: freezeMap(mapping)
12997
+ raw: freezeMap(mapping2)
12998
+ });
12999
+ validateFlowQueryIndexContract(status, expectedId);
13000
+ return status;
13001
+ }
13002
+ function decodeIndex(value, index) {
13003
+ const mapping2 = requiredMap2(value, `FLOW.QUERY.INDEXES index ${index}`);
13004
+ const fields = decodeIndexFields(field(mapping2, "fields"));
13005
+ return Object.freeze({
13006
+ id: requiredBoundedText2(mapping2, "id", "FLOW.QUERY.INDEXES index", 64),
13007
+ version: positiveIndexInteger(
13008
+ field(mapping2, "version"),
13009
+ "FLOW.QUERY.INDEXES index version"
13010
+ ),
13011
+ buildId: requiredBoundedText2(
13012
+ mapping2,
13013
+ "build_id",
13014
+ "FLOW.QUERY.INDEXES index",
13015
+ 128
13016
+ ),
13017
+ source: requiredChoice(mapping2, "source", "FLOW.QUERY.INDEXES index", [
13018
+ "runs"
13019
+ ]),
13020
+ state: requiredChoice(mapping2, "state", "FLOW.QUERY.INDEXES index", [
13021
+ "building",
13022
+ "validating",
13023
+ "active",
13024
+ "retiring",
13025
+ "failed"
13026
+ ]),
13027
+ queryable: requiredBoolean(
13028
+ mapping2,
13029
+ "queryable",
13030
+ "FLOW.QUERY.INDEXES index"
13031
+ ),
13032
+ fields,
13033
+ workloads: decodeUniqueTextArray(
13034
+ field(mapping2, "workloads"),
13035
+ "FLOW.QUERY.INDEXES index workloads",
13036
+ 16,
13037
+ 64
13038
+ ),
13039
+ countPrefixes: decodeCountPrefixes(
13040
+ field(mapping2, "count_prefixes"),
13041
+ fields.length
13042
+ ),
13043
+ coveringFields: decodeCoveringFields(mapping2),
13044
+ format: decodeIndexFormat(mapping2),
13045
+ coverage: decodeIndexCoverage(field(mapping2, "coverage")),
13046
+ build: decodeIndexBuild(field(mapping2, "build")),
13047
+ validation: decodeIndexValidation(field(mapping2, "validation")),
13048
+ retirement: decodeIndexRetirement(field(mapping2, "retirement")),
13049
+ statistics: decodeIndexStatistics(field(mapping2, "statistics")),
13050
+ raw: freezeMap(mapping2)
13051
+ });
13052
+ }
13053
+ function decodeIndexServices(value) {
13054
+ const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES services");
13055
+ const choices = ["ready", "unavailable"];
13056
+ return Object.freeze({
13057
+ registry: requiredChoice(
13058
+ mapping2,
13059
+ "registry",
13060
+ "FLOW.QUERY.INDEXES services",
13061
+ choices
13062
+ ),
13063
+ lifecycleWorker: requiredChoice(
13064
+ mapping2,
13065
+ "lifecycle_worker",
13066
+ "FLOW.QUERY.INDEXES services",
13067
+ choices
13068
+ ),
13069
+ statisticsStore: requiredChoice(
13070
+ mapping2,
13071
+ "statistics_store",
13072
+ "FLOW.QUERY.INDEXES services",
13073
+ choices
13074
+ ),
13075
+ statisticsWorker: requiredChoice(
13076
+ mapping2,
13077
+ "statistics_worker",
13078
+ "FLOW.QUERY.INDEXES services",
13079
+ choices
13080
+ ),
13081
+ raw: freezeMap(mapping2)
13082
+ });
13083
+ }
13084
+ function decodeIndexFields(value) {
13085
+ const context = "FLOW.QUERY.INDEXES index fields";
13086
+ if (!Array.isArray(value) || value.length < 2 || value.length > 8) {
13087
+ throw decodeError(`${context} must contain 2 to 8 entries`, value);
13088
+ }
13089
+ const fields = new Array(value.length);
13090
+ const names = /* @__PURE__ */ new Set();
13091
+ for (let index = 0; index < value.length; index += 1) {
13092
+ if (!Object.hasOwn(value, index)) {
13093
+ throw decodeError(`${context} must be dense`, value);
13094
+ }
13095
+ const mapping2 = requiredMap2(
13096
+ value[index],
13097
+ `FLOW.QUERY.INDEXES index field ${index}`
13098
+ );
13099
+ const name = requiredBoundedText2(
13100
+ mapping2,
13101
+ "name",
13102
+ "FLOW.QUERY.INDEXES index field",
13103
+ 512
13104
+ );
13105
+ if (names.has(name)) {
13106
+ throw decodeError(`${context} contain duplicates`, value);
13107
+ }
13108
+ names.add(name);
13109
+ fields[index] = Object.freeze({
13110
+ name,
13111
+ direction: requiredChoice(
13112
+ mapping2,
13113
+ "direction",
13114
+ "FLOW.QUERY.INDEXES index field",
13115
+ ["asc", "desc"]
13116
+ ),
13117
+ encoding: requiredChoice(
13118
+ mapping2,
13119
+ "encoding",
13120
+ "FLOW.QUERY.INDEXES index field",
13121
+ ["hashed", "ordered"]
13122
+ ),
13123
+ raw: freezeMap(mapping2)
13124
+ });
13125
+ }
13126
+ return Object.freeze(fields);
13127
+ }
13128
+ function decodeCountPrefixes(value, fieldCount) {
13129
+ const context = "FLOW.QUERY.INDEXES index count_prefixes";
13130
+ if (!Array.isArray(value) || value.length > fieldCount) {
13131
+ throw decodeError(`${context} are invalid`, value);
13132
+ }
13133
+ const prefixes = new Array(value.length);
13134
+ let previous = 0;
13135
+ for (let index = 0; index < value.length; index += 1) {
13136
+ if (!Object.hasOwn(value, index)) {
13137
+ throw decodeError(`${context} must be dense`, value);
13138
+ }
13139
+ const prefix = positiveInteger(value[index], `${context} entry`);
13140
+ if (prefix > fieldCount || prefix <= previous) {
13141
+ throw decodeError(`${context} are invalid`, value);
13142
+ }
13143
+ prefixes[index] = prefix;
13144
+ previous = prefix;
13145
+ }
13146
+ return Object.freeze(prefixes);
13147
+ }
13148
+ function decodeCoveringFields(mapping2) {
13149
+ const raw = field(mapping2, "covering_fields");
13150
+ if (!Array.isArray(raw) || raw.length > 32) {
13151
+ throw decodeError(
13152
+ "FLOW.QUERY.INDEXES index covering_fields must contain at most 32 entries",
13153
+ raw
13154
+ );
13155
+ }
13156
+ const fields = new Array(raw.length);
13157
+ const seen = /* @__PURE__ */ new Set();
13158
+ for (let index = 0; index < raw.length; index += 1) {
13159
+ if (!Object.hasOwn(raw, index)) {
13160
+ throw decodeError(
13161
+ "FLOW.QUERY.INDEXES index covering_fields must be dense",
13162
+ raw
13163
+ );
13164
+ }
13165
+ const value = boundedText(
13166
+ raw[index],
13167
+ `FLOW.QUERY.INDEXES index covering_fields entry ${index}`,
13168
+ 512
13169
+ );
13170
+ if (seen.has(value)) {
13171
+ throw decodeError(
13172
+ "FLOW.QUERY.INDEXES index covering_fields contains duplicates",
13173
+ raw
13174
+ );
13175
+ }
13176
+ seen.add(value);
13177
+ fields[index] = value;
13178
+ }
13179
+ return Object.freeze(fields);
13180
+ }
13181
+ function decodeIndexFormat(mapping2) {
13182
+ const raw = requiredMap2(
13183
+ field(mapping2, "format"),
13184
+ "FLOW.QUERY.INDEXES index format"
13185
+ );
13186
+ if (!hasKey(raw, "counter")) {
13187
+ throw decodeError(
13188
+ "FLOW.QUERY.INDEXES index format counter is missing",
13189
+ raw
13190
+ );
13191
+ }
13192
+ const counterValue = field(raw, "counter");
13193
+ const counter = counterValue === null ? void 0 : boundedText(
13194
+ counterValue,
13195
+ "FLOW.QUERY.INDEXES index format counter",
13196
+ 128
13197
+ );
13198
+ return Object.freeze({
13199
+ queryRow: requiredBoundedText2(
13200
+ raw,
13201
+ "query_row",
13202
+ "FLOW.QUERY.INDEXES index format",
13203
+ 128
13204
+ ),
13205
+ key: requiredBoundedText2(
13206
+ raw,
13207
+ "key",
13208
+ "FLOW.QUERY.INDEXES index format",
13209
+ 128
13210
+ ),
13211
+ entry: requiredBoundedText2(
13212
+ raw,
13213
+ "entry",
13214
+ "FLOW.QUERY.INDEXES index format",
13215
+ 128
13216
+ ),
13217
+ reverse: requiredBoundedText2(
13218
+ raw,
13219
+ "reverse",
13220
+ "FLOW.QUERY.INDEXES index format",
13221
+ 128
13222
+ ),
13223
+ counter,
13224
+ raw: freezeMap(raw)
13225
+ });
13226
+ }
13227
+
13228
+ // src/flow-query-response.ts
13229
+ var FLOW_QUERY_RESULT_CONTRACT = "ferric.flow.query.result/v1";
13230
+ var FLOW_EXPLAIN_CONTRACT = "ferric.flow.explain/v1";
13231
+ var USAGE_FIELDS2 = [
13232
+ ["range_seeks", "rangeSeeks"],
13233
+ ["range_pages", "rangePages"],
13234
+ ["scanned_entries", "scannedEntries"],
13235
+ ["scanned_bytes", "scannedBytes"],
13236
+ ["hydrated_records", "hydratedRecords"],
13237
+ ["residual_checks", "residualChecks"],
13238
+ ["duplicate_entries", "duplicateEntries"],
13239
+ ["result_records", "resultRecords"],
13240
+ ["response_bytes", "responseBytes"],
13241
+ ["memory_high_water_bytes", "memoryHighWaterBytes"],
13242
+ ["wall_time_us", "wallTimeUs"]
13243
+ ];
13244
+ var MAX_SIGNED_643 = (1n << 63n) - 1n;
13245
+ var QUALITY_VALUES2 = {
13246
+ exactness: ["authoritative", "projected_exact", "exact", "not_applicable"],
13247
+ freshness: ["current", "projection_watermark", "not_applicable"],
13248
+ coverage: ["complete", "unavailable"],
13249
+ pagination: ["none", "complete", "authenticated_seek", "live_seek"]
13250
+ };
13251
+ function decodeFlowQueryResult(value) {
13252
+ const result = decodeFlowQueryResponse(value, (mapping2) => freezeMap(mapping2));
13253
+ if (result.kind === "records") {
13254
+ return Object.freeze({
13255
+ kind: "records",
13256
+ version: FLOW_QUERY_RESULT_CONTRACT,
13257
+ records: Object.freeze(result.records),
13258
+ page: result.page,
13259
+ quality: result.quality,
13260
+ usage: result.usage,
13261
+ raw: result.raw
13262
+ });
13263
+ }
13264
+ return Object.freeze({
13265
+ kind: "count",
13266
+ version: FLOW_QUERY_RESULT_CONTRACT,
13267
+ count: result.count,
13268
+ quality: result.quality,
13269
+ usage: result.usage,
13270
+ raw: result.raw
13271
+ });
13272
+ }
13273
+ function decodeFlowQueryRecords(value, decodeRecord) {
13274
+ const result = decodeFlowQueryResponse(
13275
+ value,
13276
+ (mapping2, index) => decodeRecord(mapping2 instanceof Map ? freezeMap(mapping2) : mapping2, index)
13277
+ );
13278
+ if (result.kind === "count") {
13279
+ throw new FerricStoreError("Flow record convenience query returned a count result", {
13280
+ raw: result.raw
13281
+ });
13282
+ }
13283
+ return result.records;
13284
+ }
13285
+ function decodeFlowQueryResponse(value, decodeRecord) {
13286
+ const mapping2 = requiredMap2(value, "FLOW.QUERY result");
13287
+ requireContract(mapping2, "version", FLOW_QUERY_RESULT_CONTRACT, "FLOW.QUERY result");
13288
+ const quality = decodeQuality(field(mapping2, "quality"));
13289
+ const usage = decodeUsage(field(mapping2, "usage"));
13290
+ const hasRecords = hasKey(mapping2, "records");
13291
+ const hasCount = hasKey(mapping2, "result");
13292
+ if (hasRecords === hasCount) {
13293
+ throw decodeError("FLOW.QUERY result must contain exactly one records or count shape", value);
13294
+ }
13295
+ const raw = freezeMap(mapping2);
13296
+ if (hasRecords) {
13297
+ const rawRecords = field(mapping2, "records");
13298
+ if (!Array.isArray(rawRecords) || rawRecords.length > 100) {
13299
+ throw decodeError("FLOW.QUERY records must be an array of at most 100 maps", value);
13300
+ }
13301
+ const records = new Array(rawRecords.length);
13302
+ for (let index = 0; index < rawRecords.length; index += 1) {
13303
+ if (!Object.hasOwn(rawRecords, index)) {
13304
+ throw decodeError("FLOW.QUERY records must be a dense array", value);
13305
+ }
13306
+ records[index] = decodeRecord(
13307
+ requiredMap2(rawRecords[index], `FLOW.QUERY record ${index}`),
13308
+ index
13309
+ );
13310
+ }
13311
+ if (usage.resultRecords !== records.length || usage.resultRecords > usage.scannedEntries) {
13312
+ throw decodeError("FLOW.QUERY usage result_records is inconsistent with records", value);
13313
+ }
13314
+ return {
13315
+ kind: "records",
13316
+ records,
13317
+ page: decodePage(field(mapping2, "page")),
13318
+ quality,
13319
+ usage,
13320
+ raw
13321
+ };
13322
+ }
13323
+ if (hasKey(mapping2, "page")) {
13324
+ throw decodeError("FLOW.QUERY count result contains an unexpected page", value);
13325
+ }
13326
+ const countResult = requiredMap2(field(mapping2, "result"), "FLOW.QUERY count result");
13327
+ if (requiredText2(countResult, "kind", "FLOW.QUERY count result") !== "count") {
13328
+ throw decodeError("FLOW.QUERY count result kind must be count", value);
13329
+ }
13330
+ const count = boundedInteger(
13331
+ field(countResult, "value"),
13332
+ MAX_SIGNED_643,
13333
+ "FLOW.QUERY count value"
13334
+ );
13335
+ if (usage.resultRecords !== 1) {
13336
+ throw decodeError("FLOW.QUERY count usage result_records must be 1", value);
13337
+ }
13338
+ return {
13339
+ kind: "count",
13340
+ count,
13341
+ quality,
13342
+ usage,
13343
+ raw
13344
+ };
13345
+ }
13346
+ function decodeFlowExplainResult(value) {
13347
+ const mapping2 = requiredMap2(value, "FLOW.QUERY explain");
13348
+ requireContract(mapping2, "version", FLOW_EXPLAIN_CONTRACT, "FLOW.QUERY explain");
13349
+ const queryFingerprint = requiredText2(mapping2, "query_fingerprint", "FLOW.QUERY explain");
13350
+ if (!/^[0-9a-f]{64}$/iu.test(queryFingerprint)) {
13351
+ throw decodeError("FLOW.QUERY explain query_fingerprint is invalid", value);
13352
+ }
13353
+ const status = requiredText2(mapping2, "status", "FLOW.QUERY explain");
13354
+ if (status !== "planned" && status !== "rejected" && status !== "executed") {
13355
+ throw decodeError(`FLOW.QUERY explain status ${JSON.stringify(status)} is unsupported`, value);
13356
+ }
13357
+ const plan = freezeMetadataMap(
13358
+ requiredMap2(field(mapping2, "plan"), "FLOW.QUERY explain plan"),
13359
+ "FLOW.QUERY explain plan"
13360
+ );
13361
+ const estimate = freezeMetadataMap(
13362
+ requiredMap2(field(mapping2, "estimate"), "FLOW.QUERY explain estimate"),
13363
+ "FLOW.QUERY explain estimate"
13364
+ );
13365
+ const bounds = freezeMetadataMap(
13366
+ requiredMap2(field(mapping2, "bounds"), "FLOW.QUERY explain bounds"),
13367
+ "FLOW.QUERY explain bounds"
13368
+ );
13369
+ const capabilities = decodeExplainCapabilities(mapping2);
13370
+ const extendedFields = ["stats", "quality", "pressure", "decision", "alternatives"];
13371
+ const extendedPresence = extendedFields.map((name) => hasKey(mapping2, name));
13372
+ const specialized = capabilities != null && !extendedPresence.some(Boolean);
13373
+ let stats;
13374
+ let quality;
13375
+ let pressure;
13376
+ let decision;
13377
+ let alternatives;
13378
+ if (specialized) {
13379
+ if (status !== "planned") {
13380
+ throw decodeError("FLOW.QUERY specialized explain must be planned", value);
13381
+ }
13382
+ if (hasKey(mapping2, "actual") || hasKey(mapping2, "diagnostic")) {
13383
+ throw decodeError("FLOW.QUERY specialized explain has extended status fields", value);
13384
+ }
13385
+ alternatives = Object.freeze([]);
13386
+ } else {
13387
+ if (!extendedPresence.every(Boolean) || !hasKey(mapping2, "actual") || !hasKey(mapping2, "diagnostic")) {
13388
+ throw decodeError("FLOW.QUERY explain is missing required v1 fields", value);
13389
+ }
13390
+ stats = freezeMetadataMap(
13391
+ requiredMap2(field(mapping2, "stats"), "FLOW.QUERY explain stats"),
13392
+ "FLOW.QUERY explain stats"
13393
+ );
13394
+ quality = decodeQuality(field(mapping2, "quality"));
13395
+ pressure = freezeMetadataMap(
13396
+ requiredMap2(field(mapping2, "pressure"), "FLOW.QUERY explain pressure"),
13397
+ "FLOW.QUERY explain pressure"
13398
+ );
13399
+ decision = freezeMetadataMap(
13400
+ requiredMap2(field(mapping2, "decision"), "FLOW.QUERY explain decision"),
13401
+ "FLOW.QUERY explain decision"
13402
+ );
13403
+ alternatives = decodeExplainAlternatives(field(mapping2, "alternatives"));
13404
+ }
13405
+ const actualValue = field(mapping2, "actual");
13406
+ let actual;
13407
+ if (status === "executed") {
13408
+ if (actualValue == null) {
13409
+ throw decodeError("FLOW.QUERY executed explain is missing actual usage", value);
13410
+ }
13411
+ actual = decodeUsage(actualValue);
13412
+ } else if (actualValue != null) {
13413
+ throw decodeError("FLOW.QUERY non-executed explain contains actual usage", value);
13414
+ }
13415
+ const diagnosticValue = field(mapping2, "diagnostic");
13416
+ let diagnostic;
13417
+ if (status === "rejected") {
13418
+ diagnostic = tryDecodeFlowQueryError(diagnosticValue);
13419
+ if (diagnostic == null) {
13420
+ throw decodeError("FLOW.QUERY rejected explain has an invalid diagnostic", value);
13421
+ }
13422
+ } else if (diagnosticValue != null) {
13423
+ throw decodeError("FLOW.QUERY non-rejected explain contains a diagnostic", value);
13424
+ }
13425
+ return Object.freeze({
13426
+ version: FLOW_EXPLAIN_CONTRACT,
13427
+ queryFingerprint,
13428
+ status,
13429
+ plan,
13430
+ estimate,
13431
+ stats,
13432
+ quality,
13433
+ bounds,
13434
+ pressure,
13435
+ decision,
13436
+ alternatives,
13437
+ capabilities,
13438
+ actual,
13439
+ diagnostic,
13440
+ raw: freezeMap(mapping2)
12338
13441
  });
12339
13442
  }
12340
- function decodeIndex(value, index) {
12341
- const mapping = requiredMap2(value, `FLOW.QUERY.INDEXES index ${index}`);
13443
+ function decodeExplainCapabilities(mapping2) {
13444
+ if (!hasKey(mapping2, "capabilities")) return void 0;
13445
+ const value = requiredMap2(
13446
+ field(mapping2, "capabilities"),
13447
+ "FLOW.QUERY explain capabilities"
13448
+ );
12342
13449
  return Object.freeze({
12343
- id: requiredText2(mapping, "id", "FLOW.QUERY.INDEXES index"),
12344
- version: positiveBoundedInteger(
12345
- field(mapping, "version"),
12346
- MAX_UNSIGNED_64,
12347
- "FLOW.QUERY.INDEXES index version"
12348
- ),
12349
- buildId: requiredText2(mapping, "build_id", "FLOW.QUERY.INDEXES index"),
12350
- state: requiredText2(mapping, "state", "FLOW.QUERY.INDEXES index"),
12351
- queryable: requiredBoolean(mapping, "queryable", "FLOW.QUERY.INDEXES index"),
12352
- coveringFields: decodeCoveringFields(mapping),
12353
- format: decodeIndexFormat(mapping),
12354
- raw: freezeMap(mapping)
13450
+ requested: decodeExplainCapabilityList(field(value, "requested"), "requested"),
13451
+ available: decodeExplainCapabilityList(field(value, "available"), "available"),
13452
+ missing: decodeExplainCapabilityList(field(value, "missing"), "missing"),
13453
+ raw: freezeMetadataMap(value, "FLOW.QUERY explain capabilities")
12355
13454
  });
12356
13455
  }
12357
- function decodeCoveringFields(mapping) {
12358
- const raw = field(mapping, "covering_fields");
12359
- if (!Array.isArray(raw) || raw.length > 32) {
13456
+ function decodeExplainCapabilityList(value, name) {
13457
+ if (!Array.isArray(value) || value.length > 64) {
12360
13458
  throw decodeError(
12361
- "FLOW.QUERY.INDEXES index covering_fields must contain at most 32 entries",
12362
- raw
13459
+ `FLOW.QUERY explain capabilities ${name} must contain at most 64 entries`,
13460
+ value
12363
13461
  );
12364
13462
  }
12365
- const fields = new Array(raw.length);
13463
+ const result = new Array(value.length);
12366
13464
  const seen = /* @__PURE__ */ new Set();
12367
- for (let index = 0; index < raw.length; index += 1) {
12368
- if (!Object.hasOwn(raw, index)) {
12369
- throw decodeError("FLOW.QUERY.INDEXES index covering_fields must be dense", raw);
13465
+ for (let index = 0; index < value.length; index += 1) {
13466
+ if (!Object.hasOwn(value, index)) {
13467
+ throw decodeError(`FLOW.QUERY explain capabilities ${name} must be dense`, value);
12370
13468
  }
12371
- const value = boundedText(
12372
- raw[index],
12373
- `FLOW.QUERY.INDEXES index covering_fields entry ${index}`,
12374
- 512
13469
+ const capability = boundedText(
13470
+ value[index],
13471
+ `FLOW.QUERY explain capabilities ${name} entry ${index}`,
13472
+ 128
12375
13473
  );
12376
- if (seen.has(value)) {
12377
- throw decodeError("FLOW.QUERY.INDEXES index covering_fields contains duplicates", raw);
13474
+ if (seen.has(capability)) {
13475
+ throw decodeError(
13476
+ `FLOW.QUERY explain capabilities ${name} contains duplicates`,
13477
+ value
13478
+ );
12378
13479
  }
12379
- seen.add(value);
12380
- fields[index] = value;
13480
+ seen.add(capability);
13481
+ result[index] = capability;
12381
13482
  }
12382
- return Object.freeze(fields);
13483
+ return Object.freeze(result);
12383
13484
  }
12384
- function decodeIndexFormat(mapping) {
12385
- const raw = requiredMap2(field(mapping, "format"), "FLOW.QUERY.INDEXES index format");
12386
- if (!hasKey(raw, "counter")) {
12387
- throw decodeError("FLOW.QUERY.INDEXES index format counter is missing", raw);
13485
+ function decodeExplainAlternatives(value) {
13486
+ if (!Array.isArray(value) || value.length > 31) {
13487
+ throw decodeError("FLOW.QUERY explain alternatives must be an array of at most 31 maps", value);
12388
13488
  }
12389
- const counterValue = field(raw, "counter");
12390
- const counter = counterValue === null ? void 0 : boundedText(counterValue, "FLOW.QUERY.INDEXES index format counter", 128);
12391
- return Object.freeze({
12392
- queryRow: requiredBoundedText2(raw, "query_row", "FLOW.QUERY.INDEXES index format", 128),
12393
- key: requiredBoundedText2(raw, "key", "FLOW.QUERY.INDEXES index format", 128),
12394
- entry: requiredBoundedText2(raw, "entry", "FLOW.QUERY.INDEXES index format", 128),
12395
- reverse: requiredBoundedText2(raw, "reverse", "FLOW.QUERY.INDEXES index format", 128),
12396
- counter,
12397
- raw: freezeMap(raw)
12398
- });
13489
+ const alternatives = new Array(value.length);
13490
+ for (let index = 0; index < value.length; index += 1) {
13491
+ if (!Object.hasOwn(value, index)) {
13492
+ throw decodeError("FLOW.QUERY explain alternatives must be dense", value);
13493
+ }
13494
+ alternatives[index] = freezeMetadataMap(
13495
+ requiredMap2(value[index], `FLOW.QUERY explain alternative ${index}`),
13496
+ `FLOW.QUERY explain alternative ${index}`
13497
+ );
13498
+ }
13499
+ return Object.freeze(alternatives);
12399
13500
  }
12400
13501
  function decodeQuality(value) {
12401
- const mapping = requiredMap2(value, "FLOW.QUERY quality");
13502
+ const mapping2 = requiredMap2(value, "FLOW.QUERY quality");
12402
13503
  return Object.freeze({
12403
- exactness: requiredBoundedText2(mapping, "exactness", "FLOW.QUERY quality", 64),
12404
- freshness: requiredBoundedText2(mapping, "freshness", "FLOW.QUERY quality", 64),
12405
- coverage: requiredBoundedText2(mapping, "coverage", "FLOW.QUERY quality", 64),
12406
- pagination: requiredBoundedText2(mapping, "pagination", "FLOW.QUERY quality", 64)
13504
+ exactness: decodeQualityValue(mapping2, "exactness", QUALITY_VALUES2.exactness),
13505
+ freshness: decodeQualityValue(mapping2, "freshness", QUALITY_VALUES2.freshness),
13506
+ coverage: decodeQualityValue(mapping2, "coverage", QUALITY_VALUES2.coverage),
13507
+ pagination: decodeQualityValue(mapping2, "pagination", QUALITY_VALUES2.pagination)
12407
13508
  });
12408
13509
  }
13510
+ function decodeQualityValue(mapping2, name, allowed) {
13511
+ const value = requiredBoundedText2(mapping2, name, "FLOW.QUERY quality", 64);
13512
+ if (!allowed.includes(value)) {
13513
+ throw decodeError(`FLOW.QUERY quality ${name} is unsupported`, mapping2);
13514
+ }
13515
+ return value;
13516
+ }
12409
13517
  function decodeUsage(value) {
12410
- const mapping = requiredMap2(value, "FLOW.QUERY usage");
13518
+ const mapping2 = requiredMap2(value, "FLOW.QUERY usage");
12411
13519
  const usage = /* @__PURE__ */ Object.create(null);
12412
13520
  for (const [wireName, property] of USAGE_FIELDS2) {
12413
13521
  usage[property] = nonNegativeInteger(
12414
- field(mapping, wireName),
13522
+ field(mapping2, wireName),
12415
13523
  `FLOW.QUERY usage ${wireName}`
12416
13524
  );
12417
13525
  }
12418
- return Object.freeze(usage);
13526
+ const decoded = usage;
13527
+ if (decoded.hydratedRecords > decoded.scannedEntries || decoded.duplicateEntries > decoded.scannedEntries || decoded.rangePages > decoded.scannedEntries + decoded.rangeSeeks || decoded.residualChecks > decoded.scannedEntries * 12) {
13528
+ throw decodeError("FLOW.QUERY usage counters are inconsistent", value);
13529
+ }
13530
+ return Object.freeze(decoded);
12419
13531
  }
12420
13532
  function decodePage(value) {
12421
- const mapping = requiredMap2(value, "FLOW.QUERY page");
12422
- const hasMore = requiredBoolean(mapping, "has_more", "FLOW.QUERY page");
12423
- const cursor = optionalText2(mapping, "cursor", "FLOW.QUERY page");
12424
- if (cursor != null && (!cursor.startsWith("fqc1_") || import_node_buffer45.Buffer.byteLength(cursor) > 4096)) {
13533
+ const mapping2 = requiredMap2(value, "FLOW.QUERY page");
13534
+ const hasMore = requiredBoolean(mapping2, "has_more", "FLOW.QUERY page");
13535
+ const cursor = optionalText2(mapping2, "cursor", "FLOW.QUERY page");
13536
+ if (cursor != null && (!cursor.startsWith("fqc1_") || import_node_buffer48.Buffer.byteLength(cursor) < 16 || import_node_buffer48.Buffer.byteLength(cursor) > 4096)) {
12425
13537
  throw decodeError("FLOW.QUERY page cursor is invalid", value);
12426
13538
  }
12427
13539
  if (hasMore !== (cursor != null)) {
@@ -12429,26 +13541,14 @@ function decodePage(value) {
12429
13541
  }
12430
13542
  return Object.freeze({ hasMore, ...cursor == null ? {} : { cursor } });
12431
13543
  }
12432
- function decodePosition(value) {
12433
- if (value == null) return void 0;
12434
- const mapping = requiredMap2(value, "FLOW.QUERY diagnostic position");
12435
- return Object.freeze({
12436
- byte: positiveInteger(field(mapping, "byte"), "FLOW.QUERY diagnostic position byte"),
12437
- line: positiveInteger(field(mapping, "line"), "FLOW.QUERY diagnostic position line"),
12438
- column: positiveInteger(
12439
- field(mapping, "column"),
12440
- "FLOW.QUERY diagnostic position column"
12441
- )
12442
- });
12443
- }
12444
13544
 
12445
13545
  // src/client-core.ts
12446
- var import_node_buffer50 = require("buffer");
13546
+ var import_node_buffer53 = require("buffer");
12447
13547
 
12448
13548
  // src/client-core-helpers.ts
12449
- var import_node_buffer46 = require("buffer");
13549
+ var import_node_buffer49 = require("buffer");
12450
13550
  function bgsaveResponse(response) {
12451
- if ((typeof response === "string" || import_node_buffer46.Buffer.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
13551
+ if ((typeof response === "string" || import_node_buffer49.Buffer.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
12452
13552
  return true;
12453
13553
  }
12454
13554
  return okResponse(response);
@@ -12462,7 +13562,7 @@ function fetchOrComputeCompletionToken(options) {
12462
13562
  "fetch-or-compute completion requires computeToken"
12463
13563
  );
12464
13564
  }
12465
- if (!import_node_buffer46.Buffer.isBuffer(options.computeToken)) {
13565
+ if (!import_node_buffer49.Buffer.isBuffer(options.computeToken)) {
12466
13566
  throw new TypeError("fetch-or-compute computeToken must be a Buffer");
12467
13567
  }
12468
13568
  return options.computeToken;
@@ -12485,10 +13585,10 @@ function unsupportedClientCaching() {
12485
13585
  }
12486
13586
 
12487
13587
  // src/client-base.ts
12488
- var import_node_buffer48 = require("buffer");
13588
+ var import_node_buffer51 = require("buffer");
12489
13589
 
12490
13590
  // src/client-executor.ts
12491
- var import_node_buffer47 = require("buffer");
13591
+ var import_node_buffer50 = require("buffer");
12492
13592
  var ErrorMappingExecutor = class {
12493
13593
  constructor(executor) {
12494
13594
  this.executor = executor;
@@ -12548,12 +13648,12 @@ var ErrorMappingExecutor = class {
12548
13648
 
12549
13649
  // src/client-config.ts
12550
13650
  function snapshotClientOptions(options) {
12551
- const own = { ...options };
13651
+ const own2 = { ...options };
12552
13652
  const snapshot = {
12553
- ...own,
12554
- ...typeof own.autoBatch === "object" && own.autoBatch != null ? { autoBatch: Object.freeze({ ...own.autoBatch }) } : {},
12555
- ...own.backpressure == null ? {} : { backpressure: Object.freeze({ ...own.backpressure }) },
12556
- ...typeof own.reconnect === "object" && own.reconnect != null ? { reconnect: Object.freeze({ ...own.reconnect }) } : {}
13653
+ ...own2,
13654
+ ...typeof own2.autoBatch === "object" && own2.autoBatch != null ? { autoBatch: Object.freeze({ ...own2.autoBatch }) } : {},
13655
+ ...own2.backpressure == null ? {} : { backpressure: Object.freeze({ ...own2.backpressure }) },
13656
+ ...typeof own2.reconnect === "object" && own2.reconnect != null ? { reconnect: Object.freeze({ ...own2.reconnect }) } : {}
12557
13657
  };
12558
13658
  return Object.freeze(snapshot);
12559
13659
  }
@@ -12822,14 +13922,14 @@ var FerricStoreAdministrationClient = class extends FerricStoreClientBase {
12822
13922
  };
12823
13923
 
12824
13924
  // src/store-utilities.ts
12825
- var import_node_buffer49 = require("buffer");
13925
+ var import_node_buffer52 = require("buffer");
12826
13926
  function encode(codec, value) {
12827
13927
  return codec.encode(value);
12828
13928
  }
12829
13929
  function decode(codec, value) {
12830
13930
  if (value == null) return null;
12831
- if (import_node_buffer49.Buffer.isBuffer(value)) return codec.decode(value);
12832
- if (value instanceof Uint8Array) return codec.decode(import_node_buffer49.Buffer.from(value));
13931
+ if (import_node_buffer52.Buffer.isBuffer(value)) return codec.decode(value);
13932
+ if (value instanceof Uint8Array) return codec.decode(import_node_buffer52.Buffer.from(value));
12833
13933
  return value;
12834
13934
  }
12835
13935
  function number(value) {
@@ -14742,33 +15842,511 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
14742
15842
  }
14743
15843
  };
14744
15844
 
14745
- // src/client-management.ts
14746
- var FerricStoreManagementClient = class extends FerricStoreClientCore {
14747
- async stats(type, options = {}) {
14748
- const args = ["FLOW.STATS", type];
14749
- append(args, "STATE", options.state);
14750
- append(args, "COUNT", options.count);
14751
- append(args, "PARTITION", options.partitionKey);
14752
- appendAttributes(args, options.attributes);
14753
- appendBool(args, "CONSISTENT_PROJECTION", options.consistentProjection);
14754
- return adminRecordResponse(await this.commandArgs(args), "FLOW.STATS");
15845
+ // src/schedule-request.ts
15846
+ var MAX_EXACT_INTEGER = Number.MAX_SAFE_INTEGER;
15847
+ var SCHEDULE_KINDS = /* @__PURE__ */ new Set(["one_shot", "delay", "interval", "cron"]);
15848
+ var RECURRING_KINDS = /* @__PURE__ */ new Set(["interval", "cron"]);
15849
+ var OVERLAP_POLICIES = /* @__PURE__ */ new Set([
15850
+ "allow",
15851
+ "skip",
15852
+ "queue_after_previous",
15853
+ "fail_schedule"
15854
+ ]);
15855
+ var SCHEDULE_STATES = /* @__PURE__ */ new Set([
15856
+ "active",
15857
+ "paused",
15858
+ "running",
15859
+ "completed",
15860
+ "failed",
15861
+ "cancelled",
15862
+ "all"
15863
+ ]);
15864
+ var TARGET_FIELDS = /* @__PURE__ */ new Set([
15865
+ "correlation_id",
15866
+ "id",
15867
+ "id_prefix",
15868
+ "parent_flow_id",
15869
+ "partition_key",
15870
+ "payload",
15871
+ "payload_ref",
15872
+ "priority",
15873
+ "root_flow_id",
15874
+ "run_at_ms",
15875
+ "state",
15876
+ "type",
15877
+ "value_refs",
15878
+ "values"
15879
+ ]);
15880
+ var TARGET_TEXT_FIELDS = [
15881
+ "state",
15882
+ "id",
15883
+ "id_prefix",
15884
+ "partition_key",
15885
+ "correlation_id",
15886
+ "parent_flow_id",
15887
+ "root_flow_id",
15888
+ "payload_ref"
15889
+ ];
15890
+ var INTERNAL_SCHEDULE_TYPE = "__ferricstore_schedule";
15891
+ var INTERNAL_SCHEDULE_ID_PREFIX = "__ferricstore_schedule__:";
15892
+ function validateScheduleCreate(id, options) {
15893
+ requiredText3(id, "id");
15894
+ if (!mapping(options)) throw new TypeError("schedule options must be a mapping");
15895
+ const kind = effectiveKind(options);
15896
+ validateTiming(kind, options);
15897
+ const recurring = RECURRING_KINDS.has(kind);
15898
+ validateTarget(options.target, recurring);
15899
+ nonNegativeInteger2(options.atMs, "at_ms");
15900
+ nonNegativeInteger2(options.delayMs, "delay_ms");
15901
+ nonNegativeInteger2(options.startAtMs, "start_at_ms");
15902
+ positiveInteger2(options.everyMs, "every_ms");
15903
+ positiveInteger2(options.overlapRetryMs, "overlap_retry_ms");
15904
+ positiveInteger2(options.maxFires, "max_fires");
15905
+ nonNegativeInteger2(options.endAtMs, "end_at_ms");
15906
+ nonNegativeInteger2(options.nowMs, "now_ms");
15907
+ optionalBoolean(options.overwrite, "overwrite");
15908
+ if (kind === "delay") {
15909
+ if (options.delayMs == null) throw new TypeError("delay_ms is required for delay schedules");
15910
+ if (options.nowMs != null && options.nowMs > MAX_EXACT_INTEGER - options.delayMs) {
15911
+ throw new TypeError("now_ms plus delay_ms exceeds the exact integer range");
15912
+ }
15913
+ }
15914
+ if (kind === "interval" && options.everyMs == null) {
15915
+ throw new TypeError("every_ms is required for interval schedules");
15916
+ }
15917
+ if (kind === "cron") {
15918
+ requiredText3(options.cron, "cron");
15919
+ optionalText3(options.timezone, "timezone");
15920
+ } else if (options.timezone != null) {
15921
+ throw new TypeError("timezone is only supported for cron schedules");
15922
+ }
15923
+ if (kind === "interval") {
15924
+ if (options.catchupPolicy != null && options.catchupPolicy !== "fire_once") {
15925
+ throw new TypeError("catchup_policy must be fire_once");
15926
+ }
15927
+ } else if (options.catchupPolicy != null) {
15928
+ throw new TypeError("catchup_policy is only supported for interval schedules");
15929
+ }
15930
+ if (recurring) {
15931
+ if (options.overlapPolicy != null && !OVERLAP_POLICIES.has(options.overlapPolicy)) {
15932
+ throw new TypeError(
15933
+ "overlap_policy must be allow, skip, queue_after_previous, or fail_schedule"
15934
+ );
15935
+ }
15936
+ if (options.overlapRetryMs != null && options.overlapPolicy !== "queue_after_previous") {
15937
+ throw new TypeError("overlap_retry_ms requires overlap_policy queue_after_previous");
15938
+ }
15939
+ const firstRun = knownFirstRun(kind, options);
15940
+ if (options.endAtMs != null && firstRun != null && options.endAtMs < firstRun) {
15941
+ throw new TypeError("end_at_ms must be at or after first run");
15942
+ }
15943
+ } else {
15944
+ if (options.overlapPolicy != null) {
15945
+ throw new TypeError("overlap_policy is only supported for recurring schedules");
15946
+ }
15947
+ if (options.overlapRetryMs != null) {
15948
+ throw new TypeError("overlap_retry_ms requires overlap_policy queue_after_previous");
15949
+ }
15950
+ if (options.maxFires != null) {
15951
+ throw new TypeError("max_fires is only supported for recurring schedules");
15952
+ }
15953
+ if (options.endAtMs != null) {
15954
+ throw new TypeError("end_at_ms is only supported for recurring schedules");
15955
+ }
14755
15956
  }
14756
- async countByState(type, state, options = {}) {
14757
- const stats = await this.stats(type, { ...options, state });
14758
- if (!("count" in stats)) throw new TypeError("FLOW.STATS response missing count");
14759
- return integer(stats.count);
15957
+ }
15958
+ function validateScheduleID(id) {
15959
+ requiredText3(id, "id");
15960
+ }
15961
+ function validateScheduleFire(id, options) {
15962
+ validateScheduleID(id);
15963
+ nonNegativeInteger2(options.fireAtMs, "fire_at_ms");
15964
+ nonNegativeInteger2(options.nowMs, "now_ms");
15965
+ }
15966
+ function validateScheduleStatus(id, nowMs2) {
15967
+ validateScheduleID(id);
15968
+ nonNegativeInteger2(nowMs2, "now_ms");
15969
+ }
15970
+ function validateScheduleFireDue(options) {
15971
+ if (!mapping(options)) throw new TypeError("schedule fire-due options must be a mapping");
15972
+ nonNegativeInteger2(options.nowMs, "now_ms");
15973
+ optionalText3(options.worker, "worker");
15974
+ positiveInteger2(options.leaseMs, "lease_ms");
15975
+ nonNegativeInteger2(options.blockMs, "block_ms");
15976
+ positiveInteger2(options.limit, "limit");
15977
+ const leaseMs = options.leaseMs ?? 3e4;
15978
+ if (options.nowMs != null && options.nowMs > MAX_EXACT_INTEGER - leaseMs) {
15979
+ throw new TypeError("now_ms plus lease_ms exceeds the exact integer range");
14760
15980
  }
14761
- async attributes(type, options = {}) {
14762
- const args = ["FLOW.ATTRIBUTES", type];
14763
- appendAttributeQueryOptions(args, options);
14764
- return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTES");
15981
+ }
15982
+ function validateScheduleList(options) {
15983
+ if (!mapping(options)) throw new TypeError("schedule list options must be a mapping");
15984
+ if (options.kind != null && !SCHEDULE_KINDS.has(options.kind)) {
15985
+ throw new TypeError("kind must be one_shot, delay, interval, or cron");
14765
15986
  }
14766
- async attributeValues(type, attribute, options = {}) {
14767
- const args = ["FLOW.ATTRIBUTE_VALUES", type, attribute];
14768
- appendAttributeQueryOptions(args, options);
14769
- return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTE_VALUES");
15987
+ if (options.state != null && !SCHEDULE_STATES.has(options.state)) {
15988
+ throw new TypeError("state is invalid");
15989
+ }
15990
+ optionalText3(options.timezone, "timezone");
15991
+ optionalText3(options.targetType, "target_type");
15992
+ nonNegativeInteger2(options.fromMs, "from_ms");
15993
+ nonNegativeInteger2(options.toMs, "to_ms");
15994
+ if (options.fromMs != null && options.toMs != null && options.fromMs > options.toMs) {
15995
+ throw new TypeError("from_ms must not exceed to_ms");
15996
+ }
15997
+ positiveInteger2(options.count, "count");
15998
+ optionalBoolean(options.rev, "rev");
15999
+ }
16000
+ function effectiveKind(options) {
16001
+ if (options.kind != null) {
16002
+ if (!SCHEDULE_KINDS.has(options.kind)) {
16003
+ throw new TypeError("kind must be one_shot, delay, interval, or cron");
16004
+ }
16005
+ return options.kind;
16006
+ }
16007
+ if (options.cron != null) return "cron";
16008
+ if (options.everyMs != null) return "interval";
16009
+ if (options.delayMs != null) return "delay";
16010
+ return "one_shot";
16011
+ }
16012
+ function validateTiming(kind, options) {
16013
+ if (options.atMs != null && options.startAtMs != null) {
16014
+ throw new TypeError("cannot set both at_ms and start_at_ms");
16015
+ }
16016
+ if (options.delayMs != null && kind !== "delay") {
16017
+ throw new TypeError("delay_ms is only supported for delay schedules");
16018
+ }
16019
+ if (options.everyMs != null && kind !== "interval") {
16020
+ throw new TypeError("every_ms is only supported for interval schedules");
16021
+ }
16022
+ if (options.cron != null && kind !== "cron") {
16023
+ throw new TypeError("cron is only supported for cron schedules");
16024
+ }
16025
+ if (kind === "delay" && (options.atMs != null || options.startAtMs != null)) {
16026
+ throw new TypeError("at_ms and start_at_ms are not supported for delay schedules");
16027
+ }
16028
+ }
16029
+ function validateTarget(value, recurring) {
16030
+ if (!mapping(value)) throw new TypeError("target must be a mapping with a non-empty type");
16031
+ const target = value;
16032
+ for (const key of Object.keys(target)) {
16033
+ if (!TARGET_FIELDS.has(key)) throw new TypeError(`target contains unknown field ${key}`);
14770
16034
  }
16035
+ const type = own(target, "type");
16036
+ requiredText3(type, "target type");
16037
+ if (type === INTERNAL_SCHEDULE_TYPE) {
16038
+ throw new TypeError("target type is reserved for internal use");
16039
+ }
16040
+ for (const key of TARGET_TEXT_FIELDS) optionalText3(own(target, key), `target ${key}`);
16041
+ const id = own(target, "id");
16042
+ const idPrefix = own(target, "id_prefix");
16043
+ if (id != null && idPrefix != null) {
16044
+ throw new TypeError("target cannot set both id and id_prefix");
16045
+ }
16046
+ if (recurring && id != null) {
16047
+ throw new TypeError("target id is not supported for recurring schedules; use id_prefix");
16048
+ }
16049
+ if (typeof id === "string" && id.startsWith(INTERNAL_SCHEDULE_ID_PREFIX)) {
16050
+ throw new TypeError("target id is reserved for internal use");
16051
+ }
16052
+ if (typeof idPrefix === "string" && idPrefix.startsWith(INTERNAL_SCHEDULE_ID_PREFIX)) {
16053
+ throw new TypeError("target id_prefix is reserved for internal use");
16054
+ }
16055
+ nonNegativeInteger2(own(target, "run_at_ms"), "target run_at_ms");
16056
+ const priority = own(target, "priority");
16057
+ if (priority != null && (typeof priority !== "number" || !Number.isSafeInteger(priority) || priority < 0 || priority > 2)) {
16058
+ throw new TypeError("target priority must be an integer between 0 and 2");
16059
+ }
16060
+ }
16061
+ function knownFirstRun(kind, options) {
16062
+ if (kind === "delay") {
16063
+ if (options.nowMs == null || options.delayMs == null) return void 0;
16064
+ return options.nowMs + options.delayMs;
16065
+ }
16066
+ if (kind === "cron") return options.startAtMs ?? options.atMs ?? options.nowMs;
16067
+ return options.atMs ?? options.startAtMs ?? options.nowMs;
16068
+ }
16069
+ function requiredText3(value, name) {
16070
+ if (typeof value !== "string" || value.length === 0) {
16071
+ throw new TypeError(`${name} must be a non-empty string`);
16072
+ }
16073
+ }
16074
+ function optionalText3(value, name) {
16075
+ if (value != null) requiredText3(value, name);
16076
+ }
16077
+ function nonNegativeInteger2(value, name) {
16078
+ if (value != null && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)) {
16079
+ throw new TypeError(`${name} must be a non-negative exact integer`);
16080
+ }
16081
+ }
16082
+ function positiveInteger2(value, name) {
16083
+ if (value != null && (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)) {
16084
+ throw new TypeError(`${name} must be a positive exact integer`);
16085
+ }
16086
+ }
16087
+ function optionalBoolean(value, name) {
16088
+ if (value != null && typeof value !== "boolean") {
16089
+ throw new TypeError(`${name} must be a boolean`);
16090
+ }
16091
+ }
16092
+ function mapping(value) {
16093
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16094
+ }
16095
+ function own(mapping2, key) {
16096
+ return Object.prototype.hasOwnProperty.call(mapping2, key) ? mapping2[key] : void 0;
16097
+ }
16098
+
16099
+ // src/schedule-response.ts
16100
+ var SCHEDULE_INTEGER_FIELDS = [
16101
+ "end_at_ms",
16102
+ "last_catchup_at_ms",
16103
+ "last_fire_at_ms",
16104
+ "last_overlap_at_ms",
16105
+ "last_skipped_at_ms",
16106
+ "max_fires",
16107
+ "next_run_at_ms",
16108
+ "overlap_queued_due_at_ms"
16109
+ ];
16110
+ var REQUIRED_SCHEDULE_INTEGER_FIELDS = [
16111
+ "attempts",
16112
+ "coalesced_count",
16113
+ "fire_count",
16114
+ "last_coalesced_count",
16115
+ "skipped_count"
16116
+ ];
16117
+ var OPTIONAL_SCHEDULE_TEXT_FIELDS = [
16118
+ "cron",
16119
+ "end_reason",
16120
+ "flow_id",
16121
+ "last_overlap_reason",
16122
+ "last_overlap_target_id",
16123
+ "last_planning_error",
16124
+ "last_target_id",
16125
+ "timezone"
16126
+ ];
16127
+ var SCHEDULE_KINDS2 = /* @__PURE__ */ new Set(["one_shot", "delay", "interval", "cron"]);
16128
+ var SCHEDULE_STATES2 = /* @__PURE__ */ new Set([
16129
+ "active",
16130
+ "paused",
16131
+ "running",
16132
+ "completed",
16133
+ "failed",
16134
+ "cancelled"
16135
+ ]);
16136
+ var SCHEDULE_OVERLAP_POLICIES = /* @__PURE__ */ new Set([
16137
+ "allow",
16138
+ "skip",
16139
+ "queue_after_previous",
16140
+ "fail_schedule"
16141
+ ]);
16142
+ function scheduleRecordResponse(value, context) {
16143
+ const record = adminRecordResponse(value, context);
16144
+ if (typeof record.id !== "string" || record.id.length === 0) {
16145
+ throw new TypeError(`${context} response missing id`);
16146
+ }
16147
+ if (typeof record.kind !== "string" || !SCHEDULE_KINDS2.has(record.kind)) {
16148
+ throw new TypeError(`${context} response missing or invalid kind`);
16149
+ }
16150
+ if (typeof record.state !== "string" || !SCHEDULE_STATES2.has(record.state)) {
16151
+ throw new TypeError(`${context} response missing or invalid state`);
16152
+ }
16153
+ if (typeof record.target !== "object" || record.target == null || Array.isArray(record.target)) {
16154
+ throw new TypeError(`${context} response missing or invalid target`);
16155
+ }
16156
+ const target = record.target;
16157
+ if (typeof target.type !== "string" || target.type.length === 0) {
16158
+ throw new TypeError(`${context} response missing or invalid target type`);
16159
+ }
16160
+ if (!("catchup_policy" in record) || record.kind === "interval" && record.catchup_policy !== "fire_once" || record.kind !== "interval" && record.catchup_policy != null) {
16161
+ throw new TypeError(`${context} returned an invalid catchup_policy`);
16162
+ }
16163
+ if (typeof record.overlap_policy !== "string" || !SCHEDULE_OVERLAP_POLICIES.has(record.overlap_policy)) {
16164
+ throw new TypeError(`${context} response missing or invalid overlap_policy`);
16165
+ }
16166
+ safeNonNegativeInteger(record, "created_at_ms", context, true);
16167
+ const everyMs = requiredNullableNonNegativeInteger(record, "every_ms", context);
16168
+ const cron = requiredNullableResponseText(record, "cron", context);
16169
+ const timezone = requiredNullableResponseText(record, "timezone", context);
16170
+ const overlapRetryMs = requiredNullableNonNegativeInteger(
16171
+ record,
16172
+ "overlap_retry_ms",
16173
+ context
16174
+ );
16175
+ validateRecurrence(record, context, everyMs, cron, timezone, overlapRetryMs);
16176
+ let coalescedCount = 0;
16177
+ let lastCoalescedCount = 0;
16178
+ for (const field3 of REQUIRED_SCHEDULE_INTEGER_FIELDS) {
16179
+ const value2 = safeNonNegativeInteger(record, field3, context, true);
16180
+ if (field3 === "coalesced_count") coalescedCount = value2;
16181
+ if (field3 === "last_coalesced_count") lastCoalescedCount = value2;
16182
+ }
16183
+ for (const field3 of SCHEDULE_INTEGER_FIELDS) {
16184
+ if (record[field3] != null) safeNonNegativeInteger(record, field3, context);
16185
+ }
16186
+ for (const field3 of OPTIONAL_SCHEDULE_TEXT_FIELDS) {
16187
+ optionalResponseText(record, field3, context);
16188
+ }
16189
+ validateCatchupState(record, context, coalescedCount, lastCoalescedCount);
16190
+ return record;
16191
+ }
16192
+ function validateRecurrence(record, context, everyMs, cron, timezone, overlapRetryMs) {
16193
+ if (record.kind === "interval") {
16194
+ if (everyMs == null || everyMs <= 0) {
16195
+ throw new TypeError(`${context} interval every_ms must be positive`);
16196
+ }
16197
+ } else if (everyMs != null) {
16198
+ throw new TypeError(`${context} every_ms is only valid for interval schedules`);
16199
+ }
16200
+ if (record.kind === "cron") {
16201
+ if (cron == null) throw new TypeError(`${context} cron schedule is missing cron`);
16202
+ if (timezone == null) throw new TypeError(`${context} cron schedule is missing timezone`);
16203
+ } else if (cron != null) {
16204
+ throw new TypeError(`${context} cron is only valid for cron schedules`);
16205
+ } else if (timezone != null) {
16206
+ throw new TypeError(`${context} timezone is only valid for cron schedules`);
16207
+ }
16208
+ if (record.kind !== "interval" && record.kind !== "cron" && record.overlap_policy !== "allow") {
16209
+ throw new TypeError(`${context} overlap_policy is only valid for recurring schedules`);
16210
+ }
16211
+ if (overlapRetryMs != null) {
16212
+ if (overlapRetryMs <= 0) {
16213
+ throw new TypeError(`${context} overlap_retry_ms must be positive`);
16214
+ }
16215
+ if (record.overlap_policy !== "queue_after_previous") {
16216
+ throw new TypeError(`${context} overlap_retry_ms requires queue_after_previous`);
16217
+ }
16218
+ }
16219
+ }
16220
+ function validateCatchupState(record, context, coalescedCount, lastCoalescedCount) {
16221
+ const lastCatchupAt = record.last_catchup_at_ms;
16222
+ if (record.kind !== "interval") {
16223
+ if (coalescedCount !== 0) {
16224
+ throw new TypeError(`${context} non-interval coalesced_count must be zero`);
16225
+ }
16226
+ if (lastCoalescedCount !== 0) {
16227
+ throw new TypeError(`${context} non-interval last_coalesced_count must be zero`);
16228
+ }
16229
+ if (lastCatchupAt != null) {
16230
+ throw new TypeError(`${context} non-interval last_catchup_at_ms must be null`);
16231
+ }
16232
+ return;
16233
+ }
16234
+ if (lastCoalescedCount > coalescedCount) {
16235
+ throw new TypeError(`${context} last_coalesced_count exceeds coalesced_count`);
16236
+ }
16237
+ if (lastCoalescedCount > 0 && lastCatchupAt == null) {
16238
+ throw new TypeError(`${context} response missing last_catchup_at_ms after catch-up`);
16239
+ }
16240
+ if (lastCoalescedCount === 0 && lastCatchupAt != null) {
16241
+ throw new TypeError(`${context} last_catchup_at_ms requires a catch-up`);
16242
+ }
16243
+ }
16244
+ function optionalScheduleRecord(value, context) {
16245
+ return value == null ? null : scheduleRecordResponse(value, context);
16246
+ }
16247
+ function scheduleRecordList(value, context) {
16248
+ return arrayResponse(value).map((item) => scheduleRecordResponse(item, context));
16249
+ }
16250
+ function scheduleFireDueResponse(value) {
16251
+ const context = "FLOW.SCHEDULE.FIRE_DUE";
16252
+ const record = adminRecordResponse(value, context);
16253
+ const claimed = safeNonNegativeInteger(record, "claimed", context, true);
16254
+ const fired = safeNonNegativeInteger(record, "fired", context, true);
16255
+ const skipped = safeNonNegativeInteger(record, "skipped", context, true);
16256
+ const coalesced = safeNonNegativeInteger(record, "coalesced", context, true);
16257
+ if (!Array.isArray(record.errors)) {
16258
+ throw new TypeError(`${context} response missing errors`);
16259
+ }
16260
+ for (const error of record.errors) {
16261
+ if (!Array.isArray(error) || error.length !== 2 || typeof error[0] !== "string" || error[0].length === 0 || typeof error[1] !== "string" || error[1].length === 0) {
16262
+ throw new TypeError(`${context} returned an invalid error entry`);
16263
+ }
16264
+ }
16265
+ if (fired + skipped + record.errors.length !== claimed) {
16266
+ throw new TypeError(`${context} outcomes do not equal claimed`);
16267
+ }
16268
+ optionalResponseText(record, "claim_error", context);
16269
+ const lastTargetId = optionalResponseText(record, "last_target_id", context);
16270
+ const lastSkipReason = optionalResponseText(record, "last_skip_reason", context);
16271
+ if (fired > 0 && lastTargetId == null) {
16272
+ throw new TypeError(`${context} response missing last_target_id`);
16273
+ }
16274
+ if (fired === 0 && lastTargetId != null) {
16275
+ throw new TypeError(`${context} last_target_id requires a fired outcome`);
16276
+ }
16277
+ if (skipped > 0 && lastSkipReason == null) {
16278
+ throw new TypeError(`${context} response missing last_skip_reason`);
16279
+ }
16280
+ if (skipped === 0 && lastSkipReason != null) {
16281
+ throw new TypeError(`${context} last_skip_reason requires a skipped outcome`);
16282
+ }
16283
+ if (coalesced > 0 && fired + skipped === 0) {
16284
+ throw new TypeError(`${context} coalesced count requires a fired or skipped outcome`);
16285
+ }
16286
+ return record;
16287
+ }
16288
+ function scheduleFireResponse(value) {
16289
+ const context = "FLOW.SCHEDULE.FIRE";
16290
+ const record = adminRecordResponse(value, context);
16291
+ const schedule = scheduleRecordResponse(record.schedule, `${context} schedule`);
16292
+ const fired = safeNonNegativeInteger(record, "fired", context, true);
16293
+ const skipped = record.skipped == null ? 0 : safeNonNegativeInteger(record, "skipped", context);
16294
+ if (fired > 1 || skipped > 1 || fired + skipped !== 1) {
16295
+ throw new TypeError(`${context} outcomes must equal one`);
16296
+ }
16297
+ if (fired === 1 && (typeof record.target_id !== "string" || record.target_id.length === 0)) {
16298
+ throw new TypeError(`${context} response missing target_id`);
16299
+ }
16300
+ if (skipped === 1 && (typeof record.reason !== "string" || record.reason.length === 0)) {
16301
+ throw new TypeError(`${context} response missing reason`);
16302
+ }
16303
+ if (fired === 0 && record.target_id != null) {
16304
+ throw new TypeError(`${context} target_id requires a fired outcome`);
16305
+ }
16306
+ if (skipped === 0 && record.reason != null) {
16307
+ throw new TypeError(`${context} reason requires a skipped outcome`);
16308
+ }
16309
+ return { ...record, fired, skipped, schedule };
16310
+ }
16311
+ function optionalResponseText(record, field3, context) {
16312
+ const value = record[field3];
16313
+ if (value == null) return void 0;
16314
+ if (typeof value !== "string" || value.length === 0) {
16315
+ throw new TypeError(`${context} ${field3} must be non-empty text`);
16316
+ }
16317
+ return value;
16318
+ }
16319
+ function requiredNullableResponseText(record, field3, context) {
16320
+ if (!(field3 in record)) throw new TypeError(`${context} response missing ${field3}`);
16321
+ if (record[field3] === void 0) {
16322
+ throw new TypeError(`${context} response missing ${field3}`);
16323
+ }
16324
+ return optionalResponseText(record, field3, context) ?? null;
16325
+ }
16326
+ function requiredNullableNonNegativeInteger(record, field3, context) {
16327
+ if (!(field3 in record)) throw new TypeError(`${context} response missing ${field3}`);
16328
+ if (record[field3] === void 0) {
16329
+ throw new TypeError(`${context} response missing ${field3}`);
16330
+ }
16331
+ if (record[field3] === null) return null;
16332
+ return safeNonNegativeInteger(record, field3, context);
16333
+ }
16334
+ function safeNonNegativeInteger(record, field3, context, required = false) {
16335
+ if (!(field3 in record) || record[field3] == null) {
16336
+ if (required) throw new TypeError(`${context} response missing ${field3}`);
16337
+ return 0;
16338
+ }
16339
+ const value = record[field3];
16340
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
16341
+ throw new TypeError(`${context} ${field3} must be a safe non-negative integer`);
16342
+ }
16343
+ return value;
16344
+ }
16345
+
16346
+ // src/client-schedules.ts
16347
+ var FerricStoreScheduleClient = class extends FerricStoreClientCore {
14771
16348
  async scheduleCreate(id, options) {
16349
+ validateScheduleCreate(id, options);
14772
16350
  const args = ["FLOW.SCHEDULE.CREATE", id];
14773
16351
  append(args, "KIND", options.kind);
14774
16352
  append(args, "AT_MS", options.atMs);
@@ -14778,6 +16356,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
14778
16356
  append(args, "CRON", options.cron);
14779
16357
  append(args, "TIMEZONE", options.timezone);
14780
16358
  append(args, "TARGET", options.target);
16359
+ append(args, "CATCHUP_POLICY", options.catchupPolicy);
14781
16360
  append(args, "OVERLAP_POLICY", options.overlapPolicy);
14782
16361
  append(args, "OVERLAP_RETRY_MS", options.overlapRetryMs);
14783
16362
  append(args, "MAX_FIRES", options.maxFires);
@@ -14787,34 +16366,55 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
14787
16366
  for (const [name, value] of Object.entries(options.extraOptions ?? {})) {
14788
16367
  args.push(name.toUpperCase(), value);
14789
16368
  }
14790
- return adminRecordResponse(await this.commandArgs(args), "FLOW.SCHEDULE.CREATE");
16369
+ return scheduleRecordResponse(await this.commandArgs(args), "FLOW.SCHEDULE.CREATE");
14791
16370
  }
14792
- async scheduleGet(id, options = {}) {
14793
- const args = ["FLOW.SCHEDULE.GET", id];
14794
- append(args, "NOW", options.nowMs);
14795
- return optionalAdminRecord(await this.commandArgs(args), "FLOW.SCHEDULE.GET");
16371
+ async scheduleGet(id) {
16372
+ validateScheduleID(id);
16373
+ return optionalScheduleRecord(
16374
+ await this.commandArgs(["FLOW.SCHEDULE.GET", id]),
16375
+ "FLOW.SCHEDULE.GET"
16376
+ );
14796
16377
  }
14797
16378
  async scheduleFire(id, options = {}) {
14798
- return await this.scheduleStatus("FLOW.SCHEDULE.FIRE", id, options.nowMs);
16379
+ validateScheduleFire(id, options);
16380
+ const args = ["FLOW.SCHEDULE.FIRE", id];
16381
+ append(args, "FIRE_AT_MS", options.fireAtMs);
16382
+ append(args, "NOW", options.nowMs);
16383
+ return scheduleFireResponse(await this.commandArgs(args));
14799
16384
  }
14800
16385
  async schedulePause(id, options = {}) {
14801
- return await this.scheduleStatus("FLOW.SCHEDULE.PAUSE", id, options.nowMs);
16386
+ validateScheduleStatus(id, options.nowMs);
16387
+ return scheduleRecordResponse(
16388
+ await this.scheduleStatusResponse("FLOW.SCHEDULE.PAUSE", id, options.nowMs),
16389
+ "FLOW.SCHEDULE.PAUSE"
16390
+ );
14802
16391
  }
14803
16392
  async scheduleResume(id, options = {}) {
14804
- return await this.scheduleStatus("FLOW.SCHEDULE.RESUME", id, options.nowMs);
16393
+ validateScheduleStatus(id, options.nowMs);
16394
+ return scheduleRecordResponse(
16395
+ await this.scheduleStatusResponse("FLOW.SCHEDULE.RESUME", id, options.nowMs),
16396
+ "FLOW.SCHEDULE.RESUME"
16397
+ );
14805
16398
  }
14806
16399
  async scheduleDelete(id, options = {}) {
14807
- return await this.scheduleStatus("FLOW.SCHEDULE.DELETE", id, options.nowMs);
16400
+ validateScheduleStatus(id, options.nowMs);
16401
+ const response = await this.scheduleStatusResponse("FLOW.SCHEDULE.DELETE", id, options.nowMs);
16402
+ if (!okLike(response)) {
16403
+ throw new TypeError("FLOW.SCHEDULE.DELETE response must be OK");
16404
+ }
14808
16405
  }
14809
16406
  async scheduleFireDue(options = {}) {
16407
+ validateScheduleFireDue(options);
14810
16408
  const args = ["FLOW.SCHEDULE.FIRE_DUE"];
14811
16409
  append(args, "NOW", options.nowMs);
14812
16410
  append(args, "WORKER", options.worker);
16411
+ append(args, "LEASE_MS", options.leaseMs);
14813
16412
  append(args, "BLOCK", options.blockMs);
14814
16413
  append(args, "LIMIT", options.limit);
14815
- return adminRecordResponse(await this.commandArgs(args), "FLOW.SCHEDULE.FIRE_DUE");
16414
+ return scheduleFireDueResponse(await this.commandArgs(args));
14816
16415
  }
14817
16416
  async scheduleList(options = {}) {
16417
+ validateScheduleList(options);
14818
16418
  const args = ["FLOW.SCHEDULE.LIST"];
14819
16419
  append(args, "KIND", options.kind);
14820
16420
  append(args, "STATE", options.state);
@@ -14824,14 +16424,40 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
14824
16424
  append(args, "TO_MS", options.toMs);
14825
16425
  append(args, "COUNT", options.count);
14826
16426
  appendBool(args, "REV", options.rev);
14827
- return adminRecordList(await this.commandArgs(args), "FLOW.SCHEDULE.LIST");
16427
+ return scheduleRecordList(await this.commandArgs(args), "FLOW.SCHEDULE.LIST");
14828
16428
  }
14829
- async scheduleStatus(command, id, now) {
16429
+ async scheduleStatusResponse(command, id, now) {
14830
16430
  const args = [command, id];
14831
16431
  append(args, "NOW", now);
14832
- const response = await this.commandArgs(args);
14833
- if (okLike(response)) return { id, status: "deleted" };
14834
- return adminRecordResponse(response, command);
16432
+ return await this.commandArgs(args);
16433
+ }
16434
+ };
16435
+
16436
+ // src/client-management.ts
16437
+ var FerricStoreManagementClient = class extends FerricStoreScheduleClient {
16438
+ async stats(type, options = {}) {
16439
+ const args = ["FLOW.STATS", type];
16440
+ append(args, "STATE", options.state);
16441
+ append(args, "COUNT", options.count);
16442
+ append(args, "PARTITION", options.partitionKey);
16443
+ appendAttributes(args, options.attributes);
16444
+ appendBool(args, "CONSISTENT_PROJECTION", options.consistentProjection);
16445
+ return adminRecordResponse(await this.commandArgs(args), "FLOW.STATS");
16446
+ }
16447
+ async countByState(type, state, options = {}) {
16448
+ const stats = await this.stats(type, { ...options, state });
16449
+ if (!("count" in stats)) throw new TypeError("FLOW.STATS response missing count");
16450
+ return integer(stats.count);
16451
+ }
16452
+ async attributes(type, options = {}) {
16453
+ const args = ["FLOW.ATTRIBUTES", type];
16454
+ appendAttributeQueryOptions(args, options);
16455
+ return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTES");
16456
+ }
16457
+ async attributeValues(type, attribute, options = {}) {
16458
+ const args = ["FLOW.ATTRIBUTE_VALUES", type, attribute];
16459
+ appendAttributeQueryOptions(args, options);
16460
+ return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTE_VALUES");
14835
16461
  }
14836
16462
  async effectReserve(id, effectKey, effectType, options) {
14837
16463
  const args = [
@@ -15064,7 +16690,7 @@ var FerricStoreFlowQueryClient = class extends FerricStoreManagementClient {
15064
16690
  args.push(indexId);
15065
16691
  }
15066
16692
  try {
15067
- return decodeFlowQueryIndexStatus(await this.commandArgs(args));
16693
+ return decodeFlowQueryIndexStatus(await this.commandArgs(args), indexId);
15068
16694
  } catch (error) {
15069
16695
  this.throwFlowQueryError(error);
15070
16696
  }
@@ -15126,7 +16752,7 @@ var FerricStoreFlowQueryClient = class extends FerricStoreManagementClient {
15126
16752
  if (hasFlowExplainPrefix(query)) {
15127
16753
  throw new TypeError("query already contains an EXPLAIN prefix");
15128
16754
  }
15129
- return decodeFlowExplainResult(await this.executeFlowQuery(prefix + query.trim(), params));
16755
+ return decodeFlowExplainResult(await this.executeFlowQuery(prefix + query, params));
15130
16756
  }
15131
16757
  throwFlowQueryError(error) {
15132
16758
  if (error instanceof FerricStoreError) {
@@ -15159,7 +16785,7 @@ function throwIfClosed(signal) {
15159
16785
  }
15160
16786
 
15161
16787
  // src/flow-policy.ts
15162
- var import_node_buffer51 = require("buffer");
16788
+ var import_node_buffer54 = require("buffer");
15163
16789
  var MAX_FLOW_POLICY_GENERATION = Number.MAX_SAFE_INTEGER;
15164
16790
  function assertFlowPolicyGeneration(value) {
15165
16791
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
@@ -15169,15 +16795,15 @@ function assertFlowPolicyGeneration(value) {
15169
16795
  function flowPolicySnapshotFromResp(value) {
15170
16796
  const policy = requiredRecord(value, "FLOW policy snapshot");
15171
16797
  const maxActiveMs = optionalPositiveInteger(field(policy, "max_active_ms"), "max_active_ms");
15172
- const state = optionalText3(field(policy, "state"), "state");
16798
+ const state = optionalText4(field(policy, "state"), "state");
15173
16799
  const mode = optionalMode(field(policy, "mode"), "mode");
15174
16800
  const version = optionalVersion(field(policy, "version"));
15175
- const indexedStateMeta = optionalText3(field(policy, "indexed_state_meta"), "indexed_state_meta");
16801
+ const indexedStateMeta = optionalText4(field(policy, "indexed_state_meta"), "indexed_state_meta");
15176
16802
  const governance = optionalRecord(field(policy, "governance"), "governance");
15177
16803
  const states = optionalStates(field(policy, "states"));
15178
16804
  return {
15179
- type: requiredText3(field(policy, "type"), "type"),
15180
- generation: nonNegativeInteger2(field(policy, "generation"), "generation"),
16805
+ type: requiredText4(field(policy, "type"), "type"),
16806
+ generation: nonNegativeInteger3(field(policy, "generation"), "generation"),
15181
16807
  ...state == null ? {} : { state },
15182
16808
  ...version == null ? {} : { version },
15183
16809
  ...mode == null ? {} : { mode },
@@ -15195,21 +16821,21 @@ function retrySnapshot(value, context) {
15195
16821
  const retry2 = requiredRecord(value, `FLOW policy ${context}`);
15196
16822
  const backoff = requiredRecord(field(retry2, "backoff"), `FLOW policy ${context}.backoff`);
15197
16823
  return {
15198
- maxRetries: nonNegativeInteger2(field(retry2, "max_retries"), `${context}.max_retries`),
16824
+ maxRetries: nonNegativeInteger3(field(retry2, "max_retries"), `${context}.max_retries`),
15199
16825
  backoff: {
15200
16826
  kind: backoffKind(field(backoff, "kind"), `${context}.backoff.kind`),
15201
- baseMs: nonNegativeInteger2(field(backoff, "base_ms"), `${context}.backoff.base_ms`),
15202
- maxMs: nonNegativeInteger2(field(backoff, "max_ms"), `${context}.backoff.max_ms`),
15203
- jitterPct: nonNegativeInteger2(field(backoff, "jitter_pct"), `${context}.backoff.jitter_pct`)
16827
+ baseMs: nonNegativeInteger3(field(backoff, "base_ms"), `${context}.backoff.base_ms`),
16828
+ maxMs: nonNegativeInteger3(field(backoff, "max_ms"), `${context}.backoff.max_ms`),
16829
+ jitterPct: nonNegativeInteger3(field(backoff, "jitter_pct"), `${context}.backoff.jitter_pct`)
15204
16830
  },
15205
- exhaustedTo: requiredText3(field(retry2, "exhausted_to"), `${context}.exhausted_to`)
16831
+ exhaustedTo: requiredText4(field(retry2, "exhausted_to"), `${context}.exhausted_to`)
15206
16832
  };
15207
16833
  }
15208
16834
  function retentionSnapshot(value, context) {
15209
16835
  const retention = requiredRecord(value, `FLOW policy ${context}`);
15210
16836
  return {
15211
- ttlMs: nonNegativeInteger2(field(retention, "ttl_ms"), `${context}.ttl_ms`),
15212
- historyMaxEvents: nonNegativeInteger2(
16837
+ ttlMs: nonNegativeInteger3(field(retention, "ttl_ms"), `${context}.ttl_ms`),
16838
+ historyMaxEvents: nonNegativeInteger3(
15213
16839
  field(retention, "history_max_events"),
15214
16840
  `${context}.history_max_events`
15215
16841
  )
@@ -15245,11 +16871,11 @@ function requiredRecord(value, context) {
15245
16871
  if (value instanceof Map) {
15246
16872
  const result = {};
15247
16873
  for (const [key, item] of value.entries()) {
15248
- setOwnValue(result, requiredText3(key, `${context} key`), item);
16874
+ setOwnValue(result, requiredText4(key, `${context} key`), item);
15249
16875
  }
15250
16876
  return result;
15251
16877
  }
15252
- if (typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer51.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return value;
16878
+ if (typeof value === "object" && value != null && !Array.isArray(value) && !import_node_buffer54.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return value;
15253
16879
  throw new TypeError(`${context} returned an unexpected response`);
15254
16880
  }
15255
16881
  function optionalRecord(value, context) {
@@ -15264,21 +16890,21 @@ function stringArray(value, context) {
15264
16890
  if (!Object.hasOwn(value, index)) {
15265
16891
  throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
15266
16892
  }
15267
- result[index] = requiredText3(value[index], `${context}[${index}]`);
16893
+ result[index] = requiredText4(value[index], `${context}[${index}]`);
15268
16894
  }
15269
16895
  return result;
15270
16896
  }
15271
- function requiredText3(value, context) {
16897
+ function requiredText4(value, context) {
15272
16898
  if (typeof value === "string") return value;
15273
- if (import_node_buffer51.Buffer.isBuffer(value) || value instanceof Uint8Array) {
15274
- return import_node_buffer51.Buffer.from(value).toString("utf8");
16899
+ if (import_node_buffer54.Buffer.isBuffer(value) || value instanceof Uint8Array) {
16900
+ return import_node_buffer54.Buffer.from(value).toString("utf8");
15275
16901
  }
15276
16902
  throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
15277
16903
  }
15278
- function optionalText3(value, context) {
15279
- return value == null ? void 0 : requiredText3(value, context);
16904
+ function optionalText4(value, context) {
16905
+ return value == null ? void 0 : requiredText4(value, context);
15280
16906
  }
15281
- function nonNegativeInteger2(value, context) {
16907
+ function nonNegativeInteger3(value, context) {
15282
16908
  let parsed;
15283
16909
  try {
15284
16910
  parsed = integer(value);
@@ -15290,26 +16916,26 @@ function nonNegativeInteger2(value, context) {
15290
16916
  }
15291
16917
  function optionalPositiveInteger(value, context) {
15292
16918
  if (value == null) return void 0;
15293
- const parsed = nonNegativeInteger2(value, context);
16919
+ const parsed = nonNegativeInteger3(value, context);
15294
16920
  if (parsed === 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
15295
16921
  return parsed;
15296
16922
  }
15297
16923
  function optionalVersion(value) {
15298
16924
  if (value == null) return void 0;
15299
16925
  if (typeof value === "number" || typeof value === "bigint") {
15300
- return nonNegativeInteger2(value, "version");
16926
+ return nonNegativeInteger3(value, "version");
15301
16927
  }
15302
- return requiredText3(value, "version");
16928
+ return requiredText4(value, "version");
15303
16929
  }
15304
16930
  function backoffKind(value, context) {
15305
- const kind = requiredText3(value, context).toLowerCase();
16931
+ const kind = requiredText4(value, context).toLowerCase();
15306
16932
  if (kind === "none" || kind === "fixed" || kind === "linear" || kind === "exponential") {
15307
16933
  return kind;
15308
16934
  }
15309
16935
  throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
15310
16936
  }
15311
16937
  function requiredMode(value, context) {
15312
- const mode = requiredText3(value, context).toLowerCase();
16938
+ const mode = requiredText4(value, context).toLowerCase();
15313
16939
  if (mode === "fifo" || mode === "parallel") return mode;
15314
16940
  throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
15315
16941
  }
@@ -15527,7 +17153,7 @@ var FerricStoreFlowSupportClient = class extends FerricStoreFlowQueryClient {
15527
17153
  };
15528
17154
 
15529
17155
  // src/flow-many-snapshot.ts
15530
- var import_node_buffer53 = require("buffer");
17156
+ var import_node_buffer56 = require("buffer");
15531
17157
  var encodedOptionFields = ["error", "payload", "reason", "result"];
15532
17158
  function snapshotCreateItem(item, codec) {
15533
17159
  const snapshot = {
@@ -15567,7 +17193,7 @@ function snapshotFlowManyOptions(options, codec) {
15567
17193
  }
15568
17194
  function snapshotClaimedItem(item) {
15569
17195
  const wire = item[CLAIMED_ITEM_WIRE];
15570
- const leaseToken = import_node_buffer53.Buffer.from(wire?.leaseToken ?? item.leaseToken);
17196
+ const leaseToken = import_node_buffer56.Buffer.from(wire?.leaseToken ?? item.leaseToken);
15571
17197
  const snapshot = {
15572
17198
  ...item,
15573
17199
  fencingToken: wire?.fencingToken ?? item.fencingToken,
@@ -15584,15 +17210,15 @@ function snapshotClaimedItem(item) {
15584
17210
  function snapshotFencedItem(item) {
15585
17211
  return Object.freeze({
15586
17212
  ...item,
15587
- ...item.leaseToken == null ? {} : { leaseToken: import_node_buffer53.Buffer.from(item.leaseToken) }
17213
+ ...item.leaseToken == null ? {} : { leaseToken: import_node_buffer56.Buffer.from(item.leaseToken) }
15588
17214
  });
15589
17215
  }
15590
17216
  function snapshotClaimedItemWire(wire, leaseToken) {
15591
17217
  return Object.freeze({
15592
17218
  fencingToken: wire.fencingToken,
15593
- id: import_node_buffer53.Buffer.from(wire.id),
17219
+ id: import_node_buffer56.Buffer.from(wire.id),
15594
17220
  leaseToken,
15595
- partitionKey: wire.partitionKey == null ? wire.partitionKey : import_node_buffer53.Buffer.from(wire.partitionKey)
17221
+ partitionKey: wire.partitionKey == null ? wire.partitionKey : import_node_buffer56.Buffer.from(wire.partitionKey)
15596
17222
  });
15597
17223
  }
15598
17224
  function snapshotArray(values) {
@@ -15603,7 +17229,7 @@ function snapshotArray(values) {
15603
17229
  return Object.freeze(snapshot);
15604
17230
  }
15605
17231
  function snapshotStateMeta(stateMeta) {
15606
- return snapshotRecord(stateMeta, (value) => import_node_buffer53.Buffer.isBuffer(value) ? import_node_buffer53.Buffer.from(value) : value);
17232
+ return snapshotRecord(stateMeta, (value) => import_node_buffer56.Buffer.isBuffer(value) ? import_node_buffer56.Buffer.from(value) : value);
15607
17233
  }
15608
17234
  function snapshotCommandRecord(values) {
15609
17235
  const seen = /* @__PURE__ */ new WeakMap();
@@ -15621,7 +17247,7 @@ function snapshotRecord(values, capture) {
15621
17247
  }
15622
17248
  function snapshotCommandArgument(value, seen) {
15623
17249
  if (typeof value !== "object" || value == null) return value;
15624
- if (import_node_buffer53.Buffer.isBuffer(value) || value instanceof Uint8Array) return import_node_buffer53.Buffer.from(value);
17250
+ if (import_node_buffer56.Buffer.isBuffer(value) || value instanceof Uint8Array) return import_node_buffer56.Buffer.from(value);
15625
17251
  const objectValue = value;
15626
17252
  const existing = seen.get(objectValue);
15627
17253
  if (existing != null) return existing;
@@ -16609,7 +18235,7 @@ function nativeOptionsForBootstrap(options, signal) {
16609
18235
  }
16610
18236
 
16611
18237
  // src/flow-query-projection.ts
16612
- var import_node_buffer54 = require("buffer");
18238
+ var import_node_buffer57 = require("buffer");
16613
18239
  var MAX_PROJECTION_FIELDS = 32;
16614
18240
  var MAX_DYNAMIC_NAME_BYTES = 64;
16615
18241
  var FIELD_BRAND = /* @__PURE__ */ Symbol("FerricStoreFlowProjectionField");
@@ -16677,20 +18303,38 @@ function projectFlowQuery(query, shape, ...fields) {
16677
18303
  if (containsReturnKeyword(query)) {
16678
18304
  throw new TypeError("Flow query already contains a RETURN clause");
16679
18305
  }
16680
- const trimmed = query.trim();
16681
- const base = trimmed.endsWith(";") ? trimmed.slice(0, -1).trimEnd() : trimmed;
18306
+ const base = stripOptionalTerminator(query);
16682
18307
  const result = `${base} RETURN ${shape.toUpperCase()} (${selectors.join(", ")})`;
16683
- if (import_node_buffer54.Buffer.byteLength(result, "utf8") > FLOW_QUERY_MAX_BYTES) {
18308
+ if (import_node_buffer57.Buffer.byteLength(result, "utf8") > FLOW_QUERY_MAX_BYTES) {
16684
18309
  throw new TypeError(`FLOW.QUERY query exceeds ${FLOW_QUERY_MAX_BYTES} bytes`);
16685
18310
  }
16686
18311
  return result;
16687
18312
  }
18313
+ function stripOptionalTerminator(query) {
18314
+ const trimmed = trimFlowQueryAsciiWhitespace(query);
18315
+ if (!trimmed.endsWith(";")) return trimmed;
18316
+ const base = trimFlowQueryAsciiWhitespace(trimmed.slice(0, -1));
18317
+ if (base.endsWith(";")) {
18318
+ throw new TypeError("Flow query accepts at most one trailing semicolon");
18319
+ }
18320
+ return base;
18321
+ }
16688
18322
  function querySource(query) {
16689
- const match = /^\s*FROM\s+(runs|events)\b/iu.exec(query);
16690
- if (match?.[1] == null) {
18323
+ const source = trimFlowQueryAsciiWhitespaceStart(query);
18324
+ if (!matchesFlowQueryAsciiKeyword(source, 0, "FROM") || !isFlowQueryAsciiWhitespaceCode(source.charCodeAt(4))) {
16691
18325
  throw new TypeError("Projected Flow query must start with FROM runs or FROM events");
16692
18326
  }
16693
- return match[1].toLowerCase();
18327
+ let offset = 4;
18328
+ while (isFlowQueryAsciiWhitespaceCode(source.charCodeAt(offset))) offset += 1;
18329
+ for (const candidate of ["runs", "events"]) {
18330
+ if (matchesFlowQueryAsciiKeyword(source, offset, candidate.toUpperCase())) {
18331
+ const boundary = source[offset + candidate.length];
18332
+ if (boundary == null || isFlowQueryAsciiWhitespaceCode(boundary.charCodeAt(0))) {
18333
+ return candidate;
18334
+ }
18335
+ }
18336
+ }
18337
+ throw new TypeError("Projected Flow query must start with FROM runs or FROM events");
16694
18338
  }
16695
18339
  function containsReturnKeyword(query) {
16696
18340
  let quoted = false;
@@ -16721,7 +18365,7 @@ function quoteName(value, allowPrivate) {
16721
18365
  throw new TypeError("Flow query projection metadata name must be text");
16722
18366
  }
16723
18367
  validateUnicodeScalarText2(value);
16724
- const size = import_node_buffer54.Buffer.byteLength(value, "utf8");
18368
+ const size = import_node_buffer57.Buffer.byteLength(value, "utf8");
16725
18369
  if (size === 0 || size > MAX_DYNAMIC_NAME_BYTES || !allowPrivate && value.startsWith("__")) {
16726
18370
  throw new TypeError(
16727
18371
  `Flow query projection metadata names must be 1..${MAX_DYNAMIC_NAME_BYTES} UTF-8 bytes`
@@ -16755,7 +18399,7 @@ function complete(options = {}) {
16755
18399
  function retry(options = {}) {
16756
18400
  return brandOutcome({ ...options, kind: "retry" });
16757
18401
  }
16758
- function fail(options = {}) {
18402
+ function fail2(options = {}) {
16759
18403
  return brandOutcome({ ...options, kind: "fail" });
16760
18404
  }
16761
18405
  function isOutcome(value) {
@@ -16823,7 +18467,7 @@ var LeaseRenewalGuard = class {
16823
18467
  if (options.leaseRenewal === false) {
16824
18468
  return;
16825
18469
  }
16826
- const intervalMs = positiveInteger2(options.leaseRenewIntervalMs, Math.max(1, Math.trunc(leaseMs / 2)));
18470
+ const intervalMs = positiveInteger3(options.leaseRenewIntervalMs, Math.max(1, Math.trunc(leaseMs / 2)));
16827
18471
  this.timer = setLongInterval(() => this.renew(), intervalMs);
16828
18472
  this.timer.unref();
16829
18473
  }
@@ -16895,7 +18539,7 @@ function snapshotLeasedJob(job) {
16895
18539
  return snapshot;
16896
18540
  }
16897
18541
  function workerConcurrency(options) {
16898
- return positiveInteger2(options.concurrency ?? options.workers, 1);
18542
+ return positiveInteger3(options.concurrency ?? options.workers, 1);
16899
18543
  }
16900
18544
  function workerRefillStrategy(options) {
16901
18545
  return options.refillStrategy === "wave" ? "wave" : "continuous";
@@ -16938,18 +18582,18 @@ function workerErrorPayload(error, options, codec) {
16938
18582
  }
16939
18583
  function workerBatchSize(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
16940
18584
  return Math.min(
16941
- positiveInteger2(options.batchSize, 10),
16942
- positiveInteger2(flowManyBatchLimit, DEFAULT_FLOW_MANY_BATCH_LIMIT2)
18585
+ positiveInteger3(options.batchSize, 10),
18586
+ positiveInteger3(flowManyBatchLimit, DEFAULT_FLOW_MANY_BATCH_LIMIT2)
16943
18587
  );
16944
18588
  }
16945
18589
  function workerClaimLimit(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
16946
18590
  return Math.min(workerBatchSize(options, flowManyBatchLimit), workerConcurrency(options));
16947
18591
  }
16948
18592
  function workerDrainBatches(options) {
16949
- return positiveInteger2(options.claimDrainBatches, 1);
18593
+ return positiveInteger3(options.claimDrainBatches, 1);
16950
18594
  }
16951
18595
  function workerLeaseMs(options, fallback = 3e4) {
16952
- return positiveInteger2(options.leaseMs, positiveInteger2(fallback, 3e4));
18596
+ return positiveInteger3(options.leaseMs, positiveInteger3(fallback, 3e4));
16953
18597
  }
16954
18598
  function workerIdleSleepMs(options) {
16955
18599
  return nonNegativeDuration(options.idleSleepMs, 250);
@@ -16961,7 +18605,7 @@ function workerClaimBlockMs(options, useBlocking) {
16961
18605
  if (options.signal == null || !Number.isFinite(options.blockMs) || options.blockMs < 0) {
16962
18606
  return options.blockMs;
16963
18607
  }
16964
- const abortPollMs = positiveInteger2(options.abortPollMs, 1e3);
18608
+ const abortPollMs = positiveInteger3(options.abortPollMs, 1e3);
16965
18609
  return options.blockMs === 0 ? abortPollMs : Math.min(Math.trunc(options.blockMs), abortPollMs);
16966
18610
  }
16967
18611
  function workerMaxIdleSleepMs(options, idleSleepMs = workerIdleSleepMs(options)) {
@@ -16972,14 +18616,14 @@ function nextWorkerIdleSleepMs(currentIdleSleepMs, maxIdleSleepMs) {
16972
18616
  return Math.min(maxIdleSleepMs, Math.max(1, currentIdleSleepMs * 2));
16973
18617
  }
16974
18618
  async function runContinuousWorkerPool(options) {
16975
- const concurrency = positiveInteger2(options.concurrency, 1);
16976
- const maxClaimSize = positiveInteger2(options.maxClaimSize, concurrency);
18619
+ const concurrency = positiveInteger3(options.concurrency, 1);
18620
+ const maxClaimSize = positiveInteger3(options.maxClaimSize, concurrency);
16977
18621
  const initialIdleSleepMs = nonNegativeDuration(options.idleSleepMs, 250);
16978
18622
  const maxIdleSleepMs = Math.max(
16979
18623
  initialIdleSleepMs,
16980
18624
  nonNegativeDuration(options.maxIdleSleepMs, 5e3)
16981
18625
  );
16982
- const refillDelayMs = nonNegativeInteger3(options.refillDelayMs, 0);
18626
+ const refillDelayMs = nonNegativeInteger4(options.refillDelayMs, 0);
16983
18627
  const active = /* @__PURE__ */ new Set();
16984
18628
  const pending = [];
16985
18629
  let pendingIndex = 0;
@@ -17095,10 +18739,10 @@ function normalizeWorkflowWorkerConfig(options) {
17095
18739
  ...captured
17096
18740
  });
17097
18741
  }
17098
- function positiveInteger2(value, fallback) {
18742
+ function positiveInteger3(value, fallback) {
17099
18743
  return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
17100
18744
  }
17101
- function nonNegativeInteger3(value, fallback) {
18745
+ function nonNegativeInteger4(value, fallback) {
17102
18746
  return value == null || !Number.isFinite(value) ? fallback : Math.max(0, Math.trunc(value));
17103
18747
  }
17104
18748
  function nonNegativeDuration(value, fallback) {
@@ -17552,7 +19196,7 @@ async function applyQueueException(queue, options, job, error) {
17552
19196
  const policy = normalizeExceptionPolicy(options.exceptionPolicy);
17553
19197
  if (policy === "raise") throw error;
17554
19198
  const payload = workerErrorPayload(error, options, queue.client.codec);
17555
- await applyQueueOutcome(queue, job, policy === "fail" ? fail({ error: payload }) : retry({ error: payload }));
19199
+ await applyQueueOutcome(queue, job, policy === "fail" ? fail2({ error: payload }) : retry({ error: payload }));
17556
19200
  }
17557
19201
 
17558
19202
  // src/queue-pending-completions.ts
@@ -17799,7 +19443,7 @@ var QueueWorker = class {
17799
19443
  const policy = normalizeExceptionPolicy(this.options.exceptionPolicy);
17800
19444
  if (policy !== "raise") {
17801
19445
  const payload = workerErrorPayload(error, this.options, this.queue.client.codec);
17802
- outcome = policy === "fail" ? fail({ error: payload }) : retry({ error: payload });
19446
+ outcome = policy === "fail" ? fail2({ error: payload }) : retry({ error: payload });
17803
19447
  }
17804
19448
  }
17805
19449
  const stopped = await stopQueueBatchGuards(guards.map((guard) => guard.job), guards);
@@ -17982,13 +19626,13 @@ var QueueWorker = class {
17982
19626
 
17983
19627
  // src/flow-client-options.ts
17984
19628
  function snapshotFlowClientOptions(options, context) {
17985
- const own = { ...options };
17986
- if (typeof own.type !== "string" || own.type.length === 0) {
19629
+ const own2 = { ...options };
19630
+ if (typeof own2.type !== "string" || own2.type.length === 0) {
17987
19631
  throw new TypeError(`${context} type must be an own non-empty string`);
17988
19632
  }
17989
- const valueConfig = own.valueConfig;
19633
+ const valueConfig = own2.valueConfig;
17990
19634
  return Object.freeze({
17991
- ...own,
19635
+ ...own2,
17992
19636
  ...valueConfig == null ? {} : { valueConfig: Object.freeze({ ...valueConfig }) }
17993
19637
  });
17994
19638
  }
@@ -18411,19 +20055,19 @@ async function applyFail(client, ctx, outcome, returnRecord) {
18411
20055
 
18412
20056
  // src/workflow-registration.ts
18413
20057
  function createWorkflowStateRegistration(name, handler, options, defaultValueMaxBytes) {
18414
- const own = { ...options };
18415
- const claimValues = own.claimValues == null ? void 0 : snapshotOwnStringArray(own.claimValues, "claimValues");
18416
- const retryPolicy = own.retryPolicy == null ? void 0 : Object.freeze({ ...own.retryPolicy });
20058
+ const own2 = { ...options };
20059
+ const claimValues = own2.claimValues == null ? void 0 : snapshotOwnStringArray(own2.claimValues, "claimValues");
20060
+ const retryPolicy = own2.retryPolicy == null ? void 0 : Object.freeze({ ...own2.retryPolicy });
18417
20061
  return Object.freeze({
18418
- claimPayload: own.claimPayload ?? true,
18419
- claimRecord: own.claimRecord ?? true,
18420
- exceptionPolicy: normalizeExceptionPolicy(own.exceptionPolicy),
20062
+ claimPayload: own2.claimPayload ?? true,
20063
+ claimRecord: own2.claimRecord ?? true,
20064
+ exceptionPolicy: normalizeExceptionPolicy(own2.exceptionPolicy),
18421
20065
  handler,
18422
- leaseMs: own.leaseMs ?? 3e4,
18423
- ...own.mode == null ? {} : { mode: normalizeStateMode(own.mode) },
20066
+ leaseMs: own2.leaseMs ?? 3e4,
20067
+ ...own2.mode == null ? {} : { mode: normalizeStateMode(own2.mode) },
18424
20068
  name,
18425
- returnRecord: own.returnRecord ?? false,
18426
- valueMaxBytes: own.valueMaxBytes ?? defaultValueMaxBytes,
20069
+ returnRecord: own2.returnRecord ?? false,
20070
+ valueMaxBytes: own2.valueMaxBytes ?? defaultValueMaxBytes,
18427
20071
  ...claimValues == null ? {} : { claimValues },
18428
20072
  ...retryPolicy == null ? {} : { retryPolicy }
18429
20073
  });
@@ -18712,7 +20356,7 @@ var WorkflowWorker = class {
18712
20356
  await applyWorkflowOutcome(
18713
20357
  this.workflow.client,
18714
20358
  ctx,
18715
- fail({ error: workerErrorPayload(error, this.options, this.workflow.client.codec) }),
20359
+ fail2({ error: workerErrorPayload(error, this.options, this.workflow.client.codec) }),
18716
20360
  registration.returnRecord
18717
20361
  );
18718
20362
  return;
@@ -18727,8 +20371,8 @@ var WorkflowWorker = class {
18727
20371
  };
18728
20372
 
18729
20373
  // src/version.ts
18730
- var FERRICSTORE_SDK_VERSION = "0.5.1";
18731
- var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.0";
20374
+ var FERRICSTORE_SDK_VERSION = "0.11.4";
20375
+ var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
18732
20376
  var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
18733
20377
  // Annotate the CommonJS export names for ESM import in node:
18734
20378
  0 && (module.exports = {