semola 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +18 -45
  2. package/dist/chunk-CKQMccvm.cjs +28 -0
  3. package/dist/lib/api/index.cjs +29 -15
  4. package/dist/lib/api/index.mjs +30 -16
  5. package/dist/lib/cache/index.cjs +47 -22
  6. package/dist/lib/cache/index.d.cts +3 -24
  7. package/dist/lib/cache/index.d.mts +3 -24
  8. package/dist/lib/cache/index.mjs +48 -23
  9. package/dist/lib/cron/index.cjs +117 -117
  10. package/dist/lib/cron/index.mjs +118 -118
  11. package/dist/lib/errors/index.d.cts +12 -1
  12. package/dist/lib/errors/index.d.mts +12 -1
  13. package/dist/lib/logging/index.cjs +1 -0
  14. package/dist/lib/orm/index.cjs +1642 -0
  15. package/dist/lib/orm/index.d.cts +402 -0
  16. package/dist/lib/orm/index.d.mts +402 -0
  17. package/dist/lib/orm/index.mjs +1630 -0
  18. package/dist/lib/prompts/index.cjs +89 -89
  19. package/dist/lib/prompts/index.d.cts +12 -33
  20. package/dist/lib/prompts/index.d.mts +12 -33
  21. package/dist/lib/prompts/index.mjs +89 -90
  22. package/dist/lib/pubsub/index.cjs +43 -19
  23. package/dist/lib/pubsub/index.d.cts +3 -18
  24. package/dist/lib/pubsub/index.d.mts +3 -18
  25. package/dist/lib/pubsub/index.mjs +44 -20
  26. package/dist/lib/queue/index.cjs +40 -10
  27. package/dist/lib/queue/index.d.cts +11 -4
  28. package/dist/lib/queue/index.d.mts +11 -4
  29. package/dist/lib/queue/index.mjs +39 -11
  30. package/dist/lib/workflow/index.cjs +285 -282
  31. package/dist/lib/workflow/index.d.cts +76 -11
  32. package/dist/lib/workflow/index.d.mts +76 -11
  33. package/dist/lib/workflow/index.mjs +278 -284
  34. package/package.json +11 -1
  35. package/dist/index-BhGNDjPq.d.mts +0 -13
  36. package/dist/index-DxSbeGP-.d.cts +0 -13
@@ -1,11 +1,73 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_chunk = require("../../chunk-CKQMccvm.cjs");
2
3
  const require_lib_errors_index = require("../errors/index.cjs");
4
+ let node_assert = require("node:assert");
5
+ node_assert = require_chunk.__toESM(node_assert);
6
+ //#region src/lib/workflow/errors.ts
7
+ var WorkflowError = class extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "WorkflowError";
11
+ }
12
+ };
13
+ var NotFoundError = class extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "NotFoundError";
17
+ }
18
+ };
19
+ var StateError = class extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "StateError";
23
+ }
24
+ };
25
+ var SerializationError = class extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = "SerializationError";
29
+ }
30
+ };
31
+ var ExecutionError = class extends Error {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = "ExecutionError";
35
+ }
36
+ };
37
+ var LockError = class extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "LockError";
41
+ }
42
+ };
43
+ var CancelledError = class extends Error {
44
+ constructor(message) {
45
+ super(message);
46
+ this.name = "CancelledError";
47
+ }
48
+ };
49
+ //#endregion
3
50
  //#region src/lib/workflow/index.ts
4
51
  const DEFAULT_LOCK_TTL = 300 * 1e3;
52
+ const DEFAULT_RETRIES = 3;
53
+ const DEFAULT_RETRY_BASE_DELAY = 1e3;
54
+ const DEFAULT_RETRY_MULTIPLIER = 2;
55
+ const DEFAULT_RETRY_MAX_DELAY = 3e4;
5
56
  const now = () => Date.now();
6
- const toErrorMessage = (error) => {
7
- if (error instanceof Error) return error.message;
8
- return String(error);
57
+ const delay = async (ms, signal, isCancelled) => {
58
+ if (signal.aborted) throw new CancelledError("Workflow execution was aborted during retry backoff");
59
+ const deadline = now() + ms;
60
+ const pollInterval = 50;
61
+ while (now() < deadline) {
62
+ if (signal.aborted) throw new CancelledError("Workflow execution was aborted during retry backoff");
63
+ if (isCancelled) {
64
+ if (await isCancelled()) throw new CancelledError("Workflow execution was cancelled during retry backoff");
65
+ }
66
+ const remaining = deadline - now();
67
+ await new Promise((resolve) => {
68
+ setTimeout(resolve, Math.min(pollInterval, remaining));
69
+ });
70
+ }
9
71
  };
10
72
  const envelopeSerialize = (value) => {
11
73
  return JSON.stringify({ value });
@@ -27,69 +89,67 @@ const knownStatuses = [
27
89
  var WorkflowDefinition = class {
28
90
  options;
29
91
  lockTTL;
92
+ retries;
93
+ retryBaseDelay;
94
+ retryMultiplier;
95
+ retryMaxDelay;
30
96
  constructor(options) {
31
97
  this.options = options;
32
98
  this.lockTTL = options.lockTTL ?? DEFAULT_LOCK_TTL;
99
+ this.retries = options.retries ?? DEFAULT_RETRIES;
100
+ this.retryBaseDelay = options.retryBackoff?.baseDelay ?? DEFAULT_RETRY_BASE_DELAY;
101
+ this.retryMultiplier = options.retryBackoff?.multiplier ?? DEFAULT_RETRY_MULTIPLIER;
102
+ this.retryMaxDelay = options.retryBackoff?.maxDelay ?? DEFAULT_RETRY_MAX_DELAY;
103
+ node_assert.default.ok(Number.isFinite(this.retries) && this.retries >= 0, "Invalid retries: must be a non-negative finite number");
104
+ node_assert.default.ok(Number.isFinite(this.retryBaseDelay) && this.retryBaseDelay > 0, "Invalid retryBackoff.baseDelay: must be a positive finite number");
105
+ node_assert.default.ok(Number.isFinite(this.retryMultiplier) && this.retryMultiplier > 0, "Invalid retryBackoff.multiplier: must be a positive finite number");
106
+ node_assert.default.ok(Number.isFinite(this.retryMaxDelay) && this.retryMaxDelay > 0, "Invalid retryBackoff.maxDelay: must be a positive finite number");
107
+ }
108
+ runHook(hook) {
109
+ return require_lib_errors_index.mightThrow(Promise.resolve().then(() => hook()));
110
+ }
111
+ computeBackoffDelay(attempt) {
112
+ return Math.min(this.retryBaseDelay * this.retryMultiplier ** (attempt - 1), this.retryMaxDelay);
33
113
  }
34
114
  async start(input, options) {
35
115
  const executionId = options?.executionId ?? crypto.randomUUID();
36
- const [createError] = await this.createExecution(executionId, input);
37
- if (createError) return require_lib_errors_index.err(createError.type, createError.message);
116
+ await this.createExecution(executionId, input);
38
117
  return this.execute(executionId, input);
39
118
  }
40
119
  async run(input, options) {
41
- const [startError, startData] = await this.start(input, options);
42
- if (startError) return require_lib_errors_index.err(startError.type, startError.message);
43
- if (!startData) return require_lib_errors_index.err("WorkflowError", "Unexpected empty start result");
44
- if (startData.status === "cancelled") return require_lib_errors_index.err("WorkflowCancelledError", `Workflow execution ${startData.executionId} was cancelled`);
45
- if (startData.status !== "completed") return require_lib_errors_index.err("WorkflowExecutionError", `Workflow execution ${startData.executionId} did not complete`);
46
- const [getError, execution] = await this.get(startData.executionId);
47
- if (getError) return require_lib_errors_index.err(getError.type, getError.message);
48
- if (!execution) return require_lib_errors_index.err("WorkflowError", "Unexpected empty execution");
49
- return require_lib_errors_index.ok(execution.result);
120
+ const startData = await this.start(input, options);
121
+ if (startData.status === "cancelled") throw new CancelledError(`Workflow execution ${startData.executionId} was cancelled`);
122
+ return (await this.get(startData.executionId)).result;
50
123
  }
51
124
  async resume(executionId) {
52
- const [getError, execution] = await this.get(executionId);
53
- if (getError) return require_lib_errors_index.err(getError.type, getError.message);
54
- if (!execution) return require_lib_errors_index.err("WorkflowNotFoundError", `Workflow execution ${executionId} not found`);
55
- if (execution.status === "completed") return require_lib_errors_index.ok({
125
+ const execution = await this.get(executionId);
126
+ if (execution.status === "completed") return {
56
127
  executionId,
57
128
  status: execution.status
58
- });
59
- if (execution.status === "cancelled") return require_lib_errors_index.ok({
129
+ };
130
+ if (execution.status === "cancelled") return {
60
131
  executionId,
61
132
  status: execution.status
62
- });
133
+ };
63
134
  return this.execute(executionId, execution.input);
64
135
  }
65
136
  async get(executionId) {
66
- const [statusError, status] = await this.getMeta(executionId, "status");
67
- if (statusError) return require_lib_errors_index.err(statusError.type, statusError.message);
68
- if (!status) return require_lib_errors_index.err("WorkflowNotFoundError", `Workflow execution ${executionId} not found`);
137
+ const status = await this.getMeta(executionId, "status");
138
+ if (!status) throw new NotFoundError(`Workflow execution ${executionId} not found`);
69
139
  const normalizedStatus = this.normalizeStatus(status);
70
- if (!normalizedStatus) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} has invalid status ${status}`);
71
- const [inputError, input] = await this.readInput(executionId);
72
- if (inputError) return require_lib_errors_index.err(inputError.type, inputError.message);
73
- if (input === null) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} has invalid input`);
74
- const [resultError, result] = await this.readResult(executionId);
75
- if (resultError) return require_lib_errors_index.err(resultError.type, resultError.message);
76
- const [stepsError, steps] = await this.readStepSnapshots(executionId);
77
- if (stepsError) return require_lib_errors_index.err(stepsError.type, stepsError.message);
78
- const [createdAtError, createdAt] = await this.readNumberMeta(executionId, "createdAt");
79
- if (createdAtError) return require_lib_errors_index.err(createdAtError.type, createdAtError.message);
80
- if (createdAt === null) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} is missing createdAt`);
81
- const [updatedAtError, updatedAt] = await this.readNumberMeta(executionId, "updatedAt");
82
- if (updatedAtError) return require_lib_errors_index.err(updatedAtError.type, updatedAtError.message);
83
- if (updatedAt === null) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} is missing updatedAt`);
84
- const [metaError, errorMessage] = await this.getMeta(executionId, "error");
85
- if (metaError) return require_lib_errors_index.err(metaError.type, metaError.message);
86
- const [completedAtError, completedAt] = await this.readNumberMeta(executionId, "completedAt");
87
- if (completedAtError) return require_lib_errors_index.err(completedAtError.type, completedAtError.message);
88
- const [failedAtError, failedAt] = await this.readNumberMeta(executionId, "failedAt");
89
- if (failedAtError) return require_lib_errors_index.err(failedAtError.type, failedAtError.message);
90
- const [cancelledAtError, cancelledAt] = await this.readNumberMeta(executionId, "cancelledAt");
91
- if (cancelledAtError) return require_lib_errors_index.err(cancelledAtError.type, cancelledAtError.message);
92
- return require_lib_errors_index.ok({
140
+ if (!normalizedStatus) throw new StateError(`Workflow execution ${executionId} has invalid status ${status}`);
141
+ const input = await this.readInput(executionId);
142
+ const result = await this.readResult(executionId);
143
+ const steps = await this.readStepSnapshots(executionId);
144
+ const createdAt = await this.readNumberMeta(executionId, "createdAt");
145
+ const updatedAt = await this.readNumberMeta(executionId, "updatedAt");
146
+ const errorMessage = await this.getMeta(executionId, "error");
147
+ const completedAt = await this.readNumberMeta(executionId, "completedAt");
148
+ const failedAt = await this.readNumberMeta(executionId, "failedAt");
149
+ const cancelledAt = await this.readNumberMeta(executionId, "cancelledAt");
150
+ if (createdAt === null) throw new StateError(`Workflow execution ${executionId} is missing createdAt`);
151
+ if (updatedAt === null) throw new StateError(`Workflow execution ${executionId} is missing updatedAt`);
152
+ return {
93
153
  id: executionId,
94
154
  name: this.options.name,
95
155
  status: normalizedStatus,
@@ -102,31 +162,24 @@ var WorkflowDefinition = class {
102
162
  failedAt,
103
163
  cancelledAt,
104
164
  steps
105
- });
165
+ };
106
166
  }
107
167
  async cancel(executionId) {
108
- const [getError, execution] = await this.get(executionId);
109
- if (getError) return require_lib_errors_index.err(getError.type, getError.message);
110
- if (!execution) return require_lib_errors_index.err("WorkflowNotFoundError", `Workflow execution ${executionId} not found`);
111
- if (execution.status === "completed") return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} is already completed`);
168
+ const execution = await this.get(executionId);
169
+ if (execution.status === "completed") throw new StateError(`Workflow execution ${executionId} is already completed`);
112
170
  const timestamp = now();
113
- const [statusError] = await this.setMeta(executionId, "status", "cancelled");
114
- if (statusError) return require_lib_errors_index.err(statusError.type, statusError.message);
115
- const [updatedAtError] = await this.setMeta(executionId, "updatedAt", String(timestamp));
116
- if (updatedAtError) return require_lib_errors_index.err(updatedAtError.type, updatedAtError.message);
117
- const [cancelledAtError] = await this.setMeta(executionId, "cancelledAt", String(timestamp));
118
- if (cancelledAtError) return require_lib_errors_index.err(cancelledAtError.type, cancelledAtError.message);
119
- const [clearErrorError] = await this.setMeta(executionId, "error", "");
120
- if (clearErrorError) return require_lib_errors_index.err(clearErrorError.type, clearErrorError.message);
121
- const [clearFailedAtError] = await this.setMeta(executionId, "failedAt", "");
122
- if (clearFailedAtError) return require_lib_errors_index.err(clearFailedAtError.type, clearFailedAtError.message);
123
- return require_lib_errors_index.ok({
171
+ await this.setMeta(executionId, "status", "cancelled");
172
+ await this.setMeta(executionId, "updatedAt", String(timestamp));
173
+ await this.setMeta(executionId, "cancelledAt", String(timestamp));
174
+ await this.setMeta(executionId, "error", "");
175
+ await this.setMeta(executionId, "failedAt", "");
176
+ return {
124
177
  executionId,
125
178
  createdAt: execution.createdAt,
126
179
  cancelledAt: timestamp,
127
180
  updatedAt: timestamp,
128
181
  status: "cancelled"
129
- });
182
+ };
130
183
  }
131
184
  executionKey(executionId) {
132
185
  return `workflow:${this.options.name}:execution:${executionId}`;
@@ -141,12 +194,9 @@ var WorkflowDefinition = class {
141
194
  return `${this.executionKey(executionId)}:lock`;
142
195
  }
143
196
  async createExecution(executionId, input) {
144
- const [serializeError, serializedInput] = this.serializeInput(input);
145
- if (serializeError) return require_lib_errors_index.err("WorkflowSerializationError", `Unable to serialize workflow input for ${executionId}`);
197
+ const serializedInput = this.serializeInput(input);
146
198
  const timestamp = now();
147
- const [statusReadError, existingStatus] = await this.getMeta(executionId, "status");
148
- if (statusReadError) return require_lib_errors_index.err(statusReadError.type, statusReadError.message);
149
- if (existingStatus) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} already exists`);
199
+ if (await this.getMeta(executionId, "status")) throw new StateError(`Workflow execution ${executionId} already exists`);
150
200
  const metadata = {
151
201
  status: "pending",
152
202
  input: serializedInput,
@@ -160,61 +210,44 @@ var WorkflowDefinition = class {
160
210
  steps: "[]"
161
211
  };
162
212
  const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.metaKey(executionId), metadata));
163
- if (writeError) return require_lib_errors_index.err("WorkflowError", `Unable to persist metadata for execution ${executionId}`);
164
- return require_lib_errors_index.ok(null);
213
+ if (writeError) throw new WorkflowError(`Unable to persist metadata for execution ${executionId}`);
165
214
  }
166
215
  async execute(executionId, input) {
167
216
  const token = crypto.randomUUID();
168
- const [lockError] = await this.acquireLock(executionId, token);
169
- if (lockError) return require_lib_errors_index.err(lockError.type, lockError.message);
170
- const [statusCheckError, currentStatus] = await this.getMeta(executionId, "status");
171
- if (statusCheckError) {
217
+ await this.acquireLock(executionId, token);
218
+ if (await this.getMeta(executionId, "status") === "cancelled") {
172
219
  await this.releaseLock(executionId, token);
173
- return require_lib_errors_index.err(statusCheckError.type, statusCheckError.message);
174
- }
175
- if (currentStatus === "cancelled") {
176
- await this.releaseLock(executionId, token);
177
- return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} was cancelled`);
220
+ throw new StateError(`Workflow execution ${executionId} was cancelled`);
178
221
  }
179
222
  const timestamp = now();
180
- const [runningStatusError] = await this.setMeta(executionId, "status", "running");
181
- if (runningStatusError) {
182
- await this.releaseLock(executionId, token);
183
- return require_lib_errors_index.err(runningStatusError.type, runningStatusError.message);
184
- }
185
- const [runningUpdatedAtError] = await this.setMeta(executionId, "updatedAt", String(timestamp));
186
- if (runningUpdatedAtError) {
187
- await this.releaseLock(executionId, token);
188
- return require_lib_errors_index.err(runningUpdatedAtError.type, runningUpdatedAtError.message);
189
- }
223
+ await this.setMeta(executionId, "status", "running");
224
+ await this.setMeta(executionId, "updatedAt", String(timestamp));
190
225
  const controller = new AbortController();
191
226
  const renewInterval = Math.floor(this.lockTTL / 3);
192
227
  let lockLost = false;
193
228
  const renewTimer = setInterval(async () => {
194
- const [renewError] = await this.extendLock(executionId, token);
229
+ const [renewError] = await require_lib_errors_index.mightThrow(this.extendLock(executionId, token));
195
230
  if (renewError) {
196
231
  lockLost = true;
197
232
  controller.abort();
198
233
  clearInterval(renewTimer);
199
234
  }
200
235
  }, renewInterval);
236
+ if (this.options.hooks?.onStart) await this.runHook(() => this.options.hooks?.onStart?.({
237
+ executionId,
238
+ input
239
+ }));
201
240
  const step = async (name, handler) => {
202
- const [cancelledError, cancelled] = await this.isCancelled(executionId);
203
- if (cancelledError) return Promise.reject(new Error(cancelledError.message));
204
- if (cancelled) {
241
+ await this.throwIfCancelled(executionId, () => {
205
242
  controller.abort();
206
- return Promise.reject(/* @__PURE__ */ new Error("Workflow cancelled"));
207
- }
208
- const [readError, cachedStep] = await this.readStepOutput(executionId, name);
209
- if (readError) return Promise.reject(new Error(readError.message));
243
+ });
244
+ const cachedStep = await this.readStepOutput(executionId, name);
210
245
  if (cachedStep.found) return cachedStep.value;
211
- const [stepError, output] = await require_lib_errors_index.mightThrow(Promise.resolve(handler(input, controller.signal)));
212
- if (stepError) return Promise.reject(stepError);
213
- const [writeError] = await this.writeStepOutput(executionId, name, output);
214
- if (writeError) return Promise.reject(new Error(writeError.message));
215
- return output;
246
+ return this.runStepWithRetries(executionId, input, name, handler, controller.signal, () => {
247
+ controller.abort();
248
+ });
216
249
  };
217
- const [handlerError, result] = await require_lib_errors_index.mightThrow(Promise.resolve(this.options.handler({
250
+ const handlerOutcome = await require_lib_errors_index.mightThrow(Promise.resolve(this.options.handler({
218
251
  input,
219
252
  executionId,
220
253
  signal: controller.signal,
@@ -223,188 +256,166 @@ var WorkflowDefinition = class {
223
256
  clearInterval(renewTimer);
224
257
  if (lockLost) {
225
258
  await this.releaseLock(executionId, token);
226
- return require_lib_errors_index.err("WorkflowLockError", `Lock expired during execution ${executionId}`);
227
- }
228
- const [cancelledError, cancelled] = await this.isCancelled(executionId);
229
- if (cancelledError) {
230
- await this.releaseLock(executionId, token);
231
- return require_lib_errors_index.err(cancelledError.type, cancelledError.message);
259
+ throw new LockError(`Lock expired during execution ${executionId}`);
232
260
  }
233
- if (cancelled) {
261
+ if (await this.isCancelled(executionId)) {
234
262
  const cancelledAt = now();
235
- const [cancelledStatusError] = await this.setMeta(executionId, "status", "cancelled");
236
- if (cancelledStatusError) {
237
- await this.releaseLock(executionId, token);
238
- return require_lib_errors_index.err(cancelledStatusError.type, cancelledStatusError.message);
239
- }
240
- const [cancelledUpdatedAtError] = await this.setMeta(executionId, "updatedAt", String(cancelledAt));
241
- if (cancelledUpdatedAtError) {
242
- await this.releaseLock(executionId, token);
243
- return require_lib_errors_index.err(cancelledUpdatedAtError.type, cancelledUpdatedAtError.message);
244
- }
245
- const [cancelledAtError] = await this.setMeta(executionId, "cancelledAt", String(cancelledAt));
246
- if (cancelledAtError) {
247
- await this.releaseLock(executionId, token);
248
- return require_lib_errors_index.err(cancelledAtError.type, cancelledAtError.message);
249
- }
263
+ await this.setMeta(executionId, "status", "cancelled");
264
+ await this.setMeta(executionId, "updatedAt", String(cancelledAt));
265
+ await this.setMeta(executionId, "cancelledAt", String(cancelledAt));
266
+ if (this.options.hooks?.onCancel) await this.runHook(() => this.options.hooks?.onCancel?.({
267
+ executionId,
268
+ input
269
+ }));
250
270
  await this.releaseLock(executionId, token);
251
- return require_lib_errors_index.ok({
271
+ return {
252
272
  executionId,
253
273
  status: "cancelled"
254
- });
274
+ };
255
275
  }
276
+ const [handlerError, result] = handlerOutcome;
256
277
  if (handlerError) {
257
278
  const failedAt = now();
258
- const [failedStatusError] = await this.setMeta(executionId, "status", "failed");
259
- if (failedStatusError) {
260
- await this.releaseLock(executionId, token);
261
- return require_lib_errors_index.err(failedStatusError.type, failedStatusError.message);
262
- }
263
- const [failedMessageError] = await this.setMeta(executionId, "error", toErrorMessage(handlerError));
264
- if (failedMessageError) {
265
- await this.releaseLock(executionId, token);
266
- return require_lib_errors_index.err(failedMessageError.type, failedMessageError.message);
267
- }
268
- const [failedUpdatedAtError] = await this.setMeta(executionId, "updatedAt", String(failedAt));
269
- if (failedUpdatedAtError) {
270
- await this.releaseLock(executionId, token);
271
- return require_lib_errors_index.err(failedUpdatedAtError.type, failedUpdatedAtError.message);
272
- }
273
- const [failedAtError] = await this.setMeta(executionId, "failedAt", String(failedAt));
274
- if (failedAtError) {
275
- await this.releaseLock(executionId, token);
276
- return require_lib_errors_index.err(failedAtError.type, failedAtError.message);
277
- }
279
+ await this.setMeta(executionId, "status", "failed");
280
+ await this.setMeta(executionId, "error", handlerError.message);
281
+ await this.setMeta(executionId, "updatedAt", String(failedAt));
282
+ await this.setMeta(executionId, "failedAt", String(failedAt));
278
283
  await this.releaseLock(executionId, token);
279
- return require_lib_errors_index.err("WorkflowExecutionError", `Workflow execution ${executionId} failed: ${toErrorMessage(handlerError)}`);
284
+ throw new ExecutionError(`Workflow execution ${executionId} failed: ${handlerError.message}`);
280
285
  }
281
- const [serializeResultError, serializedResult] = this.serializeResult(result);
286
+ const [serializeResultError, serializedResult] = require_lib_errors_index.mightThrowSync(() => this.serializeResult(result));
282
287
  if (serializeResultError) {
283
288
  const failedAt = now();
284
- const [failedStatusError] = await this.setMeta(executionId, "status", "failed");
285
- if (failedStatusError) {
286
- await this.releaseLock(executionId, token);
287
- return require_lib_errors_index.err(failedStatusError.type, failedStatusError.message);
288
- }
289
- const [failedMessageError] = await this.setMeta(executionId, "error", serializeResultError.message);
290
- if (failedMessageError) {
291
- await this.releaseLock(executionId, token);
292
- return require_lib_errors_index.err(failedMessageError.type, failedMessageError.message);
293
- }
294
- const [failedUpdatedAtError] = await this.setMeta(executionId, "updatedAt", String(failedAt));
295
- if (failedUpdatedAtError) {
296
- await this.releaseLock(executionId, token);
297
- return require_lib_errors_index.err(failedUpdatedAtError.type, failedUpdatedAtError.message);
298
- }
299
- const [failedAtError] = await this.setMeta(executionId, "failedAt", String(failedAt));
300
- if (failedAtError) {
301
- await this.releaseLock(executionId, token);
302
- return require_lib_errors_index.err(failedAtError.type, failedAtError.message);
303
- }
289
+ await this.setMeta(executionId, "status", "failed");
290
+ await this.setMeta(executionId, "error", serializeResultError.message);
291
+ await this.setMeta(executionId, "updatedAt", String(failedAt));
292
+ await this.setMeta(executionId, "failedAt", String(failedAt));
304
293
  await this.releaseLock(executionId, token);
305
- return require_lib_errors_index.err("WorkflowSerializationError", `Unable to serialize workflow result for ${executionId}`);
294
+ throw new SerializationError(`Unable to serialize workflow result for ${executionId}`);
306
295
  }
307
296
  const completedAt = now();
308
- const [completedResultError] = await this.setMeta(executionId, "result", serializedResult);
309
- if (completedResultError) {
310
- await this.releaseLock(executionId, token);
311
- return require_lib_errors_index.err(completedResultError.type, completedResultError.message);
312
- }
313
- const [completedStatusError] = await this.setMeta(executionId, "status", "completed");
314
- if (completedStatusError) {
315
- await this.releaseLock(executionId, token);
316
- return require_lib_errors_index.err(completedStatusError.type, completedStatusError.message);
317
- }
318
- const [completedClearErrorError] = await this.setMeta(executionId, "error", "");
319
- if (completedClearErrorError) {
320
- await this.releaseLock(executionId, token);
321
- return require_lib_errors_index.err(completedClearErrorError.type, completedClearErrorError.message);
322
- }
323
- const [completedClearFailedAtError] = await this.setMeta(executionId, "failedAt", "");
324
- if (completedClearFailedAtError) {
325
- await this.releaseLock(executionId, token);
326
- return require_lib_errors_index.err(completedClearFailedAtError.type, completedClearFailedAtError.message);
327
- }
328
- const [completedUpdatedAtError] = await this.setMeta(executionId, "updatedAt", String(completedAt));
329
- if (completedUpdatedAtError) {
330
- await this.releaseLock(executionId, token);
331
- return require_lib_errors_index.err(completedUpdatedAtError.type, completedUpdatedAtError.message);
332
- }
333
- const [completedAtError] = await this.setMeta(executionId, "completedAt", String(completedAt));
334
- if (completedAtError) {
335
- await this.releaseLock(executionId, token);
336
- return require_lib_errors_index.err(completedAtError.type, completedAtError.message);
337
- }
297
+ await this.setMeta(executionId, "result", serializedResult);
298
+ await this.setMeta(executionId, "status", "completed");
299
+ await this.setMeta(executionId, "error", "");
300
+ await this.setMeta(executionId, "failedAt", "");
301
+ await this.setMeta(executionId, "updatedAt", String(completedAt));
302
+ await this.setMeta(executionId, "completedAt", String(completedAt));
303
+ if (this.options.hooks?.onComplete) await this.runHook(() => this.options.hooks?.onComplete?.({
304
+ executionId,
305
+ input,
306
+ result
307
+ }));
338
308
  await this.releaseLock(executionId, token);
339
- return require_lib_errors_index.ok({
309
+ return {
340
310
  executionId,
341
311
  status: "completed"
342
- });
312
+ };
313
+ }
314
+ async throwIfCancelled(executionId, abort) {
315
+ if (await this.isCancelled(executionId)) {
316
+ abort();
317
+ throw new CancelledError(`Workflow execution ${executionId} was cancelled`);
318
+ }
319
+ }
320
+ async runStepWithRetries(executionId, input, stepName, handler, signal, abort) {
321
+ let attempt = 1;
322
+ const errorHistory = [];
323
+ while (true) {
324
+ await this.throwIfCancelled(executionId, abort);
325
+ const [stepError, output] = await require_lib_errors_index.mightThrow(Promise.resolve(handler(input, signal)));
326
+ if (!stepError) {
327
+ await this.writeStepOutput(executionId, stepName, output);
328
+ return output;
329
+ }
330
+ const errorMsg = stepError.message;
331
+ errorHistory.push({
332
+ attempt,
333
+ error: errorMsg,
334
+ timestamp: now()
335
+ });
336
+ if (attempt <= this.retries) {
337
+ const nextRetryDelayMs = this.computeBackoffDelay(attempt);
338
+ if (this.options.hooks?.onRetry) await this.runHook(() => this.options.hooks?.onRetry?.({
339
+ executionId,
340
+ input,
341
+ stepName,
342
+ error: errorMsg,
343
+ attempt,
344
+ nextRetryDelayMs,
345
+ retriesRemaining: this.retries - attempt
346
+ }));
347
+ const [delayError] = await require_lib_errors_index.mightThrow(delay(nextRetryDelayMs, signal, () => this.isCancelled(executionId)));
348
+ if (delayError) throw delayError;
349
+ attempt++;
350
+ continue;
351
+ }
352
+ if (this.options.hooks?.onError) await this.runHook(() => this.options.hooks?.onError?.({
353
+ executionId,
354
+ input,
355
+ stepName,
356
+ error: errorMsg,
357
+ totalAttempts: attempt,
358
+ errorHistory
359
+ }));
360
+ throw stepError;
361
+ }
343
362
  }
344
363
  async acquireLock(executionId, token) {
345
364
  const [lockError, lockResult] = await require_lib_errors_index.mightThrow(this.options.redis.set(this.lockKey(executionId), token, "PX", String(this.lockTTL), "NX"));
346
- if (lockError) return require_lib_errors_index.err("WorkflowLockError", `Unable to acquire lock for execution ${executionId}`);
347
- if (lockResult !== "OK") return require_lib_errors_index.err("WorkflowLockError", `Workflow execution ${executionId} is already running`);
348
- return require_lib_errors_index.ok(null);
365
+ if (lockError) throw new LockError(`Unable to acquire lock for execution ${executionId}`);
366
+ if (lockResult !== "OK") throw new LockError(`Workflow execution ${executionId} is already running`);
349
367
  }
350
368
  async releaseLock(executionId, token) {
351
- const [evalError] = await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
369
+ await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
352
370
  "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end",
353
371
  "1",
354
372
  this.lockKey(executionId),
355
373
  token
356
374
  ]));
357
- if (evalError) return require_lib_errors_index.err("WorkflowLockError", `Unable to release lock for execution ${executionId}`);
358
- return require_lib_errors_index.ok(null);
359
375
  }
360
376
  async extendLock(executionId, token) {
361
- const [evalError, result] = await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
377
+ const [evalError, extendResult] = await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
362
378
  "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end",
363
379
  "1",
364
380
  this.lockKey(executionId),
365
381
  token,
366
382
  String(this.lockTTL)
367
383
  ]));
368
- if (evalError) return require_lib_errors_index.err("WorkflowLockError", `Unable to extend lock for execution ${executionId}`);
369
- if (result === 0) return require_lib_errors_index.err("WorkflowLockError", `Lock ownership lost for execution ${executionId}`);
370
- return require_lib_errors_index.ok(null);
384
+ if (evalError) throw new LockError(`Unable to extend lock for execution ${executionId}`);
385
+ if (extendResult === 0) throw new LockError(`Lock ownership lost for execution ${executionId}`);
371
386
  }
372
387
  async isCancelled(executionId) {
373
- const [statusError, status] = await this.getMeta(executionId, "status");
374
- if (statusError) return require_lib_errors_index.err(statusError.type, statusError.message);
375
- return require_lib_errors_index.ok(status === "cancelled");
388
+ return await this.getMeta(executionId, "status") === "cancelled";
376
389
  }
377
390
  async setMeta(executionId, field, value) {
378
391
  const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.metaKey(executionId), field, value));
379
- if (writeError) return require_lib_errors_index.err("WorkflowError", `Unable to persist ${field} for execution ${executionId}`);
380
- return require_lib_errors_index.ok(null);
392
+ if (writeError) throw new WorkflowError(`Unable to persist ${field} for execution ${executionId}`);
381
393
  }
382
394
  async getMeta(executionId, field) {
383
395
  const [readError, value] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.metaKey(executionId), field));
384
- if (readError) return require_lib_errors_index.err("WorkflowError", `Unable to read ${field} for execution ${executionId}`);
385
- if (value === null || value === void 0) return require_lib_errors_index.ok(null);
386
- if (typeof value !== "string") return require_lib_errors_index.err("WorkflowStateError", `Invalid ${field} value for execution ${executionId}`);
387
- if (value.length === 0) return require_lib_errors_index.ok(null);
388
- return require_lib_errors_index.ok(value);
396
+ if (readError) throw new WorkflowError(`Unable to read ${field} for execution ${executionId}`);
397
+ if (value === null || value === void 0) return null;
398
+ if (typeof value !== "string") throw new StateError(`Invalid ${field} value for execution ${executionId}`);
399
+ if (value.length === 0) return null;
400
+ return value;
389
401
  }
390
402
  async readNumberMeta(executionId, field) {
391
- const [readError, value] = await this.getMeta(executionId, field);
392
- if (readError) return require_lib_errors_index.err(readError.type, readError.message);
393
- if (!value) return require_lib_errors_index.ok(null);
403
+ const value = await this.getMeta(executionId, field);
404
+ if (!value) return null;
394
405
  const parsed = Number(value);
395
- if (!Number.isFinite(parsed)) return require_lib_errors_index.err("WorkflowStateError", `Invalid ${field} value for execution ${executionId}`);
396
- return require_lib_errors_index.ok(parsed);
406
+ if (!Number.isFinite(parsed)) throw new StateError(`Invalid ${field} value for execution ${executionId}`);
407
+ return parsed;
397
408
  }
398
409
  runSerializer(value, serializer, label) {
399
410
  const [serializeError, serialized] = require_lib_errors_index.mightThrowSync(() => serializer(value));
400
- if (serializeError) return require_lib_errors_index.err("WorkflowSerializationError", `Unable to serialize ${label}: ${toErrorMessage(serializeError)}`);
401
- if (typeof serialized !== "string") return require_lib_errors_index.err("WorkflowSerializationError", `${label} serializer must return a string`);
402
- return require_lib_errors_index.ok(serialized);
411
+ if (serializeError) throw new SerializationError(`Unable to serialize ${label}: ${serializeError.message}`);
412
+ if (typeof serialized !== "string") throw new SerializationError(`${label} serializer must return a string`);
413
+ return serialized;
403
414
  }
404
415
  runDeserializer(raw, deserializer, label) {
405
- const [deserializeError, value] = require_lib_errors_index.mightThrowSync(() => deserializer(raw));
406
- if (deserializeError) return require_lib_errors_index.err("WorkflowSerializationError", `Unable to deserialize ${label}: ${toErrorMessage(deserializeError)}`);
407
- return require_lib_errors_index.ok(value);
416
+ const result = require_lib_errors_index.mightThrowSync(() => deserializer(raw));
417
+ if (result[0]) throw new SerializationError(`Unable to deserialize ${label}: ${result[0].message}`);
418
+ return result[1];
408
419
  }
409
420
  serializeInput(input) {
410
421
  return this.runSerializer(input, this.options.serializeInput ?? envelopeSerialize, "workflow input");
@@ -414,7 +425,7 @@ var WorkflowDefinition = class {
414
425
  return this.runDeserializer(raw, deserializer, "workflow input");
415
426
  }
416
427
  serializeResult(result) {
417
- if (result === null) return require_lib_errors_index.ok(envelopeSerialize(null));
428
+ if (result === null) return envelopeSerialize(null);
418
429
  return this.runSerializer(result, this.options.serializeResult ?? envelopeSerialize, "workflow result");
419
430
  }
420
431
  deserializeResult(raw) {
@@ -429,91 +440,76 @@ var WorkflowDefinition = class {
429
440
  return this.runDeserializer(raw, deserializer, "step output");
430
441
  }
431
442
  async readInput(executionId) {
432
- const [readError, raw] = await this.getMeta(executionId, "input");
433
- if (readError) return require_lib_errors_index.err(readError.type, readError.message);
434
- if (!raw) return require_lib_errors_index.err("WorkflowStateError", `Workflow execution ${executionId} input not found`);
443
+ const raw = await this.getMeta(executionId, "input");
444
+ if (!raw) throw new StateError(`Workflow execution ${executionId} input not found`);
435
445
  return this.deserializeInput(raw);
436
446
  }
437
447
  async readResult(executionId) {
438
- const [readError, raw] = await this.getMeta(executionId, "result");
439
- if (readError) return require_lib_errors_index.err(readError.type, readError.message);
440
- if (!raw) return require_lib_errors_index.ok(null);
441
- const [deserializeError, result] = this.deserializeResult(raw);
442
- if (deserializeError) return require_lib_errors_index.err(deserializeError.type, deserializeError.message);
443
- return require_lib_errors_index.ok(result);
448
+ const raw = await this.getMeta(executionId, "result");
449
+ if (!raw) return null;
450
+ return this.deserializeResult(raw);
444
451
  }
445
452
  async writeStepOutput(executionId, stepName, output) {
446
- const [serializeError, serializedOutput] = this.serializeStepOutput(output);
447
- if (serializeError) return require_lib_errors_index.err("WorkflowSerializationError", `Unable to serialize step ${stepName} output`);
448
453
  const payload = {
449
- output: serializedOutput,
454
+ output: this.serializeStepOutput(output),
450
455
  completedAt: now()
451
456
  };
452
457
  const [payloadError, payloadRaw] = require_lib_errors_index.mightThrowSync(() => JSON.stringify(payload));
453
- if (payloadError || typeof payloadRaw !== "string") return require_lib_errors_index.err("WorkflowSerializationError", `Unable to persist step ${stepName} output`);
458
+ if (payloadError || typeof payloadRaw !== "string") throw new SerializationError(`Unable to persist step ${stepName} output`);
454
459
  const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.stepsKey(executionId), stepName, payloadRaw));
455
- if (writeError) return require_lib_errors_index.err("WorkflowError", `Unable to persist step ${stepName} for execution ${executionId}`);
456
- const [stepNamesError, stepNames] = await this.readStepNames(executionId);
457
- if (stepNamesError) return require_lib_errors_index.err(stepNamesError.type, stepNamesError.message);
460
+ if (writeError) throw new WorkflowError(`Unable to persist step ${stepName} for execution ${executionId}`);
461
+ const stepNames = await this.readStepNames(executionId);
458
462
  if (!stepNames.includes(stepName)) {
459
463
  const nextStepNames = [...stepNames, stepName];
460
464
  const [serializeStepsError, serializedSteps] = require_lib_errors_index.mightThrowSync(() => JSON.stringify(nextStepNames));
461
- if (serializeStepsError || typeof serializedSteps !== "string") return require_lib_errors_index.err("WorkflowSerializationError", `Unable to persist step history for execution ${executionId}`);
462
- const [updateStepsError] = await this.setMeta(executionId, "steps", serializedSteps);
463
- if (updateStepsError) return require_lib_errors_index.err(updateStepsError.type, updateStepsError.message);
465
+ if (serializeStepsError || typeof serializedSteps !== "string") throw new SerializationError(`Unable to persist step history for execution ${executionId}`);
466
+ await this.setMeta(executionId, "steps", serializedSteps);
464
467
  }
465
- const [updatedError] = await this.setMeta(executionId, "updatedAt", String(now()));
466
- if (updatedError) return require_lib_errors_index.err(updatedError.type, updatedError.message);
467
- return require_lib_errors_index.ok(null);
468
+ await this.setMeta(executionId, "updatedAt", String(now()));
468
469
  }
469
470
  async readStepOutput(executionId, stepName) {
470
471
  const [readError, payloadRaw] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.stepsKey(executionId), stepName));
471
- if (readError) return require_lib_errors_index.err("WorkflowError", `Unable to read step ${stepName} for execution ${executionId}`);
472
- if (!payloadRaw) return require_lib_errors_index.ok({
472
+ if (readError) throw new WorkflowError(`Unable to read step ${stepName} for execution ${executionId}`);
473
+ if (!payloadRaw) return {
473
474
  found: false,
474
475
  value: null
475
- });
476
- if (typeof payloadRaw !== "string") return require_lib_errors_index.err("WorkflowStateError", `Invalid step payload for ${stepName} in execution ${executionId}`);
476
+ };
477
+ if (typeof payloadRaw !== "string") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
477
478
  const [parseError, parsed] = require_lib_errors_index.mightThrowSync(() => JSON.parse(payloadRaw));
478
- if (parseError || parsed === null || typeof parsed !== "object") return require_lib_errors_index.err("WorkflowStateError", `Invalid step payload for ${stepName} in execution ${executionId}`);
479
- if (typeof parsed.output !== "string") return require_lib_errors_index.err("WorkflowStateError", `Invalid step output for ${stepName} in execution ${executionId}`);
479
+ if (parseError || parsed === null || typeof parsed !== "object") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
480
+ if (typeof parsed.output !== "string") throw new StateError(`Invalid step output for ${stepName} in execution ${executionId}`);
480
481
  const outputRaw = parsed.output;
481
- const [deserializeError, value] = this.deserializeStepOutput(outputRaw);
482
- if (deserializeError) return require_lib_errors_index.err(deserializeError.type, deserializeError.message);
483
- return require_lib_errors_index.ok({
482
+ return {
484
483
  found: true,
485
- value
486
- });
484
+ value: this.deserializeStepOutput(outputRaw)
485
+ };
487
486
  }
488
487
  async readStepNames(executionId) {
489
- const [readError, stepsRaw] = await this.getMeta(executionId, "steps");
490
- if (readError) return [readError, []];
491
- if (!stepsRaw) return require_lib_errors_index.ok([]);
488
+ const stepsRaw = await this.getMeta(executionId, "steps");
489
+ if (!stepsRaw) return [];
492
490
  const [parseError, values] = require_lib_errors_index.mightThrowSync(() => JSON.parse(stepsRaw));
493
- if (parseError || !Array.isArray(values)) return require_lib_errors_index.err("WorkflowStateError", `Invalid step index for execution ${executionId}`);
491
+ if (parseError || !Array.isArray(values)) throw new StateError(`Invalid step index for execution ${executionId}`);
494
492
  const stepNames = [];
495
493
  for (const value of values) if (typeof value === "string") stepNames.push(value);
496
- return require_lib_errors_index.ok(stepNames);
494
+ return stepNames;
497
495
  }
498
496
  async readStepSnapshots(executionId) {
499
- const [stepNamesError, stepNames] = await this.readStepNames(executionId);
500
- if (stepNamesError) return [stepNamesError, []];
497
+ const stepNames = await this.readStepNames(executionId);
501
498
  const steps = [];
502
499
  for (const stepName of stepNames) {
503
500
  const [readError, payloadRaw] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.stepsKey(executionId), stepName));
504
- if (readError) return require_lib_errors_index.err("WorkflowError", `Unable to read step ${stepName} for execution ${executionId}`);
501
+ if (readError) throw new WorkflowError(`Unable to read step ${stepName} for execution ${executionId}`);
505
502
  if (!payloadRaw) continue;
506
- if (typeof payloadRaw !== "string") return require_lib_errors_index.err("WorkflowStateError", `Invalid step payload for ${stepName} in execution ${executionId}`);
503
+ if (typeof payloadRaw !== "string") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
507
504
  const [parseError, parsed] = require_lib_errors_index.mightThrowSync(() => JSON.parse(payloadRaw));
508
- if (parseError || parsed === null || typeof parsed !== "object") return require_lib_errors_index.err("WorkflowStateError", `Invalid step payload for ${stepName} in execution ${executionId}`);
509
- if (typeof parsed.completedAt !== "number") return require_lib_errors_index.err("WorkflowStateError", `Invalid step payload for ${stepName} in execution ${executionId}`);
510
- const completedAt = parsed.completedAt;
505
+ if (parseError || parsed === null || typeof parsed !== "object") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
506
+ if (typeof parsed.completedAt !== "number") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
511
507
  steps.push({
512
508
  name: stepName,
513
- completedAt
509
+ completedAt: parsed.completedAt
514
510
  });
515
511
  }
516
- return require_lib_errors_index.ok(steps);
512
+ return steps;
517
513
  }
518
514
  normalizeStatus(value) {
519
515
  for (const status of knownStatuses) if (status === value) return status;
@@ -531,4 +527,11 @@ const defineWorkflow = (options) => {
531
527
  };
532
528
  };
533
529
  //#endregion
530
+ exports.CancelledError = CancelledError;
531
+ exports.ExecutionError = ExecutionError;
532
+ exports.LockError = LockError;
533
+ exports.NotFoundError = NotFoundError;
534
+ exports.SerializationError = SerializationError;
535
+ exports.StateError = StateError;
536
+ exports.WorkflowError = WorkflowError;
534
537
  exports.defineWorkflow = defineWorkflow;