@ferricstore/ferricstore 0.12.2 → 0.13.1
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 +45 -3
- package/dist/index.cjs +804 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -9
- package/dist/index.d.ts +87 -9
- package/dist/index.js +798 -194
- package/dist/index.js.map +1 -1
- package/dist/langgraph.cjs.map +1 -1
- package/dist/langgraph.d.cts +1 -1
- package/dist/langgraph.d.ts +1 -1
- package/dist/langgraph.js.map +1 -1
- package/dist/openai-agents.cjs.map +1 -1
- package/dist/openai-agents.js.map +1 -1
- package/dist/{outcomes-DmBwnq0Y.d.cts → outcomes-CcX956h4.d.cts} +20 -1
- package/dist/{outcomes-BbFDp3AH.d.ts → outcomes-DLJYTrX8.d.ts} +20 -1
- package/docs/api/assets/hierarchy.js +1 -1
- package/docs/api/assets/highlight.css +9 -2
- package/docs/api/assets/navigation.js +1 -1
- package/docs/api/assets/search.js +1 -1
- package/docs/api/classes/ConnectionClosedError.html +5 -5
- package/docs/api/classes/FerricStoreClient.html +68 -23
- package/docs/api/classes/FerricStoreError.html +2 -1
- package/docs/api/classes/FlowAlreadyExistsError.html +3 -3
- package/docs/api/classes/FlowBatchError.html +1 -1
- package/docs/api/classes/FlowNotFoundError.html +3 -3
- package/docs/api/classes/FlowQueryError.html +1 -1
- package/docs/api/classes/FlowWrongStateError.html +3 -3
- package/docs/api/classes/HTTPTransportError.html +14 -8
- package/docs/api/classes/InvalidCommandError.html +3 -3
- package/docs/api/classes/LeaseRenewalError.html +3 -3
- package/docs/api/classes/LockHeldError.html +3 -3
- package/docs/api/classes/LockNotOwnedError.html +3 -3
- package/docs/api/classes/NativeAdapter.html +3 -3
- package/docs/api/classes/OverloadedError.html +5 -5
- package/docs/api/classes/QueueCompletionError.html +1 -1
- package/docs/api/classes/RequestNotSentError.html +225 -0
- package/docs/api/classes/RequestTimeoutError.html +6 -6
- package/docs/api/classes/RerouteError.html +3 -3
- package/docs/api/classes/StaleLeaseError.html +3 -3
- package/docs/api/classes/StalePolicyGenerationError.html +3 -3
- package/docs/api/classes/Workflow.html +19 -19
- package/docs/api/classes/WorkflowClient.html +4 -4
- package/docs/api/classes/WorkflowContext.html +73 -37
- package/docs/api/classes/WorkflowFlowCommands.html +26 -22
- package/docs/api/classes/WorkflowWorker.html +6 -6
- package/docs/api/functions/classifyServerError.html +1 -1
- package/docs/api/functions/complete.html +1 -1
- package/docs/api/functions/fail.html +1 -1
- package/docs/api/functions/isAlreadyAppliedOutcome.html +41 -0
- package/docs/api/functions/isOutcome.html +1 -1
- package/docs/api/functions/mapException.html +1 -1
- package/docs/api/functions/retry.html +1 -1
- package/docs/api/functions/transition.html +1 -1
- package/docs/api/hierarchy.html +56 -47
- package/docs/api/index.html +32 -4
- package/docs/api/interfaces/AdminListOptions.html +1 -1
- package/docs/api/interfaces/AdvanceOptions.html +145 -0
- package/docs/api/interfaces/AlreadyAppliedOutcome.html +69 -0
- package/docs/api/interfaces/AppliedStepOutcome.html +87 -0
- package/docs/api/interfaces/CancelOptions.html +13 -9
- package/docs/api/interfaces/CompleteOptions.html +13 -9
- package/docs/api/interfaces/CompleteOutcome.html +13 -13
- package/docs/api/interfaces/CreateManyOptions.html +1 -1
- package/docs/api/interfaces/EffectCompensateOptions.html +1 -1
- package/docs/api/interfaces/EffectConfirmOptions.html +1 -1
- package/docs/api/interfaces/EffectFailOptions.html +1 -1
- package/docs/api/interfaces/EffectStatusOptions.html +1 -1
- package/docs/api/interfaces/ExtendLeaseOptions.html +10 -8
- package/docs/api/interfaces/FailOptions.html +13 -9
- package/docs/api/interfaces/FailOutcome.html +13 -13
- package/docs/api/interfaces/FerricStoreClientFromUrlOptions.html +1 -1
- package/docs/api/interfaces/FerricStoreClientOptions.html +1 -1
- package/docs/api/interfaces/FlowQueryCountResult.html +1 -1
- package/docs/api/interfaces/FlowQueryIndexBuild.html +1 -1
- package/docs/api/interfaces/FlowQueryIndexProgress.html +1 -1
- package/docs/api/interfaces/FlowQueryIndexValidation.html +1 -1
- package/docs/api/interfaces/FlowQueryRecordsResult.html +1 -1
- package/docs/api/interfaces/HistoryOptions.html +16 -16
- package/docs/api/interfaces/InvocationCreateOptions.html +1 -1
- package/docs/api/interfaces/LeaseMutationOptions.html +11 -7
- package/docs/api/interfaces/LimitListOptions.html +1 -1
- package/docs/api/interfaces/MutateOptions.html +8 -4
- package/docs/api/interfaces/NamedValueMutation.html +9 -9
- package/docs/api/interfaces/ReadOptions.html +12 -12
- package/docs/api/interfaces/ReclaimOptions.html +1 -1
- package/docs/api/interfaces/ReplayedStepResult.html +73 -0
- package/docs/api/interfaces/RequestContextOptions.html +1 -1
- package/docs/api/interfaces/RetryOptions.html +13 -9
- package/docs/api/interfaces/RetryOutcome.html +13 -13
- package/docs/api/interfaces/RunStepsItem.html +3 -3
- package/docs/api/interfaces/RunStepsManyOptions.html +11 -11
- package/docs/api/interfaces/SearchOptions.html +13 -13
- package/docs/api/interfaces/StepContinueOptions.html +16 -12
- package/docs/api/interfaces/StepOptions.html +166 -0
- package/docs/api/interfaces/StepResult.html +67 -0
- package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
- package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
- package/docs/api/interfaces/TransitionOptions.html +15 -11
- package/docs/api/interfaces/TransitionOutcome.html +14 -14
- package/docs/api/media/design.md +12 -1
- package/docs/api/modules.html +8 -8
- package/docs/api/types/ConnectionRequestDisposition.html +1 -1
- package/docs/api/types/ManagementPairs.html +1 -1
- package/docs/api/types/Outcome.html +1 -1
- package/docs/api/types/RequestDisposition.html +1 -1
- package/docs/api/types/SearchStateMeta.html +1 -1
- package/docs/api/types/WorkflowStepResult.html +38 -0
- package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
- package/docs/design.md +12 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ __export(index_exports, {
|
|
|
69
69
|
QueueWorker: () => QueueWorker,
|
|
70
70
|
RawCodec: () => RawCodec,
|
|
71
71
|
ReconnectingExecutor: () => ReconnectingExecutor,
|
|
72
|
+
RequestNotSentError: () => RequestNotSentError,
|
|
72
73
|
RequestTimeoutError: () => RequestTimeoutError,
|
|
73
74
|
RerouteError: () => RerouteError,
|
|
74
75
|
RoutingTopology: () => RoutingTopology,
|
|
@@ -89,6 +90,7 @@ __export(index_exports, {
|
|
|
89
90
|
complete: () => complete,
|
|
90
91
|
fail: () => fail2,
|
|
91
92
|
httpCommandDisposition: () => httpCommandDisposition,
|
|
93
|
+
isAlreadyAppliedOutcome: () => isAlreadyAppliedOutcome,
|
|
92
94
|
isOutcome: () => isOutcome,
|
|
93
95
|
isReconnectableClosedConnectionError: () => isReconnectableClosedConnectionError,
|
|
94
96
|
mapException: () => mapException,
|
|
@@ -130,9 +132,16 @@ var FerricStoreError = class extends Error {
|
|
|
130
132
|
};
|
|
131
133
|
var HTTPTransportError = class extends FerricStoreError {
|
|
132
134
|
code = "http_transport";
|
|
135
|
+
requestDisposition;
|
|
133
136
|
statusCode;
|
|
134
137
|
constructor(message, options = {}) {
|
|
135
|
-
|
|
138
|
+
const disposition = options.requestDisposition;
|
|
139
|
+
super(message, {
|
|
140
|
+
...options,
|
|
141
|
+
retryable: options.retryable ?? (disposition === "unsent" ? false : void 0),
|
|
142
|
+
safeToRetry: options.safeToRetry ?? (disposition === "unsent" ? true : disposition === "possibly_sent" ? false : void 0)
|
|
143
|
+
});
|
|
144
|
+
this.requestDisposition = disposition;
|
|
136
145
|
this.statusCode = options.statusCode;
|
|
137
146
|
}
|
|
138
147
|
};
|
|
@@ -157,6 +166,20 @@ var RequestTimeoutError = class extends FerricStoreError {
|
|
|
157
166
|
this.timeoutMs = timeoutMs;
|
|
158
167
|
}
|
|
159
168
|
};
|
|
169
|
+
var RequestNotSentError = class extends FerricStoreError {
|
|
170
|
+
code = "request_not_sent";
|
|
171
|
+
requestDisposition = "unsent";
|
|
172
|
+
constructor(message, options = {}) {
|
|
173
|
+
super(message, { ...options, retryable: false, safeToRetry: true });
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
function requestNotSentError(error, message) {
|
|
177
|
+
if (error instanceof RequestNotSentError) return error;
|
|
178
|
+
return new RequestNotSentError(
|
|
179
|
+
message ?? (error instanceof Error ? error.message : String(error)),
|
|
180
|
+
{ cause: error, raw: error }
|
|
181
|
+
);
|
|
182
|
+
}
|
|
160
183
|
var FlowNotFoundError = class extends FerricStoreError {
|
|
161
184
|
code = "flow_not_found";
|
|
162
185
|
};
|
|
@@ -6415,7 +6438,10 @@ function decodeCompactPipeline(data, budget, claimModes) {
|
|
|
6415
6438
|
);
|
|
6416
6439
|
offset = read.offset;
|
|
6417
6440
|
} else {
|
|
6418
|
-
throw new FerricStoreError(
|
|
6441
|
+
throw new FerricStoreError(`unknown compact pipeline status ${status}`, {
|
|
6442
|
+
retryable: false,
|
|
6443
|
+
safeToRetry: false
|
|
6444
|
+
});
|
|
6419
6445
|
}
|
|
6420
6446
|
}
|
|
6421
6447
|
if (offset !== data.byteLength) throw new FerricStoreError("trailing compact pipeline bytes");
|
|
@@ -6585,6 +6611,7 @@ var COUNTED_COMPACT_TAGS = /* @__PURE__ */ new Set([
|
|
|
6585
6611
|
]);
|
|
6586
6612
|
|
|
6587
6613
|
// src/protocol-response.ts
|
|
6614
|
+
var KNOWN_NATIVE_RESPONSE_STATUSES = /* @__PURE__ */ new Set([0, 1, 2, 3, 4, 5, 6]);
|
|
6588
6615
|
function decodeResponse(frame, expectedOpcode, hints = {}) {
|
|
6589
6616
|
if (frame.opcode !== expectedOpcode) {
|
|
6590
6617
|
throw new FerricStoreError(
|
|
@@ -6609,6 +6636,13 @@ function decodeResponse(frame, expectedOpcode, hints = {}) {
|
|
|
6609
6636
|
customPayload
|
|
6610
6637
|
);
|
|
6611
6638
|
if (status === STATUS_OK) return value;
|
|
6639
|
+
if (!KNOWN_NATIVE_RESPONSE_STATUSES.has(status)) {
|
|
6640
|
+
throw new FerricStoreError(`unknown native response status ${status}`, {
|
|
6641
|
+
raw: value,
|
|
6642
|
+
retryable: false,
|
|
6643
|
+
safeToRetry: false
|
|
6644
|
+
});
|
|
6645
|
+
}
|
|
6612
6646
|
const message = protocolErrorMessage(status, value);
|
|
6613
6647
|
throw classifyServerError(message, value, void 0, status);
|
|
6614
6648
|
}
|
|
@@ -6648,7 +6682,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
|
|
|
6648
6682
|
continue;
|
|
6649
6683
|
}
|
|
6650
6684
|
const error = classifyServerError(
|
|
6651
|
-
protocolErrorMessage(status
|
|
6685
|
+
protocolErrorMessage(pipelineStatusCode(status), payload),
|
|
6652
6686
|
payload,
|
|
6653
6687
|
void 0,
|
|
6654
6688
|
status
|
|
@@ -6657,15 +6691,46 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
|
|
|
6657
6691
|
out[index] = error;
|
|
6658
6692
|
continue;
|
|
6659
6693
|
}
|
|
6694
|
+
if (pipelineStatusToken(item[0]) != null) {
|
|
6695
|
+
const unknown = pipelineStatusToken(item[0]) ?? "unknown";
|
|
6696
|
+
const error = new FerricStoreError(`unknown native pipeline status ${JSON.stringify(unknown)}`, {
|
|
6697
|
+
raw: item,
|
|
6698
|
+
retryable: false,
|
|
6699
|
+
safeToRetry: false
|
|
6700
|
+
});
|
|
6701
|
+
if (options.throwOnItemError !== false) throw error;
|
|
6702
|
+
out[index] = error;
|
|
6703
|
+
continue;
|
|
6704
|
+
}
|
|
6660
6705
|
}
|
|
6661
6706
|
out[index] = item;
|
|
6662
6707
|
}
|
|
6663
6708
|
return out;
|
|
6664
6709
|
}
|
|
6665
6710
|
function pipelineStatus(value) {
|
|
6711
|
+
const status = pipelineStatusToken(value);
|
|
6712
|
+
if (status == null) return null;
|
|
6713
|
+
return status === "ok" || status === "error" || status === "auth" || status === "noperm" || status === "busy" || status === "reroute" || status === "bad_request" ? status : null;
|
|
6714
|
+
}
|
|
6715
|
+
function pipelineStatusToken(value) {
|
|
6666
6716
|
if (typeof value !== "string" && !import_node_buffer26.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return null;
|
|
6667
|
-
|
|
6668
|
-
|
|
6717
|
+
return asText(value).toLowerCase();
|
|
6718
|
+
}
|
|
6719
|
+
function pipelineStatusCode(status) {
|
|
6720
|
+
switch (status) {
|
|
6721
|
+
case "error":
|
|
6722
|
+
return 1;
|
|
6723
|
+
case "auth":
|
|
6724
|
+
return 2;
|
|
6725
|
+
case "noperm":
|
|
6726
|
+
return 3;
|
|
6727
|
+
case "busy":
|
|
6728
|
+
return 4;
|
|
6729
|
+
case "reroute":
|
|
6730
|
+
return 5;
|
|
6731
|
+
case "bad_request":
|
|
6732
|
+
return 6;
|
|
6733
|
+
}
|
|
6669
6734
|
}
|
|
6670
6735
|
function decodeResponseValue(opcode, body, hints, allowCompact, customPayload) {
|
|
6671
6736
|
if (customPayload && allowCompact) {
|
|
@@ -8325,6 +8390,22 @@ function plainRecord(value) {
|
|
|
8325
8390
|
return value;
|
|
8326
8391
|
}
|
|
8327
8392
|
|
|
8393
|
+
// src/native-command-preparation.ts
|
|
8394
|
+
function prepareNativeCommand(args, maxFrameBytes) {
|
|
8395
|
+
try {
|
|
8396
|
+
return buildProtocolCommand(args, maxFrameBytes);
|
|
8397
|
+
} catch (error) {
|
|
8398
|
+
throw requestNotSentError(error);
|
|
8399
|
+
}
|
|
8400
|
+
}
|
|
8401
|
+
function encodeNativeRequest(command, requestId, maxFrameBytes) {
|
|
8402
|
+
try {
|
|
8403
|
+
return encodeRequest(command, requestId, maxFrameBytes);
|
|
8404
|
+
} catch (error) {
|
|
8405
|
+
throw requestNotSentError(error);
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8408
|
+
|
|
8328
8409
|
// src/native-adapter.ts
|
|
8329
8410
|
var NativeAdapter = class _NativeAdapter {
|
|
8330
8411
|
socket;
|
|
@@ -8432,15 +8513,13 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
8432
8513
|
return await this.executeCommandArgs(args);
|
|
8433
8514
|
}
|
|
8434
8515
|
async executeCommandArgs(args) {
|
|
8435
|
-
const command =
|
|
8516
|
+
const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
|
|
8436
8517
|
return await this.request(command);
|
|
8437
8518
|
}
|
|
8438
8519
|
/** @internal Execute raw command arguments on a topology-selected lane. */
|
|
8439
8520
|
async executeCommandOnLane(args, laneId) {
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
laneId
|
|
8443
|
-
);
|
|
8521
|
+
const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
|
|
8522
|
+
return await this.executeProtocolCommand(command, laneId);
|
|
8444
8523
|
}
|
|
8445
8524
|
async executeProtocolCommand(command, laneId) {
|
|
8446
8525
|
return await this.request(laneId == null ? command : { ...command, laneId });
|
|
@@ -8534,14 +8613,14 @@ var NativeAdapter = class _NativeAdapter {
|
|
|
8534
8613
|
let frame;
|
|
8535
8614
|
try {
|
|
8536
8615
|
requestId = this.requestScheduler.nextRequestId();
|
|
8537
|
-
frame =
|
|
8616
|
+
frame = encodeNativeRequest(
|
|
8538
8617
|
withFlowQueryDeadline(command, timeoutMs),
|
|
8539
8618
|
requestId,
|
|
8540
8619
|
this.capabilities.requestFrameBytes
|
|
8541
8620
|
);
|
|
8542
8621
|
} catch (error) {
|
|
8543
8622
|
if (hasFlowControlCredit) this.flowControl.release(laneId);
|
|
8544
|
-
return Promise.reject(error instanceof Error ? error :
|
|
8623
|
+
return Promise.reject(error instanceof Error ? error : requestNotSentError(error));
|
|
8545
8624
|
}
|
|
8546
8625
|
return new Promise((resolve, reject) => {
|
|
8547
8626
|
const timer = timeoutMs == null ? void 0 : setLongTimeout(() => {
|
|
@@ -9116,12 +9195,16 @@ var HTTPTransport = class {
|
|
|
9116
9195
|
#requests = /* @__PURE__ */ new Set();
|
|
9117
9196
|
#closed = false;
|
|
9118
9197
|
async post(body, timeoutMs) {
|
|
9119
|
-
if (this.#closed)
|
|
9198
|
+
if (this.#closed) {
|
|
9199
|
+
throw new HTTPTransportError("HTTP transport is closed", { requestDisposition: "unsent" });
|
|
9200
|
+
}
|
|
9120
9201
|
const controller = new AbortController();
|
|
9121
9202
|
this.#requests.add(controller);
|
|
9122
9203
|
const timer = timeoutMs == null ? void 0 : setLongTimeout(() => controller.abort(requestTimeoutReason), timeoutMs);
|
|
9123
9204
|
timer?.unref();
|
|
9124
9205
|
try {
|
|
9206
|
+
const response = await this.request(this.config.commandUrl, "POST", body, 0, controller.signal);
|
|
9207
|
+
if (response.status !== 401 || this.config.headers.authorization == null) return response;
|
|
9125
9208
|
return await this.request(this.config.commandUrl, "POST", body, 0, controller.signal);
|
|
9126
9209
|
} catch (error) {
|
|
9127
9210
|
if (controller.signal.aborted) {
|
|
@@ -9131,9 +9214,22 @@ var HTTPTransport = class {
|
|
|
9131
9214
|
raw: { retryable: true, safe_to_retry: false }
|
|
9132
9215
|
});
|
|
9133
9216
|
}
|
|
9134
|
-
if (error instanceof
|
|
9217
|
+
if (error instanceof RequestTimeoutError) throw error;
|
|
9218
|
+
if (error instanceof HTTPTransportError) {
|
|
9219
|
+
if (error.requestDisposition != null) throw error;
|
|
9220
|
+
throw new HTTPTransportError(error.message, {
|
|
9221
|
+
cause: error.cause ?? error,
|
|
9222
|
+
raw: error.raw,
|
|
9223
|
+
requestDisposition: "possibly_sent",
|
|
9224
|
+
retryAfterMs: error.retryAfterMs,
|
|
9225
|
+
retryable: error.retryable,
|
|
9226
|
+
safeToRetry: false,
|
|
9227
|
+
statusCode: error.statusCode
|
|
9228
|
+
});
|
|
9229
|
+
}
|
|
9135
9230
|
throw new HTTPTransportError("HTTP command request failed", {
|
|
9136
9231
|
cause: error,
|
|
9232
|
+
requestDisposition: "possibly_sent",
|
|
9137
9233
|
retryable: true,
|
|
9138
9234
|
safeToRetry: false
|
|
9139
9235
|
});
|
|
@@ -9145,7 +9241,9 @@ var HTTPTransport = class {
|
|
|
9145
9241
|
async close() {
|
|
9146
9242
|
if (this.#closed) return;
|
|
9147
9243
|
this.#closed = true;
|
|
9148
|
-
const error = new HTTPTransportError("HTTP transport is closed"
|
|
9244
|
+
const error = new HTTPTransportError("HTTP transport is closed", {
|
|
9245
|
+
requestDisposition: "possibly_sent"
|
|
9246
|
+
});
|
|
9149
9247
|
for (const controller of this.#requests) controller.abort(error);
|
|
9150
9248
|
this.#httpAgent.destroy();
|
|
9151
9249
|
this.#httpsAgent.destroy();
|
|
@@ -9388,16 +9486,22 @@ var HTTPAdapter = class _HTTPAdapter {
|
|
|
9388
9486
|
await this.#transport.close();
|
|
9389
9487
|
}
|
|
9390
9488
|
async executeBatch(commands) {
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9489
|
+
let body;
|
|
9490
|
+
let serverBlockMs;
|
|
9491
|
+
try {
|
|
9492
|
+
if (commands.length > this.#config.maxBatchItems) {
|
|
9493
|
+
throw new HTTPTransportError("HTTP command batch exceeds maxBatchItems");
|
|
9494
|
+
}
|
|
9495
|
+
for (const command of commands) assertHTTPCommandSupported(command[0]);
|
|
9496
|
+
const prepared = commands.map((command) => prepareHTTPCommand(command, this.#config.maxRequestBytes));
|
|
9497
|
+
body = encodeHTTPCommands(prepared.map((command) => command.encoded), this.#config.maxRequestBytes);
|
|
9498
|
+
if (body.byteLength > this.#config.maxRequestBytes) {
|
|
9499
|
+
throw new HTTPTransportError("HTTP command request exceeds maxRequestBytes");
|
|
9500
|
+
}
|
|
9501
|
+
serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
|
|
9502
|
+
} catch (error) {
|
|
9503
|
+
throw unsentHTTPError(error);
|
|
9399
9504
|
}
|
|
9400
|
-
const serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
|
|
9401
9505
|
const response = await this.#transport.post(
|
|
9402
9506
|
body,
|
|
9403
9507
|
serverResponseTimeoutMs(this.#config.timeoutMs, serverBlockMs)
|
|
@@ -9454,16 +9558,32 @@ function commandError(value) {
|
|
|
9454
9558
|
}
|
|
9455
9559
|
function topLevelError(status, envelope, retryAfter) {
|
|
9456
9560
|
const details = isRecord2(envelope.error) ? envelope.error : {};
|
|
9561
|
+
const code = typeof details.code === "string" ? details.code.toLowerCase() : void 0;
|
|
9457
9562
|
const message = typeof details.message === "string" ? details.message : `HTTP command request failed with status ${status}`;
|
|
9458
9563
|
const retryAfterMs2 = retryAfterMilliseconds(retryAfter);
|
|
9564
|
+
const ambiguousTimeout = status === 408 || code === "request_timeout";
|
|
9565
|
+
const safeToRetry = !ambiguousTimeout && (details.safe_to_retry === true || definitelyRejectedHTTPStatus(status));
|
|
9459
9566
|
return new HTTPTransportError(message, {
|
|
9460
9567
|
raw: details,
|
|
9461
9568
|
retryable: status === 408 || status === 425 || status === 429 || status >= 500,
|
|
9462
9569
|
retryAfterMs: retryAfterMs2,
|
|
9463
|
-
|
|
9570
|
+
requestDisposition: "possibly_sent",
|
|
9571
|
+
safeToRetry,
|
|
9464
9572
|
statusCode: status
|
|
9465
9573
|
});
|
|
9466
9574
|
}
|
|
9575
|
+
function definitelyRejectedHTTPStatus(status) {
|
|
9576
|
+
return status === 400 || status === 401 || status === 403 || status === 404 || status === 405 || status === 406 || status === 411 || status === 413 || status === 414 || status === 415 || status === 422 || status === 426 || status === 431;
|
|
9577
|
+
}
|
|
9578
|
+
function unsentHTTPError(error) {
|
|
9579
|
+
if (error instanceof HTTPTransportError && error.requestDisposition === "unsent") return error;
|
|
9580
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9581
|
+
return new HTTPTransportError(message, {
|
|
9582
|
+
cause: error,
|
|
9583
|
+
raw: error instanceof FerricStoreError ? error.raw ?? error : error,
|
|
9584
|
+
requestDisposition: "unsent"
|
|
9585
|
+
});
|
|
9586
|
+
}
|
|
9467
9587
|
function retryAfterMilliseconds(value) {
|
|
9468
9588
|
if (value == null) return void 0;
|
|
9469
9589
|
if (/^\d+$/u.test(value)) {
|
|
@@ -9556,9 +9676,9 @@ var ReconnectingExecutor = class {
|
|
|
9556
9676
|
const snapshot = snapshotPipelineCommands(commands);
|
|
9557
9677
|
const snapshotOptions = snapshotPipelineOptions(options) ?? {};
|
|
9558
9678
|
for (const command of snapshot) assertCommandHasStableConnectionState(command);
|
|
9559
|
-
if (this.closed) throw new
|
|
9679
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9560
9680
|
const initialExecutor = await this.executorPromise;
|
|
9561
|
-
if (this.closed) throw new
|
|
9681
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9562
9682
|
if (initialExecutor.executePipeline == null) {
|
|
9563
9683
|
return await executeCommandArraysIndividually(
|
|
9564
9684
|
async (command) => await this.executeCommandArgs(command),
|
|
@@ -9579,9 +9699,9 @@ var ReconnectingExecutor = class {
|
|
|
9579
9699
|
const snapshot = snapshotPipelineCommands(commands);
|
|
9580
9700
|
const snapshotOptions = snapshotPipelineOptions(options) ?? {};
|
|
9581
9701
|
for (const command of snapshot) assertCommandHasStableConnectionState(command);
|
|
9582
|
-
if (this.closed) throw new
|
|
9702
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9583
9703
|
const initialExecutor = await this.executorPromise;
|
|
9584
|
-
if (this.closed) throw new
|
|
9704
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9585
9705
|
if (initialExecutor.executeFusedPipeline == null) return void 0;
|
|
9586
9706
|
try {
|
|
9587
9707
|
return await initialExecutor.executeFusedPipeline(snapshot, snapshotOptions);
|
|
@@ -9614,7 +9734,7 @@ var ReconnectingExecutor = class {
|
|
|
9614
9734
|
return;
|
|
9615
9735
|
}
|
|
9616
9736
|
this.closed = true;
|
|
9617
|
-
this.retryAbortController.abort(new
|
|
9737
|
+
this.retryAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
|
|
9618
9738
|
const executorPromise = this.executorPromise;
|
|
9619
9739
|
const reconnectPromise = this.reconnectPromise;
|
|
9620
9740
|
this.closePromise = (async () => {
|
|
@@ -9636,7 +9756,7 @@ var ReconnectingExecutor = class {
|
|
|
9636
9756
|
let executor = await this.executorPromise;
|
|
9637
9757
|
let retries = 0;
|
|
9638
9758
|
for (; ; ) {
|
|
9639
|
-
if (this.closed) throw new
|
|
9759
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9640
9760
|
try {
|
|
9641
9761
|
return await operation(executor);
|
|
9642
9762
|
} catch (error) {
|
|
@@ -9665,17 +9785,17 @@ var ReconnectingExecutor = class {
|
|
|
9665
9785
|
try {
|
|
9666
9786
|
await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
|
|
9667
9787
|
} catch (error) {
|
|
9668
|
-
if (this.closed) throw new
|
|
9788
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed", { cause: error });
|
|
9669
9789
|
throw error;
|
|
9670
9790
|
}
|
|
9671
9791
|
}
|
|
9672
9792
|
async reconnect(staleExecutor) {
|
|
9673
|
-
if (this.closed) throw new
|
|
9793
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9674
9794
|
const competingReconnect = this.reconnectPromise;
|
|
9675
9795
|
if (competingReconnect != null) return await competingReconnect;
|
|
9676
9796
|
const observedExecutorPromise = this.executorPromise;
|
|
9677
9797
|
const currentExecutor = await observedExecutorPromise.catch(() => void 0);
|
|
9678
|
-
if (this.closed) throw new
|
|
9798
|
+
if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
|
|
9679
9799
|
if (this.reconnectPromise != null) return await this.reconnectPromise;
|
|
9680
9800
|
if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
|
|
9681
9801
|
if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
|
|
@@ -9684,7 +9804,7 @@ var ReconnectingExecutor = class {
|
|
|
9684
9804
|
const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
|
|
9685
9805
|
if (this.closed) {
|
|
9686
9806
|
await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
|
|
9687
|
-
throw new
|
|
9807
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
9688
9808
|
}
|
|
9689
9809
|
this.executorPromise = Promise.resolve(nextExecutor);
|
|
9690
9810
|
return nextExecutor;
|
|
@@ -11094,7 +11214,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
|
|
|
11094
11214
|
);
|
|
11095
11215
|
}
|
|
11096
11216
|
assertOpen() {
|
|
11097
|
-
if (this.closed) throw new
|
|
11217
|
+
if (this.closed) throw new RequestNotSentError("FerricStore topology adapter pool is closed");
|
|
11098
11218
|
}
|
|
11099
11219
|
};
|
|
11100
11220
|
|
|
@@ -12220,7 +12340,7 @@ var AutoBatchExecutor = class {
|
|
|
12220
12340
|
}
|
|
12221
12341
|
async executeCommandArgs(args) {
|
|
12222
12342
|
if (this.closed) {
|
|
12223
|
-
throw new
|
|
12343
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12224
12344
|
}
|
|
12225
12345
|
const command = snapshotCommandArguments(args);
|
|
12226
12346
|
if (this.executor.executePipeline == null) {
|
|
@@ -12240,7 +12360,7 @@ var AutoBatchExecutor = class {
|
|
|
12240
12360
|
if (orderingBarrier != null) {
|
|
12241
12361
|
await orderingBarrier;
|
|
12242
12362
|
if (this.closed) {
|
|
12243
|
-
throw new
|
|
12363
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12244
12364
|
}
|
|
12245
12365
|
}
|
|
12246
12366
|
return await new Promise((resolve, reject) => {
|
|
@@ -12254,7 +12374,7 @@ var AutoBatchExecutor = class {
|
|
|
12254
12374
|
}
|
|
12255
12375
|
async executePipeline(commands, options) {
|
|
12256
12376
|
if (this.closed) {
|
|
12257
|
-
throw new
|
|
12377
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12258
12378
|
}
|
|
12259
12379
|
const snapshot = snapshotPipelineCommands(commands);
|
|
12260
12380
|
const snapshotOptions = snapshotPipelineOptions(options);
|
|
@@ -12271,7 +12391,7 @@ var AutoBatchExecutor = class {
|
|
|
12271
12391
|
}
|
|
12272
12392
|
async executeFusedPipeline(commands, options) {
|
|
12273
12393
|
if (this.closed) {
|
|
12274
|
-
throw new
|
|
12394
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12275
12395
|
}
|
|
12276
12396
|
if (this.executor.executeFusedPipeline == null) return void 0;
|
|
12277
12397
|
const snapshot = snapshotPipelineCommands(commands);
|
|
@@ -12282,7 +12402,7 @@ var AutoBatchExecutor = class {
|
|
|
12282
12402
|
}
|
|
12283
12403
|
async refreshTopology() {
|
|
12284
12404
|
if (this.closed) {
|
|
12285
|
-
throw new
|
|
12405
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12286
12406
|
}
|
|
12287
12407
|
const refreshTopology = this.executor.refreshTopology?.bind(this.executor);
|
|
12288
12408
|
if (refreshTopology == null) {
|
|
@@ -12292,7 +12412,7 @@ var AutoBatchExecutor = class {
|
|
|
12292
12412
|
}
|
|
12293
12413
|
async route(key) {
|
|
12294
12414
|
if (this.closed) {
|
|
12295
|
-
throw new
|
|
12415
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12296
12416
|
}
|
|
12297
12417
|
const route = this.executor.route?.bind(this.executor);
|
|
12298
12418
|
if (route == null) {
|
|
@@ -12312,7 +12432,7 @@ var AutoBatchExecutor = class {
|
|
|
12312
12432
|
this.timer.cancel();
|
|
12313
12433
|
this.timer = void 0;
|
|
12314
12434
|
}
|
|
12315
|
-
this.failPending(new
|
|
12435
|
+
this.failPending(new RequestNotSentError("FerricStore client closed before auto-batch flush"));
|
|
12316
12436
|
await this.orderingBarrier;
|
|
12317
12437
|
await this.waitForInFlightBatches();
|
|
12318
12438
|
await Promise.allSettled([...this.inFlightHelpers]);
|
|
@@ -12322,7 +12442,7 @@ var AutoBatchExecutor = class {
|
|
|
12322
12442
|
}
|
|
12323
12443
|
async executeAfterPendingBatches(operation) {
|
|
12324
12444
|
if (this.closed) {
|
|
12325
|
-
throw new
|
|
12445
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12326
12446
|
}
|
|
12327
12447
|
const previousBarrier = this.orderingBarrier;
|
|
12328
12448
|
let releaseBarrier;
|
|
@@ -12343,7 +12463,7 @@ var AutoBatchExecutor = class {
|
|
|
12343
12463
|
}
|
|
12344
12464
|
executeHelper(operation) {
|
|
12345
12465
|
if (this.closed) {
|
|
12346
|
-
return Promise.reject(new
|
|
12466
|
+
return Promise.reject(new RequestNotSentError("FerricStore client is closed"));
|
|
12347
12467
|
}
|
|
12348
12468
|
const task = Promise.resolve().then(operation);
|
|
12349
12469
|
this.inFlightHelpers.add(task);
|
|
@@ -12365,7 +12485,7 @@ var AutoBatchExecutor = class {
|
|
|
12365
12485
|
await previousBarrier;
|
|
12366
12486
|
await this.flushNow();
|
|
12367
12487
|
if (this.closed) {
|
|
12368
|
-
throw new
|
|
12488
|
+
throw new RequestNotSentError("FerricStore client is closed");
|
|
12369
12489
|
}
|
|
12370
12490
|
dispatched = operation();
|
|
12371
12491
|
} finally {
|
|
@@ -14704,7 +14824,7 @@ var FerricStoreClientBase = class {
|
|
|
14704
14824
|
return validatePipelineResponse(results, snapshot.length);
|
|
14705
14825
|
}
|
|
14706
14826
|
async close() {
|
|
14707
|
-
this.closeAbortController.abort(new
|
|
14827
|
+
this.closeAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
|
|
14708
14828
|
this.closePromise ??= (async () => await this.executor.close?.())();
|
|
14709
14829
|
await this.closePromise;
|
|
14710
14830
|
}
|
|
@@ -17780,7 +17900,7 @@ async function executeProducerWriteWithBackpressure(execute, policy, signal) {
|
|
|
17780
17900
|
function throwIfClosed(signal) {
|
|
17781
17901
|
if (!signal.aborted) return;
|
|
17782
17902
|
if (signal.reason instanceof Error) throw signal.reason;
|
|
17783
|
-
throw new
|
|
17903
|
+
throw new RequestNotSentError("FerricStore client is closed", { raw: signal.reason });
|
|
17784
17904
|
}
|
|
17785
17905
|
|
|
17786
17906
|
// src/flow-policy.ts
|
|
@@ -18598,12 +18718,20 @@ var FerricStoreClaimClient = class extends FerricStoreMutationClient {
|
|
|
18598
18718
|
return this.records(items);
|
|
18599
18719
|
}
|
|
18600
18720
|
async claimJobs(type, options) {
|
|
18601
|
-
|
|
18721
|
+
const requestedState = options.state;
|
|
18722
|
+
const includeState = options.includeState ?? requestedState == null;
|
|
18723
|
+
const jobs = await this.claimDue(type, {
|
|
18602
18724
|
...options,
|
|
18603
|
-
includeState
|
|
18725
|
+
includeState,
|
|
18604
18726
|
jobOnly: true,
|
|
18605
18727
|
limit: options.limit ?? 100
|
|
18606
18728
|
});
|
|
18729
|
+
if (!includeState && requestedState != null) {
|
|
18730
|
+
for (const job of jobs) {
|
|
18731
|
+
if (job[CLAIMED_ITEM_WIRE] != null) job.runState ??= requestedState;
|
|
18732
|
+
}
|
|
18733
|
+
}
|
|
18734
|
+
return jobs;
|
|
18607
18735
|
}
|
|
18608
18736
|
async reclaim(type, options) {
|
|
18609
18737
|
const captured = snapshotClaimOptions(options);
|
|
@@ -18626,7 +18754,7 @@ var FerricStoreClaimClient = class extends FerricStoreMutationClient {
|
|
|
18626
18754
|
this.appendPartitionOptions(args, captured);
|
|
18627
18755
|
append(args, "PRIORITY", captured.priority);
|
|
18628
18756
|
if (jobOnly) {
|
|
18629
|
-
append(args, "RETURN", compactClaimReturnMode2(
|
|
18757
|
+
append(args, "RETURN", compactClaimReturnMode2(true, captured.includeAttributes === true));
|
|
18630
18758
|
}
|
|
18631
18759
|
appendPayloadRead(args, captured.payload, captured.payloadMaxBytes);
|
|
18632
18760
|
appendValueReturn(args, { values: captured.values, valueMaxBytes: captured.valueMaxBytes });
|
|
@@ -19007,6 +19135,291 @@ var FerricStoreProducerClient = class extends FerricStoreClaimClient {
|
|
|
19007
19135
|
}
|
|
19008
19136
|
};
|
|
19009
19137
|
|
|
19138
|
+
// src/client-durable-step.ts
|
|
19139
|
+
var import_node_crypto2 = require("crypto");
|
|
19140
|
+
|
|
19141
|
+
// src/workflow-utilities.ts
|
|
19142
|
+
function resolveWorkflowStates(configured, registrationFor) {
|
|
19143
|
+
const stateNames = [...new Set(configured)];
|
|
19144
|
+
if (stateNames.length === 0) throw new Error("Workflow worker requires at least one state");
|
|
19145
|
+
return stateNames.map((stateName) => {
|
|
19146
|
+
const registration = registrationFor(stateName);
|
|
19147
|
+
if (registration == null) {
|
|
19148
|
+
throw new Error(`No handler registered for workflow state '${stateName}'`);
|
|
19149
|
+
}
|
|
19150
|
+
return { registration, stateName };
|
|
19151
|
+
});
|
|
19152
|
+
}
|
|
19153
|
+
function normalizeStateMode(mode) {
|
|
19154
|
+
if (mode === "fifo" || mode === "parallel") return mode;
|
|
19155
|
+
throw new Error("state mode must be 'fifo' or 'parallel'");
|
|
19156
|
+
}
|
|
19157
|
+
function valueRefToString(value) {
|
|
19158
|
+
if (typeof value === "string") return value;
|
|
19159
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8");
|
|
19160
|
+
if (typeof value === "object" && value != null && Object.hasOwn(value, "ref")) {
|
|
19161
|
+
const ref = value.ref;
|
|
19162
|
+
return typeof ref === "string" ? ref : Buffer.isBuffer(ref) ? ref.toString("utf8") : void 0;
|
|
19163
|
+
}
|
|
19164
|
+
return void 0;
|
|
19165
|
+
}
|
|
19166
|
+
|
|
19167
|
+
// src/client-durable-step.ts
|
|
19168
|
+
var DURABLE_STEP_VALUE_PREFIX = "__ferricstore_step__:sha256:";
|
|
19169
|
+
function durableMutationMayHaveCommitted(error) {
|
|
19170
|
+
if (error instanceof ConnectionClosedError || error instanceof RequestTimeoutError) {
|
|
19171
|
+
return error.requestDisposition === "possibly_sent";
|
|
19172
|
+
}
|
|
19173
|
+
if (error instanceof HTTPTransportError) return error.safeToRetry !== true;
|
|
19174
|
+
if (error instanceof FlowNotFoundError || error instanceof FlowWrongStateError || error instanceof InvalidCommandError || error instanceof StaleLeaseError) {
|
|
19175
|
+
return false;
|
|
19176
|
+
}
|
|
19177
|
+
if (error instanceof FerricStoreError && error.safeToRetry === true) return false;
|
|
19178
|
+
return true;
|
|
19179
|
+
}
|
|
19180
|
+
async function advanceClaim(client, job, options) {
|
|
19181
|
+
assertDurableClaim(job);
|
|
19182
|
+
assertNonEmptyString(options.toState, "toState");
|
|
19183
|
+
const partitionKey = job.partitionKey === "" ? void 0 : job.partitionKey;
|
|
19184
|
+
const response = await client.stepContinue(job.id, {
|
|
19185
|
+
attributesDelete: options.attributesDelete,
|
|
19186
|
+
attributesMerge: options.attributesMerge,
|
|
19187
|
+
dropValues: options.dropValues,
|
|
19188
|
+
fencingToken: job.fencingToken,
|
|
19189
|
+
fromState: job.runState,
|
|
19190
|
+
leaseMs: options.leaseMs,
|
|
19191
|
+
leaseToken: job.leaseToken,
|
|
19192
|
+
nowMs: options.nowMs,
|
|
19193
|
+
overrideValues: options.overrideValues,
|
|
19194
|
+
partitionKey,
|
|
19195
|
+
payload: options.payload,
|
|
19196
|
+
returnJob: true,
|
|
19197
|
+
stateMeta: options.stateMeta,
|
|
19198
|
+
type: job.type,
|
|
19199
|
+
toState: options.toState,
|
|
19200
|
+
valueRefs: options.valueRefs,
|
|
19201
|
+
values: options.values
|
|
19202
|
+
});
|
|
19203
|
+
const refreshed = response;
|
|
19204
|
+
if (refreshed[CLAIMED_ITEM_WIRE] != null) {
|
|
19205
|
+
if (refreshed.runState != null && refreshed.runState !== options.toState) {
|
|
19206
|
+
throw new FerricStoreError("compact continuation response returned the wrong runState");
|
|
19207
|
+
}
|
|
19208
|
+
refreshed.runState ??= options.toState;
|
|
19209
|
+
if (refreshed.type == null && job.type != null) refreshed.type = job.type;
|
|
19210
|
+
}
|
|
19211
|
+
assertContinuationClaim(job, refreshed, options.toState);
|
|
19212
|
+
return refreshed;
|
|
19213
|
+
}
|
|
19214
|
+
async function runDurableStep(client, job, options, hooks = {}) {
|
|
19215
|
+
assertDurableClaim(job);
|
|
19216
|
+
const name = options.name;
|
|
19217
|
+
const run2 = options.run;
|
|
19218
|
+
const continuation = snapshotAdvanceOptions(options);
|
|
19219
|
+
assertNonEmptyString(name, "name");
|
|
19220
|
+
if (name.trim().length === 0) throw new TypeError("name must not be blank");
|
|
19221
|
+
if (!name.isWellFormed()) throw new TypeError("name must contain valid Unicode");
|
|
19222
|
+
assertNonEmptyString(continuation.toState, "toState");
|
|
19223
|
+
if (typeof run2 !== "function") throw new TypeError("run must be a function");
|
|
19224
|
+
const resultName = durableStepValueName(name);
|
|
19225
|
+
assertStepValueIsReserved(continuation, resultName);
|
|
19226
|
+
const leaseMs = continuation.leaseMs ?? 3e4;
|
|
19227
|
+
const partitionKey = job.partitionKey === "" ? void 0 : job.partitionKey;
|
|
19228
|
+
const validated = await client.extendLease(job.id, {
|
|
19229
|
+
fencingToken: job.fencingToken,
|
|
19230
|
+
leaseMs,
|
|
19231
|
+
leaseToken: job.leaseToken,
|
|
19232
|
+
nowMs: continuation.nowMs,
|
|
19233
|
+
partitionKey
|
|
19234
|
+
});
|
|
19235
|
+
assertValidatedLease(job, validated);
|
|
19236
|
+
if (validated.valueRefs != null && Object.hasOwn(validated.valueRefs, resultName)) {
|
|
19237
|
+
if (validated.runState !== continuation.toState) {
|
|
19238
|
+
throw new FerricStoreError("committed durable step result does not match the requested target state");
|
|
19239
|
+
}
|
|
19240
|
+
const ref = valueRefToString(validated.valueRefs[resultName]);
|
|
19241
|
+
if (ref == null) {
|
|
19242
|
+
throw new FerricStoreError("committed durable step result has invalid reference metadata");
|
|
19243
|
+
}
|
|
19244
|
+
const entry = (await valueMGetEntries(client, [ref]))[0];
|
|
19245
|
+
if (entry?.found !== true) {
|
|
19246
|
+
throw new FerricStoreError("committed durable step result is missing");
|
|
19247
|
+
}
|
|
19248
|
+
const replayedJob = claimedItemFromRecord(validated);
|
|
19249
|
+
notifyWithoutReplacingOutcome(hooks.replayed, replayedJob);
|
|
19250
|
+
return { job: replayedJob, result: entry.value };
|
|
19251
|
+
}
|
|
19252
|
+
const result = normalizeStepResult(client, await run2());
|
|
19253
|
+
await hooks.beforeCommit?.();
|
|
19254
|
+
let refreshed;
|
|
19255
|
+
try {
|
|
19256
|
+
refreshed = await advanceClaim(client, validated, {
|
|
19257
|
+
...continuation,
|
|
19258
|
+
leaseMs,
|
|
19259
|
+
values: { ...continuation.values, [resultName]: result }
|
|
19260
|
+
});
|
|
19261
|
+
} catch (error) {
|
|
19262
|
+
notifyWithoutReplacingOutcome(hooks.commitFailed, error);
|
|
19263
|
+
throw error;
|
|
19264
|
+
}
|
|
19265
|
+
notifyWithoutReplacingOutcome(hooks.committed, refreshed);
|
|
19266
|
+
return { job: refreshed, result };
|
|
19267
|
+
}
|
|
19268
|
+
function notifyWithoutReplacingOutcome(callback, value) {
|
|
19269
|
+
try {
|
|
19270
|
+
callback?.(value);
|
|
19271
|
+
} catch {
|
|
19272
|
+
}
|
|
19273
|
+
}
|
|
19274
|
+
function normalizeStepResult(client, value) {
|
|
19275
|
+
return client.codec.decode(client.codec.encode(value));
|
|
19276
|
+
}
|
|
19277
|
+
function durableStepValueName(name) {
|
|
19278
|
+
const digest = (0, import_node_crypto2.createHash)("sha256").update(name, "utf8").digest("hex");
|
|
19279
|
+
return `${DURABLE_STEP_VALUE_PREFIX}${digest}`;
|
|
19280
|
+
}
|
|
19281
|
+
function snapshotAdvanceOptions(options) {
|
|
19282
|
+
return {
|
|
19283
|
+
...options.attributesDelete == null ? {} : { attributesDelete: [...options.attributesDelete] },
|
|
19284
|
+
...options.attributesMerge == null ? {} : { attributesMerge: { ...options.attributesMerge } },
|
|
19285
|
+
...options.dropValues == null ? {} : { dropValues: [...options.dropValues] },
|
|
19286
|
+
...options.leaseMs == null ? {} : { leaseMs: options.leaseMs },
|
|
19287
|
+
...options.nowMs == null ? {} : { nowMs: options.nowMs },
|
|
19288
|
+
...options.overrideValues == null ? {} : { overrideValues: [...options.overrideValues] },
|
|
19289
|
+
...options.payload === void 0 ? {} : { payload: options.payload },
|
|
19290
|
+
...options.stateMeta == null ? {} : { stateMeta: { ...options.stateMeta } },
|
|
19291
|
+
toState: options.toState,
|
|
19292
|
+
...options.valueRefs == null ? {} : { valueRefs: { ...options.valueRefs } },
|
|
19293
|
+
...options.values == null ? {} : { values: { ...options.values } }
|
|
19294
|
+
};
|
|
19295
|
+
}
|
|
19296
|
+
function assertDurableClaim(job) {
|
|
19297
|
+
if (typeof job !== "object" || job == null) throw new TypeError("job must be a claimed Flow");
|
|
19298
|
+
assertNonEmptyString(job.id, "job.id");
|
|
19299
|
+
assertNonEmptyString(job.runState, "job.runState");
|
|
19300
|
+
if (!Buffer.isBuffer(job.leaseToken) || job.leaseToken.byteLength === 0) {
|
|
19301
|
+
throw new TypeError("job.leaseToken must be a non-empty Buffer");
|
|
19302
|
+
}
|
|
19303
|
+
if ((typeof job.fencingToken !== "number" || !Number.isSafeInteger(job.fencingToken)) && typeof job.fencingToken !== "bigint") {
|
|
19304
|
+
throw new TypeError("job.fencingToken must be an integer");
|
|
19305
|
+
}
|
|
19306
|
+
if (fencingValue(job.fencingToken) <= 0n) {
|
|
19307
|
+
throw new TypeError("job.fencingToken must be a positive integer");
|
|
19308
|
+
}
|
|
19309
|
+
if (job.state !== "running") {
|
|
19310
|
+
throw new TypeError("job.state must be running");
|
|
19311
|
+
}
|
|
19312
|
+
}
|
|
19313
|
+
function assertValidatedLease(expected, actual) {
|
|
19314
|
+
try {
|
|
19315
|
+
assertDurableClaim(actual);
|
|
19316
|
+
} catch (error) {
|
|
19317
|
+
throw new FerricStoreError("lease validation returned an invalid claim", { cause: error });
|
|
19318
|
+
}
|
|
19319
|
+
if (actual.id !== expected.id) throw leaseValidationMismatch("id");
|
|
19320
|
+
if (normalizedPartition(actual.partitionKey) !== normalizedPartition(expected.partitionKey)) {
|
|
19321
|
+
throw leaseValidationMismatch("partition");
|
|
19322
|
+
}
|
|
19323
|
+
if (!actual.leaseToken.equals(expected.leaseToken)) throw leaseValidationMismatch("lease token");
|
|
19324
|
+
if (fencingValue(actual.fencingToken) !== fencingValue(expected.fencingToken)) {
|
|
19325
|
+
throw leaseValidationMismatch("fencing token");
|
|
19326
|
+
}
|
|
19327
|
+
if (actual.state !== expected.state) throw leaseValidationMismatch("physical state");
|
|
19328
|
+
if (actual.runState !== expected.runState) throw leaseValidationMismatch("runState");
|
|
19329
|
+
}
|
|
19330
|
+
function assertContinuationClaim(previous, refreshed, toState) {
|
|
19331
|
+
try {
|
|
19332
|
+
assertDurableClaim(refreshed);
|
|
19333
|
+
} catch (error) {
|
|
19334
|
+
throw new FerricStoreError("continuation response returned an invalid claim", { cause: error });
|
|
19335
|
+
}
|
|
19336
|
+
if (refreshed.id !== previous.id) throw continuationMismatch("id");
|
|
19337
|
+
if (normalizedPartition(refreshed.partitionKey) !== normalizedPartition(previous.partitionKey)) {
|
|
19338
|
+
throw continuationMismatch("partition");
|
|
19339
|
+
}
|
|
19340
|
+
if (refreshed.leaseToken.equals(previous.leaseToken)) {
|
|
19341
|
+
throw continuationMismatch("lease token did not change");
|
|
19342
|
+
}
|
|
19343
|
+
if (fencingValue(refreshed.fencingToken) <= fencingValue(previous.fencingToken)) {
|
|
19344
|
+
throw continuationMismatch("fencing token did not increase");
|
|
19345
|
+
}
|
|
19346
|
+
if (refreshed.state !== "running") throw continuationMismatch("physical state");
|
|
19347
|
+
if (refreshed.runState !== toState) throw continuationMismatch("runState");
|
|
19348
|
+
}
|
|
19349
|
+
function leaseValidationMismatch(field3) {
|
|
19350
|
+
return new FerricStoreError(`lease validation returned a mismatched ${field3}`);
|
|
19351
|
+
}
|
|
19352
|
+
function continuationMismatch(field3) {
|
|
19353
|
+
return new FerricStoreError(`continuation response returned a mismatched ${field3}`);
|
|
19354
|
+
}
|
|
19355
|
+
function normalizedPartition(value) {
|
|
19356
|
+
return value == null || value === "" ? void 0 : value;
|
|
19357
|
+
}
|
|
19358
|
+
function fencingValue(value) {
|
|
19359
|
+
return typeof value === "bigint" ? value : BigInt(value);
|
|
19360
|
+
}
|
|
19361
|
+
function assertNonEmptyString(value, name) {
|
|
19362
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
19363
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
19364
|
+
}
|
|
19365
|
+
}
|
|
19366
|
+
function assertStepValueIsReserved(options, resultName) {
|
|
19367
|
+
if (options.values != null && Object.hasOwn(options.values, resultName)) {
|
|
19368
|
+
throw new TypeError("values cannot mutate the reserved durable step result");
|
|
19369
|
+
}
|
|
19370
|
+
if (options.valueRefs != null && Object.hasOwn(options.valueRefs, resultName)) {
|
|
19371
|
+
throw new TypeError("valueRefs cannot mutate the reserved durable step result");
|
|
19372
|
+
}
|
|
19373
|
+
if (options.dropValues?.includes(resultName) === true) {
|
|
19374
|
+
throw new TypeError("dropValues cannot mutate the reserved durable step result");
|
|
19375
|
+
}
|
|
19376
|
+
if (options.overrideValues?.includes(resultName) === true) {
|
|
19377
|
+
throw new TypeError("overrideValues cannot mutate the reserved durable step result");
|
|
19378
|
+
}
|
|
19379
|
+
}
|
|
19380
|
+
function claimedItemFromRecord(record) {
|
|
19381
|
+
return {
|
|
19382
|
+
...record.attributes == null ? {} : { attributes: record.attributes },
|
|
19383
|
+
fencingToken: record.fencingToken,
|
|
19384
|
+
id: record.id,
|
|
19385
|
+
leaseToken: Buffer.from(record.leaseToken),
|
|
19386
|
+
...record.partitionKey === "" ? {} : { partitionKey: record.partitionKey },
|
|
19387
|
+
...record.payload === void 0 ? {} : { payload: record.payload },
|
|
19388
|
+
...record.runState == null ? {} : { runState: record.runState },
|
|
19389
|
+
state: record.state,
|
|
19390
|
+
type: record.type
|
|
19391
|
+
};
|
|
19392
|
+
}
|
|
19393
|
+
|
|
19394
|
+
// src/client-step-continue.ts
|
|
19395
|
+
function stepContinueArguments(id, options, codec) {
|
|
19396
|
+
try {
|
|
19397
|
+
const args = [
|
|
19398
|
+
"FLOW.STEP_CONTINUE",
|
|
19399
|
+
id,
|
|
19400
|
+
options.leaseToken,
|
|
19401
|
+
options.fromState,
|
|
19402
|
+
options.toState,
|
|
19403
|
+
"FENCING",
|
|
19404
|
+
options.fencingToken,
|
|
19405
|
+
"LEASE_MS",
|
|
19406
|
+
options.leaseMs ?? 3e4,
|
|
19407
|
+
"NOW",
|
|
19408
|
+
options.nowMs ?? nowMs()
|
|
19409
|
+
];
|
|
19410
|
+
append(args, "PARTITION", options.partitionKey);
|
|
19411
|
+
append(args, "WORKER", options.worker);
|
|
19412
|
+
appendEncoded(args, "PAYLOAD", codec, options.payload);
|
|
19413
|
+
if (options.returnJob === true) args.push("RETURN", "JOBS_COMPACT");
|
|
19414
|
+
appendStateMeta(args, options.stateMeta);
|
|
19415
|
+
appendNamedValues(args, codec, options);
|
|
19416
|
+
appendAttributeMutations(args, options);
|
|
19417
|
+
return args;
|
|
19418
|
+
} catch (error) {
|
|
19419
|
+
throw requestNotSentError(error);
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19422
|
+
|
|
19010
19423
|
// src/client.ts
|
|
19011
19424
|
var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClient {
|
|
19012
19425
|
static async fromUrl(url, options = {}) {
|
|
@@ -19121,33 +19534,30 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
|
|
|
19121
19534
|
appendNamedValues(args, this.codec, options);
|
|
19122
19535
|
return await this.recordOrGet(await this.commandArgs(args), id, partitionKey);
|
|
19123
19536
|
}
|
|
19537
|
+
/**
|
|
19538
|
+
* @deprecated Low-level compatibility API. Use {@link advance} for state-only
|
|
19539
|
+
* continuation or {@link step} for a durable closure.
|
|
19540
|
+
*/
|
|
19124
19541
|
async stepContinue(id, options) {
|
|
19125
19542
|
const partitionKey = options.partitionKey;
|
|
19126
19543
|
const returnJob = options.returnJob === true;
|
|
19127
19544
|
const type = options.type;
|
|
19128
|
-
const args =
|
|
19129
|
-
"FLOW.STEP_CONTINUE",
|
|
19130
|
-
id,
|
|
19131
|
-
options.leaseToken,
|
|
19132
|
-
options.fromState,
|
|
19133
|
-
options.toState,
|
|
19134
|
-
"FENCING",
|
|
19135
|
-
options.fencingToken,
|
|
19136
|
-
"LEASE_MS",
|
|
19137
|
-
options.leaseMs ?? 3e4,
|
|
19138
|
-
"NOW",
|
|
19139
|
-
options.nowMs ?? nowMs()
|
|
19140
|
-
];
|
|
19141
|
-
append(args, "PARTITION", partitionKey);
|
|
19142
|
-
append(args, "WORKER", options.worker);
|
|
19143
|
-
appendEncoded(args, "PAYLOAD", this.codec, options.payload);
|
|
19144
|
-
if (returnJob) args.push("RETURN", "JOBS_COMPACT");
|
|
19145
|
-
appendStateMeta(args, options.stateMeta);
|
|
19146
|
-
appendNamedValues(args, this.codec, options);
|
|
19147
|
-
appendAttributeMutations(args, options);
|
|
19545
|
+
const args = stepContinueArguments(id, options, this.codec);
|
|
19148
19546
|
const response = await this.commandArgs(args);
|
|
19149
19547
|
return returnJob ? claimedItemFromResp(response, this.codec, { type }) : await this.recordOrGet(response, id, partitionKey);
|
|
19150
19548
|
}
|
|
19549
|
+
/** Advance a claimed Flow and return its renewed claim. */
|
|
19550
|
+
async advance(job, options) {
|
|
19551
|
+
return await advanceClaim(this, job, options);
|
|
19552
|
+
}
|
|
19553
|
+
/**
|
|
19554
|
+
* Run a closure after validating the claim, then atomically journal its
|
|
19555
|
+
* result and advance the Flow. A committed result is replayed without
|
|
19556
|
+
* invoking the closure again.
|
|
19557
|
+
*/
|
|
19558
|
+
async step(job, options) {
|
|
19559
|
+
return await runDurableStep(this, job, options);
|
|
19560
|
+
}
|
|
19151
19561
|
async runStepsMany(items, options) {
|
|
19152
19562
|
if (items.length === 0) return Buffer.from("OK");
|
|
19153
19563
|
const hasStates = options.states != null;
|
|
@@ -19397,6 +19807,8 @@ function validateUnicodeScalarText2(value) {
|
|
|
19397
19807
|
|
|
19398
19808
|
// src/outcomes.ts
|
|
19399
19809
|
var OUTCOME_BRAND = /* @__PURE__ */ Symbol("ferricstore.outcome");
|
|
19810
|
+
var ALREADY_APPLIED_BRAND = /* @__PURE__ */ Symbol("ferricstore.alreadyApplied");
|
|
19811
|
+
var REPLAYED_STEP_BRAND = /* @__PURE__ */ Symbol("ferricstore.replayedStep");
|
|
19400
19812
|
function transition(toState, options = {}) {
|
|
19401
19813
|
return brandOutcome({ ...options, kind: "transition", toState });
|
|
19402
19814
|
}
|
|
@@ -19409,6 +19821,22 @@ function retry(options = {}) {
|
|
|
19409
19821
|
function fail2(options = {}) {
|
|
19410
19822
|
return brandOutcome({ ...options, kind: "fail" });
|
|
19411
19823
|
}
|
|
19824
|
+
function alreadyApplied(job) {
|
|
19825
|
+
return brandAlreadyApplied({ job, kind: "already_applied" });
|
|
19826
|
+
}
|
|
19827
|
+
function appliedStep(job, result) {
|
|
19828
|
+
return brandAlreadyApplied({ applied: true, job, kind: "already_applied", result });
|
|
19829
|
+
}
|
|
19830
|
+
function replayedStep(job, result) {
|
|
19831
|
+
const replayed = { applied: false, job, result };
|
|
19832
|
+
Object.defineProperty(replayed, REPLAYED_STEP_BRAND, {
|
|
19833
|
+
configurable: false,
|
|
19834
|
+
enumerable: false,
|
|
19835
|
+
value: true,
|
|
19836
|
+
writable: false
|
|
19837
|
+
});
|
|
19838
|
+
return replayed;
|
|
19839
|
+
}
|
|
19412
19840
|
function isOutcome(value) {
|
|
19413
19841
|
if (typeof value !== "object" || value == null || !Object.hasOwn(value, OUTCOME_BRAND) || value[OUTCOME_BRAND] !== true) {
|
|
19414
19842
|
return false;
|
|
@@ -19419,6 +19847,12 @@ function isOutcome(value) {
|
|
|
19419
19847
|
}
|
|
19420
19848
|
return outcome.kind === "complete" || outcome.kind === "retry" || outcome.kind === "fail";
|
|
19421
19849
|
}
|
|
19850
|
+
function isAlreadyAppliedOutcome(value) {
|
|
19851
|
+
return typeof value === "object" && value != null && Object.hasOwn(value, ALREADY_APPLIED_BRAND) && value[ALREADY_APPLIED_BRAND] === true && value.kind === "already_applied";
|
|
19852
|
+
}
|
|
19853
|
+
function isReplayedStepResult(value) {
|
|
19854
|
+
return typeof value === "object" && value != null && Object.hasOwn(value, REPLAYED_STEP_BRAND) && value[REPLAYED_STEP_BRAND] === true && value.applied === false;
|
|
19855
|
+
}
|
|
19422
19856
|
function brandOutcome(outcome) {
|
|
19423
19857
|
Object.defineProperty(outcome, "kind", {
|
|
19424
19858
|
configurable: false,
|
|
@@ -19442,6 +19876,21 @@ function brandOutcome(outcome) {
|
|
|
19442
19876
|
});
|
|
19443
19877
|
return outcome;
|
|
19444
19878
|
}
|
|
19879
|
+
function brandAlreadyApplied(outcome) {
|
|
19880
|
+
Object.defineProperty(outcome, "kind", {
|
|
19881
|
+
configurable: false,
|
|
19882
|
+
enumerable: true,
|
|
19883
|
+
value: "already_applied",
|
|
19884
|
+
writable: false
|
|
19885
|
+
});
|
|
19886
|
+
Object.defineProperty(outcome, ALREADY_APPLIED_BRAND, {
|
|
19887
|
+
configurable: false,
|
|
19888
|
+
enumerable: false,
|
|
19889
|
+
value: true,
|
|
19890
|
+
writable: false
|
|
19891
|
+
});
|
|
19892
|
+
return outcome;
|
|
19893
|
+
}
|
|
19445
19894
|
|
|
19446
19895
|
// src/worker-config.ts
|
|
19447
19896
|
function snapshotWorkerConfig(options) {
|
|
@@ -19458,8 +19907,7 @@ function snapshotWorkerConfig(options) {
|
|
|
19458
19907
|
return Object.freeze(snapshot);
|
|
19459
19908
|
}
|
|
19460
19909
|
|
|
19461
|
-
// src/worker-
|
|
19462
|
-
var DEFAULT_FLOW_MANY_BATCH_LIMIT2 = 1e3;
|
|
19910
|
+
// src/worker-lease-guard.ts
|
|
19463
19911
|
var LeaseRenewalError = class extends Error {
|
|
19464
19912
|
constructor(cause) {
|
|
19465
19913
|
super("FerricStore lease renewal failed", { cause });
|
|
@@ -19470,37 +19918,58 @@ var LeaseRenewalGuard = class {
|
|
|
19470
19918
|
constructor(client, leasedJob, leaseMs, options) {
|
|
19471
19919
|
this.client = client;
|
|
19472
19920
|
this.leaseMs = leaseMs;
|
|
19473
|
-
this.
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
const intervalMs = positiveInteger4(options.leaseRenewIntervalMs, Math.max(1, Math.trunc(leaseMs / 2)));
|
|
19478
|
-
this.timer = setLongInterval(() => this.renew(), intervalMs);
|
|
19479
|
-
this.timer.unref();
|
|
19921
|
+
this.currentJob = snapshotLeasedJob(leasedJob);
|
|
19922
|
+
this.renewalEnabled = options.leaseRenewal !== false;
|
|
19923
|
+
this.intervalMs = positiveInteger4(options.leaseRenewIntervalMs, Math.max(1, Math.trunc(leaseMs / 2)));
|
|
19924
|
+
this.startRenewal();
|
|
19480
19925
|
}
|
|
19481
19926
|
client;
|
|
19482
19927
|
leaseMs;
|
|
19483
19928
|
error;
|
|
19484
19929
|
inFlight;
|
|
19485
|
-
|
|
19930
|
+
currentJob;
|
|
19931
|
+
intervalMs;
|
|
19932
|
+
renewalEnabled;
|
|
19933
|
+
state = "active";
|
|
19486
19934
|
timer;
|
|
19935
|
+
get job() {
|
|
19936
|
+
return this.currentJob;
|
|
19937
|
+
}
|
|
19487
19938
|
assertActive() {
|
|
19488
|
-
if (this.error != null)
|
|
19489
|
-
throw this.error;
|
|
19490
|
-
}
|
|
19939
|
+
if (this.error != null) throw this.error;
|
|
19491
19940
|
}
|
|
19492
19941
|
async stop() {
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19942
|
+
this.state = "stopped";
|
|
19943
|
+
this.cancelRenewal();
|
|
19944
|
+
await this.inFlight;
|
|
19945
|
+
this.assertActive();
|
|
19946
|
+
}
|
|
19947
|
+
async pauseForLeaseMutation() {
|
|
19948
|
+
if (this.state === "stopped") throw new Error("lease renewal guard is stopped");
|
|
19949
|
+
if (this.state === "paused") return;
|
|
19950
|
+
this.state = "paused";
|
|
19951
|
+
this.cancelRenewal();
|
|
19497
19952
|
await this.inFlight;
|
|
19498
19953
|
this.assertActive();
|
|
19499
19954
|
}
|
|
19955
|
+
resumeWith(job) {
|
|
19956
|
+
if (this.state !== "paused") throw new Error("lease renewal guard is not paused");
|
|
19957
|
+
this.currentJob = snapshotLeasedJob(job);
|
|
19958
|
+
this.state = "active";
|
|
19959
|
+
this.startRenewal();
|
|
19960
|
+
}
|
|
19961
|
+
cancelRenewal() {
|
|
19962
|
+
if (this.timer == null) return;
|
|
19963
|
+
this.timer.cancel();
|
|
19964
|
+
this.timer = void 0;
|
|
19965
|
+
}
|
|
19966
|
+
startRenewal() {
|
|
19967
|
+
if (!this.renewalEnabled || this.state !== "active") return;
|
|
19968
|
+
this.timer = setLongInterval(() => this.renew(), this.intervalMs);
|
|
19969
|
+
this.timer.unref();
|
|
19970
|
+
}
|
|
19500
19971
|
renew() {
|
|
19501
|
-
if (this.error != null || this.inFlight != null)
|
|
19502
|
-
return;
|
|
19503
|
-
}
|
|
19972
|
+
if (this.state !== "active" || this.error != null || this.inFlight != null) return;
|
|
19504
19973
|
const renewal = this.client.extendLease(this.job.id, {
|
|
19505
19974
|
fencingToken: this.job.fencingToken,
|
|
19506
19975
|
leaseMs: this.leaseMs,
|
|
@@ -19511,17 +19980,12 @@ var LeaseRenewalGuard = class {
|
|
|
19511
19980
|
() => void 0,
|
|
19512
19981
|
(error) => {
|
|
19513
19982
|
this.error = new LeaseRenewalError(error);
|
|
19514
|
-
|
|
19515
|
-
this.timer.cancel();
|
|
19516
|
-
this.timer = void 0;
|
|
19517
|
-
}
|
|
19983
|
+
this.cancelRenewal();
|
|
19518
19984
|
}
|
|
19519
19985
|
);
|
|
19520
19986
|
this.inFlight = renewal;
|
|
19521
19987
|
void renewal.finally(() => {
|
|
19522
|
-
if (this.inFlight === renewal)
|
|
19523
|
-
this.inFlight = void 0;
|
|
19524
|
-
}
|
|
19988
|
+
if (this.inFlight === renewal) this.inFlight = void 0;
|
|
19525
19989
|
});
|
|
19526
19990
|
}
|
|
19527
19991
|
};
|
|
@@ -19545,8 +20009,14 @@ function snapshotLeasedJob(job) {
|
|
|
19545
20009
|
}
|
|
19546
20010
|
return snapshot;
|
|
19547
20011
|
}
|
|
20012
|
+
function positiveInteger4(value, fallback) {
|
|
20013
|
+
return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
|
|
20014
|
+
}
|
|
20015
|
+
|
|
20016
|
+
// src/worker-internal.ts
|
|
20017
|
+
var DEFAULT_FLOW_MANY_BATCH_LIMIT2 = 1e3;
|
|
19548
20018
|
function workerConcurrency(options) {
|
|
19549
|
-
return
|
|
20019
|
+
return positiveInteger5(options.concurrency ?? options.workers, 1);
|
|
19550
20020
|
}
|
|
19551
20021
|
function workerRefillStrategy(options) {
|
|
19552
20022
|
return options.refillStrategy === "wave" ? "wave" : "continuous";
|
|
@@ -19589,18 +20059,18 @@ function workerErrorPayload(error, options, codec) {
|
|
|
19589
20059
|
}
|
|
19590
20060
|
function workerBatchSize(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
|
|
19591
20061
|
return Math.min(
|
|
19592
|
-
|
|
19593
|
-
|
|
20062
|
+
positiveInteger5(options.batchSize, 10),
|
|
20063
|
+
positiveInteger5(flowManyBatchLimit, DEFAULT_FLOW_MANY_BATCH_LIMIT2)
|
|
19594
20064
|
);
|
|
19595
20065
|
}
|
|
19596
20066
|
function workerClaimLimit(options, flowManyBatchLimit = DEFAULT_FLOW_MANY_BATCH_LIMIT2) {
|
|
19597
20067
|
return Math.min(workerBatchSize(options, flowManyBatchLimit), workerConcurrency(options));
|
|
19598
20068
|
}
|
|
19599
20069
|
function workerDrainBatches(options) {
|
|
19600
|
-
return
|
|
20070
|
+
return positiveInteger5(options.claimDrainBatches, 1);
|
|
19601
20071
|
}
|
|
19602
20072
|
function workerLeaseMs(options, fallback = 3e4) {
|
|
19603
|
-
return
|
|
20073
|
+
return positiveInteger5(options.leaseMs, positiveInteger5(fallback, 3e4));
|
|
19604
20074
|
}
|
|
19605
20075
|
function workerIdleSleepMs(options) {
|
|
19606
20076
|
return nonNegativeDuration(options.idleSleepMs, 250);
|
|
@@ -19612,7 +20082,7 @@ function workerClaimBlockMs(options, useBlocking) {
|
|
|
19612
20082
|
if (options.signal == null || !Number.isFinite(options.blockMs) || options.blockMs < 0) {
|
|
19613
20083
|
return options.blockMs;
|
|
19614
20084
|
}
|
|
19615
|
-
const abortPollMs =
|
|
20085
|
+
const abortPollMs = positiveInteger5(options.abortPollMs, 1e3);
|
|
19616
20086
|
return options.blockMs === 0 ? abortPollMs : Math.min(Math.trunc(options.blockMs), abortPollMs);
|
|
19617
20087
|
}
|
|
19618
20088
|
function workerMaxIdleSleepMs(options, idleSleepMs = workerIdleSleepMs(options)) {
|
|
@@ -19623,8 +20093,8 @@ function nextWorkerIdleSleepMs(currentIdleSleepMs, maxIdleSleepMs) {
|
|
|
19623
20093
|
return Math.min(maxIdleSleepMs, Math.max(1, currentIdleSleepMs * 2));
|
|
19624
20094
|
}
|
|
19625
20095
|
async function runContinuousWorkerPool(options) {
|
|
19626
|
-
const concurrency =
|
|
19627
|
-
const maxClaimSize =
|
|
20096
|
+
const concurrency = positiveInteger5(options.concurrency, 1);
|
|
20097
|
+
const maxClaimSize = positiveInteger5(options.maxClaimSize, concurrency);
|
|
19628
20098
|
const initialIdleSleepMs = nonNegativeDuration(options.idleSleepMs, 250);
|
|
19629
20099
|
const maxIdleSleepMs = Math.max(
|
|
19630
20100
|
initialIdleSleepMs,
|
|
@@ -19746,7 +20216,7 @@ function normalizeWorkflowWorkerConfig(options) {
|
|
|
19746
20216
|
...captured
|
|
19747
20217
|
});
|
|
19748
20218
|
}
|
|
19749
|
-
function
|
|
20219
|
+
function positiveInteger5(value, fallback) {
|
|
19750
20220
|
return value == null || !Number.isFinite(value) ? fallback : Math.max(1, Math.trunc(value));
|
|
19751
20221
|
}
|
|
19752
20222
|
function nonNegativeInteger5(value, fallback) {
|
|
@@ -19799,7 +20269,7 @@ async function waitForRefillWindow(delayMs, signal) {
|
|
|
19799
20269
|
}
|
|
19800
20270
|
|
|
19801
20271
|
// src/queue.ts
|
|
19802
|
-
var
|
|
20272
|
+
var import_node_crypto3 = require("crypto");
|
|
19803
20273
|
|
|
19804
20274
|
// src/queue-completion-batcher.ts
|
|
19805
20275
|
function queueClaimOptions(queue, options, overrides = {}) {
|
|
@@ -20664,7 +21134,7 @@ var Queue = class {
|
|
|
20664
21134
|
this.client = client;
|
|
20665
21135
|
this.type = captured.type;
|
|
20666
21136
|
this.state = captured.state ?? "queued";
|
|
20667
|
-
this.defaultWorker = captured.worker ?? `${this.type}-${(0,
|
|
21137
|
+
this.defaultWorker = captured.worker ?? `${this.type}-${(0, import_node_crypto3.randomUUID)()}`;
|
|
20668
21138
|
}
|
|
20669
21139
|
async enqueue(id, options = {}) {
|
|
20670
21140
|
return await this.client.enqueue(id, {
|
|
@@ -20686,53 +21156,59 @@ var Queue = class {
|
|
|
20686
21156
|
};
|
|
20687
21157
|
|
|
20688
21158
|
// src/workflow.ts
|
|
20689
|
-
var
|
|
21159
|
+
var import_node_crypto4 = require("crypto");
|
|
20690
21160
|
|
|
20691
|
-
// src/workflow-
|
|
20692
|
-
function
|
|
20693
|
-
const
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
21161
|
+
// src/workflow-registration.ts
|
|
21162
|
+
function createWorkflowStateRegistration(name, handler, options, defaultValueMaxBytes) {
|
|
21163
|
+
const own2 = { ...options };
|
|
21164
|
+
const claimValues = own2.claimValues == null ? void 0 : snapshotOwnStringArray(own2.claimValues, "claimValues");
|
|
21165
|
+
const retryPolicy = own2.retryPolicy == null ? void 0 : Object.freeze({ ...own2.retryPolicy });
|
|
21166
|
+
return Object.freeze({
|
|
21167
|
+
claimPayload: own2.claimPayload ?? true,
|
|
21168
|
+
claimRecord: own2.claimRecord ?? true,
|
|
21169
|
+
exceptionPolicy: normalizeExceptionPolicy(own2.exceptionPolicy),
|
|
21170
|
+
handler,
|
|
21171
|
+
leaseMs: own2.leaseMs ?? 3e4,
|
|
21172
|
+
...own2.mode == null ? {} : { mode: normalizeStateMode(own2.mode) },
|
|
21173
|
+
name,
|
|
21174
|
+
returnRecord: own2.returnRecord ?? false,
|
|
21175
|
+
valueMaxBytes: own2.valueMaxBytes ?? defaultValueMaxBytes,
|
|
21176
|
+
...claimValues == null ? {} : { claimValues },
|
|
21177
|
+
...retryPolicy == null ? {} : { retryPolicy }
|
|
20701
21178
|
});
|
|
20702
21179
|
}
|
|
20703
|
-
function normalizeStateMode(mode) {
|
|
20704
|
-
if (mode === "fifo" || mode === "parallel") return mode;
|
|
20705
|
-
throw new Error("state mode must be 'fifo' or 'parallel'");
|
|
20706
|
-
}
|
|
20707
|
-
function valueRefToString(value) {
|
|
20708
|
-
if (typeof value === "string") return value;
|
|
20709
|
-
if (Buffer.isBuffer(value)) return value.toString("utf8");
|
|
20710
|
-
if (typeof value === "object" && value != null && Object.hasOwn(value, "ref")) {
|
|
20711
|
-
const ref = value.ref;
|
|
20712
|
-
return typeof ref === "string" ? ref : Buffer.isBuffer(ref) ? ref.toString("utf8") : void 0;
|
|
20713
|
-
}
|
|
20714
|
-
return void 0;
|
|
20715
|
-
}
|
|
20716
21180
|
|
|
20717
21181
|
// src/workflow-context.ts
|
|
20718
21182
|
var MISSING_VALUE = /* @__PURE__ */ Symbol("ferricstore.missingValue");
|
|
20719
21183
|
var WorkflowContext = class {
|
|
20720
21184
|
workflow;
|
|
20721
21185
|
job;
|
|
20722
|
-
stateName;
|
|
20723
21186
|
flow;
|
|
21187
|
+
appliedMutation = false;
|
|
21188
|
+
currentStateName;
|
|
20724
21189
|
leaseJob;
|
|
21190
|
+
mutationFailure;
|
|
21191
|
+
mutationPhase = "idle";
|
|
21192
|
+
mutationCoordinator;
|
|
20725
21193
|
valueCache = /* @__PURE__ */ new Map();
|
|
20726
|
-
constructor(workflow, job, stateName
|
|
21194
|
+
constructor(workflow, job, stateName) {
|
|
20727
21195
|
this.workflow = workflow;
|
|
20728
21196
|
this.job = job;
|
|
20729
|
-
this.leaseJob =
|
|
20730
|
-
this.
|
|
21197
|
+
this.leaseJob = job;
|
|
21198
|
+
this.currentStateName = stateName;
|
|
20731
21199
|
this.flow = new WorkflowFlowCommands(this);
|
|
20732
21200
|
}
|
|
21201
|
+
/** @internal */
|
|
21202
|
+
configureWorkerMutation(leaseJob, coordinator) {
|
|
21203
|
+
this.leaseJob = leaseJob;
|
|
21204
|
+
this.mutationCoordinator = coordinator;
|
|
21205
|
+
}
|
|
20733
21206
|
get client() {
|
|
20734
21207
|
return this.workflow.client;
|
|
20735
21208
|
}
|
|
21209
|
+
get stateName() {
|
|
21210
|
+
return this.currentStateName;
|
|
21211
|
+
}
|
|
20736
21212
|
get id() {
|
|
20737
21213
|
return this.leaseJob.id;
|
|
20738
21214
|
}
|
|
@@ -20743,7 +21219,7 @@ var WorkflowContext = class {
|
|
|
20743
21219
|
return this.leaseJob.state;
|
|
20744
21220
|
}
|
|
20745
21221
|
get logicalState() {
|
|
20746
|
-
return this.
|
|
21222
|
+
return this.currentStateName;
|
|
20747
21223
|
}
|
|
20748
21224
|
get partitionKey() {
|
|
20749
21225
|
return this.leaseJob.partitionKey;
|
|
@@ -20781,6 +21257,53 @@ var WorkflowContext = class {
|
|
|
20781
21257
|
get correlationId() {
|
|
20782
21258
|
return "correlationId" in this.job ? this.job.correlationId : void 0;
|
|
20783
21259
|
}
|
|
21260
|
+
async advance(toState, options = {}) {
|
|
21261
|
+
if (typeof toState !== "string" || toState.length === 0) {
|
|
21262
|
+
throw new TypeError("toState must be a non-empty string");
|
|
21263
|
+
}
|
|
21264
|
+
const current = this.durableClaim();
|
|
21265
|
+
await this.beforeCommit();
|
|
21266
|
+
let refreshed;
|
|
21267
|
+
try {
|
|
21268
|
+
refreshed = await advanceClaim(this.client, current, { ...options, toState });
|
|
21269
|
+
} catch (error) {
|
|
21270
|
+
this.commitFailed(error);
|
|
21271
|
+
throw error;
|
|
21272
|
+
}
|
|
21273
|
+
this.committed(refreshed);
|
|
21274
|
+
return alreadyApplied(refreshed);
|
|
21275
|
+
}
|
|
21276
|
+
async step(options) {
|
|
21277
|
+
let applied = false;
|
|
21278
|
+
const stepped = await runDurableStep(this.client, this.durableClaim(), options, {
|
|
21279
|
+
beforeCommit: async () => await this.beforeCommit(),
|
|
21280
|
+
commitFailed: (error) => this.commitFailed(error),
|
|
21281
|
+
committed: (job) => {
|
|
21282
|
+
applied = true;
|
|
21283
|
+
this.committed(job);
|
|
21284
|
+
},
|
|
21285
|
+
replayed: (job) => this.replayed(job)
|
|
21286
|
+
});
|
|
21287
|
+
return applied ? appliedStep(stepped.job, stepped.result) : replayedStep(stepped.job, stepped.result);
|
|
21288
|
+
}
|
|
21289
|
+
/** @internal */
|
|
21290
|
+
get hasAppliedMutation() {
|
|
21291
|
+
return this.appliedMutation;
|
|
21292
|
+
}
|
|
21293
|
+
/** @internal */
|
|
21294
|
+
get uncertainMutationFailure() {
|
|
21295
|
+
return this.mutationPhase === "uncertain" ? this.mutationFailure : void 0;
|
|
21296
|
+
}
|
|
21297
|
+
/** @internal */
|
|
21298
|
+
get hasUncertainMutation() {
|
|
21299
|
+
return this.mutationPhase === "uncertain";
|
|
21300
|
+
}
|
|
21301
|
+
/** @internal */
|
|
21302
|
+
assertAppliedOutcome(outcome) {
|
|
21303
|
+
if (!this.appliedMutation || !outcome.job.leaseToken.equals(this.leaseJob.leaseToken) || outcome.job.fencingToken !== this.leaseJob.fencingToken) {
|
|
21304
|
+
throw new Error("already-applied outcome does not match the workflow's refreshed claim");
|
|
21305
|
+
}
|
|
21306
|
+
}
|
|
20784
21307
|
async value(name, defaultValue, options = {}) {
|
|
20785
21308
|
const useLocalCache = options.localCache ?? this.workflow.valueConfig.localCache;
|
|
20786
21309
|
if (useLocalCache && this.valueCache.has(name)) {
|
|
@@ -20860,7 +21383,49 @@ var WorkflowContext = class {
|
|
|
20860
21383
|
return result;
|
|
20861
21384
|
}
|
|
20862
21385
|
valueMaxBytes() {
|
|
20863
|
-
return this.workflow.stateRegistration(this.
|
|
21386
|
+
return this.workflow.stateRegistration(this.currentStateName)?.valueMaxBytes;
|
|
21387
|
+
}
|
|
21388
|
+
durableClaim() {
|
|
21389
|
+
const runState = this.leaseJob.runState ?? (this.leaseJob[CLAIMED_ITEM_WIRE] == null ? void 0 : this.currentStateName);
|
|
21390
|
+
if (typeof runState !== "string" || runState.length === 0) {
|
|
21391
|
+
throw new TypeError("job.runState must be a non-empty string");
|
|
21392
|
+
}
|
|
21393
|
+
return {
|
|
21394
|
+
...this.leaseJob,
|
|
21395
|
+
leaseToken: Buffer.from(this.leaseJob.leaseToken),
|
|
21396
|
+
runState
|
|
21397
|
+
};
|
|
21398
|
+
}
|
|
21399
|
+
async beforeCommit() {
|
|
21400
|
+
if (this.mutationPhase !== "idle") {
|
|
21401
|
+
throw new Error("a durable workflow mutation is already in progress or has an uncertain result");
|
|
21402
|
+
}
|
|
21403
|
+
await this.mutationCoordinator?.pause();
|
|
21404
|
+
this.mutationPhase = "committing";
|
|
21405
|
+
}
|
|
21406
|
+
commitFailed(error) {
|
|
21407
|
+
if (!durableMutationMayHaveCommitted(error)) {
|
|
21408
|
+
this.mutationFailure = void 0;
|
|
21409
|
+
this.mutationPhase = "idle";
|
|
21410
|
+
this.mutationCoordinator?.resume(this.leaseJob);
|
|
21411
|
+
return;
|
|
21412
|
+
}
|
|
21413
|
+
this.mutationFailure = error;
|
|
21414
|
+
this.mutationPhase = "uncertain";
|
|
21415
|
+
}
|
|
21416
|
+
committed(job) {
|
|
21417
|
+
this.acceptJob(job);
|
|
21418
|
+
this.appliedMutation = true;
|
|
21419
|
+
this.mutationCoordinator?.resume(job);
|
|
21420
|
+
}
|
|
21421
|
+
replayed(job) {
|
|
21422
|
+
this.acceptJob(job);
|
|
21423
|
+
}
|
|
21424
|
+
acceptJob(job) {
|
|
21425
|
+
this.leaseJob = job;
|
|
21426
|
+
this.currentStateName = job.runState ?? this.currentStateName;
|
|
21427
|
+
this.mutationFailure = void 0;
|
|
21428
|
+
this.mutationPhase = "idle";
|
|
20864
21429
|
}
|
|
20865
21430
|
};
|
|
20866
21431
|
var WorkflowFlowCommands = class {
|
|
@@ -20898,6 +21463,7 @@ var WorkflowFlowCommands = class {
|
|
|
20898
21463
|
toState
|
|
20899
21464
|
});
|
|
20900
21465
|
}
|
|
21466
|
+
/** @deprecated Use `ctx.advance(toState)`. */
|
|
20901
21467
|
async stepContinue(toState, options = {}) {
|
|
20902
21468
|
return await this.ctx.client.stepContinue(this.ctx.id, {
|
|
20903
21469
|
...options,
|
|
@@ -21060,25 +21626,86 @@ async function applyFail(client, ctx, outcome, returnRecord) {
|
|
|
21060
21626
|
});
|
|
21061
21627
|
}
|
|
21062
21628
|
|
|
21063
|
-
// src/workflow-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
|
|
21077
|
-
|
|
21078
|
-
|
|
21079
|
-
|
|
21629
|
+
// src/workflow-worker-context.ts
|
|
21630
|
+
var WorkerWorkflowContext = class extends WorkflowContext {
|
|
21631
|
+
constructor(workflow, job, stateName, leaseJob, mutationCoordinator) {
|
|
21632
|
+
super(workflow, job, stateName);
|
|
21633
|
+
this.configureWorkerMutation(leaseJob, mutationCoordinator);
|
|
21634
|
+
}
|
|
21635
|
+
};
|
|
21636
|
+
|
|
21637
|
+
// src/workflow-job-execution.ts
|
|
21638
|
+
async function executeWorkflowJob(workflow, workerOptions, job, registration, guard) {
|
|
21639
|
+
guard.assertActive();
|
|
21640
|
+
const ctx = new WorkerWorkflowContext(workflow, job, registration.name, guard.job, {
|
|
21641
|
+
pause: async () => await guard.pauseForLeaseMutation(),
|
|
21642
|
+
resume: (refreshed) => guard.resumeWith(refreshed)
|
|
21643
|
+
});
|
|
21644
|
+
let value;
|
|
21645
|
+
try {
|
|
21646
|
+
value = await registration.handler(ctx);
|
|
21647
|
+
} catch (error) {
|
|
21648
|
+
await guard.stop();
|
|
21649
|
+
if (ctx.hasUncertainMutation) throw asError(error, "durable workflow mutation result is uncertain");
|
|
21650
|
+
await applyHandlerError(workflow, workerOptions, ctx, registration, error);
|
|
21651
|
+
return;
|
|
21652
|
+
}
|
|
21653
|
+
await guard.stop();
|
|
21654
|
+
if (ctx.hasUncertainMutation) {
|
|
21655
|
+
throw asError(ctx.uncertainMutationFailure, "durable workflow mutation result is uncertain");
|
|
21656
|
+
}
|
|
21657
|
+
if (isAlreadyAppliedOutcome(value)) {
|
|
21658
|
+
ctx.assertAppliedOutcome(value);
|
|
21659
|
+
await releaseAppliedMutation(workflow, ctx);
|
|
21660
|
+
return;
|
|
21661
|
+
}
|
|
21662
|
+
if (isReplayedStepResult(value)) {
|
|
21663
|
+
await applyWorkflowOutcome(
|
|
21664
|
+
workflow.client,
|
|
21665
|
+
ctx,
|
|
21666
|
+
complete({ result: value.result }),
|
|
21667
|
+
registration.returnRecord
|
|
21668
|
+
);
|
|
21669
|
+
return;
|
|
21670
|
+
}
|
|
21671
|
+
if (isOutcome(value)) {
|
|
21672
|
+
await applyWorkflowOutcome(workflow.client, ctx, value, registration.returnRecord);
|
|
21673
|
+
return;
|
|
21674
|
+
}
|
|
21675
|
+
if (ctx.hasAppliedMutation) {
|
|
21676
|
+
await releaseAppliedMutation(workflow, ctx);
|
|
21677
|
+
return;
|
|
21678
|
+
}
|
|
21679
|
+
await applyWorkflowOutcome(
|
|
21680
|
+
workflow.client,
|
|
21681
|
+
ctx,
|
|
21682
|
+
complete({ result: value }),
|
|
21683
|
+
registration.returnRecord
|
|
21684
|
+
);
|
|
21685
|
+
}
|
|
21686
|
+
function asError(value, message) {
|
|
21687
|
+
return value instanceof Error ? value : new Error(message, { cause: value });
|
|
21688
|
+
}
|
|
21689
|
+
async function releaseAppliedMutation(workflow, ctx) {
|
|
21690
|
+
await workflow.client.transition(ctx.id, {
|
|
21691
|
+
fencingToken: ctx.fencingToken,
|
|
21692
|
+
fromState: ctx.state,
|
|
21693
|
+
leaseToken: ctx.leaseToken,
|
|
21694
|
+
partitionKey: ctx.partitionKey,
|
|
21695
|
+
toState: ctx.logicalState
|
|
21080
21696
|
});
|
|
21081
21697
|
}
|
|
21698
|
+
async function applyHandlerError(workflow, workerOptions, ctx, registration, error) {
|
|
21699
|
+
const policy = workerOptions.exceptionPolicy ?? registration.exceptionPolicy;
|
|
21700
|
+
if (policy === "raise") throw error;
|
|
21701
|
+
const encodedError = workerErrorPayload(error, workerOptions, workflow.client.codec);
|
|
21702
|
+
await applyWorkflowOutcome(
|
|
21703
|
+
workflow.client,
|
|
21704
|
+
ctx,
|
|
21705
|
+
policy === "fail" ? fail2({ error: encodedError }) : retry({ error: encodedError }),
|
|
21706
|
+
registration.returnRecord
|
|
21707
|
+
);
|
|
21708
|
+
}
|
|
21082
21709
|
|
|
21083
21710
|
// src/workflow.ts
|
|
21084
21711
|
var WorkflowClient = class {
|
|
@@ -21106,7 +21733,7 @@ var Workflow = class {
|
|
|
21106
21733
|
localCache: captured.valueConfig?.localCache ?? false,
|
|
21107
21734
|
valueMaxBytes: captured.valueConfig?.valueMaxBytes
|
|
21108
21735
|
};
|
|
21109
|
-
this.defaultWorker = captured.worker ?? `${this.type}-${(0,
|
|
21736
|
+
this.defaultWorker = captured.worker ?? `${this.type}-${(0, import_node_crypto4.randomUUID)()}`;
|
|
21110
21737
|
}
|
|
21111
21738
|
state(name, handler, options = {}) {
|
|
21112
21739
|
this.states.set(name, createWorkflowStateRegistration(
|
|
@@ -21207,6 +21834,10 @@ var WorkflowWorker = class {
|
|
|
21207
21834
|
claimed += jobs.length;
|
|
21208
21835
|
const guards = jobs.map((job) => new LeaseRenewalGuard(this.workflow.client, job, leaseMs, this.options));
|
|
21209
21836
|
let cursor = 0;
|
|
21837
|
+
let batchFailure;
|
|
21838
|
+
const recordFailure = (error) => {
|
|
21839
|
+
batchFailure ??= { error };
|
|
21840
|
+
};
|
|
21210
21841
|
const runNext = async () => {
|
|
21211
21842
|
while (cursor < jobs.length) {
|
|
21212
21843
|
const index = cursor;
|
|
@@ -21217,19 +21848,22 @@ var WorkflowWorker = class {
|
|
|
21217
21848
|
try {
|
|
21218
21849
|
await this.applyJob(job, registration, guard);
|
|
21219
21850
|
applied += 1;
|
|
21851
|
+
} catch (error) {
|
|
21852
|
+
recordFailure(error);
|
|
21220
21853
|
} finally {
|
|
21221
|
-
|
|
21854
|
+
try {
|
|
21855
|
+
await guard.stop();
|
|
21856
|
+
} catch (error) {
|
|
21857
|
+
recordFailure(error);
|
|
21858
|
+
}
|
|
21222
21859
|
}
|
|
21223
21860
|
}
|
|
21224
21861
|
};
|
|
21225
21862
|
try {
|
|
21226
|
-
|
|
21863
|
+
await Promise.all(
|
|
21227
21864
|
Array.from({ length: Math.min(concurrency, jobs.length) }, () => runNext())
|
|
21228
21865
|
);
|
|
21229
|
-
|
|
21230
|
-
if (failed != null) {
|
|
21231
|
-
throw failed.reason;
|
|
21232
|
-
}
|
|
21866
|
+
if (batchFailure != null) throwError(batchFailure.error);
|
|
21233
21867
|
} finally {
|
|
21234
21868
|
await Promise.allSettled(
|
|
21235
21869
|
guards.slice(cursor).map(async (guard) => await guard.stop())
|
|
@@ -21340,45 +21974,15 @@ var WorkflowWorker = class {
|
|
|
21340
21974
|
});
|
|
21341
21975
|
}
|
|
21342
21976
|
async applyJob(job, registration, guard) {
|
|
21343
|
-
guard
|
|
21344
|
-
const ctx = new WorkflowContext(this.workflow, job, registration.name, guard.job);
|
|
21345
|
-
let outcome;
|
|
21346
|
-
try {
|
|
21347
|
-
const value = await registration.handler(ctx);
|
|
21348
|
-
outcome = isOutcome(value) ? value : complete({ result: value });
|
|
21349
|
-
} catch (error) {
|
|
21350
|
-
await guard.stop();
|
|
21351
|
-
await this.applyHandlerError(ctx, registration, error);
|
|
21352
|
-
return;
|
|
21353
|
-
}
|
|
21354
|
-
await guard.stop();
|
|
21355
|
-
await applyWorkflowOutcome(this.workflow.client, ctx, outcome, registration.returnRecord);
|
|
21356
|
-
}
|
|
21357
|
-
async applyHandlerError(ctx, registration, error) {
|
|
21358
|
-
const policy = this.options.exceptionPolicy ?? registration.exceptionPolicy;
|
|
21359
|
-
if (policy === "raise") {
|
|
21360
|
-
throw error;
|
|
21361
|
-
}
|
|
21362
|
-
if (policy === "fail") {
|
|
21363
|
-
await applyWorkflowOutcome(
|
|
21364
|
-
this.workflow.client,
|
|
21365
|
-
ctx,
|
|
21366
|
-
fail2({ error: workerErrorPayload(error, this.options, this.workflow.client.codec) }),
|
|
21367
|
-
registration.returnRecord
|
|
21368
|
-
);
|
|
21369
|
-
return;
|
|
21370
|
-
}
|
|
21371
|
-
await applyWorkflowOutcome(
|
|
21372
|
-
this.workflow.client,
|
|
21373
|
-
ctx,
|
|
21374
|
-
retry({ error: workerErrorPayload(error, this.options, this.workflow.client.codec) }),
|
|
21375
|
-
registration.returnRecord
|
|
21376
|
-
);
|
|
21977
|
+
await executeWorkflowJob(this.workflow, this.options, job, registration, guard);
|
|
21377
21978
|
}
|
|
21378
21979
|
};
|
|
21980
|
+
function throwError(value) {
|
|
21981
|
+
throw value instanceof Error ? value : new Error("workflow worker failed", { cause: value });
|
|
21982
|
+
}
|
|
21379
21983
|
|
|
21380
21984
|
// src/version.ts
|
|
21381
|
-
var FERRICSTORE_SDK_VERSION = "0.
|
|
21985
|
+
var FERRICSTORE_SDK_VERSION = "0.13.1";
|
|
21382
21986
|
var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
|
|
21383
21987
|
var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
|
|
21384
21988
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -21422,6 +22026,7 @@ var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
|
|
|
21422
22026
|
QueueWorker,
|
|
21423
22027
|
RawCodec,
|
|
21424
22028
|
ReconnectingExecutor,
|
|
22029
|
+
RequestNotSentError,
|
|
21425
22030
|
RequestTimeoutError,
|
|
21426
22031
|
RerouteError,
|
|
21427
22032
|
RoutingTopology,
|
|
@@ -21442,6 +22047,7 @@ var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
|
|
|
21442
22047
|
complete,
|
|
21443
22048
|
fail,
|
|
21444
22049
|
httpCommandDisposition,
|
|
22050
|
+
isAlreadyAppliedOutcome,
|
|
21445
22051
|
isOutcome,
|
|
21446
22052
|
isReconnectableClosedConnectionError,
|
|
21447
22053
|
mapException,
|