@sonamu-kit/tasks 0.2.0 → 0.3.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/.oxlintrc.json +3 -0
- package/AGENTS.md +21 -0
- package/dist/backend.d.ts +126 -107
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +4 -1
- package/dist/backend.js.map +1 -1
- package/dist/client.d.ts +145 -132
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +219 -213
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +15 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +22 -17
- package/dist/config.js.map +1 -1
- package/dist/core/duration.d.ts +5 -4
- package/dist/core/duration.d.ts.map +1 -1
- package/dist/core/duration.js +54 -59
- package/dist/core/duration.js.map +1 -1
- package/dist/core/error.d.ts +10 -7
- package/dist/core/error.d.ts.map +1 -1
- package/dist/core/error.js +21 -21
- package/dist/core/error.js.map +1 -1
- package/dist/core/json.d.ts +8 -3
- package/dist/core/json.d.ts.map +1 -1
- package/dist/core/result.d.ts +10 -14
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +21 -16
- package/dist/core/result.js.map +1 -1
- package/dist/core/retry.d.ts +37 -31
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +44 -51
- package/dist/core/retry.js.map +1 -1
- package/dist/core/schema.d.ts +57 -53
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/step.d.ts +28 -78
- package/dist/core/step.d.ts.map +1 -1
- package/dist/core/step.js +53 -63
- package/dist/core/step.js.map +1 -1
- package/dist/core/workflow.d.ts +33 -61
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/core/workflow.js +31 -41
- package/dist/core/workflow.js.map +1 -1
- package/dist/database/backend.d.ts +53 -46
- package/dist/database/backend.d.ts.map +1 -1
- package/dist/database/backend.js +544 -577
- package/dist/database/backend.js.map +1 -1
- package/dist/database/base.js +48 -25
- package/dist/database/base.js.map +1 -1
- package/dist/database/migrations/20251212000000_0_init.d.ts +10 -0
- package/dist/database/migrations/20251212000000_0_init.d.ts.map +1 -0
- package/dist/database/migrations/20251212000000_0_init.js +8 -4
- package/dist/database/migrations/20251212000000_0_init.js.map +1 -1
- package/dist/database/migrations/20251212000000_1_tables.d.ts +10 -0
- package/dist/database/migrations/20251212000000_1_tables.d.ts.map +1 -0
- package/dist/database/migrations/20251212000000_1_tables.js +81 -83
- package/dist/database/migrations/20251212000000_1_tables.js.map +1 -1
- package/dist/database/migrations/20251212000000_2_fk.d.ts +10 -0
- package/dist/database/migrations/20251212000000_2_fk.d.ts.map +1 -0
- package/dist/database/migrations/20251212000000_2_fk.js +20 -43
- package/dist/database/migrations/20251212000000_2_fk.js.map +1 -1
- package/dist/database/migrations/20251212000000_3_indexes.d.ts +10 -0
- package/dist/database/migrations/20251212000000_3_indexes.d.ts.map +1 -0
- package/dist/database/migrations/20251212000000_3_indexes.js +88 -102
- package/dist/database/migrations/20251212000000_3_indexes.js.map +1 -1
- package/dist/database/pubsub.d.ts +7 -16
- package/dist/database/pubsub.d.ts.map +1 -1
- package/dist/database/pubsub.js +75 -73
- package/dist/database/pubsub.js.map +1 -1
- package/dist/execution.d.ts +20 -59
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +175 -188
- package/dist/execution.js.map +1 -1
- package/dist/index.d.ts +5 -8
- package/dist/index.js +5 -5
- package/dist/internal.d.ts +12 -13
- package/dist/internal.js +4 -4
- package/dist/registry.d.ts +33 -27
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +58 -49
- package/dist/registry.js.map +1 -1
- package/dist/worker.d.ts +57 -50
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +194 -199
- package/dist/worker.js.map +1 -1
- package/dist/workflow.d.ts +26 -30
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +20 -15
- package/dist/workflow.js.map +1 -1
- package/nodemon.json +1 -1
- package/package.json +17 -19
- package/src/backend.ts +25 -9
- package/src/chaos.test.ts +3 -1
- package/src/client.test.ts +2 -0
- package/src/client.ts +30 -8
- package/src/config.test.ts +1 -0
- package/src/config.ts +3 -2
- package/src/core/duration.test.ts +2 -1
- package/src/core/duration.ts +1 -1
- package/src/core/error.test.ts +1 -0
- package/src/core/error.ts +1 -1
- package/src/core/result.test.ts +1 -0
- package/src/core/retry.test.ts +3 -2
- package/src/core/retry.ts +1 -1
- package/src/core/schema.ts +2 -2
- package/src/core/step.test.ts +2 -1
- package/src/core/step.ts +4 -3
- package/src/core/workflow.test.ts +2 -1
- package/src/core/workflow.ts +4 -3
- package/src/database/backend.test.ts +1 -0
- package/src/database/backend.testsuite.ts +44 -40
- package/src/database/backend.ts +207 -25
- package/src/database/base.test.ts +41 -0
- package/src/database/base.ts +51 -2
- package/src/database/migrations/20251212000000_0_init.ts +2 -1
- package/src/database/migrations/20251212000000_1_tables.ts +2 -1
- package/src/database/migrations/20251212000000_2_fk.ts +2 -1
- package/src/database/migrations/20251212000000_3_indexes.ts +2 -1
- package/src/database/pubsub.test.ts +6 -3
- package/src/database/pubsub.ts +55 -33
- package/src/execution.test.ts +2 -0
- package/src/execution.ts +49 -10
- package/src/internal.ts +15 -15
- package/src/practices/01-remote-workflow.ts +1 -0
- package/src/registry.test.ts +1 -0
- package/src/registry.ts +1 -1
- package/src/testing/connection.ts +3 -1
- package/src/worker.test.ts +2 -0
- package/src/worker.ts +30 -9
- package/src/workflow.test.ts +1 -0
- package/src/workflow.ts +3 -3
- package/templates/openworkflow.config.ts +2 -1
- package/tsdown.config.ts +31 -0
- package/.swcrc +0 -17
- package/dist/chaos.test.d.ts +0 -2
- package/dist/chaos.test.d.ts.map +0 -1
- package/dist/chaos.test.js +0 -92
- package/dist/chaos.test.js.map +0 -1
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.d.ts.map +0 -1
- package/dist/client.test.js +0 -340
- package/dist/client.test.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -24
- package/dist/config.test.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 -265
- package/dist/core/duration.test.js.map +0 -1
- package/dist/core/error.test.d.ts +0 -2
- package/dist/core/error.test.d.ts.map +0 -1
- package/dist/core/error.test.js +0 -63
- package/dist/core/error.test.js.map +0 -1
- package/dist/core/json.js +0 -3
- package/dist/core/json.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 -19
- 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 -198
- package/dist/core/retry.test.js.map +0 -1
- package/dist/core/schema.js +0 -4
- package/dist/core/schema.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 -356
- 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 -172
- package/dist/core/workflow.test.js.map +0 -1
- package/dist/database/backend.test.d.ts +0 -2
- package/dist/database/backend.test.d.ts.map +0 -1
- package/dist/database/backend.test.js +0 -19
- package/dist/database/backend.test.js.map +0 -1
- package/dist/database/backend.testsuite.d.ts +0 -20
- package/dist/database/backend.testsuite.d.ts.map +0 -1
- package/dist/database/backend.testsuite.js +0 -1280
- package/dist/database/backend.testsuite.js.map +0 -1
- package/dist/database/base.d.ts +0 -12
- package/dist/database/base.d.ts.map +0 -1
- package/dist/database/pubsub.test.d.ts +0 -2
- package/dist/database/pubsub.test.d.ts.map +0 -1
- package/dist/database/pubsub.test.js +0 -86
- package/dist/database/pubsub.test.js.map +0 -1
- package/dist/execution.test.d.ts +0 -2
- package/dist/execution.test.d.ts.map +0 -1
- package/dist/execution.test.js +0 -662
- package/dist/execution.test.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal.d.ts.map +0 -1
- package/dist/internal.js.map +0 -1
- package/dist/practices/01-remote-workflow.d.ts +0 -2
- package/dist/practices/01-remote-workflow.d.ts.map +0 -1
- package/dist/practices/01-remote-workflow.js +0 -70
- package/dist/practices/01-remote-workflow.js.map +0 -1
- package/dist/registry.test.d.ts +0 -2
- package/dist/registry.test.d.ts.map +0 -1
- package/dist/registry.test.js +0 -95
- package/dist/registry.test.js.map +0 -1
- package/dist/testing/connection.d.ts +0 -7
- package/dist/testing/connection.d.ts.map +0 -1
- package/dist/testing/connection.js +0 -39
- package/dist/testing/connection.js.map +0 -1
- package/dist/worker.test.d.ts +0 -2
- package/dist/worker.test.d.ts.map +0 -1
- package/dist/worker.test.js +0 -1164
- package/dist/worker.test.js.map +0 -1
- package/dist/workflow.test.d.ts +0 -2
- package/dist/workflow.test.d.ts.map +0 -1
- package/dist/workflow.test.js +0 -73
- package/dist/workflow.test.js.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
|
|
2
3
|
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
3
|
-
|
|
4
|
-
import type
|
|
5
|
-
import type
|
|
6
|
-
import type
|
|
4
|
+
|
|
5
|
+
import { type Backend } from "..//backend";
|
|
6
|
+
import { type SerializableRetryPolicy } from "../core/retry";
|
|
7
|
+
import { type StepAttempt } from "../core/step";
|
|
8
|
+
import { type WorkflowRun } from "../core/workflow";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Options for the Backend test suite.
|
|
@@ -138,7 +140,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
138
140
|
// p2
|
|
139
141
|
const page2 = await backend.listWorkflowRuns({
|
|
140
142
|
limit: 2,
|
|
141
|
-
//
|
|
143
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
142
144
|
after: page1.pagination.next!,
|
|
143
145
|
});
|
|
144
146
|
expect(page2.data).toHaveLength(2);
|
|
@@ -150,7 +152,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
150
152
|
// p3
|
|
151
153
|
const page3 = await backend.listWorkflowRuns({
|
|
152
154
|
limit: 2,
|
|
153
|
-
//
|
|
155
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
154
156
|
after: page2.pagination.next!,
|
|
155
157
|
});
|
|
156
158
|
expect(page3.data).toHaveLength(1);
|
|
@@ -161,7 +163,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
161
163
|
// p2 again
|
|
162
164
|
const page2Back = await backend.listWorkflowRuns({
|
|
163
165
|
limit: 2,
|
|
164
|
-
//
|
|
166
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
165
167
|
before: page3.pagination.prev!,
|
|
166
168
|
});
|
|
167
169
|
expect(page2Back.data).toHaveLength(2);
|
|
@@ -203,7 +205,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
203
205
|
|
|
204
206
|
const page2 = await backend.listWorkflowRuns({
|
|
205
207
|
limit: 2,
|
|
206
|
-
//
|
|
208
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
207
209
|
after: page1.pagination.next!,
|
|
208
210
|
});
|
|
209
211
|
expect(page2.data).toHaveLength(2);
|
|
@@ -213,7 +215,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
213
215
|
|
|
214
216
|
const page3 = await backend.listWorkflowRuns({
|
|
215
217
|
limit: 2,
|
|
216
|
-
//
|
|
218
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
217
219
|
after: page2.pagination.next!,
|
|
218
220
|
});
|
|
219
221
|
expect(page3.data).toHaveLength(1);
|
|
@@ -222,7 +224,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
222
224
|
|
|
223
225
|
const page2Back = await backend.listWorkflowRuns({
|
|
224
226
|
limit: 2,
|
|
225
|
-
//
|
|
227
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
226
228
|
before: page3.pagination.prev!,
|
|
227
229
|
});
|
|
228
230
|
expect(page2Back.data).toHaveLength(2);
|
|
@@ -678,7 +680,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
678
680
|
|
|
679
681
|
const created = await backend.createStepAttempt({
|
|
680
682
|
workflowRunId: expected.workflowRunId,
|
|
681
|
-
//
|
|
683
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
682
684
|
workerId: workflowRun.workerId!,
|
|
683
685
|
stepName: expected.stepName,
|
|
684
686
|
kind: expected.kind,
|
|
@@ -704,7 +706,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
704
706
|
|
|
705
707
|
const created = await backend.createStepAttempt({
|
|
706
708
|
workflowRunId: claimed.id,
|
|
707
|
-
//
|
|
709
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
708
710
|
workerId: claimed.workerId!,
|
|
709
711
|
stepName: randomUUID(),
|
|
710
712
|
kind: "function",
|
|
@@ -733,7 +735,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
733
735
|
|
|
734
736
|
const first = await backend.createStepAttempt({
|
|
735
737
|
workflowRunId: claimed.id,
|
|
736
|
-
//
|
|
738
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
737
739
|
workerId: claimed.workerId!,
|
|
738
740
|
stepName: randomUUID(),
|
|
739
741
|
kind: "function",
|
|
@@ -743,7 +745,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
743
745
|
await backend.completeStepAttempt({
|
|
744
746
|
workflowRunId: claimed.id,
|
|
745
747
|
stepAttemptId: first.id,
|
|
746
|
-
//
|
|
748
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
747
749
|
workerId: claimed.workerId!,
|
|
748
750
|
output: { ok: true },
|
|
749
751
|
});
|
|
@@ -752,7 +754,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
752
754
|
|
|
753
755
|
const second = await backend.createStepAttempt({
|
|
754
756
|
workflowRunId: claimed.id,
|
|
755
|
-
//
|
|
757
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
756
758
|
workerId: claimed.workerId!,
|
|
757
759
|
stepName: randomUUID(),
|
|
758
760
|
kind: "function",
|
|
@@ -773,7 +775,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
773
775
|
for (let i = 0; i < 5; i++) {
|
|
774
776
|
await backend.createStepAttempt({
|
|
775
777
|
workflowRunId: claimed.id,
|
|
776
|
-
//
|
|
778
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
777
779
|
workerId: claimed.workerId!,
|
|
778
780
|
stepName: `step-${String(i)}`,
|
|
779
781
|
kind: "function",
|
|
@@ -799,7 +801,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
799
801
|
const page2 = await backend.listStepAttempts({
|
|
800
802
|
workflowRunId: claimed.id,
|
|
801
803
|
limit: 2,
|
|
802
|
-
//
|
|
804
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
803
805
|
after: page1.pagination.next!,
|
|
804
806
|
});
|
|
805
807
|
expect(page2.data).toHaveLength(2);
|
|
@@ -812,7 +814,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
812
814
|
const page3 = await backend.listStepAttempts({
|
|
813
815
|
workflowRunId: claimed.id,
|
|
814
816
|
limit: 2,
|
|
815
|
-
//
|
|
817
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
816
818
|
after: page2.pagination.next!,
|
|
817
819
|
});
|
|
818
820
|
expect(page3.data).toHaveLength(1);
|
|
@@ -824,7 +826,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
824
826
|
const page2Back = await backend.listStepAttempts({
|
|
825
827
|
workflowRunId: claimed.id,
|
|
826
828
|
limit: 2,
|
|
827
|
-
//
|
|
829
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
828
830
|
before: page3.pagination.prev!,
|
|
829
831
|
});
|
|
830
832
|
expect(page2Back.data).toHaveLength(2);
|
|
@@ -848,7 +850,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
848
850
|
const claimed = await createClaimedWorkflowRun(backend);
|
|
849
851
|
await backend.createStepAttempt({
|
|
850
852
|
workflowRunId: claimed.id,
|
|
851
|
-
//
|
|
853
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
852
854
|
workerId: claimed.workerId!,
|
|
853
855
|
stepName: "step-1",
|
|
854
856
|
kind: "function",
|
|
@@ -880,7 +882,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
880
882
|
|
|
881
883
|
const created = await backend.createStepAttempt({
|
|
882
884
|
workflowRunId: claimed.id,
|
|
883
|
-
//
|
|
885
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
884
886
|
workerId: claimed.workerId!,
|
|
885
887
|
stepName: randomUUID(),
|
|
886
888
|
kind: "function",
|
|
@@ -892,15 +894,16 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
892
894
|
const completed = await backend.completeStepAttempt({
|
|
893
895
|
workflowRunId: claimed.id,
|
|
894
896
|
stepAttemptId: created.id,
|
|
895
|
-
//
|
|
897
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
896
898
|
workerId: claimed.workerId!,
|
|
897
899
|
output,
|
|
898
900
|
});
|
|
899
901
|
|
|
900
|
-
expect(completed
|
|
901
|
-
expect(completed
|
|
902
|
-
expect(completed
|
|
903
|
-
expect(completed
|
|
902
|
+
expect(completed).not.toBeNull();
|
|
903
|
+
expect(completed?.status).toBe("completed");
|
|
904
|
+
expect(completed?.output).toEqual(output);
|
|
905
|
+
expect(completed?.error).toBeNull();
|
|
906
|
+
expect(completed?.finishedAt).not.toBeNull();
|
|
904
907
|
|
|
905
908
|
const fetched = await backend.getStepAttempt({
|
|
906
909
|
stepAttemptId: created.id,
|
|
@@ -924,7 +927,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
924
927
|
|
|
925
928
|
const stepAttempt = await backend.createStepAttempt({
|
|
926
929
|
workflowRunId: claimed.id,
|
|
927
|
-
//
|
|
930
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
928
931
|
workerId: claimed.workerId!,
|
|
929
932
|
stepName: randomUUID(),
|
|
930
933
|
kind: "function",
|
|
@@ -935,7 +938,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
935
938
|
// complete the workflow so it's no longer running
|
|
936
939
|
await backend.completeWorkflowRun({
|
|
937
940
|
workflowRunId: claimed.id,
|
|
938
|
-
//
|
|
941
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
939
942
|
workerId: claimed.workerId!,
|
|
940
943
|
output: null,
|
|
941
944
|
});
|
|
@@ -945,7 +948,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
945
948
|
backend.completeStepAttempt({
|
|
946
949
|
workflowRunId: claimed.id,
|
|
947
950
|
stepAttemptId: stepAttempt.id,
|
|
948
|
-
//
|
|
951
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
949
952
|
workerId: claimed.workerId!,
|
|
950
953
|
output: { foo: "bar" },
|
|
951
954
|
}),
|
|
@@ -962,7 +965,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
962
965
|
backend.completeStepAttempt({
|
|
963
966
|
workflowRunId: claimed.id,
|
|
964
967
|
stepAttemptId: randomUUID(),
|
|
965
|
-
//
|
|
968
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
966
969
|
workerId: claimed.workerId!,
|
|
967
970
|
output: { foo: "bar" },
|
|
968
971
|
}),
|
|
@@ -986,7 +989,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
986
989
|
|
|
987
990
|
const created = await backend.createStepAttempt({
|
|
988
991
|
workflowRunId: claimed.id,
|
|
989
|
-
//
|
|
992
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
990
993
|
workerId: claimed.workerId!,
|
|
991
994
|
stepName: randomUUID(),
|
|
992
995
|
kind: "function",
|
|
@@ -998,15 +1001,16 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
998
1001
|
const failed = await backend.failStepAttempt({
|
|
999
1002
|
workflowRunId: claimed.id,
|
|
1000
1003
|
stepAttemptId: created.id,
|
|
1001
|
-
//
|
|
1004
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
1002
1005
|
workerId: claimed.workerId!,
|
|
1003
1006
|
error,
|
|
1004
1007
|
});
|
|
1005
1008
|
|
|
1006
|
-
expect(failed
|
|
1007
|
-
expect(failed
|
|
1008
|
-
expect(failed
|
|
1009
|
-
expect(failed
|
|
1009
|
+
expect(failed).not.toBeNull();
|
|
1010
|
+
expect(failed?.status).toBe("failed");
|
|
1011
|
+
expect(failed?.error).toEqual(error);
|
|
1012
|
+
expect(failed?.output).toBeNull();
|
|
1013
|
+
expect(failed?.finishedAt).not.toBeNull();
|
|
1010
1014
|
|
|
1011
1015
|
const fetched = await backend.getStepAttempt({
|
|
1012
1016
|
stepAttemptId: created.id,
|
|
@@ -1030,7 +1034,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
1030
1034
|
|
|
1031
1035
|
const stepAttempt = await backend.createStepAttempt({
|
|
1032
1036
|
workflowRunId: claimed.id,
|
|
1033
|
-
//
|
|
1037
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
1034
1038
|
workerId: claimed.workerId!,
|
|
1035
1039
|
stepName: randomUUID(),
|
|
1036
1040
|
kind: "function",
|
|
@@ -1041,7 +1045,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
1041
1045
|
// complete the workflow so it's no longer running
|
|
1042
1046
|
await backend.completeWorkflowRun({
|
|
1043
1047
|
workflowRunId: claimed.id,
|
|
1044
|
-
//
|
|
1048
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
1045
1049
|
workerId: claimed.workerId!,
|
|
1046
1050
|
output: null,
|
|
1047
1051
|
});
|
|
@@ -1051,7 +1055,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
1051
1055
|
backend.failStepAttempt({
|
|
1052
1056
|
workflowRunId: claimed.id,
|
|
1053
1057
|
stepAttemptId: stepAttempt.id,
|
|
1054
|
-
//
|
|
1058
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
1055
1059
|
workerId: claimed.workerId!,
|
|
1056
1060
|
error: { message: "nope" },
|
|
1057
1061
|
}),
|
|
@@ -1068,7 +1072,7 @@ export function testBackend(options: TestBackendOptions): void {
|
|
|
1068
1072
|
backend.failStepAttempt({
|
|
1069
1073
|
workflowRunId: claimed.id,
|
|
1070
1074
|
stepAttemptId: randomUUID(),
|
|
1071
|
-
//
|
|
1075
|
+
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion -- for test
|
|
1072
1076
|
workerId: claimed.workerId!,
|
|
1073
1077
|
error: { message: "nope" },
|
|
1074
1078
|
}),
|
package/src/database/backend.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { getLogger } from "@logtape/logtape";
|
|
2
2
|
import { camelize } from "inflection";
|
|
3
|
-
import knex
|
|
3
|
+
import knex from "knex";
|
|
4
|
+
import { type Knex } from "knex";
|
|
5
|
+
|
|
6
|
+
import { DEFAULT_NAMESPACE_ID } from "../backend";
|
|
4
7
|
import {
|
|
5
8
|
type Backend,
|
|
6
9
|
type CancelWorkflowRunParams,
|
|
@@ -9,7 +12,6 @@ import {
|
|
|
9
12
|
type CompleteWorkflowRunParams,
|
|
10
13
|
type CreateStepAttemptParams,
|
|
11
14
|
type CreateWorkflowRunParams,
|
|
12
|
-
DEFAULT_NAMESPACE_ID,
|
|
13
15
|
type ExtendWorkflowRunLeaseParams,
|
|
14
16
|
type FailStepAttemptParams,
|
|
15
17
|
type FailWorkflowRunParams,
|
|
@@ -18,13 +20,17 @@ import {
|
|
|
18
20
|
type ListStepAttemptsParams,
|
|
19
21
|
type ListWorkflowRunsParams,
|
|
20
22
|
type PaginatedResponse,
|
|
23
|
+
type PauseWorkflowRunParams,
|
|
24
|
+
type ResumeWorkflowRunParams,
|
|
21
25
|
type SleepWorkflowRunParams,
|
|
22
26
|
} from "../backend";
|
|
23
|
-
import { mergeRetryPolicy
|
|
24
|
-
import type
|
|
25
|
-
import type
|
|
27
|
+
import { mergeRetryPolicy } from "../core/retry";
|
|
28
|
+
import { type SerializableRetryPolicy } from "../core/retry";
|
|
29
|
+
import { type StepAttempt } from "../core/step";
|
|
30
|
+
import { type WorkflowRun } from "../core/workflow";
|
|
26
31
|
import { DEFAULT_SCHEMA, migrate } from "./base";
|
|
27
|
-
import {
|
|
32
|
+
import { PostgresPubSub } from "./pubsub";
|
|
33
|
+
import { type OnSubscribed } from "./pubsub";
|
|
28
34
|
|
|
29
35
|
export const DEFAULT_LISTEN_CHANNEL = "new_tasks" as const;
|
|
30
36
|
const DEFAULT_PAGINATION_PAGE_SIZE = 100 as const;
|
|
@@ -155,6 +161,8 @@ export class BackendPostgres implements Backend {
|
|
|
155
161
|
await this.pubsub?.destroy();
|
|
156
162
|
this.pubsub = null;
|
|
157
163
|
await this.knex.destroy();
|
|
164
|
+
this._knex = null;
|
|
165
|
+
this.initialized = false;
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {
|
|
@@ -253,6 +261,8 @@ export class BackendPostgres implements Backend {
|
|
|
253
261
|
});
|
|
254
262
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
255
263
|
const { after, before } = params;
|
|
264
|
+
const order = params.order ?? "asc";
|
|
265
|
+
const reverseOrder = order === "asc" ? "desc" : "asc";
|
|
256
266
|
|
|
257
267
|
let cursor: Cursor | null = null;
|
|
258
268
|
if (after) {
|
|
@@ -261,10 +271,10 @@ export class BackendPostgres implements Backend {
|
|
|
261
271
|
cursor = decodeCursor(before);
|
|
262
272
|
}
|
|
263
273
|
|
|
264
|
-
const qb = this.buildListWorkflowRunsWhere(params, cursor);
|
|
274
|
+
const qb = this.buildListWorkflowRunsWhere(params, cursor, order);
|
|
265
275
|
const rows = await qb
|
|
266
|
-
.orderBy("created_at", before ?
|
|
267
|
-
.orderBy("id", before ?
|
|
276
|
+
.orderBy("created_at", before ? reverseOrder : order)
|
|
277
|
+
.orderBy("id", before ? reverseOrder : order)
|
|
268
278
|
.limit(limit + 1);
|
|
269
279
|
|
|
270
280
|
return this.processPaginationResults(
|
|
@@ -275,7 +285,11 @@ export class BackendPostgres implements Backend {
|
|
|
275
285
|
);
|
|
276
286
|
}
|
|
277
287
|
|
|
278
|
-
private buildListWorkflowRunsWhere(
|
|
288
|
+
private buildListWorkflowRunsWhere(
|
|
289
|
+
params: ListWorkflowRunsParams,
|
|
290
|
+
cursor: Cursor | null,
|
|
291
|
+
order: "asc" | "desc",
|
|
292
|
+
) {
|
|
279
293
|
const { after } = params;
|
|
280
294
|
const qb = this.knex
|
|
281
295
|
.withSchema(DEFAULT_SCHEMA)
|
|
@@ -283,13 +297,28 @@ export class BackendPostgres implements Backend {
|
|
|
283
297
|
.where("namespace_id", this.namespaceId);
|
|
284
298
|
|
|
285
299
|
if (cursor) {
|
|
286
|
-
|
|
287
|
-
|
|
300
|
+
// asc: after → ">", before → "<"
|
|
301
|
+
// desc: after → "<", before → ">"
|
|
302
|
+
const operator = (order === "asc") === !!after ? ">" : "<";
|
|
303
|
+
qb.whereRaw(`("created_at", "id") ${operator} (?, ?)`, [
|
|
288
304
|
cursor.createdAt.toISOString(),
|
|
289
305
|
cursor.id,
|
|
290
306
|
]);
|
|
291
307
|
}
|
|
292
308
|
|
|
309
|
+
if (params.status && params.status.length > 0) {
|
|
310
|
+
qb.whereIn("status", params.status);
|
|
311
|
+
}
|
|
312
|
+
if (params.workflowName) {
|
|
313
|
+
qb.where("workflow_name", params.workflowName);
|
|
314
|
+
}
|
|
315
|
+
if (params.createdAfter) {
|
|
316
|
+
qb.where("created_at", ">=", params.createdAfter);
|
|
317
|
+
}
|
|
318
|
+
if (params.createdBefore) {
|
|
319
|
+
qb.where("created_at", "<=", params.createdBefore);
|
|
320
|
+
}
|
|
321
|
+
|
|
293
322
|
return qb;
|
|
294
323
|
}
|
|
295
324
|
|
|
@@ -381,6 +410,11 @@ export class BackendPostgres implements Backend {
|
|
|
381
410
|
.returning("*");
|
|
382
411
|
|
|
383
412
|
if (!updated) {
|
|
413
|
+
const wr = await this.getWorkflowRun({ workflowRunId: params.workflowRunId });
|
|
414
|
+
if (wr && (wr.status === "paused" || wr.status === "canceled")) {
|
|
415
|
+
throw new Error("Workflow run is paused or canceled");
|
|
416
|
+
}
|
|
417
|
+
|
|
384
418
|
logger.error("Failed to extend lease for workflow run: {params}", { params });
|
|
385
419
|
throw new Error("Failed to extend lease for workflow run");
|
|
386
420
|
}
|
|
@@ -575,7 +609,7 @@ export class BackendPostgres implements Backend {
|
|
|
575
609
|
.table("workflow_runs")
|
|
576
610
|
.where("namespace_id", this.namespaceId)
|
|
577
611
|
.where("id", params.workflowRunId)
|
|
578
|
-
.whereIn("status", ["pending", "running", "sleeping"])
|
|
612
|
+
.whereIn("status", ["pending", "running", "sleeping", "paused"])
|
|
579
613
|
.update({
|
|
580
614
|
status: "canceled",
|
|
581
615
|
worker_id: null,
|
|
@@ -618,6 +652,111 @@ export class BackendPostgres implements Backend {
|
|
|
618
652
|
return updated;
|
|
619
653
|
}
|
|
620
654
|
|
|
655
|
+
async pauseWorkflowRun(params: PauseWorkflowRunParams): Promise<WorkflowRun> {
|
|
656
|
+
if (!this.initialized) {
|
|
657
|
+
throw new Error("Backend not initialized");
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
logger.info("Pausing workflow run: {workflowRunId}", { workflowRunId: params.workflowRunId });
|
|
661
|
+
|
|
662
|
+
const [updated] = await this.knex
|
|
663
|
+
.withSchema(DEFAULT_SCHEMA)
|
|
664
|
+
.table("workflow_runs")
|
|
665
|
+
.where("namespace_id", this.namespaceId)
|
|
666
|
+
.where("id", params.workflowRunId)
|
|
667
|
+
.whereIn("status", ["pending", "running", "sleeping"])
|
|
668
|
+
.update({
|
|
669
|
+
status: "paused",
|
|
670
|
+
worker_id: null,
|
|
671
|
+
available_at: null,
|
|
672
|
+
updated_at: this.knex.fn.now(),
|
|
673
|
+
})
|
|
674
|
+
.returning("*");
|
|
675
|
+
|
|
676
|
+
if (!updated) {
|
|
677
|
+
const existing = await this.getWorkflowRun({
|
|
678
|
+
workflowRunId: params.workflowRunId,
|
|
679
|
+
});
|
|
680
|
+
if (!existing) {
|
|
681
|
+
throw new Error(`Workflow run ${params.workflowRunId} does not exist`);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// 이미 paused이면 멱등하게 반환합니다.
|
|
685
|
+
if (existing.status === "paused") {
|
|
686
|
+
return existing;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// 터미널 상태에서는 pause할 수 없습니다.
|
|
690
|
+
// 'succeeded' status is deprecated
|
|
691
|
+
if (["succeeded", "completed", "failed", "canceled"].includes(existing.status)) {
|
|
692
|
+
logger.error("Cannot pause workflow run: {params} with status {status}", {
|
|
693
|
+
params,
|
|
694
|
+
status: existing.status,
|
|
695
|
+
});
|
|
696
|
+
throw new Error(
|
|
697
|
+
`Cannot pause workflow run ${params.workflowRunId} with status ${existing.status}`,
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
logger.error("Failed to pause workflow run: {params}", { params });
|
|
702
|
+
throw new Error("Failed to pause workflow run");
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return updated;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async resumeWorkflowRun(params: ResumeWorkflowRunParams): Promise<WorkflowRun> {
|
|
709
|
+
if (!this.initialized) {
|
|
710
|
+
throw new Error("Backend not initialized");
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
logger.info("Resuming workflow run: {workflowRunId}", { workflowRunId: params.workflowRunId });
|
|
714
|
+
|
|
715
|
+
const [updated] = await this.knex
|
|
716
|
+
.withSchema(DEFAULT_SCHEMA)
|
|
717
|
+
.table("workflow_runs")
|
|
718
|
+
.where("namespace_id", this.namespaceId)
|
|
719
|
+
.where("id", params.workflowRunId)
|
|
720
|
+
.where("status", "paused")
|
|
721
|
+
.update({
|
|
722
|
+
status: "pending",
|
|
723
|
+
available_at: this.knex.fn.now(),
|
|
724
|
+
updated_at: this.knex.fn.now(),
|
|
725
|
+
})
|
|
726
|
+
.returning("*");
|
|
727
|
+
|
|
728
|
+
if (!updated) {
|
|
729
|
+
const existing = await this.getWorkflowRun({
|
|
730
|
+
workflowRunId: params.workflowRunId,
|
|
731
|
+
});
|
|
732
|
+
if (!existing) {
|
|
733
|
+
throw new Error(`Workflow run ${params.workflowRunId} does not exist`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// 이미 pending/running이면 멱등하게 반환합니다.
|
|
737
|
+
if (existing.status === "pending" || existing.status === "running") {
|
|
738
|
+
return existing;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// 터미널 상태에서는 resume할 수 없습니다.
|
|
742
|
+
// 'succeeded' status is deprecated
|
|
743
|
+
if (["succeeded", "completed", "failed", "canceled"].includes(existing.status)) {
|
|
744
|
+
logger.error("Cannot resume workflow run: {params} with status {status}", {
|
|
745
|
+
params,
|
|
746
|
+
status: existing.status,
|
|
747
|
+
});
|
|
748
|
+
throw new Error(
|
|
749
|
+
`Cannot resume workflow run ${params.workflowRunId} with status ${existing.status}`,
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
logger.error("Failed to resume workflow run: {params}", { params });
|
|
754
|
+
throw new Error("Failed to resume workflow run");
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return updated;
|
|
758
|
+
}
|
|
759
|
+
|
|
621
760
|
async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {
|
|
622
761
|
if (!this.initialized) {
|
|
623
762
|
throw new Error("Backend not initialized");
|
|
@@ -685,6 +824,8 @@ export class BackendPostgres implements Backend {
|
|
|
685
824
|
|
|
686
825
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
687
826
|
const { after, before } = params;
|
|
827
|
+
const order = params.order ?? "asc";
|
|
828
|
+
const reverseOrder = order === "asc" ? "desc" : "asc";
|
|
688
829
|
|
|
689
830
|
let cursor: Cursor | null = null;
|
|
690
831
|
if (after) {
|
|
@@ -693,10 +834,10 @@ export class BackendPostgres implements Backend {
|
|
|
693
834
|
cursor = decodeCursor(before);
|
|
694
835
|
}
|
|
695
836
|
|
|
696
|
-
const qb = this.buildListStepAttemptsWhere(params, cursor);
|
|
837
|
+
const qb = this.buildListStepAttemptsWhere(params, cursor, order);
|
|
697
838
|
const rows = await qb
|
|
698
|
-
.orderBy("created_at", before ?
|
|
699
|
-
.orderBy("id", before ?
|
|
839
|
+
.orderBy("created_at", before ? reverseOrder : order)
|
|
840
|
+
.orderBy("id", before ? reverseOrder : order)
|
|
700
841
|
.limit(limit + 1);
|
|
701
842
|
|
|
702
843
|
return this.processPaginationResults(
|
|
@@ -707,7 +848,11 @@ export class BackendPostgres implements Backend {
|
|
|
707
848
|
);
|
|
708
849
|
}
|
|
709
850
|
|
|
710
|
-
private buildListStepAttemptsWhere(
|
|
851
|
+
private buildListStepAttemptsWhere(
|
|
852
|
+
params: ListStepAttemptsParams,
|
|
853
|
+
cursor: Cursor | null,
|
|
854
|
+
order: "asc" | "desc",
|
|
855
|
+
) {
|
|
711
856
|
const { after } = params;
|
|
712
857
|
const qb = this.knex
|
|
713
858
|
.withSchema(DEFAULT_SCHEMA)
|
|
@@ -716,7 +861,9 @@ export class BackendPostgres implements Backend {
|
|
|
716
861
|
.where("workflow_run_id", params.workflowRunId);
|
|
717
862
|
|
|
718
863
|
if (cursor) {
|
|
719
|
-
|
|
864
|
+
// asc: after → ">", before → "<"
|
|
865
|
+
// desc: after → "<", before → ">"
|
|
866
|
+
const operator = (order === "asc") === !!after ? ">" : "<";
|
|
720
867
|
return qb.whereRaw(`("created_at", "id") ${operator} (?, ?)`, [
|
|
721
868
|
cursor.createdAt.toISOString(),
|
|
722
869
|
cursor.id,
|
|
@@ -767,8 +914,10 @@ export class BackendPostgres implements Backend {
|
|
|
767
914
|
};
|
|
768
915
|
}
|
|
769
916
|
|
|
770
|
-
//
|
|
771
|
-
|
|
917
|
+
// WHERE 조건에 wr.status='running', sa.status='running'이 포함되어 있어,
|
|
918
|
+
// 외부에서 워크플로우 상태가 변경된 경우(pause/cancel) null을 반환합니다.
|
|
919
|
+
// 예상하지 못한 이유로 실패한 경우에는 에러를 로깅합니다.
|
|
920
|
+
async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt | null> {
|
|
772
921
|
if (!this.initialized) {
|
|
773
922
|
throw new Error("Backend not initialized");
|
|
774
923
|
}
|
|
@@ -801,14 +950,13 @@ export class BackendPostgres implements Backend {
|
|
|
801
950
|
.returning("sa.*");
|
|
802
951
|
|
|
803
952
|
if (!updated) {
|
|
804
|
-
|
|
805
|
-
throw new Error("Failed to mark step attempt completed");
|
|
953
|
+
return this.handleStepAttemptUpdateMiss("completed", params);
|
|
806
954
|
}
|
|
807
955
|
|
|
808
956
|
return updated;
|
|
809
957
|
}
|
|
810
958
|
|
|
811
|
-
async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {
|
|
959
|
+
async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt | null> {
|
|
812
960
|
if (!this.initialized) {
|
|
813
961
|
throw new Error("Backend not initialized");
|
|
814
962
|
}
|
|
@@ -842,12 +990,46 @@ export class BackendPostgres implements Backend {
|
|
|
842
990
|
.returning("sa.*");
|
|
843
991
|
|
|
844
992
|
if (!updated) {
|
|
845
|
-
|
|
846
|
-
throw new Error("Failed to mark step attempt failed");
|
|
993
|
+
return this.handleStepAttemptUpdateMiss("failed", params);
|
|
847
994
|
}
|
|
848
995
|
|
|
849
996
|
return updated;
|
|
850
997
|
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* completeStepAttempt/failStepAttempt에서 UPDATE가 0건일 때,
|
|
1001
|
+
* 외부 상태 변경(pause/cancel)에 의한 것인지 판단합니다.
|
|
1002
|
+
* - 외부 상태 변경이면 해당 step의 상태도 워크플로우와 동일하게 맞추고 null을 반환합니다.
|
|
1003
|
+
* - 그 외에는 예상하지 못한 상황이므로 에러를 throw합니다.
|
|
1004
|
+
*/
|
|
1005
|
+
private async handleStepAttemptUpdateMiss(
|
|
1006
|
+
method: string,
|
|
1007
|
+
params: { workflowRunId: string; stepAttemptId: string; workerId: string },
|
|
1008
|
+
): Promise<null> {
|
|
1009
|
+
const wr = await this.getWorkflowRun({ workflowRunId: params.workflowRunId });
|
|
1010
|
+
|
|
1011
|
+
// 워크플로우가 외부에서 paused/canceled된 경우 → step 상태도 동일하게 갱신하고 null 반환
|
|
1012
|
+
if (wr && (wr.status === "paused" || wr.status === "canceled")) {
|
|
1013
|
+
await this.knex
|
|
1014
|
+
.withSchema(DEFAULT_SCHEMA)
|
|
1015
|
+
.table("step_attempts")
|
|
1016
|
+
.where("namespace_id", this.namespaceId)
|
|
1017
|
+
.where("id", params.stepAttemptId)
|
|
1018
|
+
.whereIn("status", ["running", "paused"])
|
|
1019
|
+
.update({
|
|
1020
|
+
status: wr.status,
|
|
1021
|
+
updated_at: this.knex.fn.now(),
|
|
1022
|
+
});
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// 그 외(워크플로우가 여전히 running인데 UPDATE가 안 된 경우 등) → 예상 못한 상황
|
|
1027
|
+
logger.error("Failed to mark step attempt {method}: {params}", {
|
|
1028
|
+
method,
|
|
1029
|
+
params,
|
|
1030
|
+
});
|
|
1031
|
+
throw new Error(`Failed to mark step attempt ${method}`);
|
|
1032
|
+
}
|
|
851
1033
|
}
|
|
852
1034
|
|
|
853
1035
|
/**
|