openworkflow 0.4.1 → 0.5.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 +53 -48
- package/dist/backend-postgres/index.d.ts +44 -0
- package/dist/backend-postgres/index.d.ts.map +1 -0
- package/dist/backend-postgres/index.js +535 -0
- package/dist/backend-postgres/index.js.map +1 -0
- package/dist/backend-postgres/postgres.d.ts +42 -0
- package/dist/backend-postgres/postgres.d.ts.map +1 -0
- package/dist/backend-postgres/postgres.js +234 -0
- package/dist/backend-postgres/postgres.js.map +1 -0
- package/dist/backend-sqlite/index.d.ts +41 -1
- package/dist/backend-sqlite/index.d.ts.map +1 -1
- package/dist/backend-sqlite/index.js +654 -1
- package/dist/backend-sqlite/index.js.map +1 -1
- package/dist/backend-sqlite/sqlite.d.ts +18 -2
- package/dist/backend-sqlite/sqlite.d.ts.map +1 -1
- package/dist/backend-sqlite/sqlite.js +20 -2
- package/dist/backend-sqlite/sqlite.js.map +1 -1
- package/dist/{core/backend.d.ts → backend.d.ts} +7 -5
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js.map +1 -0
- package/dist/bin/openworkflow.d.ts +3 -0
- package/dist/bin/openworkflow.d.ts.map +1 -0
- package/dist/bin/openworkflow.js +44 -0
- package/dist/bin/openworkflow.js.map +1 -0
- package/dist/client.d.ts +141 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/{sdk/sdk.js → client.js} +44 -71
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +27 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +44 -36
- package/dist/config.js.map +1 -1
- package/dist/core/duration.d.ts +4 -2
- package/dist/core/duration.d.ts.map +1 -1
- package/dist/core/duration.js +3 -1
- package/dist/core/duration.js.map +1 -1
- package/dist/core/error.d.ts +14 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +18 -0
- package/dist/core/error.js.map +1 -0
- package/dist/core/result.d.ts +14 -4
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +10 -0
- package/dist/core/result.js.map +1 -1
- package/dist/core/retry.d.ts +0 -9
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +0 -14
- package/dist/core/retry.js.map +1 -1
- package/dist/core/step.d.ts +1 -32
- package/dist/core/step.d.ts.map +1 -1
- package/dist/core/step.js +0 -35
- package/dist/core/step.js.map +1 -1
- package/dist/core/workflow.d.ts +2 -47
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/core/workflow.js +0 -44
- package/dist/core/workflow.js.map +1 -1
- 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 -4
- package/dist/execution.js.map +1 -0
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +8 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +5 -0
- package/dist/internal.js.map +1 -0
- package/dist/pg/backend.d.ts +42 -0
- package/dist/pg/backend.d.ts.map +1 -0
- package/dist/pg/backend.js +534 -0
- package/dist/pg/backend.js.map +1 -0
- package/dist/pg/index.d.ts +3 -0
- package/dist/pg/index.d.ts.map +1 -0
- package/dist/pg/index.js +3 -0
- package/dist/pg/index.js.map +1 -0
- package/dist/pg/postgres.d.ts +42 -0
- package/dist/pg/postgres.d.ts.map +1 -0
- package/dist/pg/postgres.js +234 -0
- package/dist/pg/postgres.js.map +1 -0
- package/dist/pg/scripts/db-migrate.d.ts +2 -0
- package/dist/pg/scripts/db-migrate.d.ts.map +1 -0
- package/dist/pg/scripts/db-migrate.js +5 -0
- package/dist/pg/scripts/db-migrate.js.map +1 -0
- package/dist/pg/scripts/db-reset.d.ts +2 -0
- package/dist/pg/scripts/db-reset.d.ts.map +1 -0
- package/dist/pg/scripts/db-reset.js +6 -0
- package/dist/pg/scripts/db-reset.js.map +1 -0
- package/dist/pg/scripts/squawk.d.ts +2 -0
- package/dist/pg/scripts/squawk.d.ts.map +1 -0
- package/dist/pg/scripts/squawk.js +17 -0
- package/dist/pg/scripts/squawk.js.map +1 -0
- package/dist/pg/vitest.global-setup.d.ts +3 -0
- package/dist/pg/vitest.global-setup.d.ts.map +1 -0
- package/dist/pg/vitest.global-setup.js +8 -0
- package/dist/pg/vitest.global-setup.js.map +1 -0
- package/dist/registry.d.ts +27 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +49 -0
- package/dist/registry.js.map +1 -0
- package/dist/{backend-sqlite → sqlite}/backend.d.ts +6 -4
- package/dist/sqlite/backend.d.ts.map +1 -0
- package/dist/{backend-sqlite → sqlite}/backend.js +31 -5
- package/dist/sqlite/backend.js.map +1 -0
- package/dist/sqlite/index.d.ts +3 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +3 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/sqlite.d.ts +61 -0
- package/dist/sqlite/sqlite.d.ts.map +1 -0
- package/dist/sqlite/sqlite.js +247 -0
- package/dist/sqlite/sqlite.js.map +1 -0
- package/dist/testing/backend.testsuite.d.ts +20 -0
- package/dist/testing/backend.testsuite.d.ts.map +1 -0
- package/dist/{core → testing}/backend.testsuite.js +186 -53
- package/dist/testing/backend.testsuite.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +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 -10
- package/dist/worker.js.map +1 -0
- package/dist/workflow.d.ts +60 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +49 -0
- package/dist/workflow.js.map +1 -0
- package/package.json +13 -3
- package/dist/backend-sqlite/backend.d.ts.map +0 -1
- package/dist/backend-sqlite/backend.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/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 +0 -960
- 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.test.d.ts +0 -2
- package/dist/core/duration.test.d.ts.map +0 -1
- package/dist/core/duration.test.js +0 -264
- package/dist/core/duration.test.js.map +0 -1
- package/dist/core/result.test.d.ts +0 -2
- package/dist/core/result.test.d.ts.map +0 -1
- package/dist/core/result.test.js +0 -11
- package/dist/core/result.test.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/step.test.d.ts +0 -2
- package/dist/core/step.test.d.ts.map +0 -1
- package/dist/core/step.test.js +0 -340
- package/dist/core/step.test.js.map +0 -1
- package/dist/core/workflow.test.d.ts +0 -2
- package/dist/core/workflow.test.d.ts.map +0 -1
- package/dist/core/workflow.test.js +0 -216
- 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 +0 -2
- package/dist/execution/execution.test.d.ts.map +0 -1
- package/dist/execution/execution.test.js +0 -382
- 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/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 +0 -195
- 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.d.ts +0 -2
- package/dist/worker/worker.test.d.ts.map +0 -1
- package/dist/worker/worker.test.js +0 -786
- package/dist/worker/worker.test.js.map +0 -1
- /package/dist/{core/backend.js → backend.js} +0 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
/**
|
|
4
|
+
* newDatabase creates a new SQLite database connection.
|
|
5
|
+
* @param path - Database file path (or ":memory:") for testing
|
|
6
|
+
* @returns SQLite database connection
|
|
7
|
+
*/
|
|
8
|
+
export function newDatabase(path) {
|
|
9
|
+
const db = new DatabaseSync(path);
|
|
10
|
+
// Only enable WAL mode for file-based databases
|
|
11
|
+
if (path !== ":memory:") {
|
|
12
|
+
db.exec("PRAGMA journal_mode = WAL;");
|
|
13
|
+
}
|
|
14
|
+
db.exec("PRAGMA foreign_keys = ON;");
|
|
15
|
+
return db;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* migrations returns the list of migration SQL statements.
|
|
19
|
+
* @returns Migration SQL statements
|
|
20
|
+
*/
|
|
21
|
+
export function migrations() {
|
|
22
|
+
return [
|
|
23
|
+
// 0 - init
|
|
24
|
+
`BEGIN;
|
|
25
|
+
|
|
26
|
+
CREATE TABLE IF NOT EXISTS "openworkflow_migrations" (
|
|
27
|
+
"version" INTEGER NOT NULL PRIMARY KEY
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
INSERT OR IGNORE INTO "openworkflow_migrations" ("version")
|
|
31
|
+
VALUES (0);
|
|
32
|
+
|
|
33
|
+
COMMIT;`,
|
|
34
|
+
// 1 - add workflow_runs and step_attempts tables
|
|
35
|
+
`BEGIN;
|
|
36
|
+
|
|
37
|
+
PRAGMA defer_foreign_keys = ON;
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS "workflow_runs" (
|
|
40
|
+
"namespace_id" TEXT NOT NULL,
|
|
41
|
+
"id" TEXT NOT NULL,
|
|
42
|
+
--
|
|
43
|
+
"workflow_name" TEXT NOT NULL,
|
|
44
|
+
"version" TEXT,
|
|
45
|
+
"status" TEXT NOT NULL,
|
|
46
|
+
"idempotency_key" TEXT,
|
|
47
|
+
"config" TEXT NOT NULL,
|
|
48
|
+
"context" TEXT,
|
|
49
|
+
"input" TEXT,
|
|
50
|
+
"output" TEXT,
|
|
51
|
+
"error" TEXT,
|
|
52
|
+
"attempts" INTEGER NOT NULL,
|
|
53
|
+
"parent_step_attempt_namespace_id" TEXT,
|
|
54
|
+
"parent_step_attempt_id" TEXT,
|
|
55
|
+
"worker_id" TEXT,
|
|
56
|
+
"available_at" TEXT,
|
|
57
|
+
"deadline_at" TEXT,
|
|
58
|
+
"started_at" TEXT,
|
|
59
|
+
"finished_at" TEXT,
|
|
60
|
+
"created_at" TEXT NOT NULL,
|
|
61
|
+
"updated_at" TEXT NOT NULL,
|
|
62
|
+
PRIMARY KEY ("namespace_id", "id"),
|
|
63
|
+
FOREIGN KEY ("parent_step_attempt_namespace_id", "parent_step_attempt_id")
|
|
64
|
+
REFERENCES "step_attempts" ("namespace_id", "id")
|
|
65
|
+
ON DELETE SET NULL
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS "step_attempts" (
|
|
69
|
+
"namespace_id" TEXT NOT NULL,
|
|
70
|
+
"id" TEXT NOT NULL,
|
|
71
|
+
--
|
|
72
|
+
"workflow_run_id" TEXT NOT NULL,
|
|
73
|
+
"step_name" TEXT NOT NULL,
|
|
74
|
+
"kind" TEXT NOT NULL,
|
|
75
|
+
"status" TEXT NOT NULL,
|
|
76
|
+
"config" TEXT NOT NULL,
|
|
77
|
+
"context" TEXT,
|
|
78
|
+
"output" TEXT,
|
|
79
|
+
"error" TEXT,
|
|
80
|
+
"child_workflow_run_namespace_id" TEXT,
|
|
81
|
+
"child_workflow_run_id" TEXT,
|
|
82
|
+
"started_at" TEXT,
|
|
83
|
+
"finished_at" TEXT,
|
|
84
|
+
"created_at" TEXT NOT NULL,
|
|
85
|
+
"updated_at" TEXT NOT NULL,
|
|
86
|
+
PRIMARY KEY ("namespace_id", "id"),
|
|
87
|
+
FOREIGN KEY ("namespace_id", "workflow_run_id")
|
|
88
|
+
REFERENCES "workflow_runs" ("namespace_id", "id")
|
|
89
|
+
ON DELETE CASCADE,
|
|
90
|
+
FOREIGN KEY ("child_workflow_run_namespace_id", "child_workflow_run_id")
|
|
91
|
+
REFERENCES "workflow_runs" ("namespace_id", "id")
|
|
92
|
+
ON DELETE SET NULL
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
INSERT OR IGNORE INTO "openworkflow_migrations" ("version")
|
|
96
|
+
VALUES (1);
|
|
97
|
+
|
|
98
|
+
COMMIT;`,
|
|
99
|
+
// 2 - foreign keys
|
|
100
|
+
`BEGIN;
|
|
101
|
+
|
|
102
|
+
-- Foreign keys are defined in migration 1 since SQLite requires them during table creation
|
|
103
|
+
-- This migration exists for version parity with PostgreSQL backend
|
|
104
|
+
|
|
105
|
+
INSERT OR IGNORE INTO "openworkflow_migrations" ("version")
|
|
106
|
+
VALUES (2);
|
|
107
|
+
|
|
108
|
+
COMMIT;`,
|
|
109
|
+
// 3 - validate foreign keys
|
|
110
|
+
`BEGIN;
|
|
111
|
+
|
|
112
|
+
-- Foreign key validation happens automatically in SQLite when PRAGMA foreign_keys = ON
|
|
113
|
+
-- This migration exists for version parity with PostgreSQL backend
|
|
114
|
+
|
|
115
|
+
INSERT OR IGNORE INTO "openworkflow_migrations" ("version")
|
|
116
|
+
VALUES (3);
|
|
117
|
+
|
|
118
|
+
COMMIT;`,
|
|
119
|
+
// 4 - indexes
|
|
120
|
+
`BEGIN;
|
|
121
|
+
|
|
122
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_status_available_at_created_at_idx"
|
|
123
|
+
ON "workflow_runs" ("namespace_id", "status", "available_at", "created_at");
|
|
124
|
+
|
|
125
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_workflow_name_idempotency_key_created_at_idx"
|
|
126
|
+
ON "workflow_runs" ("namespace_id", "workflow_name", "idempotency_key", "created_at");
|
|
127
|
+
|
|
128
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_parent_step_idx"
|
|
129
|
+
ON "workflow_runs" ("parent_step_attempt_namespace_id", "parent_step_attempt_id")
|
|
130
|
+
WHERE parent_step_attempt_namespace_id IS NOT NULL AND parent_step_attempt_id IS NOT NULL;
|
|
131
|
+
|
|
132
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_created_at_desc_idx"
|
|
133
|
+
ON "workflow_runs" ("namespace_id", "created_at" DESC);
|
|
134
|
+
|
|
135
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_status_created_at_desc_idx"
|
|
136
|
+
ON "workflow_runs" ("namespace_id", "status", "created_at" DESC);
|
|
137
|
+
|
|
138
|
+
CREATE INDEX IF NOT EXISTS "workflow_runs_workflow_name_status_created_at_desc_idx"
|
|
139
|
+
ON "workflow_runs" ("namespace_id", "workflow_name", "status", "created_at" DESC);
|
|
140
|
+
|
|
141
|
+
CREATE INDEX IF NOT EXISTS "step_attempts_workflow_run_created_at_idx"
|
|
142
|
+
ON "step_attempts" ("namespace_id", "workflow_run_id", "created_at");
|
|
143
|
+
|
|
144
|
+
CREATE INDEX IF NOT EXISTS "step_attempts_workflow_run_step_name_created_at_idx"
|
|
145
|
+
ON "step_attempts" ("namespace_id", "workflow_run_id", "step_name", "created_at");
|
|
146
|
+
|
|
147
|
+
CREATE INDEX IF NOT EXISTS "step_attempts_child_workflow_run_idx"
|
|
148
|
+
ON "step_attempts" ("child_workflow_run_namespace_id", "child_workflow_run_id")
|
|
149
|
+
WHERE child_workflow_run_namespace_id IS NOT NULL AND child_workflow_run_id IS NOT NULL;
|
|
150
|
+
|
|
151
|
+
INSERT OR IGNORE INTO "openworkflow_migrations" ("version")
|
|
152
|
+
VALUES (4);
|
|
153
|
+
|
|
154
|
+
COMMIT;`,
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* migrate applies pending migrations to the database. Does nothing if the
|
|
159
|
+
* database is already up to date.
|
|
160
|
+
* @param db - SQLite database
|
|
161
|
+
*/
|
|
162
|
+
export function migrate(db) {
|
|
163
|
+
const currentMigrationVersion = getCurrentMigrationVersion(db);
|
|
164
|
+
for (const [i, migrationSql] of migrations().entries()) {
|
|
165
|
+
if (i <= currentMigrationVersion)
|
|
166
|
+
continue; // already applied
|
|
167
|
+
db.exec(migrationSql);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* getCurrentMigrationVersion returns the current migration version of the database.
|
|
172
|
+
* @param db - SQLite database
|
|
173
|
+
* @returns Current migration version
|
|
174
|
+
*/
|
|
175
|
+
function getCurrentMigrationVersion(db) {
|
|
176
|
+
// check if migrations table exists
|
|
177
|
+
const existsStmt = db.prepare(`
|
|
178
|
+
SELECT COUNT(*) as count
|
|
179
|
+
FROM sqlite_master
|
|
180
|
+
WHERE type = 'table' AND name = 'openworkflow_migrations'
|
|
181
|
+
`);
|
|
182
|
+
const existsResult = existsStmt.get();
|
|
183
|
+
if (!existsResult || existsResult.count === 0)
|
|
184
|
+
return -1;
|
|
185
|
+
// get current version
|
|
186
|
+
const versionStmt = db.prepare(`SELECT MAX("version") AS "version" FROM "openworkflow_migrations";`);
|
|
187
|
+
const versionResult = versionStmt.get();
|
|
188
|
+
return versionResult?.version ?? -1;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Helper to generate UUIDs (SQLite doesn't have built-in UUID generation)
|
|
192
|
+
* @returns A UUID string
|
|
193
|
+
*/
|
|
194
|
+
export function generateUUID() {
|
|
195
|
+
return randomUUID();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Helper to get current timestamp in ISO8601 format
|
|
199
|
+
* @returns ISO8601 timestamp string
|
|
200
|
+
*/
|
|
201
|
+
export function now() {
|
|
202
|
+
return new Date().toISOString();
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Helper to add milliseconds to a date and return ISO8601 string
|
|
206
|
+
* @param date - ISO8601 date string
|
|
207
|
+
* @param ms - Milliseconds to add
|
|
208
|
+
* @returns Updated ISO8601 date string
|
|
209
|
+
*/
|
|
210
|
+
export function addMilliseconds(date, ms) {
|
|
211
|
+
const d = new Date(date);
|
|
212
|
+
d.setMilliseconds(d.getMilliseconds() + ms);
|
|
213
|
+
return d.toISOString();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Helper to serialize JSON for SQLite storage
|
|
217
|
+
* @param value - Value to serialize
|
|
218
|
+
* @returns JSON string or null
|
|
219
|
+
*/
|
|
220
|
+
export function toJSON(value) {
|
|
221
|
+
return value === null || value === undefined ? null : JSON.stringify(value);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Helper to deserialize JSON from SQLite storage
|
|
225
|
+
* @param value - JSON string or null
|
|
226
|
+
* @returns Parsed value
|
|
227
|
+
*/
|
|
228
|
+
export function fromJSON(value) {
|
|
229
|
+
return value === null ? null : JSON.parse(value);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Helper to convert Date to ISO8601 string for SQLite
|
|
233
|
+
* @param date - Date or null
|
|
234
|
+
* @returns ISO8601 date string or null
|
|
235
|
+
*/
|
|
236
|
+
export function toISO(date) {
|
|
237
|
+
return date ? date.toISOString() : null;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Helper to convert ISO8601 string from SQLite to Date
|
|
241
|
+
* @param dateStr - ISO8601 date string or null
|
|
242
|
+
* @returns Date or null
|
|
243
|
+
*/
|
|
244
|
+
export function fromISO(dateStr) {
|
|
245
|
+
return dateStr ? new Date(dateStr) : null;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../sqlite/sqlite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Backend } from "openworkflow/internal";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the Backend test suite.
|
|
4
|
+
*/
|
|
5
|
+
export interface TestBackendOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new isolated Backend instance.
|
|
8
|
+
*/
|
|
9
|
+
setup: () => Promise<Backend>;
|
|
10
|
+
/**
|
|
11
|
+
* Cleans up a Backend instance.
|
|
12
|
+
*/
|
|
13
|
+
teardown: (backend: Backend) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs the Backend test suite.
|
|
17
|
+
* @param options - Test suite options
|
|
18
|
+
*/
|
|
19
|
+
export declare function testBackend(options: TestBackendOptions): void;
|
|
20
|
+
//# sourceMappingURL=backend.testsuite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.testsuite.d.ts","sourceRoot":"","sources":["../../testing/backend.testsuite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAA4B,MAAM,uBAAuB,CAAC;AAG/E;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA8sC7D"}
|
|
@@ -2,10 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
3
3
|
/**
|
|
4
4
|
* Runs the Backend test suite.
|
|
5
|
-
*
|
|
6
|
-
* This function wraps all the tests that verify a Backend implementation
|
|
7
|
-
* conforms to the Backend interface contract. It uses the setup function to
|
|
8
|
-
* create backend instances and the teardown function to clean them up.
|
|
5
|
+
* @param options - Test suite options
|
|
9
6
|
*/
|
|
10
7
|
export function testBackend(options) {
|
|
11
8
|
const { setup, teardown } = options;
|
|
@@ -90,7 +87,8 @@ export function testBackend(options) {
|
|
|
90
87
|
await sleep(10); // ensure timestamp difference
|
|
91
88
|
const second = await createPendingWorkflowRun(backend);
|
|
92
89
|
const listed = await backend.listWorkflowRuns({});
|
|
93
|
-
|
|
90
|
+
const listedIds = listed.data.map((run) => run.id);
|
|
91
|
+
expect(listedIds).toEqual([first.id, second.id]);
|
|
94
92
|
await teardown(backend);
|
|
95
93
|
});
|
|
96
94
|
test("paginates workflow runs", async () => {
|
|
@@ -146,6 +144,47 @@ export function testBackend(options) {
|
|
|
146
144
|
expect(listed.pagination.prev).toBeNull();
|
|
147
145
|
await teardown(backend);
|
|
148
146
|
});
|
|
147
|
+
test("paginates correctly with id as tiebreaker when multiple items have the same created_at timestamp", async () => {
|
|
148
|
+
const backend = await setup();
|
|
149
|
+
const runs = [];
|
|
150
|
+
for (let i = 0; i < 5; i++) {
|
|
151
|
+
runs.push(await createPendingWorkflowRun(backend));
|
|
152
|
+
}
|
|
153
|
+
runs.sort((a, b) => {
|
|
154
|
+
const timeDiff = a.createdAt.getTime() - b.createdAt.getTime();
|
|
155
|
+
if (timeDiff !== 0)
|
|
156
|
+
return timeDiff;
|
|
157
|
+
return a.id.localeCompare(b.id);
|
|
158
|
+
});
|
|
159
|
+
const page1 = await backend.listWorkflowRuns({ limit: 2 });
|
|
160
|
+
expect(page1.data).toHaveLength(2);
|
|
161
|
+
expect(page1.data[0]?.id).toBe(runs[0]?.id);
|
|
162
|
+
expect(page1.data[1]?.id).toBe(runs[1]?.id);
|
|
163
|
+
expect(page1.pagination.next).not.toBeNull();
|
|
164
|
+
const page2 = await backend.listWorkflowRuns({
|
|
165
|
+
limit: 2,
|
|
166
|
+
after: page1.pagination.next, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
167
|
+
});
|
|
168
|
+
expect(page2.data).toHaveLength(2);
|
|
169
|
+
expect(page2.data[0]?.id).toBe(runs[2]?.id);
|
|
170
|
+
expect(page2.data[1]?.id).toBe(runs[3]?.id);
|
|
171
|
+
expect(page2.pagination.next).not.toBeNull();
|
|
172
|
+
const page3 = await backend.listWorkflowRuns({
|
|
173
|
+
limit: 2,
|
|
174
|
+
after: page2.pagination.next, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
175
|
+
});
|
|
176
|
+
expect(page3.data).toHaveLength(1);
|
|
177
|
+
expect(page3.data[0]?.id).toBe(runs[4]?.id);
|
|
178
|
+
expect(page3.pagination.next).toBeNull();
|
|
179
|
+
const page2Back = await backend.listWorkflowRuns({
|
|
180
|
+
limit: 2,
|
|
181
|
+
before: page3.pagination.prev, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
182
|
+
});
|
|
183
|
+
expect(page2Back.data).toHaveLength(2);
|
|
184
|
+
expect(page2Back.data[0]?.id).toBe(runs[2]?.id);
|
|
185
|
+
expect(page2Back.data[1]?.id).toBe(runs[3]?.id);
|
|
186
|
+
await teardown(backend);
|
|
187
|
+
});
|
|
149
188
|
});
|
|
150
189
|
describe("claimWorkflowRun()", () => {
|
|
151
190
|
// because claims involve timing and leases, we create and teardown a new
|
|
@@ -169,7 +208,7 @@ export function testBackend(options) {
|
|
|
169
208
|
leaseDurationMs: 10,
|
|
170
209
|
});
|
|
171
210
|
expect(blocked).toBeNull();
|
|
172
|
-
await sleep(firstLeaseMs);
|
|
211
|
+
await sleep(firstLeaseMs + 5); // small buffer for timing variability
|
|
173
212
|
const reclaimed = await backend.claimWorkflowRun({
|
|
174
213
|
workerId: secondWorker,
|
|
175
214
|
leaseDurationMs: 10,
|
|
@@ -278,7 +317,7 @@ export function testBackend(options) {
|
|
|
278
317
|
await backend.failWorkflowRun({
|
|
279
318
|
workflowRunId: claimed.id,
|
|
280
319
|
workerId: claimed.workerId ?? "",
|
|
281
|
-
error:
|
|
320
|
+
error: { message: "failed" },
|
|
282
321
|
});
|
|
283
322
|
await expect(backend.sleepWorkflowRun({
|
|
284
323
|
workflowRunId: claimed.id,
|
|
@@ -344,6 +383,7 @@ export function testBackend(options) {
|
|
|
344
383
|
expect(failed.output).toBeNull();
|
|
345
384
|
expect(failed.finishedAt).toBeNull();
|
|
346
385
|
expect(failed.workerId).toBeNull();
|
|
386
|
+
expect(failed.startedAt).toBeNull(); // cleared on failure for retry
|
|
347
387
|
expect(failed.availableAt).not.toBeNull();
|
|
348
388
|
if (!failed.availableAt)
|
|
349
389
|
throw new Error("Expected availableAt");
|
|
@@ -351,7 +391,7 @@ export function testBackend(options) {
|
|
|
351
391
|
expect(delayMs).toBeGreaterThanOrEqual(900); // ~1s with some tolerance
|
|
352
392
|
expect(delayMs).toBeLessThan(1500);
|
|
353
393
|
});
|
|
354
|
-
test("reschedules with increasing backoff on multiple failures
|
|
394
|
+
test("reschedules with increasing backoff on multiple failures", async () => {
|
|
355
395
|
// this test needs isolated namespace
|
|
356
396
|
const backend = await setup();
|
|
357
397
|
await createPendingWorkflowRun(backend);
|
|
@@ -470,6 +510,7 @@ export function testBackend(options) {
|
|
|
470
510
|
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion,
|
|
471
511
|
output: { ok: true },
|
|
472
512
|
});
|
|
513
|
+
await sleep(10); // ensure timestamp difference
|
|
473
514
|
const second = await backend.createStepAttempt({
|
|
474
515
|
workflowRunId: claimed.id,
|
|
475
516
|
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
@@ -481,10 +522,8 @@ export function testBackend(options) {
|
|
|
481
522
|
const listed = await backend.listStepAttempts({
|
|
482
523
|
workflowRunId: claimed.id,
|
|
483
524
|
});
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
second.stepName,
|
|
487
|
-
]);
|
|
525
|
+
const listedStepNames = listed.data.map((step) => step.stepName);
|
|
526
|
+
expect(listedStepNames).toEqual([first.stepName, second.stepName]);
|
|
488
527
|
});
|
|
489
528
|
test("paginates step attempts", async () => {
|
|
490
529
|
const claimed = await createClaimedWorkflowRun(backend);
|
|
@@ -570,23 +609,6 @@ export function testBackend(options) {
|
|
|
570
609
|
expect(listed.pagination.prev).toBeNull();
|
|
571
610
|
});
|
|
572
611
|
});
|
|
573
|
-
describe("getStepAttempt() duplicate", () => {
|
|
574
|
-
test("returns a persisted step attempt", async () => {
|
|
575
|
-
const claimed = await createClaimedWorkflowRun(backend);
|
|
576
|
-
const created = await backend.createStepAttempt({
|
|
577
|
-
workflowRunId: claimed.id,
|
|
578
|
-
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
579
|
-
stepName: randomUUID(),
|
|
580
|
-
kind: "function",
|
|
581
|
-
config: {},
|
|
582
|
-
context: null,
|
|
583
|
-
});
|
|
584
|
-
const got = await backend.getStepAttempt({
|
|
585
|
-
stepAttemptId: created.id,
|
|
586
|
-
});
|
|
587
|
-
expect(got).toEqual(created);
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
612
|
describe("completeStepAttempt()", () => {
|
|
591
613
|
test("marks running step attempts as completed", async () => {
|
|
592
614
|
const claimed = await createClaimedWorkflowRun(backend);
|
|
@@ -617,6 +639,50 @@ export function testBackend(options) {
|
|
|
617
639
|
expect(fetched?.error).toBeNull();
|
|
618
640
|
expect(fetched?.finishedAt).not.toBeNull();
|
|
619
641
|
});
|
|
642
|
+
test("throws when workflow is not running", async () => {
|
|
643
|
+
const backend = await setup();
|
|
644
|
+
await createPendingWorkflowRun(backend);
|
|
645
|
+
// create a step attempt by first claiming the workflow
|
|
646
|
+
const claimed = await backend.claimWorkflowRun({
|
|
647
|
+
workerId: randomUUID(),
|
|
648
|
+
leaseDurationMs: 100,
|
|
649
|
+
});
|
|
650
|
+
if (!claimed)
|
|
651
|
+
throw new Error("Failed to claim workflow run");
|
|
652
|
+
const stepAttempt = await backend.createStepAttempt({
|
|
653
|
+
workflowRunId: claimed.id,
|
|
654
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
655
|
+
stepName: randomUUID(),
|
|
656
|
+
kind: "function",
|
|
657
|
+
config: {},
|
|
658
|
+
context: null,
|
|
659
|
+
});
|
|
660
|
+
// complete the workflow so it's no longer running
|
|
661
|
+
await backend.completeWorkflowRun({
|
|
662
|
+
workflowRunId: claimed.id,
|
|
663
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
664
|
+
output: null,
|
|
665
|
+
});
|
|
666
|
+
// try to complete the step attempt
|
|
667
|
+
await expect(backend.completeStepAttempt({
|
|
668
|
+
workflowRunId: claimed.id,
|
|
669
|
+
stepAttemptId: stepAttempt.id,
|
|
670
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
671
|
+
output: { foo: "bar" },
|
|
672
|
+
})).rejects.toThrow("Failed to mark step attempt completed");
|
|
673
|
+
await teardown(backend);
|
|
674
|
+
});
|
|
675
|
+
test("throws when step attempt does not exist", async () => {
|
|
676
|
+
const backend = await setup();
|
|
677
|
+
const claimed = await createClaimedWorkflowRun(backend);
|
|
678
|
+
await expect(backend.completeStepAttempt({
|
|
679
|
+
workflowRunId: claimed.id,
|
|
680
|
+
stepAttemptId: randomUUID(),
|
|
681
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
682
|
+
output: { foo: "bar" },
|
|
683
|
+
})).rejects.toThrow("Failed to mark step attempt completed");
|
|
684
|
+
await teardown(backend);
|
|
685
|
+
});
|
|
620
686
|
});
|
|
621
687
|
describe("failStepAttempt()", () => {
|
|
622
688
|
test("marks running step attempts as failed", async () => {
|
|
@@ -648,6 +714,50 @@ export function testBackend(options) {
|
|
|
648
714
|
expect(fetched?.output).toBeNull();
|
|
649
715
|
expect(fetched?.finishedAt).not.toBeNull();
|
|
650
716
|
});
|
|
717
|
+
test("throws when workflow is not running", async () => {
|
|
718
|
+
const backend = await setup();
|
|
719
|
+
await createPendingWorkflowRun(backend);
|
|
720
|
+
// create a step attempt by first claiming the workflow
|
|
721
|
+
const claimed = await backend.claimWorkflowRun({
|
|
722
|
+
workerId: randomUUID(),
|
|
723
|
+
leaseDurationMs: 100,
|
|
724
|
+
});
|
|
725
|
+
if (!claimed)
|
|
726
|
+
throw new Error("Failed to claim workflow run");
|
|
727
|
+
const stepAttempt = await backend.createStepAttempt({
|
|
728
|
+
workflowRunId: claimed.id,
|
|
729
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
730
|
+
stepName: randomUUID(),
|
|
731
|
+
kind: "function",
|
|
732
|
+
config: {},
|
|
733
|
+
context: null,
|
|
734
|
+
});
|
|
735
|
+
// complete the workflow so it's no longer running
|
|
736
|
+
await backend.completeWorkflowRun({
|
|
737
|
+
workflowRunId: claimed.id,
|
|
738
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
739
|
+
output: null,
|
|
740
|
+
});
|
|
741
|
+
// try to fail the step attempt
|
|
742
|
+
await expect(backend.failStepAttempt({
|
|
743
|
+
workflowRunId: claimed.id,
|
|
744
|
+
stepAttemptId: stepAttempt.id,
|
|
745
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
746
|
+
error: { message: "nope" },
|
|
747
|
+
})).rejects.toThrow("Failed to mark step attempt failed");
|
|
748
|
+
await teardown(backend);
|
|
749
|
+
});
|
|
750
|
+
test("throws when step attempt does not exist", async () => {
|
|
751
|
+
const backend = await setup();
|
|
752
|
+
const claimed = await createClaimedWorkflowRun(backend);
|
|
753
|
+
await expect(backend.failStepAttempt({
|
|
754
|
+
workflowRunId: claimed.id,
|
|
755
|
+
stepAttemptId: randomUUID(),
|
|
756
|
+
workerId: claimed.workerId, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
757
|
+
error: { message: "nope" },
|
|
758
|
+
})).rejects.toThrow("Failed to mark step attempt failed");
|
|
759
|
+
await teardown(backend);
|
|
760
|
+
});
|
|
651
761
|
});
|
|
652
762
|
describe("deadline_at", () => {
|
|
653
763
|
test("creates a workflow run with a deadline", async () => {
|
|
@@ -744,6 +854,7 @@ export function testBackend(options) {
|
|
|
744
854
|
expect(failed.status).toBe("failed");
|
|
745
855
|
expect(failed.availableAt).toBeNull();
|
|
746
856
|
expect(failed.finishedAt).not.toBeNull();
|
|
857
|
+
expect(failed.startedAt).toBeNull(); // cleared on permanent failure
|
|
747
858
|
await teardown(backend);
|
|
748
859
|
});
|
|
749
860
|
test("reschedules failed workflow runs if retry would complete before deadline", async () => {
|
|
@@ -916,42 +1027,64 @@ export function testBackend(options) {
|
|
|
916
1027
|
await teardown(backend);
|
|
917
1028
|
});
|
|
918
1029
|
});
|
|
919
|
-
// Helper function for creating workflow runs that uses the shared backend
|
|
920
|
-
async function createPendingWorkflowRun(b) {
|
|
921
|
-
return await b.createWorkflowRun({
|
|
922
|
-
workflowName: randomUUID(),
|
|
923
|
-
version: null,
|
|
924
|
-
idempotencyKey: null,
|
|
925
|
-
input: null,
|
|
926
|
-
config: {},
|
|
927
|
-
context: null,
|
|
928
|
-
availableAt: null,
|
|
929
|
-
deadlineAt: null,
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
async function createClaimedWorkflowRun(b) {
|
|
933
|
-
await createPendingWorkflowRun(b);
|
|
934
|
-
const claimed = await b.claimWorkflowRun({
|
|
935
|
-
workerId: randomUUID(),
|
|
936
|
-
leaseDurationMs: 100,
|
|
937
|
-
});
|
|
938
|
-
if (!claimed)
|
|
939
|
-
throw new Error("Failed to claim workflow run");
|
|
940
|
-
return claimed;
|
|
941
|
-
}
|
|
942
1030
|
});
|
|
943
1031
|
}
|
|
944
|
-
|
|
1032
|
+
/**
|
|
1033
|
+
* Create a pending workflow run for tests.
|
|
1034
|
+
* @param b - Backend
|
|
1035
|
+
* @returns Created workflow run
|
|
1036
|
+
*/
|
|
1037
|
+
async function createPendingWorkflowRun(b) {
|
|
1038
|
+
return await b.createWorkflowRun({
|
|
1039
|
+
workflowName: randomUUID(),
|
|
1040
|
+
version: null,
|
|
1041
|
+
idempotencyKey: null,
|
|
1042
|
+
input: null,
|
|
1043
|
+
config: {},
|
|
1044
|
+
context: null,
|
|
1045
|
+
availableAt: null,
|
|
1046
|
+
deadlineAt: null,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Create and claim a workflow run for tests.
|
|
1051
|
+
* @param b - Backend
|
|
1052
|
+
* @returns Claimed workflow run
|
|
1053
|
+
*/
|
|
1054
|
+
async function createClaimedWorkflowRun(b) {
|
|
1055
|
+
await createPendingWorkflowRun(b);
|
|
1056
|
+
const claimed = await b.claimWorkflowRun({
|
|
1057
|
+
workerId: randomUUID(),
|
|
1058
|
+
leaseDurationMs: 100,
|
|
1059
|
+
});
|
|
1060
|
+
if (!claimed)
|
|
1061
|
+
throw new Error("Failed to claim workflow run");
|
|
1062
|
+
return claimed;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Get delta in seconds from now.
|
|
1066
|
+
* @param date - Date to compare
|
|
1067
|
+
* @returns Delta in seconds
|
|
1068
|
+
*/
|
|
945
1069
|
function deltaSeconds(date) {
|
|
946
1070
|
if (!date)
|
|
947
1071
|
return Infinity;
|
|
948
1072
|
return Math.abs((Date.now() - date.getTime()) / 1000);
|
|
949
1073
|
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Create a Date one year in the future.
|
|
1076
|
+
* @returns Future Date
|
|
1077
|
+
*/
|
|
950
1078
|
function newDateInOneYear() {
|
|
951
1079
|
const d = new Date();
|
|
952
1080
|
d.setFullYear(d.getFullYear() + 1);
|
|
953
1081
|
return d;
|
|
954
1082
|
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Sleep for a given duration.
|
|
1085
|
+
* @param ms - Milliseconds to sleep
|
|
1086
|
+
* @returns Promise resolved after sleeping
|
|
1087
|
+
*/
|
|
955
1088
|
function sleep(ms) {
|
|
956
1089
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
957
1090
|
}
|