semola 0.6.0 → 0.6.2

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 (51) hide show
  1. package/README.md +19 -5
  2. package/dist/index-DISN0WKZ.d.mts +78 -0
  3. package/dist/lib/api/index.cjs +1 -537
  4. package/dist/lib/api/index.d.cts +1 -271
  5. package/dist/lib/api/index.d.mts +100 -164
  6. package/dist/lib/api/index.mjs +1 -535
  7. package/dist/lib/cache/index.cjs +1 -99
  8. package/dist/lib/cache/index.d.cts +1 -27
  9. package/dist/lib/cache/index.mjs +1 -98
  10. package/dist/lib/cli/index.cjs +3 -0
  11. package/dist/lib/cli/index.d.cts +1 -0
  12. package/dist/lib/cli/index.d.mts +90 -0
  13. package/dist/lib/cli/index.mjs +3 -0
  14. package/dist/lib/cron/index.cjs +1 -735
  15. package/dist/lib/cron/index.d.cts +1 -146
  16. package/dist/lib/cron/index.d.mts +99 -36
  17. package/dist/lib/cron/index.mjs +1 -726
  18. package/dist/lib/errors/index.cjs +1 -30
  19. package/dist/lib/errors/index.d.cts +1 -13
  20. package/dist/lib/errors/index.mjs +1 -26
  21. package/dist/lib/i18n/index.cjs +1 -42
  22. package/dist/lib/i18n/index.d.cts +1 -28
  23. package/dist/lib/i18n/index.d.mts +2 -2
  24. package/dist/lib/i18n/index.mjs +1 -41
  25. package/dist/lib/logging/index.cjs +1 -388
  26. package/dist/lib/logging/index.d.cts +1 -108
  27. package/dist/lib/logging/index.mjs +1 -374
  28. package/dist/lib/orm/index.cjs +1 -1642
  29. package/dist/lib/orm/index.d.cts +1 -402
  30. package/dist/lib/orm/index.d.mts +27 -25
  31. package/dist/lib/orm/index.mjs +1 -1630
  32. package/dist/lib/policy/index.cjs +1 -285
  33. package/dist/lib/policy/index.d.cts +1 -77
  34. package/dist/lib/policy/index.d.mts +1 -1
  35. package/dist/lib/policy/index.mjs +1 -265
  36. package/dist/lib/prompts/index.cjs +6 -769
  37. package/dist/lib/prompts/index.d.cts +1 -75
  38. package/dist/lib/prompts/index.mjs +6 -762
  39. package/dist/lib/pubsub/index.cjs +1 -141
  40. package/dist/lib/pubsub/index.d.cts +1 -26
  41. package/dist/lib/pubsub/index.mjs +1 -140
  42. package/dist/lib/queue/index.cjs +1 -212
  43. package/dist/lib/queue/index.d.cts +1 -81
  44. package/dist/lib/queue/index.mjs +1 -209
  45. package/dist/lib/workflow/index.cjs +1 -537
  46. package/dist/lib/workflow/index.d.cts +1 -150
  47. package/dist/lib/workflow/index.d.mts +0 -1
  48. package/dist/lib/workflow/index.mjs +1 -527
  49. package/dist/rolldown-runtime-CMqjfN_6.cjs +1 -0
  50. package/package.json +17 -5
  51. package/dist/chunk-CKQMccvm.cjs +0 -28
@@ -1,537 +1 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_chunk = require("../../chunk-CKQMccvm.cjs");
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
50
- //#region src/lib/workflow/index.ts
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;
56
- const now = () => Date.now();
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
- }
71
- };
72
- const envelopeSerialize = (value) => {
73
- return JSON.stringify({ value });
74
- };
75
- const envelopeDeserialize = (raw) => {
76
- const [parseError, parsed] = require_lib_errors_index.mightThrowSync(() => JSON.parse(raw));
77
- if (parseError) throw parseError;
78
- if (parsed === null) return;
79
- if (typeof parsed !== "object") return;
80
- if ("value" in parsed) return parsed.value;
81
- };
82
- const knownStatuses = [
83
- "pending",
84
- "running",
85
- "completed",
86
- "failed",
87
- "cancelled"
88
- ];
89
- var WorkflowDefinition = class {
90
- options;
91
- lockTTL;
92
- retries;
93
- retryBaseDelay;
94
- retryMultiplier;
95
- retryMaxDelay;
96
- constructor(options) {
97
- this.options = options;
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);
113
- }
114
- async start(input, options) {
115
- const executionId = options?.executionId ?? crypto.randomUUID();
116
- await this.createExecution(executionId, input);
117
- return this.execute(executionId, input);
118
- }
119
- async run(input, options) {
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;
123
- }
124
- async resume(executionId) {
125
- const execution = await this.get(executionId);
126
- if (execution.status === "completed") return {
127
- executionId,
128
- status: execution.status
129
- };
130
- if (execution.status === "cancelled") return {
131
- executionId,
132
- status: execution.status
133
- };
134
- return this.execute(executionId, execution.input);
135
- }
136
- async get(executionId) {
137
- const status = await this.getMeta(executionId, "status");
138
- if (!status) throw new NotFoundError(`Workflow execution ${executionId} not found`);
139
- const normalizedStatus = this.normalizeStatus(status);
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 {
153
- id: executionId,
154
- name: this.options.name,
155
- status: normalizedStatus,
156
- input,
157
- result,
158
- error: errorMessage,
159
- createdAt,
160
- updatedAt,
161
- completedAt,
162
- failedAt,
163
- cancelledAt,
164
- steps
165
- };
166
- }
167
- async cancel(executionId) {
168
- const execution = await this.get(executionId);
169
- if (execution.status === "completed") throw new StateError(`Workflow execution ${executionId} is already completed`);
170
- const timestamp = now();
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 {
177
- executionId,
178
- createdAt: execution.createdAt,
179
- cancelledAt: timestamp,
180
- updatedAt: timestamp,
181
- status: "cancelled"
182
- };
183
- }
184
- executionKey(executionId) {
185
- return `workflow:${this.options.name}:execution:${executionId}`;
186
- }
187
- metaKey(executionId) {
188
- return `${this.executionKey(executionId)}:meta`;
189
- }
190
- stepsKey(executionId) {
191
- return `${this.executionKey(executionId)}:steps`;
192
- }
193
- lockKey(executionId) {
194
- return `${this.executionKey(executionId)}:lock`;
195
- }
196
- async createExecution(executionId, input) {
197
- const serializedInput = this.serializeInput(input);
198
- const timestamp = now();
199
- if (await this.getMeta(executionId, "status")) throw new StateError(`Workflow execution ${executionId} already exists`);
200
- const metadata = {
201
- status: "pending",
202
- input: serializedInput,
203
- result: "",
204
- error: "",
205
- createdAt: String(timestamp),
206
- updatedAt: String(timestamp),
207
- completedAt: "",
208
- failedAt: "",
209
- cancelledAt: "",
210
- steps: "[]"
211
- };
212
- const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.metaKey(executionId), metadata));
213
- if (writeError) throw new WorkflowError(`Unable to persist metadata for execution ${executionId}`);
214
- }
215
- async execute(executionId, input) {
216
- const token = crypto.randomUUID();
217
- await this.acquireLock(executionId, token);
218
- if (await this.getMeta(executionId, "status") === "cancelled") {
219
- await this.releaseLock(executionId, token);
220
- throw new StateError(`Workflow execution ${executionId} was cancelled`);
221
- }
222
- const timestamp = now();
223
- await this.setMeta(executionId, "status", "running");
224
- await this.setMeta(executionId, "updatedAt", String(timestamp));
225
- const controller = new AbortController();
226
- const renewInterval = Math.floor(this.lockTTL / 3);
227
- let lockLost = false;
228
- const renewTimer = setInterval(async () => {
229
- const [renewError] = await require_lib_errors_index.mightThrow(this.extendLock(executionId, token));
230
- if (renewError) {
231
- lockLost = true;
232
- controller.abort();
233
- clearInterval(renewTimer);
234
- }
235
- }, renewInterval);
236
- if (this.options.hooks?.onStart) await this.runHook(() => this.options.hooks?.onStart?.({
237
- executionId,
238
- input
239
- }));
240
- const step = async (name, handler) => {
241
- await this.throwIfCancelled(executionId, () => {
242
- controller.abort();
243
- });
244
- const cachedStep = await this.readStepOutput(executionId, name);
245
- if (cachedStep.found) return cachedStep.value;
246
- return this.runStepWithRetries(executionId, input, name, handler, controller.signal, () => {
247
- controller.abort();
248
- });
249
- };
250
- const handlerOutcome = await require_lib_errors_index.mightThrow(Promise.resolve(this.options.handler({
251
- input,
252
- executionId,
253
- signal: controller.signal,
254
- step
255
- })));
256
- clearInterval(renewTimer);
257
- if (lockLost) {
258
- await this.releaseLock(executionId, token);
259
- throw new LockError(`Lock expired during execution ${executionId}`);
260
- }
261
- if (await this.isCancelled(executionId)) {
262
- const cancelledAt = now();
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
- }));
270
- await this.releaseLock(executionId, token);
271
- return {
272
- executionId,
273
- status: "cancelled"
274
- };
275
- }
276
- const [handlerError, result] = handlerOutcome;
277
- if (handlerError) {
278
- const failedAt = now();
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));
283
- await this.releaseLock(executionId, token);
284
- throw new ExecutionError(`Workflow execution ${executionId} failed: ${handlerError.message}`);
285
- }
286
- const [serializeResultError, serializedResult] = require_lib_errors_index.mightThrowSync(() => this.serializeResult(result));
287
- if (serializeResultError) {
288
- const failedAt = now();
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));
293
- await this.releaseLock(executionId, token);
294
- throw new SerializationError(`Unable to serialize workflow result for ${executionId}`);
295
- }
296
- const completedAt = now();
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
- }));
308
- await this.releaseLock(executionId, token);
309
- return {
310
- executionId,
311
- status: "completed"
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
- }
362
- }
363
- async acquireLock(executionId, token) {
364
- const [lockError, lockResult] = await require_lib_errors_index.mightThrow(this.options.redis.set(this.lockKey(executionId), token, "PX", String(this.lockTTL), "NX"));
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`);
367
- }
368
- async releaseLock(executionId, token) {
369
- await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
370
- "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end",
371
- "1",
372
- this.lockKey(executionId),
373
- token
374
- ]));
375
- }
376
- async extendLock(executionId, token) {
377
- const [evalError, extendResult] = await require_lib_errors_index.mightThrow(this.options.redis.send("EVAL", [
378
- "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end",
379
- "1",
380
- this.lockKey(executionId),
381
- token,
382
- String(this.lockTTL)
383
- ]));
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}`);
386
- }
387
- async isCancelled(executionId) {
388
- return await this.getMeta(executionId, "status") === "cancelled";
389
- }
390
- async setMeta(executionId, field, value) {
391
- const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.metaKey(executionId), field, value));
392
- if (writeError) throw new WorkflowError(`Unable to persist ${field} for execution ${executionId}`);
393
- }
394
- async getMeta(executionId, field) {
395
- const [readError, value] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.metaKey(executionId), field));
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;
401
- }
402
- async readNumberMeta(executionId, field) {
403
- const value = await this.getMeta(executionId, field);
404
- if (!value) return null;
405
- const parsed = Number(value);
406
- if (!Number.isFinite(parsed)) throw new StateError(`Invalid ${field} value for execution ${executionId}`);
407
- return parsed;
408
- }
409
- runSerializer(value, serializer, label) {
410
- const [serializeError, serialized] = require_lib_errors_index.mightThrowSync(() => serializer(value));
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;
414
- }
415
- runDeserializer(raw, deserializer, label) {
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];
419
- }
420
- serializeInput(input) {
421
- return this.runSerializer(input, this.options.serializeInput ?? envelopeSerialize, "workflow input");
422
- }
423
- deserializeInput(raw) {
424
- const deserializer = this.options.deserializeInput ?? ((value) => envelopeDeserialize(value));
425
- return this.runDeserializer(raw, deserializer, "workflow input");
426
- }
427
- serializeResult(result) {
428
- if (result === null) return envelopeSerialize(null);
429
- return this.runSerializer(result, this.options.serializeResult ?? envelopeSerialize, "workflow result");
430
- }
431
- deserializeResult(raw) {
432
- const deserializer = this.options.deserializeResult ?? ((value) => envelopeDeserialize(value));
433
- return this.runDeserializer(raw, deserializer, "workflow result");
434
- }
435
- serializeStepOutput(output) {
436
- return this.runSerializer(output, this.options.serializeStepOutput ?? envelopeSerialize, "step output");
437
- }
438
- deserializeStepOutput(raw) {
439
- const deserializer = this.options.deserializeStepOutput ?? ((value) => envelopeDeserialize(value));
440
- return this.runDeserializer(raw, deserializer, "step output");
441
- }
442
- async readInput(executionId) {
443
- const raw = await this.getMeta(executionId, "input");
444
- if (!raw) throw new StateError(`Workflow execution ${executionId} input not found`);
445
- return this.deserializeInput(raw);
446
- }
447
- async readResult(executionId) {
448
- const raw = await this.getMeta(executionId, "result");
449
- if (!raw) return null;
450
- return this.deserializeResult(raw);
451
- }
452
- async writeStepOutput(executionId, stepName, output) {
453
- const payload = {
454
- output: this.serializeStepOutput(output),
455
- completedAt: now()
456
- };
457
- const [payloadError, payloadRaw] = require_lib_errors_index.mightThrowSync(() => JSON.stringify(payload));
458
- if (payloadError || typeof payloadRaw !== "string") throw new SerializationError(`Unable to persist step ${stepName} output`);
459
- const [writeError] = await require_lib_errors_index.mightThrow(this.options.redis.hset(this.stepsKey(executionId), stepName, payloadRaw));
460
- if (writeError) throw new WorkflowError(`Unable to persist step ${stepName} for execution ${executionId}`);
461
- const stepNames = await this.readStepNames(executionId);
462
- if (!stepNames.includes(stepName)) {
463
- const nextStepNames = [...stepNames, stepName];
464
- const [serializeStepsError, serializedSteps] = require_lib_errors_index.mightThrowSync(() => JSON.stringify(nextStepNames));
465
- if (serializeStepsError || typeof serializedSteps !== "string") throw new SerializationError(`Unable to persist step history for execution ${executionId}`);
466
- await this.setMeta(executionId, "steps", serializedSteps);
467
- }
468
- await this.setMeta(executionId, "updatedAt", String(now()));
469
- }
470
- async readStepOutput(executionId, stepName) {
471
- const [readError, payloadRaw] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.stepsKey(executionId), stepName));
472
- if (readError) throw new WorkflowError(`Unable to read step ${stepName} for execution ${executionId}`);
473
- if (!payloadRaw) return {
474
- found: false,
475
- value: null
476
- };
477
- if (typeof payloadRaw !== "string") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
478
- const [parseError, parsed] = require_lib_errors_index.mightThrowSync(() => JSON.parse(payloadRaw));
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}`);
481
- const outputRaw = parsed.output;
482
- return {
483
- found: true,
484
- value: this.deserializeStepOutput(outputRaw)
485
- };
486
- }
487
- async readStepNames(executionId) {
488
- const stepsRaw = await this.getMeta(executionId, "steps");
489
- if (!stepsRaw) return [];
490
- const [parseError, values] = require_lib_errors_index.mightThrowSync(() => JSON.parse(stepsRaw));
491
- if (parseError || !Array.isArray(values)) throw new StateError(`Invalid step index for execution ${executionId}`);
492
- const stepNames = [];
493
- for (const value of values) if (typeof value === "string") stepNames.push(value);
494
- return stepNames;
495
- }
496
- async readStepSnapshots(executionId) {
497
- const stepNames = await this.readStepNames(executionId);
498
- const steps = [];
499
- for (const stepName of stepNames) {
500
- const [readError, payloadRaw] = await require_lib_errors_index.mightThrow(this.options.redis.hget(this.stepsKey(executionId), stepName));
501
- if (readError) throw new WorkflowError(`Unable to read step ${stepName} for execution ${executionId}`);
502
- if (!payloadRaw) continue;
503
- if (typeof payloadRaw !== "string") throw new StateError(`Invalid step payload for ${stepName} in execution ${executionId}`);
504
- const [parseError, parsed] = require_lib_errors_index.mightThrowSync(() => JSON.parse(payloadRaw));
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}`);
507
- steps.push({
508
- name: stepName,
509
- completedAt: parsed.completedAt
510
- });
511
- }
512
- return steps;
513
- }
514
- normalizeStatus(value) {
515
- for (const status of knownStatuses) if (status === value) return status;
516
- return null;
517
- }
518
- };
519
- const defineWorkflow = (options) => {
520
- const workflow = new WorkflowDefinition(options);
521
- return {
522
- start: (input, startOptions) => workflow.start(input, startOptions),
523
- run: (input, startOptions) => workflow.run(input, startOptions),
524
- resume: (executionId) => workflow.resume(executionId),
525
- get: (executionId) => workflow.get(executionId),
526
- cancel: (executionId) => workflow.cancel(executionId)
527
- };
528
- };
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;
537
- exports.defineWorkflow = defineWorkflow;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../rolldown-runtime-CMqjfN_6.cjs"),t=require("../errors/index.cjs");let n=require("node:assert");n=e.t(n,1);var r=class extends Error{constructor(e){super(e),this.name=`WorkflowError`}},i=class extends Error{constructor(e){super(e),this.name=`NotFoundError`}},a=class extends Error{constructor(e){super(e),this.name=`StateError`}},o=class extends Error{constructor(e){super(e),this.name=`SerializationError`}},s=class extends Error{constructor(e){super(e),this.name=`ExecutionError`}},c=class extends Error{constructor(e){super(e),this.name=`LockError`}},l=class extends Error{constructor(e){super(e),this.name=`CancelledError`}};const u=()=>Date.now(),d=async(e,t,n)=>{if(t.aborted)throw new l(`Workflow execution was aborted during retry backoff`);let r=u()+e;for(;u()<r;){if(t.aborted)throw new l(`Workflow execution was aborted during retry backoff`);if(n&&await n())throw new l(`Workflow execution was cancelled during retry backoff`);let e=r-u();await new Promise(t=>{setTimeout(t,Math.min(50,e))})}},f=e=>JSON.stringify({value:e}),p=e=>{let[n,r]=t.mightThrowSync(()=>JSON.parse(e));if(n)throw n;if(typeof r==`object`&&r&&`value`in r)return r.value},m=[`pending`,`running`,`completed`,`failed`,`cancelled`];var h=class{options;lockTTL;retries;retryBaseDelay;retryMultiplier;retryMaxDelay;constructor(e){this.options=e,this.lockTTL=e.lockTTL??3e5,this.retries=e.retries??3,this.retryBaseDelay=e.retryBackoff?.baseDelay??1e3,this.retryMultiplier=e.retryBackoff?.multiplier??2,this.retryMaxDelay=e.retryBackoff?.maxDelay??3e4,n.default.ok(Number.isFinite(this.retries)&&this.retries>=0,`Invalid retries: must be a non-negative finite number`),n.default.ok(Number.isFinite(this.retryBaseDelay)&&this.retryBaseDelay>0,`Invalid retryBackoff.baseDelay: must be a positive finite number`),n.default.ok(Number.isFinite(this.retryMultiplier)&&this.retryMultiplier>0,`Invalid retryBackoff.multiplier: must be a positive finite number`),n.default.ok(Number.isFinite(this.retryMaxDelay)&&this.retryMaxDelay>0,`Invalid retryBackoff.maxDelay: must be a positive finite number`)}runHook(e){return t.mightThrow(Promise.resolve().then(()=>e()))}computeBackoffDelay(e){return Math.min(this.retryBaseDelay*this.retryMultiplier**(e-1),this.retryMaxDelay)}async start(e,t){let n=t?.executionId??crypto.randomUUID();return await this.createExecution(n,e),this.scheduleExecution(n,e),{executionId:n,status:`pending`}}async resume(e){let t=await this.get(e);return t.status===`completed`||t.status===`cancelled`?{executionId:e,status:t.status}:(this.scheduleExecution(e,t.input),{executionId:e,status:`pending`})}async get(e){let t=await this.getMeta(e,`status`);if(!t)throw new i(`Workflow execution ${e} not found`);let n=this.normalizeStatus(t);if(!n)throw new a(`Workflow execution ${e} has invalid status ${t}`);let r=await this.readInput(e),o=await this.readResult(e),s=await this.readStepSnapshots(e),c=await this.readNumberMeta(e,`createdAt`),l=await this.readNumberMeta(e,`updatedAt`),u=await this.getMeta(e,`error`),d=await this.readNumberMeta(e,`completedAt`),f=await this.readNumberMeta(e,`failedAt`),p=await this.readNumberMeta(e,`cancelledAt`);if(c===null)throw new a(`Workflow execution ${e} is missing createdAt`);if(l===null)throw new a(`Workflow execution ${e} is missing updatedAt`);return{id:e,name:this.options.name,status:n,input:r,result:o,error:u,createdAt:c,updatedAt:l,completedAt:d,failedAt:f,cancelledAt:p,steps:s}}async cancel(e){let t=await this.get(e);if(t.status===`completed`)throw new a(`Workflow execution ${e} is already completed`);let n=u();return await this.setMeta(e,`status`,`cancelled`),await this.setMeta(e,`updatedAt`,String(n)),await this.setMeta(e,`cancelledAt`,String(n)),await this.setMeta(e,`error`,``),await this.setMeta(e,`failedAt`,``),{executionId:e,createdAt:t.createdAt,cancelledAt:n,updatedAt:n,status:`cancelled`}}executionKey(e){return`workflow:${this.options.name}:execution:${e}`}metaKey(e){return`${this.executionKey(e)}:meta`}stepsKey(e){return`${this.executionKey(e)}:steps`}lockKey(e){return`${this.executionKey(e)}:lock`}async createExecution(e,n){let i=this.serializeInput(n),o=u();if(await this.getMeta(e,`status`))throw new a(`Workflow execution ${e} already exists`);let s={status:`pending`,input:i,result:``,error:``,createdAt:String(o),updatedAt:String(o),completedAt:``,failedAt:``,cancelledAt:``,steps:`[]`},[c]=await t.mightThrow(this.options.redis.hset(this.metaKey(e),s));if(c)throw new r(`Unable to persist metadata for execution ${e}`)}scheduleExecution(e,t){queueMicrotask(()=>{this.executeInBackground(e,t)})}async executeInBackground(e,n){let[r]=await t.mightThrow(this.execute(e,n));if(!r)return;let[i]=await t.mightThrow(this.recordBackgroundFailure(e,r));i&&console.error(`Unable to record background workflow failure`,{executionId:e,error:i})}async recordBackgroundFailure(e,t){if(await this.getMeta(e,`status`)!==`pending`)return;let n=u();await this.setMeta(e,`status`,`failed`),await this.setMeta(e,`error`,t.message),await this.setMeta(e,`updatedAt`,String(n)),await this.setMeta(e,`failedAt`,String(n))}async execute(e,n){let r=crypto.randomUUID();if(await this.acquireLock(e,r),await this.getMeta(e,`status`)===`cancelled`)throw await this.releaseLock(e,r),new a(`Workflow execution ${e} was cancelled`);let i=u();await this.setMeta(e,`status`,`running`),await this.setMeta(e,`updatedAt`,String(i));let l=new AbortController,d=Math.floor(this.lockTTL/3),f=!1,p=setInterval(async()=>{let[n]=await t.mightThrow(this.extendLock(e,r));n&&(f=!0,l.abort(),clearInterval(p))},d);this.options.hooks?.onStart&&await this.runHook(()=>this.options.hooks?.onStart?.({executionId:e,input:n}));let m=await t.mightThrow(Promise.resolve(this.options.handler({input:n,executionId:e,signal:l.signal,step:async(t,r)=>{await this.throwIfCancelled(e,()=>{l.abort()});let i=await this.readStepOutput(e,t);return i.found?i.value:this.runStepWithRetries(e,n,t,r,l.signal,()=>{l.abort()})}})));if(clearInterval(p),f)throw await this.releaseLock(e,r),new c(`Lock expired during execution ${e}`);if(await this.isCancelled(e)){let t=u();return await this.setMeta(e,`status`,`cancelled`),await this.setMeta(e,`updatedAt`,String(t)),await this.setMeta(e,`cancelledAt`,String(t)),this.options.hooks?.onCancel&&await this.runHook(()=>this.options.hooks?.onCancel?.({executionId:e,input:n})),await this.releaseLock(e,r),{executionId:e,status:`cancelled`}}let[h,g]=m;if(h){let t=u();throw await this.setMeta(e,`status`,`failed`),await this.setMeta(e,`error`,h.message),await this.setMeta(e,`updatedAt`,String(t)),await this.setMeta(e,`failedAt`,String(t)),await this.releaseLock(e,r),new s(`Workflow execution ${e} failed: ${h.message}`)}let[_,v]=t.mightThrowSync(()=>this.serializeResult(g));if(_){let t=u();throw await this.setMeta(e,`status`,`failed`),await this.setMeta(e,`error`,_.message),await this.setMeta(e,`updatedAt`,String(t)),await this.setMeta(e,`failedAt`,String(t)),await this.releaseLock(e,r),new o(`Unable to serialize workflow result for ${e}`)}let y=u();return await this.setMeta(e,`result`,v),await this.setMeta(e,`status`,`completed`),await this.setMeta(e,`error`,``),await this.setMeta(e,`failedAt`,``),await this.setMeta(e,`updatedAt`,String(y)),await this.setMeta(e,`completedAt`,String(y)),this.options.hooks?.onComplete&&await this.runHook(()=>this.options.hooks?.onComplete?.({executionId:e,input:n,result:g})),await this.releaseLock(e,r),{executionId:e,status:`completed`}}async throwIfCancelled(e,t){if(await this.isCancelled(e))throw t(),new l(`Workflow execution ${e} was cancelled`)}async runStepWithRetries(e,n,r,i,a,o){let s=1,c=[];for(;;){await this.throwIfCancelled(e,o);let[l,f]=await t.mightThrow(Promise.resolve(i(n,a)));if(!l)return await this.writeStepOutput(e,r,f),f;let p=l.message;if(c.push({attempt:s,error:p,timestamp:u()}),s<=this.retries){let i=this.computeBackoffDelay(s);this.options.hooks?.onRetry&&await this.runHook(()=>this.options.hooks?.onRetry?.({executionId:e,input:n,stepName:r,error:p,attempt:s,nextRetryDelayMs:i,retriesRemaining:this.retries-s}));let[o]=await t.mightThrow(d(i,a,()=>this.isCancelled(e)));if(o)throw o;s++;continue}throw this.options.hooks?.onError&&await this.runHook(()=>this.options.hooks?.onError?.({executionId:e,input:n,stepName:r,error:p,totalAttempts:s,errorHistory:c})),l}}async acquireLock(e,n){let[r,i]=await t.mightThrow(this.options.redis.set(this.lockKey(e),n,`PX`,String(this.lockTTL),`NX`));if(r)throw new c(`Unable to acquire lock for execution ${e}`);if(i!==`OK`)throw new c(`Workflow execution ${e} is already running`)}async releaseLock(e,n){await t.mightThrow(this.options.redis.send(`EVAL`,[`if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end`,`1`,this.lockKey(e),n]))}async extendLock(e,n){let[r,i]=await t.mightThrow(this.options.redis.send(`EVAL`,[`if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end`,`1`,this.lockKey(e),n,String(this.lockTTL)]));if(r)throw new c(`Unable to extend lock for execution ${e}`);if(i===0)throw new c(`Lock ownership lost for execution ${e}`)}async isCancelled(e){return await this.getMeta(e,`status`)===`cancelled`}async setMeta(e,n,i){let[a]=await t.mightThrow(this.options.redis.hset(this.metaKey(e),n,i));if(a)throw new r(`Unable to persist ${n} for execution ${e}`)}async getMeta(e,n){let[i,o]=await t.mightThrow(this.options.redis.hget(this.metaKey(e),n));if(i)throw new r(`Unable to read ${n} for execution ${e}`);if(o==null)return null;if(typeof o!=`string`)throw new a(`Invalid ${n} value for execution ${e}`);return o.length===0?null:o}async readNumberMeta(e,t){let n=await this.getMeta(e,t);if(!n)return null;let r=Number(n);if(!Number.isFinite(r))throw new a(`Invalid ${t} value for execution ${e}`);return r}runSerializer(e,n,r){let[i,a]=t.mightThrowSync(()=>n(e));if(i)throw new o(`Unable to serialize ${r}: ${i.message}`);if(typeof a!=`string`)throw new o(`${r} serializer must return a string`);return a}runDeserializer(e,n,r){let i=t.mightThrowSync(()=>n(e));if(i[0])throw new o(`Unable to deserialize ${r}: ${i[0].message}`);return i[1]}serializeInput(e){return this.runSerializer(e,this.options.serializeInput??f,`workflow input`)}deserializeInput(e){let t=this.options.deserializeInput??(e=>p(e));return this.runDeserializer(e,t,`workflow input`)}serializeResult(e){return e===null?f(null):this.runSerializer(e,this.options.serializeResult??f,`workflow result`)}deserializeResult(e){let t=this.options.deserializeResult??(e=>p(e));return this.runDeserializer(e,t,`workflow result`)}serializeStepOutput(e){return this.runSerializer(e,this.options.serializeStepOutput??f,`step output`)}deserializeStepOutput(e){let t=this.options.deserializeStepOutput??(e=>p(e));return this.runDeserializer(e,t,`step output`)}async readInput(e){let t=await this.getMeta(e,`input`);if(!t)throw new a(`Workflow execution ${e} input not found`);return this.deserializeInput(t)}async readResult(e){let t=await this.getMeta(e,`result`);return t?this.deserializeResult(t):null}async writeStepOutput(e,n,i){let a={output:this.serializeStepOutput(i),completedAt:u()},[s,c]=t.mightThrowSync(()=>JSON.stringify(a));if(s||typeof c!=`string`)throw new o(`Unable to persist step ${n} output`);let[l]=await t.mightThrow(this.options.redis.hset(this.stepsKey(e),n,c));if(l)throw new r(`Unable to persist step ${n} for execution ${e}`);let d=await this.readStepNames(e);if(!d.includes(n)){let r=[...d,n],[i,a]=t.mightThrowSync(()=>JSON.stringify(r));if(i||typeof a!=`string`)throw new o(`Unable to persist step history for execution ${e}`);await this.setMeta(e,`steps`,a)}await this.setMeta(e,`updatedAt`,String(u()))}async readStepOutput(e,n){let[i,o]=await t.mightThrow(this.options.redis.hget(this.stepsKey(e),n));if(i)throw new r(`Unable to read step ${n} for execution ${e}`);if(!o)return{found:!1,value:null};if(typeof o!=`string`)throw new a(`Invalid step payload for ${n} in execution ${e}`);let[s,c]=t.mightThrowSync(()=>JSON.parse(o));if(s||typeof c!=`object`||!c)throw new a(`Invalid step payload for ${n} in execution ${e}`);if(typeof c.output!=`string`)throw new a(`Invalid step output for ${n} in execution ${e}`);let l=c.output;return{found:!0,value:this.deserializeStepOutput(l)}}async readStepNames(e){let n=await this.getMeta(e,`steps`);if(!n)return[];let[r,i]=t.mightThrowSync(()=>JSON.parse(n));if(r||!Array.isArray(i))throw new a(`Invalid step index for execution ${e}`);let o=[];for(let e of i)typeof e==`string`&&o.push(e);return o}async readStepSnapshots(e){let n=await this.readStepNames(e),i=[];for(let o of n){let[n,s]=await t.mightThrow(this.options.redis.hget(this.stepsKey(e),o));if(n)throw new r(`Unable to read step ${o} for execution ${e}`);if(!s)continue;if(typeof s!=`string`)throw new a(`Invalid step payload for ${o} in execution ${e}`);let[c,l]=t.mightThrowSync(()=>JSON.parse(s));if(c||typeof l!=`object`||!l||typeof l.completedAt!=`number`)throw new a(`Invalid step payload for ${o} in execution ${e}`);i.push({name:o,completedAt:l.completedAt})}return i}normalizeStatus(e){for(let t of m)if(t===e)return t;return null}};const g=e=>{let t=new h(e);return{start:(e,n)=>t.start(e,n),resume:e=>t.resume(e),get:e=>t.get(e),cancel:e=>t.cancel(e)}};exports.CancelledError=l,exports.ExecutionError=s,exports.LockError=c,exports.NotFoundError=i,exports.SerializationError=o,exports.StateError=a,exports.WorkflowError=r,exports.defineWorkflow=g;