@tanstack/workflow-store-drizzle-postgres 0.0.1 → 0.0.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 +15 -1
- package/SCHEMA_MIGRATIONS.md +85 -0
- package/dist/index.cjs +18 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/migrations.cjs +23 -0
- package/dist/migrations.cjs.map +1 -0
- package/dist/migrations.d.cts +16 -0
- package/dist/migrations.d.ts +16 -0
- package/dist/migrations.js +21 -0
- package/dist/migrations.js.map +1 -0
- package/dist/schema-contract.cjs +169 -0
- package/dist/schema-contract.cjs.map +1 -0
- package/dist/schema-contract.d.cts +22 -0
- package/dist/schema-contract.d.ts +22 -0
- package/dist/schema-contract.js +165 -0
- package/dist/schema-contract.js.map +1 -0
- package/dist/store.cjs +59 -147
- package/dist/store.cjs.map +1 -1
- package/dist/store.d.cts +2 -12
- package/dist/store.d.ts +2 -12
- package/dist/store.js +52 -139
- package/dist/store.js.map +1 -1
- package/dist/tables.cjs +103 -0
- package/dist/tables.cjs.map +1 -0
- package/dist/tables.d.cts +1259 -0
- package/dist/tables.d.ts +1259 -0
- package/dist/tables.js +95 -0
- package/dist/tables.js.map +1 -0
- package/migrations/0000_workflow_store.sql +136 -0
- package/package.json +4 -2
- package/src/index.ts +25 -3
- package/src/migrations.ts +34 -0
- package/src/schema-contract.ts +208 -0
- package/src/store.ts +97 -162
- package/src/tables.ts +149 -0
package/src/store.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { LogConflictError } from '@tanstack/workflow-core'
|
|
2
2
|
import { sql } from 'drizzle-orm'
|
|
3
|
+
import {
|
|
4
|
+
getDrizzlePostgresWorkflowStoreSchemaStatements,
|
|
5
|
+
qualifiedTableName,
|
|
6
|
+
resolveDrizzlePostgresWorkflowStoreTables,
|
|
7
|
+
} from './schema-contract'
|
|
3
8
|
import type { RunState, WorkflowEvent } from '@tanstack/workflow-core'
|
|
4
9
|
import type { SQL } from 'drizzle-orm'
|
|
10
|
+
import type { DrizzlePostgresWorkflowStoreTables } from './schema-contract'
|
|
5
11
|
import type {
|
|
6
12
|
AppendEventsArgs,
|
|
7
13
|
AppendEventsResult,
|
|
@@ -50,17 +56,6 @@ export interface DrizzlePostgresDatabase {
|
|
|
50
56
|
) => Promise<TResult>
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
export interface DrizzlePostgresWorkflowStoreTables {
|
|
54
|
-
runs: string
|
|
55
|
-
runStates: string
|
|
56
|
-
eventLocks: string
|
|
57
|
-
events: string
|
|
58
|
-
timers: string
|
|
59
|
-
signalDeliveries: string
|
|
60
|
-
schedules: string
|
|
61
|
-
scheduleBuckets: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
59
|
export interface DrizzlePostgresWorkflowStoreOptions {
|
|
65
60
|
db: DrizzlePostgresDatabase
|
|
66
61
|
schema?: string
|
|
@@ -72,31 +67,19 @@ export type DrizzlePostgresWorkflowStore = WorkflowExecutionStore &
|
|
|
72
67
|
ensureSchema: () => Promise<void>
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
export const defaultDrizzlePostgresWorkflowStoreTables: DrizzlePostgresWorkflowStoreTables =
|
|
76
|
-
{
|
|
77
|
-
runs: 'workflow_runs',
|
|
78
|
-
runStates: 'workflow_run_states',
|
|
79
|
-
eventLocks: 'workflow_event_locks',
|
|
80
|
-
events: 'workflow_events',
|
|
81
|
-
timers: 'workflow_timers',
|
|
82
|
-
signalDeliveries: 'workflow_signal_deliveries',
|
|
83
|
-
schedules: 'workflow_schedules',
|
|
84
|
-
scheduleBuckets: 'workflow_schedule_buckets',
|
|
85
|
-
}
|
|
86
|
-
|
|
87
70
|
export function createDrizzlePostgresWorkflowStore(
|
|
88
71
|
options: DrizzlePostgresWorkflowStoreOptions,
|
|
89
72
|
): DrizzlePostgresWorkflowStore {
|
|
90
|
-
const tableNames =
|
|
91
|
-
...defaultDrizzlePostgresWorkflowStoreTables,
|
|
92
|
-
...options.tables,
|
|
93
|
-
}
|
|
73
|
+
const tableNames = resolveDrizzlePostgresWorkflowStoreTables(options.tables)
|
|
94
74
|
const tableSql = tableSqls(options.schema, tableNames)
|
|
95
75
|
const db = options.db
|
|
96
76
|
|
|
97
77
|
return {
|
|
98
78
|
async ensureSchema() {
|
|
99
|
-
for (const statement of
|
|
79
|
+
for (const statement of getDrizzlePostgresWorkflowStoreSchemaStatements({
|
|
80
|
+
schema: options.schema,
|
|
81
|
+
tables: tableNames,
|
|
82
|
+
})) {
|
|
100
83
|
await db.execute(sql.raw(statement))
|
|
101
84
|
}
|
|
102
85
|
},
|
|
@@ -165,6 +148,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
165
148
|
input,
|
|
166
149
|
output,
|
|
167
150
|
error,
|
|
151
|
+
awaiting,
|
|
168
152
|
waiting_for,
|
|
169
153
|
pending_approval,
|
|
170
154
|
created_at,
|
|
@@ -178,6 +162,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
178
162
|
${encodeJson(state.input)}::jsonb,
|
|
179
163
|
${encodeJsonOrNull(state.output)}::jsonb,
|
|
180
164
|
${encodeJsonOrNull(state.error)}::jsonb,
|
|
165
|
+
${encodeJsonOrNull(state.awaiting)}::jsonb,
|
|
181
166
|
${encodeJsonOrNull(state.waitingFor)}::jsonb,
|
|
182
167
|
${encodeJsonOrNull(state.pendingApproval)}::jsonb,
|
|
183
168
|
${state.createdAt},
|
|
@@ -190,6 +175,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
190
175
|
input = excluded.input,
|
|
191
176
|
output = excluded.output,
|
|
192
177
|
error = excluded.error,
|
|
178
|
+
awaiting = excluded.awaiting,
|
|
193
179
|
waiting_for = excluded.waiting_for,
|
|
194
180
|
pending_approval = excluded.pending_approval,
|
|
195
181
|
created_at = excluded.created_at,
|
|
@@ -204,6 +190,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
204
190
|
input,
|
|
205
191
|
output,
|
|
206
192
|
error,
|
|
193
|
+
awaiting,
|
|
207
194
|
waiting_for,
|
|
208
195
|
pending_approval,
|
|
209
196
|
wake_at,
|
|
@@ -218,6 +205,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
218
205
|
${encodeJson(state.input)}::jsonb,
|
|
219
206
|
${encodeJsonOrNull(state.output)}::jsonb,
|
|
220
207
|
${encodeJsonOrNull(state.error)}::jsonb,
|
|
208
|
+
${encodeJsonOrNull(state.awaiting)}::jsonb,
|
|
221
209
|
${encodeJsonOrNull(state.waitingFor)}::jsonb,
|
|
222
210
|
${encodeJsonOrNull(state.pendingApproval)}::jsonb,
|
|
223
211
|
${
|
|
@@ -235,6 +223,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
235
223
|
input = excluded.input,
|
|
236
224
|
output = excluded.output,
|
|
237
225
|
error = excluded.error,
|
|
226
|
+
awaiting = excluded.awaiting,
|
|
238
227
|
waiting_for = excluded.waiting_for,
|
|
239
228
|
pending_approval = excluded.pending_approval,
|
|
240
229
|
wake_at = excluded.wake_at,
|
|
@@ -402,6 +391,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
402
391
|
update ${tableSql.runs}
|
|
403
392
|
set
|
|
404
393
|
status = 'paused',
|
|
394
|
+
awaiting = ${encodeJsonOrNull(args.awaiting)}::jsonb,
|
|
405
395
|
waiting_for = ${encodeJsonOrNull(args.waitingFor)}::jsonb,
|
|
406
396
|
pending_approval = ${encodeJsonOrNull(args.pendingApproval)}::jsonb,
|
|
407
397
|
wake_at = ${args.wakeAt ?? null},
|
|
@@ -418,6 +408,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
418
408
|
set
|
|
419
409
|
status = 'finished',
|
|
420
410
|
output = ${encodeJson(args.output)}::jsonb,
|
|
411
|
+
awaiting = null,
|
|
421
412
|
waiting_for = null,
|
|
422
413
|
pending_approval = null,
|
|
423
414
|
wake_at = null,
|
|
@@ -435,6 +426,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
435
426
|
set
|
|
436
427
|
status = 'errored',
|
|
437
428
|
error = ${encodeJson(args.error)}::jsonb,
|
|
429
|
+
awaiting = null,
|
|
438
430
|
waiting_for = null,
|
|
439
431
|
pending_approval = null,
|
|
440
432
|
wake_at = null,
|
|
@@ -524,7 +516,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
524
516
|
)
|
|
525
517
|
if (existingDelivery) return { kind: 'duplicate', run }
|
|
526
518
|
|
|
527
|
-
if (run
|
|
519
|
+
if (!isRunWaitingForSignal(run, args.delivery)) {
|
|
528
520
|
return { kind: 'not-waiting', run }
|
|
529
521
|
}
|
|
530
522
|
|
|
@@ -549,6 +541,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
549
541
|
update ${tableSql.runs}
|
|
550
542
|
set
|
|
551
543
|
status = 'queued',
|
|
544
|
+
awaiting = null,
|
|
552
545
|
waiting_for = null,
|
|
553
546
|
pending_approval = null,
|
|
554
547
|
wake_at = null,
|
|
@@ -578,7 +571,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
578
571
|
)
|
|
579
572
|
if (existingDelivery) return { kind: 'duplicate', run }
|
|
580
573
|
|
|
581
|
-
if (run
|
|
574
|
+
if (!isRunWaitingForApproval(run, args.approval)) {
|
|
582
575
|
return { kind: 'not-waiting', run }
|
|
583
576
|
}
|
|
584
577
|
|
|
@@ -597,6 +590,7 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
597
590
|
update ${tableSql.runs}
|
|
598
591
|
set
|
|
599
592
|
status = 'queued',
|
|
593
|
+
awaiting = null,
|
|
600
594
|
waiting_for = null,
|
|
601
595
|
pending_approval = null,
|
|
602
596
|
wake_at = null,
|
|
@@ -650,12 +644,26 @@ export function createDrizzlePostgresWorkflowStore(
|
|
|
650
644
|
const schedules = await queryRows<ScheduleRow>(
|
|
651
645
|
db,
|
|
652
646
|
sql`
|
|
653
|
-
select
|
|
654
|
-
from ${tableSql.schedules}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
and
|
|
658
|
-
|
|
647
|
+
select schedule.*
|
|
648
|
+
from ${tableSql.schedules} schedule
|
|
649
|
+
left join ${tableSql.scheduleBuckets} bucket
|
|
650
|
+
on bucket.schedule_id = schedule.schedule_id
|
|
651
|
+
and bucket.bucket_id = schedule.next_fire_at::text
|
|
652
|
+
where schedule.enabled = true
|
|
653
|
+
and schedule.next_fire_at is not null
|
|
654
|
+
and schedule.next_fire_at <= ${args.now}
|
|
655
|
+
and (
|
|
656
|
+
bucket.schedule_id is null
|
|
657
|
+
or (
|
|
658
|
+
bucket.status <> 'started'
|
|
659
|
+
and (
|
|
660
|
+
bucket.lease_owner is null
|
|
661
|
+
or bucket.lease_owner = ${args.leaseOwner}
|
|
662
|
+
or bucket.lease_expires_at <= ${args.now}
|
|
663
|
+
)
|
|
664
|
+
)
|
|
665
|
+
)
|
|
666
|
+
order by schedule.next_fire_at asc, schedule.schedule_id asc
|
|
659
667
|
limit ${args.limit}
|
|
660
668
|
`,
|
|
661
669
|
)
|
|
@@ -810,6 +818,7 @@ interface RunRow {
|
|
|
810
818
|
input: unknown
|
|
811
819
|
output: unknown
|
|
812
820
|
error: unknown
|
|
821
|
+
awaiting: unknown
|
|
813
822
|
waiting_for: unknown
|
|
814
823
|
pending_approval: unknown
|
|
815
824
|
wake_at: number | string | null
|
|
@@ -827,6 +836,7 @@ interface RunStateRow {
|
|
|
827
836
|
input: unknown
|
|
828
837
|
output: unknown
|
|
829
838
|
error: unknown
|
|
839
|
+
awaiting: unknown
|
|
830
840
|
waiting_for: unknown
|
|
831
841
|
pending_approval: unknown
|
|
832
842
|
created_at: number | string
|
|
@@ -890,122 +900,6 @@ function tableSqls(
|
|
|
890
900
|
}
|
|
891
901
|
}
|
|
892
902
|
|
|
893
|
-
function schemaStatements(
|
|
894
|
-
schema: string | undefined,
|
|
895
|
-
tables: DrizzlePostgresWorkflowStoreTables,
|
|
896
|
-
): Array<string> {
|
|
897
|
-
const runs = qualifiedTableName(schema, tables.runs)
|
|
898
|
-
const runStates = qualifiedTableName(schema, tables.runStates)
|
|
899
|
-
const eventLocks = qualifiedTableName(schema, tables.eventLocks)
|
|
900
|
-
const events = qualifiedTableName(schema, tables.events)
|
|
901
|
-
const timers = qualifiedTableName(schema, tables.timers)
|
|
902
|
-
const signalDeliveries = qualifiedTableName(schema, tables.signalDeliveries)
|
|
903
|
-
const schedules = qualifiedTableName(schema, tables.schedules)
|
|
904
|
-
const scheduleBuckets = qualifiedTableName(schema, tables.scheduleBuckets)
|
|
905
|
-
|
|
906
|
-
return [
|
|
907
|
-
...(schema ? [`create schema if not exists ${quoteIdent(schema)}`] : []),
|
|
908
|
-
`create table if not exists ${runs} (
|
|
909
|
-
run_id text primary key,
|
|
910
|
-
workflow_id text not null,
|
|
911
|
-
workflow_version text,
|
|
912
|
-
status text not null,
|
|
913
|
-
input jsonb not null,
|
|
914
|
-
output jsonb,
|
|
915
|
-
error jsonb,
|
|
916
|
-
waiting_for jsonb,
|
|
917
|
-
pending_approval jsonb,
|
|
918
|
-
wake_at bigint,
|
|
919
|
-
lease_owner text,
|
|
920
|
-
lease_expires_at bigint,
|
|
921
|
-
created_at bigint not null,
|
|
922
|
-
updated_at bigint not null
|
|
923
|
-
)`,
|
|
924
|
-
`create index if not exists ${quoteIdent(`${tables.runs}_status_idx`)}
|
|
925
|
-
on ${runs} (status, updated_at)`,
|
|
926
|
-
`create index if not exists ${quoteIdent(`${tables.runs}_lease_idx`)}
|
|
927
|
-
on ${runs} (status, lease_expires_at)`,
|
|
928
|
-
`create table if not exists ${runStates} (
|
|
929
|
-
run_id text primary key,
|
|
930
|
-
workflow_id text not null,
|
|
931
|
-
workflow_version text,
|
|
932
|
-
status text not null,
|
|
933
|
-
input jsonb not null,
|
|
934
|
-
output jsonb,
|
|
935
|
-
error jsonb,
|
|
936
|
-
waiting_for jsonb,
|
|
937
|
-
pending_approval jsonb,
|
|
938
|
-
created_at bigint not null,
|
|
939
|
-
updated_at bigint not null
|
|
940
|
-
)`,
|
|
941
|
-
`create table if not exists ${eventLocks} (
|
|
942
|
-
run_id text primary key,
|
|
943
|
-
created_at bigint not null
|
|
944
|
-
)`,
|
|
945
|
-
`create table if not exists ${events} (
|
|
946
|
-
run_id text not null,
|
|
947
|
-
event_index integer not null,
|
|
948
|
-
event_type text not null,
|
|
949
|
-
step_id text,
|
|
950
|
-
event jsonb not null,
|
|
951
|
-
created_at bigint not null,
|
|
952
|
-
primary key (run_id, event_index)
|
|
953
|
-
)`,
|
|
954
|
-
`create index if not exists ${quoteIdent(`${tables.events}_type_idx`)}
|
|
955
|
-
on ${events} (run_id, event_type)`,
|
|
956
|
-
`create table if not exists ${timers} (
|
|
957
|
-
run_id text not null,
|
|
958
|
-
signal_id text not null,
|
|
959
|
-
workflow_id text not null,
|
|
960
|
-
workflow_version text,
|
|
961
|
-
wake_at bigint not null,
|
|
962
|
-
lease_owner text,
|
|
963
|
-
lease_expires_at bigint,
|
|
964
|
-
primary key (run_id, signal_id)
|
|
965
|
-
)`,
|
|
966
|
-
`create index if not exists ${quoteIdent(`${tables.timers}_due_idx`)}
|
|
967
|
-
on ${timers} (wake_at, lease_expires_at)`,
|
|
968
|
-
`create table if not exists ${signalDeliveries} (
|
|
969
|
-
run_id text not null,
|
|
970
|
-
signal_id text not null,
|
|
971
|
-
created_at bigint not null,
|
|
972
|
-
primary key (run_id, signal_id)
|
|
973
|
-
)`,
|
|
974
|
-
`create table if not exists ${schedules} (
|
|
975
|
-
schedule_id text primary key,
|
|
976
|
-
workflow_id text not null,
|
|
977
|
-
workflow_version text,
|
|
978
|
-
schedule jsonb not null,
|
|
979
|
-
overlap_policy text not null,
|
|
980
|
-
input jsonb,
|
|
981
|
-
next_fire_at bigint,
|
|
982
|
-
enabled boolean not null,
|
|
983
|
-
updated_at bigint not null
|
|
984
|
-
)`,
|
|
985
|
-
`create index if not exists ${quoteIdent(`${tables.schedules}_due_idx`)}
|
|
986
|
-
on ${schedules} (enabled, next_fire_at)`,
|
|
987
|
-
`create table if not exists ${scheduleBuckets} (
|
|
988
|
-
schedule_id text not null,
|
|
989
|
-
bucket_id text not null,
|
|
990
|
-
workflow_id text not null,
|
|
991
|
-
workflow_version text,
|
|
992
|
-
run_id text not null,
|
|
993
|
-
fire_at bigint not null,
|
|
994
|
-
input jsonb,
|
|
995
|
-
overlap_policy text not null,
|
|
996
|
-
status text not null,
|
|
997
|
-
lease_owner text,
|
|
998
|
-
lease_expires_at bigint,
|
|
999
|
-
started_at bigint,
|
|
1000
|
-
primary key (schedule_id, bucket_id)
|
|
1001
|
-
)`,
|
|
1002
|
-
`create index if not exists ${quoteIdent(
|
|
1003
|
-
`${tables.scheduleBuckets}_lease_idx`,
|
|
1004
|
-
)}
|
|
1005
|
-
on ${scheduleBuckets} (status, fire_at, lease_expires_at)`,
|
|
1006
|
-
]
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
903
|
async function loadRunById(
|
|
1010
904
|
db: DrizzlePostgresDatabase,
|
|
1011
905
|
tables: TableSqls,
|
|
@@ -1144,6 +1038,7 @@ function runFromRow(row: RunRow): WorkflowExecution {
|
|
|
1144
1038
|
input: decodeJson(row.input),
|
|
1145
1039
|
output: decodeJsonOrUndefined(row.output),
|
|
1146
1040
|
error: decodeJsonOrUndefined(row.error),
|
|
1041
|
+
awaiting: decodeJsonOrUndefined(row.awaiting),
|
|
1147
1042
|
waitingFor: decodeJsonOrUndefined(row.waiting_for),
|
|
1148
1043
|
pendingApproval: decodeJsonOrUndefined(row.pending_approval),
|
|
1149
1044
|
wakeAt: numberOrUndefined(row.wake_at),
|
|
@@ -1168,6 +1063,7 @@ function runStateFromRow(row: RunStateRow): RunState {
|
|
|
1168
1063
|
input: decodeJson(row.input),
|
|
1169
1064
|
output: decodeJsonOrUndefined(row.output),
|
|
1170
1065
|
error: decodeJsonOrUndefined(row.error),
|
|
1066
|
+
awaiting: decodeJsonOrUndefined(row.awaiting),
|
|
1171
1067
|
waitingFor: decodeJsonOrUndefined(row.waiting_for),
|
|
1172
1068
|
pendingApproval: decodeJsonOrUndefined(row.pending_approval),
|
|
1173
1069
|
createdAt: Number(row.created_at),
|
|
@@ -1196,6 +1092,54 @@ function timerFromRow(row: TimerRow): TimerWakeup {
|
|
|
1196
1092
|
}
|
|
1197
1093
|
}
|
|
1198
1094
|
|
|
1095
|
+
function isRunWaitingForSignal(
|
|
1096
|
+
run: WorkflowExecution,
|
|
1097
|
+
delivery: DeliverSignalArgs['delivery'],
|
|
1098
|
+
) {
|
|
1099
|
+
return (
|
|
1100
|
+
signalAwaitableMatches(run.waitingFor, delivery) ||
|
|
1101
|
+
run.awaiting?.some(
|
|
1102
|
+
(awaitable) =>
|
|
1103
|
+
awaitable.type === 'signal' &&
|
|
1104
|
+
signalAwaitableMatches(awaitable, delivery),
|
|
1105
|
+
) === true
|
|
1106
|
+
)
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function signalAwaitableMatches(
|
|
1110
|
+
awaitable:
|
|
1111
|
+
| NonNullable<WorkflowExecution['waitingFor']>
|
|
1112
|
+
| Extract<
|
|
1113
|
+
NonNullable<WorkflowExecution['awaiting']>[number],
|
|
1114
|
+
{
|
|
1115
|
+
type: 'signal'
|
|
1116
|
+
}
|
|
1117
|
+
>
|
|
1118
|
+
| undefined,
|
|
1119
|
+
delivery: DeliverSignalArgs['delivery'],
|
|
1120
|
+
) {
|
|
1121
|
+
return (
|
|
1122
|
+
awaitable?.signalName === delivery.name &&
|
|
1123
|
+
(delivery.stepId === undefined ||
|
|
1124
|
+
awaitable.stepId === undefined ||
|
|
1125
|
+
awaitable.stepId === delivery.stepId)
|
|
1126
|
+
)
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function isRunWaitingForApproval(
|
|
1130
|
+
run: WorkflowExecution,
|
|
1131
|
+
approval: DeliverApprovalArgs['approval'],
|
|
1132
|
+
) {
|
|
1133
|
+
return (
|
|
1134
|
+
run.pendingApproval?.approvalId === approval.approvalId ||
|
|
1135
|
+
run.awaiting?.some(
|
|
1136
|
+
(awaitable) =>
|
|
1137
|
+
awaitable.type === 'approval' &&
|
|
1138
|
+
awaitable.approvalId === approval.approvalId,
|
|
1139
|
+
) === true
|
|
1140
|
+
)
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1199
1143
|
function scheduleFromRow(row: ScheduleRow) {
|
|
1200
1144
|
return {
|
|
1201
1145
|
scheduleId: row.schedule_id satisfies ScheduleId,
|
|
@@ -1227,6 +1171,7 @@ function toRunSummary(row: RunRow): RunSummary {
|
|
|
1227
1171
|
workflowId: run.workflowId,
|
|
1228
1172
|
workflowVersion: run.workflowVersion,
|
|
1229
1173
|
status: run.status,
|
|
1174
|
+
awaiting: run.awaiting,
|
|
1230
1175
|
waitingFor: run.waitingFor,
|
|
1231
1176
|
pendingApproval: run.pendingApproval,
|
|
1232
1177
|
wakeAt: run.wakeAt,
|
|
@@ -1266,13 +1211,3 @@ function numberOrUndefined(value: number | string | null) {
|
|
|
1266
1211
|
function getStepId(event: WorkflowEvent) {
|
|
1267
1212
|
return 'stepId' in event ? event.stepId : undefined
|
|
1268
1213
|
}
|
|
1269
|
-
|
|
1270
|
-
function qualifiedTableName(schema: string | undefined, table: string) {
|
|
1271
|
-
return schema
|
|
1272
|
-
? `${quoteIdent(schema)}.${quoteIdent(table)}`
|
|
1273
|
-
: quoteIdent(table)
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
function quoteIdent(identifier: string) {
|
|
1277
|
-
return `"${identifier.replaceAll('"', '""')}"`
|
|
1278
|
-
}
|
package/src/tables.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bigint,
|
|
3
|
+
boolean,
|
|
4
|
+
index,
|
|
5
|
+
integer,
|
|
6
|
+
jsonb,
|
|
7
|
+
pgTable,
|
|
8
|
+
primaryKey,
|
|
9
|
+
text,
|
|
10
|
+
} from 'drizzle-orm/pg-core'
|
|
11
|
+
|
|
12
|
+
export const workflowSchemaMigrations = pgTable('workflow_schema_migrations', {
|
|
13
|
+
migrationId: text('migration_id').primaryKey(),
|
|
14
|
+
packageName: text('package_name').notNull(),
|
|
15
|
+
packageVersion: text('package_version'),
|
|
16
|
+
appliedAt: bigint('applied_at', { mode: 'number' }).notNull(),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const workflowRuns = pgTable(
|
|
20
|
+
'workflow_runs',
|
|
21
|
+
{
|
|
22
|
+
runId: text('run_id').primaryKey(),
|
|
23
|
+
workflowId: text('workflow_id').notNull(),
|
|
24
|
+
workflowVersion: text('workflow_version'),
|
|
25
|
+
status: text('status').notNull(),
|
|
26
|
+
input: jsonb('input').notNull(),
|
|
27
|
+
output: jsonb('output'),
|
|
28
|
+
error: jsonb('error'),
|
|
29
|
+
awaiting: jsonb('awaiting'),
|
|
30
|
+
waitingFor: jsonb('waiting_for'),
|
|
31
|
+
pendingApproval: jsonb('pending_approval'),
|
|
32
|
+
wakeAt: bigint('wake_at', { mode: 'number' }),
|
|
33
|
+
leaseOwner: text('lease_owner'),
|
|
34
|
+
leaseExpiresAt: bigint('lease_expires_at', { mode: 'number' }),
|
|
35
|
+
createdAt: bigint('created_at', { mode: 'number' }).notNull(),
|
|
36
|
+
updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
|
|
37
|
+
},
|
|
38
|
+
(table) => [
|
|
39
|
+
index('workflow_runs_status_idx').on(table.status, table.updatedAt),
|
|
40
|
+
index('workflow_runs_lease_idx').on(table.status, table.leaseExpiresAt),
|
|
41
|
+
],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
export const workflowRunStates = pgTable('workflow_run_states', {
|
|
45
|
+
runId: text('run_id').primaryKey(),
|
|
46
|
+
workflowId: text('workflow_id').notNull(),
|
|
47
|
+
workflowVersion: text('workflow_version'),
|
|
48
|
+
status: text('status').notNull(),
|
|
49
|
+
input: jsonb('input').notNull(),
|
|
50
|
+
output: jsonb('output'),
|
|
51
|
+
error: jsonb('error'),
|
|
52
|
+
awaiting: jsonb('awaiting'),
|
|
53
|
+
waitingFor: jsonb('waiting_for'),
|
|
54
|
+
pendingApproval: jsonb('pending_approval'),
|
|
55
|
+
createdAt: bigint('created_at', { mode: 'number' }).notNull(),
|
|
56
|
+
updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export const workflowEventLocks = pgTable('workflow_event_locks', {
|
|
60
|
+
runId: text('run_id').primaryKey(),
|
|
61
|
+
createdAt: bigint('created_at', { mode: 'number' }).notNull(),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const workflowEvents = pgTable(
|
|
65
|
+
'workflow_events',
|
|
66
|
+
{
|
|
67
|
+
runId: text('run_id').notNull(),
|
|
68
|
+
eventIndex: integer('event_index').notNull(),
|
|
69
|
+
eventType: text('event_type').notNull(),
|
|
70
|
+
stepId: text('step_id'),
|
|
71
|
+
event: jsonb('event').notNull(),
|
|
72
|
+
createdAt: bigint('created_at', { mode: 'number' }).notNull(),
|
|
73
|
+
},
|
|
74
|
+
(table) => [
|
|
75
|
+
primaryKey({ columns: [table.runId, table.eventIndex] }),
|
|
76
|
+
index('workflow_events_type_idx').on(table.runId, table.eventType),
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
export const workflowTimers = pgTable(
|
|
81
|
+
'workflow_timers',
|
|
82
|
+
{
|
|
83
|
+
runId: text('run_id').notNull(),
|
|
84
|
+
signalId: text('signal_id').notNull(),
|
|
85
|
+
workflowId: text('workflow_id').notNull(),
|
|
86
|
+
workflowVersion: text('workflow_version'),
|
|
87
|
+
wakeAt: bigint('wake_at', { mode: 'number' }).notNull(),
|
|
88
|
+
leaseOwner: text('lease_owner'),
|
|
89
|
+
leaseExpiresAt: bigint('lease_expires_at', { mode: 'number' }),
|
|
90
|
+
},
|
|
91
|
+
(table) => [
|
|
92
|
+
primaryKey({ columns: [table.runId, table.signalId] }),
|
|
93
|
+
index('workflow_timers_due_idx').on(table.wakeAt, table.leaseExpiresAt),
|
|
94
|
+
],
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
export const workflowSignalDeliveries = pgTable(
|
|
98
|
+
'workflow_signal_deliveries',
|
|
99
|
+
{
|
|
100
|
+
runId: text('run_id').notNull(),
|
|
101
|
+
signalId: text('signal_id').notNull(),
|
|
102
|
+
createdAt: bigint('created_at', { mode: 'number' }).notNull(),
|
|
103
|
+
},
|
|
104
|
+
(table) => [primaryKey({ columns: [table.runId, table.signalId] })],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
export const workflowSchedules = pgTable(
|
|
108
|
+
'workflow_schedules',
|
|
109
|
+
{
|
|
110
|
+
scheduleId: text('schedule_id').primaryKey(),
|
|
111
|
+
workflowId: text('workflow_id').notNull(),
|
|
112
|
+
workflowVersion: text('workflow_version'),
|
|
113
|
+
schedule: jsonb('schedule').notNull(),
|
|
114
|
+
overlapPolicy: text('overlap_policy').notNull(),
|
|
115
|
+
input: jsonb('input'),
|
|
116
|
+
nextFireAt: bigint('next_fire_at', { mode: 'number' }),
|
|
117
|
+
enabled: boolean('enabled').notNull(),
|
|
118
|
+
updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
|
|
119
|
+
},
|
|
120
|
+
(table) => [
|
|
121
|
+
index('workflow_schedules_due_idx').on(table.enabled, table.nextFireAt),
|
|
122
|
+
],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
export const workflowScheduleBuckets = pgTable(
|
|
126
|
+
'workflow_schedule_buckets',
|
|
127
|
+
{
|
|
128
|
+
scheduleId: text('schedule_id').notNull(),
|
|
129
|
+
bucketId: text('bucket_id').notNull(),
|
|
130
|
+
workflowId: text('workflow_id').notNull(),
|
|
131
|
+
workflowVersion: text('workflow_version'),
|
|
132
|
+
runId: text('run_id').notNull(),
|
|
133
|
+
fireAt: bigint('fire_at', { mode: 'number' }).notNull(),
|
|
134
|
+
input: jsonb('input'),
|
|
135
|
+
overlapPolicy: text('overlap_policy').notNull(),
|
|
136
|
+
status: text('status').notNull(),
|
|
137
|
+
leaseOwner: text('lease_owner'),
|
|
138
|
+
leaseExpiresAt: bigint('lease_expires_at', { mode: 'number' }),
|
|
139
|
+
startedAt: bigint('started_at', { mode: 'number' }),
|
|
140
|
+
},
|
|
141
|
+
(table) => [
|
|
142
|
+
primaryKey({ columns: [table.scheduleId, table.bucketId] }),
|
|
143
|
+
index('workflow_schedule_buckets_lease_idx').on(
|
|
144
|
+
table.status,
|
|
145
|
+
table.fireAt,
|
|
146
|
+
table.leaseExpiresAt,
|
|
147
|
+
),
|
|
148
|
+
],
|
|
149
|
+
)
|