openworkflow 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -345
- package/dist/backend-test/backend.testsuite.d.ts +20 -0
- package/dist/backend-test/backend.testsuite.d.ts.map +1 -0
- package/dist/{core → backend-test}/backend.testsuite.js +191 -59
- package/dist/backend-test/index.d.ts +2 -0
- package/dist/backend-test/index.d.ts.map +1 -0
- package/dist/backend-test/index.js +1 -0
- package/dist/{core/backend.d.ts → backend.d.ts} +7 -5
- package/dist/backend.d.ts.map +1 -0
- package/dist/{core/backend.js → backend.js} +0 -1
- package/dist/backend.testsuite.d.ts +20 -0
- package/dist/backend.testsuite.d.ts.map +1 -0
- package/dist/{core/backend-test-suite.js → backend.testsuite.js} +301 -171
- package/dist/bin/openworkflow.d.ts +3 -0
- package/dist/bin/openworkflow.d.ts.map +1 -0
- package/dist/bin/openworkflow.js +43 -0
- package/dist/chaos.test.d.ts +2 -0
- package/dist/chaos.test.d.ts.map +1 -0
- package/dist/chaos.test.js +88 -0
- package/dist/client.d.ts +141 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/{sdk/sdk.js → client.js} +43 -71
- package/dist/client.test.d.ts +2 -0
- package/dist/client.test.d.ts.map +1 -0
- package/dist/{sdk/sdk.test.js → client.test.js} +130 -14
- package/dist/core/duration.d.ts +4 -2
- package/dist/core/duration.d.ts.map +1 -1
- package/dist/core/duration.js +3 -2
- package/dist/core/duration.test.js +0 -1
- package/dist/core/error.d.ts +14 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +17 -0
- package/dist/core/error.test.d.ts +2 -0
- package/dist/core/error.test.d.ts.map +1 -0
- package/dist/core/error.test.js +60 -0
- package/dist/core/json.js +0 -1
- package/dist/core/result.d.ts +14 -4
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +10 -1
- package/dist/core/result.test.js +2 -2
- package/dist/core/retry.d.ts +0 -9
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +0 -15
- package/dist/core/schema.js +0 -1
- package/dist/core/step.d.ts +1 -32
- package/dist/core/step.d.ts.map +1 -1
- package/dist/core/step.js +0 -36
- package/dist/core/step.test.js +1 -75
- package/dist/core/workflow.d.ts +2 -47
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/core/workflow.js +0 -45
- package/dist/core/workflow.test.js +1 -104
- package/dist/driver.d.ts +116 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +1 -0
- package/dist/{execution/execution.d.ts → execution.d.ts} +4 -26
- package/dist/execution.d.ts.map +1 -0
- package/dist/{execution/execution.js → execution.js} +4 -5
- package/dist/execution.test.d.ts.map +1 -0
- package/dist/{execution/execution.test.js → execution.test.js} +4 -5
- package/dist/factory.d.ts +74 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +72 -0
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/node-sqlite/backend.d.ts +52 -0
- package/dist/node-sqlite/backend.d.ts.map +1 -0
- package/dist/node-sqlite/backend.js +673 -0
- package/dist/node-sqlite/index.d.ts +11 -0
- package/dist/node-sqlite/index.d.ts.map +1 -0
- package/dist/node-sqlite/index.js +7 -0
- package/dist/node-sqlite/sqlite.d.ts +60 -0
- package/dist/node-sqlite/sqlite.d.ts.map +1 -0
- package/dist/{backend-sqlite → node-sqlite}/sqlite.js +20 -3
- package/dist/postgres/backend.d.ts +44 -0
- package/dist/postgres/backend.d.ts.map +1 -0
- package/dist/postgres/backend.js +534 -0
- package/dist/postgres/backend.test.d.ts +2 -0
- package/dist/postgres/backend.test.d.ts.map +1 -0
- package/dist/postgres/backend.test.js +19 -0
- package/dist/postgres/driver.d.ts +81 -0
- package/dist/postgres/driver.d.ts.map +1 -0
- package/dist/postgres/driver.js +63 -0
- package/dist/postgres/index.d.ts +11 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +7 -0
- package/dist/postgres/internal.d.ts +2 -0
- package/dist/postgres/internal.d.ts.map +1 -0
- package/dist/postgres/internal.js +1 -0
- package/dist/postgres/postgres.d.ts +42 -0
- package/dist/postgres/postgres.d.ts.map +1 -0
- package/dist/postgres/postgres.js +233 -0
- package/dist/postgres/postgres.test.d.ts +2 -0
- package/dist/postgres/postgres.test.d.ts.map +1 -0
- package/dist/postgres/postgres.test.js +45 -0
- package/dist/postgres/scripts/db-migrate.d.ts +2 -0
- package/dist/postgres/scripts/db-migrate.d.ts.map +1 -0
- package/dist/postgres/scripts/db-migrate.js +4 -0
- package/dist/postgres/scripts/db-reset.d.ts +2 -0
- package/dist/postgres/scripts/db-reset.d.ts.map +1 -0
- package/dist/postgres/scripts/db-reset.js +5 -0
- package/dist/postgres/scripts/squawk.d.ts +2 -0
- package/dist/postgres/scripts/squawk.d.ts.map +1 -0
- package/dist/postgres/scripts/squawk.js +16 -0
- package/dist/postgres/vitest.global-setup.d.ts +3 -0
- package/dist/postgres/vitest.global-setup.d.ts.map +1 -0
- package/dist/postgres/vitest.global-setup.js +7 -0
- package/dist/postgres.d.ts +2 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +1 -0
- package/dist/registry.d.ts +27 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +48 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +109 -0
- package/dist/{backend-sqlite → sqlite}/backend.d.ts +8 -4
- package/dist/sqlite/backend.d.ts.map +1 -0
- package/dist/{backend-sqlite → sqlite}/backend.js +35 -9
- package/dist/sqlite/backend.test.d.ts +2 -0
- package/dist/sqlite/backend.test.d.ts.map +1 -0
- package/dist/sqlite/backend.test.js +50 -0
- package/dist/sqlite/driver.d.ts +79 -0
- package/dist/sqlite/driver.d.ts.map +1 -0
- package/dist/sqlite/driver.js +62 -0
- package/dist/sqlite/index.d.ts +13 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +11 -0
- package/dist/sqlite/internal.d.ts +2 -0
- package/dist/sqlite/internal.d.ts.map +1 -0
- package/dist/sqlite/internal.js +1 -0
- package/dist/{backend-sqlite → sqlite}/sqlite.d.ts +18 -2
- package/dist/sqlite/sqlite.d.ts.map +1 -0
- package/dist/sqlite/sqlite.js +246 -0
- package/dist/sqlite/sqlite.test.d.ts +2 -0
- package/dist/sqlite/sqlite.test.d.ts.map +1 -0
- package/dist/sqlite/sqlite.test.js +171 -0
- package/dist/sqlite.d.ts +2 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{worker/worker.d.ts → worker.d.ts} +11 -4
- package/dist/worker.d.ts.map +1 -0
- package/dist/{worker/worker.js → worker.js} +20 -11
- package/dist/{worker/worker.test.d.ts.map → worker.test.d.ts.map} +1 -1
- package/dist/{worker/worker.test.js → worker.test.js} +136 -22
- package/dist/workflow.d.ts +60 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +48 -0
- package/dist/workflow.test.d.ts +2 -0
- package/dist/workflow.test.d.ts.map +1 -0
- package/dist/workflow.test.js +84 -0
- package/package.json +28 -4
- package/dist/backend-sqlite/backend.d.ts.map +0 -1
- package/dist/backend-sqlite/backend.js.map +0 -1
- package/dist/backend-sqlite/index.d.ts +0 -2
- package/dist/backend-sqlite/index.d.ts.map +0 -1
- package/dist/backend-sqlite/index.js +0 -2
- package/dist/backend-sqlite/index.js.map +0 -1
- package/dist/backend-sqlite/sqlite.d.ts.map +0 -1
- package/dist/backend-sqlite/sqlite.js.map +0 -1
- package/dist/config/config.d.ts +0 -102
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/config.js +0 -29
- package/dist/config/config.js.map +0 -1
- package/dist/config/index.d.ts +0 -3
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -2
- package/dist/config/index.js.map +0 -1
- package/dist/config.d.ts +0 -28
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -41
- package/dist/config.js.map +0 -1
- package/dist/core/backend-test-suite.d.ts +0 -22
- package/dist/core/backend-test-suite.d.ts.map +0 -1
- package/dist/core/backend-test-suite.js.map +0 -1
- package/dist/core/backend.d.ts.map +0 -1
- package/dist/core/backend.js.map +0 -1
- package/dist/core/backend.testsuite.d.ts +0 -21
- package/dist/core/backend.testsuite.d.ts.map +0 -1
- package/dist/core/backend.testsuite.js.map +0 -1
- package/dist/core/duration.js.map +0 -1
- package/dist/core/duration.test.js.map +0 -1
- package/dist/core/json.js.map +0 -1
- package/dist/core/result.js.map +0 -1
- package/dist/core/result.test.js.map +0 -1
- package/dist/core/retry.js.map +0 -1
- package/dist/core/retry.test.d.ts +0 -2
- package/dist/core/retry.test.d.ts.map +0 -1
- package/dist/core/retry.test.js +0 -36
- package/dist/core/retry.test.js.map +0 -1
- package/dist/core/schema.js.map +0 -1
- package/dist/core/step.js.map +0 -1
- package/dist/core/step.test.js.map +0 -1
- package/dist/core/workflow.js.map +0 -1
- package/dist/core/workflow.test.js.map +0 -1
- package/dist/execution/execution.d.ts.map +0 -1
- package/dist/execution/execution.js.map +0 -1
- package/dist/execution/execution.test.d.ts.map +0 -1
- package/dist/execution/execution.test.js.map +0 -1
- package/dist/global.d.ts +0 -62
- package/dist/global.d.ts.map +0 -1
- package/dist/global.js +0 -78
- package/dist/global.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/sdk/sdk.d.ts +0 -182
- package/dist/sdk/sdk.d.ts.map +0 -1
- package/dist/sdk/sdk.js.map +0 -1
- package/dist/sdk/sdk.test.d.ts +0 -2
- package/dist/sdk/sdk.test.d.ts.map +0 -1
- package/dist/sdk/sdk.test.js.map +0 -1
- package/dist/worker/worker.d.ts.map +0 -1
- package/dist/worker/worker.js.map +0 -1
- package/dist/worker/worker.test.js.map +0 -1
- /package/dist/{execution/execution.test.d.ts → execution.test.d.ts} +0 -0
- /package/dist/{worker/worker.test.d.ts → worker.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openworkflow.d.ts","sourceRoot":"","sources":["../../bin/openworkflow.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* This script is a zero-dependency entrypoint for the OpenWorkflow CLI. It
|
|
4
|
+
* allows users to run `npx openworkflow <command>` without requiring the core
|
|
5
|
+
* 'openworkflow' package to have any production dependencies (like commander,
|
|
6
|
+
* consola, etc).
|
|
7
|
+
*
|
|
8
|
+
* Note: This file is transpiled to `./dist/bin/openworkflow.js`. Relative paths
|
|
9
|
+
* below are calculated based on that runtime location.
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
16
|
+
// get the version of the current package to ensure we call the matching CLI
|
|
17
|
+
const packageJsonPath = path.join(__dirname, "../../package.json");
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
19
|
+
const version = packageJson.version;
|
|
20
|
+
// capture all args passed to npx openworkflow (e.g., "init", "worker start")
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
// build the command, using tsx to run local TypeScript CLI if in dev and
|
|
23
|
+
// otherwise using npx to run the published CLI package
|
|
24
|
+
const cliScriptFilePath = path.resolve(__dirname, "../../../cli/cli.ts");
|
|
25
|
+
const isMonorepo = existsSync(cliScriptFilePath);
|
|
26
|
+
if (isMonorepo) {
|
|
27
|
+
console.log("⚠️ Running OpenWorkflow CLI from local source (monorepo development mode)\n");
|
|
28
|
+
}
|
|
29
|
+
const command = isMonorepo
|
|
30
|
+
? // `npx tsx ../../../cli/cli.ts ...args`
|
|
31
|
+
["tsx", cliScriptFilePath, ...args]
|
|
32
|
+
: // `npx -y @openworkflow/cli@<version> ...args`
|
|
33
|
+
// uses -y to skip the "Need to install @openworkflow/cli" prompt
|
|
34
|
+
["-y", `@openworkflow/cli@${version}`, ...args];
|
|
35
|
+
// spawn the CLI the command to run the actual CLI package
|
|
36
|
+
const result = spawnSync(
|
|
37
|
+
// eslint-disable-next-line sonarjs/no-os-command-from-path
|
|
38
|
+
"npx", command, {
|
|
39
|
+
stdio: "inherit",
|
|
40
|
+
shell: true,
|
|
41
|
+
});
|
|
42
|
+
// exit with the same status code as the CLI
|
|
43
|
+
process.exit(result.status ?? 0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chaos.test.d.ts","sourceRoot":"","sources":["../chaos.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { OpenWorkflow } from "./client.js";
|
|
2
|
+
import { BackendPostgres } from "./postgres.js";
|
|
3
|
+
import { DEFAULT_POSTGRES_URL } from "./postgres/postgres.js";
|
|
4
|
+
import { randomInt, randomUUID } from "node:crypto";
|
|
5
|
+
import { describe, expect, test } from "vitest";
|
|
6
|
+
const TOTAL_STEPS = 50;
|
|
7
|
+
const WORKER_COUNT = 3;
|
|
8
|
+
const WORKER_CONCURRENCY = 2;
|
|
9
|
+
const STEP_DURATION_MS = 25;
|
|
10
|
+
const CHAOS_DURATION_MS = 5000;
|
|
11
|
+
const CHAOS_INTERVAL_MS = 200;
|
|
12
|
+
const TEST_TIMEOUT_MS = 30_000;
|
|
13
|
+
describe("chaos test", () => {
|
|
14
|
+
test("workflow completes despite random worker deaths", async () => {
|
|
15
|
+
const backend = await createBackend();
|
|
16
|
+
const client = new OpenWorkflow({ backend });
|
|
17
|
+
const workflow = client.defineWorkflow({ name: "chaos-workflow" }, async ({ step }) => {
|
|
18
|
+
const results = [];
|
|
19
|
+
for (let i = 0; i < TOTAL_STEPS; i++) {
|
|
20
|
+
const stepName = `step-${i.toString()}`;
|
|
21
|
+
const result = await step.run({ name: stepName }, async () => {
|
|
22
|
+
await sleep(STEP_DURATION_MS); // fake work
|
|
23
|
+
return i;
|
|
24
|
+
});
|
|
25
|
+
results.push(result);
|
|
26
|
+
}
|
|
27
|
+
return results;
|
|
28
|
+
});
|
|
29
|
+
const workers = await Promise.all(Array.from({ length: WORKER_COUNT }, () => createAndStartWorker(client)));
|
|
30
|
+
const handle = await workflow.run();
|
|
31
|
+
let workflowCompleted = false;
|
|
32
|
+
let chaosTask = null;
|
|
33
|
+
try {
|
|
34
|
+
chaosTask = runChaosTest({
|
|
35
|
+
client,
|
|
36
|
+
workers,
|
|
37
|
+
durationMs: CHAOS_DURATION_MS,
|
|
38
|
+
intervalMs: CHAOS_INTERVAL_MS,
|
|
39
|
+
shouldStop: () => workflowCompleted,
|
|
40
|
+
});
|
|
41
|
+
const result = await handle.result();
|
|
42
|
+
workflowCompleted = true;
|
|
43
|
+
const restarts = await chaosTask;
|
|
44
|
+
expect(result).toHaveLength(TOTAL_STEPS);
|
|
45
|
+
expect(result[TOTAL_STEPS - 1]).toBe(TOTAL_STEPS - 1);
|
|
46
|
+
expect(restarts).toBeGreaterThan(0);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
workflowCompleted = true;
|
|
50
|
+
if (chaosTask)
|
|
51
|
+
await chaosTask;
|
|
52
|
+
await Promise.all(workers.map((worker) => worker.stop()));
|
|
53
|
+
await backend.stop();
|
|
54
|
+
}
|
|
55
|
+
}, TEST_TIMEOUT_MS);
|
|
56
|
+
});
|
|
57
|
+
async function runChaosTest(params) {
|
|
58
|
+
const { client, workers, durationMs, intervalMs, shouldStop } = params;
|
|
59
|
+
const chaosEndsAt = Date.now() + durationMs;
|
|
60
|
+
let restartCount = 0;
|
|
61
|
+
while (Date.now() < chaosEndsAt && !shouldStop()) {
|
|
62
|
+
await sleep(intervalMs);
|
|
63
|
+
if (workers.length === 0) {
|
|
64
|
+
workers.push(await createAndStartWorker(client));
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const index = randomInt(workers.length);
|
|
68
|
+
const victim = workers.splice(index, 1)[0];
|
|
69
|
+
await victim?.stop();
|
|
70
|
+
const replacement = await createAndStartWorker(client);
|
|
71
|
+
workers.push(replacement);
|
|
72
|
+
restartCount++;
|
|
73
|
+
}
|
|
74
|
+
return restartCount;
|
|
75
|
+
}
|
|
76
|
+
async function createBackend() {
|
|
77
|
+
return await BackendPostgres.connect(DEFAULT_POSTGRES_URL, {
|
|
78
|
+
namespaceId: randomUUID(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function createAndStartWorker(client) {
|
|
82
|
+
const worker = client.newWorker({ concurrency: WORKER_CONCURRENCY });
|
|
83
|
+
await worker.start();
|
|
84
|
+
return worker;
|
|
85
|
+
}
|
|
86
|
+
function sleep(ms) {
|
|
87
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Backend } from "./backend.js";
|
|
2
|
+
import type { StandardSchemaV1 } from "./core/schema.js";
|
|
3
|
+
import type { SchemaInput, SchemaOutput, WorkflowRun } from "./core/workflow.js";
|
|
4
|
+
import type { WorkflowFunction } from "./execution.js";
|
|
5
|
+
import { Worker } from "./worker.js";
|
|
6
|
+
import { type Workflow, type WorkflowSpec } from "./workflow.js";
|
|
7
|
+
type WorkflowHandlerInput<TSchema, Input> = SchemaOutput<TSchema, Input>;
|
|
8
|
+
type WorkflowRunInput<TSchema, Input> = SchemaInput<TSchema, Input>;
|
|
9
|
+
/**
|
|
10
|
+
* Options for the OpenWorkflow client.
|
|
11
|
+
*/
|
|
12
|
+
export interface OpenWorkflowOptions {
|
|
13
|
+
backend: Backend;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Client used to register workflows and start runs.
|
|
17
|
+
*/
|
|
18
|
+
export declare class OpenWorkflow {
|
|
19
|
+
private backend;
|
|
20
|
+
private registry;
|
|
21
|
+
constructor(options: OpenWorkflowOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Create a new Worker with this client's backend and workflows.
|
|
24
|
+
* @param options - Worker options
|
|
25
|
+
* @param options.concurrency - Max concurrent workflow runs
|
|
26
|
+
* @returns Worker instance
|
|
27
|
+
*/
|
|
28
|
+
newWorker(options?: {
|
|
29
|
+
concurrency?: number | undefined;
|
|
30
|
+
}): Worker;
|
|
31
|
+
/**
|
|
32
|
+
* Provide the implementation for a declared workflow. This links the workflow
|
|
33
|
+
* specification to its execution logic and registers it with this
|
|
34
|
+
* OpenWorkflow instance for worker execution.
|
|
35
|
+
* @param spec - Workflow spec
|
|
36
|
+
* @param fn - Workflow implementation
|
|
37
|
+
*/
|
|
38
|
+
implementWorkflow<Input, Output, RunInput = Input>(spec: WorkflowSpec<Input, Output, RunInput>, fn: WorkflowFunction<Input, Output>): void;
|
|
39
|
+
/**
|
|
40
|
+
* Run a workflow from its specification. This is the primary way to schedule
|
|
41
|
+
* a workflow using only its WorkflowSpec.
|
|
42
|
+
* @param spec - Workflow spec
|
|
43
|
+
* @param input - Workflow input
|
|
44
|
+
* @param options - Run options
|
|
45
|
+
* @returns Handle for awaiting the result
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const handle = await ow.runWorkflow(emailWorkflow, { to: 'user@example.com' });
|
|
49
|
+
* const result = await handle.result();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
runWorkflow<Input, Output, RunInput = Input>(spec: WorkflowSpec<Input, Output, RunInput>, input?: RunInput, options?: WorkflowRunOptions): Promise<WorkflowRunHandle<Output>>;
|
|
53
|
+
/**
|
|
54
|
+
* Define and register a new workflow.
|
|
55
|
+
* @param spec - Workflow spec
|
|
56
|
+
* @param fn - Workflow implementation
|
|
57
|
+
* @returns Runnable workflow
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const workflow = ow.defineWorkflow(
|
|
61
|
+
* { name: 'my-workflow' },
|
|
62
|
+
* async ({ input, step }) => {
|
|
63
|
+
* // workflow implementation
|
|
64
|
+
* },
|
|
65
|
+
* );
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
defineWorkflow<Input, Output, TSchema extends StandardSchemaV1 | undefined = undefined>(spec: WorkflowSpec<WorkflowHandlerInput<TSchema, Input>, Output, WorkflowRunInput<TSchema, Input>>, fn: WorkflowFunction<WorkflowHandlerInput<TSchema, Input>, Output>): RunnableWorkflow<WorkflowHandlerInput<TSchema, Input>, Output, WorkflowRunInput<TSchema, Input>>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A fully defined workflow with its implementation. This class is returned by
|
|
72
|
+
* `client.defineWorkflow` and provides the `.run()` method for scheduling
|
|
73
|
+
* workflow runs.
|
|
74
|
+
*/
|
|
75
|
+
export declare class RunnableWorkflow<Input, Output, RunInput = Input> {
|
|
76
|
+
private readonly ow;
|
|
77
|
+
readonly workflow: Workflow<Input, Output, RunInput>;
|
|
78
|
+
constructor(ow: OpenWorkflow, workflow: Workflow<Input, Output, RunInput>);
|
|
79
|
+
/**
|
|
80
|
+
* Starts a new workflow run.
|
|
81
|
+
* @param input - Workflow input
|
|
82
|
+
* @param options - Run options
|
|
83
|
+
* @returns Workflow run handle
|
|
84
|
+
*/
|
|
85
|
+
run(input?: RunInput, options?: WorkflowRunOptions): Promise<WorkflowRunHandle<Output>>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Options for creating a new workflow run from a runnable workflow when calling
|
|
89
|
+
* `workflowDef.run()`.
|
|
90
|
+
*/
|
|
91
|
+
export interface WorkflowRunOptions {
|
|
92
|
+
/**
|
|
93
|
+
* Set a deadline for the workflow run. If the workflow exceeds this deadline,
|
|
94
|
+
* it will be marked as failed.
|
|
95
|
+
*/
|
|
96
|
+
deadlineAt?: Date;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for WorkflowHandle.
|
|
100
|
+
*/
|
|
101
|
+
export interface WorkflowHandleOptions {
|
|
102
|
+
backend: Backend;
|
|
103
|
+
workflowRun: WorkflowRun;
|
|
104
|
+
resultPollIntervalMs: number;
|
|
105
|
+
resultTimeoutMs: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Options for result() on a WorkflowRunHandle.
|
|
109
|
+
*/
|
|
110
|
+
export interface WorkflowRunHandleResultOptions {
|
|
111
|
+
/**
|
|
112
|
+
* Time to wait for a workflow run to complete. Throws an error if the timeout
|
|
113
|
+
* is exceeded.
|
|
114
|
+
* @default 300000 (5 minutes)
|
|
115
|
+
*/
|
|
116
|
+
timeoutMs?: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Represents a started workflow run and provides methods to await its result.
|
|
120
|
+
* Returned from `workflowDef.run()`.
|
|
121
|
+
*/
|
|
122
|
+
export declare class WorkflowRunHandle<Output> {
|
|
123
|
+
private backend;
|
|
124
|
+
readonly workflowRun: WorkflowRun;
|
|
125
|
+
private resultPollIntervalMs;
|
|
126
|
+
private resultTimeoutMs;
|
|
127
|
+
constructor(options: WorkflowHandleOptions);
|
|
128
|
+
/**
|
|
129
|
+
* Waits for the workflow run to complete and returns the result.
|
|
130
|
+
* @param options - Options for waiting for the result
|
|
131
|
+
* @returns Workflow output
|
|
132
|
+
*/
|
|
133
|
+
result(options?: WorkflowRunHandleResultOptions): Promise<Output>;
|
|
134
|
+
/**
|
|
135
|
+
* Cancels the workflow run. Only workflows in pending, running, or sleeping
|
|
136
|
+
* status can be canceled.
|
|
137
|
+
*/
|
|
138
|
+
cancel(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
export {};
|
|
141
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { validateInput } from "
|
|
2
|
-
import {
|
|
1
|
+
import { validateInput } from "./core/workflow.js";
|
|
2
|
+
import { WorkflowRegistry } from "./registry.js";
|
|
3
|
+
import { Worker } from "./worker.js";
|
|
4
|
+
import { defineWorkflow, } from "./workflow.js";
|
|
3
5
|
const DEFAULT_RESULT_POLL_INTERVAL_MS = 1000; // 1s
|
|
4
6
|
const DEFAULT_RESULT_TIMEOUT_MS = 5 * 60 * 1000; // 5m
|
|
5
7
|
/**
|
|
@@ -7,17 +9,20 @@ const DEFAULT_RESULT_TIMEOUT_MS = 5 * 60 * 1000; // 5m
|
|
|
7
9
|
*/
|
|
8
10
|
export class OpenWorkflow {
|
|
9
11
|
backend;
|
|
10
|
-
|
|
12
|
+
registry = new WorkflowRegistry();
|
|
11
13
|
constructor(options) {
|
|
12
14
|
this.backend = options.backend;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Create a new Worker with this client's backend and workflows.
|
|
18
|
+
* @param options - Worker options
|
|
19
|
+
* @param options.concurrency - Max concurrent workflow runs
|
|
20
|
+
* @returns Worker instance
|
|
16
21
|
*/
|
|
17
22
|
newWorker(options) {
|
|
18
23
|
return new Worker({
|
|
19
24
|
backend: this.backend,
|
|
20
|
-
workflows: this.
|
|
25
|
+
workflows: this.registry.getAll(),
|
|
21
26
|
concurrency: options?.concurrency,
|
|
22
27
|
});
|
|
23
28
|
}
|
|
@@ -25,20 +30,23 @@ export class OpenWorkflow {
|
|
|
25
30
|
* Provide the implementation for a declared workflow. This links the workflow
|
|
26
31
|
* specification to its execution logic and registers it with this
|
|
27
32
|
* OpenWorkflow instance for worker execution.
|
|
33
|
+
* @param spec - Workflow spec
|
|
34
|
+
* @param fn - Workflow implementation
|
|
28
35
|
*/
|
|
29
36
|
implementWorkflow(spec, fn) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const definition = new WorkflowDefinition(this, spec, fn);
|
|
36
|
-
this.registeredWorkflows.set(key, definition);
|
|
37
|
+
const workflow = {
|
|
38
|
+
spec,
|
|
39
|
+
fn,
|
|
40
|
+
};
|
|
41
|
+
this.registry.register(workflow);
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* Run a workflow from its specification. This is the primary way to schedule
|
|
40
45
|
* a workflow using only its WorkflowSpec.
|
|
41
|
-
*
|
|
46
|
+
* @param spec - Workflow spec
|
|
47
|
+
* @param input - Workflow input
|
|
48
|
+
* @param options - Run options
|
|
49
|
+
* @returns Handle for awaiting the result
|
|
42
50
|
* @example
|
|
43
51
|
* ```ts
|
|
44
52
|
* const handle = await ow.runWorkflow(emailWorkflow, { to: 'user@example.com' });
|
|
@@ -53,7 +61,7 @@ export class OpenWorkflow {
|
|
|
53
61
|
const parsedInput = validationResult.value;
|
|
54
62
|
const workflowRun = await this.backend.createWorkflowRun({
|
|
55
63
|
workflowName: spec.name,
|
|
56
|
-
version: spec.version,
|
|
64
|
+
version: spec.version ?? null,
|
|
57
65
|
idempotencyKey: null,
|
|
58
66
|
config: {},
|
|
59
67
|
context: null,
|
|
@@ -70,12 +78,9 @@ export class OpenWorkflow {
|
|
|
70
78
|
}
|
|
71
79
|
/**
|
|
72
80
|
* Define and register a new workflow.
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* separate declaration from implementation, consider using those methods
|
|
77
|
-
* separately.
|
|
78
|
-
*
|
|
81
|
+
* @param spec - Workflow spec
|
|
82
|
+
* @param fn - Workflow implementation
|
|
83
|
+
* @returns Runnable workflow
|
|
79
84
|
* @example
|
|
80
85
|
* ```ts
|
|
81
86
|
* const workflow = ow.defineWorkflow(
|
|
@@ -86,67 +91,32 @@ export class OpenWorkflow {
|
|
|
86
91
|
* );
|
|
87
92
|
* ```
|
|
88
93
|
*/
|
|
89
|
-
defineWorkflow(
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
this.registeredWorkflows.set(key, definition);
|
|
94
|
-
return definition;
|
|
94
|
+
defineWorkflow(spec, fn) {
|
|
95
|
+
const workflow = defineWorkflow(spec, fn);
|
|
96
|
+
this.registry.register(workflow);
|
|
97
|
+
return new RunnableWorkflow(this, workflow);
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Declare a workflow without providing its implementation (which is provided
|
|
99
|
-
* separately via `implementWorkflow`). Returns a lightweight WorkflowSpec
|
|
100
|
-
* that can be used to schedule workflow runs.
|
|
101
|
-
*
|
|
102
|
-
* @example
|
|
103
|
-
* ```ts
|
|
104
|
-
* export const emailWorkflow = declareWorkflow({
|
|
105
|
-
* name: 'send-email',
|
|
106
|
-
* schema: z.object({ to: z.string().email() }),
|
|
107
|
-
* });
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
export function declareWorkflow(config) {
|
|
111
|
-
return {
|
|
112
|
-
name: config.name,
|
|
113
|
-
version: config.version ?? null,
|
|
114
|
-
schema: config.schema ?? null,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Generate a composite key for workflow registration that includes both name
|
|
119
|
-
* and version.
|
|
120
|
-
*
|
|
121
|
-
* @param name - The workflow name
|
|
122
|
-
* @param version - The workflow version (null for unversioned)
|
|
123
|
-
* @returns A composite key string
|
|
124
|
-
*/
|
|
125
|
-
export function getWorkflowKey(name, version) {
|
|
126
|
-
return version ? `${name}@${version}` : name;
|
|
127
|
-
}
|
|
128
|
-
//
|
|
129
|
-
// --- Workflow Definition
|
|
130
|
-
//
|
|
131
100
|
/**
|
|
132
101
|
* A fully defined workflow with its implementation. This class is returned by
|
|
133
|
-
* `defineWorkflow` and provides the `.run()` method for scheduling
|
|
134
|
-
* runs.
|
|
102
|
+
* `client.defineWorkflow` and provides the `.run()` method for scheduling
|
|
103
|
+
* workflow runs.
|
|
135
104
|
*/
|
|
136
|
-
export class
|
|
105
|
+
export class RunnableWorkflow {
|
|
137
106
|
ow;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
constructor(ow, spec, fn) {
|
|
107
|
+
workflow;
|
|
108
|
+
constructor(ow, workflow) {
|
|
141
109
|
this.ow = ow;
|
|
142
|
-
this.
|
|
143
|
-
this.fn = fn;
|
|
110
|
+
this.workflow = workflow;
|
|
144
111
|
}
|
|
145
112
|
/**
|
|
146
113
|
* Starts a new workflow run.
|
|
114
|
+
* @param input - Workflow input
|
|
115
|
+
* @param options - Run options
|
|
116
|
+
* @returns Workflow run handle
|
|
147
117
|
*/
|
|
148
118
|
async run(input, options) {
|
|
149
|
-
return this.ow.runWorkflow(this.spec, input, options);
|
|
119
|
+
return this.ow.runWorkflow(this.workflow.spec, input, options);
|
|
150
120
|
}
|
|
151
121
|
}
|
|
152
122
|
/**
|
|
@@ -166,9 +136,12 @@ export class WorkflowRunHandle {
|
|
|
166
136
|
}
|
|
167
137
|
/**
|
|
168
138
|
* Waits for the workflow run to complete and returns the result.
|
|
139
|
+
* @param options - Options for waiting for the result
|
|
140
|
+
* @returns Workflow output
|
|
169
141
|
*/
|
|
170
|
-
async result() {
|
|
142
|
+
async result(options) {
|
|
171
143
|
const start = Date.now();
|
|
144
|
+
const timeout = options?.timeoutMs ?? this.resultTimeoutMs;
|
|
172
145
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
173
146
|
while (true) {
|
|
174
147
|
const latest = await this.backend.getWorkflowRun({
|
|
@@ -187,7 +160,7 @@ export class WorkflowRunHandle {
|
|
|
187
160
|
if (latest.status === "canceled") {
|
|
188
161
|
throw new Error(`Workflow ${this.workflowRun.workflowName} was canceled`);
|
|
189
162
|
}
|
|
190
|
-
if (Date.now() - start >
|
|
163
|
+
if (Date.now() - start > timeout) {
|
|
191
164
|
throw new Error(`Timed out waiting for workflow run ${this.workflowRun.id} to finish`);
|
|
192
165
|
}
|
|
193
166
|
await new Promise((resolve) => {
|
|
@@ -205,4 +178,3 @@ export class WorkflowRunHandle {
|
|
|
205
178
|
});
|
|
206
179
|
}
|
|
207
180
|
}
|
|
208
|
-
//# sourceMappingURL=sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../client.test.ts"],"names":[],"mappings":""}
|