@usehelical/workflows 0.0.1-alpha.15 → 0.0.1-alpha.16

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 CHANGED
@@ -10,114 +10,3 @@ Simple, typesafe durable workflows without bundler magic
10
10
  - simple & typesafe api
11
11
  - minimal deployment complexity (just postgres)
12
12
  - low latency (making it suitable for user-facing ai workflows)
13
-
14
- # Getting started
15
-
16
- ## Writing workflows
17
-
18
- ```ts
19
- import * as wf from 'helical-workflows/workflow';
20
-
21
- async function indexDocument(url: string) {
22
- // idempotent retryable durable steps
23
- const document = await wf.runStep(async () => fetchDocument(url), { maxRetries: 5 });
24
- const parsedDocument = await wf.runStep(async () => parseDocument(document));
25
- await wf.runStep(async () => upsertIntoDb(parsedDocument));
26
- }
27
-
28
- const indexDocumentWorkflow = defineWorkflow(indexDocument);
29
-
30
- const indexingQueue = defineQueue();
31
-
32
- async function processDocumentBatch(urls: string[]) {
33
- const handles = [];
34
-
35
- // enqueue all documents for indexing
36
- for (const url of urls) {
37
- const runHandle = await wf.enqueueWorkflow(indexingQueue, indexDocument, [url]);
38
- handles.push(runHandle);
39
- }
40
-
41
- let successfulCount = 0;
42
- let failedCount = 0;
43
-
44
- // wait for all documents to be indexed
45
- for (const handle of handles) {
46
- try {
47
- await handle.result();
48
- successfulCount++;
49
- } catch (error) {
50
- failedCount++;
51
- }
52
- }
53
-
54
- return { successfulCount, failedCount };
55
- }
56
-
57
- const processDocumentBatchWorkflow = defineWorkflow(processDocumentBatch);
58
- ```
59
-
60
- ## Running a workflow
61
-
62
- ```ts
63
- // create instance and register workflows and queues
64
- const { runWorkflow } = createInstance({
65
- workflows: { indexDocumentWorkflow, processDocumentBatchWorkflow },
66
- queues: { indexingQueue },
67
- options: { connectionString: 'dummy' },
68
- });
69
-
70
- const urls = ['a', 'b', 'c'];
71
-
72
- const run = await runWorkflow(processDocumentBatchWorkflow, urls);
73
-
74
- console.log(await run.result());
75
- ```
76
-
77
- # Development roadmap
78
-
79
- ## Messages
80
-
81
- Messages are a way of communicating with a workflow from the outside. This can be useful for human-in-the-loop workflows or for other signals. Messages are persisted in the database and atomically consumed adhering to once-and-only-once semantics.
82
-
83
- ```ts
84
- const approvalMessage = defineMessage<ApprovalMessage>('approval');
85
-
86
- async function myWorkflowFunction() {
87
- await wf.runStep(() => stageForApproval());
88
- await wf.receiveMessage(approvalMessage);
89
- await wf.runStep(() => publish());
90
- }
91
- ```
92
-
93
- ## State
94
-
95
- Workflow state is a way for workflows to expose state to the outside. The benefit of this state approach in comparison to other request handler approaches like in Temporal is that the workflow doesn’t have to be executing in order for the state to be retrievable. This makes it possible to retrieve state even if a workflow is already cancelled or finished.
96
-
97
- ```ts
98
- async function myWorkflowFunction() {
99
- await wf.setState({ progress: 50 });
100
- }
101
- ```
102
-
103
- ## Schedules
104
-
105
- Allow a workflow to be scheduled…
106
-
107
- ## Streams
108
-
109
- Streams allow for publishing streaming data from workflows to the outside the primary use case for this is realtime streaming of LLM responses. I want to take a different approach to other workflow systems here and treat streams as side effects inside of workflows and not as durable outputs. Since LLM streaming responses can be quite quick persisting the token stream into the database is costly. My hypothesis is that stream persistence is not needed since resuming LLM streaming responses require [special handling](<[https://platform.claude.com/docs/en/build-with-claude/streaming#error-recovery](https://platform.claude.com/docs/en/build-with-claude/streaming#error-recovery)>) and in most cases it makes more sense to just regenerate the response. Usually non-idempotent side effects in workflows are prohibited to achieve durability guarantees. But in this case the correctness of the workflow doesn’t depend on the stream the stream is just a user experience optimisation.
110
-
111
- ## Workflow suspension
112
-
113
- When implementing very long running workflows (days, months…) the workflow would still be the whole time in memory idling waiting for instance for a message or for a sleep() event. Workflow suspension would allow for workflows to be suspended when waiting and then replayed and continued when the event arrives. This happens at the cost of latency (since the workflow has to be replayed to reach its original state again after has been suspended) which is why I want to make this an optional feature.
114
-
115
- ## Workflow version management
116
-
117
- A tricky topic. In production systems it is possible that workflows with old versions are still Pending or are Queued. I don’t know yet what is the best way to handle this but I’m leaning towards workflow versions that can be manually specified using a versionId or could be auto generated by creating a hash from the workflow code.
118
-
119
- ## Chaos tests
120
-
121
- ## OTEL Integration
122
-
123
- ## CLI & Observability UI
package/dist/api.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { S as StateDefinition, M as MessageDefinition } from './state-DygxhGtl.js';
2
+ export { Q as QueueDefinition, b as QueueOptions, c as QueueRateLimit, d as QueueSignature, R as RunStatus, T as TERMINAL_STATES, W as WorkflowDefinition, e as WorkflowFunction, a as WorkflowSignature, f as defineMessage, g as defineQueue, h as defineState, i as defineWorkflow } from './state-DygxhGtl.js';
3
+
4
+ type RetryConfig = {
5
+ maxRetries?: number;
6
+ retryDelay?: number;
7
+ backOffRate?: number;
8
+ };
9
+ type RunStepOptions = RetryConfig & {
10
+ name?: string;
11
+ };
12
+ declare function runStep<TReturn>(stepFn: () => Promise<TReturn>, options?: RunStepOptions): Promise<TReturn | (TReturn extends void ? void : TReturn)>;
13
+ declare function executeStepWithRetries<TReturn>(stepName: string, fn: () => Promise<TReturn>, retryConfig: RetryConfig): Promise<TReturn>;
14
+
15
+ declare function setState<T = unknown>(state: StateDefinition<T> | string, value: T): Promise<void>;
16
+
17
+ type ReceiveMessageOptions = {
18
+ timeout?: number;
19
+ };
20
+ declare function receiveMessage<T>(message: MessageDefinition<T> | string, options?: ReceiveMessageOptions): Promise<T>;
21
+
22
+ /**
23
+ * Generates a stable, unique step ID based on the current run ID and sequence ID.
24
+ * This ID is deterministic and will remain the same across retries, making it
25
+ * suitable for use as an idempotency key with third-party systems.
26
+ *
27
+ * The step ID is a SHA-256 hash of the run ID and sequence ID, formatted as a
28
+ * hex string for easy use with external APIs.
29
+ *
30
+ * @returns A unique, stable identifier for the current step execution
31
+ */
32
+ declare function getStepId(): string;
33
+
34
+ declare function getAbortSignal(): AbortSignal;
35
+
36
+ declare function getRunId(): string;
37
+
38
+ declare function sleep(ms: number): Promise<void>;
39
+ declare function cancellableSleep(ms: number, signal?: AbortSignal): Promise<void>;
40
+
41
+ declare function randomUUID(): Promise<string>;
42
+
43
+ declare function now(): Promise<number>;
44
+
45
+ export { MessageDefinition, type ReceiveMessageOptions, type RetryConfig, StateDefinition, cancellableSleep, executeStepWithRetries, getAbortSignal, getRunId, getStepId, now, randomUUID, receiveMessage, runStep, setState, sleep };
@@ -1,26 +1,26 @@
1
- import { getExecutionContext, returnOrThrowOperationResult, executeAndRecordOperation, FatalError, MaxRetriesExceededError, sleep, ErrorThatShouldNeverHappen, withDbRetry, serialize, WorkflowNotFoundError, deserializeError, deserialize, createRunHandle, insertPendingRun, executeWorkflow, insertMessage, QueueNotFoundError, enqueueRun, getState, StateNotAvailableError, waitForStateNotification, serializeError, OperationTimedOutError, RunCancelledError } from './chunk-TZSPUZQ3.js';
2
- export { MaxRecoveryAttemptsExceededError, QueueNotFoundError, TERMINAL_STATES, TimeoutError, WorkflowNotFoundError, defineWorkflow } from './chunk-TZSPUZQ3.js';
1
+ import { getExecutionContext, returnOrThrowOperationResult, executeAndRecordOperation, FatalError, MaxRetriesExceededError, sleep, withDbRetry, serialize, OperationTimedOutError, serializeError, deserialize, RunCancelledError } from './chunk-HRDI7EOY.js';
2
+ export { TERMINAL_STATES, defineWorkflow } from './chunk-HRDI7EOY.js';
3
3
  import { sql } from 'kysely';
4
- import crypto2, { createHash } from 'crypto';
4
+ import crypto, { createHash } from 'crypto';
5
5
 
6
- // core/message.ts
6
+ // src/api/message.ts
7
7
  function defineMessage(name) {
8
8
  return { name };
9
9
  }
10
10
 
11
- // core/queue.ts
12
- function defineQueue(options = {}) {
13
- return () => options;
11
+ // src/api/queue.ts
12
+ function defineQueue(name, options = {}) {
13
+ return { name, ...options };
14
14
  }
15
15
 
16
- // core/state.ts
16
+ // src/api/state.ts
17
17
  function defineState(name) {
18
18
  return {
19
19
  name
20
20
  };
21
21
  }
22
22
 
23
- // core/steps/run-step.ts
23
+ // src/api/steps/run-step.ts
24
24
  async function runStep(stepFn, options = {}) {
25
25
  const { maxRetries, retryDelay, backOffRate } = options;
26
26
  const { operationManager } = getExecutionContext();
@@ -33,6 +33,11 @@ async function runStep(stepFn, options = {}) {
33
33
  return await executeStepWithRetries(stepName, stepFn, { maxRetries, retryDelay, backOffRate });
34
34
  });
35
35
  }
36
+ var ErrorThatShouldNeverHappen = class extends Error {
37
+ constructor() {
38
+ super("This error should never happen");
39
+ }
40
+ };
36
41
  async function executeStepWithRetries(stepName, fn, retryConfig) {
37
42
  const maxRetries = retryConfig.maxRetries ?? 0;
38
43
  const retryDelay = retryConfig.retryDelay ?? 0;
@@ -57,10 +62,10 @@ async function executeStepWithRetries(stepName, fn, retryConfig) {
57
62
  await sleep(delay);
58
63
  }
59
64
  }
60
- throw new ErrorThatShouldNeverHappen(`Step "${stepName}" should never be reached`);
65
+ throw new ErrorThatShouldNeverHappen();
61
66
  }
62
67
 
63
- // core/internal/repository/insert-state.ts
68
+ // src/internal/db/commands/insert-state.ts
64
69
  async function insertState(tx, options) {
65
70
  await tx.insertInto("state").values({
66
71
  run_id: options.runId,
@@ -79,7 +84,7 @@ async function insertState(tx, options) {
79
84
  }).execute();
80
85
  }
81
86
 
82
- // core/steps/set-state.ts
87
+ // src/api/steps/set-state.ts
83
88
  var SET_STATE_OPERATION_NAME = "workflow::state::set";
84
89
  async function setState(state, value) {
85
90
  const stateKey = typeof state === "string" ? state : state.name;
@@ -116,7 +121,7 @@ async function readAndDeleteMessage(tx, runId, messageType) {
116
121
  } : void 0;
117
122
  }
118
123
 
119
- // core/internal/with-durable-deadline.ts
124
+ // src/internal/with-durable-deadline.ts
120
125
  async function withDurableDeadline(timeoutMs, operationName, fn) {
121
126
  if (!timeoutMs) {
122
127
  return await fn(void 0);
@@ -135,7 +140,7 @@ async function withDurableDeadline(timeoutMs, operationName, fn) {
135
140
  return await fn(deadlineMs);
136
141
  }
137
142
 
138
- // core/steps/receive-message.ts
143
+ // src/api/steps/receive-message.ts
139
144
  var RECEIVE_MESSAGE_OPERATION_NAME = "receiveMessage";
140
145
  var RECEIVE_MESSAGE_DURABLE_DEADLINE_OPERATION_NAME = "receiveMessageDurableDeadline";
141
146
  var MessageNotAvailableError = class extends Error {
@@ -229,18 +234,18 @@ function getStepId() {
229
234
  return hash.digest("hex");
230
235
  }
231
236
 
232
- // core/steps/helpers/get-abort-signal.ts
237
+ // src/api/steps/helpers/get-abort-signal.ts
233
238
  function getAbortSignal() {
234
239
  return getExecutionContext().abortSignal;
235
240
  }
236
241
 
237
- // core/steps/helpers/get-run-id.ts
242
+ // src/api/steps/helpers/get-run-id.ts
238
243
  function getRunId() {
239
244
  const { runId } = getExecutionContext();
240
245
  return runId;
241
246
  }
242
247
 
243
- // core/steps/sleep.ts
248
+ // src/api/steps/sleep.ts
244
249
  var SLEEP_OPERATION_NAME = "_helical::sleep";
245
250
  async function sleep2(ms) {
246
251
  const { abortSignal } = getExecutionContext();
@@ -281,111 +286,12 @@ async function randomUUID() {
281
286
  return existingResult.result;
282
287
  } else {
283
288
  return await executeAndRecordOperation(operationManager, "randomUUID", async () => {
284
- return crypto2.randomUUID();
285
- });
286
- }
287
- }
288
- async function runWorkflow(wf, args = [], options = {}) {
289
- const ctx = getExecutionContext();
290
- const { operationManager, runPath, workflowRegistry, db, executorId } = ctx;
291
- const workflow = typeof wf === "string" ? workflowRegistry.getByName(wf) : workflowRegistry.getByWorkflowDefinition(wf);
292
- if (!workflow) {
293
- throw new WorkflowNotFoundError("Workflow not found");
294
- }
295
- const op = operationManager.getOperationResult();
296
- if (op) {
297
- if (op.error) {
298
- throw deserializeError(op.error);
299
- }
300
- const newRun2 = deserialize(op.result);
301
- return createRunHandle(ctx, newRun2.runId);
302
- }
303
- const newRun = await executeAndRecordOperation(operationManager, "runWorkflow", async () => {
304
- const newRunId = options.id ?? crypto2.randomUUID();
305
- const newRun2 = {
306
- runId: newRunId,
307
- runPath: [...runPath, newRunId],
308
- workflowName: workflow.name
309
- };
310
- withDbRetry(async () => {
311
- return await insertPendingRun(db, {
312
- ...newRun2,
313
- path: newRun2.runPath,
314
- inputs: serialize(args),
315
- executorId
316
- });
289
+ return crypto.randomUUID();
317
290
  });
318
- return newRun2;
319
- });
320
- await executeWorkflow(ctx, {
321
- runId: newRun.runId,
322
- runPath: newRun.runPath,
323
- workflowName: newRun.workflowName,
324
- fn: workflow.fn,
325
- args,
326
- options
327
- });
328
- return createRunHandle(ctx, newRun.runId);
329
- }
330
-
331
- // core/steps/send-message.ts
332
- var SEND_MESSAGE_OPERATION_NAME = "workflow::message::send";
333
- async function sendMessage(target, name, data) {
334
- const { operationManager, db } = getExecutionContext();
335
- const destinationWorkflowId = typeof target === "string" ? target : target.id;
336
- const messageType = typeof name === "string" ? name : name.name;
337
- const prevOp = operationManager.getOperationResult();
338
- if (prevOp) {
339
- return returnOrThrowOperationResult(prevOp);
340
- }
341
- const seqId = operationManager.reserveSequenceId();
342
- const serializedData = serialize(data);
343
- await withDbRetry(async () => {
344
- return await db.transaction().execute(async (tx) => {
345
- await insertMessage(tx, { destinationWorkflowId, messageType, data: serializedData });
346
- await operationManager.recordResult(SEND_MESSAGE_OPERATION_NAME, seqId, null, tx);
347
- });
348
- });
349
- }
350
-
351
- // core/steps/queue-workflow.ts
352
- async function queueWorkflow(queue, wf, args, options) {
353
- const ctx = getExecutionContext();
354
- const { db, operationManager, queueRegistry, workflowRegistry, runPath, executorId } = ctx;
355
- const workflowName = typeof wf === "string" ? wf : workflowRegistry.getByWorkflowDefinition(wf)?.name;
356
- if (!workflowName) {
357
- throw new WorkflowNotFoundError("Workflow name not specified");
358
- }
359
- const queueName = typeof queue === "string" ? queue : queueRegistry.getByQueueEntry(queue)?.name;
360
- if (!queueName) {
361
- throw new QueueNotFoundError("Queue name not specified");
362
291
  }
363
- const op = operationManager.getOperationResult();
364
- if (op) {
365
- if (op.error) {
366
- throw deserializeError(op.error);
367
- }
368
- const newRunId2 = deserialize(op.result);
369
- return createRunHandle(ctx, newRunId2);
370
- }
371
- const newRunId = await executeAndRecordOperation(operationManager, "queueWorkflow", async () => {
372
- const newRunId2 = options?.id ?? crypto.randomUUID();
373
- const { runId } = await enqueueRun(db, {
374
- runId: newRunId2,
375
- path: [...runPath, newRunId2],
376
- inputs: serialize(args),
377
- executorId,
378
- workflowName,
379
- queueName,
380
- timeout: options?.timeout,
381
- deadline: options?.deadline
382
- });
383
- return runId;
384
- });
385
- return createRunHandle(ctx, newRunId);
386
292
  }
387
293
 
388
- // core/steps/now.ts
294
+ // src/api/steps/now.ts
389
295
  async function now() {
390
296
  const { operationManager } = getExecutionContext();
391
297
  const op = operationManager.getOperationResult();
@@ -398,37 +304,6 @@ async function now() {
398
304
  }
399
305
  }
400
306
 
401
- // core/steps/get-state.ts
402
- async function getState2(target, key) {
403
- const { operationManager, db, stateEventBus } = getExecutionContext();
404
- const stateKey = typeof key === "string" ? key : key.name;
405
- const targetId = typeof target === "string" ? target : target.id;
406
- const op = operationManager.getOperationResult();
407
- if (op) {
408
- return returnOrThrowOperationResult(op);
409
- }
410
- const seqId = operationManager.reserveSequenceId();
411
- while (true) {
412
- try {
413
- return await withDbRetry(async () => {
414
- const state = await getState(db, targetId, stateKey);
415
- if (!state) {
416
- throw new StateNotAvailableError();
417
- }
418
- await operationManager.recordResult("getState", seqId, state);
419
- return deserialize(state);
420
- });
421
- } catch (error) {
422
- if (error instanceof StateNotAvailableError) {
423
- await waitForStateNotification(stateEventBus, targetId, stateKey);
424
- continue;
425
- }
426
- await operationManager.recordError("getState", seqId, serializeError(error));
427
- throw error;
428
- }
429
- }
430
- }
431
-
432
- export { cancellableSleep, defineMessage, defineQueue, defineState, executeStepWithRetries, getAbortSignal, getRunId, getState2 as getState, getStepId, now, queueWorkflow, randomUUID, receiveMessage, runStep, runWorkflow, sendMessage, setState, sleep2 as sleep };
433
- //# sourceMappingURL=workflows.js.map
434
- //# sourceMappingURL=workflows.js.map
307
+ export { cancellableSleep, defineMessage, defineQueue, defineState, executeStepWithRetries, getAbortSignal, getRunId, getStepId, now, randomUUID, receiveMessage, runStep, setState, sleep2 as sleep };
308
+ //# sourceMappingURL=api.js.map
309
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/message.ts","../src/api/queue.ts","../src/api/state.ts","../src/api/steps/run-step.ts","../src/internal/db/commands/insert-state.ts","../src/api/steps/set-state.ts","../src/internal/db/commands/read-and-delete-message.ts","../src/internal/with-durable-deadline.ts","../src/api/steps/receive-message.ts","../src/api/steps/helpers/get-step-id.ts","../src/api/steps/helpers/get-abort-signal.ts","../src/api/steps/helpers/get-run-id.ts","../src/api/steps/sleep.ts","../src/api/steps/random-uuid.ts","../src/api/steps/now.ts"],"names":["sleep"],"mappings":"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","file":"api.js","sourcesContent":["export type MessageDefinition<T> = {\n name: string;\n __data?: T;\n};\n\nexport function defineMessage<T>(name: string): MessageDefinition<T> {\n return { name } as MessageDefinition<T>;\n}\n","export type QueueRateLimit = {\n limitPerPeriod: number;\n period: number;\n};\n\nexport type QueueOptions = {\n workerConcurrency?: number;\n concurrency?: number;\n rateLimit?: QueueRateLimit;\n priorityEnabled?: boolean;\n partitioningEnabled?: boolean;\n};\n\nexport type QueueDefinition = QueueOptions & { name: string };\n\nexport type QueueSignature = {\n name: string;\n};\n\nexport function defineQueue(name: string, options: QueueOptions = {}): QueueDefinition {\n return { name, ...options };\n}\n","export interface StateDefinition<T> {\n name: string;\n data?: T;\n}\n\nexport function defineState<T>(name: string) {\n return {\n name,\n } as StateDefinition<T>;\n}\n","import { FatalError, MaxRetriesExceededError } from '@internal/errors';\nimport { getExecutionContext } from '@internal/context/execution-context';\nimport { sleep } from '@internal/utils/sleep';\nimport {\n executeAndRecordOperation,\n returnOrThrowOperationResult,\n} from '@internal/context/operation-manager';\n\nexport type RetryConfig = {\n maxRetries?: number;\n retryDelay?: number;\n backOffRate?: number;\n};\n\ntype RunStepOptions = RetryConfig & {\n name?: string;\n};\n\nexport async function runStep<TReturn>(\n stepFn: () => Promise<TReturn>,\n options: RunStepOptions = {},\n) {\n const { maxRetries, retryDelay, backOffRate } = options;\n const { operationManager } = getExecutionContext();\n const stepName = options.name || stepFn.name || '<unknown>';\n\n const op = operationManager.getOperationResult();\n if (op) {\n return returnOrThrowOperationResult<TReturn>(op);\n }\n return await executeAndRecordOperation(operationManager, stepName, async () => {\n return await executeStepWithRetries(stepName, stepFn, { maxRetries, retryDelay, backOffRate });\n });\n}\n\nclass ErrorThatShouldNeverHappen extends Error {\n constructor() {\n super('This error should never happen');\n }\n}\n\nexport async function executeStepWithRetries<TReturn>(\n stepName: string,\n fn: () => Promise<TReturn>,\n retryConfig: RetryConfig,\n): Promise<TReturn> {\n const maxRetries = retryConfig.maxRetries ?? 0;\n const retryDelay = retryConfig.retryDelay ?? 0;\n const backOffRate = retryConfig.backOffRate ?? 1;\n\n const attemptErrors: Error[] = [];\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n attemptErrors.push(err);\n if (err instanceof FatalError) {\n throw err;\n }\n if (maxRetries === 0) {\n throw err;\n }\n if (attempt >= maxRetries) {\n throw new MaxRetriesExceededError(stepName, maxRetries, attemptErrors);\n }\n const delay = retryDelay * Math.pow(backOffRate, attempt);\n await sleep(delay);\n }\n }\n throw new ErrorThatShouldNeverHappen();\n}\n","import { Database, Transaction } from '../db';\n\ntype InsertStateOptions = {\n runId: string;\n key: string;\n value: string;\n sequenceId: number;\n};\n\nexport async function insertState(tx: Transaction | Database, options: InsertStateOptions) {\n await tx\n .insertInto('state')\n .values({\n run_id: options.runId,\n key: options.key,\n value: options.value,\n })\n .onConflict((oc) =>\n oc.column('run_id').column('key').doUpdateSet({\n value: options.value,\n }),\n )\n .execute();\n\n await tx\n .insertInto('state_history')\n .values({\n run_id: options.runId,\n sequence_id: options.sequenceId,\n key: options.key,\n value: options.value,\n })\n .execute();\n}\n","import { withDbRetry } from '@internal/db/retry';\nimport { getExecutionContext } from '@internal/context/execution-context';\nimport { returnOrThrowOperationResult } from '@internal/context/operation-manager';\nimport { insertState } from '@internal/db/commands/insert-state';\nimport { serialize } from '@internal/utils/serialization';\nimport { StateDefinition } from '@api/state';\n\nconst SET_STATE_OPERATION_NAME = 'workflow::state::set';\n\nexport async function setState<T = unknown>(state: StateDefinition<T> | string, value: T) {\n const stateKey = typeof state === 'string' ? state : state.name;\n const { operationManager, runId, db } = getExecutionContext();\n\n const op = operationManager.getOperationResult();\n if (op) {\n return returnOrThrowOperationResult<void>(op);\n }\n\n const seqId = operationManager.reserveSequenceId();\n\n await withDbRetry(async () => {\n return await db.transaction().execute(async (tx) => {\n await insertState(tx, { runId, key: stateKey, value: serialize(value), sequenceId: seqId });\n await operationManager.recordResult(SET_STATE_OPERATION_NAME, seqId, null, tx);\n });\n });\n}\n","import { sql } from 'kysely';\nimport { Transaction } from '../db';\n\ntype Message = {\n id: string;\n payload?: string;\n type?: string;\n};\n\nexport async function readAndDeleteMessage(\n tx: Transaction,\n runId: string,\n messageType?: string,\n): Promise<Message | undefined> {\n const results = await sql<Message>`\n DELETE FROM messages\n WHERE id IN (\n SELECT id FROM messages\n WHERE destination_run_id = ${runId}\n ${messageType !== undefined ? sql`AND type = ${messageType}` : sql``}\n ORDER BY created_at_epoch_ms ASC\n LIMIT 1\n )\n RETURNING id, payload, type\n `.execute(tx);\n\n const result = results.rows[0];\n\n return result\n ? {\n id: result.id,\n payload: result.payload ?? undefined,\n type: result.type ?? undefined,\n }\n : undefined;\n}\n","import { getExecutionContext } from './context/execution-context';\nimport { executeAndRecordOperation } from './context/operation-manager';\n\nexport async function withDurableDeadline<T>(\n timeoutMs: number | undefined,\n operationName: string,\n fn: (deadlineMs: number | undefined) => Promise<T>,\n): Promise<T> {\n if (!timeoutMs) {\n return await fn(undefined);\n }\n\n const { operationManager } = getExecutionContext();\n\n let deadlineMs: number;\n const op = operationManager.getOperationResult();\n if (op) {\n deadlineMs = Number(op.result);\n } else {\n deadlineMs = Date.now() + timeoutMs;\n await executeAndRecordOperation(operationManager, operationName, async () => {\n return deadlineMs;\n });\n }\n\n return await fn(deadlineMs);\n}\n","import { withDbRetry } from '@internal/db/retry';\nimport { OperationTimedOutError } from '@internal/errors';\nimport { MessageEventBus } from '@internal/events/message-event-bus';\nimport { getExecutionContext } from '@internal/context/execution-context';\nimport { returnOrThrowOperationResult } from '@internal/context/operation-manager';\nimport { readAndDeleteMessage } from '@internal/db/commands/read-and-delete-message';\nimport { deserialize, serialize, serializeError } from '@internal/utils/serialization';\nimport { withDurableDeadline } from '@internal/with-durable-deadline';\nimport { MessageDefinition } from '@api/message';\n\nconst RECEIVE_MESSAGE_OPERATION_NAME = 'receiveMessage';\n\nconst RECEIVE_MESSAGE_DURABLE_DEADLINE_OPERATION_NAME = 'receiveMessageDurableDeadline';\n\nclass MessageNotAvailableError extends Error {}\n\nexport type ReceiveMessageOptions = {\n timeout?: number;\n};\n\nexport async function receiveMessage<T>(\n message: MessageDefinition<T> | string,\n options?: ReceiveMessageOptions,\n): Promise<T> {\n const messageType = typeof message === 'string' ? message : message.name;\n return await withDurableDeadline(\n options?.timeout,\n RECEIVE_MESSAGE_DURABLE_DEADLINE_OPERATION_NAME,\n async (deadlineMs) => {\n return await receiveMessageWithDeadline(messageType, deadlineMs);\n },\n );\n}\n\nasync function receiveMessageWithDeadline<T>(\n messageType: string,\n deadlineMs: number | undefined,\n): Promise<T> {\n const { runId, operationManager, messageEventBus, db } = getExecutionContext();\n\n const op = operationManager.getOperationResult();\n if (op) {\n return returnOrThrowOperationResult<T>(op) as T;\n }\n\n const seqId = operationManager.reserveSequenceId();\n\n while (true) {\n // Check if timeout expired\n if (deadlineMs && Date.now() >= deadlineMs) {\n const error = new OperationTimedOutError('receiveMessage');\n await operationManager.recordError(\n RECEIVE_MESSAGE_OPERATION_NAME,\n seqId,\n serializeError(error),\n );\n throw error;\n }\n\n try {\n return await withDbRetry(async () => {\n return await db.transaction().execute(async (tx) => {\n const msg = await readAndDeleteMessage(tx, runId, messageType);\n if (!msg) {\n throw new MessageNotAvailableError();\n }\n await operationManager.recordResult(\n RECEIVE_MESSAGE_OPERATION_NAME,\n seqId,\n serialize(msg.payload),\n tx,\n );\n return deserialize(msg.payload!) as T;\n });\n });\n } catch (error) {\n if (error instanceof MessageNotAvailableError) {\n const remainingMs = deadlineMs ? deadlineMs - Date.now() : undefined;\n await waitForMessageNotification(messageEventBus, runId, messageType, remainingMs);\n continue;\n }\n // Record and re-throw other errors\n await operationManager.recordError(\n RECEIVE_MESSAGE_OPERATION_NAME,\n seqId,\n serializeError(error as Error),\n );\n throw error;\n }\n }\n}\n\nasync function waitForMessageNotification(\n messageEventBus: MessageEventBus,\n runId: string,\n messageType: string,\n timeoutMs?: number,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | undefined;\n\n if (timeoutMs !== undefined) {\n if (timeoutMs <= 0) {\n reject(new OperationTimedOutError('receiveMessageDurableDeadline'));\n return;\n }\n\n timeoutId = setTimeout(() => {\n unsubscribe();\n reject(new OperationTimedOutError('waitForMessageNotification'));\n }, timeoutMs);\n }\n\n const unsubscribe = messageEventBus.subscribe(runId, messageType, () => {\n if (timeoutId) clearTimeout(timeoutId);\n unsubscribe();\n resolve();\n });\n });\n}\n","import { getExecutionContext } from '@internal/context/execution-context';\nimport { createHash } from 'node:crypto';\n\n/**\n * Generates a stable, unique step ID based on the current run ID and sequence ID.\n * This ID is deterministic and will remain the same across retries, making it\n * suitable for use as an idempotency key with third-party systems.\n *\n * The step ID is a SHA-256 hash of the run ID and sequence ID, formatted as a\n * hex string for easy use with external APIs.\n *\n * @returns A unique, stable identifier for the current step execution\n */\nexport function getStepId(): string {\n const ctx = getExecutionContext();\n const sequenceId = ctx.operationManager.getActiveSequenceId();\n\n if (sequenceId === null) {\n throw new Error('getStepId() can only be called from within a step function');\n }\n\n const hash = createHash('sha256');\n hash.update(`${ctx.runId}:${sequenceId}`);\n return hash.digest('hex');\n}\n","import { getExecutionContext } from '@internal/context/execution-context';\n\nexport function getAbortSignal() {\n return getExecutionContext().abortSignal;\n}\n","import { getExecutionContext } from '@internal/context/execution-context';\n\nexport function getRunId() {\n const { runId } = getExecutionContext();\n return runId;\n}\n","import { RunCancelledError } from '@internal/errors';\nimport { getExecutionContext } from '@internal/context/execution-context';\nimport { withDurableDeadline } from '@internal/with-durable-deadline';\n\nconst SLEEP_OPERATION_NAME = '_helical::sleep';\n\nexport async function sleep(ms: number) {\n const { abortSignal } = getExecutionContext();\n return await withDurableDeadline(ms, SLEEP_OPERATION_NAME, async (deadlineMs) => {\n const remainingMs = deadlineMs! - Date.now();\n try {\n await cancellableSleep(remainingMs, abortSignal);\n } catch {\n throw new RunCancelledError();\n }\n });\n}\n\nexport function cancellableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Sleep aborted'));\n return;\n }\n\n const timeoutId = setTimeout(() => {\n cleanup();\n resolve();\n }, ms);\n\n const onAbort = () => {\n cleanup();\n reject(new Error('Sleep aborted'));\n };\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n signal?.removeEventListener('abort', onAbort);\n };\n\n signal?.addEventListener('abort', onAbort);\n });\n}\n","import crypto from 'node:crypto';\nimport { getExecutionContext } from '@internal/context/execution-context';\nimport { executeAndRecordOperation } from '@internal/context/operation-manager';\n\nexport async function randomUUID() {\n const { operationManager } = getExecutionContext();\n const existingResult = operationManager.getOperationResult();\n if (existingResult) {\n return existingResult.result as string;\n } else {\n return await executeAndRecordOperation(operationManager, 'randomUUID', async () => {\n return crypto.randomUUID();\n });\n }\n}\n","import { getExecutionContext } from '@internal/context/execution-context';\nimport { executeAndRecordOperation } from '@internal/context/operation-manager';\n\nexport async function now() {\n const { operationManager } = getExecutionContext();\n const op = operationManager.getOperationResult();\n if (op) {\n return Number(op.result);\n } else {\n return await executeAndRecordOperation(operationManager, 'now', async () => {\n return Date.now();\n });\n }\n}\n"]}