sayiir 0.1.0-alpha.3

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 ADDED
@@ -0,0 +1,291 @@
1
+ # Sayiir
2
+
3
+ **Durable workflows for Node.js and TypeScript, powered by a Rust runtime.**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sayiir/sayiir/blob/main/LICENSE)
6
+ [![Node.js 18+](https://img.shields.io/badge/node-18+-339933.svg)](https://nodejs.org)
7
+ [![Discord](https://img.shields.io/badge/Discord-Join-7289da)](https://discord.gg/MWSzsHeg)
8
+
9
+ Write plain TypeScript functions. Sayiir makes them durable — automatic checkpointing, crash recovery, and parallel execution with zero infrastructure.
10
+
11
+ ```typescript
12
+ import { task, flow, runWorkflow } from "sayiir";
13
+
14
+ const fetchUser = task("fetch-user", async (id: number) => {
15
+ return { id, name: "Alice" };
16
+ });
17
+
18
+ const sendEmail = task("send-email", (user: { id: number; name: string }) => {
19
+ return `Sent welcome to ${user.name}`;
20
+ });
21
+
22
+ const workflow = flow<number>("welcome")
23
+ .then(fetchUser)
24
+ .then(sendEmail)
25
+ .build();
26
+
27
+ const result = runWorkflow(workflow, 42);
28
+ // "Sent welcome to Alice"
29
+ ```
30
+
31
+ No DSL. No YAML. No determinism constraints. No infrastructure to deploy.
32
+
33
+ ## Why Sayiir?
34
+
35
+ - **No replay, no determinism rules** — Unlike Temporal, Restate, and other replay-based engines, Sayiir checkpoints after each task and resumes from the last checkpoint. Your tasks can call any API, use any library, read the clock, generate random values. No restrictions.
36
+ - **A library, not a platform** — `pnpm add sayiir` and write workflows. No server cluster, no separate services. Optional PostgreSQL for production persistence.
37
+ - **Rust core** — All orchestration, checkpointing, and execution runs in Rust via NAPI-RS. You write TypeScript; Rust handles the hard parts.
38
+ - **Type-safe** — Generic `Flow<TInput, TLast>` builder tracks types through the chain. Full inference, no manual annotations.
39
+ - **Zod integration** — Optional input/output validation with Zod schemas as a peer dependency.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pnpm add sayiir
45
+ ```
46
+
47
+ Requires Node.js 18 or higher.
48
+
49
+ ## Quickstart
50
+
51
+ ### Inline lambdas — zero boilerplate
52
+
53
+ ```typescript
54
+ import { flow, runWorkflow } from "sayiir";
55
+
56
+ const workflow = flow<number>("pipeline")
57
+ .then("double", (x) => x * 2)
58
+ .then("add-one", (x) => x + 1)
59
+ .then("stringify", (x) => String(x))
60
+ .build();
61
+
62
+ const result = runWorkflow(workflow, 5);
63
+ // "11" (5 * 2 = 10, 10 + 1 = 11, String(11))
64
+ ```
65
+
66
+ No decorators, no registration — just pass any function. Use `task()` when you need metadata (retries, timeouts, tags) or reusable task definitions.
67
+
68
+ ### Sequential workflow
69
+
70
+ ```typescript
71
+ import { task, flow, runWorkflow } from "sayiir";
72
+
73
+ const double = task("double", (x: number) => x * 2);
74
+ const addTen = task("add-ten", (x: number) => x + 10);
75
+
76
+ const workflow = flow<number>("math")
77
+ .then(double)
78
+ .then(addTen)
79
+ .build();
80
+
81
+ const result = runWorkflow(workflow, 5);
82
+ // 20 (5 * 2 = 10, 10 + 10 = 20)
83
+ ```
84
+
85
+ ### Durable workflow (survives crashes)
86
+
87
+ ```typescript
88
+ import { task, flow, runDurableWorkflow, InMemoryBackend } from "sayiir";
89
+
90
+ const processOrder = task("process-order", (orderId: number) => {
91
+ return { orderId, status: "processed" };
92
+ }, { timeout: "30s" });
93
+
94
+ const sendConfirmation = task("send-confirmation", (order: { orderId: number }) => {
95
+ return `Confirmed order ${order.orderId}`;
96
+ });
97
+
98
+ const workflow = flow<number>("order")
99
+ .then(processOrder)
100
+ .then(sendConfirmation)
101
+ .build();
102
+
103
+ const backend = new InMemoryBackend();
104
+
105
+ // Checkpoints after each task — resumes from last checkpoint on crash
106
+ const status = runDurableWorkflow(workflow, "order-123", 42, backend);
107
+
108
+ if (status.status === "completed") {
109
+ console.log(status.output); // "Confirmed order 42"
110
+ }
111
+ ```
112
+
113
+ ### PostgreSQL persistence
114
+
115
+ ```typescript
116
+ import { PostgresBackend, runDurableWorkflow } from "sayiir";
117
+
118
+ // Auto-runs migrations on first connect
119
+ const backend = PostgresBackend.connect("postgresql://localhost/sayiir");
120
+ const status = runDurableWorkflow(workflow, "run-001", 21, backend);
121
+ ```
122
+
123
+ ### Retry policy
124
+
125
+ ```typescript
126
+ import { task } from "sayiir";
127
+
128
+ const flakyCall = task("flaky-call", async (url: string) => {
129
+ const res = await fetch(url);
130
+ return res.json();
131
+ }, {
132
+ retry: { maxAttempts: 3, initialDelay: "500ms", backoffMultiplier: 2.0 },
133
+ });
134
+ ```
135
+
136
+ ### Parallel execution (fork/join)
137
+
138
+ ```typescript
139
+ import { task, flow, branch, runWorkflow } from "sayiir";
140
+
141
+ const validatePayment = task("validate-payment", (order: { id: number }) => {
142
+ return { payment: "valid" };
143
+ });
144
+
145
+ const checkInventory = task("check-inventory", (order: { id: number }) => {
146
+ return { stock: "available" };
147
+ });
148
+
149
+ const workflow = flow<{ id: number }>("checkout")
150
+ .fork([
151
+ branch("payment", validatePayment),
152
+ branch("inventory", checkInventory),
153
+ ])
154
+ .join("finalize", ([payment, inventory]) => {
155
+ return { ...payment, ...inventory };
156
+ })
157
+ .build();
158
+
159
+ const result = runWorkflow(workflow, { id: 1 });
160
+ ```
161
+
162
+ ### Delays and signals
163
+
164
+ ```typescript
165
+ import { flow, runDurableWorkflow, sendSignal, resumeWorkflow } from "sayiir";
166
+
167
+ const workflow = flow<number>("approval")
168
+ .then("submit", (id) => ({ requestId: id }))
169
+ .waitForSignal("approval", "manager_approval", { timeout: "48h" })
170
+ .then("process", (signal) => `Approved: ${signal}`)
171
+ .build();
172
+
173
+ // First run — parks at the signal
174
+ const status = runDurableWorkflow(workflow, "req-1", 42, backend);
175
+ // status.status === "awaiting_signal"
176
+
177
+ // Later, when the approval arrives:
178
+ sendSignal("req-1", "manager_approval", { approved: true }, backend);
179
+ const final = resumeWorkflow(workflow, "req-1", backend);
180
+ ```
181
+
182
+ ### Zod validation
183
+
184
+ ```typescript
185
+ import { z } from "zod";
186
+ import { task, flow, runWorkflow } from "sayiir";
187
+
188
+ const OrderSchema = z.object({
189
+ id: z.string(),
190
+ amount: z.number().positive(),
191
+ });
192
+
193
+ const processOrder = task("process-order", (order) => {
194
+ return { status: "charged", amount: order.amount };
195
+ }, {
196
+ input: OrderSchema,
197
+ });
198
+
199
+ const workflow = flow("checkout").then(processOrder).build();
200
+ const result = runWorkflow(workflow, { id: "abc", amount: 99.99 });
201
+ // Zod validates input before the task runs
202
+ ```
203
+
204
+ ### Task metadata
205
+
206
+ ```typescript
207
+ const processPayment = task("process-payment", async (order) => {
208
+ // ...
209
+ }, {
210
+ timeout: "60s",
211
+ retries: 3,
212
+ tags: ["payments", "critical"],
213
+ description: "Charges the customer's payment method",
214
+ });
215
+ ```
216
+
217
+ ## API Reference
218
+
219
+ ### Task Definition
220
+
221
+ - **`task(id, fn, opts?)`** — Create a named task. Optional: `timeout`, `retries`, `retry`, `tags`, `description`, `input`/`output` (Zod schemas).
222
+
223
+ ### Flow Builder
224
+
225
+ - **`flow<TInput>(name)`** — Create a new type-safe flow builder.
226
+ - **`.then(fn)`** / **`.then(id, fn, opts?)`** — Append a task step. Accepts `task()` functions, plain functions, or lambdas.
227
+ - **`.fork(branches)`** — Start parallel branches. Takes an array of `branch()` definitions.
228
+ - **`.join(id, fn)`** — Merge branches with a combining function.
229
+ - **`.delay(id, duration)`** — Durable delay (`"30s"`, `"5m"`, `"1h"`, or milliseconds).
230
+ - **`.waitForSignal(id, signalName, opts?)`** — Wait for an external signal.
231
+ - **`.build()`** — Compile and return a `Workflow<TIn, TOut>`.
232
+
233
+ ### Execution
234
+
235
+ - **`runWorkflow(workflow, input)`** — Execute in-memory. Returns the final output.
236
+ - **`runDurableWorkflow(workflow, instanceId, input, backend)`** — Execute with checkpointing. Returns `WorkflowStatus<TOut>`.
237
+ - **`resumeWorkflow(workflow, instanceId, backend)`** — Resume from last checkpoint.
238
+ - **`cancelWorkflow(instanceId, backend, opts?)`** — Cancel a running workflow.
239
+ - **`pauseWorkflow(instanceId, backend, opts?)`** — Pause a running workflow.
240
+ - **`unpauseWorkflow(instanceId, backend)`** — Unpause a paused workflow.
241
+ - **`sendSignal(instanceId, signalName, payload, backend)`** — Send an external signal.
242
+
243
+ ### WorkflowStatus\<TOut\>
244
+
245
+ Discriminated union — use `status.status` with TypeScript narrowing:
246
+
247
+ ```typescript
248
+ if (status.status === "completed") {
249
+ console.log(status.output); // TOut
250
+ } else if (status.status === "failed") {
251
+ console.log(status.error); // string
252
+ }
253
+ ```
254
+
255
+ Variants: `completed`, `in_progress`, `failed`, `cancelled`, `paused`, `waiting`, `awaiting_signal`.
256
+
257
+ ### Backends
258
+
259
+ - **`new InMemoryBackend()`** — In-memory storage for development and testing.
260
+ - **`PostgresBackend.connect(url)`** — PostgreSQL persistence. Auto-runs migrations.
261
+
262
+ ## Architecture
263
+
264
+ ```
265
+ Your TypeScript code Sayiir (Rust) Storage
266
+ ┌──────────────┐ ┌─────────────────────┐ ┌──────────────┐
267
+ │ task() │───>│ Orchestration │───>│ Checkpoint │
268
+ │ functions │ │ Checkpointing │ │ after each │
269
+ │ │<───│ Crash recovery │<───│ task │
270
+ └──────────────┘ │ Fork/join │ └──────────────┘
271
+ │ Serialization │
272
+ └─────────────────────┘
273
+ ```
274
+
275
+ TypeScript provides task implementations. Rust handles everything else: building the execution graph, running tasks in order, checkpointing results, recovering from crashes, and managing parallel branches.
276
+
277
+ ## Requirements
278
+
279
+ - Node.js 18+
280
+ - Optional: `zod` for input/output validation
281
+
282
+ ## License
283
+
284
+ MIT
285
+
286
+ ## Links
287
+
288
+ - [Documentation](https://docs.sayiir.dev/getting-started/nodejs/)
289
+ - [GitHub](https://github.com/sayiir/sayiir)
290
+ - [Discord](https://discord.gg/MWSzsHeg)
291
+ - [Roadmap](https://github.com/sayiir/sayiir/blob/main/ROADMAP.md)
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Duration parsing utility.
3
+ *
4
+ * Converts human-readable duration strings (via `ms` library) or numeric
5
+ * milliseconds to milliseconds.
6
+ */
7
+ import type { Duration } from "./types.js";
8
+ /**
9
+ * Parse a Duration value to milliseconds.
10
+ *
11
+ * - Numbers are returned as-is (assumed to be ms).
12
+ * - Strings are parsed via the `ms` library (e.g. "30s", "5m", "1h").
13
+ */
14
+ export declare function parseDuration(d: Duration): number;
15
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAY3C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAOjD"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Duration parsing utility.
4
+ *
5
+ * Converts human-readable duration strings (via `ms` library) or numeric
6
+ * milliseconds to milliseconds.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.parseDuration = parseDuration;
10
+ let _ms;
11
+ function getMs() {
12
+ if (!_ms) {
13
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
14
+ _ms = require("ms");
15
+ }
16
+ return _ms;
17
+ }
18
+ /**
19
+ * Parse a Duration value to milliseconds.
20
+ *
21
+ * - Numbers are returned as-is (assumed to be ms).
22
+ * - Strings are parsed via the `ms` library (e.g. "30s", "5m", "1h").
23
+ */
24
+ function parseDuration(d) {
25
+ if (typeof d === "number")
26
+ return d;
27
+ const result = getMs()(d);
28
+ if (result == null) {
29
+ throw new Error(`Invalid duration: "${d}"`);
30
+ }
31
+ return result;
32
+ }
33
+ //# sourceMappingURL=duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAoBH,sCAOC;AAvBD,IAAI,GAAsD,CAAC;AAE3D,SAAS,KAAK;IACZ,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,iEAAiE;QACjE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAwC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,CAAW;IACvC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Workflow execution utilities.
3
+ *
4
+ * Thin wrappers around the native addon, mirroring the Python executor.
5
+ */
6
+ import type { Workflow } from "./flow.js";
7
+ import type { WorkflowStatus } from "./types.js";
8
+ import type { NapiInMemoryBackend, NapiPostgresBackend } from "./native.js";
9
+ /** Backend type union. */
10
+ export type Backend = InMemoryBackend | PostgresBackend;
11
+ /** In-memory persistence backend for testing and development. */
12
+ export declare class InMemoryBackend {
13
+ /** @internal */
14
+ readonly _inner: NapiInMemoryBackend;
15
+ constructor();
16
+ }
17
+ /** PostgreSQL persistence backend for durable production workflows. */
18
+ export declare class PostgresBackend {
19
+ /** @internal */
20
+ readonly _inner: NapiPostgresBackend;
21
+ private constructor();
22
+ static connect(url: string): PostgresBackend;
23
+ }
24
+ /**
25
+ * Run a workflow to completion (no persistence).
26
+ *
27
+ * Returns the workflow output directly.
28
+ */
29
+ export declare function runWorkflow<TIn, TOut>(workflow: Workflow<TIn, TOut>, input: TIn): TOut;
30
+ /**
31
+ * Run a workflow with checkpointing and durability.
32
+ *
33
+ * Returns a WorkflowStatus indicating the outcome.
34
+ */
35
+ export declare function runDurableWorkflow<TIn, TOut>(workflow: Workflow<TIn, TOut>, instanceId: string, input: TIn, backend: Backend): WorkflowStatus<TOut>;
36
+ /** Resume a workflow from a saved checkpoint. */
37
+ export declare function resumeWorkflow<TIn, TOut>(workflow: Workflow<TIn, TOut>, instanceId: string, backend: Backend): WorkflowStatus<TOut>;
38
+ /** Request cancellation of a running workflow. */
39
+ export declare function cancelWorkflow(instanceId: string, backend: Backend, opts?: {
40
+ reason?: string;
41
+ cancelledBy?: string;
42
+ }): void;
43
+ /** Request pausing of a running workflow. */
44
+ export declare function pauseWorkflow(instanceId: string, backend: Backend, opts?: {
45
+ reason?: string;
46
+ pausedBy?: string;
47
+ }): void;
48
+ /** Unpause a paused workflow so it can be resumed. */
49
+ export declare function unpauseWorkflow(instanceId: string, backend: Backend): void;
50
+ /** Send an external signal to a workflow instance. */
51
+ export declare function sendSignal(instanceId: string, signalName: string, payload: unknown, backend: Backend): void;
52
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAGrB,0BAA0B;AAC1B,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,eAAe,CAAC;AAExD,iEAAiE;AACjE,qBAAa,eAAe;IAC1B,gBAAgB;IAChB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;;CAKtC;AAED,uEAAuE;AACvE,qBAAa,eAAe;IAC1B,gBAAgB;IAChB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAErC,OAAO;IAIP,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe;CAI7C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EACnC,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAC7B,KAAK,EAAE,GAAG,GACT,IAAI,CAON;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAC1C,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,GAAG,EACV,OAAO,EAAE,OAAO,GACf,cAAc,CAAC,IAAI,CAAC,CAStB;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,EACtC,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAC7B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,cAAc,CAAC,IAAI,CAAC,CAQtB;AAED,kDAAkD;AAClD,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C,IAAI,CAGN;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,IAAI,CAGN;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,IAAI,CAGN;AAED,sDAAsD;AACtD,wBAAgB,UAAU,CACxB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACf,IAAI,CAGN"}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /**
3
+ * Workflow execution utilities.
4
+ *
5
+ * Thin wrappers around the native addon, mirroring the Python executor.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.PostgresBackend = exports.InMemoryBackend = void 0;
9
+ exports.runWorkflow = runWorkflow;
10
+ exports.runDurableWorkflow = runDurableWorkflow;
11
+ exports.resumeWorkflow = resumeWorkflow;
12
+ exports.cancelWorkflow = cancelWorkflow;
13
+ exports.pauseWorkflow = pauseWorkflow;
14
+ exports.unpauseWorkflow = unpauseWorkflow;
15
+ exports.sendSignal = sendSignal;
16
+ const types_js_1 = require("./types.js");
17
+ const native_js_1 = require("./native.js");
18
+ /** In-memory persistence backend for testing and development. */
19
+ class InMemoryBackend {
20
+ /** @internal */
21
+ _inner;
22
+ constructor() {
23
+ this._inner = new ((0, native_js_1.getNative)().NapiInMemoryBackend)();
24
+ }
25
+ }
26
+ exports.InMemoryBackend = InMemoryBackend;
27
+ /** PostgreSQL persistence backend for durable production workflows. */
28
+ class PostgresBackend {
29
+ /** @internal */
30
+ _inner;
31
+ constructor(inner) {
32
+ this._inner = inner;
33
+ }
34
+ static connect(url) {
35
+ const inner = (0, native_js_1.getNative)().NapiPostgresBackend.connect(url);
36
+ return new PostgresBackend(inner);
37
+ }
38
+ }
39
+ exports.PostgresBackend = PostgresBackend;
40
+ /**
41
+ * Run a workflow to completion (no persistence).
42
+ *
43
+ * Returns the workflow output directly.
44
+ */
45
+ function runWorkflow(workflow, input) {
46
+ const engine = new ((0, native_js_1.getNative)().NapiWorkflowEngine)();
47
+ return engine.run(workflow._inner, input, workflow._taskRegistry);
48
+ }
49
+ /**
50
+ * Run a workflow with checkpointing and durability.
51
+ *
52
+ * Returns a WorkflowStatus indicating the outcome.
53
+ */
54
+ function runDurableWorkflow(workflow, instanceId, input, backend) {
55
+ const engine = createDurableEngine(backend);
56
+ const raw = engine.run(workflow._inner, instanceId, input, workflow._taskRegistry);
57
+ return parseWorkflowStatus(raw);
58
+ }
59
+ /** Resume a workflow from a saved checkpoint. */
60
+ function resumeWorkflow(workflow, instanceId, backend) {
61
+ const engine = createDurableEngine(backend);
62
+ const raw = engine.resume(workflow._inner, instanceId, workflow._taskRegistry);
63
+ return parseWorkflowStatus(raw);
64
+ }
65
+ /** Request cancellation of a running workflow. */
66
+ function cancelWorkflow(instanceId, backend, opts) {
67
+ const engine = createDurableEngine(backend);
68
+ engine.cancel(instanceId, opts?.reason, opts?.cancelledBy);
69
+ }
70
+ /** Request pausing of a running workflow. */
71
+ function pauseWorkflow(instanceId, backend, opts) {
72
+ const engine = createDurableEngine(backend);
73
+ engine.pause(instanceId, opts?.reason, opts?.pausedBy);
74
+ }
75
+ /** Unpause a paused workflow so it can be resumed. */
76
+ function unpauseWorkflow(instanceId, backend) {
77
+ const engine = createDurableEngine(backend);
78
+ engine.unpause(instanceId);
79
+ }
80
+ /** Send an external signal to a workflow instance. */
81
+ function sendSignal(instanceId, signalName, payload, backend) {
82
+ const engine = createDurableEngine(backend);
83
+ engine.sendSignal(instanceId, signalName, payload);
84
+ }
85
+ // ---- Internal helpers ----
86
+ function createDurableEngine(backend) {
87
+ const native = (0, native_js_1.getNative)();
88
+ if (backend instanceof InMemoryBackend) {
89
+ return native.NapiDurableEngine.withInMemory(backend._inner);
90
+ }
91
+ if (backend instanceof PostgresBackend) {
92
+ return native.NapiDurableEngine.withPostgres(backend._inner);
93
+ }
94
+ throw new types_js_1.WorkflowError("backend must be InMemoryBackend or PostgresBackend");
95
+ }
96
+ function parseWorkflowStatus(raw) {
97
+ switch (raw.status) {
98
+ case "completed":
99
+ return {
100
+ status: "completed",
101
+ output: (raw.outputJson != null
102
+ ? JSON.parse(raw.outputJson)
103
+ : undefined),
104
+ };
105
+ case "in_progress":
106
+ return { status: "in_progress" };
107
+ case "failed":
108
+ return { status: "failed", error: raw.error ?? "unknown error" };
109
+ case "cancelled":
110
+ return {
111
+ status: "cancelled",
112
+ reason: raw.reason,
113
+ cancelledBy: raw.cancelledBy,
114
+ };
115
+ case "paused":
116
+ return {
117
+ status: "paused",
118
+ reason: raw.reason,
119
+ pausedBy: raw.cancelledBy,
120
+ };
121
+ default:
122
+ return { status: raw.status };
123
+ }
124
+ }
125
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AA6CH,kCAUC;AAOD,gDAcC;AAGD,wCAYC;AAGD,wCAOC;AAGD,sCAOC;AAGD,0CAMC;AAGD,gCAQC;AA/HD,yCAA2C;AAM3C,2CAAwC;AAKxC,iEAAiE;AACjE,MAAa,eAAe;IAC1B,gBAAgB;IACP,MAAM,CAAsB;IAErC;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAA,qBAAS,GAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC;IACxD,CAAC;CACF;AAPD,0CAOC;AAED,uEAAuE;AACvE,MAAa,eAAe;IAC1B,gBAAgB;IACP,MAAM,CAAsB;IAErC,YAAoB,KAA0B;QAC5C,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,GAAW;QACxB,MAAM,KAAK,GAAG,IAAA,qBAAS,GAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3D,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;CACF;AAZD,0CAYC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CACzB,QAA6B,EAC7B,KAAU;IAEV,MAAM,MAAM,GAAG,IAAI,CAAC,IAAA,qBAAS,GAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC;IACtD,OAAO,MAAM,CAAC,GAAG,CACf,QAAQ,CAAC,MAAM,EACf,KAAK,EACL,QAAQ,CAAC,aAAa,CACf,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,QAA6B,EAC7B,UAAkB,EAClB,KAAU,EACV,OAAgB;IAEhB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CACpB,QAAQ,CAAC,MAAM,EACf,UAAU,EACV,KAAK,EACL,QAAQ,CAAC,aAAa,CACvB,CAAC;IACF,OAAO,mBAAmB,CAAO,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,iDAAiD;AACjD,SAAgB,cAAc,CAC5B,QAA6B,EAC7B,UAAkB,EAClB,OAAgB;IAEhB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CACvB,QAAQ,CAAC,MAAM,EACf,UAAU,EACV,QAAQ,CAAC,aAAa,CACvB,CAAC;IACF,OAAO,mBAAmB,CAAO,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,kDAAkD;AAClD,SAAgB,cAAc,CAC5B,UAAkB,EAClB,OAAgB,EAChB,IAAgD;IAEhD,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC7D,CAAC;AAED,6CAA6C;AAC7C,SAAgB,aAAa,CAC3B,UAAkB,EAClB,OAAgB,EAChB,IAA6C;IAE7C,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,sDAAsD;AACtD,SAAgB,eAAe,CAC7B,UAAkB,EAClB,OAAgB;IAEhB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED,sDAAsD;AACtD,SAAgB,UAAU,CACxB,UAAkB,EAClB,UAAkB,EAClB,OAAgB,EAChB,OAAgB;IAEhB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,6BAA6B;AAE7B,SAAS,mBAAmB,CAAC,OAAgB;IAC3C,MAAM,MAAM,GAAG,IAAA,qBAAS,GAAE,CAAC;IAC3B,IAAI,OAAO,YAAY,eAAe,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,OAAO,YAAY,eAAe,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,IAAI,wBAAa,CAAC,oDAAoD,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAyB;IAEzB,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,WAAW;YACd,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI;oBAC7B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;oBAC5B,CAAC,CAAC,SAAS,CAAS;aACvB,CAAC;QACJ,KAAK,aAAa;YAChB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC;QACnE,KAAK,WAAW;YACd,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,WAAW,EAAE,GAAG,CAAC,WAAW;aAC7B,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,QAAQ,EAAE,GAAG,CAAC,WAAW;aAC1B,CAAC;QACJ;YACE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAuB,EAAE,CAAC;IACnD,CAAC;AACH,CAAC"}
package/dist/flow.d.ts ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Type-safe flow builder for constructing workflows.
3
+ *
4
+ * The builder tracks input/output types through the chain using generics,
5
+ * providing full type inference without manual annotations.
6
+ *
7
+ * ```ts
8
+ * const wf = flow<number>("welcome")
9
+ * .then("fetch", (id) => getUser(id)) // id: number -> User
10
+ * .then("greet", (user) => `Hi ${user.name}`) // user: User -> string
11
+ * .build(); // Workflow<number, string>
12
+ * ```
13
+ */
14
+ import type { Duration, StepOptions, ZodLike } from "./types.js";
15
+ import type { TaskFn } from "./task.js";
16
+ import type { NapiFlowBuilder, NapiTaskMetadata, NapiWorkflow } from "./native.js";
17
+ /** A compiled workflow ready for execution. */
18
+ export declare class Workflow<TIn, TOut> {
19
+ /** @internal */
20
+ readonly _inner: NapiWorkflow;
21
+ /** @internal */
22
+ readonly _taskRegistry: Record<string, Function>;
23
+ constructor(inner: NapiWorkflow, taskRegistry: Record<string, Function>);
24
+ get workflowId(): string;
25
+ get definitionHash(): string;
26
+ }
27
+ /** A branch definition for fork/join. */
28
+ export interface BranchDef<TIn, TOut> {
29
+ readonly name: string;
30
+ readonly steps: readonly BranchStep[];
31
+ /** @internal — phantom type marker */
32
+ readonly _in?: TIn;
33
+ readonly _out?: TOut;
34
+ }
35
+ interface BranchStep {
36
+ taskId: string;
37
+ fn: Function;
38
+ metadata?: NapiTaskMetadata;
39
+ }
40
+ /** Infer the output types from a tuple of branch definitions. */
41
+ export type InferBranchOutputs<T extends readonly BranchDef<any, any>[]> = {
42
+ [K in keyof T]: T[K] extends BranchDef<any, infer O> ? O : never;
43
+ };
44
+ /**
45
+ * Create a branch for use with `.fork()`.
46
+ *
47
+ * ```ts
48
+ * flow<Order>("process")
49
+ * .then(chargePayment)
50
+ * .fork([
51
+ * branch("email", sendConfirmation),
52
+ * branch("ship", shipOrder),
53
+ * ])
54
+ * .join("finalize", ([email, ship]) => ({ email, ship }))
55
+ * .build();
56
+ * ```
57
+ */
58
+ export declare function branch<TIn, TOut>(name: string, fn: TaskFn<TIn, TOut> | ((input: TIn) => TOut | Promise<TOut>)): BranchDef<TIn, Awaited<TOut>>;
59
+ /** Type-safe workflow builder. */
60
+ export declare class Flow<TInput, TLast = TInput> {
61
+ /** @internal */
62
+ readonly _builder: NapiFlowBuilder;
63
+ /** @internal */
64
+ readonly _taskRegistry: Record<string, Function>;
65
+ /** @internal */
66
+ _lambdaCounter: number;
67
+ constructor(name: string);
68
+ /**
69
+ * Add a sequential task step.
70
+ *
71
+ * Accepts either a `TaskFn` (created by `task()`) or an inline function with an id.
72
+ */
73
+ then<TOut>(fn: TaskFn<TLast, TOut>): Flow<TInput, Awaited<TOut>>;
74
+ then<TOut>(id: string, fn: ((input: TLast) => TOut | Promise<TOut>) | TaskFn<TLast, TOut>, opts?: StepOptions): Flow<TInput, Awaited<TOut>>;
75
+ /**
76
+ * Start a fork for parallel execution.
77
+ *
78
+ * ```ts
79
+ * .fork([
80
+ * branch("email", sendEmail),
81
+ * branch("sms", sendSms),
82
+ * ])
83
+ * .join("merge", ([email, sms]) => ({ email, sms }))
84
+ * ```
85
+ */
86
+ fork<TBranches extends readonly BranchDef<TLast, any>[]>(branches: [...TBranches]): ForkBuilder<TInput, TLast, TBranches>;
87
+ /**
88
+ * Add a durable delay. No workers are held during the delay.
89
+ *
90
+ * Duration can be a number (ms) or a string like "30s", "5m", "1h".
91
+ */
92
+ delay(id: string, duration: Duration): Flow<TInput, TLast>;
93
+ /**
94
+ * Wait for an external signal before continuing.
95
+ *
96
+ * The workflow parks and releases the worker until the signal arrives.
97
+ */
98
+ waitForSignal<TSignal = unknown>(id: string, signalName: string, opts?: {
99
+ timeout?: Duration;
100
+ schema?: ZodLike<TSignal>;
101
+ }): Flow<TInput, TSignal>;
102
+ /** Build the workflow definition. */
103
+ build(): Workflow<TInput, TLast>;
104
+ }
105
+ /** Builder for fork/join parallel branches. */
106
+ export declare class ForkBuilder<TInput, TLast, TBranches extends readonly BranchDef<TLast, any>[]> {
107
+ private readonly flow;
108
+ private readonly branches;
109
+ constructor(flow: Flow<TInput, TLast>, branches: TBranches);
110
+ /**
111
+ * Join branches with a combining function.
112
+ *
113
+ * The join function receives a tuple of branch outputs.
114
+ */
115
+ join<TOut>(id: string, fn: (branches: InferBranchOutputs<TBranches>) => TOut | Promise<TOut>): Flow<TInput, Awaited<TOut>>;
116
+ }
117
+ /** Factory function to create a new flow. */
118
+ export declare function flow<TInput>(name: string): Flow<TInput>;
119
+ export {};
120
+ //# sourceMappingURL=flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../src/flow.ts"],"names":[],"mappings":"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"}