@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.
- package/README.md +44 -5
- package/dist/index.cjs +2162 -518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -34
- package/dist/index.d.ts +183 -34
- package/dist/index.js +2134 -490
- package/dist/index.js.map +1 -1
- package/docs/api/assets/hierarchy.js +1 -1
- package/docs/api/assets/navigation.js +1 -1
- package/docs/api/assets/search.js +1 -1
- package/docs/api/classes/ConnectionClosedError.html +1 -1
- package/docs/api/classes/FerricStoreClient.html +3 -3
- package/docs/api/classes/FerricStoreError.html +1 -1
- package/docs/api/classes/FlowAlreadyExistsError.html +1 -1
- package/docs/api/classes/FlowBatchError.html +1 -1
- package/docs/api/classes/FlowNotFoundError.html +1 -1
- package/docs/api/classes/FlowQueryError.html +1 -1
- package/docs/api/classes/FlowWrongStateError.html +1 -1
- package/docs/api/classes/InvalidCommandError.html +1 -1
- package/docs/api/classes/LeaseRenewalError.html +1 -1
- package/docs/api/classes/LockHeldError.html +1 -1
- package/docs/api/classes/LockNotOwnedError.html +1 -1
- package/docs/api/classes/NativeAdapter.html +1 -1
- package/docs/api/classes/OverloadedError.html +1 -1
- package/docs/api/classes/QueueCompletionError.html +1 -1
- package/docs/api/classes/RequestTimeoutError.html +1 -1
- package/docs/api/classes/RerouteError.html +1 -1
- package/docs/api/classes/StaleLeaseError.html +1 -1
- package/docs/api/classes/StalePolicyGenerationError.html +1 -1
- package/docs/api/functions/projectFlowQuery.html +1 -1
- package/docs/api/hierarchy.html +1 -1
- package/docs/api/index.html +34 -5
- package/docs/api/interfaces/AdminListOptions.html +2 -2
- package/docs/api/interfaces/ApprovalListOptions.html +2 -2
- package/docs/api/interfaces/ApprovalRequestOptions.html +2 -2
- package/docs/api/interfaces/AttributeQueryOptions.html +2 -2
- package/docs/api/interfaces/AutoBatchOptions.html +2 -2
- package/docs/api/interfaces/BudgetCommitOptions.html +2 -2
- package/docs/api/interfaces/BudgetReserveOptions.html +2 -2
- package/docs/api/interfaces/CancelOptions.html +2 -2
- package/docs/api/interfaces/CircuitOpenOptions.html +2 -2
- package/docs/api/interfaces/ClaimDueOptions.html +3 -3
- package/docs/api/interfaces/ClaimHydrationItem.html +2 -2
- package/docs/api/interfaces/CompleteJobsAndClaimJobsResult.html +6 -6
- package/docs/api/interfaces/CompleteManyOptions.html +2 -2
- package/docs/api/interfaces/CompleteOptions.html +2 -2
- package/docs/api/interfaces/CompleteOutcome.html +1 -1
- package/docs/api/interfaces/CreateManyOptions.html +5 -5
- package/docs/api/interfaces/CreateOptions.html +3 -3
- package/docs/api/interfaces/EffectCompensateOptions.html +2 -2
- package/docs/api/interfaces/EffectConfirmOptions.html +2 -2
- package/docs/api/interfaces/EffectFailOptions.html +2 -2
- package/docs/api/interfaces/EffectReserveOptions.html +2 -2
- package/docs/api/interfaces/EffectStatusOptions.html +2 -2
- package/docs/api/interfaces/ExtendLeaseOptions.html +2 -2
- package/docs/api/interfaces/FailOptions.html +2 -2
- package/docs/api/interfaces/FailOutcome.html +1 -1
- package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +4 -4
- package/docs/api/interfaces/FerricStoreClientOptions.html +4 -4
- package/docs/api/interfaces/FlowBatchCompletedItem.html +2 -2
- package/docs/api/interfaces/FlowEventProjectionField.html +2 -2
- package/docs/api/interfaces/FlowExplainCapabilities.html +5 -0
- package/docs/api/interfaces/FlowExplainResult.html +8 -2
- package/docs/api/interfaces/FlowPolicyOptions.html +5 -5
- package/docs/api/interfaces/FlowQueryCountResult.html +1 -1
- package/docs/api/interfaces/FlowQueryIndex.html +11 -2
- package/docs/api/interfaces/FlowQueryIndexBuild.html +10 -0
- package/docs/api/interfaces/FlowQueryIndexCoverage.html +5 -0
- package/docs/api/interfaces/FlowQueryIndexField.html +5 -0
- package/docs/api/interfaces/FlowQueryIndexFormat.html +2 -2
- package/docs/api/interfaces/FlowQueryIndexProgress.html +7 -0
- package/docs/api/interfaces/FlowQueryIndexRegistry.html +2 -2
- package/docs/api/interfaces/FlowQueryIndexRetirement.html +10 -0
- package/docs/api/interfaces/FlowQueryIndexServices.html +6 -0
- package/docs/api/interfaces/FlowQueryIndexStatistics.html +11 -0
- package/docs/api/interfaces/FlowQueryIndexStatus.html +2 -2
- package/docs/api/interfaces/FlowQueryIndexValidation.html +13 -0
- package/docs/api/interfaces/FlowQueryQuality.html +2 -2
- package/docs/api/interfaces/FlowQueryRecordsResult.html +1 -1
- package/docs/api/interfaces/FlowRunProjectionField.html +2 -2
- package/docs/api/interfaces/FlowStatePolicy.html +2 -2
- package/docs/api/interfaces/FlowStatsOptions.html +2 -2
- package/docs/api/interfaces/GovernanceLedgerOptions.html +2 -2
- package/docs/api/interfaces/HistoryOptions.html +2 -2
- package/docs/api/interfaces/InvocationCreateOptions.html +2 -2
- package/docs/api/interfaces/LeaseMutationOptions.html +2 -2
- package/docs/api/interfaces/LimitAmountOptions.html +2 -2
- package/docs/api/interfaces/LimitLeaseOptions.html +2 -2
- package/docs/api/interfaces/LimitListOptions.html +2 -2
- package/docs/api/interfaces/LimitReleaseOptions.html +2 -2
- package/docs/api/interfaces/MutateOptions.html +2 -2
- package/docs/api/interfaces/NamedValueMutation.html +1 -1
- package/docs/api/interfaces/ReadOptions.html +3 -3
- package/docs/api/interfaces/ReclaimOptions.html +2 -2
- package/docs/api/interfaces/RequestContext.html +2 -2
- package/docs/api/interfaces/RequestContextOptions.html +2 -2
- package/docs/api/interfaces/RetryOptions.html +2 -2
- package/docs/api/interfaces/RetryOutcome.html +1 -1
- package/docs/api/interfaces/RunStepsItem.html +2 -2
- package/docs/api/interfaces/RunStepsManyOptions.html +2 -2
- package/docs/api/interfaces/ScheduleFireDueOptions.html +3 -2
- package/docs/api/interfaces/ScheduleFireOptions.html +3 -0
- package/docs/api/interfaces/ScheduleListOptions.html +2 -2
- package/docs/api/interfaces/ScheduleOptions.html +4 -2
- package/docs/api/interfaces/SearchOptions.html +3 -3
- package/docs/api/interfaces/StartAndClaimOptions.html +3 -3
- package/docs/api/interfaces/StepContinueOptions.html +3 -3
- package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
- package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
- package/docs/api/interfaces/TransitionOptions.html +2 -2
- package/docs/api/interfaces/TransitionOutcome.html +1 -1
- package/docs/api/modules.html +1 -1
- package/docs/api/types/ConnectionRequestDisposition.html +1 -1
- package/docs/api/types/FlowProjectionField.html +1 -1
- package/docs/api/types/FlowProjectionShape.html +1 -1
- package/docs/api/types/FlowQueryIndexServiceState.html +1 -0
- package/docs/api/types/FlowStateMode.html +1 -1
- package/docs/api/types/FlowStatePolicyLike.html +1 -1
- package/docs/api/types/ManagementPairs.html +1 -1
- package/docs/api/types/ScheduleCatchupPolicy.html +2 -0
- package/docs/api/types/ScheduleFireDueResult.html +3 -0
- package/docs/api/types/ScheduleFireResult.html +2 -0
- package/docs/api/types/ScheduleKind.html +1 -0
- package/docs/api/types/ScheduleOverlapPolicy.html +1 -0
- package/docs/api/types/ScheduleRecord.html +12 -0
- package/docs/api/types/ScheduleState.html +1 -0
- package/docs/api/types/SearchStateMeta.html +1 -1
- package/docs/api/variables/FERRICSTORE_MINIMUM_SERVER_VERSION.html +1 -1
- package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
- package/docs/api/variables/FlowProjection.html +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1140,10 +1140,10 @@ var NativeFlowControl = class {
|
|
|
1140
1140
|
|
|
1141
1141
|
// src/native-heartbeat.ts
|
|
1142
1142
|
var NativeHeartbeat = class {
|
|
1143
|
-
constructor(intervalMs, send,
|
|
1143
|
+
constructor(intervalMs, send, fail3) {
|
|
1144
1144
|
this.intervalMs = intervalMs;
|
|
1145
1145
|
this.send = send;
|
|
1146
|
-
this.fail =
|
|
1146
|
+
this.fail = fail3;
|
|
1147
1147
|
}
|
|
1148
1148
|
intervalMs;
|
|
1149
1149
|
send;
|
|
@@ -1190,9 +1190,11 @@ var FLOW_QUERY_LANGUAGE_VERSION = "FQL1";
|
|
|
1190
1190
|
var FLOW_QUERY_MAX_BYTES = 16 * 1024;
|
|
1191
1191
|
var FLOW_QUERY_MAX_PARAMETERS = 64;
|
|
1192
1192
|
var FLOW_QUERY_MAX_PARAMETER_NAME_BYTES = 128;
|
|
1193
|
+
var FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES = 65535;
|
|
1193
1194
|
var MIN_I64 = -(1n << 63n);
|
|
1194
1195
|
var MAX_I64 = (1n << 63n) - 1n;
|
|
1195
1196
|
var INDEX_ID = /^[A-Za-z0-9_.:-]{1,64}$/u;
|
|
1197
|
+
var PARAMETER_NAME = /^[A-Za-z0-9_.-]+$/u;
|
|
1196
1198
|
function flowQueryArgs(query, params = {}) {
|
|
1197
1199
|
validateFlowQueryText(query);
|
|
1198
1200
|
const entries = flowQueryParameterEntries(params);
|
|
@@ -1246,9 +1248,34 @@ function validateFlowQueryIndexId(indexId) {
|
|
|
1246
1248
|
}
|
|
1247
1249
|
}
|
|
1248
1250
|
function hasFlowExplainPrefix(query) {
|
|
1249
|
-
const source = query
|
|
1250
|
-
|
|
1251
|
-
|
|
1251
|
+
const source = trimFlowQueryAsciiWhitespaceStart(query);
|
|
1252
|
+
return matchesFlowQueryAsciiKeyword(source, 0, "EXPLAIN") && (source.length === 7 || isFlowQueryAsciiWhitespaceCode(source.charCodeAt(7)));
|
|
1253
|
+
}
|
|
1254
|
+
function matchesFlowQueryAsciiKeyword(value, offset, keyword) {
|
|
1255
|
+
if (offset < 0 || offset + keyword.length > value.length) return false;
|
|
1256
|
+
for (let index = 0; index < keyword.length; index += 1) {
|
|
1257
|
+
let actual = value.charCodeAt(offset + index);
|
|
1258
|
+
if (actual >= 97 && actual <= 122) actual -= 32;
|
|
1259
|
+
if (actual !== keyword.charCodeAt(index)) return false;
|
|
1260
|
+
}
|
|
1261
|
+
return true;
|
|
1262
|
+
}
|
|
1263
|
+
function trimFlowQueryAsciiWhitespaceStart(value) {
|
|
1264
|
+
let start = 0;
|
|
1265
|
+
while (start < value.length && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(start))) {
|
|
1266
|
+
start += 1;
|
|
1267
|
+
}
|
|
1268
|
+
return start === 0 ? value : value.slice(start);
|
|
1269
|
+
}
|
|
1270
|
+
function trimFlowQueryAsciiWhitespace(value) {
|
|
1271
|
+
let start = 0;
|
|
1272
|
+
let end = value.length;
|
|
1273
|
+
while (start < end && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(start))) start += 1;
|
|
1274
|
+
while (end > start && isFlowQueryAsciiWhitespaceCode(value.charCodeAt(end - 1))) end -= 1;
|
|
1275
|
+
return start === 0 && end === value.length ? value : value.slice(start, end);
|
|
1276
|
+
}
|
|
1277
|
+
function isFlowQueryAsciiWhitespaceCode(code) {
|
|
1278
|
+
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
1252
1279
|
}
|
|
1253
1280
|
function flowQueryParameterEntries(params) {
|
|
1254
1281
|
if (typeof params !== "object" || params == null || Array.isArray(params)) {
|
|
@@ -1274,19 +1301,26 @@ function flowQueryParameterEntries(params) {
|
|
|
1274
1301
|
function validateFlowQueryParameterName(name) {
|
|
1275
1302
|
validateUnicodeScalarText(name, "FLOW.QUERY parameter name");
|
|
1276
1303
|
const size = Buffer4.byteLength(name, "utf8");
|
|
1277
|
-
if (size === 0 || size > FLOW_QUERY_MAX_PARAMETER_NAME_BYTES) {
|
|
1304
|
+
if (size === 0 || size > FLOW_QUERY_MAX_PARAMETER_NAME_BYTES || !PARAMETER_NAME.test(name)) {
|
|
1278
1305
|
throw new TypeError(
|
|
1279
|
-
`FLOW.QUERY parameter names must be 1..${FLOW_QUERY_MAX_PARAMETER_NAME_BYTES}
|
|
1306
|
+
`FLOW.QUERY parameter names must be 1..${FLOW_QUERY_MAX_PARAMETER_NAME_BYTES} ASCII letters, digits, '_', '.', or '-'`
|
|
1280
1307
|
);
|
|
1281
1308
|
}
|
|
1282
1309
|
}
|
|
1283
1310
|
function normalizeFlowQueryParameter(value, name) {
|
|
1284
1311
|
if (typeof value === "string") {
|
|
1285
1312
|
validateUnicodeScalarText(value, `FLOW.QUERY parameter ${JSON.stringify(name)}`);
|
|
1313
|
+
validateParameterValueSize(Buffer4.byteLength(value, "utf8"), name);
|
|
1314
|
+
return value;
|
|
1315
|
+
}
|
|
1316
|
+
if (Buffer4.isBuffer(value)) {
|
|
1317
|
+
validateParameterValueSize(value.byteLength, name);
|
|
1286
1318
|
return value;
|
|
1287
1319
|
}
|
|
1288
|
-
if (
|
|
1289
|
-
|
|
1320
|
+
if (value instanceof Uint8Array) {
|
|
1321
|
+
validateParameterValueSize(value.byteLength, name);
|
|
1322
|
+
return Buffer4.from(value);
|
|
1323
|
+
}
|
|
1290
1324
|
if (typeof value === "boolean") return value;
|
|
1291
1325
|
if (typeof value === "bigint" && value >= MIN_I64 && value <= MAX_I64) return value;
|
|
1292
1326
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -1296,6 +1330,13 @@ function normalizeFlowQueryParameter(value, name) {
|
|
|
1296
1330
|
`FLOW.QUERY parameter ${JSON.stringify(name)} must be text, bytes, boolean, a finite float, or a signed 64-bit integer`
|
|
1297
1331
|
);
|
|
1298
1332
|
}
|
|
1333
|
+
function validateParameterValueSize(size, name) {
|
|
1334
|
+
if (size > FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES) {
|
|
1335
|
+
throw new TypeError(
|
|
1336
|
+
`FLOW.QUERY parameter ${JSON.stringify(name)} exceeds ${FLOW_QUERY_MAX_PARAMETER_VALUE_BYTES} bytes`
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1299
1340
|
function commandText(value, context) {
|
|
1300
1341
|
if (typeof value === "string") {
|
|
1301
1342
|
validateUnicodeScalarText(value, context);
|
|
@@ -3049,6 +3090,7 @@ var FLOW_ADMIN_FIELDS = {
|
|
|
3049
3090
|
AT_MS: "at_ms",
|
|
3050
3091
|
BLOCK: "block_ms",
|
|
3051
3092
|
BLOCK_MS: "block_ms",
|
|
3093
|
+
CATCHUP_POLICY: "catchup_policy",
|
|
3052
3094
|
CONSISTENT_PROJECTION: "consistent_projection",
|
|
3053
3095
|
CORRELATION_ID: "correlation_id",
|
|
3054
3096
|
COUNT: "count",
|
|
@@ -5482,15 +5524,15 @@ function requiredMap(value, context) {
|
|
|
5482
5524
|
}
|
|
5483
5525
|
throw incompatibleServer(`${context} is missing or invalid`);
|
|
5484
5526
|
}
|
|
5485
|
-
function requiredBoundedText(
|
|
5486
|
-
const value = strictText(field(
|
|
5527
|
+
function requiredBoundedText(mapping2, name) {
|
|
5528
|
+
const value = strictText(field(mapping2, name));
|
|
5487
5529
|
if (value == null || value.length === 0 || Buffer20.byteLength(value) > 256) {
|
|
5488
5530
|
throw incompatibleServer(`HELLO flow_query ${name} must be bounded non-empty text`);
|
|
5489
5531
|
}
|
|
5490
5532
|
return value;
|
|
5491
5533
|
}
|
|
5492
|
-
function boundedTextSet(
|
|
5493
|
-
const raw = field(
|
|
5534
|
+
function boundedTextSet(mapping2, name, maximum) {
|
|
5535
|
+
const raw = field(mapping2, name);
|
|
5494
5536
|
if (!Array.isArray(raw) || raw.length < 1 || raw.length > maximum) {
|
|
5495
5537
|
throw incompatibleServer(`HELLO ${name} must contain 1..${maximum} entries`);
|
|
5496
5538
|
}
|
|
@@ -5730,7 +5772,7 @@ function truncate(value) {
|
|
|
5730
5772
|
}
|
|
5731
5773
|
|
|
5732
5774
|
// src/protocol-flow-query-result.ts
|
|
5733
|
-
import { Buffer as Buffer24 } from "buffer";
|
|
5775
|
+
import { Buffer as Buffer24, isUtf8 as isUtf82 } from "buffer";
|
|
5734
5776
|
|
|
5735
5777
|
// src/protocol-binary-detacher.ts
|
|
5736
5778
|
import { Buffer as Buffer23 } from "buffer";
|
|
@@ -5767,7 +5809,10 @@ function isDecodedRecord(value) {
|
|
|
5767
5809
|
// src/protocol-flow-query-result.ts
|
|
5768
5810
|
var CONTRACT = "ferric.flow.query.result/v1";
|
|
5769
5811
|
var MAX_RECORDS = 100;
|
|
5812
|
+
var MIN_CURSOR_BYTES = 16;
|
|
5770
5813
|
var MAX_CURSOR_BYTES = 4096;
|
|
5814
|
+
var CURSOR_PREFIX = Buffer24.from("fqc1_");
|
|
5815
|
+
var MAX_SIGNED_64 = (1n << 63n) - 1n;
|
|
5771
5816
|
var RECORD_FIELDS = [
|
|
5772
5817
|
"id",
|
|
5773
5818
|
"type",
|
|
@@ -5831,9 +5876,13 @@ function decodeCompactFlowQueryResult(data, budget) {
|
|
|
5831
5876
|
const usage = {};
|
|
5832
5877
|
for (const field3 of USAGE_FIELDS) {
|
|
5833
5878
|
const read = readU64(data, offset);
|
|
5879
|
+
if (BigInt(read.value) > MAX_SIGNED_64) {
|
|
5880
|
+
throw new FerricStoreError("compact FLOW.QUERY usage exceeds signed 64-bit range");
|
|
5881
|
+
}
|
|
5834
5882
|
usage[field3] = read.value;
|
|
5835
5883
|
offset = read.offset;
|
|
5836
5884
|
}
|
|
5885
|
+
validateCompactUsage(usage);
|
|
5837
5886
|
let result;
|
|
5838
5887
|
if (kind === 0) {
|
|
5839
5888
|
const page = readPage(data, offset);
|
|
@@ -5842,6 +5891,7 @@ function decodeCompactFlowQueryResult(data, budget) {
|
|
|
5842
5891
|
const count = data.readUInt32BE(offset);
|
|
5843
5892
|
offset += 4;
|
|
5844
5893
|
if (count > MAX_RECORDS) throw new FerricStoreError("compact FLOW.QUERY page exceeds 100 records");
|
|
5894
|
+
validateCompactRecordUsage(usage, count);
|
|
5845
5895
|
consumeDecodeItems(count, budget);
|
|
5846
5896
|
const records = new Array(count);
|
|
5847
5897
|
for (let index = 0; index < count; index += 1) {
|
|
@@ -5851,7 +5901,13 @@ function decodeCompactFlowQueryResult(data, budget) {
|
|
|
5851
5901
|
}
|
|
5852
5902
|
result = { version: Buffer24.from(CONTRACT), records, page: page.value, quality, usage };
|
|
5853
5903
|
} else if (kind === 1) {
|
|
5904
|
+
if (usageInteger(usage, "result_records") !== 1n) {
|
|
5905
|
+
throw new FerricStoreError("compact FLOW.QUERY count usage is inconsistent");
|
|
5906
|
+
}
|
|
5854
5907
|
const count = readU64(data, offset);
|
|
5908
|
+
if (BigInt(count.value) > MAX_SIGNED_64) {
|
|
5909
|
+
throw new FerricStoreError("compact FLOW.QUERY count exceeds signed 64-bit range");
|
|
5910
|
+
}
|
|
5855
5911
|
offset = count.offset;
|
|
5856
5912
|
result = {
|
|
5857
5913
|
version: Buffer24.from(CONTRACT),
|
|
@@ -5876,18 +5932,37 @@ function readPage(data, offset) {
|
|
|
5876
5932
|
if (hasMoreCode === 0 && size === NULL_U32) {
|
|
5877
5933
|
return { value: { has_more: false, cursor: null }, offset };
|
|
5878
5934
|
}
|
|
5879
|
-
if (hasMoreCode !== 1 || size
|
|
5935
|
+
if (hasMoreCode !== 1 || size < MIN_CURSOR_BYTES || size === NULL_U32 || size > MAX_CURSOR_BYTES) {
|
|
5880
5936
|
throw new FerricStoreError("invalid compact FLOW.QUERY page cursor");
|
|
5881
5937
|
}
|
|
5882
5938
|
requireAvailable(data, offset, size);
|
|
5939
|
+
const cursor = data.subarray(offset, offset + size);
|
|
5940
|
+
if (!cursor.subarray(0, CURSOR_PREFIX.byteLength).equals(CURSOR_PREFIX) || !isUtf82(cursor)) {
|
|
5941
|
+
throw new FerricStoreError("invalid compact FLOW.QUERY page cursor");
|
|
5942
|
+
}
|
|
5883
5943
|
return {
|
|
5884
5944
|
value: {
|
|
5885
5945
|
has_more: true,
|
|
5886
|
-
cursor: detachDecodedBinary(
|
|
5946
|
+
cursor: detachDecodedBinary(cursor, data.byteLength)
|
|
5887
5947
|
},
|
|
5888
5948
|
offset: offset + size
|
|
5889
5949
|
};
|
|
5890
5950
|
}
|
|
5951
|
+
function validateCompactUsage(usage) {
|
|
5952
|
+
const scanned = usageInteger(usage, "scanned_entries");
|
|
5953
|
+
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) {
|
|
5954
|
+
throw new FerricStoreError("compact FLOW.QUERY usage counters are inconsistent");
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
function validateCompactRecordUsage(usage, count) {
|
|
5958
|
+
const resultRecords = usageInteger(usage, "result_records");
|
|
5959
|
+
if (resultRecords !== BigInt(count) || resultRecords > usageInteger(usage, "scanned_entries")) {
|
|
5960
|
+
throw new FerricStoreError("compact FLOW.QUERY record usage is inconsistent");
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
function usageInteger(usage, field3) {
|
|
5964
|
+
return BigInt(usage[field3]);
|
|
5965
|
+
}
|
|
5891
5966
|
function readRecord(data, offset, budget) {
|
|
5892
5967
|
requireAvailable(data, offset, 4);
|
|
5893
5968
|
const bitmap = data.readUInt32BE(offset);
|
|
@@ -6780,15 +6855,15 @@ function snapshotOwnStringArray(values, name) {
|
|
|
6780
6855
|
|
|
6781
6856
|
// src/client-native-options.ts
|
|
6782
6857
|
function snapshotNativeClientOptions(options) {
|
|
6783
|
-
const
|
|
6858
|
+
const own2 = { ...options };
|
|
6784
6859
|
const snapshot = {
|
|
6785
|
-
...
|
|
6786
|
-
...typeof
|
|
6787
|
-
...
|
|
6788
|
-
...
|
|
6789
|
-
...
|
|
6790
|
-
...
|
|
6791
|
-
...
|
|
6860
|
+
...own2,
|
|
6861
|
+
...typeof own2.autoReconnect === "object" && own2.autoReconnect != null ? { autoReconnect: Object.freeze({ ...own2.autoReconnect }) } : {},
|
|
6862
|
+
...own2.endpointPolicy == null ? {} : { endpointPolicy: snapshotEndpointPolicy(own2.endpointPolicy) },
|
|
6863
|
+
...own2.events == null ? {} : { events: snapshotOwnStringArray(own2.events, "events") },
|
|
6864
|
+
...own2.seeds == null ? {} : { seeds: snapshotOwnStringArray(own2.seeds, "seeds") },
|
|
6865
|
+
...own2.tlsOptions == null ? {} : { tlsOptions: snapshotTlsOptions(own2.tlsOptions) },
|
|
6866
|
+
...own2.trustedHosts == null ? {} : { trustedHosts: snapshotOwnStringArray(own2.trustedHosts, "trustedHosts") }
|
|
6792
6867
|
};
|
|
6793
6868
|
return Object.freeze(snapshot);
|
|
6794
6869
|
}
|
|
@@ -6829,12 +6904,12 @@ function topologyNativeOptions(options) {
|
|
|
6829
6904
|
}
|
|
6830
6905
|
function snapshotEndpointPolicy(policy) {
|
|
6831
6906
|
if (typeof policy !== "object") return policy;
|
|
6832
|
-
const
|
|
6833
|
-
if (!Object.hasOwn(
|
|
6907
|
+
const own2 = { ...policy };
|
|
6908
|
+
if (!Object.hasOwn(own2, "allowHosts") || !Array.isArray(own2.allowHosts)) {
|
|
6834
6909
|
throw new TypeError("endpointPolicy.allowHosts must be an own array of strings");
|
|
6835
6910
|
}
|
|
6836
6911
|
return Object.freeze({
|
|
6837
|
-
allowHosts: snapshotOwnStringArray(
|
|
6912
|
+
allowHosts: snapshotOwnStringArray(own2.allowHosts, "endpointPolicy.allowHosts")
|
|
6838
6913
|
});
|
|
6839
6914
|
}
|
|
6840
6915
|
function snapshotTlsOptions(options) {
|
|
@@ -7913,6 +7988,63 @@ function nativeStartupPayload(clientName, events) {
|
|
|
7913
7988
|
};
|
|
7914
7989
|
}
|
|
7915
7990
|
|
|
7991
|
+
// src/flow-query-deadline.ts
|
|
7992
|
+
import { Buffer as Buffer31 } from "buffer";
|
|
7993
|
+
function withFlowQueryDeadline(command, timeoutMs, nowMs2) {
|
|
7994
|
+
if (timeoutMs == null) return command;
|
|
7995
|
+
if (command.opcode === OPCODES.pipeline) {
|
|
7996
|
+
return withPipelineQueryDeadlines(command, timeoutMs, nowMs2);
|
|
7997
|
+
}
|
|
7998
|
+
if (command.opcode !== OPCODES.flowQuery) return command;
|
|
7999
|
+
const payload = command.payload;
|
|
8000
|
+
if (payload instanceof Map) {
|
|
8001
|
+
const nextPayload = new Map(payload);
|
|
8002
|
+
nextPayload.set("deadline_ms", flowQueryDeadlineMs(timeoutMs, nowMs2));
|
|
8003
|
+
return { ...command, payload: nextPayload };
|
|
8004
|
+
}
|
|
8005
|
+
if (typeof payload !== "object" || payload == null || Array.isArray(payload) || Buffer31.isBuffer(payload) || payload instanceof Uint8Array) {
|
|
8006
|
+
return command;
|
|
8007
|
+
}
|
|
8008
|
+
return {
|
|
8009
|
+
...command,
|
|
8010
|
+
payload: {
|
|
8011
|
+
...payload,
|
|
8012
|
+
deadline_ms: flowQueryDeadlineMs(timeoutMs, nowMs2)
|
|
8013
|
+
}
|
|
8014
|
+
};
|
|
8015
|
+
}
|
|
8016
|
+
function withPipelineQueryDeadlines(command, timeoutMs, nowMs2) {
|
|
8017
|
+
const payload = plainRecord(command.payload);
|
|
8018
|
+
if (payload == null) return command;
|
|
8019
|
+
const rawCommands = payload.commands;
|
|
8020
|
+
if (!Array.isArray(rawCommands)) return command;
|
|
8021
|
+
let commands;
|
|
8022
|
+
let deadlineMs;
|
|
8023
|
+
for (let index = 0; index < rawCommands.length; index += 1) {
|
|
8024
|
+
const rawItem = rawCommands[index];
|
|
8025
|
+
const item = plainRecord(rawItem);
|
|
8026
|
+
if (item?.opcode !== OPCODES.flowQuery) continue;
|
|
8027
|
+
const body = plainRecord(item.body);
|
|
8028
|
+
if (body == null) continue;
|
|
8029
|
+
commands ??= rawCommands.slice();
|
|
8030
|
+
deadlineMs ??= flowQueryDeadlineMs(timeoutMs, nowMs2);
|
|
8031
|
+
commands[index] = { ...item, body: { ...body, deadline_ms: deadlineMs } };
|
|
8032
|
+
}
|
|
8033
|
+
return commands == null ? command : { ...command, payload: { ...payload, commands } };
|
|
8034
|
+
}
|
|
8035
|
+
function flowQueryDeadlineMs(timeoutMs, nowMs2) {
|
|
8036
|
+
return Math.min(
|
|
8037
|
+
Number.MAX_SAFE_INTEGER,
|
|
8038
|
+
(nowMs2 ?? Date.now()) + Math.max(1, Math.ceil(timeoutMs))
|
|
8039
|
+
);
|
|
8040
|
+
}
|
|
8041
|
+
function plainRecord(value) {
|
|
8042
|
+
if (typeof value !== "object" || value == null || Array.isArray(value) || Buffer31.isBuffer(value) || value instanceof Uint8Array) {
|
|
8043
|
+
return void 0;
|
|
8044
|
+
}
|
|
8045
|
+
return value;
|
|
8046
|
+
}
|
|
8047
|
+
|
|
7916
8048
|
// src/native-adapter.ts
|
|
7917
8049
|
var NativeAdapter = class _NativeAdapter {
|
|
7918
8050
|
socket;
|
|
@@ -8118,7 +8250,11 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
8118
8250
|
let frame;
|
|
8119
8251
|
try {
|
|
8120
8252
|
requestId = this.requestScheduler.nextRequestId();
|
|
8121
|
-
frame = encodeRequest(
|
|
8253
|
+
frame = encodeRequest(
|
|
8254
|
+
withFlowQueryDeadline(command, timeoutMs),
|
|
8255
|
+
requestId,
|
|
8256
|
+
this.capabilities.requestFrameBytes
|
|
8257
|
+
);
|
|
8122
8258
|
} catch (error) {
|
|
8123
8259
|
if (hasFlowControlCredit) this.flowControl.release(laneId);
|
|
8124
8260
|
return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
|
|
@@ -8266,7 +8402,7 @@ function executeCommandArgs(executor, args) {
|
|
|
8266
8402
|
import "buffer";
|
|
8267
8403
|
|
|
8268
8404
|
// src/command-retry-policy.ts
|
|
8269
|
-
import { Buffer as
|
|
8405
|
+
import { Buffer as Buffer33 } from "buffer";
|
|
8270
8406
|
function isCasMutation(args) {
|
|
8271
8407
|
const offset = commandName2(args[0]) === "COMMAND_EXEC" ? 1 : 0;
|
|
8272
8408
|
const name = commandName2(args[offset]);
|
|
@@ -8280,8 +8416,8 @@ function isCasMutation(args) {
|
|
|
8280
8416
|
}
|
|
8281
8417
|
function commandName2(value) {
|
|
8282
8418
|
if (typeof value === "string") return value.toUpperCase();
|
|
8283
|
-
if (
|
|
8284
|
-
return
|
|
8419
|
+
if (Buffer33.isBuffer(value) || value instanceof Uint8Array) {
|
|
8420
|
+
return Buffer33.from(value).toString("utf8").toUpperCase();
|
|
8285
8421
|
}
|
|
8286
8422
|
return void 0;
|
|
8287
8423
|
}
|
|
@@ -8496,12 +8632,12 @@ function normalizeNonNegativeInteger2(value, fallback) {
|
|
|
8496
8632
|
import "buffer";
|
|
8497
8633
|
|
|
8498
8634
|
// src/topology-routing.ts
|
|
8499
|
-
import { Buffer as
|
|
8635
|
+
import { Buffer as Buffer36 } from "buffer";
|
|
8500
8636
|
|
|
8501
8637
|
// src/flow-partition-route-cache.ts
|
|
8502
|
-
import { Buffer as
|
|
8638
|
+
import { Buffer as Buffer35 } from "buffer";
|
|
8503
8639
|
import { createHash } from "crypto";
|
|
8504
|
-
var AUTO_PREFIX =
|
|
8640
|
+
var AUTO_PREFIX = Buffer35.from("__flow_auto__:", "ascii");
|
|
8505
8641
|
var MAX_CACHEABLE_PARTITION_BYTES = 4 * 1024;
|
|
8506
8642
|
var MAX_CACHE_BYTES = 1 * 1024 * 1024;
|
|
8507
8643
|
var MAX_CACHE_ENTRIES = 1024;
|
|
@@ -8511,10 +8647,10 @@ var cacheBytes = 0;
|
|
|
8511
8647
|
var cacheHits = 0;
|
|
8512
8648
|
var cacheMisses = 0;
|
|
8513
8649
|
function flowLogicalPartitionRoutingKey(value) {
|
|
8514
|
-
if (typeof value !== "string" && !
|
|
8650
|
+
if (typeof value !== "string" && !Buffer35.isBuffer(value)) return void 0;
|
|
8515
8651
|
const autoBucket = flowAutoBucket(value);
|
|
8516
8652
|
if (autoBucket != null) return `{fa:${autoBucket}}`;
|
|
8517
|
-
const bytes2 =
|
|
8653
|
+
const bytes2 = Buffer35.isBuffer(value) ? value : Buffer35.from(value);
|
|
8518
8654
|
if (bytes2.byteLength > MAX_CACHEABLE_PARTITION_BYTES) return hashRoute(bytes2);
|
|
8519
8655
|
const cacheKey = bytes2.toString("base64");
|
|
8520
8656
|
const cached = routeCache.get(cacheKey);
|
|
@@ -8588,7 +8724,7 @@ function routingKeyFromProtocolPayload(name, command) {
|
|
|
8588
8724
|
"scope"
|
|
8589
8725
|
]) {
|
|
8590
8726
|
const value = getField(command.payload, field3);
|
|
8591
|
-
if (typeof value === "string" ||
|
|
8727
|
+
if (typeof value === "string" || Buffer36.isBuffer(value)) {
|
|
8592
8728
|
return value;
|
|
8593
8729
|
}
|
|
8594
8730
|
}
|
|
@@ -8636,7 +8772,7 @@ function flowRoutingData(name, args) {
|
|
|
8636
8772
|
if (typeof partition === "string" && partition.toUpperCase() !== "AUTO" && partition.toUpperCase() !== "MIXED") {
|
|
8637
8773
|
return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
|
|
8638
8774
|
}
|
|
8639
|
-
if (
|
|
8775
|
+
if (Buffer36.isBuffer(partition)) {
|
|
8640
8776
|
const text3 = partition.toString("utf8").toUpperCase();
|
|
8641
8777
|
if (text3 !== "AUTO" && text3 !== "MIXED") {
|
|
8642
8778
|
return flowRoutingResult(flowLogicalPartitionRoutingKey(partition));
|
|
@@ -8727,17 +8863,17 @@ function flowPartitionRoutingKeyFromCommand(command, claim) {
|
|
|
8727
8863
|
return { handled: false };
|
|
8728
8864
|
}
|
|
8729
8865
|
function isRoutingKey(value) {
|
|
8730
|
-
return typeof value === "string" ||
|
|
8866
|
+
return typeof value === "string" || Buffer36.isBuffer(value);
|
|
8731
8867
|
}
|
|
8732
8868
|
function flowAutoIdRoutingKey(value) {
|
|
8733
|
-
if (typeof value !== "string" && !
|
|
8869
|
+
if (typeof value !== "string" && !Buffer36.isBuffer(value)) {
|
|
8734
8870
|
return void 0;
|
|
8735
8871
|
}
|
|
8736
|
-
const bucket = (
|
|
8872
|
+
const bucket = (Buffer36.isBuffer(value) ? crc32(value) : crc32Utf8(value)) & 255;
|
|
8737
8873
|
return `{fa:${bucket}}`;
|
|
8738
8874
|
}
|
|
8739
8875
|
function flowClaimLogicalPartitionRoutingKey(value) {
|
|
8740
|
-
if (typeof value !== "string" && !
|
|
8876
|
+
if (typeof value !== "string" && !Buffer36.isBuffer(value)) return void 0;
|
|
8741
8877
|
const selector = commandPart(value);
|
|
8742
8878
|
if (selector === "AUTO" || selector === "ANY") return void 0;
|
|
8743
8879
|
if (selector === "GLOBAL") return "{f}";
|
|
@@ -8752,7 +8888,7 @@ function singleShardFlowClaimPartitionKey(values) {
|
|
|
8752
8888
|
return keys.some((key) => key == null) ? void 0 : singleShardKey(keys);
|
|
8753
8889
|
}
|
|
8754
8890
|
function singleShardKey(keys) {
|
|
8755
|
-
if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !
|
|
8891
|
+
if (keys.length === 0 || keys.some((key) => typeof key !== "string" && !Buffer36.isBuffer(key))) {
|
|
8756
8892
|
return void 0;
|
|
8757
8893
|
}
|
|
8758
8894
|
const usable = keys;
|
|
@@ -8785,7 +8921,7 @@ function routedKeyGroups(keys, routeKey) {
|
|
|
8785
8921
|
const groups = /* @__PURE__ */ new Map();
|
|
8786
8922
|
for (let index = 0; index < keys.length; index += 1) {
|
|
8787
8923
|
const key = keys[index];
|
|
8788
|
-
if (typeof key !== "string" && !
|
|
8924
|
+
if (typeof key !== "string" && !Buffer36.isBuffer(key)) return void 0;
|
|
8789
8925
|
const route = routeKey(key);
|
|
8790
8926
|
const groupKey = `${route.endpointKey}\0${route.laneId}`;
|
|
8791
8927
|
const group = groups.get(groupKey);
|
|
@@ -9874,7 +10010,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
|
|
|
9874
10010
|
};
|
|
9875
10011
|
|
|
9876
10012
|
// src/response-map-preservation.ts
|
|
9877
|
-
import { Buffer as
|
|
10013
|
+
import { Buffer as Buffer38 } from "buffer";
|
|
9878
10014
|
function toStringKeyMapPreservingValues(value) {
|
|
9879
10015
|
if (value == null) return void 0;
|
|
9880
10016
|
const result = {};
|
|
@@ -9884,7 +10020,7 @@ function toStringKeyMapPreservingValues(value) {
|
|
|
9884
10020
|
}
|
|
9885
10021
|
return result;
|
|
9886
10022
|
}
|
|
9887
|
-
if (typeof value === "object" && !Array.isArray(value) && !
|
|
10023
|
+
if (typeof value === "object" && !Array.isArray(value) && !Buffer38.isBuffer(value) && !(value instanceof Uint8Array)) {
|
|
9888
10024
|
for (const [key, item] of Object.entries(value)) {
|
|
9889
10025
|
setOwnValue(result, text(key), normalizeMapStructurePreservingBytes(item));
|
|
9890
10026
|
}
|
|
@@ -9893,7 +10029,7 @@ function toStringKeyMapPreservingValues(value) {
|
|
|
9893
10029
|
return void 0;
|
|
9894
10030
|
}
|
|
9895
10031
|
function normalizeMapStructurePreservingBytes(value) {
|
|
9896
|
-
if (
|
|
10032
|
+
if (Buffer38.isBuffer(value) || value instanceof Uint8Array) return value;
|
|
9897
10033
|
if (value instanceof Map) {
|
|
9898
10034
|
const result = {};
|
|
9899
10035
|
for (const [key, item] of value.entries()) {
|
|
@@ -9915,7 +10051,7 @@ function normalizeMapStructurePreservingBytes(value) {
|
|
|
9915
10051
|
}
|
|
9916
10052
|
|
|
9917
10053
|
// src/native-kv-responses.ts
|
|
9918
|
-
import { Buffer as
|
|
10054
|
+
import { Buffer as Buffer39 } from "buffer";
|
|
9919
10055
|
function rateLimitResultFromResp(value) {
|
|
9920
10056
|
if (!Array.isArray(value) || value.length !== 4) {
|
|
9921
10057
|
throw new TypeError("RATELIMIT.ADD returned an unexpected response");
|
|
@@ -9972,13 +10108,13 @@ function fetchOrComputeResultFromResp(value, codec) {
|
|
|
9972
10108
|
}
|
|
9973
10109
|
function decodePayload(codec, value) {
|
|
9974
10110
|
if (value == null) return null;
|
|
9975
|
-
if (
|
|
9976
|
-
if (value instanceof Uint8Array) return codec.decode(
|
|
9977
|
-
if (typeof value === "string") return codec.decode(
|
|
10111
|
+
if (Buffer39.isBuffer(value)) return codec.decode(value);
|
|
10112
|
+
if (value instanceof Uint8Array) return codec.decode(Buffer39.from(value));
|
|
10113
|
+
if (typeof value === "string") return codec.decode(Buffer39.from(value));
|
|
9978
10114
|
return normalizeRefMeta(value);
|
|
9979
10115
|
}
|
|
9980
10116
|
function requiredResponseString(value, context) {
|
|
9981
|
-
if (typeof value !== "string" && !
|
|
10117
|
+
if (typeof value !== "string" && !Buffer39.isBuffer(value) && !(value instanceof Uint8Array)) {
|
|
9982
10118
|
throw new TypeError(`${context} returned an unexpected response`);
|
|
9983
10119
|
}
|
|
9984
10120
|
const result = text(value);
|
|
@@ -9995,7 +10131,7 @@ function requiredNonNegativeInteger(value, context) {
|
|
|
9995
10131
|
return result;
|
|
9996
10132
|
}
|
|
9997
10133
|
function responseBytes(value, context) {
|
|
9998
|
-
if (typeof value !== "string" && !
|
|
10134
|
+
if (typeof value !== "string" && !Buffer39.isBuffer(value) && !(value instanceof Uint8Array)) {
|
|
9999
10135
|
throw new TypeError(`${context} returned an unexpected response`);
|
|
10000
10136
|
}
|
|
10001
10137
|
return bytes(value);
|
|
@@ -10678,7 +10814,7 @@ function groupAutoPartitionItems(items) {
|
|
|
10678
10814
|
}
|
|
10679
10815
|
|
|
10680
10816
|
// src/client-values.ts
|
|
10681
|
-
import { Buffer as
|
|
10817
|
+
import { Buffer as Buffer40 } from "buffer";
|
|
10682
10818
|
async function valueMGetEntries(client, refs, options = {}) {
|
|
10683
10819
|
const refCount = refs.length;
|
|
10684
10820
|
if (refCount === 0) {
|
|
@@ -10713,12 +10849,12 @@ async function valueMGetEntries(client, refs, options = {}) {
|
|
|
10713
10849
|
const item = response[index];
|
|
10714
10850
|
if (item == null) {
|
|
10715
10851
|
entries[index] = { found: false };
|
|
10716
|
-
} else if (
|
|
10852
|
+
} else if (Buffer40.isBuffer(item)) {
|
|
10717
10853
|
entries[index] = { found: true, value: client.codec.decode(item) };
|
|
10718
10854
|
} else if (item instanceof Uint8Array) {
|
|
10719
|
-
entries[index] = { found: true, value: client.codec.decode(
|
|
10855
|
+
entries[index] = { found: true, value: client.codec.decode(Buffer40.from(item)) };
|
|
10720
10856
|
} else if (typeof item === "string") {
|
|
10721
|
-
entries[index] = { found: true, value: client.codec.decode(
|
|
10857
|
+
entries[index] = { found: true, value: client.codec.decode(Buffer40.from(item)) };
|
|
10722
10858
|
} else {
|
|
10723
10859
|
entries[index] = { found: true, value: item };
|
|
10724
10860
|
}
|
|
@@ -10730,7 +10866,7 @@ async function valueMGetEntries(client, refs, options = {}) {
|
|
|
10730
10866
|
import "buffer";
|
|
10731
10867
|
|
|
10732
10868
|
// src/auto-batch-ordering.ts
|
|
10733
|
-
import { Buffer as
|
|
10869
|
+
import { Buffer as Buffer41 } from "buffer";
|
|
10734
10870
|
function autoBatchOrderingPlan(batch) {
|
|
10735
10871
|
const accesses = /* @__PURE__ */ new Map();
|
|
10736
10872
|
const fallbackDependencies = [];
|
|
@@ -10845,13 +10981,13 @@ function flowManyAutoBatchIds(command, name) {
|
|
|
10845
10981
|
return fixedFlowItemIds(
|
|
10846
10982
|
command,
|
|
10847
10983
|
mixed ? 4 : 3,
|
|
10848
|
-
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) &&
|
|
10984
|
+
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && isFencingToken(command[itemIndex + (mixed ? 2 : 1)]) && Buffer41.isBuffer(command[itemIndex + (mixed ? 3 : 2)])
|
|
10849
10985
|
);
|
|
10850
10986
|
}
|
|
10851
10987
|
return fixedFlowItemIds(
|
|
10852
10988
|
command,
|
|
10853
10989
|
mixed ? 4 : 3,
|
|
10854
|
-
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) &&
|
|
10990
|
+
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer41.isBuffer(command[itemIndex + (mixed ? 2 : 1)]) && isFencingToken(command[itemIndex + (mixed ? 3 : 2)])
|
|
10855
10991
|
);
|
|
10856
10992
|
}
|
|
10857
10993
|
function createManyAutoBatchIds(command) {
|
|
@@ -10864,7 +11000,7 @@ function createManyAutoBatchIds(command) {
|
|
|
10864
11000
|
const ids = fixedFlowItemIds(
|
|
10865
11001
|
command,
|
|
10866
11002
|
width,
|
|
10867
|
-
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) &&
|
|
11003
|
+
(itemIndex) => isAutoBatchResourceValue(command[itemIndex]) && (!mixed || isAutoBatchResourceValue(command[itemIndex + 1])) && Buffer41.isBuffer(command[itemIndex + width - 1]),
|
|
10868
11004
|
markerIndex
|
|
10869
11005
|
);
|
|
10870
11006
|
if (ids != null) return ids;
|
|
@@ -10882,7 +11018,7 @@ function extendedCreateManyAutoBatchIds(command, markerIndex) {
|
|
|
10882
11018
|
let cursor = markerIndex + 2;
|
|
10883
11019
|
for (let itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
|
|
10884
11020
|
const id = command[cursor];
|
|
10885
|
-
if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !
|
|
11021
|
+
if (!isAutoBatchResourceValue(id) || !isAutoBatchResourceValue(command[cursor + 1]) || !Buffer41.isBuffer(command[cursor + 2])) return void 0;
|
|
10886
11022
|
ids.push(id);
|
|
10887
11023
|
cursor += 3;
|
|
10888
11024
|
const afterValues = skipExtendedNamedItems(command, cursor, true);
|
|
@@ -10901,7 +11037,7 @@ function skipExtendedNamedItems(command, countIndex, encodedValues) {
|
|
|
10901
11037
|
for (let index = 0; index < count; index += 1) {
|
|
10902
11038
|
const name = command[firstItem + index * 2];
|
|
10903
11039
|
const value = command[firstItem + index * 2 + 1];
|
|
10904
|
-
if (!isAutoBatchResourceValue(name) || (encodedValues ? !
|
|
11040
|
+
if (!isAutoBatchResourceValue(name) || (encodedValues ? !Buffer41.isBuffer(value) : !isAutoBatchResourceValue(value))) return void 0;
|
|
10905
11041
|
}
|
|
10906
11042
|
return firstItem + count * 2;
|
|
10907
11043
|
}
|
|
@@ -10916,7 +11052,7 @@ function runStepsManyAutoBatchIds(command) {
|
|
|
10916
11052
|
ids.push(item);
|
|
10917
11053
|
continue;
|
|
10918
11054
|
}
|
|
10919
|
-
if (typeof item !== "object" || item == null || Array.isArray(item) ||
|
|
11055
|
+
if (typeof item !== "object" || item == null || Array.isArray(item) || Buffer41.isBuffer(item)) return void 0;
|
|
10920
11056
|
const id = item.id;
|
|
10921
11057
|
if (!isAutoBatchResourceValue(id)) return void 0;
|
|
10922
11058
|
ids.push(id);
|
|
@@ -10957,7 +11093,7 @@ function flowValuePutOwner(command, start) {
|
|
|
10957
11093
|
}
|
|
10958
11094
|
var flowValuePutOptionTokens = /* @__PURE__ */ new Set(["NAME", "NOW", "OVERRIDE", "OWNER_FLOW_ID", "PARTITION", "TTL", "TTL_MS"]);
|
|
10959
11095
|
function isAutoBatchResourceValue(value) {
|
|
10960
|
-
return typeof value === "string" ||
|
|
11096
|
+
return typeof value === "string" || Buffer41.isBuffer(value);
|
|
10961
11097
|
}
|
|
10962
11098
|
function isFencingToken(value) {
|
|
10963
11099
|
return typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint";
|
|
@@ -10966,7 +11102,7 @@ function nonNegativeItemCount(value) {
|
|
|
10966
11102
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
10967
11103
|
}
|
|
10968
11104
|
function autoBatchResourceKey(namespace, value) {
|
|
10969
|
-
return `${namespace}:${
|
|
11105
|
+
return `${namespace}:${Buffer41.from(value).toString("base64")}`;
|
|
10970
11106
|
}
|
|
10971
11107
|
function autoBatchCommandName(command) {
|
|
10972
11108
|
return commandView(command).name ?? null;
|
|
@@ -11378,10 +11514,10 @@ function claimFailure(error) {
|
|
|
11378
11514
|
import "buffer";
|
|
11379
11515
|
|
|
11380
11516
|
// src/flow-query-builder.ts
|
|
11381
|
-
import { Buffer as
|
|
11517
|
+
import { Buffer as Buffer44 } from "buffer";
|
|
11382
11518
|
|
|
11383
11519
|
// src/flow-query-metadata.ts
|
|
11384
|
-
import { Buffer as
|
|
11520
|
+
import { Buffer as Buffer43 } from "buffer";
|
|
11385
11521
|
var MAX_FLOW_QUERY_METADATA_KEY_BYTES = 64;
|
|
11386
11522
|
function normalizeStateMeta(value, state) {
|
|
11387
11523
|
if (value == null) return {};
|
|
@@ -11406,7 +11542,7 @@ function normalizeStateMeta(value, state) {
|
|
|
11406
11542
|
function metadataEntries(value, context) {
|
|
11407
11543
|
const entries = objectEntries(value ?? {}, context).map(([rawName, item]) => {
|
|
11408
11544
|
const name = rawName.trim();
|
|
11409
|
-
const size =
|
|
11545
|
+
const size = Buffer43.byteLength(name, "utf8");
|
|
11410
11546
|
if (size === 0 || size > MAX_FLOW_QUERY_METADATA_KEY_BYTES || name.startsWith("__")) {
|
|
11411
11547
|
throw new TypeError(`${context} key is invalid or reserved`);
|
|
11412
11548
|
}
|
|
@@ -11430,7 +11566,7 @@ function objectEntries(value, context) {
|
|
|
11430
11566
|
return keys.map((key) => [key, value[key]]);
|
|
11431
11567
|
}
|
|
11432
11568
|
function isPlainRecord(value) {
|
|
11433
|
-
if (typeof value !== "object" || value == null || Array.isArray(value) ||
|
|
11569
|
+
if (typeof value !== "object" || value == null || Array.isArray(value) || Buffer43.isBuffer(value)) {
|
|
11434
11570
|
return false;
|
|
11435
11571
|
}
|
|
11436
11572
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -11500,7 +11636,7 @@ var FlowCollectionQuery = class {
|
|
|
11500
11636
|
const states = /* @__PURE__ */ new Set();
|
|
11501
11637
|
for (const [rawState, metadata] of objectEntries(values, "stateMeta")) {
|
|
11502
11638
|
const state = requiredText(rawState, "stateMeta state").trim();
|
|
11503
|
-
const stateBytes =
|
|
11639
|
+
const stateBytes = Buffer44.byteLength(state, "utf8");
|
|
11504
11640
|
if (stateBytes === 0 || stateBytes > MAX_FLOW_QUERY_STATE_BYTES) {
|
|
11505
11641
|
throw new TypeError(
|
|
11506
11642
|
`stateMeta state names must be 1..${MAX_FLOW_QUERY_STATE_BYTES} bytes`
|
|
@@ -11679,7 +11815,7 @@ function requiredPartition(value) {
|
|
|
11679
11815
|
"FLOW.QUERY convenience methods require a partition key"
|
|
11680
11816
|
);
|
|
11681
11817
|
}
|
|
11682
|
-
const size =
|
|
11818
|
+
const size = Buffer44.byteLength(value, "utf8");
|
|
11683
11819
|
if (size === 0 || size > MAX_FLOW_QUERY_PARTITION_BYTES) {
|
|
11684
11820
|
throw new TypeError(
|
|
11685
11821
|
`FLOW.QUERY partition key must be 1..${MAX_FLOW_QUERY_PARTITION_BYTES} bytes`
|
|
@@ -11727,20 +11863,23 @@ function requiredText(value, context) {
|
|
|
11727
11863
|
return value;
|
|
11728
11864
|
}
|
|
11729
11865
|
function queryParameter(value, context) {
|
|
11730
|
-
if (typeof value === "string" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "number" ||
|
|
11866
|
+
if (typeof value === "string" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "number" || Buffer44.isBuffer(value)) {
|
|
11731
11867
|
return value;
|
|
11732
11868
|
}
|
|
11733
11869
|
throw new TypeError(`${context} must be a scalar FLOW.QUERY parameter`);
|
|
11734
11870
|
}
|
|
11735
11871
|
|
|
11736
11872
|
// src/flow-query-response.ts
|
|
11737
|
-
import { Buffer as
|
|
11873
|
+
import { Buffer as Buffer48 } from "buffer";
|
|
11874
|
+
|
|
11875
|
+
// src/flow-query-diagnostic-response.ts
|
|
11876
|
+
import { Buffer as Buffer46 } from "buffer";
|
|
11738
11877
|
|
|
11739
11878
|
// src/flow-query-response-validation.ts
|
|
11740
|
-
import { Buffer as
|
|
11879
|
+
import { Buffer as Buffer45 } from "buffer";
|
|
11741
11880
|
function requiredMap2(value, context) {
|
|
11742
11881
|
if (value instanceof Map) return value;
|
|
11743
|
-
if (typeof value !== "object" || value == null || Array.isArray(value) ||
|
|
11882
|
+
if (typeof value !== "object" || value == null || Array.isArray(value) || Buffer45.isBuffer(value) || value instanceof Uint8Array) {
|
|
11744
11883
|
throw decodeError(`${context} must be a map`, value);
|
|
11745
11884
|
}
|
|
11746
11885
|
return value;
|
|
@@ -11797,7 +11936,7 @@ function normalizeMetadataValue(value, context, budget, ancestors, depth) {
|
|
|
11797
11936
|
if (!value.isWellFormed()) throw decodeError(`${context} contains invalid text`, value);
|
|
11798
11937
|
return value;
|
|
11799
11938
|
}
|
|
11800
|
-
if (
|
|
11939
|
+
if (Buffer45.isBuffer(value) || value instanceof Uint8Array) {
|
|
11801
11940
|
const decoded = strictText2(value);
|
|
11802
11941
|
if (decoded == null) throw decodeError(`${context} contains invalid UTF-8`, value);
|
|
11803
11942
|
return decoded;
|
|
@@ -11850,36 +11989,36 @@ function isPlainObject6(value) {
|
|
|
11850
11989
|
const prototype = Object.getPrototypeOf(value);
|
|
11851
11990
|
return prototype === Object.prototype || prototype === null;
|
|
11852
11991
|
}
|
|
11853
|
-
function requireContract(
|
|
11854
|
-
const actual = requiredText2(
|
|
11992
|
+
function requireContract(mapping2, name, expected, context) {
|
|
11993
|
+
const actual = requiredText2(mapping2, name, context);
|
|
11855
11994
|
if (actual !== expected) {
|
|
11856
11995
|
throw decodeError(
|
|
11857
11996
|
`${context} has unsupported contract ${JSON.stringify(actual)}`,
|
|
11858
|
-
|
|
11997
|
+
mapping2
|
|
11859
11998
|
);
|
|
11860
11999
|
}
|
|
11861
12000
|
}
|
|
11862
|
-
function requiredText2(
|
|
11863
|
-
const value = strictText2(field(
|
|
12001
|
+
function requiredText2(mapping2, name, context) {
|
|
12002
|
+
const value = strictText2(field(mapping2, name));
|
|
11864
12003
|
if (value == null || value.length === 0) {
|
|
11865
|
-
throw decodeError(`${context} ${name} must be non-empty text`,
|
|
12004
|
+
throw decodeError(`${context} ${name} must be non-empty text`, mapping2);
|
|
11866
12005
|
}
|
|
11867
12006
|
return value;
|
|
11868
12007
|
}
|
|
11869
|
-
function optionalText2(
|
|
11870
|
-
const raw = field(
|
|
12008
|
+
function optionalText2(mapping2, name, context) {
|
|
12009
|
+
const raw = field(mapping2, name);
|
|
11871
12010
|
if (raw == null) return void 0;
|
|
11872
12011
|
const value = strictText2(raw);
|
|
11873
12012
|
if (value == null)
|
|
11874
|
-
throw decodeError(`${context} ${name} must be text`,
|
|
12013
|
+
throw decodeError(`${context} ${name} must be text`, mapping2);
|
|
11875
12014
|
return value;
|
|
11876
12015
|
}
|
|
11877
|
-
function requiredBoundedText2(
|
|
11878
|
-
const value = requiredText2(
|
|
11879
|
-
if (
|
|
12016
|
+
function requiredBoundedText2(mapping2, name, context, maximumBytes) {
|
|
12017
|
+
const value = requiredText2(mapping2, name, context);
|
|
12018
|
+
if (Buffer45.byteLength(value, "utf8") > maximumBytes) {
|
|
11880
12019
|
throw decodeError(
|
|
11881
12020
|
`${context} ${name} exceeds ${maximumBytes} bytes`,
|
|
11882
|
-
|
|
12021
|
+
mapping2
|
|
11883
12022
|
);
|
|
11884
12023
|
}
|
|
11885
12024
|
return value;
|
|
@@ -11889,15 +12028,15 @@ function boundedText(value, context, maximumBytes) {
|
|
|
11889
12028
|
if (decoded == null || decoded.length === 0) {
|
|
11890
12029
|
throw decodeError(`${context} must be non-empty text`, value);
|
|
11891
12030
|
}
|
|
11892
|
-
if (
|
|
12031
|
+
if (Buffer45.byteLength(decoded, "utf8") > maximumBytes) {
|
|
11893
12032
|
throw decodeError(`${context} exceeds ${maximumBytes} bytes`, value);
|
|
11894
12033
|
}
|
|
11895
12034
|
return decoded;
|
|
11896
12035
|
}
|
|
11897
|
-
function requiredBoolean(
|
|
11898
|
-
const value = field(
|
|
12036
|
+
function requiredBoolean(mapping2, name, context) {
|
|
12037
|
+
const value = field(mapping2, name);
|
|
11899
12038
|
if (typeof value !== "boolean") {
|
|
11900
|
-
throw decodeError(`${context} ${name} must be boolean`,
|
|
12039
|
+
throw decodeError(`${context} ${name} must be boolean`, mapping2);
|
|
11901
12040
|
}
|
|
11902
12041
|
return value;
|
|
11903
12042
|
}
|
|
@@ -11940,13 +12079,13 @@ function positiveBoundedInteger(value, maximum, context) {
|
|
|
11940
12079
|
if (parsed === 0) throw decodeError(`${context} must be positive`, value);
|
|
11941
12080
|
return parsed;
|
|
11942
12081
|
}
|
|
11943
|
-
function hasKey(
|
|
11944
|
-
if (!(
|
|
11945
|
-
if (
|
|
11946
|
-
const binaryName =
|
|
11947
|
-
for (const key of
|
|
11948
|
-
if (
|
|
11949
|
-
if (key instanceof Uint8Array &&
|
|
12082
|
+
function hasKey(mapping2, name) {
|
|
12083
|
+
if (!(mapping2 instanceof Map)) return Object.hasOwn(mapping2, name);
|
|
12084
|
+
if (mapping2.has(name)) return true;
|
|
12085
|
+
const binaryName = Buffer45.from(name);
|
|
12086
|
+
for (const key of mapping2.keys()) {
|
|
12087
|
+
if (Buffer45.isBuffer(key) && key.equals(binaryName)) return true;
|
|
12088
|
+
if (key instanceof Uint8Array && Buffer45.from(key).equals(binaryName))
|
|
11950
12089
|
return true;
|
|
11951
12090
|
}
|
|
11952
12091
|
return false;
|
|
@@ -11957,7 +12096,7 @@ function decodeError(message, raw) {
|
|
|
11957
12096
|
function strictText2(value) {
|
|
11958
12097
|
if (typeof value === "string")
|
|
11959
12098
|
return value.isWellFormed() ? value : void 0;
|
|
11960
|
-
if (
|
|
12099
|
+
if (Buffer45.isBuffer(value) || value instanceof Uint8Array) {
|
|
11961
12100
|
try {
|
|
11962
12101
|
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
11963
12102
|
} catch {
|
|
@@ -11990,341 +12129,1314 @@ var FlowQueryError = class extends FerricStoreError {
|
|
|
11990
12129
|
}
|
|
11991
12130
|
};
|
|
11992
12131
|
|
|
11993
|
-
// src/flow-query-response.ts
|
|
11994
|
-
var
|
|
11995
|
-
var
|
|
11996
|
-
var
|
|
11997
|
-
var
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12132
|
+
// src/flow-query-diagnostic-response.ts
|
|
12133
|
+
var MIN_SIGNED_64 = -(1n << 63n);
|
|
12134
|
+
var MAX_SIGNED_642 = (1n << 63n) - 1n;
|
|
12135
|
+
var DIAGNOSTIC_TEXT_BYTES = 1024;
|
|
12136
|
+
var DIAGNOSTIC_CONTEXT_ENTRIES = 16;
|
|
12137
|
+
var DIAGNOSTIC_CONTEXT_LIST_ITEMS = 32;
|
|
12138
|
+
var DIAGNOSTIC_CONTEXT_KEY_BYTES = 128;
|
|
12139
|
+
var DIAGNOSTIC_CONTEXT_DEPTH = 6;
|
|
12140
|
+
var DIAGNOSTIC_CONTEXT_NODES = 512;
|
|
12141
|
+
function tryDecodeFlowQueryError(value, cause) {
|
|
12142
|
+
try {
|
|
12143
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic");
|
|
12144
|
+
const context = decodeDiagnosticContext(field(mapping2, "context"));
|
|
12145
|
+
const position = decodePosition(field(mapping2, "position"));
|
|
12146
|
+
return new FlowQueryError({
|
|
12147
|
+
code: requiredBoundedText2(
|
|
12148
|
+
mapping2,
|
|
12149
|
+
"code",
|
|
12150
|
+
"FLOW.QUERY diagnostic",
|
|
12151
|
+
DIAGNOSTIC_TEXT_BYTES
|
|
12152
|
+
),
|
|
12153
|
+
message: requiredBoundedText2(
|
|
12154
|
+
mapping2,
|
|
12155
|
+
"message",
|
|
12156
|
+
"FLOW.QUERY diagnostic",
|
|
12157
|
+
DIAGNOSTIC_TEXT_BYTES
|
|
12158
|
+
),
|
|
12159
|
+
detail: optionalDiagnosticText(mapping2, "detail"),
|
|
12160
|
+
hint: optionalDiagnosticText(mapping2, "hint"),
|
|
12161
|
+
retryable: requiredBoolean(mapping2, "retryable", "FLOW.QUERY diagnostic"),
|
|
12162
|
+
safeToRetry: requiredBoolean(mapping2, "safe_to_retry", "FLOW.QUERY diagnostic"),
|
|
12163
|
+
retryAfterMs: nonNegativeInteger(
|
|
12164
|
+
field(mapping2, "retry_after_ms"),
|
|
12165
|
+
"FLOW.QUERY diagnostic retry_after_ms"
|
|
12166
|
+
),
|
|
12167
|
+
position,
|
|
12168
|
+
context,
|
|
12169
|
+
raw: value,
|
|
12170
|
+
cause
|
|
12023
12171
|
});
|
|
12172
|
+
} catch (error) {
|
|
12173
|
+
if (error instanceof FerricStoreError) return void 0;
|
|
12174
|
+
throw error;
|
|
12024
12175
|
}
|
|
12025
|
-
return Object.freeze({
|
|
12026
|
-
kind: "count",
|
|
12027
|
-
version: FLOW_QUERY_RESULT_CONTRACT,
|
|
12028
|
-
count: result.count,
|
|
12029
|
-
quality: result.quality,
|
|
12030
|
-
usage: result.usage,
|
|
12031
|
-
raw: result.raw
|
|
12032
|
-
});
|
|
12033
12176
|
}
|
|
12034
|
-
function
|
|
12035
|
-
const
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
raw: result.raw
|
|
12042
|
-
});
|
|
12177
|
+
function optionalDiagnosticText(mapping2, name) {
|
|
12178
|
+
const value = optionalText2(mapping2, name, "FLOW.QUERY diagnostic");
|
|
12179
|
+
if (value != null && Buffer46.byteLength(value, "utf8") > DIAGNOSTIC_TEXT_BYTES) {
|
|
12180
|
+
throw decodeError(
|
|
12181
|
+
`FLOW.QUERY diagnostic ${name} exceeds ${DIAGNOSTIC_TEXT_BYTES} bytes`,
|
|
12182
|
+
mapping2
|
|
12183
|
+
);
|
|
12043
12184
|
}
|
|
12044
|
-
return
|
|
12185
|
+
return value;
|
|
12045
12186
|
}
|
|
12046
|
-
function
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
const
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12187
|
+
function decodeDiagnosticContext(value) {
|
|
12188
|
+
if (value == null) return void 0;
|
|
12189
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic context");
|
|
12190
|
+
const size = mapping2 instanceof Map ? mapping2.size : Object.keys(mapping2).length;
|
|
12191
|
+
if (size > DIAGNOSTIC_CONTEXT_ENTRIES) {
|
|
12192
|
+
throw decodeError("FLOW.QUERY diagnostic context contains too many entries", value);
|
|
12193
|
+
}
|
|
12194
|
+
validateDiagnosticContextValue(
|
|
12195
|
+
mapping2,
|
|
12196
|
+
DIAGNOSTIC_CONTEXT_DEPTH,
|
|
12197
|
+
{ remaining: DIAGNOSTIC_CONTEXT_NODES }
|
|
12198
|
+
);
|
|
12199
|
+
return freezeMetadataMap(mapping2, "FLOW.QUERY diagnostic context");
|
|
12200
|
+
}
|
|
12201
|
+
function validateDiagnosticContextValue(value, depth, budget) {
|
|
12202
|
+
consumeDiagnosticContextNode(value, budget);
|
|
12203
|
+
if (value == null || typeof value === "boolean") return;
|
|
12204
|
+
if (typeof value === "number") {
|
|
12205
|
+
if (Number.isSafeInteger(value)) return;
|
|
12206
|
+
throw decodeError("FLOW.QUERY diagnostic context contains an invalid integer", value);
|
|
12055
12207
|
}
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12208
|
+
if (typeof value === "bigint") {
|
|
12209
|
+
if (value >= MIN_SIGNED_64 && value <= MAX_SIGNED_642) return;
|
|
12210
|
+
throw decodeError("FLOW.QUERY diagnostic context contains an invalid integer", value);
|
|
12211
|
+
}
|
|
12212
|
+
const text3 = diagnosticContextText(value);
|
|
12213
|
+
if (text3 != null) {
|
|
12214
|
+
if (Buffer46.byteLength(text3, "utf8") <= DIAGNOSTIC_TEXT_BYTES) return;
|
|
12215
|
+
throw decodeError("FLOW.QUERY diagnostic context contains oversized text", value);
|
|
12216
|
+
}
|
|
12217
|
+
if (depth <= 0) {
|
|
12218
|
+
throw decodeError("FLOW.QUERY diagnostic context exceeds its maximum depth", value);
|
|
12219
|
+
}
|
|
12220
|
+
if (Array.isArray(value)) {
|
|
12221
|
+
if (value.length > DIAGNOSTIC_CONTEXT_LIST_ITEMS) {
|
|
12222
|
+
throw decodeError("FLOW.QUERY diagnostic context list is too large", value);
|
|
12061
12223
|
}
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
throw decodeError("FLOW.QUERY records must be a dense array", value);
|
|
12224
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
12225
|
+
if (!Object.hasOwn(value, index)) {
|
|
12226
|
+
throw decodeError("FLOW.QUERY diagnostic context list must be dense", value);
|
|
12066
12227
|
}
|
|
12067
|
-
|
|
12068
|
-
requiredMap2(rawRecords[index], `FLOW.QUERY record ${index}`),
|
|
12069
|
-
index
|
|
12070
|
-
);
|
|
12228
|
+
validateDiagnosticContextValue(value[index], depth - 1, budget);
|
|
12071
12229
|
}
|
|
12072
|
-
|
|
12073
|
-
|
|
12230
|
+
return;
|
|
12231
|
+
}
|
|
12232
|
+
if (value instanceof Map || isDiagnosticContextRecord(value)) {
|
|
12233
|
+
const entries = value instanceof Map ? value.entries() : Object.entries(value);
|
|
12234
|
+
const size = value instanceof Map ? value.size : Object.keys(value).length;
|
|
12235
|
+
if (size > DIAGNOSTIC_CONTEXT_ENTRIES) {
|
|
12236
|
+
throw decodeError("FLOW.QUERY diagnostic context map is too large", value);
|
|
12074
12237
|
}
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12238
|
+
for (const [rawKey, item] of entries) {
|
|
12239
|
+
const key = diagnosticContextText(rawKey);
|
|
12240
|
+
if (key == null || key.length === 0 || Buffer46.byteLength(key, "utf8") > DIAGNOSTIC_CONTEXT_KEY_BYTES) {
|
|
12241
|
+
throw decodeError("FLOW.QUERY diagnostic context contains an invalid key", value);
|
|
12242
|
+
}
|
|
12243
|
+
validateDiagnosticContextValue(item, depth - 1, budget);
|
|
12244
|
+
}
|
|
12245
|
+
return;
|
|
12083
12246
|
}
|
|
12084
|
-
|
|
12085
|
-
|
|
12247
|
+
throw decodeError("FLOW.QUERY diagnostic context contains an unsupported value", value);
|
|
12248
|
+
}
|
|
12249
|
+
function consumeDiagnosticContextNode(value, budget) {
|
|
12250
|
+
if (budget.remaining <= 0) {
|
|
12251
|
+
throw decodeError("FLOW.QUERY diagnostic context exceeds its node limit", value);
|
|
12086
12252
|
}
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12253
|
+
budget.remaining -= 1;
|
|
12254
|
+
}
|
|
12255
|
+
function diagnosticContextText(value) {
|
|
12256
|
+
if (typeof value === "string") return value.isWellFormed() ? value : void 0;
|
|
12257
|
+
if (!Buffer46.isBuffer(value) && !(value instanceof Uint8Array)) return void 0;
|
|
12258
|
+
try {
|
|
12259
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
12260
|
+
} catch {
|
|
12261
|
+
return void 0;
|
|
12090
12262
|
}
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12263
|
+
}
|
|
12264
|
+
function isDiagnosticContextRecord(value) {
|
|
12265
|
+
if (typeof value !== "object" || value == null) return false;
|
|
12266
|
+
const prototype = Object.getPrototypeOf(value);
|
|
12267
|
+
return prototype === Object.prototype || prototype === null;
|
|
12268
|
+
}
|
|
12269
|
+
function decodePosition(value) {
|
|
12270
|
+
if (value == null) return void 0;
|
|
12271
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY diagnostic position");
|
|
12272
|
+
return Object.freeze({
|
|
12273
|
+
byte: positiveInteger(field(mapping2, "byte"), "FLOW.QUERY diagnostic position byte"),
|
|
12274
|
+
line: positiveInteger(field(mapping2, "line"), "FLOW.QUERY diagnostic position line"),
|
|
12275
|
+
column: positiveInteger(
|
|
12276
|
+
field(mapping2, "column"),
|
|
12277
|
+
"FLOW.QUERY diagnostic position column"
|
|
12278
|
+
)
|
|
12279
|
+
});
|
|
12280
|
+
}
|
|
12281
|
+
|
|
12282
|
+
// src/flow-query-index-contract.ts
|
|
12283
|
+
import { Buffer as Buffer47 } from "buffer";
|
|
12284
|
+
var FLOW_QUERY_BUILD_PHASES = ["pending", "snapshot", "backfill", "done"];
|
|
12285
|
+
var FLOW_QUERY_VALIDATION_PHASES = [
|
|
12286
|
+
"pending",
|
|
12287
|
+
"source",
|
|
12288
|
+
"index",
|
|
12289
|
+
"counter",
|
|
12290
|
+
"cleanup",
|
|
12291
|
+
"done"
|
|
12292
|
+
];
|
|
12293
|
+
var FLOW_QUERY_RETIREMENT_PHASES = [
|
|
12294
|
+
"pending",
|
|
12295
|
+
"fence",
|
|
12296
|
+
"index",
|
|
12297
|
+
"counter",
|
|
12298
|
+
"reverse",
|
|
12299
|
+
"cleanup",
|
|
12300
|
+
"done"
|
|
12301
|
+
];
|
|
12302
|
+
var IDENTIFIER = /^[A-Za-z0-9_.:-]+$/u;
|
|
12303
|
+
var UNQUOTED_METADATA = /^[A-Za-z0-9_-]+$/u;
|
|
12304
|
+
var ATTRIBUTE_SELECTOR = /^attribute\['((?:[^']|'')*)'\]$/u;
|
|
12305
|
+
var STATE_META_SELECTOR = /^state_meta\['((?:[^']|'')*)'\]\['((?:[^']|'')*)'\]$/u;
|
|
12306
|
+
var INTEGER_FIELDS = /* @__PURE__ */ new Set([
|
|
12307
|
+
"version",
|
|
12308
|
+
"priority",
|
|
12309
|
+
"created_at_ms",
|
|
12310
|
+
"updated_at_ms",
|
|
12311
|
+
"next_run_at_ms",
|
|
12312
|
+
"lease_deadline_ms",
|
|
12313
|
+
"attempts",
|
|
12314
|
+
"max_active_ms"
|
|
12315
|
+
]);
|
|
12316
|
+
var BUILTIN_FIELDS = /* @__PURE__ */ new Set([
|
|
12317
|
+
...INTEGER_FIELDS,
|
|
12318
|
+
"partition_key",
|
|
12319
|
+
"run_id",
|
|
12320
|
+
"event_id",
|
|
12321
|
+
"type",
|
|
12322
|
+
"state",
|
|
12323
|
+
"run_state",
|
|
12324
|
+
"parent_flow_id",
|
|
12325
|
+
"root_flow_id",
|
|
12326
|
+
"correlation_id"
|
|
12327
|
+
]);
|
|
12328
|
+
var RETIREMENT_PROGRESS_FIELDS = [
|
|
12329
|
+
"phase_counts",
|
|
12330
|
+
"current_phases",
|
|
12331
|
+
"completed_shards",
|
|
12332
|
+
"total_shards",
|
|
12333
|
+
"deleted_entries",
|
|
12334
|
+
"deleted_bytes",
|
|
12335
|
+
"rewritten_reverse_rows"
|
|
12336
|
+
];
|
|
12337
|
+
function validateFlowQueryIndexContract(status, expectedId) {
|
|
12338
|
+
let previousId;
|
|
12339
|
+
let previousVersion;
|
|
12340
|
+
for (const index of status.indexes) {
|
|
12341
|
+
const version = BigInt(index.version);
|
|
12342
|
+
if (previousId != null && (index.id < previousId || index.id === previousId && previousVersion != null && version <= previousVersion)) {
|
|
12343
|
+
fail("indexes must be uniquely sorted by id and version", status.raw);
|
|
12344
|
+
}
|
|
12345
|
+
if (expectedId != null && index.id !== expectedId) {
|
|
12346
|
+
fail("filtered indexes do not match the requested id", status.raw);
|
|
12347
|
+
}
|
|
12348
|
+
previousId = index.id;
|
|
12349
|
+
previousVersion = version;
|
|
12350
|
+
validateIndex(index, status);
|
|
12351
|
+
}
|
|
12352
|
+
if (expectedId != null && status.indexes.length === 0) {
|
|
12353
|
+
fail("filtered indexes do not match the requested id", status.raw);
|
|
12354
|
+
}
|
|
12355
|
+
const hasUnavailableStatistics = status.indexes.some(
|
|
12356
|
+
(index) => index.statistics.status === "unavailable"
|
|
12095
12357
|
);
|
|
12096
|
-
if (
|
|
12097
|
-
|
|
12358
|
+
if (status.services.statisticsStore === "unavailable") {
|
|
12359
|
+
if (status.indexes.some((index) => index.statistics.status !== "unavailable")) {
|
|
12360
|
+
fail("statistics must be unavailable when the service is unavailable", status.raw);
|
|
12361
|
+
}
|
|
12362
|
+
} else if (hasUnavailableStatistics) {
|
|
12363
|
+
fail("statistics cannot be unavailable while the service is ready", status.raw);
|
|
12098
12364
|
}
|
|
12099
|
-
return {
|
|
12100
|
-
kind: "count",
|
|
12101
|
-
count,
|
|
12102
|
-
quality,
|
|
12103
|
-
usage,
|
|
12104
|
-
raw
|
|
12105
|
-
};
|
|
12106
12365
|
}
|
|
12107
|
-
function
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
const queryFingerprint = requiredText2(mapping, "query_fingerprint", "FLOW.QUERY explain");
|
|
12111
|
-
if (!/^[0-9a-f]{64}$/iu.test(queryFingerprint)) {
|
|
12112
|
-
throw decodeError("FLOW.QUERY explain query_fingerprint is invalid", value);
|
|
12366
|
+
function validateIndex(index, status) {
|
|
12367
|
+
if (!IDENTIFIER.test(index.id) || index.workloads.some((workload) => !IDENTIFIER.test(workload))) {
|
|
12368
|
+
fail("index identity contains invalid characters", index.raw);
|
|
12113
12369
|
}
|
|
12114
|
-
const
|
|
12115
|
-
if (
|
|
12116
|
-
|
|
12370
|
+
const first = index.fields[0];
|
|
12371
|
+
if (first?.name !== "partition_key" || first.direction !== "asc" || first.encoding !== "hashed") {
|
|
12372
|
+
fail("index must begin with partition_key asc hashed", index.raw);
|
|
12117
12373
|
}
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
"
|
|
12121
|
-
|
|
12122
|
-
const estimate = freezeMetadataMap(
|
|
12123
|
-
requiredMap2(field(mapping, "estimate"), "FLOW.QUERY explain estimate"),
|
|
12124
|
-
"FLOW.QUERY explain estimate"
|
|
12125
|
-
);
|
|
12126
|
-
const bounds = freezeMetadataMap(
|
|
12127
|
-
requiredMap2(field(mapping, "bounds"), "FLOW.QUERY explain bounds"),
|
|
12128
|
-
"FLOW.QUERY explain bounds"
|
|
12129
|
-
);
|
|
12130
|
-
const actualValue = field(mapping, "actual");
|
|
12131
|
-
let actual;
|
|
12132
|
-
if (status === "executed") {
|
|
12133
|
-
if (actualValue == null) {
|
|
12134
|
-
throw decodeError("FLOW.QUERY executed explain is missing actual usage", value);
|
|
12374
|
+
let attributeFields = 0;
|
|
12375
|
+
for (const indexField of index.fields) {
|
|
12376
|
+
if (indexField.encoding === "hashed" && indexField.direction !== "asc") {
|
|
12377
|
+
fail("hashed index fields must be ascending", index.raw);
|
|
12135
12378
|
}
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12379
|
+
const kind = fieldKind(indexField.name);
|
|
12380
|
+
if (kind == null) fail("index contains an unsupported field selector", index.raw);
|
|
12381
|
+
if (indexField.encoding === "ordered" && kind !== "integer") {
|
|
12382
|
+
fail("ordered index fields must be integers", index.raw);
|
|
12383
|
+
}
|
|
12384
|
+
if (kind === "attribute") attributeFields += 1;
|
|
12139
12385
|
}
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
if (diagnostic == null) {
|
|
12145
|
-
throw decodeError("FLOW.QUERY rejected explain has an invalid diagnostic", value);
|
|
12386
|
+
if (attributeFields > 1) fail("index may contain at most one attribute field", index.raw);
|
|
12387
|
+
for (const prefix of index.countPrefixes) {
|
|
12388
|
+
if (index.fields.slice(0, prefix).some((field3) => field3.encoding !== "hashed")) {
|
|
12389
|
+
fail("count prefixes may cover only hashed fields", index.raw);
|
|
12146
12390
|
}
|
|
12147
|
-
} else if (diagnosticValue != null) {
|
|
12148
|
-
throw decodeError("FLOW.QUERY non-rejected explain contains a diagnostic", value);
|
|
12149
12391
|
}
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
}
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12392
|
+
if (index.coveringFields.length > 0) {
|
|
12393
|
+
if (index.coveringFields.some((field3) => fieldKind(field3) == null)) {
|
|
12394
|
+
fail("index contains an unsupported covering field selector", index.raw);
|
|
12395
|
+
}
|
|
12396
|
+
const required = /* @__PURE__ */ new Set(["run_id", "version", ...index.fields.map((field3) => field3.name)]);
|
|
12397
|
+
if ([...required].some((field3) => !index.coveringFields.includes(field3))) {
|
|
12398
|
+
fail("index covering fields omit an identity or index field", index.raw);
|
|
12399
|
+
}
|
|
12400
|
+
}
|
|
12401
|
+
if (index.countPrefixes.length > 0 !== (index.format.counter != null)) {
|
|
12402
|
+
fail("index counter format is inconsistent with count prefixes", index.raw);
|
|
12403
|
+
}
|
|
12404
|
+
validateProgress(index.build, FLOW_QUERY_BUILD_PHASES, "build");
|
|
12405
|
+
validateProgress(index.validation, FLOW_QUERY_VALIDATION_PHASES, "validation");
|
|
12406
|
+
if (index.retirement.status === "not_applicable") {
|
|
12407
|
+
if (RETIREMENT_PROGRESS_FIELDS.some((name) => hasKey(index.retirement.raw, name))) {
|
|
12408
|
+
fail("not_applicable retirement must not contain progress", index.retirement.raw);
|
|
12409
|
+
}
|
|
12410
|
+
} else {
|
|
12411
|
+
const retirement = index.retirement;
|
|
12412
|
+
if (retirement.phaseCounts == null || retirement.currentPhases == null || retirement.completedShards == null || retirement.totalShards == null) {
|
|
12413
|
+
fail("retirement progress is incomplete", retirement.raw);
|
|
12414
|
+
}
|
|
12415
|
+
validateProgress(
|
|
12416
|
+
{
|
|
12417
|
+
phaseCounts: retirement.phaseCounts,
|
|
12418
|
+
currentPhases: retirement.currentPhases,
|
|
12419
|
+
completedShards: retirement.completedShards,
|
|
12420
|
+
totalShards: retirement.totalShards,
|
|
12421
|
+
raw: retirement.raw
|
|
12422
|
+
},
|
|
12423
|
+
FLOW_QUERY_RETIREMENT_PHASES,
|
|
12424
|
+
"retirement"
|
|
12169
12425
|
);
|
|
12170
|
-
const position = decodePosition(field(mapping, "position"));
|
|
12171
|
-
return new FlowQueryError({
|
|
12172
|
-
code: requiredText2(mapping, "code", "FLOW.QUERY diagnostic"),
|
|
12173
|
-
message: requiredText2(mapping, "message", "FLOW.QUERY diagnostic"),
|
|
12174
|
-
detail: optionalText2(mapping, "detail", "FLOW.QUERY diagnostic"),
|
|
12175
|
-
hint: optionalText2(mapping, "hint", "FLOW.QUERY diagnostic"),
|
|
12176
|
-
retryable: requiredBoolean(mapping, "retryable", "FLOW.QUERY diagnostic"),
|
|
12177
|
-
safeToRetry: requiredBoolean(mapping, "safe_to_retry", "FLOW.QUERY diagnostic"),
|
|
12178
|
-
retryAfterMs: nonNegativeInteger(
|
|
12179
|
-
field(mapping, "retry_after_ms"),
|
|
12180
|
-
"FLOW.QUERY diagnostic retry_after_ms"
|
|
12181
|
-
),
|
|
12182
|
-
position,
|
|
12183
|
-
context,
|
|
12184
|
-
raw: value,
|
|
12185
|
-
cause
|
|
12186
|
-
});
|
|
12187
|
-
} catch (error) {
|
|
12188
|
-
if (error instanceof FerricStoreError) return void 0;
|
|
12189
|
-
throw error;
|
|
12190
12426
|
}
|
|
12427
|
+
const totals = new Set(
|
|
12428
|
+
[
|
|
12429
|
+
index.coverage.totalShards,
|
|
12430
|
+
index.build.totalShards,
|
|
12431
|
+
index.validation.totalShards,
|
|
12432
|
+
...index.retirement.totalShards == null ? [] : [index.retirement.totalShards]
|
|
12433
|
+
].map(indexInteger)
|
|
12434
|
+
);
|
|
12435
|
+
if (totals.size !== 1) fail("index shard totals are inconsistent", index.raw);
|
|
12436
|
+
if (!sameIndexInteger(index.coverage.completeShards, index.build.completedShards)) {
|
|
12437
|
+
fail("coverage and build completion are inconsistent", index.raw);
|
|
12438
|
+
}
|
|
12439
|
+
if (index.coverage.validation !== index.validation.status) {
|
|
12440
|
+
fail("coverage and validation status are inconsistent", index.raw);
|
|
12441
|
+
}
|
|
12442
|
+
const queryable = index.state === "active" && sameIndexInteger(
|
|
12443
|
+
index.coverage.completeShards,
|
|
12444
|
+
index.coverage.totalShards
|
|
12445
|
+
) && index.coverage.validation === "passed";
|
|
12446
|
+
if (index.queryable !== queryable) fail("index queryable flag is inconsistent", index.raw);
|
|
12447
|
+
validateLifecycle(index);
|
|
12448
|
+
validateValidation(index);
|
|
12449
|
+
validateStatistics(index, status);
|
|
12450
|
+
}
|
|
12451
|
+
function validateProgress(progress, phases, section) {
|
|
12452
|
+
const entries = Object.entries(progress.phaseCounts);
|
|
12453
|
+
if (entries.length === 0 || entries.some(
|
|
12454
|
+
([phase, count]) => !phases.includes(phase) || indexInteger(count) <= 0n
|
|
12455
|
+
) || entries.reduce((sum, [, count]) => sum + indexInteger(count), 0n) !== indexInteger(progress.totalShards)) {
|
|
12456
|
+
fail(`${section} phase_counts are invalid`, progress.raw);
|
|
12457
|
+
}
|
|
12458
|
+
const current = phases.filter((phase) => Object.hasOwn(progress.phaseCounts, phase));
|
|
12459
|
+
if (current.length !== progress.currentPhases.length || current.some((phase, position) => phase !== progress.currentPhases[position])) {
|
|
12460
|
+
fail(`${section} current_phases are inconsistent`, progress.raw);
|
|
12461
|
+
}
|
|
12462
|
+
if (!sameIndexInteger(
|
|
12463
|
+
progress.completedShards,
|
|
12464
|
+
progress.phaseCounts.done ?? 0
|
|
12465
|
+
)) {
|
|
12466
|
+
fail(`${section} completed_shards is inconsistent`, progress.raw);
|
|
12467
|
+
}
|
|
12468
|
+
}
|
|
12469
|
+
function validateLifecycle(index) {
|
|
12470
|
+
const built = sameIndexInteger(
|
|
12471
|
+
index.build.completedShards,
|
|
12472
|
+
index.build.totalShards
|
|
12473
|
+
);
|
|
12474
|
+
const validation = index.validation.status;
|
|
12475
|
+
const retirement = index.retirement.status;
|
|
12476
|
+
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";
|
|
12477
|
+
if (!valid) fail("index lifecycle fields are inconsistent", index.raw);
|
|
12478
|
+
}
|
|
12479
|
+
function validateValidation(index) {
|
|
12480
|
+
const validation = index.validation;
|
|
12481
|
+
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;
|
|
12482
|
+
if (!valid) fail("validation status fields are inconsistent", validation.raw);
|
|
12483
|
+
}
|
|
12484
|
+
function validateStatistics(index, status) {
|
|
12485
|
+
const statistics = index.statistics;
|
|
12486
|
+
const times = [
|
|
12487
|
+
statistics.oldestCollectedAtMs,
|
|
12488
|
+
statistics.newestCollectedAtMs,
|
|
12489
|
+
statistics.oldestAgeMs,
|
|
12490
|
+
statistics.newestAgeMs
|
|
12491
|
+
];
|
|
12492
|
+
if (indexInteger(statistics.samples) === 0n) {
|
|
12493
|
+
if (statistics.status !== "missing" && statistics.status !== "unavailable" || times.some((value) => value != null)) {
|
|
12494
|
+
fail("empty statistics fields are inconsistent", statistics.raw);
|
|
12495
|
+
}
|
|
12496
|
+
return;
|
|
12497
|
+
}
|
|
12498
|
+
if (times.some((value) => value == null)) {
|
|
12499
|
+
fail("sampled statistics require timestamps and ages", statistics.raw);
|
|
12500
|
+
}
|
|
12501
|
+
const [oldest, newest, oldestAge, newestAge] = times;
|
|
12502
|
+
if (indexInteger(oldest) > indexInteger(newest) || indexInteger(oldestAge) !== indexAge(status.observedAtMs, oldest) || indexInteger(newestAge) !== indexAge(status.observedAtMs, newest)) {
|
|
12503
|
+
fail("statistics timestamps or ages are inconsistent", statistics.raw);
|
|
12504
|
+
}
|
|
12505
|
+
const expected = sameIndexInteger(statistics.freshSamples, statistics.samples) ? ["fresh"] : indexInteger(statistics.freshSamples) === 0n ? indexInteger(statistics.futureSamples) > 0n ? ["stale", "future"] : ["stale"] : ["mixed"];
|
|
12506
|
+
if (!expected.includes(statistics.status)) {
|
|
12507
|
+
fail("statistics status does not match sample counters", statistics.raw);
|
|
12508
|
+
}
|
|
12509
|
+
}
|
|
12510
|
+
function fieldKind(name) {
|
|
12511
|
+
if (BUILTIN_FIELDS.has(name)) return INTEGER_FIELDS.has(name) ? "integer" : "keyword";
|
|
12512
|
+
const parts = name.split(".");
|
|
12513
|
+
if (parts.length === 2 && parts[0] === "attribute" && validUnquoted(parts[1] ?? "")) return "attribute";
|
|
12514
|
+
if (parts.length === 3 && parts[0] === "state_meta" && validUnquoted(parts[1] ?? "") && validUnquoted(parts[2] ?? "")) return "state_meta";
|
|
12515
|
+
const attribute = ATTRIBUTE_SELECTOR.exec(name);
|
|
12516
|
+
if (attribute != null) {
|
|
12517
|
+
const metadata = (attribute[1] ?? "").replaceAll("''", "'");
|
|
12518
|
+
if (validMetadata(metadata, true) && name === externalSelector("attribute", metadata)) return "attribute";
|
|
12519
|
+
}
|
|
12520
|
+
const stateMeta = STATE_META_SELECTOR.exec(name);
|
|
12521
|
+
if (stateMeta != null) {
|
|
12522
|
+
const state = (stateMeta[1] ?? "").replaceAll("''", "'");
|
|
12523
|
+
const metadata = (stateMeta[2] ?? "").replaceAll("''", "'");
|
|
12524
|
+
if (validMetadata(state, false) && validMetadata(metadata, true) && name === externalSelector("state_meta", state, metadata)) return "state_meta";
|
|
12525
|
+
}
|
|
12526
|
+
return void 0;
|
|
12527
|
+
}
|
|
12528
|
+
function validUnquoted(value) {
|
|
12529
|
+
return !value.startsWith("__") && UNQUOTED_METADATA.test(value) && Buffer47.byteLength(value, "ascii") <= 64;
|
|
12530
|
+
}
|
|
12531
|
+
function validMetadata(value, rejectReserved) {
|
|
12532
|
+
return value.length > 0 && Buffer47.byteLength(value, "utf8") <= 64 && (!rejectReserved || !value.startsWith("__"));
|
|
12533
|
+
}
|
|
12534
|
+
function externalSelector(root, ...segments) {
|
|
12535
|
+
return segments.every(validUnquoted) ? [root, ...segments].join(".") : root + segments.map((segment) => `['${segment.replaceAll("'", "''")}']`).join("");
|
|
12536
|
+
}
|
|
12537
|
+
function sameIndexInteger(left, right) {
|
|
12538
|
+
return indexInteger(left) === indexInteger(right);
|
|
12539
|
+
}
|
|
12540
|
+
function indexAge(observedAtMs, collectedAtMs) {
|
|
12541
|
+
const age = indexInteger(observedAtMs) - indexInteger(collectedAtMs);
|
|
12542
|
+
return age < 0n ? 0n : age;
|
|
12543
|
+
}
|
|
12544
|
+
function indexInteger(value) {
|
|
12545
|
+
return BigInt(value);
|
|
12546
|
+
}
|
|
12547
|
+
function fail(message, raw) {
|
|
12548
|
+
throw decodeError(`FLOW.QUERY.INDEXES ${message}`, raw);
|
|
12549
|
+
}
|
|
12550
|
+
|
|
12551
|
+
// src/flow-query-index-decode.ts
|
|
12552
|
+
var MAX_UNSIGNED_64 = (1n << 64n) - 1n;
|
|
12553
|
+
function decodeUniqueTextArray(value, context, maximum, maximumBytes) {
|
|
12554
|
+
if (!Array.isArray(value) || value.length > maximum) {
|
|
12555
|
+
throw decodeError(`${context} must be a bounded array`, value);
|
|
12556
|
+
}
|
|
12557
|
+
const result = new Array(value.length);
|
|
12558
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12559
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
12560
|
+
if (!Object.hasOwn(value, index)) {
|
|
12561
|
+
throw decodeError(`${context} must be dense`, value);
|
|
12562
|
+
}
|
|
12563
|
+
const text3 = boundedText(
|
|
12564
|
+
value[index],
|
|
12565
|
+
`${context} entry ${index}`,
|
|
12566
|
+
maximumBytes
|
|
12567
|
+
);
|
|
12568
|
+
if (seen.has(text3)) {
|
|
12569
|
+
throw decodeError(`${context} contains duplicates`, value);
|
|
12570
|
+
}
|
|
12571
|
+
seen.add(text3);
|
|
12572
|
+
result[index] = text3;
|
|
12573
|
+
}
|
|
12574
|
+
return Object.freeze(result);
|
|
12575
|
+
}
|
|
12576
|
+
function requiredChoice(mapping2, name, context, choices) {
|
|
12577
|
+
const value = requiredBoundedText2(mapping2, name, context, 64);
|
|
12578
|
+
if (!choices.includes(value)) {
|
|
12579
|
+
throw decodeError(`${context} ${name} is unsupported`, mapping2);
|
|
12580
|
+
}
|
|
12581
|
+
return value;
|
|
12582
|
+
}
|
|
12583
|
+
function optionalBoundedText(mapping2, name, context, maximumBytes) {
|
|
12584
|
+
const value = field(mapping2, name);
|
|
12585
|
+
if (value == null) return void 0;
|
|
12586
|
+
return boundedText(value, `${context} ${name}`, maximumBytes);
|
|
12191
12587
|
}
|
|
12192
|
-
function
|
|
12193
|
-
const
|
|
12588
|
+
function optionalIndexInteger(mapping2, name, context) {
|
|
12589
|
+
const value = field(mapping2, name);
|
|
12590
|
+
return value == null ? void 0 : unsignedIndexInteger(value, `${context} ${name}`);
|
|
12591
|
+
}
|
|
12592
|
+
function indexCounter(mapping2, name, section) {
|
|
12593
|
+
return unsignedIndexInteger(
|
|
12594
|
+
field(mapping2, name),
|
|
12595
|
+
`FLOW.QUERY.INDEXES index ${section} ${name}`
|
|
12596
|
+
);
|
|
12597
|
+
}
|
|
12598
|
+
function unsignedIndexInteger(value, context) {
|
|
12599
|
+
return boundedInteger(value, MAX_UNSIGNED_64, context);
|
|
12600
|
+
}
|
|
12601
|
+
function positiveIndexInteger(value, context) {
|
|
12602
|
+
return positiveBoundedInteger(value, MAX_UNSIGNED_64, context);
|
|
12603
|
+
}
|
|
12604
|
+
function indexInteger2(value) {
|
|
12605
|
+
return BigInt(value);
|
|
12606
|
+
}
|
|
12607
|
+
|
|
12608
|
+
// src/flow-query-index-lifecycle-response.ts
|
|
12609
|
+
function decodeIndexCoverage(value) {
|
|
12610
|
+
const context = "FLOW.QUERY.INDEXES index coverage";
|
|
12611
|
+
const mapping2 = requiredMap2(value, context);
|
|
12612
|
+
const completeShards = unsignedIndexInteger(
|
|
12613
|
+
field(mapping2, "complete_shards"),
|
|
12614
|
+
`${context} complete_shards`
|
|
12615
|
+
);
|
|
12616
|
+
const totalShards = positiveIndexInteger(
|
|
12617
|
+
field(mapping2, "total_shards"),
|
|
12618
|
+
`${context} total_shards`
|
|
12619
|
+
);
|
|
12620
|
+
if (indexInteger2(completeShards) > indexInteger2(totalShards)) {
|
|
12621
|
+
throw decodeError(`${context} complete_shards exceeds total_shards`, value);
|
|
12622
|
+
}
|
|
12623
|
+
return Object.freeze({
|
|
12624
|
+
completeShards,
|
|
12625
|
+
totalShards,
|
|
12626
|
+
validation: requiredChoice(mapping2, "validation", context, [
|
|
12627
|
+
"pending",
|
|
12628
|
+
"passed",
|
|
12629
|
+
"failed"
|
|
12630
|
+
]),
|
|
12631
|
+
raw: freezeMap(mapping2)
|
|
12632
|
+
});
|
|
12633
|
+
}
|
|
12634
|
+
function decodeIndexBuild(value) {
|
|
12635
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES index build");
|
|
12636
|
+
const progress = decodeIndexProgress(
|
|
12637
|
+
mapping2,
|
|
12638
|
+
"build",
|
|
12639
|
+
FLOW_QUERY_BUILD_PHASES
|
|
12640
|
+
);
|
|
12641
|
+
return Object.freeze({
|
|
12642
|
+
...progress,
|
|
12643
|
+
scannedRecords: indexCounter(mapping2, "scanned_records", "build"),
|
|
12644
|
+
writtenEntries: indexCounter(mapping2, "written_entries", "build"),
|
|
12645
|
+
writtenBytes: indexCounter(mapping2, "written_bytes", "build")
|
|
12646
|
+
});
|
|
12647
|
+
}
|
|
12648
|
+
function decodeIndexValidation(value) {
|
|
12649
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES index validation");
|
|
12650
|
+
if (!hasKey(mapping2, "failure_reason") || !hasKey(mapping2, "validated_at_ms")) {
|
|
12651
|
+
throw decodeError(
|
|
12652
|
+
"FLOW.QUERY.INDEXES index validation is missing nullable fields",
|
|
12653
|
+
value
|
|
12654
|
+
);
|
|
12655
|
+
}
|
|
12656
|
+
const progress = decodeIndexProgress(
|
|
12657
|
+
mapping2,
|
|
12658
|
+
"validation",
|
|
12659
|
+
FLOW_QUERY_VALIDATION_PHASES
|
|
12660
|
+
);
|
|
12661
|
+
return Object.freeze({
|
|
12662
|
+
...progress,
|
|
12663
|
+
status: requiredChoice(
|
|
12664
|
+
mapping2,
|
|
12665
|
+
"status",
|
|
12666
|
+
"FLOW.QUERY.INDEXES index validation",
|
|
12667
|
+
["pending", "passed", "failed"]
|
|
12668
|
+
),
|
|
12669
|
+
checkedRecords: indexCounter(mapping2, "checked_records", "validation"),
|
|
12670
|
+
checkedEntries: indexCounter(mapping2, "checked_entries", "validation"),
|
|
12671
|
+
mismatches: indexCounter(mapping2, "mismatches", "validation"),
|
|
12672
|
+
failureReason: optionalBoundedText(
|
|
12673
|
+
mapping2,
|
|
12674
|
+
"failure_reason",
|
|
12675
|
+
"FLOW.QUERY.INDEXES index validation",
|
|
12676
|
+
128
|
|
12677
|
+
),
|
|
12678
|
+
validatedAtMs: optionalIndexInteger(
|
|
12679
|
+
mapping2,
|
|
12680
|
+
"validated_at_ms",
|
|
12681
|
+
"FLOW.QUERY.INDEXES index validation"
|
|
12682
|
+
)
|
|
12683
|
+
});
|
|
12684
|
+
}
|
|
12685
|
+
function decodeIndexRetirement(value) {
|
|
12686
|
+
const context = "FLOW.QUERY.INDEXES index retirement";
|
|
12687
|
+
const mapping2 = requiredMap2(value, context);
|
|
12688
|
+
const status = requiredChoice(mapping2, "status", context, [
|
|
12689
|
+
"not_applicable",
|
|
12690
|
+
"pending",
|
|
12691
|
+
"complete"
|
|
12692
|
+
]);
|
|
12693
|
+
if (status === "not_applicable") {
|
|
12694
|
+
return Object.freeze({ status, raw: freezeMap(mapping2) });
|
|
12695
|
+
}
|
|
12696
|
+
const completedShards = unsignedIndexInteger(
|
|
12697
|
+
field(mapping2, "completed_shards"),
|
|
12698
|
+
`${context} completed_shards`
|
|
12699
|
+
);
|
|
12700
|
+
const totalShards = positiveIndexInteger(
|
|
12701
|
+
field(mapping2, "total_shards"),
|
|
12702
|
+
`${context} total_shards`
|
|
12703
|
+
);
|
|
12704
|
+
if (indexInteger2(completedShards) > indexInteger2(totalShards)) {
|
|
12705
|
+
throw decodeError(
|
|
12706
|
+
`${context} completed_shards exceeds total_shards`,
|
|
12707
|
+
mapping2
|
|
12708
|
+
);
|
|
12709
|
+
}
|
|
12710
|
+
return Object.freeze({
|
|
12711
|
+
status,
|
|
12712
|
+
phaseCounts: decodePhaseCounts(
|
|
12713
|
+
field(mapping2, "phase_counts"),
|
|
12714
|
+
"retirement"
|
|
12715
|
+
),
|
|
12716
|
+
currentPhases: decodeAllowedPhases(
|
|
12717
|
+
field(mapping2, "current_phases"),
|
|
12718
|
+
"retirement",
|
|
12719
|
+
FLOW_QUERY_RETIREMENT_PHASES
|
|
12720
|
+
),
|
|
12721
|
+
completedShards,
|
|
12722
|
+
totalShards,
|
|
12723
|
+
deletedEntries: indexCounter(mapping2, "deleted_entries", "retirement"),
|
|
12724
|
+
deletedBytes: indexCounter(mapping2, "deleted_bytes", "retirement"),
|
|
12725
|
+
rewrittenReverseRows: indexCounter(
|
|
12726
|
+
mapping2,
|
|
12727
|
+
"rewritten_reverse_rows",
|
|
12728
|
+
"retirement"
|
|
12729
|
+
),
|
|
12730
|
+
raw: freezeMap(mapping2)
|
|
12731
|
+
});
|
|
12732
|
+
}
|
|
12733
|
+
function decodeIndexStatistics(value) {
|
|
12734
|
+
const context = "FLOW.QUERY.INDEXES index statistics";
|
|
12735
|
+
const mapping2 = requiredMap2(value, context);
|
|
12736
|
+
for (const name of [
|
|
12737
|
+
"oldest_collected_at_ms",
|
|
12738
|
+
"newest_collected_at_ms",
|
|
12739
|
+
"oldest_age_ms",
|
|
12740
|
+
"newest_age_ms"
|
|
12741
|
+
]) {
|
|
12742
|
+
if (!hasKey(mapping2, name)) {
|
|
12743
|
+
throw decodeError(
|
|
12744
|
+
`${context} is missing required nullable fields`,
|
|
12745
|
+
value
|
|
12746
|
+
);
|
|
12747
|
+
}
|
|
12748
|
+
}
|
|
12749
|
+
const samples = indexCounter(mapping2, "samples", "statistics");
|
|
12750
|
+
const freshSamples = indexCounter(mapping2, "fresh_samples", "statistics");
|
|
12751
|
+
const staleSamples = indexCounter(mapping2, "stale_samples", "statistics");
|
|
12752
|
+
const futureSamples = indexCounter(mapping2, "future_samples", "statistics");
|
|
12753
|
+
if (indexInteger2(freshSamples) + indexInteger2(staleSamples) !== indexInteger2(samples) || indexInteger2(futureSamples) > indexInteger2(staleSamples)) {
|
|
12754
|
+
throw decodeError(`${context} counters are inconsistent`, value);
|
|
12755
|
+
}
|
|
12756
|
+
return Object.freeze({
|
|
12757
|
+
status: requiredChoice(mapping2, "status", context, [
|
|
12758
|
+
"fresh",
|
|
12759
|
+
"stale",
|
|
12760
|
+
"future",
|
|
12761
|
+
"mixed",
|
|
12762
|
+
"missing",
|
|
12763
|
+
"unavailable"
|
|
12764
|
+
]),
|
|
12765
|
+
samples,
|
|
12766
|
+
freshSamples,
|
|
12767
|
+
staleSamples,
|
|
12768
|
+
futureSamples,
|
|
12769
|
+
oldestCollectedAtMs: optionalIndexInteger(
|
|
12770
|
+
mapping2,
|
|
12771
|
+
"oldest_collected_at_ms",
|
|
12772
|
+
context
|
|
12773
|
+
),
|
|
12774
|
+
newestCollectedAtMs: optionalIndexInteger(
|
|
12775
|
+
mapping2,
|
|
12776
|
+
"newest_collected_at_ms",
|
|
12777
|
+
context
|
|
12778
|
+
),
|
|
12779
|
+
oldestAgeMs: optionalIndexInteger(mapping2, "oldest_age_ms", context),
|
|
12780
|
+
newestAgeMs: optionalIndexInteger(mapping2, "newest_age_ms", context),
|
|
12781
|
+
raw: freezeMap(mapping2)
|
|
12782
|
+
});
|
|
12783
|
+
}
|
|
12784
|
+
function decodeIndexProgress(mapping2, section, allowedPhases) {
|
|
12785
|
+
const context = `FLOW.QUERY.INDEXES index ${section}`;
|
|
12786
|
+
const completedShards = unsignedIndexInteger(
|
|
12787
|
+
field(mapping2, "completed_shards"),
|
|
12788
|
+
`${context} completed_shards`
|
|
12789
|
+
);
|
|
12790
|
+
const totalShards = positiveIndexInteger(
|
|
12791
|
+
field(mapping2, "total_shards"),
|
|
12792
|
+
`${context} total_shards`
|
|
12793
|
+
);
|
|
12794
|
+
if (indexInteger2(completedShards) > indexInteger2(totalShards)) {
|
|
12795
|
+
throw decodeError(
|
|
12796
|
+
`${context} completed_shards exceeds total_shards`,
|
|
12797
|
+
mapping2
|
|
12798
|
+
);
|
|
12799
|
+
}
|
|
12800
|
+
return Object.freeze({
|
|
12801
|
+
scope: requiredChoice(mapping2, "scope", context, ["catalog_build"]),
|
|
12802
|
+
phaseCounts: decodePhaseCounts(field(mapping2, "phase_counts"), section),
|
|
12803
|
+
currentPhases: decodeAllowedPhases(
|
|
12804
|
+
field(mapping2, "current_phases"),
|
|
12805
|
+
section,
|
|
12806
|
+
allowedPhases
|
|
12807
|
+
),
|
|
12808
|
+
completedShards,
|
|
12809
|
+
totalShards,
|
|
12810
|
+
raw: freezeMap(mapping2)
|
|
12811
|
+
});
|
|
12812
|
+
}
|
|
12813
|
+
function decodePhaseCounts(value, section) {
|
|
12814
|
+
const context = `FLOW.QUERY.INDEXES index ${section} phase_counts`;
|
|
12815
|
+
const mapping2 = requiredMap2(value, context);
|
|
12816
|
+
const entries = mapping2 instanceof Map ? [...mapping2.entries()] : Object.entries(mapping2);
|
|
12817
|
+
if (entries.length > 16) {
|
|
12818
|
+
throw decodeError(`${context} contains too many entries`, value);
|
|
12819
|
+
}
|
|
12820
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
12821
|
+
for (const [rawPhase, rawCount] of entries) {
|
|
12822
|
+
const phase = boundedText(rawPhase, `${context} phase`, 64);
|
|
12823
|
+
if (Object.hasOwn(result, phase)) {
|
|
12824
|
+
throw decodeError(`${context} contains duplicate phases`, value);
|
|
12825
|
+
}
|
|
12826
|
+
result[phase] = unsignedIndexInteger(rawCount, `${context} ${phase}`);
|
|
12827
|
+
}
|
|
12828
|
+
return Object.freeze(result);
|
|
12829
|
+
}
|
|
12830
|
+
function decodeAllowedPhases(value, section, allowed) {
|
|
12831
|
+
const phases = decodeUniqueTextArray(
|
|
12832
|
+
value,
|
|
12833
|
+
`FLOW.QUERY.INDEXES index ${section} current_phases`,
|
|
12834
|
+
allowed.length,
|
|
12835
|
+
64
|
|
12836
|
+
);
|
|
12837
|
+
if (phases.some((phase) => !allowed.includes(phase))) {
|
|
12838
|
+
throw decodeError(
|
|
12839
|
+
`FLOW.QUERY.INDEXES index ${section} current_phases are invalid`,
|
|
12840
|
+
value
|
|
12841
|
+
);
|
|
12842
|
+
}
|
|
12843
|
+
return phases;
|
|
12844
|
+
}
|
|
12845
|
+
|
|
12846
|
+
// src/flow-query-index-response.ts
|
|
12847
|
+
var FLOW_QUERY_INDEXES_CONTRACT = "ferric.flow.query.indexes/v1";
|
|
12848
|
+
function decodeFlowQueryIndexStatus(value, expectedId) {
|
|
12849
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES");
|
|
12194
12850
|
requireContract(
|
|
12195
|
-
|
|
12851
|
+
mapping2,
|
|
12196
12852
|
"contract_version",
|
|
12197
12853
|
FLOW_QUERY_INDEXES_CONTRACT,
|
|
12198
12854
|
"FLOW.QUERY.INDEXES"
|
|
12199
12855
|
);
|
|
12200
|
-
const registry = requiredMap2(
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
"FLOW.QUERY.INDEXES services"
|
|
12856
|
+
const registry = requiredMap2(
|
|
12857
|
+
field(mapping2, "registry"),
|
|
12858
|
+
"FLOW.QUERY.INDEXES registry"
|
|
12204
12859
|
);
|
|
12205
|
-
const
|
|
12860
|
+
const services = decodeIndexServices(field(mapping2, "services"));
|
|
12861
|
+
const rawIndexes = field(mapping2, "indexes");
|
|
12206
12862
|
if (!Array.isArray(rawIndexes) || rawIndexes.length > 32) {
|
|
12207
|
-
throw decodeError(
|
|
12863
|
+
throw decodeError(
|
|
12864
|
+
"FLOW.QUERY.INDEXES indexes must contain at most 32 entries",
|
|
12865
|
+
value
|
|
12866
|
+
);
|
|
12208
12867
|
}
|
|
12209
12868
|
const indexes = new Array(rawIndexes.length);
|
|
12210
12869
|
for (let index = 0; index < rawIndexes.length; index += 1) {
|
|
12211
12870
|
if (!Object.hasOwn(rawIndexes, index)) {
|
|
12212
|
-
throw decodeError(
|
|
12871
|
+
throw decodeError(
|
|
12872
|
+
"FLOW.QUERY.INDEXES indexes must be a dense array",
|
|
12873
|
+
value
|
|
12874
|
+
);
|
|
12213
12875
|
}
|
|
12214
12876
|
indexes[index] = decodeIndex(rawIndexes[index], index);
|
|
12215
12877
|
}
|
|
12216
|
-
|
|
12878
|
+
const status = Object.freeze({
|
|
12217
12879
|
contractVersion: FLOW_QUERY_INDEXES_CONTRACT,
|
|
12218
|
-
observedAtMs:
|
|
12219
|
-
field(
|
|
12880
|
+
observedAtMs: unsignedIndexInteger(
|
|
12881
|
+
field(mapping2, "observed_at_ms"),
|
|
12220
12882
|
"FLOW.QUERY.INDEXES observed_at_ms"
|
|
12221
12883
|
),
|
|
12222
|
-
statisticsMaxAgeMs:
|
|
12223
|
-
field(
|
|
12884
|
+
statisticsMaxAgeMs: unsignedIndexInteger(
|
|
12885
|
+
field(mapping2, "statistics_max_age_ms"),
|
|
12224
12886
|
"FLOW.QUERY.INDEXES statistics_max_age_ms"
|
|
12225
12887
|
),
|
|
12226
12888
|
registry: Object.freeze({
|
|
12227
|
-
epoch:
|
|
12889
|
+
epoch: unsignedIndexInteger(
|
|
12228
12890
|
field(registry, "epoch"),
|
|
12229
|
-
MAX_UNSIGNED_64,
|
|
12230
12891
|
"FLOW.QUERY.INDEXES epoch"
|
|
12231
12892
|
),
|
|
12232
|
-
catalogVersion:
|
|
12893
|
+
catalogVersion: positiveIndexInteger(
|
|
12233
12894
|
field(registry, "catalog_version"),
|
|
12234
|
-
MAX_UNSIGNED_64,
|
|
12235
12895
|
"FLOW.QUERY.INDEXES catalog_version"
|
|
12236
12896
|
)
|
|
12237
12897
|
}),
|
|
12238
12898
|
services,
|
|
12239
12899
|
indexes: Object.freeze(indexes),
|
|
12240
|
-
raw: freezeMap(
|
|
12900
|
+
raw: freezeMap(mapping2)
|
|
12901
|
+
});
|
|
12902
|
+
validateFlowQueryIndexContract(status, expectedId);
|
|
12903
|
+
return status;
|
|
12904
|
+
}
|
|
12905
|
+
function decodeIndex(value, index) {
|
|
12906
|
+
const mapping2 = requiredMap2(value, `FLOW.QUERY.INDEXES index ${index}`);
|
|
12907
|
+
const fields = decodeIndexFields(field(mapping2, "fields"));
|
|
12908
|
+
return Object.freeze({
|
|
12909
|
+
id: requiredBoundedText2(mapping2, "id", "FLOW.QUERY.INDEXES index", 64),
|
|
12910
|
+
version: positiveIndexInteger(
|
|
12911
|
+
field(mapping2, "version"),
|
|
12912
|
+
"FLOW.QUERY.INDEXES index version"
|
|
12913
|
+
),
|
|
12914
|
+
buildId: requiredBoundedText2(
|
|
12915
|
+
mapping2,
|
|
12916
|
+
"build_id",
|
|
12917
|
+
"FLOW.QUERY.INDEXES index",
|
|
12918
|
+
128
|
|
12919
|
+
),
|
|
12920
|
+
source: requiredChoice(mapping2, "source", "FLOW.QUERY.INDEXES index", [
|
|
12921
|
+
"runs"
|
|
12922
|
+
]),
|
|
12923
|
+
state: requiredChoice(mapping2, "state", "FLOW.QUERY.INDEXES index", [
|
|
12924
|
+
"building",
|
|
12925
|
+
"validating",
|
|
12926
|
+
"active",
|
|
12927
|
+
"retiring",
|
|
12928
|
+
"failed"
|
|
12929
|
+
]),
|
|
12930
|
+
queryable: requiredBoolean(
|
|
12931
|
+
mapping2,
|
|
12932
|
+
"queryable",
|
|
12933
|
+
"FLOW.QUERY.INDEXES index"
|
|
12934
|
+
),
|
|
12935
|
+
fields,
|
|
12936
|
+
workloads: decodeUniqueTextArray(
|
|
12937
|
+
field(mapping2, "workloads"),
|
|
12938
|
+
"FLOW.QUERY.INDEXES index workloads",
|
|
12939
|
+
16,
|
|
12940
|
+
64
|
|
12941
|
+
),
|
|
12942
|
+
countPrefixes: decodeCountPrefixes(
|
|
12943
|
+
field(mapping2, "count_prefixes"),
|
|
12944
|
+
fields.length
|
|
12945
|
+
),
|
|
12946
|
+
coveringFields: decodeCoveringFields(mapping2),
|
|
12947
|
+
format: decodeIndexFormat(mapping2),
|
|
12948
|
+
coverage: decodeIndexCoverage(field(mapping2, "coverage")),
|
|
12949
|
+
build: decodeIndexBuild(field(mapping2, "build")),
|
|
12950
|
+
validation: decodeIndexValidation(field(mapping2, "validation")),
|
|
12951
|
+
retirement: decodeIndexRetirement(field(mapping2, "retirement")),
|
|
12952
|
+
statistics: decodeIndexStatistics(field(mapping2, "statistics")),
|
|
12953
|
+
raw: freezeMap(mapping2)
|
|
12954
|
+
});
|
|
12955
|
+
}
|
|
12956
|
+
function decodeIndexServices(value) {
|
|
12957
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY.INDEXES services");
|
|
12958
|
+
const choices = ["ready", "unavailable"];
|
|
12959
|
+
return Object.freeze({
|
|
12960
|
+
registry: requiredChoice(
|
|
12961
|
+
mapping2,
|
|
12962
|
+
"registry",
|
|
12963
|
+
"FLOW.QUERY.INDEXES services",
|
|
12964
|
+
choices
|
|
12965
|
+
),
|
|
12966
|
+
lifecycleWorker: requiredChoice(
|
|
12967
|
+
mapping2,
|
|
12968
|
+
"lifecycle_worker",
|
|
12969
|
+
"FLOW.QUERY.INDEXES services",
|
|
12970
|
+
choices
|
|
12971
|
+
),
|
|
12972
|
+
statisticsStore: requiredChoice(
|
|
12973
|
+
mapping2,
|
|
12974
|
+
"statistics_store",
|
|
12975
|
+
"FLOW.QUERY.INDEXES services",
|
|
12976
|
+
choices
|
|
12977
|
+
),
|
|
12978
|
+
statisticsWorker: requiredChoice(
|
|
12979
|
+
mapping2,
|
|
12980
|
+
"statistics_worker",
|
|
12981
|
+
"FLOW.QUERY.INDEXES services",
|
|
12982
|
+
choices
|
|
12983
|
+
),
|
|
12984
|
+
raw: freezeMap(mapping2)
|
|
12985
|
+
});
|
|
12986
|
+
}
|
|
12987
|
+
function decodeIndexFields(value) {
|
|
12988
|
+
const context = "FLOW.QUERY.INDEXES index fields";
|
|
12989
|
+
if (!Array.isArray(value) || value.length < 2 || value.length > 8) {
|
|
12990
|
+
throw decodeError(`${context} must contain 2 to 8 entries`, value);
|
|
12991
|
+
}
|
|
12992
|
+
const fields = new Array(value.length);
|
|
12993
|
+
const names = /* @__PURE__ */ new Set();
|
|
12994
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
12995
|
+
if (!Object.hasOwn(value, index)) {
|
|
12996
|
+
throw decodeError(`${context} must be dense`, value);
|
|
12997
|
+
}
|
|
12998
|
+
const mapping2 = requiredMap2(
|
|
12999
|
+
value[index],
|
|
13000
|
+
`FLOW.QUERY.INDEXES index field ${index}`
|
|
13001
|
+
);
|
|
13002
|
+
const name = requiredBoundedText2(
|
|
13003
|
+
mapping2,
|
|
13004
|
+
"name",
|
|
13005
|
+
"FLOW.QUERY.INDEXES index field",
|
|
13006
|
+
512
|
|
13007
|
+
);
|
|
13008
|
+
if (names.has(name)) {
|
|
13009
|
+
throw decodeError(`${context} contain duplicates`, value);
|
|
13010
|
+
}
|
|
13011
|
+
names.add(name);
|
|
13012
|
+
fields[index] = Object.freeze({
|
|
13013
|
+
name,
|
|
13014
|
+
direction: requiredChoice(
|
|
13015
|
+
mapping2,
|
|
13016
|
+
"direction",
|
|
13017
|
+
"FLOW.QUERY.INDEXES index field",
|
|
13018
|
+
["asc", "desc"]
|
|
13019
|
+
),
|
|
13020
|
+
encoding: requiredChoice(
|
|
13021
|
+
mapping2,
|
|
13022
|
+
"encoding",
|
|
13023
|
+
"FLOW.QUERY.INDEXES index field",
|
|
13024
|
+
["hashed", "ordered"]
|
|
13025
|
+
),
|
|
13026
|
+
raw: freezeMap(mapping2)
|
|
13027
|
+
});
|
|
13028
|
+
}
|
|
13029
|
+
return Object.freeze(fields);
|
|
13030
|
+
}
|
|
13031
|
+
function decodeCountPrefixes(value, fieldCount) {
|
|
13032
|
+
const context = "FLOW.QUERY.INDEXES index count_prefixes";
|
|
13033
|
+
if (!Array.isArray(value) || value.length > fieldCount) {
|
|
13034
|
+
throw decodeError(`${context} are invalid`, value);
|
|
13035
|
+
}
|
|
13036
|
+
const prefixes = new Array(value.length);
|
|
13037
|
+
let previous = 0;
|
|
13038
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
13039
|
+
if (!Object.hasOwn(value, index)) {
|
|
13040
|
+
throw decodeError(`${context} must be dense`, value);
|
|
13041
|
+
}
|
|
13042
|
+
const prefix = positiveInteger(value[index], `${context} entry`);
|
|
13043
|
+
if (prefix > fieldCount || prefix <= previous) {
|
|
13044
|
+
throw decodeError(`${context} are invalid`, value);
|
|
13045
|
+
}
|
|
13046
|
+
prefixes[index] = prefix;
|
|
13047
|
+
previous = prefix;
|
|
13048
|
+
}
|
|
13049
|
+
return Object.freeze(prefixes);
|
|
13050
|
+
}
|
|
13051
|
+
function decodeCoveringFields(mapping2) {
|
|
13052
|
+
const raw = field(mapping2, "covering_fields");
|
|
13053
|
+
if (!Array.isArray(raw) || raw.length > 32) {
|
|
13054
|
+
throw decodeError(
|
|
13055
|
+
"FLOW.QUERY.INDEXES index covering_fields must contain at most 32 entries",
|
|
13056
|
+
raw
|
|
13057
|
+
);
|
|
13058
|
+
}
|
|
13059
|
+
const fields = new Array(raw.length);
|
|
13060
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13061
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
13062
|
+
if (!Object.hasOwn(raw, index)) {
|
|
13063
|
+
throw decodeError(
|
|
13064
|
+
"FLOW.QUERY.INDEXES index covering_fields must be dense",
|
|
13065
|
+
raw
|
|
13066
|
+
);
|
|
13067
|
+
}
|
|
13068
|
+
const value = boundedText(
|
|
13069
|
+
raw[index],
|
|
13070
|
+
`FLOW.QUERY.INDEXES index covering_fields entry ${index}`,
|
|
13071
|
+
512
|
|
13072
|
+
);
|
|
13073
|
+
if (seen.has(value)) {
|
|
13074
|
+
throw decodeError(
|
|
13075
|
+
"FLOW.QUERY.INDEXES index covering_fields contains duplicates",
|
|
13076
|
+
raw
|
|
13077
|
+
);
|
|
13078
|
+
}
|
|
13079
|
+
seen.add(value);
|
|
13080
|
+
fields[index] = value;
|
|
13081
|
+
}
|
|
13082
|
+
return Object.freeze(fields);
|
|
13083
|
+
}
|
|
13084
|
+
function decodeIndexFormat(mapping2) {
|
|
13085
|
+
const raw = requiredMap2(
|
|
13086
|
+
field(mapping2, "format"),
|
|
13087
|
+
"FLOW.QUERY.INDEXES index format"
|
|
13088
|
+
);
|
|
13089
|
+
if (!hasKey(raw, "counter")) {
|
|
13090
|
+
throw decodeError(
|
|
13091
|
+
"FLOW.QUERY.INDEXES index format counter is missing",
|
|
13092
|
+
raw
|
|
13093
|
+
);
|
|
13094
|
+
}
|
|
13095
|
+
const counterValue = field(raw, "counter");
|
|
13096
|
+
const counter = counterValue === null ? void 0 : boundedText(
|
|
13097
|
+
counterValue,
|
|
13098
|
+
"FLOW.QUERY.INDEXES index format counter",
|
|
13099
|
+
128
|
|
13100
|
+
);
|
|
13101
|
+
return Object.freeze({
|
|
13102
|
+
queryRow: requiredBoundedText2(
|
|
13103
|
+
raw,
|
|
13104
|
+
"query_row",
|
|
13105
|
+
"FLOW.QUERY.INDEXES index format",
|
|
13106
|
+
128
|
|
13107
|
+
),
|
|
13108
|
+
key: requiredBoundedText2(
|
|
13109
|
+
raw,
|
|
13110
|
+
"key",
|
|
13111
|
+
"FLOW.QUERY.INDEXES index format",
|
|
13112
|
+
128
|
|
13113
|
+
),
|
|
13114
|
+
entry: requiredBoundedText2(
|
|
13115
|
+
raw,
|
|
13116
|
+
"entry",
|
|
13117
|
+
"FLOW.QUERY.INDEXES index format",
|
|
13118
|
+
128
|
|
13119
|
+
),
|
|
13120
|
+
reverse: requiredBoundedText2(
|
|
13121
|
+
raw,
|
|
13122
|
+
"reverse",
|
|
13123
|
+
"FLOW.QUERY.INDEXES index format",
|
|
13124
|
+
128
|
|
13125
|
+
),
|
|
13126
|
+
counter,
|
|
13127
|
+
raw: freezeMap(raw)
|
|
13128
|
+
});
|
|
13129
|
+
}
|
|
13130
|
+
|
|
13131
|
+
// src/flow-query-response.ts
|
|
13132
|
+
var FLOW_QUERY_RESULT_CONTRACT = "ferric.flow.query.result/v1";
|
|
13133
|
+
var FLOW_EXPLAIN_CONTRACT = "ferric.flow.explain/v1";
|
|
13134
|
+
var USAGE_FIELDS2 = [
|
|
13135
|
+
["range_seeks", "rangeSeeks"],
|
|
13136
|
+
["range_pages", "rangePages"],
|
|
13137
|
+
["scanned_entries", "scannedEntries"],
|
|
13138
|
+
["scanned_bytes", "scannedBytes"],
|
|
13139
|
+
["hydrated_records", "hydratedRecords"],
|
|
13140
|
+
["residual_checks", "residualChecks"],
|
|
13141
|
+
["duplicate_entries", "duplicateEntries"],
|
|
13142
|
+
["result_records", "resultRecords"],
|
|
13143
|
+
["response_bytes", "responseBytes"],
|
|
13144
|
+
["memory_high_water_bytes", "memoryHighWaterBytes"],
|
|
13145
|
+
["wall_time_us", "wallTimeUs"]
|
|
13146
|
+
];
|
|
13147
|
+
var MAX_SIGNED_643 = (1n << 63n) - 1n;
|
|
13148
|
+
var QUALITY_VALUES2 = {
|
|
13149
|
+
exactness: ["authoritative", "projected_exact", "exact", "not_applicable"],
|
|
13150
|
+
freshness: ["current", "projection_watermark", "not_applicable"],
|
|
13151
|
+
coverage: ["complete", "unavailable"],
|
|
13152
|
+
pagination: ["none", "complete", "authenticated_seek", "live_seek"]
|
|
13153
|
+
};
|
|
13154
|
+
function decodeFlowQueryResult(value) {
|
|
13155
|
+
const result = decodeFlowQueryResponse(value, (mapping2) => freezeMap(mapping2));
|
|
13156
|
+
if (result.kind === "records") {
|
|
13157
|
+
return Object.freeze({
|
|
13158
|
+
kind: "records",
|
|
13159
|
+
version: FLOW_QUERY_RESULT_CONTRACT,
|
|
13160
|
+
records: Object.freeze(result.records),
|
|
13161
|
+
page: result.page,
|
|
13162
|
+
quality: result.quality,
|
|
13163
|
+
usage: result.usage,
|
|
13164
|
+
raw: result.raw
|
|
13165
|
+
});
|
|
13166
|
+
}
|
|
13167
|
+
return Object.freeze({
|
|
13168
|
+
kind: "count",
|
|
13169
|
+
version: FLOW_QUERY_RESULT_CONTRACT,
|
|
13170
|
+
count: result.count,
|
|
13171
|
+
quality: result.quality,
|
|
13172
|
+
usage: result.usage,
|
|
13173
|
+
raw: result.raw
|
|
13174
|
+
});
|
|
13175
|
+
}
|
|
13176
|
+
function decodeFlowQueryRecords(value, decodeRecord) {
|
|
13177
|
+
const result = decodeFlowQueryResponse(
|
|
13178
|
+
value,
|
|
13179
|
+
(mapping2, index) => decodeRecord(mapping2 instanceof Map ? freezeMap(mapping2) : mapping2, index)
|
|
13180
|
+
);
|
|
13181
|
+
if (result.kind === "count") {
|
|
13182
|
+
throw new FerricStoreError("Flow record convenience query returned a count result", {
|
|
13183
|
+
raw: result.raw
|
|
13184
|
+
});
|
|
13185
|
+
}
|
|
13186
|
+
return result.records;
|
|
13187
|
+
}
|
|
13188
|
+
function decodeFlowQueryResponse(value, decodeRecord) {
|
|
13189
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY result");
|
|
13190
|
+
requireContract(mapping2, "version", FLOW_QUERY_RESULT_CONTRACT, "FLOW.QUERY result");
|
|
13191
|
+
const quality = decodeQuality(field(mapping2, "quality"));
|
|
13192
|
+
const usage = decodeUsage(field(mapping2, "usage"));
|
|
13193
|
+
const hasRecords = hasKey(mapping2, "records");
|
|
13194
|
+
const hasCount = hasKey(mapping2, "result");
|
|
13195
|
+
if (hasRecords === hasCount) {
|
|
13196
|
+
throw decodeError("FLOW.QUERY result must contain exactly one records or count shape", value);
|
|
13197
|
+
}
|
|
13198
|
+
const raw = freezeMap(mapping2);
|
|
13199
|
+
if (hasRecords) {
|
|
13200
|
+
const rawRecords = field(mapping2, "records");
|
|
13201
|
+
if (!Array.isArray(rawRecords) || rawRecords.length > 100) {
|
|
13202
|
+
throw decodeError("FLOW.QUERY records must be an array of at most 100 maps", value);
|
|
13203
|
+
}
|
|
13204
|
+
const records = new Array(rawRecords.length);
|
|
13205
|
+
for (let index = 0; index < rawRecords.length; index += 1) {
|
|
13206
|
+
if (!Object.hasOwn(rawRecords, index)) {
|
|
13207
|
+
throw decodeError("FLOW.QUERY records must be a dense array", value);
|
|
13208
|
+
}
|
|
13209
|
+
records[index] = decodeRecord(
|
|
13210
|
+
requiredMap2(rawRecords[index], `FLOW.QUERY record ${index}`),
|
|
13211
|
+
index
|
|
13212
|
+
);
|
|
13213
|
+
}
|
|
13214
|
+
if (usage.resultRecords !== records.length || usage.resultRecords > usage.scannedEntries) {
|
|
13215
|
+
throw decodeError("FLOW.QUERY usage result_records is inconsistent with records", value);
|
|
13216
|
+
}
|
|
13217
|
+
return {
|
|
13218
|
+
kind: "records",
|
|
13219
|
+
records,
|
|
13220
|
+
page: decodePage(field(mapping2, "page")),
|
|
13221
|
+
quality,
|
|
13222
|
+
usage,
|
|
13223
|
+
raw
|
|
13224
|
+
};
|
|
13225
|
+
}
|
|
13226
|
+
if (hasKey(mapping2, "page")) {
|
|
13227
|
+
throw decodeError("FLOW.QUERY count result contains an unexpected page", value);
|
|
13228
|
+
}
|
|
13229
|
+
const countResult = requiredMap2(field(mapping2, "result"), "FLOW.QUERY count result");
|
|
13230
|
+
if (requiredText2(countResult, "kind", "FLOW.QUERY count result") !== "count") {
|
|
13231
|
+
throw decodeError("FLOW.QUERY count result kind must be count", value);
|
|
13232
|
+
}
|
|
13233
|
+
const count = boundedInteger(
|
|
13234
|
+
field(countResult, "value"),
|
|
13235
|
+
MAX_SIGNED_643,
|
|
13236
|
+
"FLOW.QUERY count value"
|
|
13237
|
+
);
|
|
13238
|
+
if (usage.resultRecords !== 1) {
|
|
13239
|
+
throw decodeError("FLOW.QUERY count usage result_records must be 1", value);
|
|
13240
|
+
}
|
|
13241
|
+
return {
|
|
13242
|
+
kind: "count",
|
|
13243
|
+
count,
|
|
13244
|
+
quality,
|
|
13245
|
+
usage,
|
|
13246
|
+
raw
|
|
13247
|
+
};
|
|
13248
|
+
}
|
|
13249
|
+
function decodeFlowExplainResult(value) {
|
|
13250
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY explain");
|
|
13251
|
+
requireContract(mapping2, "version", FLOW_EXPLAIN_CONTRACT, "FLOW.QUERY explain");
|
|
13252
|
+
const queryFingerprint = requiredText2(mapping2, "query_fingerprint", "FLOW.QUERY explain");
|
|
13253
|
+
if (!/^[0-9a-f]{64}$/iu.test(queryFingerprint)) {
|
|
13254
|
+
throw decodeError("FLOW.QUERY explain query_fingerprint is invalid", value);
|
|
13255
|
+
}
|
|
13256
|
+
const status = requiredText2(mapping2, "status", "FLOW.QUERY explain");
|
|
13257
|
+
if (status !== "planned" && status !== "rejected" && status !== "executed") {
|
|
13258
|
+
throw decodeError(`FLOW.QUERY explain status ${JSON.stringify(status)} is unsupported`, value);
|
|
13259
|
+
}
|
|
13260
|
+
const plan = freezeMetadataMap(
|
|
13261
|
+
requiredMap2(field(mapping2, "plan"), "FLOW.QUERY explain plan"),
|
|
13262
|
+
"FLOW.QUERY explain plan"
|
|
13263
|
+
);
|
|
13264
|
+
const estimate = freezeMetadataMap(
|
|
13265
|
+
requiredMap2(field(mapping2, "estimate"), "FLOW.QUERY explain estimate"),
|
|
13266
|
+
"FLOW.QUERY explain estimate"
|
|
13267
|
+
);
|
|
13268
|
+
const bounds = freezeMetadataMap(
|
|
13269
|
+
requiredMap2(field(mapping2, "bounds"), "FLOW.QUERY explain bounds"),
|
|
13270
|
+
"FLOW.QUERY explain bounds"
|
|
13271
|
+
);
|
|
13272
|
+
const capabilities = decodeExplainCapabilities(mapping2);
|
|
13273
|
+
const extendedFields = ["stats", "quality", "pressure", "decision", "alternatives"];
|
|
13274
|
+
const extendedPresence = extendedFields.map((name) => hasKey(mapping2, name));
|
|
13275
|
+
const specialized = capabilities != null && !extendedPresence.some(Boolean);
|
|
13276
|
+
let stats;
|
|
13277
|
+
let quality;
|
|
13278
|
+
let pressure;
|
|
13279
|
+
let decision;
|
|
13280
|
+
let alternatives;
|
|
13281
|
+
if (specialized) {
|
|
13282
|
+
if (status !== "planned") {
|
|
13283
|
+
throw decodeError("FLOW.QUERY specialized explain must be planned", value);
|
|
13284
|
+
}
|
|
13285
|
+
if (hasKey(mapping2, "actual") || hasKey(mapping2, "diagnostic")) {
|
|
13286
|
+
throw decodeError("FLOW.QUERY specialized explain has extended status fields", value);
|
|
13287
|
+
}
|
|
13288
|
+
alternatives = Object.freeze([]);
|
|
13289
|
+
} else {
|
|
13290
|
+
if (!extendedPresence.every(Boolean) || !hasKey(mapping2, "actual") || !hasKey(mapping2, "diagnostic")) {
|
|
13291
|
+
throw decodeError("FLOW.QUERY explain is missing required v1 fields", value);
|
|
13292
|
+
}
|
|
13293
|
+
stats = freezeMetadataMap(
|
|
13294
|
+
requiredMap2(field(mapping2, "stats"), "FLOW.QUERY explain stats"),
|
|
13295
|
+
"FLOW.QUERY explain stats"
|
|
13296
|
+
);
|
|
13297
|
+
quality = decodeQuality(field(mapping2, "quality"));
|
|
13298
|
+
pressure = freezeMetadataMap(
|
|
13299
|
+
requiredMap2(field(mapping2, "pressure"), "FLOW.QUERY explain pressure"),
|
|
13300
|
+
"FLOW.QUERY explain pressure"
|
|
13301
|
+
);
|
|
13302
|
+
decision = freezeMetadataMap(
|
|
13303
|
+
requiredMap2(field(mapping2, "decision"), "FLOW.QUERY explain decision"),
|
|
13304
|
+
"FLOW.QUERY explain decision"
|
|
13305
|
+
);
|
|
13306
|
+
alternatives = decodeExplainAlternatives(field(mapping2, "alternatives"));
|
|
13307
|
+
}
|
|
13308
|
+
const actualValue = field(mapping2, "actual");
|
|
13309
|
+
let actual;
|
|
13310
|
+
if (status === "executed") {
|
|
13311
|
+
if (actualValue == null) {
|
|
13312
|
+
throw decodeError("FLOW.QUERY executed explain is missing actual usage", value);
|
|
13313
|
+
}
|
|
13314
|
+
actual = decodeUsage(actualValue);
|
|
13315
|
+
} else if (actualValue != null) {
|
|
13316
|
+
throw decodeError("FLOW.QUERY non-executed explain contains actual usage", value);
|
|
13317
|
+
}
|
|
13318
|
+
const diagnosticValue = field(mapping2, "diagnostic");
|
|
13319
|
+
let diagnostic;
|
|
13320
|
+
if (status === "rejected") {
|
|
13321
|
+
diagnostic = tryDecodeFlowQueryError(diagnosticValue);
|
|
13322
|
+
if (diagnostic == null) {
|
|
13323
|
+
throw decodeError("FLOW.QUERY rejected explain has an invalid diagnostic", value);
|
|
13324
|
+
}
|
|
13325
|
+
} else if (diagnosticValue != null) {
|
|
13326
|
+
throw decodeError("FLOW.QUERY non-rejected explain contains a diagnostic", value);
|
|
13327
|
+
}
|
|
13328
|
+
return Object.freeze({
|
|
13329
|
+
version: FLOW_EXPLAIN_CONTRACT,
|
|
13330
|
+
queryFingerprint,
|
|
13331
|
+
status,
|
|
13332
|
+
plan,
|
|
13333
|
+
estimate,
|
|
13334
|
+
stats,
|
|
13335
|
+
quality,
|
|
13336
|
+
bounds,
|
|
13337
|
+
pressure,
|
|
13338
|
+
decision,
|
|
13339
|
+
alternatives,
|
|
13340
|
+
capabilities,
|
|
13341
|
+
actual,
|
|
13342
|
+
diagnostic,
|
|
13343
|
+
raw: freezeMap(mapping2)
|
|
12241
13344
|
});
|
|
12242
13345
|
}
|
|
12243
|
-
function
|
|
12244
|
-
|
|
13346
|
+
function decodeExplainCapabilities(mapping2) {
|
|
13347
|
+
if (!hasKey(mapping2, "capabilities")) return void 0;
|
|
13348
|
+
const value = requiredMap2(
|
|
13349
|
+
field(mapping2, "capabilities"),
|
|
13350
|
+
"FLOW.QUERY explain capabilities"
|
|
13351
|
+
);
|
|
12245
13352
|
return Object.freeze({
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
"FLOW.QUERY.INDEXES index version"
|
|
12251
|
-
),
|
|
12252
|
-
buildId: requiredText2(mapping, "build_id", "FLOW.QUERY.INDEXES index"),
|
|
12253
|
-
state: requiredText2(mapping, "state", "FLOW.QUERY.INDEXES index"),
|
|
12254
|
-
queryable: requiredBoolean(mapping, "queryable", "FLOW.QUERY.INDEXES index"),
|
|
12255
|
-
coveringFields: decodeCoveringFields(mapping),
|
|
12256
|
-
format: decodeIndexFormat(mapping),
|
|
12257
|
-
raw: freezeMap(mapping)
|
|
13353
|
+
requested: decodeExplainCapabilityList(field(value, "requested"), "requested"),
|
|
13354
|
+
available: decodeExplainCapabilityList(field(value, "available"), "available"),
|
|
13355
|
+
missing: decodeExplainCapabilityList(field(value, "missing"), "missing"),
|
|
13356
|
+
raw: freezeMetadataMap(value, "FLOW.QUERY explain capabilities")
|
|
12258
13357
|
});
|
|
12259
13358
|
}
|
|
12260
|
-
function
|
|
12261
|
-
|
|
12262
|
-
if (!Array.isArray(raw) || raw.length > 32) {
|
|
13359
|
+
function decodeExplainCapabilityList(value, name) {
|
|
13360
|
+
if (!Array.isArray(value) || value.length > 64) {
|
|
12263
13361
|
throw decodeError(
|
|
12264
|
-
|
|
12265
|
-
|
|
13362
|
+
`FLOW.QUERY explain capabilities ${name} must contain at most 64 entries`,
|
|
13363
|
+
value
|
|
12266
13364
|
);
|
|
12267
13365
|
}
|
|
12268
|
-
const
|
|
13366
|
+
const result = new Array(value.length);
|
|
12269
13367
|
const seen = /* @__PURE__ */ new Set();
|
|
12270
|
-
for (let index = 0; index <
|
|
12271
|
-
if (!Object.hasOwn(
|
|
12272
|
-
throw decodeError(
|
|
13368
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
13369
|
+
if (!Object.hasOwn(value, index)) {
|
|
13370
|
+
throw decodeError(`FLOW.QUERY explain capabilities ${name} must be dense`, value);
|
|
12273
13371
|
}
|
|
12274
|
-
const
|
|
12275
|
-
|
|
12276
|
-
`FLOW.QUERY
|
|
12277
|
-
|
|
13372
|
+
const capability = boundedText(
|
|
13373
|
+
value[index],
|
|
13374
|
+
`FLOW.QUERY explain capabilities ${name} entry ${index}`,
|
|
13375
|
+
128
|
|
12278
13376
|
);
|
|
12279
|
-
if (seen.has(
|
|
12280
|
-
throw decodeError(
|
|
13377
|
+
if (seen.has(capability)) {
|
|
13378
|
+
throw decodeError(
|
|
13379
|
+
`FLOW.QUERY explain capabilities ${name} contains duplicates`,
|
|
13380
|
+
value
|
|
13381
|
+
);
|
|
12281
13382
|
}
|
|
12282
|
-
seen.add(
|
|
12283
|
-
|
|
13383
|
+
seen.add(capability);
|
|
13384
|
+
result[index] = capability;
|
|
12284
13385
|
}
|
|
12285
|
-
return Object.freeze(
|
|
13386
|
+
return Object.freeze(result);
|
|
12286
13387
|
}
|
|
12287
|
-
function
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
throw decodeError("FLOW.QUERY.INDEXES index format counter is missing", raw);
|
|
13388
|
+
function decodeExplainAlternatives(value) {
|
|
13389
|
+
if (!Array.isArray(value) || value.length > 31) {
|
|
13390
|
+
throw decodeError("FLOW.QUERY explain alternatives must be an array of at most 31 maps", value);
|
|
12291
13391
|
}
|
|
12292
|
-
const
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
}
|
|
13392
|
+
const alternatives = new Array(value.length);
|
|
13393
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
13394
|
+
if (!Object.hasOwn(value, index)) {
|
|
13395
|
+
throw decodeError("FLOW.QUERY explain alternatives must be dense", value);
|
|
13396
|
+
}
|
|
13397
|
+
alternatives[index] = freezeMetadataMap(
|
|
13398
|
+
requiredMap2(value[index], `FLOW.QUERY explain alternative ${index}`),
|
|
13399
|
+
`FLOW.QUERY explain alternative ${index}`
|
|
13400
|
+
);
|
|
13401
|
+
}
|
|
13402
|
+
return Object.freeze(alternatives);
|
|
12302
13403
|
}
|
|
12303
13404
|
function decodeQuality(value) {
|
|
12304
|
-
const
|
|
13405
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY quality");
|
|
12305
13406
|
return Object.freeze({
|
|
12306
|
-
exactness:
|
|
12307
|
-
freshness:
|
|
12308
|
-
coverage:
|
|
12309
|
-
pagination:
|
|
13407
|
+
exactness: decodeQualityValue(mapping2, "exactness", QUALITY_VALUES2.exactness),
|
|
13408
|
+
freshness: decodeQualityValue(mapping2, "freshness", QUALITY_VALUES2.freshness),
|
|
13409
|
+
coverage: decodeQualityValue(mapping2, "coverage", QUALITY_VALUES2.coverage),
|
|
13410
|
+
pagination: decodeQualityValue(mapping2, "pagination", QUALITY_VALUES2.pagination)
|
|
12310
13411
|
});
|
|
12311
13412
|
}
|
|
13413
|
+
function decodeQualityValue(mapping2, name, allowed) {
|
|
13414
|
+
const value = requiredBoundedText2(mapping2, name, "FLOW.QUERY quality", 64);
|
|
13415
|
+
if (!allowed.includes(value)) {
|
|
13416
|
+
throw decodeError(`FLOW.QUERY quality ${name} is unsupported`, mapping2);
|
|
13417
|
+
}
|
|
13418
|
+
return value;
|
|
13419
|
+
}
|
|
12312
13420
|
function decodeUsage(value) {
|
|
12313
|
-
const
|
|
13421
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY usage");
|
|
12314
13422
|
const usage = /* @__PURE__ */ Object.create(null);
|
|
12315
13423
|
for (const [wireName, property] of USAGE_FIELDS2) {
|
|
12316
13424
|
usage[property] = nonNegativeInteger(
|
|
12317
|
-
field(
|
|
13425
|
+
field(mapping2, wireName),
|
|
12318
13426
|
`FLOW.QUERY usage ${wireName}`
|
|
12319
13427
|
);
|
|
12320
13428
|
}
|
|
12321
|
-
|
|
13429
|
+
const decoded = usage;
|
|
13430
|
+
if (decoded.hydratedRecords > decoded.scannedEntries || decoded.duplicateEntries > decoded.scannedEntries || decoded.rangePages > decoded.scannedEntries + decoded.rangeSeeks || decoded.residualChecks > decoded.scannedEntries * 12) {
|
|
13431
|
+
throw decodeError("FLOW.QUERY usage counters are inconsistent", value);
|
|
13432
|
+
}
|
|
13433
|
+
return Object.freeze(decoded);
|
|
12322
13434
|
}
|
|
12323
13435
|
function decodePage(value) {
|
|
12324
|
-
const
|
|
12325
|
-
const hasMore = requiredBoolean(
|
|
12326
|
-
const cursor = optionalText2(
|
|
12327
|
-
if (cursor != null && (!cursor.startsWith("fqc1_") ||
|
|
13436
|
+
const mapping2 = requiredMap2(value, "FLOW.QUERY page");
|
|
13437
|
+
const hasMore = requiredBoolean(mapping2, "has_more", "FLOW.QUERY page");
|
|
13438
|
+
const cursor = optionalText2(mapping2, "cursor", "FLOW.QUERY page");
|
|
13439
|
+
if (cursor != null && (!cursor.startsWith("fqc1_") || Buffer48.byteLength(cursor) < 16 || Buffer48.byteLength(cursor) > 4096)) {
|
|
12328
13440
|
throw decodeError("FLOW.QUERY page cursor is invalid", value);
|
|
12329
13441
|
}
|
|
12330
13442
|
if (hasMore !== (cursor != null)) {
|
|
@@ -12332,26 +13444,14 @@ function decodePage(value) {
|
|
|
12332
13444
|
}
|
|
12333
13445
|
return Object.freeze({ hasMore, ...cursor == null ? {} : { cursor } });
|
|
12334
13446
|
}
|
|
12335
|
-
function decodePosition(value) {
|
|
12336
|
-
if (value == null) return void 0;
|
|
12337
|
-
const mapping = requiredMap2(value, "FLOW.QUERY diagnostic position");
|
|
12338
|
-
return Object.freeze({
|
|
12339
|
-
byte: positiveInteger(field(mapping, "byte"), "FLOW.QUERY diagnostic position byte"),
|
|
12340
|
-
line: positiveInteger(field(mapping, "line"), "FLOW.QUERY diagnostic position line"),
|
|
12341
|
-
column: positiveInteger(
|
|
12342
|
-
field(mapping, "column"),
|
|
12343
|
-
"FLOW.QUERY diagnostic position column"
|
|
12344
|
-
)
|
|
12345
|
-
});
|
|
12346
|
-
}
|
|
12347
13447
|
|
|
12348
13448
|
// src/client-core.ts
|
|
12349
13449
|
import "buffer";
|
|
12350
13450
|
|
|
12351
13451
|
// src/client-core-helpers.ts
|
|
12352
|
-
import { Buffer as
|
|
13452
|
+
import { Buffer as Buffer49 } from "buffer";
|
|
12353
13453
|
function bgsaveResponse(response) {
|
|
12354
|
-
if ((typeof response === "string" ||
|
|
13454
|
+
if ((typeof response === "string" || Buffer49.isBuffer(response) || response instanceof Uint8Array) && text(response) === "Background saving started") {
|
|
12355
13455
|
return true;
|
|
12356
13456
|
}
|
|
12357
13457
|
return okResponse(response);
|
|
@@ -12365,7 +13465,7 @@ function fetchOrComputeCompletionToken(options) {
|
|
|
12365
13465
|
"fetch-or-compute completion requires computeToken"
|
|
12366
13466
|
);
|
|
12367
13467
|
}
|
|
12368
|
-
if (!
|
|
13468
|
+
if (!Buffer49.isBuffer(options.computeToken)) {
|
|
12369
13469
|
throw new TypeError("fetch-or-compute computeToken must be a Buffer");
|
|
12370
13470
|
}
|
|
12371
13471
|
return options.computeToken;
|
|
@@ -12451,12 +13551,12 @@ var ErrorMappingExecutor = class {
|
|
|
12451
13551
|
|
|
12452
13552
|
// src/client-config.ts
|
|
12453
13553
|
function snapshotClientOptions(options) {
|
|
12454
|
-
const
|
|
13554
|
+
const own2 = { ...options };
|
|
12455
13555
|
const snapshot = {
|
|
12456
|
-
...
|
|
12457
|
-
...typeof
|
|
12458
|
-
...
|
|
12459
|
-
...typeof
|
|
13556
|
+
...own2,
|
|
13557
|
+
...typeof own2.autoBatch === "object" && own2.autoBatch != null ? { autoBatch: Object.freeze({ ...own2.autoBatch }) } : {},
|
|
13558
|
+
...own2.backpressure == null ? {} : { backpressure: Object.freeze({ ...own2.backpressure }) },
|
|
13559
|
+
...typeof own2.reconnect === "object" && own2.reconnect != null ? { reconnect: Object.freeze({ ...own2.reconnect }) } : {}
|
|
12460
13560
|
};
|
|
12461
13561
|
return Object.freeze(snapshot);
|
|
12462
13562
|
}
|
|
@@ -12725,14 +13825,14 @@ var FerricStoreAdministrationClient = class extends FerricStoreClientBase {
|
|
|
12725
13825
|
};
|
|
12726
13826
|
|
|
12727
13827
|
// src/store-utilities.ts
|
|
12728
|
-
import { Buffer as
|
|
13828
|
+
import { Buffer as Buffer52 } from "buffer";
|
|
12729
13829
|
function encode(codec, value) {
|
|
12730
13830
|
return codec.encode(value);
|
|
12731
13831
|
}
|
|
12732
13832
|
function decode(codec, value) {
|
|
12733
13833
|
if (value == null) return null;
|
|
12734
|
-
if (
|
|
12735
|
-
if (value instanceof Uint8Array) return codec.decode(
|
|
13834
|
+
if (Buffer52.isBuffer(value)) return codec.decode(value);
|
|
13835
|
+
if (value instanceof Uint8Array) return codec.decode(Buffer52.from(value));
|
|
12736
13836
|
return value;
|
|
12737
13837
|
}
|
|
12738
13838
|
function number(value) {
|
|
@@ -14645,33 +15745,511 @@ var FerricStoreClientCore = class extends FerricStoreAdministrationClient {
|
|
|
14645
15745
|
}
|
|
14646
15746
|
};
|
|
14647
15747
|
|
|
14648
|
-
// src/
|
|
14649
|
-
var
|
|
14650
|
-
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
15748
|
+
// src/schedule-request.ts
|
|
15749
|
+
var MAX_EXACT_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
15750
|
+
var SCHEDULE_KINDS = /* @__PURE__ */ new Set(["one_shot", "delay", "interval", "cron"]);
|
|
15751
|
+
var RECURRING_KINDS = /* @__PURE__ */ new Set(["interval", "cron"]);
|
|
15752
|
+
var OVERLAP_POLICIES = /* @__PURE__ */ new Set([
|
|
15753
|
+
"allow",
|
|
15754
|
+
"skip",
|
|
15755
|
+
"queue_after_previous",
|
|
15756
|
+
"fail_schedule"
|
|
15757
|
+
]);
|
|
15758
|
+
var SCHEDULE_STATES = /* @__PURE__ */ new Set([
|
|
15759
|
+
"active",
|
|
15760
|
+
"paused",
|
|
15761
|
+
"running",
|
|
15762
|
+
"completed",
|
|
15763
|
+
"failed",
|
|
15764
|
+
"cancelled",
|
|
15765
|
+
"all"
|
|
15766
|
+
]);
|
|
15767
|
+
var TARGET_FIELDS = /* @__PURE__ */ new Set([
|
|
15768
|
+
"correlation_id",
|
|
15769
|
+
"id",
|
|
15770
|
+
"id_prefix",
|
|
15771
|
+
"parent_flow_id",
|
|
15772
|
+
"partition_key",
|
|
15773
|
+
"payload",
|
|
15774
|
+
"payload_ref",
|
|
15775
|
+
"priority",
|
|
15776
|
+
"root_flow_id",
|
|
15777
|
+
"run_at_ms",
|
|
15778
|
+
"state",
|
|
15779
|
+
"type",
|
|
15780
|
+
"value_refs",
|
|
15781
|
+
"values"
|
|
15782
|
+
]);
|
|
15783
|
+
var TARGET_TEXT_FIELDS = [
|
|
15784
|
+
"state",
|
|
15785
|
+
"id",
|
|
15786
|
+
"id_prefix",
|
|
15787
|
+
"partition_key",
|
|
15788
|
+
"correlation_id",
|
|
15789
|
+
"parent_flow_id",
|
|
15790
|
+
"root_flow_id",
|
|
15791
|
+
"payload_ref"
|
|
15792
|
+
];
|
|
15793
|
+
var INTERNAL_SCHEDULE_TYPE = "__ferricstore_schedule";
|
|
15794
|
+
var INTERNAL_SCHEDULE_ID_PREFIX = "__ferricstore_schedule__:";
|
|
15795
|
+
function validateScheduleCreate(id, options) {
|
|
15796
|
+
requiredText3(id, "id");
|
|
15797
|
+
if (!mapping(options)) throw new TypeError("schedule options must be a mapping");
|
|
15798
|
+
const kind = effectiveKind(options);
|
|
15799
|
+
validateTiming(kind, options);
|
|
15800
|
+
const recurring = RECURRING_KINDS.has(kind);
|
|
15801
|
+
validateTarget(options.target, recurring);
|
|
15802
|
+
nonNegativeInteger2(options.atMs, "at_ms");
|
|
15803
|
+
nonNegativeInteger2(options.delayMs, "delay_ms");
|
|
15804
|
+
nonNegativeInteger2(options.startAtMs, "start_at_ms");
|
|
15805
|
+
positiveInteger2(options.everyMs, "every_ms");
|
|
15806
|
+
positiveInteger2(options.overlapRetryMs, "overlap_retry_ms");
|
|
15807
|
+
positiveInteger2(options.maxFires, "max_fires");
|
|
15808
|
+
nonNegativeInteger2(options.endAtMs, "end_at_ms");
|
|
15809
|
+
nonNegativeInteger2(options.nowMs, "now_ms");
|
|
15810
|
+
optionalBoolean(options.overwrite, "overwrite");
|
|
15811
|
+
if (kind === "delay") {
|
|
15812
|
+
if (options.delayMs == null) throw new TypeError("delay_ms is required for delay schedules");
|
|
15813
|
+
if (options.nowMs != null && options.nowMs > MAX_EXACT_INTEGER - options.delayMs) {
|
|
15814
|
+
throw new TypeError("now_ms plus delay_ms exceeds the exact integer range");
|
|
15815
|
+
}
|
|
15816
|
+
}
|
|
15817
|
+
if (kind === "interval" && options.everyMs == null) {
|
|
15818
|
+
throw new TypeError("every_ms is required for interval schedules");
|
|
15819
|
+
}
|
|
15820
|
+
if (kind === "cron") {
|
|
15821
|
+
requiredText3(options.cron, "cron");
|
|
15822
|
+
optionalText3(options.timezone, "timezone");
|
|
15823
|
+
} else if (options.timezone != null) {
|
|
15824
|
+
throw new TypeError("timezone is only supported for cron schedules");
|
|
15825
|
+
}
|
|
15826
|
+
if (kind === "interval") {
|
|
15827
|
+
if (options.catchupPolicy != null && options.catchupPolicy !== "fire_once") {
|
|
15828
|
+
throw new TypeError("catchup_policy must be fire_once");
|
|
15829
|
+
}
|
|
15830
|
+
} else if (options.catchupPolicy != null) {
|
|
15831
|
+
throw new TypeError("catchup_policy is only supported for interval schedules");
|
|
15832
|
+
}
|
|
15833
|
+
if (recurring) {
|
|
15834
|
+
if (options.overlapPolicy != null && !OVERLAP_POLICIES.has(options.overlapPolicy)) {
|
|
15835
|
+
throw new TypeError(
|
|
15836
|
+
"overlap_policy must be allow, skip, queue_after_previous, or fail_schedule"
|
|
15837
|
+
);
|
|
15838
|
+
}
|
|
15839
|
+
if (options.overlapRetryMs != null && options.overlapPolicy !== "queue_after_previous") {
|
|
15840
|
+
throw new TypeError("overlap_retry_ms requires overlap_policy queue_after_previous");
|
|
15841
|
+
}
|
|
15842
|
+
const firstRun = knownFirstRun(kind, options);
|
|
15843
|
+
if (options.endAtMs != null && firstRun != null && options.endAtMs < firstRun) {
|
|
15844
|
+
throw new TypeError("end_at_ms must be at or after first run");
|
|
15845
|
+
}
|
|
15846
|
+
} else {
|
|
15847
|
+
if (options.overlapPolicy != null) {
|
|
15848
|
+
throw new TypeError("overlap_policy is only supported for recurring schedules");
|
|
15849
|
+
}
|
|
15850
|
+
if (options.overlapRetryMs != null) {
|
|
15851
|
+
throw new TypeError("overlap_retry_ms requires overlap_policy queue_after_previous");
|
|
15852
|
+
}
|
|
15853
|
+
if (options.maxFires != null) {
|
|
15854
|
+
throw new TypeError("max_fires is only supported for recurring schedules");
|
|
15855
|
+
}
|
|
15856
|
+
if (options.endAtMs != null) {
|
|
15857
|
+
throw new TypeError("end_at_ms is only supported for recurring schedules");
|
|
15858
|
+
}
|
|
14658
15859
|
}
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
15860
|
+
}
|
|
15861
|
+
function validateScheduleID(id) {
|
|
15862
|
+
requiredText3(id, "id");
|
|
15863
|
+
}
|
|
15864
|
+
function validateScheduleFire(id, options) {
|
|
15865
|
+
validateScheduleID(id);
|
|
15866
|
+
nonNegativeInteger2(options.fireAtMs, "fire_at_ms");
|
|
15867
|
+
nonNegativeInteger2(options.nowMs, "now_ms");
|
|
15868
|
+
}
|
|
15869
|
+
function validateScheduleStatus(id, nowMs2) {
|
|
15870
|
+
validateScheduleID(id);
|
|
15871
|
+
nonNegativeInteger2(nowMs2, "now_ms");
|
|
15872
|
+
}
|
|
15873
|
+
function validateScheduleFireDue(options) {
|
|
15874
|
+
if (!mapping(options)) throw new TypeError("schedule fire-due options must be a mapping");
|
|
15875
|
+
nonNegativeInteger2(options.nowMs, "now_ms");
|
|
15876
|
+
optionalText3(options.worker, "worker");
|
|
15877
|
+
positiveInteger2(options.leaseMs, "lease_ms");
|
|
15878
|
+
nonNegativeInteger2(options.blockMs, "block_ms");
|
|
15879
|
+
positiveInteger2(options.limit, "limit");
|
|
15880
|
+
const leaseMs = options.leaseMs ?? 3e4;
|
|
15881
|
+
if (options.nowMs != null && options.nowMs > MAX_EXACT_INTEGER - leaseMs) {
|
|
15882
|
+
throw new TypeError("now_ms plus lease_ms exceeds the exact integer range");
|
|
14663
15883
|
}
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
15884
|
+
}
|
|
15885
|
+
function validateScheduleList(options) {
|
|
15886
|
+
if (!mapping(options)) throw new TypeError("schedule list options must be a mapping");
|
|
15887
|
+
if (options.kind != null && !SCHEDULE_KINDS.has(options.kind)) {
|
|
15888
|
+
throw new TypeError("kind must be one_shot, delay, interval, or cron");
|
|
14668
15889
|
}
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
15890
|
+
if (options.state != null && !SCHEDULE_STATES.has(options.state)) {
|
|
15891
|
+
throw new TypeError("state is invalid");
|
|
15892
|
+
}
|
|
15893
|
+
optionalText3(options.timezone, "timezone");
|
|
15894
|
+
optionalText3(options.targetType, "target_type");
|
|
15895
|
+
nonNegativeInteger2(options.fromMs, "from_ms");
|
|
15896
|
+
nonNegativeInteger2(options.toMs, "to_ms");
|
|
15897
|
+
if (options.fromMs != null && options.toMs != null && options.fromMs > options.toMs) {
|
|
15898
|
+
throw new TypeError("from_ms must not exceed to_ms");
|
|
15899
|
+
}
|
|
15900
|
+
positiveInteger2(options.count, "count");
|
|
15901
|
+
optionalBoolean(options.rev, "rev");
|
|
15902
|
+
}
|
|
15903
|
+
function effectiveKind(options) {
|
|
15904
|
+
if (options.kind != null) {
|
|
15905
|
+
if (!SCHEDULE_KINDS.has(options.kind)) {
|
|
15906
|
+
throw new TypeError("kind must be one_shot, delay, interval, or cron");
|
|
15907
|
+
}
|
|
15908
|
+
return options.kind;
|
|
15909
|
+
}
|
|
15910
|
+
if (options.cron != null) return "cron";
|
|
15911
|
+
if (options.everyMs != null) return "interval";
|
|
15912
|
+
if (options.delayMs != null) return "delay";
|
|
15913
|
+
return "one_shot";
|
|
15914
|
+
}
|
|
15915
|
+
function validateTiming(kind, options) {
|
|
15916
|
+
if (options.atMs != null && options.startAtMs != null) {
|
|
15917
|
+
throw new TypeError("cannot set both at_ms and start_at_ms");
|
|
15918
|
+
}
|
|
15919
|
+
if (options.delayMs != null && kind !== "delay") {
|
|
15920
|
+
throw new TypeError("delay_ms is only supported for delay schedules");
|
|
15921
|
+
}
|
|
15922
|
+
if (options.everyMs != null && kind !== "interval") {
|
|
15923
|
+
throw new TypeError("every_ms is only supported for interval schedules");
|
|
15924
|
+
}
|
|
15925
|
+
if (options.cron != null && kind !== "cron") {
|
|
15926
|
+
throw new TypeError("cron is only supported for cron schedules");
|
|
15927
|
+
}
|
|
15928
|
+
if (kind === "delay" && (options.atMs != null || options.startAtMs != null)) {
|
|
15929
|
+
throw new TypeError("at_ms and start_at_ms are not supported for delay schedules");
|
|
14673
15930
|
}
|
|
15931
|
+
}
|
|
15932
|
+
function validateTarget(value, recurring) {
|
|
15933
|
+
if (!mapping(value)) throw new TypeError("target must be a mapping with a non-empty type");
|
|
15934
|
+
const target = value;
|
|
15935
|
+
for (const key of Object.keys(target)) {
|
|
15936
|
+
if (!TARGET_FIELDS.has(key)) throw new TypeError(`target contains unknown field ${key}`);
|
|
15937
|
+
}
|
|
15938
|
+
const type = own(target, "type");
|
|
15939
|
+
requiredText3(type, "target type");
|
|
15940
|
+
if (type === INTERNAL_SCHEDULE_TYPE) {
|
|
15941
|
+
throw new TypeError("target type is reserved for internal use");
|
|
15942
|
+
}
|
|
15943
|
+
for (const key of TARGET_TEXT_FIELDS) optionalText3(own(target, key), `target ${key}`);
|
|
15944
|
+
const id = own(target, "id");
|
|
15945
|
+
const idPrefix = own(target, "id_prefix");
|
|
15946
|
+
if (id != null && idPrefix != null) {
|
|
15947
|
+
throw new TypeError("target cannot set both id and id_prefix");
|
|
15948
|
+
}
|
|
15949
|
+
if (recurring && id != null) {
|
|
15950
|
+
throw new TypeError("target id is not supported for recurring schedules; use id_prefix");
|
|
15951
|
+
}
|
|
15952
|
+
if (typeof id === "string" && id.startsWith(INTERNAL_SCHEDULE_ID_PREFIX)) {
|
|
15953
|
+
throw new TypeError("target id is reserved for internal use");
|
|
15954
|
+
}
|
|
15955
|
+
if (typeof idPrefix === "string" && idPrefix.startsWith(INTERNAL_SCHEDULE_ID_PREFIX)) {
|
|
15956
|
+
throw new TypeError("target id_prefix is reserved for internal use");
|
|
15957
|
+
}
|
|
15958
|
+
nonNegativeInteger2(own(target, "run_at_ms"), "target run_at_ms");
|
|
15959
|
+
const priority = own(target, "priority");
|
|
15960
|
+
if (priority != null && (typeof priority !== "number" || !Number.isSafeInteger(priority) || priority < 0 || priority > 2)) {
|
|
15961
|
+
throw new TypeError("target priority must be an integer between 0 and 2");
|
|
15962
|
+
}
|
|
15963
|
+
}
|
|
15964
|
+
function knownFirstRun(kind, options) {
|
|
15965
|
+
if (kind === "delay") {
|
|
15966
|
+
if (options.nowMs == null || options.delayMs == null) return void 0;
|
|
15967
|
+
return options.nowMs + options.delayMs;
|
|
15968
|
+
}
|
|
15969
|
+
if (kind === "cron") return options.startAtMs ?? options.atMs ?? options.nowMs;
|
|
15970
|
+
return options.atMs ?? options.startAtMs ?? options.nowMs;
|
|
15971
|
+
}
|
|
15972
|
+
function requiredText3(value, name) {
|
|
15973
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
15974
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
15975
|
+
}
|
|
15976
|
+
}
|
|
15977
|
+
function optionalText3(value, name) {
|
|
15978
|
+
if (value != null) requiredText3(value, name);
|
|
15979
|
+
}
|
|
15980
|
+
function nonNegativeInteger2(value, name) {
|
|
15981
|
+
if (value != null && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)) {
|
|
15982
|
+
throw new TypeError(`${name} must be a non-negative exact integer`);
|
|
15983
|
+
}
|
|
15984
|
+
}
|
|
15985
|
+
function positiveInteger2(value, name) {
|
|
15986
|
+
if (value != null && (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)) {
|
|
15987
|
+
throw new TypeError(`${name} must be a positive exact integer`);
|
|
15988
|
+
}
|
|
15989
|
+
}
|
|
15990
|
+
function optionalBoolean(value, name) {
|
|
15991
|
+
if (value != null && typeof value !== "boolean") {
|
|
15992
|
+
throw new TypeError(`${name} must be a boolean`);
|
|
15993
|
+
}
|
|
15994
|
+
}
|
|
15995
|
+
function mapping(value) {
|
|
15996
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15997
|
+
}
|
|
15998
|
+
function own(mapping2, key) {
|
|
15999
|
+
return Object.prototype.hasOwnProperty.call(mapping2, key) ? mapping2[key] : void 0;
|
|
16000
|
+
}
|
|
16001
|
+
|
|
16002
|
+
// src/schedule-response.ts
|
|
16003
|
+
var SCHEDULE_INTEGER_FIELDS = [
|
|
16004
|
+
"end_at_ms",
|
|
16005
|
+
"last_catchup_at_ms",
|
|
16006
|
+
"last_fire_at_ms",
|
|
16007
|
+
"last_overlap_at_ms",
|
|
16008
|
+
"last_skipped_at_ms",
|
|
16009
|
+
"max_fires",
|
|
16010
|
+
"next_run_at_ms",
|
|
16011
|
+
"overlap_queued_due_at_ms"
|
|
16012
|
+
];
|
|
16013
|
+
var REQUIRED_SCHEDULE_INTEGER_FIELDS = [
|
|
16014
|
+
"attempts",
|
|
16015
|
+
"coalesced_count",
|
|
16016
|
+
"fire_count",
|
|
16017
|
+
"last_coalesced_count",
|
|
16018
|
+
"skipped_count"
|
|
16019
|
+
];
|
|
16020
|
+
var OPTIONAL_SCHEDULE_TEXT_FIELDS = [
|
|
16021
|
+
"cron",
|
|
16022
|
+
"end_reason",
|
|
16023
|
+
"flow_id",
|
|
16024
|
+
"last_overlap_reason",
|
|
16025
|
+
"last_overlap_target_id",
|
|
16026
|
+
"last_planning_error",
|
|
16027
|
+
"last_target_id",
|
|
16028
|
+
"timezone"
|
|
16029
|
+
];
|
|
16030
|
+
var SCHEDULE_KINDS2 = /* @__PURE__ */ new Set(["one_shot", "delay", "interval", "cron"]);
|
|
16031
|
+
var SCHEDULE_STATES2 = /* @__PURE__ */ new Set([
|
|
16032
|
+
"active",
|
|
16033
|
+
"paused",
|
|
16034
|
+
"running",
|
|
16035
|
+
"completed",
|
|
16036
|
+
"failed",
|
|
16037
|
+
"cancelled"
|
|
16038
|
+
]);
|
|
16039
|
+
var SCHEDULE_OVERLAP_POLICIES = /* @__PURE__ */ new Set([
|
|
16040
|
+
"allow",
|
|
16041
|
+
"skip",
|
|
16042
|
+
"queue_after_previous",
|
|
16043
|
+
"fail_schedule"
|
|
16044
|
+
]);
|
|
16045
|
+
function scheduleRecordResponse(value, context) {
|
|
16046
|
+
const record = adminRecordResponse(value, context);
|
|
16047
|
+
if (typeof record.id !== "string" || record.id.length === 0) {
|
|
16048
|
+
throw new TypeError(`${context} response missing id`);
|
|
16049
|
+
}
|
|
16050
|
+
if (typeof record.kind !== "string" || !SCHEDULE_KINDS2.has(record.kind)) {
|
|
16051
|
+
throw new TypeError(`${context} response missing or invalid kind`);
|
|
16052
|
+
}
|
|
16053
|
+
if (typeof record.state !== "string" || !SCHEDULE_STATES2.has(record.state)) {
|
|
16054
|
+
throw new TypeError(`${context} response missing or invalid state`);
|
|
16055
|
+
}
|
|
16056
|
+
if (typeof record.target !== "object" || record.target == null || Array.isArray(record.target)) {
|
|
16057
|
+
throw new TypeError(`${context} response missing or invalid target`);
|
|
16058
|
+
}
|
|
16059
|
+
const target = record.target;
|
|
16060
|
+
if (typeof target.type !== "string" || target.type.length === 0) {
|
|
16061
|
+
throw new TypeError(`${context} response missing or invalid target type`);
|
|
16062
|
+
}
|
|
16063
|
+
if (!("catchup_policy" in record) || record.kind === "interval" && record.catchup_policy !== "fire_once" || record.kind !== "interval" && record.catchup_policy != null) {
|
|
16064
|
+
throw new TypeError(`${context} returned an invalid catchup_policy`);
|
|
16065
|
+
}
|
|
16066
|
+
if (typeof record.overlap_policy !== "string" || !SCHEDULE_OVERLAP_POLICIES.has(record.overlap_policy)) {
|
|
16067
|
+
throw new TypeError(`${context} response missing or invalid overlap_policy`);
|
|
16068
|
+
}
|
|
16069
|
+
safeNonNegativeInteger(record, "created_at_ms", context, true);
|
|
16070
|
+
const everyMs = requiredNullableNonNegativeInteger(record, "every_ms", context);
|
|
16071
|
+
const cron = requiredNullableResponseText(record, "cron", context);
|
|
16072
|
+
const timezone = requiredNullableResponseText(record, "timezone", context);
|
|
16073
|
+
const overlapRetryMs = requiredNullableNonNegativeInteger(
|
|
16074
|
+
record,
|
|
16075
|
+
"overlap_retry_ms",
|
|
16076
|
+
context
|
|
16077
|
+
);
|
|
16078
|
+
validateRecurrence(record, context, everyMs, cron, timezone, overlapRetryMs);
|
|
16079
|
+
let coalescedCount = 0;
|
|
16080
|
+
let lastCoalescedCount = 0;
|
|
16081
|
+
for (const field3 of REQUIRED_SCHEDULE_INTEGER_FIELDS) {
|
|
16082
|
+
const value2 = safeNonNegativeInteger(record, field3, context, true);
|
|
16083
|
+
if (field3 === "coalesced_count") coalescedCount = value2;
|
|
16084
|
+
if (field3 === "last_coalesced_count") lastCoalescedCount = value2;
|
|
16085
|
+
}
|
|
16086
|
+
for (const field3 of SCHEDULE_INTEGER_FIELDS) {
|
|
16087
|
+
if (record[field3] != null) safeNonNegativeInteger(record, field3, context);
|
|
16088
|
+
}
|
|
16089
|
+
for (const field3 of OPTIONAL_SCHEDULE_TEXT_FIELDS) {
|
|
16090
|
+
optionalResponseText(record, field3, context);
|
|
16091
|
+
}
|
|
16092
|
+
validateCatchupState(record, context, coalescedCount, lastCoalescedCount);
|
|
16093
|
+
return record;
|
|
16094
|
+
}
|
|
16095
|
+
function validateRecurrence(record, context, everyMs, cron, timezone, overlapRetryMs) {
|
|
16096
|
+
if (record.kind === "interval") {
|
|
16097
|
+
if (everyMs == null || everyMs <= 0) {
|
|
16098
|
+
throw new TypeError(`${context} interval every_ms must be positive`);
|
|
16099
|
+
}
|
|
16100
|
+
} else if (everyMs != null) {
|
|
16101
|
+
throw new TypeError(`${context} every_ms is only valid for interval schedules`);
|
|
16102
|
+
}
|
|
16103
|
+
if (record.kind === "cron") {
|
|
16104
|
+
if (cron == null) throw new TypeError(`${context} cron schedule is missing cron`);
|
|
16105
|
+
if (timezone == null) throw new TypeError(`${context} cron schedule is missing timezone`);
|
|
16106
|
+
} else if (cron != null) {
|
|
16107
|
+
throw new TypeError(`${context} cron is only valid for cron schedules`);
|
|
16108
|
+
} else if (timezone != null) {
|
|
16109
|
+
throw new TypeError(`${context} timezone is only valid for cron schedules`);
|
|
16110
|
+
}
|
|
16111
|
+
if (record.kind !== "interval" && record.kind !== "cron" && record.overlap_policy !== "allow") {
|
|
16112
|
+
throw new TypeError(`${context} overlap_policy is only valid for recurring schedules`);
|
|
16113
|
+
}
|
|
16114
|
+
if (overlapRetryMs != null) {
|
|
16115
|
+
if (overlapRetryMs <= 0) {
|
|
16116
|
+
throw new TypeError(`${context} overlap_retry_ms must be positive`);
|
|
16117
|
+
}
|
|
16118
|
+
if (record.overlap_policy !== "queue_after_previous") {
|
|
16119
|
+
throw new TypeError(`${context} overlap_retry_ms requires queue_after_previous`);
|
|
16120
|
+
}
|
|
16121
|
+
}
|
|
16122
|
+
}
|
|
16123
|
+
function validateCatchupState(record, context, coalescedCount, lastCoalescedCount) {
|
|
16124
|
+
const lastCatchupAt = record.last_catchup_at_ms;
|
|
16125
|
+
if (record.kind !== "interval") {
|
|
16126
|
+
if (coalescedCount !== 0) {
|
|
16127
|
+
throw new TypeError(`${context} non-interval coalesced_count must be zero`);
|
|
16128
|
+
}
|
|
16129
|
+
if (lastCoalescedCount !== 0) {
|
|
16130
|
+
throw new TypeError(`${context} non-interval last_coalesced_count must be zero`);
|
|
16131
|
+
}
|
|
16132
|
+
if (lastCatchupAt != null) {
|
|
16133
|
+
throw new TypeError(`${context} non-interval last_catchup_at_ms must be null`);
|
|
16134
|
+
}
|
|
16135
|
+
return;
|
|
16136
|
+
}
|
|
16137
|
+
if (lastCoalescedCount > coalescedCount) {
|
|
16138
|
+
throw new TypeError(`${context} last_coalesced_count exceeds coalesced_count`);
|
|
16139
|
+
}
|
|
16140
|
+
if (lastCoalescedCount > 0 && lastCatchupAt == null) {
|
|
16141
|
+
throw new TypeError(`${context} response missing last_catchup_at_ms after catch-up`);
|
|
16142
|
+
}
|
|
16143
|
+
if (lastCoalescedCount === 0 && lastCatchupAt != null) {
|
|
16144
|
+
throw new TypeError(`${context} last_catchup_at_ms requires a catch-up`);
|
|
16145
|
+
}
|
|
16146
|
+
}
|
|
16147
|
+
function optionalScheduleRecord(value, context) {
|
|
16148
|
+
return value == null ? null : scheduleRecordResponse(value, context);
|
|
16149
|
+
}
|
|
16150
|
+
function scheduleRecordList(value, context) {
|
|
16151
|
+
return arrayResponse(value).map((item) => scheduleRecordResponse(item, context));
|
|
16152
|
+
}
|
|
16153
|
+
function scheduleFireDueResponse(value) {
|
|
16154
|
+
const context = "FLOW.SCHEDULE.FIRE_DUE";
|
|
16155
|
+
const record = adminRecordResponse(value, context);
|
|
16156
|
+
const claimed = safeNonNegativeInteger(record, "claimed", context, true);
|
|
16157
|
+
const fired = safeNonNegativeInteger(record, "fired", context, true);
|
|
16158
|
+
const skipped = safeNonNegativeInteger(record, "skipped", context, true);
|
|
16159
|
+
const coalesced = safeNonNegativeInteger(record, "coalesced", context, true);
|
|
16160
|
+
if (!Array.isArray(record.errors)) {
|
|
16161
|
+
throw new TypeError(`${context} response missing errors`);
|
|
16162
|
+
}
|
|
16163
|
+
for (const error of record.errors) {
|
|
16164
|
+
if (!Array.isArray(error) || error.length !== 2 || typeof error[0] !== "string" || error[0].length === 0 || typeof error[1] !== "string" || error[1].length === 0) {
|
|
16165
|
+
throw new TypeError(`${context} returned an invalid error entry`);
|
|
16166
|
+
}
|
|
16167
|
+
}
|
|
16168
|
+
if (fired + skipped + record.errors.length !== claimed) {
|
|
16169
|
+
throw new TypeError(`${context} outcomes do not equal claimed`);
|
|
16170
|
+
}
|
|
16171
|
+
optionalResponseText(record, "claim_error", context);
|
|
16172
|
+
const lastTargetId = optionalResponseText(record, "last_target_id", context);
|
|
16173
|
+
const lastSkipReason = optionalResponseText(record, "last_skip_reason", context);
|
|
16174
|
+
if (fired > 0 && lastTargetId == null) {
|
|
16175
|
+
throw new TypeError(`${context} response missing last_target_id`);
|
|
16176
|
+
}
|
|
16177
|
+
if (fired === 0 && lastTargetId != null) {
|
|
16178
|
+
throw new TypeError(`${context} last_target_id requires a fired outcome`);
|
|
16179
|
+
}
|
|
16180
|
+
if (skipped > 0 && lastSkipReason == null) {
|
|
16181
|
+
throw new TypeError(`${context} response missing last_skip_reason`);
|
|
16182
|
+
}
|
|
16183
|
+
if (skipped === 0 && lastSkipReason != null) {
|
|
16184
|
+
throw new TypeError(`${context} last_skip_reason requires a skipped outcome`);
|
|
16185
|
+
}
|
|
16186
|
+
if (coalesced > 0 && fired + skipped === 0) {
|
|
16187
|
+
throw new TypeError(`${context} coalesced count requires a fired or skipped outcome`);
|
|
16188
|
+
}
|
|
16189
|
+
return record;
|
|
16190
|
+
}
|
|
16191
|
+
function scheduleFireResponse(value) {
|
|
16192
|
+
const context = "FLOW.SCHEDULE.FIRE";
|
|
16193
|
+
const record = adminRecordResponse(value, context);
|
|
16194
|
+
const schedule = scheduleRecordResponse(record.schedule, `${context} schedule`);
|
|
16195
|
+
const fired = safeNonNegativeInteger(record, "fired", context, true);
|
|
16196
|
+
const skipped = record.skipped == null ? 0 : safeNonNegativeInteger(record, "skipped", context);
|
|
16197
|
+
if (fired > 1 || skipped > 1 || fired + skipped !== 1) {
|
|
16198
|
+
throw new TypeError(`${context} outcomes must equal one`);
|
|
16199
|
+
}
|
|
16200
|
+
if (fired === 1 && (typeof record.target_id !== "string" || record.target_id.length === 0)) {
|
|
16201
|
+
throw new TypeError(`${context} response missing target_id`);
|
|
16202
|
+
}
|
|
16203
|
+
if (skipped === 1 && (typeof record.reason !== "string" || record.reason.length === 0)) {
|
|
16204
|
+
throw new TypeError(`${context} response missing reason`);
|
|
16205
|
+
}
|
|
16206
|
+
if (fired === 0 && record.target_id != null) {
|
|
16207
|
+
throw new TypeError(`${context} target_id requires a fired outcome`);
|
|
16208
|
+
}
|
|
16209
|
+
if (skipped === 0 && record.reason != null) {
|
|
16210
|
+
throw new TypeError(`${context} reason requires a skipped outcome`);
|
|
16211
|
+
}
|
|
16212
|
+
return { ...record, fired, skipped, schedule };
|
|
16213
|
+
}
|
|
16214
|
+
function optionalResponseText(record, field3, context) {
|
|
16215
|
+
const value = record[field3];
|
|
16216
|
+
if (value == null) return void 0;
|
|
16217
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
16218
|
+
throw new TypeError(`${context} ${field3} must be non-empty text`);
|
|
16219
|
+
}
|
|
16220
|
+
return value;
|
|
16221
|
+
}
|
|
16222
|
+
function requiredNullableResponseText(record, field3, context) {
|
|
16223
|
+
if (!(field3 in record)) throw new TypeError(`${context} response missing ${field3}`);
|
|
16224
|
+
if (record[field3] === void 0) {
|
|
16225
|
+
throw new TypeError(`${context} response missing ${field3}`);
|
|
16226
|
+
}
|
|
16227
|
+
return optionalResponseText(record, field3, context) ?? null;
|
|
16228
|
+
}
|
|
16229
|
+
function requiredNullableNonNegativeInteger(record, field3, context) {
|
|
16230
|
+
if (!(field3 in record)) throw new TypeError(`${context} response missing ${field3}`);
|
|
16231
|
+
if (record[field3] === void 0) {
|
|
16232
|
+
throw new TypeError(`${context} response missing ${field3}`);
|
|
16233
|
+
}
|
|
16234
|
+
if (record[field3] === null) return null;
|
|
16235
|
+
return safeNonNegativeInteger(record, field3, context);
|
|
16236
|
+
}
|
|
16237
|
+
function safeNonNegativeInteger(record, field3, context, required = false) {
|
|
16238
|
+
if (!(field3 in record) || record[field3] == null) {
|
|
16239
|
+
if (required) throw new TypeError(`${context} response missing ${field3}`);
|
|
16240
|
+
return 0;
|
|
16241
|
+
}
|
|
16242
|
+
const value = record[field3];
|
|
16243
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
16244
|
+
throw new TypeError(`${context} ${field3} must be a safe non-negative integer`);
|
|
16245
|
+
}
|
|
16246
|
+
return value;
|
|
16247
|
+
}
|
|
16248
|
+
|
|
16249
|
+
// src/client-schedules.ts
|
|
16250
|
+
var FerricStoreScheduleClient = class extends FerricStoreClientCore {
|
|
14674
16251
|
async scheduleCreate(id, options) {
|
|
16252
|
+
validateScheduleCreate(id, options);
|
|
14675
16253
|
const args = ["FLOW.SCHEDULE.CREATE", id];
|
|
14676
16254
|
append(args, "KIND", options.kind);
|
|
14677
16255
|
append(args, "AT_MS", options.atMs);
|
|
@@ -14681,6 +16259,7 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
|
|
|
14681
16259
|
append(args, "CRON", options.cron);
|
|
14682
16260
|
append(args, "TIMEZONE", options.timezone);
|
|
14683
16261
|
append(args, "TARGET", options.target);
|
|
16262
|
+
append(args, "CATCHUP_POLICY", options.catchupPolicy);
|
|
14684
16263
|
append(args, "OVERLAP_POLICY", options.overlapPolicy);
|
|
14685
16264
|
append(args, "OVERLAP_RETRY_MS", options.overlapRetryMs);
|
|
14686
16265
|
append(args, "MAX_FIRES", options.maxFires);
|
|
@@ -14690,34 +16269,55 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
|
|
|
14690
16269
|
for (const [name, value] of Object.entries(options.extraOptions ?? {})) {
|
|
14691
16270
|
args.push(name.toUpperCase(), value);
|
|
14692
16271
|
}
|
|
14693
|
-
return
|
|
16272
|
+
return scheduleRecordResponse(await this.commandArgs(args), "FLOW.SCHEDULE.CREATE");
|
|
14694
16273
|
}
|
|
14695
|
-
async scheduleGet(id
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
16274
|
+
async scheduleGet(id) {
|
|
16275
|
+
validateScheduleID(id);
|
|
16276
|
+
return optionalScheduleRecord(
|
|
16277
|
+
await this.commandArgs(["FLOW.SCHEDULE.GET", id]),
|
|
16278
|
+
"FLOW.SCHEDULE.GET"
|
|
16279
|
+
);
|
|
14699
16280
|
}
|
|
14700
16281
|
async scheduleFire(id, options = {}) {
|
|
14701
|
-
|
|
16282
|
+
validateScheduleFire(id, options);
|
|
16283
|
+
const args = ["FLOW.SCHEDULE.FIRE", id];
|
|
16284
|
+
append(args, "FIRE_AT_MS", options.fireAtMs);
|
|
16285
|
+
append(args, "NOW", options.nowMs);
|
|
16286
|
+
return scheduleFireResponse(await this.commandArgs(args));
|
|
14702
16287
|
}
|
|
14703
16288
|
async schedulePause(id, options = {}) {
|
|
14704
|
-
|
|
16289
|
+
validateScheduleStatus(id, options.nowMs);
|
|
16290
|
+
return scheduleRecordResponse(
|
|
16291
|
+
await this.scheduleStatusResponse("FLOW.SCHEDULE.PAUSE", id, options.nowMs),
|
|
16292
|
+
"FLOW.SCHEDULE.PAUSE"
|
|
16293
|
+
);
|
|
14705
16294
|
}
|
|
14706
16295
|
async scheduleResume(id, options = {}) {
|
|
14707
|
-
|
|
16296
|
+
validateScheduleStatus(id, options.nowMs);
|
|
16297
|
+
return scheduleRecordResponse(
|
|
16298
|
+
await this.scheduleStatusResponse("FLOW.SCHEDULE.RESUME", id, options.nowMs),
|
|
16299
|
+
"FLOW.SCHEDULE.RESUME"
|
|
16300
|
+
);
|
|
14708
16301
|
}
|
|
14709
16302
|
async scheduleDelete(id, options = {}) {
|
|
14710
|
-
|
|
16303
|
+
validateScheduleStatus(id, options.nowMs);
|
|
16304
|
+
const response = await this.scheduleStatusResponse("FLOW.SCHEDULE.DELETE", id, options.nowMs);
|
|
16305
|
+
if (!okLike(response)) {
|
|
16306
|
+
throw new TypeError("FLOW.SCHEDULE.DELETE response must be OK");
|
|
16307
|
+
}
|
|
14711
16308
|
}
|
|
14712
16309
|
async scheduleFireDue(options = {}) {
|
|
16310
|
+
validateScheduleFireDue(options);
|
|
14713
16311
|
const args = ["FLOW.SCHEDULE.FIRE_DUE"];
|
|
14714
16312
|
append(args, "NOW", options.nowMs);
|
|
14715
16313
|
append(args, "WORKER", options.worker);
|
|
16314
|
+
append(args, "LEASE_MS", options.leaseMs);
|
|
14716
16315
|
append(args, "BLOCK", options.blockMs);
|
|
14717
16316
|
append(args, "LIMIT", options.limit);
|
|
14718
|
-
return
|
|
16317
|
+
return scheduleFireDueResponse(await this.commandArgs(args));
|
|
14719
16318
|
}
|
|
14720
16319
|
async scheduleList(options = {}) {
|
|
16320
|
+
validateScheduleList(options);
|
|
14721
16321
|
const args = ["FLOW.SCHEDULE.LIST"];
|
|
14722
16322
|
append(args, "KIND", options.kind);
|
|
14723
16323
|
append(args, "STATE", options.state);
|
|
@@ -14727,14 +16327,40 @@ var FerricStoreManagementClient = class extends FerricStoreClientCore {
|
|
|
14727
16327
|
append(args, "TO_MS", options.toMs);
|
|
14728
16328
|
append(args, "COUNT", options.count);
|
|
14729
16329
|
appendBool(args, "REV", options.rev);
|
|
14730
|
-
return
|
|
16330
|
+
return scheduleRecordList(await this.commandArgs(args), "FLOW.SCHEDULE.LIST");
|
|
14731
16331
|
}
|
|
14732
|
-
async
|
|
16332
|
+
async scheduleStatusResponse(command, id, now) {
|
|
14733
16333
|
const args = [command, id];
|
|
14734
16334
|
append(args, "NOW", now);
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
16335
|
+
return await this.commandArgs(args);
|
|
16336
|
+
}
|
|
16337
|
+
};
|
|
16338
|
+
|
|
16339
|
+
// src/client-management.ts
|
|
16340
|
+
var FerricStoreManagementClient = class extends FerricStoreScheduleClient {
|
|
16341
|
+
async stats(type, options = {}) {
|
|
16342
|
+
const args = ["FLOW.STATS", type];
|
|
16343
|
+
append(args, "STATE", options.state);
|
|
16344
|
+
append(args, "COUNT", options.count);
|
|
16345
|
+
append(args, "PARTITION", options.partitionKey);
|
|
16346
|
+
appendAttributes(args, options.attributes);
|
|
16347
|
+
appendBool(args, "CONSISTENT_PROJECTION", options.consistentProjection);
|
|
16348
|
+
return adminRecordResponse(await this.commandArgs(args), "FLOW.STATS");
|
|
16349
|
+
}
|
|
16350
|
+
async countByState(type, state, options = {}) {
|
|
16351
|
+
const stats = await this.stats(type, { ...options, state });
|
|
16352
|
+
if (!("count" in stats)) throw new TypeError("FLOW.STATS response missing count");
|
|
16353
|
+
return integer(stats.count);
|
|
16354
|
+
}
|
|
16355
|
+
async attributes(type, options = {}) {
|
|
16356
|
+
const args = ["FLOW.ATTRIBUTES", type];
|
|
16357
|
+
appendAttributeQueryOptions(args, options);
|
|
16358
|
+
return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTES");
|
|
16359
|
+
}
|
|
16360
|
+
async attributeValues(type, attribute, options = {}) {
|
|
16361
|
+
const args = ["FLOW.ATTRIBUTE_VALUES", type, attribute];
|
|
16362
|
+
appendAttributeQueryOptions(args, options);
|
|
16363
|
+
return adminRecordList(await this.commandArgs(args), "FLOW.ATTRIBUTE_VALUES");
|
|
14738
16364
|
}
|
|
14739
16365
|
async effectReserve(id, effectKey, effectType, options) {
|
|
14740
16366
|
const args = [
|
|
@@ -14967,7 +16593,7 @@ var FerricStoreFlowQueryClient = class extends FerricStoreManagementClient {
|
|
|
14967
16593
|
args.push(indexId);
|
|
14968
16594
|
}
|
|
14969
16595
|
try {
|
|
14970
|
-
return decodeFlowQueryIndexStatus(await this.commandArgs(args));
|
|
16596
|
+
return decodeFlowQueryIndexStatus(await this.commandArgs(args), indexId);
|
|
14971
16597
|
} catch (error) {
|
|
14972
16598
|
this.throwFlowQueryError(error);
|
|
14973
16599
|
}
|
|
@@ -15029,7 +16655,7 @@ var FerricStoreFlowQueryClient = class extends FerricStoreManagementClient {
|
|
|
15029
16655
|
if (hasFlowExplainPrefix(query)) {
|
|
15030
16656
|
throw new TypeError("query already contains an EXPLAIN prefix");
|
|
15031
16657
|
}
|
|
15032
|
-
return decodeFlowExplainResult(await this.executeFlowQuery(prefix + query
|
|
16658
|
+
return decodeFlowExplainResult(await this.executeFlowQuery(prefix + query, params));
|
|
15033
16659
|
}
|
|
15034
16660
|
throwFlowQueryError(error) {
|
|
15035
16661
|
if (error instanceof FerricStoreError) {
|
|
@@ -15062,7 +16688,7 @@ function throwIfClosed(signal) {
|
|
|
15062
16688
|
}
|
|
15063
16689
|
|
|
15064
16690
|
// src/flow-policy.ts
|
|
15065
|
-
import { Buffer as
|
|
16691
|
+
import { Buffer as Buffer54 } from "buffer";
|
|
15066
16692
|
var MAX_FLOW_POLICY_GENERATION = Number.MAX_SAFE_INTEGER;
|
|
15067
16693
|
function assertFlowPolicyGeneration(value) {
|
|
15068
16694
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
@@ -15072,15 +16698,15 @@ function assertFlowPolicyGeneration(value) {
|
|
|
15072
16698
|
function flowPolicySnapshotFromResp(value) {
|
|
15073
16699
|
const policy = requiredRecord(value, "FLOW policy snapshot");
|
|
15074
16700
|
const maxActiveMs = optionalPositiveInteger(field(policy, "max_active_ms"), "max_active_ms");
|
|
15075
|
-
const state =
|
|
16701
|
+
const state = optionalText4(field(policy, "state"), "state");
|
|
15076
16702
|
const mode = optionalMode(field(policy, "mode"), "mode");
|
|
15077
16703
|
const version = optionalVersion(field(policy, "version"));
|
|
15078
|
-
const indexedStateMeta =
|
|
16704
|
+
const indexedStateMeta = optionalText4(field(policy, "indexed_state_meta"), "indexed_state_meta");
|
|
15079
16705
|
const governance = optionalRecord(field(policy, "governance"), "governance");
|
|
15080
16706
|
const states = optionalStates(field(policy, "states"));
|
|
15081
16707
|
return {
|
|
15082
|
-
type:
|
|
15083
|
-
generation:
|
|
16708
|
+
type: requiredText4(field(policy, "type"), "type"),
|
|
16709
|
+
generation: nonNegativeInteger3(field(policy, "generation"), "generation"),
|
|
15084
16710
|
...state == null ? {} : { state },
|
|
15085
16711
|
...version == null ? {} : { version },
|
|
15086
16712
|
...mode == null ? {} : { mode },
|
|
@@ -15098,21 +16724,21 @@ function retrySnapshot(value, context) {
|
|
|
15098
16724
|
const retry2 = requiredRecord(value, `FLOW policy ${context}`);
|
|
15099
16725
|
const backoff = requiredRecord(field(retry2, "backoff"), `FLOW policy ${context}.backoff`);
|
|
15100
16726
|
return {
|
|
15101
|
-
maxRetries:
|
|
16727
|
+
maxRetries: nonNegativeInteger3(field(retry2, "max_retries"), `${context}.max_retries`),
|
|
15102
16728
|
backoff: {
|
|
15103
16729
|
kind: backoffKind(field(backoff, "kind"), `${context}.backoff.kind`),
|
|
15104
|
-
baseMs:
|
|
15105
|
-
maxMs:
|
|
15106
|
-
jitterPct:
|
|
16730
|
+
baseMs: nonNegativeInteger3(field(backoff, "base_ms"), `${context}.backoff.base_ms`),
|
|
16731
|
+
maxMs: nonNegativeInteger3(field(backoff, "max_ms"), `${context}.backoff.max_ms`),
|
|
16732
|
+
jitterPct: nonNegativeInteger3(field(backoff, "jitter_pct"), `${context}.backoff.jitter_pct`)
|
|
15107
16733
|
},
|
|
15108
|
-
exhaustedTo:
|
|
16734
|
+
exhaustedTo: requiredText4(field(retry2, "exhausted_to"), `${context}.exhausted_to`)
|
|
15109
16735
|
};
|
|
15110
16736
|
}
|
|
15111
16737
|
function retentionSnapshot(value, context) {
|
|
15112
16738
|
const retention = requiredRecord(value, `FLOW policy ${context}`);
|
|
15113
16739
|
return {
|
|
15114
|
-
ttlMs:
|
|
15115
|
-
historyMaxEvents:
|
|
16740
|
+
ttlMs: nonNegativeInteger3(field(retention, "ttl_ms"), `${context}.ttl_ms`),
|
|
16741
|
+
historyMaxEvents: nonNegativeInteger3(
|
|
15116
16742
|
field(retention, "history_max_events"),
|
|
15117
16743
|
`${context}.history_max_events`
|
|
15118
16744
|
)
|
|
@@ -15148,11 +16774,11 @@ function requiredRecord(value, context) {
|
|
|
15148
16774
|
if (value instanceof Map) {
|
|
15149
16775
|
const result = {};
|
|
15150
16776
|
for (const [key, item] of value.entries()) {
|
|
15151
|
-
setOwnValue(result,
|
|
16777
|
+
setOwnValue(result, requiredText4(key, `${context} key`), item);
|
|
15152
16778
|
}
|
|
15153
16779
|
return result;
|
|
15154
16780
|
}
|
|
15155
|
-
if (typeof value === "object" && value != null && !Array.isArray(value) && !
|
|
16781
|
+
if (typeof value === "object" && value != null && !Array.isArray(value) && !Buffer54.isBuffer(value) && !(value instanceof Uint8Array)) return value;
|
|
15156
16782
|
throw new TypeError(`${context} returned an unexpected response`);
|
|
15157
16783
|
}
|
|
15158
16784
|
function optionalRecord(value, context) {
|
|
@@ -15167,21 +16793,21 @@ function stringArray(value, context) {
|
|
|
15167
16793
|
if (!Object.hasOwn(value, index)) {
|
|
15168
16794
|
throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
|
|
15169
16795
|
}
|
|
15170
|
-
result[index] =
|
|
16796
|
+
result[index] = requiredText4(value[index], `${context}[${index}]`);
|
|
15171
16797
|
}
|
|
15172
16798
|
return result;
|
|
15173
16799
|
}
|
|
15174
|
-
function
|
|
16800
|
+
function requiredText4(value, context) {
|
|
15175
16801
|
if (typeof value === "string") return value;
|
|
15176
|
-
if (
|
|
15177
|
-
return
|
|
16802
|
+
if (Buffer54.isBuffer(value) || value instanceof Uint8Array) {
|
|
16803
|
+
return Buffer54.from(value).toString("utf8");
|
|
15178
16804
|
}
|
|
15179
16805
|
throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
|
|
15180
16806
|
}
|
|
15181
|
-
function
|
|
15182
|
-
return value == null ? void 0 :
|
|
16807
|
+
function optionalText4(value, context) {
|
|
16808
|
+
return value == null ? void 0 : requiredText4(value, context);
|
|
15183
16809
|
}
|
|
15184
|
-
function
|
|
16810
|
+
function nonNegativeInteger3(value, context) {
|
|
15185
16811
|
let parsed;
|
|
15186
16812
|
try {
|
|
15187
16813
|
parsed = integer(value);
|
|
@@ -15193,26 +16819,26 @@ function nonNegativeInteger2(value, context) {
|
|
|
15193
16819
|
}
|
|
15194
16820
|
function optionalPositiveInteger(value, context) {
|
|
15195
16821
|
if (value == null) return void 0;
|
|
15196
|
-
const parsed =
|
|
16822
|
+
const parsed = nonNegativeInteger3(value, context);
|
|
15197
16823
|
if (parsed === 0) throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
|
|
15198
16824
|
return parsed;
|
|
15199
16825
|
}
|
|
15200
16826
|
function optionalVersion(value) {
|
|
15201
16827
|
if (value == null) return void 0;
|
|
15202
16828
|
if (typeof value === "number" || typeof value === "bigint") {
|
|
15203
|
-
return
|
|
16829
|
+
return nonNegativeInteger3(value, "version");
|
|
15204
16830
|
}
|
|
15205
|
-
return
|
|
16831
|
+
return requiredText4(value, "version");
|
|
15206
16832
|
}
|
|
15207
16833
|
function backoffKind(value, context) {
|
|
15208
|
-
const kind =
|
|
16834
|
+
const kind = requiredText4(value, context).toLowerCase();
|
|
15209
16835
|
if (kind === "none" || kind === "fixed" || kind === "linear" || kind === "exponential") {
|
|
15210
16836
|
return kind;
|
|
15211
16837
|
}
|
|
15212
16838
|
throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
|
|
15213
16839
|
}
|
|
15214
16840
|
function requiredMode(value, context) {
|
|
15215
|
-
const mode =
|
|
16841
|
+
const mode = requiredText4(value, context).toLowerCase();
|
|
15216
16842
|
if (mode === "fifo" || mode === "parallel") return mode;
|
|
15217
16843
|
throw new TypeError(`FLOW policy ${context} returned an unexpected response`);
|
|
15218
16844
|
}
|
|
@@ -15430,7 +17056,7 @@ var FerricStoreFlowSupportClient = class extends FerricStoreFlowQueryClient {
|
|
|
15430
17056
|
};
|
|
15431
17057
|
|
|
15432
17058
|
// src/flow-many-snapshot.ts
|
|
15433
|
-
import { Buffer as
|
|
17059
|
+
import { Buffer as Buffer56 } from "buffer";
|
|
15434
17060
|
var encodedOptionFields = ["error", "payload", "reason", "result"];
|
|
15435
17061
|
function snapshotCreateItem(item, codec) {
|
|
15436
17062
|
const snapshot = {
|
|
@@ -15470,7 +17096,7 @@ function snapshotFlowManyOptions(options, codec) {
|
|
|
15470
17096
|
}
|
|
15471
17097
|
function snapshotClaimedItem(item) {
|
|
15472
17098
|
const wire = item[CLAIMED_ITEM_WIRE];
|
|
15473
|
-
const leaseToken =
|
|
17099
|
+
const leaseToken = Buffer56.from(wire?.leaseToken ?? item.leaseToken);
|
|
15474
17100
|
const snapshot = {
|
|
15475
17101
|
...item,
|
|
15476
17102
|
fencingToken: wire?.fencingToken ?? item.fencingToken,
|
|
@@ -15487,15 +17113,15 @@ function snapshotClaimedItem(item) {
|
|
|
15487
17113
|
function snapshotFencedItem(item) {
|
|
15488
17114
|
return Object.freeze({
|
|
15489
17115
|
...item,
|
|
15490
|
-
...item.leaseToken == null ? {} : { leaseToken:
|
|
17116
|
+
...item.leaseToken == null ? {} : { leaseToken: Buffer56.from(item.leaseToken) }
|
|
15491
17117
|
});
|
|
15492
17118
|
}
|
|
15493
17119
|
function snapshotClaimedItemWire(wire, leaseToken) {
|
|
15494
17120
|
return Object.freeze({
|
|
15495
17121
|
fencingToken: wire.fencingToken,
|
|
15496
|
-
id:
|
|
17122
|
+
id: Buffer56.from(wire.id),
|
|
15497
17123
|
leaseToken,
|
|
15498
|
-
partitionKey: wire.partitionKey == null ? wire.partitionKey :
|
|
17124
|
+
partitionKey: wire.partitionKey == null ? wire.partitionKey : Buffer56.from(wire.partitionKey)
|
|
15499
17125
|
});
|
|
15500
17126
|
}
|
|
15501
17127
|
function snapshotArray(values) {
|
|
@@ -15506,7 +17132,7 @@ function snapshotArray(values) {
|
|
|
15506
17132
|
return Object.freeze(snapshot);
|
|
15507
17133
|
}
|
|
15508
17134
|
function snapshotStateMeta(stateMeta) {
|
|
15509
|
-
return snapshotRecord(stateMeta, (value) =>
|
|
17135
|
+
return snapshotRecord(stateMeta, (value) => Buffer56.isBuffer(value) ? Buffer56.from(value) : value);
|
|
15510
17136
|
}
|
|
15511
17137
|
function snapshotCommandRecord(values) {
|
|
15512
17138
|
const seen = /* @__PURE__ */ new WeakMap();
|
|
@@ -15524,7 +17150,7 @@ function snapshotRecord(values, capture) {
|
|
|
15524
17150
|
}
|
|
15525
17151
|
function snapshotCommandArgument(value, seen) {
|
|
15526
17152
|
if (typeof value !== "object" || value == null) return value;
|
|
15527
|
-
if (
|
|
17153
|
+
if (Buffer56.isBuffer(value) || value instanceof Uint8Array) return Buffer56.from(value);
|
|
15528
17154
|
const objectValue = value;
|
|
15529
17155
|
const existing = seen.get(objectValue);
|
|
15530
17156
|
if (existing != null) return existing;
|
|
@@ -16512,7 +18138,7 @@ function nativeOptionsForBootstrap(options, signal) {
|
|
|
16512
18138
|
}
|
|
16513
18139
|
|
|
16514
18140
|
// src/flow-query-projection.ts
|
|
16515
|
-
import { Buffer as
|
|
18141
|
+
import { Buffer as Buffer57 } from "buffer";
|
|
16516
18142
|
var MAX_PROJECTION_FIELDS = 32;
|
|
16517
18143
|
var MAX_DYNAMIC_NAME_BYTES = 64;
|
|
16518
18144
|
var FIELD_BRAND = /* @__PURE__ */ Symbol("FerricStoreFlowProjectionField");
|
|
@@ -16580,20 +18206,38 @@ function projectFlowQuery(query, shape, ...fields) {
|
|
|
16580
18206
|
if (containsReturnKeyword(query)) {
|
|
16581
18207
|
throw new TypeError("Flow query already contains a RETURN clause");
|
|
16582
18208
|
}
|
|
16583
|
-
const
|
|
16584
|
-
const base = trimmed.endsWith(";") ? trimmed.slice(0, -1).trimEnd() : trimmed;
|
|
18209
|
+
const base = stripOptionalTerminator(query);
|
|
16585
18210
|
const result = `${base} RETURN ${shape.toUpperCase()} (${selectors.join(", ")})`;
|
|
16586
|
-
if (
|
|
18211
|
+
if (Buffer57.byteLength(result, "utf8") > FLOW_QUERY_MAX_BYTES) {
|
|
16587
18212
|
throw new TypeError(`FLOW.QUERY query exceeds ${FLOW_QUERY_MAX_BYTES} bytes`);
|
|
16588
18213
|
}
|
|
16589
18214
|
return result;
|
|
16590
18215
|
}
|
|
18216
|
+
function stripOptionalTerminator(query) {
|
|
18217
|
+
const trimmed = trimFlowQueryAsciiWhitespace(query);
|
|
18218
|
+
if (!trimmed.endsWith(";")) return trimmed;
|
|
18219
|
+
const base = trimFlowQueryAsciiWhitespace(trimmed.slice(0, -1));
|
|
18220
|
+
if (base.endsWith(";")) {
|
|
18221
|
+
throw new TypeError("Flow query accepts at most one trailing semicolon");
|
|
18222
|
+
}
|
|
18223
|
+
return base;
|
|
18224
|
+
}
|
|
16591
18225
|
function querySource(query) {
|
|
16592
|
-
const
|
|
16593
|
-
if (
|
|
18226
|
+
const source = trimFlowQueryAsciiWhitespaceStart(query);
|
|
18227
|
+
if (!matchesFlowQueryAsciiKeyword(source, 0, "FROM") || !isFlowQueryAsciiWhitespaceCode(source.charCodeAt(4))) {
|
|
16594
18228
|
throw new TypeError("Projected Flow query must start with FROM runs or FROM events");
|
|
16595
18229
|
}
|
|
16596
|
-
|
|
18230
|
+
let offset = 4;
|
|
18231
|
+
while (isFlowQueryAsciiWhitespaceCode(source.charCodeAt(offset))) offset += 1;
|
|
18232
|
+
for (const candidate of ["runs", "events"]) {
|
|
18233
|
+
if (matchesFlowQueryAsciiKeyword(source, offset, candidate.toUpperCase())) {
|
|
18234
|
+
const boundary = source[offset + candidate.length];
|
|
18235
|
+
if (boundary == null || isFlowQueryAsciiWhitespaceCode(boundary.charCodeAt(0))) {
|
|
18236
|
+
return candidate;
|
|
18237
|
+
}
|
|
18238
|
+
}
|
|
18239
|
+
}
|
|
18240
|
+
throw new TypeError("Projected Flow query must start with FROM runs or FROM events");
|
|
16597
18241
|
}
|
|
16598
18242
|
function containsReturnKeyword(query) {
|
|
16599
18243
|
let quoted = false;
|
|
@@ -16624,7 +18268,7 @@ function quoteName(value, allowPrivate) {
|
|
|
16624
18268
|
throw new TypeError("Flow query projection metadata name must be text");
|
|
16625
18269
|
}
|
|
16626
18270
|
validateUnicodeScalarText2(value);
|
|
16627
|
-
const size =
|
|
18271
|
+
const size = Buffer57.byteLength(value, "utf8");
|
|
16628
18272
|
if (size === 0 || size > MAX_DYNAMIC_NAME_BYTES || !allowPrivate && value.startsWith("__")) {
|
|
16629
18273
|
throw new TypeError(
|
|
16630
18274
|
`Flow query projection metadata names must be 1..${MAX_DYNAMIC_NAME_BYTES} UTF-8 bytes`
|
|
@@ -16658,7 +18302,7 @@ function complete(options = {}) {
|
|
|
16658
18302
|
function retry(options = {}) {
|
|
16659
18303
|
return brandOutcome({ ...options, kind: "retry" });
|
|
16660
18304
|
}
|
|
16661
|
-
function
|
|
18305
|
+
function fail2(options = {}) {
|
|
16662
18306
|
return brandOutcome({ ...options, kind: "fail" });
|
|
16663
18307
|
}
|
|
16664
18308
|
function isOutcome(value) {
|
|
@@ -16726,7 +18370,7 @@ var LeaseRenewalGuard = class {
|
|
|
16726
18370
|
if (options.leaseRenewal === false) {
|
|
16727
18371
|
return;
|
|
16728
18372
|
}
|
|
16729
|
-
const intervalMs =
|
|
18373
|
+
const intervalMs = positiveInteger3(options.leaseRenewIntervalMs, Math.max(1, Math.trunc(leaseMs / 2)));
|
|
16730
18374
|
this.timer = setLongInterval(() => this.renew(), intervalMs);
|
|
16731
18375
|
this.timer.unref();
|
|
16732
18376
|
}
|
|
@@ -16798,7 +18442,7 @@ function snapshotLeasedJob(job) {
|
|
|
16798
18442
|
return snapshot;
|
|
16799
18443
|
}
|
|
16800
18444
|
function workerConcurrency(options) {
|
|
16801
|
-
return
|
|
18445
|
+
return positiveInteger3(options.concurrency ?? options.workers, 1);
|
|
16802
18446
|
}
|
|
16803
18447
|
function workerRefillStrategy(options) {
|
|
16804
18448
|
return options.refillStrategy === "wave" ? "wave" : "continuous";
|
|
@@ -16841,18 +18485,18 @@ function workerErrorPayload(error, options, codec) {
|
|
|
16841
18485
|
}
|
|
16842
18486
|
function workerBatchSize(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
|
|
16843
18487
|
return Math.min(
|
|
16844
|
-
|
|
16845
|
-
|
|
18488
|
+
positiveInteger3(options.batchSize, 10),
|
|
18489
|
+
positiveInteger3(flowManyBatchLimit, DEFAULT_FLOW_MANY_BATCH_LIMIT2)
|
|
16846
18490
|
);
|
|
16847
18491
|
}
|
|
16848
18492
|
function workerClaimLimit(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
|
|
16849
18493
|
return Math.min(workerBatchSize(options, flowManyBatchLimit), workerConcurrency(options));
|
|
16850
18494
|
}
|
|
16851
18495
|
function workerDrainBatches(options) {
|
|
16852
|
-
return
|
|
18496
|
+
return positiveInteger3(options.claimDrainBatches, 1);
|
|
16853
18497
|
}
|
|
16854
18498
|
function workerLeaseMs(options, fallback = 3e4) {
|
|
16855
|
-
return
|
|
18499
|
+
return positiveInteger3(options.leaseMs, positiveInteger3(fallback, 3e4));
|
|
16856
18500
|
}
|
|
16857
18501
|
function workerIdleSleepMs(options) {
|
|
16858
18502
|
return nonNegativeDuration(options.idleSleepMs, 250);
|
|
@@ -16864,7 +18508,7 @@ function workerClaimBlockMs(options, useBlocking) {
|
|
|
16864
18508
|
if (options.signal == null || !Number.isFinite(options.blockMs) || options.blockMs < 0) {
|
|
16865
18509
|
return options.blockMs;
|
|
16866
18510
|
}
|
|
16867
|
-
const abortPollMs =
|
|
18511
|
+
const abortPollMs = positiveInteger3(options.abortPollMs, 1e3);
|
|
16868
18512
|
return options.blockMs === 0 ? abortPollMs : Math.min(Math.trunc(options.blockMs), abortPollMs);
|
|
16869
18513
|
}
|
|
16870
18514
|
function workerMaxIdleSleepMs(options, idleSleepMs = workerIdleSleepMs(options)) {
|
|
@@ -16875,14 +18519,14 @@ function nextWorkerIdleSleepMs(currentIdleSleepMs, maxIdleSleepMs) {
|
|
|
16875
18519
|
return Math.min(maxIdleSleepMs, Math.max(1, currentIdleSleepMs * 2));
|
|
16876
18520
|
}
|
|
16877
18521
|
async function runContinuousWorkerPool(options) {
|
|
16878
|
-
const concurrency =
|
|
16879
|
-
const maxClaimSize =
|
|
18522
|
+
const concurrency = positiveInteger3(options.concurrency, 1);
|
|
18523
|
+
const maxClaimSize = positiveInteger3(options.maxClaimSize, concurrency);
|
|
16880
18524
|
const initialIdleSleepMs = nonNegativeDuration(options.idleSleepMs, 250);
|
|
16881
18525
|
const maxIdleSleepMs = Math.max(
|
|
16882
18526
|
initialIdleSleepMs,
|
|
16883
18527
|
nonNegativeDuration(options.maxIdleSleepMs, 5e3)
|
|
16884
18528
|
);
|
|
16885
|
-
const refillDelayMs =
|
|
18529
|
+
const refillDelayMs = nonNegativeInteger4(options.refillDelayMs, 0);
|
|
16886
18530
|
const active = /* @__PURE__ */ new Set();
|
|
16887
18531
|
const pending = [];
|
|
16888
18532
|
let pendingIndex = 0;
|
|
@@ -16998,10 +18642,10 @@ function normalizeWorkflowWorkerConfig(options) {
|
|
|
16998
18642
|
...captured
|
|
16999
18643
|
});
|
|
17000
18644
|
}
|
|
17001
|
-
function
|
|
18645
|
+
function positiveInteger3(value, fallback) {
|
|
17002
18646
|
return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
|
|
17003
18647
|
}
|
|
17004
|
-
function
|
|
18648
|
+
function nonNegativeInteger4(value, fallback) {
|
|
17005
18649
|
return value == null || !Number.isFinite(value) ? fallback : Math.max(0, Math.trunc(value));
|
|
17006
18650
|
}
|
|
17007
18651
|
function nonNegativeDuration(value, fallback) {
|
|
@@ -17455,7 +19099,7 @@ async function applyQueueException(queue, options, job, error) {
|
|
|
17455
19099
|
const policy = normalizeExceptionPolicy(options.exceptionPolicy);
|
|
17456
19100
|
if (policy === "raise") throw error;
|
|
17457
19101
|
const payload = workerErrorPayload(error, options, queue.client.codec);
|
|
17458
|
-
await applyQueueOutcome(queue, job, policy === "fail" ?
|
|
19102
|
+
await applyQueueOutcome(queue, job, policy === "fail" ? fail2({ error: payload }) : retry({ error: payload }));
|
|
17459
19103
|
}
|
|
17460
19104
|
|
|
17461
19105
|
// src/queue-pending-completions.ts
|
|
@@ -17702,7 +19346,7 @@ var QueueWorker = class {
|
|
|
17702
19346
|
const policy = normalizeExceptionPolicy(this.options.exceptionPolicy);
|
|
17703
19347
|
if (policy !== "raise") {
|
|
17704
19348
|
const payload = workerErrorPayload(error, this.options, this.queue.client.codec);
|
|
17705
|
-
outcome = policy === "fail" ?
|
|
19349
|
+
outcome = policy === "fail" ? fail2({ error: payload }) : retry({ error: payload });
|
|
17706
19350
|
}
|
|
17707
19351
|
}
|
|
17708
19352
|
const stopped = await stopQueueBatchGuards(guards.map((guard) => guard.job), guards);
|
|
@@ -17885,13 +19529,13 @@ var QueueWorker = class {
|
|
|
17885
19529
|
|
|
17886
19530
|
// src/flow-client-options.ts
|
|
17887
19531
|
function snapshotFlowClientOptions(options, context) {
|
|
17888
|
-
const
|
|
17889
|
-
if (typeof
|
|
19532
|
+
const own2 = { ...options };
|
|
19533
|
+
if (typeof own2.type !== "string" || own2.type.length === 0) {
|
|
17890
19534
|
throw new TypeError(`${context} type must be an own non-empty string`);
|
|
17891
19535
|
}
|
|
17892
|
-
const valueConfig =
|
|
19536
|
+
const valueConfig = own2.valueConfig;
|
|
17893
19537
|
return Object.freeze({
|
|
17894
|
-
...
|
|
19538
|
+
...own2,
|
|
17895
19539
|
...valueConfig == null ? {} : { valueConfig: Object.freeze({ ...valueConfig }) }
|
|
17896
19540
|
});
|
|
17897
19541
|
}
|
|
@@ -18314,19 +19958,19 @@ async function applyFail(client, ctx, outcome, returnRecord) {
|
|
|
18314
19958
|
|
|
18315
19959
|
// src/workflow-registration.ts
|
|
18316
19960
|
function createWorkflowStateRegistration(name, handler, options, defaultValueMaxBytes) {
|
|
18317
|
-
const
|
|
18318
|
-
const claimValues =
|
|
18319
|
-
const retryPolicy =
|
|
19961
|
+
const own2 = { ...options };
|
|
19962
|
+
const claimValues = own2.claimValues == null ? void 0 : snapshotOwnStringArray(own2.claimValues, "claimValues");
|
|
19963
|
+
const retryPolicy = own2.retryPolicy == null ? void 0 : Object.freeze({ ...own2.retryPolicy });
|
|
18320
19964
|
return Object.freeze({
|
|
18321
|
-
claimPayload:
|
|
18322
|
-
claimRecord:
|
|
18323
|
-
exceptionPolicy: normalizeExceptionPolicy(
|
|
19965
|
+
claimPayload: own2.claimPayload ?? true,
|
|
19966
|
+
claimRecord: own2.claimRecord ?? true,
|
|
19967
|
+
exceptionPolicy: normalizeExceptionPolicy(own2.exceptionPolicy),
|
|
18324
19968
|
handler,
|
|
18325
|
-
leaseMs:
|
|
18326
|
-
...
|
|
19969
|
+
leaseMs: own2.leaseMs ?? 3e4,
|
|
19970
|
+
...own2.mode == null ? {} : { mode: normalizeStateMode(own2.mode) },
|
|
18327
19971
|
name,
|
|
18328
|
-
returnRecord:
|
|
18329
|
-
valueMaxBytes:
|
|
19972
|
+
returnRecord: own2.returnRecord ?? false,
|
|
19973
|
+
valueMaxBytes: own2.valueMaxBytes ?? defaultValueMaxBytes,
|
|
18330
19974
|
...claimValues == null ? {} : { claimValues },
|
|
18331
19975
|
...retryPolicy == null ? {} : { retryPolicy }
|
|
18332
19976
|
});
|
|
@@ -18615,7 +20259,7 @@ var WorkflowWorker = class {
|
|
|
18615
20259
|
await applyWorkflowOutcome(
|
|
18616
20260
|
this.workflow.client,
|
|
18617
20261
|
ctx,
|
|
18618
|
-
|
|
20262
|
+
fail2({ error: workerErrorPayload(error, this.options, this.workflow.client.codec) }),
|
|
18619
20263
|
registration.returnRecord
|
|
18620
20264
|
);
|
|
18621
20265
|
return;
|
|
@@ -18630,8 +20274,8 @@ var WorkflowWorker = class {
|
|
|
18630
20274
|
};
|
|
18631
20275
|
|
|
18632
20276
|
// src/version.ts
|
|
18633
|
-
var FERRICSTORE_SDK_VERSION = "0.
|
|
18634
|
-
var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.
|
|
20277
|
+
var FERRICSTORE_SDK_VERSION = "0.11.4";
|
|
20278
|
+
var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
|
|
18635
20279
|
var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
|
|
18636
20280
|
export {
|
|
18637
20281
|
BitmapStore,
|
|
@@ -18689,7 +20333,7 @@ export {
|
|
|
18689
20333
|
WorkflowWorker,
|
|
18690
20334
|
classifyServerError,
|
|
18691
20335
|
complete,
|
|
18692
|
-
fail,
|
|
20336
|
+
fail2 as fail,
|
|
18693
20337
|
isOutcome,
|
|
18694
20338
|
isReconnectableClosedConnectionError,
|
|
18695
20339
|
mapException,
|