@sonamu-kit/tasks 0.0.2 → 0.1.1
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/dist/client.test.js +2 -1
- package/dist/client.test.js.map +1 -1
- package/dist/database/backend.d.ts +7 -8
- package/dist/database/backend.d.ts.map +1 -1
- package/dist/database/backend.js +97 -39
- package/dist/database/backend.js.map +1 -1
- package/dist/database/backend.test.js +3 -1
- package/dist/database/backend.test.js.map +1 -1
- package/dist/database/base.d.ts +2 -2
- package/dist/database/base.d.ts.map +1 -1
- package/dist/database/base.js +17 -5
- package/dist/database/base.js.map +1 -1
- package/dist/execution.test.js +4 -2
- package/dist/execution.test.js.map +1 -1
- package/dist/practices/01-remote-workflow.js +2 -1
- package/dist/practices/01-remote-workflow.js.map +1 -1
- package/dist/testing/connection.d.ts +1 -1
- package/dist/testing/connection.d.ts.map +1 -1
- package/dist/testing/connection.js +5 -4
- package/dist/testing/connection.js.map +1 -1
- package/dist/worker.test.js +2 -1
- package/dist/worker.test.js.map +1 -1
- package/package.json +1 -1
- package/scripts/migrate.ts +1 -4
- package/src/client.test.ts +2 -1
- package/src/database/backend.test.ts +3 -1
- package/src/database/backend.ts +124 -45
- package/src/database/base.ts +12 -7
- package/src/execution.test.ts +4 -2
- package/src/practices/01-remote-workflow.ts +2 -1
- package/src/testing/connection.ts +5 -3
- package/src/worker.test.ts +2 -1
- package/templates/openworkflow.config.ts +1 -1
package/src/database/backend.ts
CHANGED
|
@@ -40,18 +40,76 @@ interface BackendPostgresOptions {
|
|
|
40
40
|
* Manages a connection to a Postgres database for workflow operations.
|
|
41
41
|
*/
|
|
42
42
|
export class BackendPostgres implements Backend {
|
|
43
|
-
private
|
|
43
|
+
private config: Knex.Config;
|
|
44
44
|
private namespaceId: string;
|
|
45
45
|
private usePubSub: boolean;
|
|
46
46
|
private pubsub: PostgresPubSub | null = null;
|
|
47
|
+
private initialized: boolean = false;
|
|
48
|
+
private runMigrations: boolean;
|
|
49
|
+
|
|
50
|
+
private _knex: Knex | null = null;
|
|
51
|
+
private get knex(): Knex {
|
|
52
|
+
if (!this._knex) {
|
|
53
|
+
this._knex = knex(this.config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this._knex;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
constructor(config: Knex.Config, options?: BackendPostgresOptions) {
|
|
60
|
+
this.config = {
|
|
61
|
+
...config,
|
|
62
|
+
postProcessResponse: (result, _queryContext) => {
|
|
63
|
+
if (result === null || result === undefined) {
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (config?.postProcessResponse) {
|
|
68
|
+
result = config.postProcessResponse(result, _queryContext);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const camelizeRow = (row: Record<string, unknown>) =>
|
|
72
|
+
Object.fromEntries(
|
|
73
|
+
Object.entries(row).map(([key, value]) => [camelize(key, true), value]),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (Array.isArray(result)) {
|
|
77
|
+
return result.map(camelizeRow);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return camelizeRow(result);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const { namespaceId, usePubSub, runMigrations } = {
|
|
85
|
+
namespaceId: DEFAULT_NAMESPACE_ID,
|
|
86
|
+
usePubSub: true,
|
|
87
|
+
runMigrations: true,
|
|
88
|
+
...options,
|
|
89
|
+
};
|
|
47
90
|
|
|
48
|
-
private constructor(knex: Knex, namespaceId: string, usePubSub: boolean) {
|
|
49
|
-
this.knex = knex;
|
|
50
91
|
this.namespaceId = namespaceId;
|
|
51
92
|
this.usePubSub = usePubSub;
|
|
93
|
+
this.runMigrations = runMigrations;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async initialize() {
|
|
97
|
+
if (this.initialized) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.runMigrations) {
|
|
102
|
+
await migrate(this.config, DEFAULT_SCHEMA);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.initialized = true;
|
|
52
106
|
}
|
|
53
107
|
|
|
54
108
|
async subscribe(callback: OnSubscribed) {
|
|
109
|
+
if (!this.initialized) {
|
|
110
|
+
throw new Error("Backend not initialized");
|
|
111
|
+
}
|
|
112
|
+
|
|
55
113
|
if (!this.usePubSub) {
|
|
56
114
|
return;
|
|
57
115
|
}
|
|
@@ -64,6 +122,10 @@ export class BackendPostgres implements Backend {
|
|
|
64
122
|
}
|
|
65
123
|
|
|
66
124
|
async publish(payload?: string): Promise<void> {
|
|
125
|
+
if (!this.initialized) {
|
|
126
|
+
throw new Error("Backend not initialized");
|
|
127
|
+
}
|
|
128
|
+
|
|
67
129
|
if (!this.usePubSub) {
|
|
68
130
|
return;
|
|
69
131
|
}
|
|
@@ -75,56 +137,21 @@ export class BackendPostgres implements Backend {
|
|
|
75
137
|
);
|
|
76
138
|
}
|
|
77
139
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* false.
|
|
82
|
-
*/
|
|
83
|
-
static async connect(
|
|
84
|
-
dbConf: Knex.Config,
|
|
85
|
-
options?: BackendPostgresOptions,
|
|
86
|
-
): Promise<BackendPostgres> {
|
|
87
|
-
const postProcessResponse: Knex.Config["postProcessResponse"] = (result, _queryContext) => {
|
|
88
|
-
if (result === null || result === undefined) {
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (dbConf?.postProcessResponse) {
|
|
93
|
-
result = dbConf.postProcessResponse(result, _queryContext);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const camelizeRow = (row: Record<string, unknown>) =>
|
|
97
|
-
Object.fromEntries(Object.entries(row).map(([key, value]) => [camelize(key, true), value]));
|
|
98
|
-
|
|
99
|
-
if (Array.isArray(result)) {
|
|
100
|
-
return result.map(camelizeRow);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return camelizeRow(result);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const { namespaceId, runMigrations, usePubSub } = {
|
|
107
|
-
namespaceId: DEFAULT_NAMESPACE_ID,
|
|
108
|
-
runMigrations: true,
|
|
109
|
-
usePubSub: true,
|
|
110
|
-
...options,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const knexInstance = knex({ ...dbConf, postProcessResponse });
|
|
114
|
-
if (runMigrations) {
|
|
115
|
-
await migrate(knexInstance, DEFAULT_SCHEMA);
|
|
140
|
+
async stop(): Promise<void> {
|
|
141
|
+
if (!this.initialized) {
|
|
142
|
+
return;
|
|
116
143
|
}
|
|
117
144
|
|
|
118
|
-
return new BackendPostgres(knexInstance, namespaceId, usePubSub);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async stop(): Promise<void> {
|
|
122
145
|
await this.pubsub?.destroy();
|
|
123
146
|
this.pubsub = null;
|
|
124
147
|
await this.knex.destroy();
|
|
125
148
|
}
|
|
126
149
|
|
|
127
150
|
async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {
|
|
151
|
+
if (!this.initialized) {
|
|
152
|
+
throw new Error("Backend not initialized");
|
|
153
|
+
}
|
|
154
|
+
|
|
128
155
|
const qb = this.knex
|
|
129
156
|
.withSchema(DEFAULT_SCHEMA)
|
|
130
157
|
.table("workflow_runs")
|
|
@@ -155,6 +182,10 @@ export class BackendPostgres implements Backend {
|
|
|
155
182
|
}
|
|
156
183
|
|
|
157
184
|
async getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null> {
|
|
185
|
+
if (!this.initialized) {
|
|
186
|
+
throw new Error("Backend not initialized");
|
|
187
|
+
}
|
|
188
|
+
|
|
158
189
|
const workflowRun = await this.knex
|
|
159
190
|
.withSchema(DEFAULT_SCHEMA)
|
|
160
191
|
.table("workflow_runs")
|
|
@@ -189,6 +220,10 @@ export class BackendPostgres implements Backend {
|
|
|
189
220
|
}
|
|
190
221
|
|
|
191
222
|
async listWorkflowRuns(params: ListWorkflowRunsParams): Promise<PaginatedResponse<WorkflowRun>> {
|
|
223
|
+
if (!this.initialized) {
|
|
224
|
+
throw new Error("Backend not initialized");
|
|
225
|
+
}
|
|
226
|
+
|
|
192
227
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
193
228
|
const { after, before } = params;
|
|
194
229
|
|
|
@@ -232,6 +267,10 @@ export class BackendPostgres implements Backend {
|
|
|
232
267
|
}
|
|
233
268
|
|
|
234
269
|
async claimWorkflowRun(params: ClaimWorkflowRunParams): Promise<WorkflowRun | null> {
|
|
270
|
+
if (!this.initialized) {
|
|
271
|
+
throw new Error("Backend not initialized");
|
|
272
|
+
}
|
|
273
|
+
|
|
235
274
|
const claimed = await this.knex
|
|
236
275
|
.with("expired", (qb) =>
|
|
237
276
|
qb
|
|
@@ -288,6 +327,10 @@ export class BackendPostgres implements Backend {
|
|
|
288
327
|
}
|
|
289
328
|
|
|
290
329
|
async extendWorkflowRunLease(params: ExtendWorkflowRunLeaseParams): Promise<WorkflowRun> {
|
|
330
|
+
if (!this.initialized) {
|
|
331
|
+
throw new Error("Backend not initialized");
|
|
332
|
+
}
|
|
333
|
+
|
|
291
334
|
const [updated] = await this.knex
|
|
292
335
|
.withSchema(DEFAULT_SCHEMA)
|
|
293
336
|
.table("workflow_runs")
|
|
@@ -309,6 +352,10 @@ export class BackendPostgres implements Backend {
|
|
|
309
352
|
}
|
|
310
353
|
|
|
311
354
|
async sleepWorkflowRun(params: SleepWorkflowRunParams): Promise<WorkflowRun> {
|
|
355
|
+
if (!this.initialized) {
|
|
356
|
+
throw new Error("Backend not initialized");
|
|
357
|
+
}
|
|
358
|
+
|
|
312
359
|
// 'succeeded' status is deprecated
|
|
313
360
|
const [updated] = await this.knex
|
|
314
361
|
.withSchema(DEFAULT_SCHEMA)
|
|
@@ -333,6 +380,10 @@ export class BackendPostgres implements Backend {
|
|
|
333
380
|
}
|
|
334
381
|
|
|
335
382
|
async completeWorkflowRun(params: CompleteWorkflowRunParams): Promise<WorkflowRun> {
|
|
383
|
+
if (!this.initialized) {
|
|
384
|
+
throw new Error("Backend not initialized");
|
|
385
|
+
}
|
|
386
|
+
|
|
336
387
|
const [updated] = await this.knex
|
|
337
388
|
.withSchema(DEFAULT_SCHEMA)
|
|
338
389
|
.table("workflow_runs")
|
|
@@ -359,6 +410,10 @@ export class BackendPostgres implements Backend {
|
|
|
359
410
|
}
|
|
360
411
|
|
|
361
412
|
async failWorkflowRun(params: FailWorkflowRunParams): Promise<WorkflowRun> {
|
|
413
|
+
if (!this.initialized) {
|
|
414
|
+
throw new Error("Backend not initialized");
|
|
415
|
+
}
|
|
416
|
+
|
|
362
417
|
const { workflowRunId, error } = params;
|
|
363
418
|
const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;
|
|
364
419
|
|
|
@@ -403,6 +458,10 @@ export class BackendPostgres implements Backend {
|
|
|
403
458
|
}
|
|
404
459
|
|
|
405
460
|
async cancelWorkflowRun(params: CancelWorkflowRunParams): Promise<WorkflowRun> {
|
|
461
|
+
if (!this.initialized) {
|
|
462
|
+
throw new Error("Backend not initialized");
|
|
463
|
+
}
|
|
464
|
+
|
|
406
465
|
const [updated] = await this.knex
|
|
407
466
|
.withSchema(DEFAULT_SCHEMA)
|
|
408
467
|
.table("workflow_runs")
|
|
@@ -447,6 +506,10 @@ export class BackendPostgres implements Backend {
|
|
|
447
506
|
}
|
|
448
507
|
|
|
449
508
|
async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {
|
|
509
|
+
if (!this.initialized) {
|
|
510
|
+
throw new Error("Backend not initialized");
|
|
511
|
+
}
|
|
512
|
+
|
|
450
513
|
const [stepAttempt] = await this.knex
|
|
451
514
|
.withSchema(DEFAULT_SCHEMA)
|
|
452
515
|
.table("step_attempts")
|
|
@@ -473,6 +536,10 @@ export class BackendPostgres implements Backend {
|
|
|
473
536
|
}
|
|
474
537
|
|
|
475
538
|
async getStepAttempt(params: GetStepAttemptParams): Promise<StepAttempt | null> {
|
|
539
|
+
if (!this.initialized) {
|
|
540
|
+
throw new Error("Backend not initialized");
|
|
541
|
+
}
|
|
542
|
+
|
|
476
543
|
const stepAttempt = await this.knex
|
|
477
544
|
.withSchema(DEFAULT_SCHEMA)
|
|
478
545
|
.table("step_attempts")
|
|
@@ -484,6 +551,10 @@ export class BackendPostgres implements Backend {
|
|
|
484
551
|
}
|
|
485
552
|
|
|
486
553
|
async listStepAttempts(params: ListStepAttemptsParams): Promise<PaginatedResponse<StepAttempt>> {
|
|
554
|
+
if (!this.initialized) {
|
|
555
|
+
throw new Error("Backend not initialized");
|
|
556
|
+
}
|
|
557
|
+
|
|
487
558
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
488
559
|
const { after, before } = params;
|
|
489
560
|
|
|
@@ -569,6 +640,10 @@ export class BackendPostgres implements Backend {
|
|
|
569
640
|
}
|
|
570
641
|
|
|
571
642
|
async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt> {
|
|
643
|
+
if (!this.initialized) {
|
|
644
|
+
throw new Error("Backend not initialized");
|
|
645
|
+
}
|
|
646
|
+
|
|
572
647
|
const [updated] = await this.knex
|
|
573
648
|
.withSchema(DEFAULT_SCHEMA)
|
|
574
649
|
.table("step_attempts as sa")
|
|
@@ -598,6 +673,10 @@ export class BackendPostgres implements Backend {
|
|
|
598
673
|
}
|
|
599
674
|
|
|
600
675
|
async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {
|
|
676
|
+
if (!this.initialized) {
|
|
677
|
+
throw new Error("Backend not initialized");
|
|
678
|
+
}
|
|
679
|
+
|
|
601
680
|
const [updated] = await this.knex
|
|
602
681
|
.withSchema(DEFAULT_SCHEMA)
|
|
603
682
|
.table("step_attempts as sa")
|
package/src/database/base.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import
|
|
2
|
+
import knex, { type Knex } from "knex";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_SCHEMA = "sonamu_tasks";
|
|
5
5
|
|
|
@@ -7,12 +7,17 @@ export const DEFAULT_SCHEMA = "sonamu_tasks";
|
|
|
7
7
|
* migrate applies pending migrations to the database. Does nothing if the
|
|
8
8
|
* database is already up to date.
|
|
9
9
|
*/
|
|
10
|
-
export async function migrate(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export async function migrate(config: Knex.Config, schema: string) {
|
|
11
|
+
const instance = knex({ ...config, pool: { min: 1, max: 1 } });
|
|
12
|
+
try {
|
|
13
|
+
await instance.schema.createSchemaIfNotExists(schema);
|
|
14
|
+
await instance.migrate.latest({
|
|
15
|
+
directory: path.join(import.meta.dirname, "migrations"),
|
|
16
|
+
schemaName: schema,
|
|
17
|
+
});
|
|
18
|
+
} finally {
|
|
19
|
+
await instance.destroy();
|
|
20
|
+
}
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
/**
|
package/src/execution.test.ts
CHANGED
|
@@ -8,10 +8,11 @@ describe("StepExecutor", () => {
|
|
|
8
8
|
let backend: BackendPostgres;
|
|
9
9
|
|
|
10
10
|
beforeAll(async () => {
|
|
11
|
-
backend =
|
|
11
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
12
12
|
namespaceId: randomUUID(),
|
|
13
13
|
runMigrations: false,
|
|
14
14
|
});
|
|
15
|
+
await backend.initialize();
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
afterAll(async () => {
|
|
@@ -164,10 +165,11 @@ describe("executeWorkflow", () => {
|
|
|
164
165
|
let backend: BackendPostgres;
|
|
165
166
|
|
|
166
167
|
beforeAll(async () => {
|
|
167
|
-
backend =
|
|
168
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
168
169
|
namespaceId: randomUUID(),
|
|
169
170
|
runMigrations: false,
|
|
170
171
|
});
|
|
172
|
+
await backend.initialize();
|
|
171
173
|
});
|
|
172
174
|
|
|
173
175
|
afterAll(async () => {
|
|
@@ -10,11 +10,12 @@ async function getBackend(): Promise<BackendPostgres> {
|
|
|
10
10
|
return _backend;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
_backend =
|
|
13
|
+
_backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
14
14
|
runMigrations: true,
|
|
15
15
|
namespaceId: randomUUID(),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
await _backend.initialize();
|
|
18
19
|
return _backend;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
2
|
+
import type { Knex } from "knex";
|
|
3
3
|
import { BackendPostgres } from "../database/backend";
|
|
4
4
|
import { migrate as baseMigrate, DEFAULT_SCHEMA } from "../database/base";
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ export const KNEX_GLOBAL_CONFIG: Knex.Config = {
|
|
|
20
20
|
} as const;
|
|
21
21
|
|
|
22
22
|
export async function migrate(): Promise<void> {
|
|
23
|
-
await baseMigrate(
|
|
23
|
+
await baseMigrate(KNEX_GLOBAL_CONFIG, DEFAULT_SCHEMA);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export async function createBackend(): Promise<BackendPostgres> {
|
|
@@ -28,9 +28,11 @@ export async function createBackend(): Promise<BackendPostgres> {
|
|
|
28
28
|
return backend;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
backend =
|
|
31
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
32
32
|
namespaceId: randomUUID(),
|
|
33
|
+
runMigrations: false,
|
|
33
34
|
});
|
|
35
|
+
await backend.initialize();
|
|
34
36
|
|
|
35
37
|
return backend;
|
|
36
38
|
}
|
package/src/worker.test.ts
CHANGED
|
@@ -8,10 +8,11 @@ describe("Worker", () => {
|
|
|
8
8
|
let backend: BackendPostgres;
|
|
9
9
|
|
|
10
10
|
beforeEach(async () => {
|
|
11
|
-
backend =
|
|
11
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
12
12
|
namespaceId: randomUUID(),
|
|
13
13
|
runMigrations: false,
|
|
14
14
|
});
|
|
15
|
+
await backend.initialize();
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
afterEach(async () => {
|