workerflow 0.1.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/.oxfmtrc.json +11 -0
- package/.oxlintrc.json +19 -0
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/demo/README.md +73 -0
- package/demo/index.html +13 -0
- package/demo/package.json +33 -0
- package/demo/public/vite.svg +1 -0
- package/demo/src/App.css +0 -0
- package/demo/src/App.tsx +9 -0
- package/demo/src/assets/Cloudflare_Logo.svg +51 -0
- package/demo/src/assets/react.svg +1 -0
- package/demo/src/index.css +1 -0
- package/demo/src/main.tsx +10 -0
- package/demo/tsconfig.app.json +28 -0
- package/demo/tsconfig.json +14 -0
- package/demo/tsconfig.node.json +25 -0
- package/demo/tsconfig.worker.json +13 -0
- package/demo/vite.config.ts +9 -0
- package/demo/worker/index.ts +16 -0
- package/demo/worker-configuration.d.ts +12851 -0
- package/demo/wrangler.jsonc +32 -0
- package/package.json +35 -0
- package/scripts/build.ts +29 -0
- package/src/brand.ts +2 -0
- package/src/definition.ts +413 -0
- package/src/index.ts +2 -0
- package/src/json.ts +14 -0
- package/src/migrations/0000_initial.ts +559 -0
- package/src/node-async-hooks.d.ts +16 -0
- package/src/runtime.ts +1705 -0
- package/test/env.d.ts +12 -0
- package/test/runtime.spec.ts +2837 -0
- package/test/tsconfig.json +11 -0
- package/test/worker.ts +21 -0
- package/tsconfig.json +23 -0
- package/turbo.json +18 -0
- package/vitest.config.mts +28 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
export default `
|
|
2
|
+
-- All timestamps are INTEGER milliseconds since Unix epoch.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE workflow_metadata (
|
|
5
|
+
id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1),
|
|
6
|
+
|
|
7
|
+
status TEXT NOT NULL CHECK (
|
|
8
|
+
status IN ('pending', 'running', 'paused', 'completed', 'failed', 'cancelled')
|
|
9
|
+
),
|
|
10
|
+
|
|
11
|
+
created_at INTEGER NOT NULL
|
|
12
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER))
|
|
13
|
+
CHECK (created_at >= 0),
|
|
14
|
+
|
|
15
|
+
updated_at INTEGER NOT NULL
|
|
16
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER)),
|
|
17
|
+
|
|
18
|
+
definition_version TEXT
|
|
19
|
+
CHECK (definition_version IS NULL OR length(definition_version) > 0),
|
|
20
|
+
definition_input TEXT
|
|
21
|
+
CHECK (definition_input IS NULL OR json_valid(definition_input)),
|
|
22
|
+
|
|
23
|
+
CHECK (updated_at >= created_at),
|
|
24
|
+
|
|
25
|
+
-- definition_version and definition_input must be set together or not set at all
|
|
26
|
+
CHECK (definition_version IS NOT NULL OR definition_input IS NULL),
|
|
27
|
+
|
|
28
|
+
-- definition must be pinned before running/paused/completing/failing; cancelled is always allowed
|
|
29
|
+
CHECK (status IN ('pending', 'cancelled') OR definition_version IS NOT NULL)
|
|
30
|
+
) STRICT;
|
|
31
|
+
|
|
32
|
+
CREATE TABLE steps (
|
|
33
|
+
id TEXT NOT NULL PRIMARY KEY CHECK (length(id) > 0),
|
|
34
|
+
type TEXT NOT NULL CHECK (type IN ('run', 'sleep', 'wait')),
|
|
35
|
+
state TEXT NOT NULL CHECK (state IN (
|
|
36
|
+
'pending',
|
|
37
|
+
'running',
|
|
38
|
+
'succeeded',
|
|
39
|
+
'failed',
|
|
40
|
+
'waiting',
|
|
41
|
+
'elapsed',
|
|
42
|
+
'satisfied',
|
|
43
|
+
'timed_out'
|
|
44
|
+
)),
|
|
45
|
+
created_at INTEGER NOT NULL
|
|
46
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER))
|
|
47
|
+
CHECK (created_at >= 0),
|
|
48
|
+
|
|
49
|
+
-- run-step fields
|
|
50
|
+
attempt_count INTEGER,
|
|
51
|
+
max_attempts INTEGER,
|
|
52
|
+
next_attempt_at INTEGER,
|
|
53
|
+
result TEXT,
|
|
54
|
+
error_message TEXT,
|
|
55
|
+
error_name TEXT,
|
|
56
|
+
|
|
57
|
+
-- sleep-step fields
|
|
58
|
+
wake_at INTEGER,
|
|
59
|
+
|
|
60
|
+
-- wait-step fields
|
|
61
|
+
event_name TEXT,
|
|
62
|
+
timeout_at INTEGER,
|
|
63
|
+
payload TEXT,
|
|
64
|
+
|
|
65
|
+
-- terminal timestamp
|
|
66
|
+
resolved_at INTEGER,
|
|
67
|
+
|
|
68
|
+
-- innermost enclosing run step when this row was created (nested run / sleep / wait under a run callback)
|
|
69
|
+
parent_step_id TEXT REFERENCES steps(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
|
70
|
+
|
|
71
|
+
CHECK (attempt_count IS NULL OR attempt_count >= 0),
|
|
72
|
+
CHECK (max_attempts IS NULL OR max_attempts >= 1),
|
|
73
|
+
CHECK (next_attempt_at IS NULL OR next_attempt_at >= 0),
|
|
74
|
+
CHECK (wake_at IS NULL OR wake_at >= 0),
|
|
75
|
+
CHECK (timeout_at IS NULL OR timeout_at >= 0),
|
|
76
|
+
CHECK (resolved_at IS NULL OR resolved_at >= created_at),
|
|
77
|
+
CHECK (error_name IS NULL OR length(error_name) > 0),
|
|
78
|
+
CHECK (event_name IS NULL OR length(event_name) > 0),
|
|
79
|
+
|
|
80
|
+
-- run steps may never exceed max_attempts
|
|
81
|
+
CHECK (
|
|
82
|
+
attempt_count IS NULL OR
|
|
83
|
+
max_attempts IS NULL OR
|
|
84
|
+
attempt_count <= max_attempts
|
|
85
|
+
),
|
|
86
|
+
|
|
87
|
+
CHECK (
|
|
88
|
+
(
|
|
89
|
+
type = 'run' AND
|
|
90
|
+
state = 'pending' AND
|
|
91
|
+
attempt_count IS NOT NULL AND attempt_count >= 0 AND
|
|
92
|
+
(max_attempts IS NULL OR max_attempts >= 1) AND
|
|
93
|
+
(max_attempts IS NULL OR attempt_count < max_attempts) AND
|
|
94
|
+
next_attempt_at IS NOT NULL AND
|
|
95
|
+
result IS NULL AND
|
|
96
|
+
error_message IS NULL AND
|
|
97
|
+
error_name IS NULL AND
|
|
98
|
+
wake_at IS NULL AND
|
|
99
|
+
event_name IS NULL AND
|
|
100
|
+
timeout_at IS NULL AND
|
|
101
|
+
payload IS NULL AND
|
|
102
|
+
resolved_at IS NULL
|
|
103
|
+
)
|
|
104
|
+
OR
|
|
105
|
+
(
|
|
106
|
+
type = 'run' AND
|
|
107
|
+
state = 'running' AND
|
|
108
|
+
attempt_count IS NOT NULL AND attempt_count >= 1 AND
|
|
109
|
+
(max_attempts IS NULL OR max_attempts >= 1) AND
|
|
110
|
+
(max_attempts IS NULL OR attempt_count <= max_attempts) AND
|
|
111
|
+
next_attempt_at IS NULL AND
|
|
112
|
+
result IS NULL AND
|
|
113
|
+
error_message IS NULL AND
|
|
114
|
+
error_name IS NULL AND
|
|
115
|
+
wake_at IS NULL AND
|
|
116
|
+
event_name IS NULL AND
|
|
117
|
+
timeout_at IS NULL AND
|
|
118
|
+
payload IS NULL AND
|
|
119
|
+
resolved_at IS NULL
|
|
120
|
+
)
|
|
121
|
+
OR
|
|
122
|
+
(
|
|
123
|
+
type = 'run' AND
|
|
124
|
+
state = 'succeeded' AND
|
|
125
|
+
attempt_count IS NOT NULL AND attempt_count >= 1 AND
|
|
126
|
+
(max_attempts IS NULL OR max_attempts >= 1) AND
|
|
127
|
+
(max_attempts IS NULL OR attempt_count <= max_attempts) AND
|
|
128
|
+
next_attempt_at IS NULL AND
|
|
129
|
+
result IS NOT NULL AND
|
|
130
|
+
error_message IS NULL AND
|
|
131
|
+
error_name IS NULL AND
|
|
132
|
+
wake_at IS NULL AND
|
|
133
|
+
event_name IS NULL AND
|
|
134
|
+
timeout_at IS NULL AND
|
|
135
|
+
payload IS NULL AND
|
|
136
|
+
resolved_at IS NOT NULL
|
|
137
|
+
)
|
|
138
|
+
OR
|
|
139
|
+
(
|
|
140
|
+
type = 'run' AND
|
|
141
|
+
state = 'failed' AND
|
|
142
|
+
attempt_count IS NOT NULL AND attempt_count >= 1 AND
|
|
143
|
+
(max_attempts IS NULL OR max_attempts >= 1) AND
|
|
144
|
+
(max_attempts IS NULL OR attempt_count <= max_attempts) AND
|
|
145
|
+
next_attempt_at IS NULL AND
|
|
146
|
+
result IS NULL AND
|
|
147
|
+
error_message IS NOT NULL AND
|
|
148
|
+
wake_at IS NULL AND
|
|
149
|
+
event_name IS NULL AND
|
|
150
|
+
timeout_at IS NULL AND
|
|
151
|
+
payload IS NULL AND
|
|
152
|
+
resolved_at IS NOT NULL
|
|
153
|
+
)
|
|
154
|
+
OR
|
|
155
|
+
(
|
|
156
|
+
type = 'sleep' AND
|
|
157
|
+
state = 'waiting' AND
|
|
158
|
+
attempt_count IS NULL AND
|
|
159
|
+
max_attempts IS NULL AND
|
|
160
|
+
next_attempt_at IS NULL AND
|
|
161
|
+
result IS NULL AND
|
|
162
|
+
error_message IS NULL AND
|
|
163
|
+
error_name IS NULL AND
|
|
164
|
+
wake_at IS NOT NULL AND
|
|
165
|
+
event_name IS NULL AND
|
|
166
|
+
timeout_at IS NULL AND
|
|
167
|
+
payload IS NULL AND
|
|
168
|
+
resolved_at IS NULL
|
|
169
|
+
)
|
|
170
|
+
OR
|
|
171
|
+
(
|
|
172
|
+
type = 'sleep' AND
|
|
173
|
+
state = 'elapsed' AND
|
|
174
|
+
attempt_count IS NULL AND
|
|
175
|
+
max_attempts IS NULL AND
|
|
176
|
+
next_attempt_at IS NULL AND
|
|
177
|
+
result IS NULL AND
|
|
178
|
+
error_message IS NULL AND
|
|
179
|
+
error_name IS NULL AND
|
|
180
|
+
wake_at IS NULL AND
|
|
181
|
+
event_name IS NULL AND
|
|
182
|
+
timeout_at IS NULL AND
|
|
183
|
+
payload IS NULL AND
|
|
184
|
+
resolved_at IS NOT NULL
|
|
185
|
+
)
|
|
186
|
+
OR
|
|
187
|
+
(
|
|
188
|
+
type = 'wait' AND
|
|
189
|
+
state = 'waiting' AND
|
|
190
|
+
attempt_count IS NULL AND
|
|
191
|
+
max_attempts IS NULL AND
|
|
192
|
+
next_attempt_at IS NULL AND
|
|
193
|
+
result IS NULL AND
|
|
194
|
+
error_message IS NULL AND
|
|
195
|
+
error_name IS NULL AND
|
|
196
|
+
wake_at IS NULL AND
|
|
197
|
+
event_name IS NOT NULL AND
|
|
198
|
+
payload IS NULL AND
|
|
199
|
+
resolved_at IS NULL
|
|
200
|
+
)
|
|
201
|
+
OR
|
|
202
|
+
(
|
|
203
|
+
type = 'wait' AND
|
|
204
|
+
state = 'satisfied' AND
|
|
205
|
+
attempt_count IS NULL AND
|
|
206
|
+
max_attempts IS NULL AND
|
|
207
|
+
next_attempt_at IS NULL AND
|
|
208
|
+
result IS NULL AND
|
|
209
|
+
error_message IS NULL AND
|
|
210
|
+
error_name IS NULL AND
|
|
211
|
+
wake_at IS NULL AND
|
|
212
|
+
event_name IS NOT NULL AND
|
|
213
|
+
timeout_at IS NULL AND
|
|
214
|
+
payload IS NOT NULL AND
|
|
215
|
+
resolved_at IS NOT NULL
|
|
216
|
+
)
|
|
217
|
+
OR
|
|
218
|
+
(
|
|
219
|
+
type = 'wait' AND
|
|
220
|
+
state = 'timed_out' AND
|
|
221
|
+
attempt_count IS NULL AND
|
|
222
|
+
max_attempts IS NULL AND
|
|
223
|
+
next_attempt_at IS NULL AND
|
|
224
|
+
result IS NULL AND
|
|
225
|
+
error_message IS NULL AND
|
|
226
|
+
error_name IS NULL AND
|
|
227
|
+
wake_at IS NULL AND
|
|
228
|
+
event_name IS NOT NULL AND
|
|
229
|
+
timeout_at IS NULL AND
|
|
230
|
+
payload IS NULL AND
|
|
231
|
+
resolved_at IS NOT NULL
|
|
232
|
+
)
|
|
233
|
+
),
|
|
234
|
+
CHECK (parent_step_id IS NULL OR parent_step_id <> id)
|
|
235
|
+
) STRICT;
|
|
236
|
+
|
|
237
|
+
CREATE TABLE step_events (
|
|
238
|
+
id TEXT NOT NULL PRIMARY KEY
|
|
239
|
+
DEFAULT (lower(hex(randomblob(16))))
|
|
240
|
+
CHECK (length(id) > 0),
|
|
241
|
+
|
|
242
|
+
step_id TEXT NOT NULL,
|
|
243
|
+
recorded_at INTEGER NOT NULL
|
|
244
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER))
|
|
245
|
+
CHECK (recorded_at >= 0),
|
|
246
|
+
|
|
247
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
248
|
+
'attempt_started',
|
|
249
|
+
'attempt_succeeded',
|
|
250
|
+
'attempt_failed',
|
|
251
|
+
'sleep_waiting',
|
|
252
|
+
'sleep_elapsed',
|
|
253
|
+
'wait_waiting',
|
|
254
|
+
'wait_satisfied',
|
|
255
|
+
'wait_timed_out'
|
|
256
|
+
)),
|
|
257
|
+
|
|
258
|
+
attempt_number INTEGER,
|
|
259
|
+
result TEXT,
|
|
260
|
+
error_message TEXT,
|
|
261
|
+
error_name TEXT,
|
|
262
|
+
next_attempt_at INTEGER,
|
|
263
|
+
wake_at INTEGER,
|
|
264
|
+
event_name TEXT,
|
|
265
|
+
timeout_at INTEGER,
|
|
266
|
+
payload TEXT,
|
|
267
|
+
|
|
268
|
+
FOREIGN KEY (step_id) REFERENCES steps(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
|
269
|
+
|
|
270
|
+
CHECK (attempt_number IS NULL OR attempt_number >= 1),
|
|
271
|
+
CHECK (next_attempt_at IS NULL OR next_attempt_at >= 0),
|
|
272
|
+
CHECK (wake_at IS NULL OR wake_at >= 0),
|
|
273
|
+
CHECK (timeout_at IS NULL OR timeout_at >= 0),
|
|
274
|
+
CHECK (error_name IS NULL OR length(error_name) > 0),
|
|
275
|
+
CHECK (event_name IS NULL OR length(event_name) > 0),
|
|
276
|
+
|
|
277
|
+
CHECK (
|
|
278
|
+
(
|
|
279
|
+
type = 'attempt_started' AND
|
|
280
|
+
attempt_number IS NOT NULL AND
|
|
281
|
+
result IS NULL AND
|
|
282
|
+
error_message IS NULL AND
|
|
283
|
+
error_name IS NULL AND
|
|
284
|
+
next_attempt_at IS NULL AND
|
|
285
|
+
wake_at IS NULL AND
|
|
286
|
+
event_name IS NULL AND
|
|
287
|
+
timeout_at IS NULL AND
|
|
288
|
+
payload IS NULL
|
|
289
|
+
)
|
|
290
|
+
OR
|
|
291
|
+
(
|
|
292
|
+
type = 'attempt_succeeded' AND
|
|
293
|
+
attempt_number IS NOT NULL AND
|
|
294
|
+
result IS NOT NULL AND
|
|
295
|
+
error_message IS NULL AND
|
|
296
|
+
error_name IS NULL AND
|
|
297
|
+
next_attempt_at IS NULL AND
|
|
298
|
+
wake_at IS NULL AND
|
|
299
|
+
event_name IS NULL AND
|
|
300
|
+
timeout_at IS NULL AND
|
|
301
|
+
payload IS NULL
|
|
302
|
+
)
|
|
303
|
+
OR
|
|
304
|
+
(
|
|
305
|
+
type = 'attempt_failed' AND
|
|
306
|
+
attempt_number IS NOT NULL AND
|
|
307
|
+
result IS NULL AND
|
|
308
|
+
error_message IS NOT NULL AND
|
|
309
|
+
wake_at IS NULL AND
|
|
310
|
+
event_name IS NULL AND
|
|
311
|
+
timeout_at IS NULL AND
|
|
312
|
+
payload IS NULL
|
|
313
|
+
)
|
|
314
|
+
OR
|
|
315
|
+
(
|
|
316
|
+
type = 'sleep_waiting' AND
|
|
317
|
+
attempt_number IS NULL AND
|
|
318
|
+
result IS NULL AND
|
|
319
|
+
error_message IS NULL AND
|
|
320
|
+
error_name IS NULL AND
|
|
321
|
+
next_attempt_at IS NULL AND
|
|
322
|
+
wake_at IS NOT NULL AND
|
|
323
|
+
event_name IS NULL AND
|
|
324
|
+
timeout_at IS NULL AND
|
|
325
|
+
payload IS NULL
|
|
326
|
+
)
|
|
327
|
+
OR
|
|
328
|
+
(
|
|
329
|
+
type = 'sleep_elapsed' AND
|
|
330
|
+
attempt_number IS NULL AND
|
|
331
|
+
result IS NULL AND
|
|
332
|
+
error_message IS NULL AND
|
|
333
|
+
error_name IS NULL AND
|
|
334
|
+
next_attempt_at IS NULL AND
|
|
335
|
+
wake_at IS NULL AND
|
|
336
|
+
event_name IS NULL AND
|
|
337
|
+
timeout_at IS NULL AND
|
|
338
|
+
payload IS NULL
|
|
339
|
+
)
|
|
340
|
+
OR
|
|
341
|
+
(
|
|
342
|
+
type = 'wait_waiting' AND
|
|
343
|
+
attempt_number IS NULL AND
|
|
344
|
+
result IS NULL AND
|
|
345
|
+
error_message IS NULL AND
|
|
346
|
+
error_name IS NULL AND
|
|
347
|
+
next_attempt_at IS NULL AND
|
|
348
|
+
wake_at IS NULL AND
|
|
349
|
+
event_name IS NOT NULL AND
|
|
350
|
+
payload IS NULL
|
|
351
|
+
)
|
|
352
|
+
OR
|
|
353
|
+
(
|
|
354
|
+
type = 'wait_satisfied' AND
|
|
355
|
+
attempt_number IS NULL AND
|
|
356
|
+
result IS NULL AND
|
|
357
|
+
error_message IS NULL AND
|
|
358
|
+
error_name IS NULL AND
|
|
359
|
+
next_attempt_at IS NULL AND
|
|
360
|
+
wake_at IS NULL AND
|
|
361
|
+
event_name IS NULL AND
|
|
362
|
+
timeout_at IS NULL AND
|
|
363
|
+
payload IS NOT NULL
|
|
364
|
+
)
|
|
365
|
+
OR
|
|
366
|
+
(
|
|
367
|
+
type = 'wait_timed_out' AND
|
|
368
|
+
attempt_number IS NULL AND
|
|
369
|
+
result IS NULL AND
|
|
370
|
+
error_message IS NULL AND
|
|
371
|
+
error_name IS NULL AND
|
|
372
|
+
next_attempt_at IS NULL AND
|
|
373
|
+
wake_at IS NULL AND
|
|
374
|
+
event_name IS NULL AND
|
|
375
|
+
timeout_at IS NULL AND
|
|
376
|
+
payload IS NULL
|
|
377
|
+
)
|
|
378
|
+
)
|
|
379
|
+
) STRICT;
|
|
380
|
+
|
|
381
|
+
CREATE TABLE workflow_events (
|
|
382
|
+
id INTEGER NOT NULL PRIMARY KEY,
|
|
383
|
+
|
|
384
|
+
recorded_at INTEGER NOT NULL
|
|
385
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER))
|
|
386
|
+
CHECK (recorded_at >= 0),
|
|
387
|
+
|
|
388
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
389
|
+
'created',
|
|
390
|
+
'started',
|
|
391
|
+
'paused',
|
|
392
|
+
'resumed',
|
|
393
|
+
'completed',
|
|
394
|
+
'failed',
|
|
395
|
+
'cancelled'
|
|
396
|
+
)),
|
|
397
|
+
|
|
398
|
+
cancellation_reason TEXT,
|
|
399
|
+
|
|
400
|
+
CHECK (cancellation_reason IS NULL OR type = 'cancelled')
|
|
401
|
+
) STRICT;
|
|
402
|
+
|
|
403
|
+
CREATE TABLE inbound_events (
|
|
404
|
+
id TEXT NOT NULL PRIMARY KEY
|
|
405
|
+
DEFAULT (lower(hex(randomblob(16))))
|
|
406
|
+
CHECK (length(id) > 0),
|
|
407
|
+
event_name TEXT NOT NULL CHECK (length(event_name) > 0),
|
|
408
|
+
payload TEXT CHECK (payload IS NULL OR json_valid(payload)),
|
|
409
|
+
created_at INTEGER NOT NULL
|
|
410
|
+
DEFAULT (CAST(unixepoch('subsecond') * 1000 AS INTEGER))
|
|
411
|
+
CHECK (created_at >= 0),
|
|
412
|
+
claimed_by TEXT,
|
|
413
|
+
claimed_at INTEGER,
|
|
414
|
+
|
|
415
|
+
FOREIGN KEY (claimed_by) REFERENCES steps(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
|
416
|
+
|
|
417
|
+
-- claimed_by and claimed_at must be set together or not set at all
|
|
418
|
+
CHECK (
|
|
419
|
+
(claimed_by IS NULL AND claimed_at IS NULL) OR
|
|
420
|
+
(claimed_by IS NOT NULL AND claimed_at IS NOT NULL)
|
|
421
|
+
)
|
|
422
|
+
) STRICT;
|
|
423
|
+
|
|
424
|
+
-- scheduler/query indexes
|
|
425
|
+
CREATE INDEX steps_run_pending_by_time_idx
|
|
426
|
+
ON steps(next_attempt_at, id)
|
|
427
|
+
WHERE type = 'run' AND state = 'pending';
|
|
428
|
+
|
|
429
|
+
CREATE INDEX steps_sleep_waiting_by_time_idx
|
|
430
|
+
ON steps(wake_at, id)
|
|
431
|
+
WHERE type = 'sleep' AND state = 'waiting';
|
|
432
|
+
|
|
433
|
+
CREATE INDEX steps_wait_waiting_by_event_idx
|
|
434
|
+
ON steps(event_name, id)
|
|
435
|
+
WHERE type = 'wait' AND state = 'waiting';
|
|
436
|
+
|
|
437
|
+
CREATE INDEX steps_wait_waiting_by_timeout_idx
|
|
438
|
+
ON steps(timeout_at, id)
|
|
439
|
+
WHERE type = 'wait' AND state = 'waiting' AND timeout_at IS NOT NULL;
|
|
440
|
+
|
|
441
|
+
CREATE INDEX steps_by_parent_step_id_idx
|
|
442
|
+
ON steps(parent_step_id, id)
|
|
443
|
+
WHERE parent_step_id IS NOT NULL;
|
|
444
|
+
|
|
445
|
+
CREATE INDEX step_events_by_step_and_time_idx
|
|
446
|
+
ON step_events(step_id, recorded_at, id);
|
|
447
|
+
|
|
448
|
+
CREATE INDEX workflow_events_by_time_idx
|
|
449
|
+
ON workflow_events(recorded_at, id);
|
|
450
|
+
|
|
451
|
+
CREATE INDEX inbound_events_by_name_and_time_idx
|
|
452
|
+
ON inbound_events(event_name, created_at, id);
|
|
453
|
+
|
|
454
|
+
CREATE INDEX inbound_events_by_claimed_by_idx
|
|
455
|
+
ON inbound_events(claimed_by, id)
|
|
456
|
+
WHERE claimed_by IS NOT NULL;
|
|
457
|
+
|
|
458
|
+
-- immutable identity fields
|
|
459
|
+
CREATE TRIGGER workflow_metadata_immutable_fields
|
|
460
|
+
BEFORE UPDATE ON workflow_metadata
|
|
461
|
+
FOR EACH ROW
|
|
462
|
+
WHEN NEW.id <> OLD.id OR NEW.created_at <> OLD.created_at
|
|
463
|
+
BEGIN
|
|
464
|
+
SELECT RAISE(ABORT, 'workflow_metadata.id and workflow_metadata.created_at are immutable');
|
|
465
|
+
END;
|
|
466
|
+
|
|
467
|
+
-- valid status transitions
|
|
468
|
+
CREATE TRIGGER workflow_metadata_valid_transition
|
|
469
|
+
BEFORE UPDATE ON workflow_metadata
|
|
470
|
+
FOR EACH ROW
|
|
471
|
+
WHEN NEW.status <> OLD.status
|
|
472
|
+
BEGIN
|
|
473
|
+
SELECT CASE
|
|
474
|
+
WHEN OLD.status = 'pending' AND NEW.status NOT IN ('running', 'cancelled') THEN
|
|
475
|
+
RAISE(ABORT, 'pending can only transition to running or cancelled')
|
|
476
|
+
WHEN OLD.status = 'running' AND NEW.status NOT IN ('paused', 'completed', 'failed', 'cancelled') THEN
|
|
477
|
+
RAISE(ABORT, 'running can only transition to paused, completed, failed, or cancelled')
|
|
478
|
+
WHEN OLD.status = 'paused' AND NEW.status NOT IN ('running', 'cancelled') THEN
|
|
479
|
+
RAISE(ABORT, 'paused can only transition to running or cancelled')
|
|
480
|
+
WHEN OLD.status IN ('completed', 'failed', 'cancelled') THEN
|
|
481
|
+
RAISE(ABORT, 'terminal status cannot transition')
|
|
482
|
+
END;
|
|
483
|
+
END;
|
|
484
|
+
|
|
485
|
+
CREATE TRIGGER steps_immutable_identity_fields
|
|
486
|
+
BEFORE UPDATE ON steps
|
|
487
|
+
FOR EACH ROW
|
|
488
|
+
WHEN NEW.id <> OLD.id OR NEW.type <> OLD.type OR NEW.created_at <> OLD.created_at
|
|
489
|
+
BEGIN
|
|
490
|
+
SELECT RAISE(ABORT, 'steps.id, steps.type, and steps.created_at are immutable');
|
|
491
|
+
END;
|
|
492
|
+
|
|
493
|
+
CREATE TRIGGER steps_parent_step_id_immutable
|
|
494
|
+
BEFORE UPDATE ON steps
|
|
495
|
+
FOR EACH ROW
|
|
496
|
+
WHEN NEW.parent_step_id IS NOT OLD.parent_step_id
|
|
497
|
+
BEGIN
|
|
498
|
+
SELECT RAISE(ABORT, 'steps.parent_step_id is immutable');
|
|
499
|
+
END;
|
|
500
|
+
|
|
501
|
+
CREATE TRIGGER steps_parent_must_be_run
|
|
502
|
+
BEFORE INSERT ON steps
|
|
503
|
+
FOR EACH ROW
|
|
504
|
+
WHEN NEW.parent_step_id IS NOT NULL
|
|
505
|
+
AND (SELECT type FROM steps WHERE id = NEW.parent_step_id) IS NOT 'run'
|
|
506
|
+
BEGIN
|
|
507
|
+
SELECT RAISE(ABORT, 'steps.parent_step_id must reference a run step');
|
|
508
|
+
END;
|
|
509
|
+
|
|
510
|
+
-- append-only step events
|
|
511
|
+
CREATE TRIGGER step_events_append_only_update
|
|
512
|
+
BEFORE UPDATE ON step_events
|
|
513
|
+
FOR EACH ROW
|
|
514
|
+
BEGIN
|
|
515
|
+
SELECT RAISE(ABORT, 'step_events is append-only');
|
|
516
|
+
END;
|
|
517
|
+
|
|
518
|
+
CREATE TRIGGER step_events_append_only_delete
|
|
519
|
+
BEFORE DELETE ON step_events
|
|
520
|
+
FOR EACH ROW
|
|
521
|
+
BEGIN
|
|
522
|
+
SELECT RAISE(ABORT, 'step_events is append-only');
|
|
523
|
+
END;
|
|
524
|
+
|
|
525
|
+
-- append-only workflow events
|
|
526
|
+
CREATE TRIGGER workflow_events_append_only_update
|
|
527
|
+
BEFORE UPDATE ON workflow_events
|
|
528
|
+
FOR EACH ROW
|
|
529
|
+
BEGIN
|
|
530
|
+
SELECT RAISE(ABORT, 'workflow_events is append-only');
|
|
531
|
+
END;
|
|
532
|
+
|
|
533
|
+
CREATE TRIGGER workflow_events_append_only_delete
|
|
534
|
+
BEFORE DELETE ON workflow_events
|
|
535
|
+
FOR EACH ROW
|
|
536
|
+
BEGIN
|
|
537
|
+
SELECT RAISE(ABORT, 'workflow_events is append-only');
|
|
538
|
+
END;
|
|
539
|
+
|
|
540
|
+
-- step_events.type must match the referenced row in steps.type
|
|
541
|
+
CREATE TRIGGER step_events_parent_type_match
|
|
542
|
+
BEFORE INSERT ON step_events
|
|
543
|
+
FOR EACH ROW
|
|
544
|
+
BEGIN
|
|
545
|
+
SELECT CASE
|
|
546
|
+
WHEN NOT EXISTS (SELECT 1 FROM steps WHERE id = NEW.step_id) THEN
|
|
547
|
+
RAISE(ABORT, 'step_events.step_id does not reference an existing steps row')
|
|
548
|
+
WHEN NEW.type IN ('attempt_started', 'attempt_succeeded', 'attempt_failed')
|
|
549
|
+
AND (SELECT type FROM steps WHERE id = NEW.step_id) <> 'run' THEN
|
|
550
|
+
RAISE(ABORT, 'run attempt events require a run step')
|
|
551
|
+
WHEN NEW.type IN ('sleep_waiting', 'sleep_elapsed')
|
|
552
|
+
AND (SELECT type FROM steps WHERE id = NEW.step_id) <> 'sleep' THEN
|
|
553
|
+
RAISE(ABORT, 'sleep events require a sleep step')
|
|
554
|
+
WHEN NEW.type IN ('wait_waiting', 'wait_satisfied', 'wait_timed_out')
|
|
555
|
+
AND (SELECT type FROM steps WHERE id = NEW.step_id) <> 'wait' THEN
|
|
556
|
+
RAISE(ABORT, 'wait events require a wait step')
|
|
557
|
+
END;
|
|
558
|
+
END;
|
|
559
|
+
`;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal typings for `node:async_hooks` without @types/node.
|
|
3
|
+
*/
|
|
4
|
+
declare module "node:async_hooks" {
|
|
5
|
+
export interface AsyncLocalStorageOptions {
|
|
6
|
+
defaultValue?: unknown;
|
|
7
|
+
name?: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class AsyncLocalStorage<T> {
|
|
11
|
+
constructor(options?: AsyncLocalStorageOptions);
|
|
12
|
+
getStore(): T | undefined;
|
|
13
|
+
run<R>(store: T, callback: () => R): R;
|
|
14
|
+
run<R, TArgs extends unknown[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
|
|
15
|
+
}
|
|
16
|
+
}
|