@sonamu-kit/tasks 0.1.2 → 0.2.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/dist/backend.d.ts +4 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/client.js.map +1 -1
- package/dist/core/retry.d.ts +35 -19
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +50 -14
- package/dist/core/retry.js.map +1 -1
- package/dist/core/retry.test.js +172 -11
- package/dist/core/retry.test.js.map +1 -1
- package/dist/database/backend.d.ts.map +1 -1
- package/dist/database/backend.js +114 -5
- package/dist/database/backend.js.map +1 -1
- package/dist/database/backend.testsuite.d.ts.map +1 -1
- package/dist/database/backend.testsuite.js +106 -0
- package/dist/database/backend.testsuite.js.map +1 -1
- package/dist/execution.d.ts +2 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +17 -3
- package/dist/execution.js.map +1 -1
- package/dist/execution.test.js +104 -0
- package/dist/execution.test.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +2 -1
- package/dist/worker.js.map +1 -1
- package/dist/workflow.d.ts +3 -0
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -3
- package/src/backend.ts +4 -0
- package/src/client.ts +2 -0
- package/src/core/retry.test.ts +180 -11
- package/src/core/retry.ts +95 -19
- package/src/database/backend.testsuite.ts +119 -0
- package/src/database/backend.ts +133 -5
- package/src/execution.test.ts +115 -0
- package/src/execution.ts +18 -2
- package/src/internal.ts +21 -1
- package/src/worker.ts +1 -0
- package/src/workflow.ts +3 -0
package/dist/execution.test.js
CHANGED
|
@@ -551,6 +551,110 @@ describe("executeWorkflow", ()=>{
|
|
|
551
551
|
});
|
|
552
552
|
});
|
|
553
553
|
});
|
|
554
|
+
describe("executeWorkflow with dynamic retryPolicy", ()=>{
|
|
555
|
+
let backend;
|
|
556
|
+
beforeAll(async ()=>{
|
|
557
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
558
|
+
namespaceId: randomUUID(),
|
|
559
|
+
runMigrations: false
|
|
560
|
+
});
|
|
561
|
+
await backend.initialize();
|
|
562
|
+
});
|
|
563
|
+
afterAll(async ()=>{
|
|
564
|
+
await backend.stop();
|
|
565
|
+
});
|
|
566
|
+
test("calls failWorkflowRun with forceComplete=true when shouldRetry returns false", async ()=>{
|
|
567
|
+
const client = new OpenWorkflow({
|
|
568
|
+
backend
|
|
569
|
+
});
|
|
570
|
+
// shouldRetry가 false를 반환하는 retryPolicy
|
|
571
|
+
const workflow = client.defineWorkflow({
|
|
572
|
+
name: "dynamic-retry-false",
|
|
573
|
+
retryPolicy: {
|
|
574
|
+
maxAttempts: 10,
|
|
575
|
+
shouldRetry: ()=>({
|
|
576
|
+
shouldRetry: false,
|
|
577
|
+
delayMs: 0
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
}, ()=>{
|
|
581
|
+
throw new Error("Intentional failure");
|
|
582
|
+
});
|
|
583
|
+
const worker = client.newWorker();
|
|
584
|
+
const handle = await workflow.run();
|
|
585
|
+
await worker.tick();
|
|
586
|
+
await sleep(100);
|
|
587
|
+
// shouldRetry가 false를 반환했으므로 즉시 failed 상태가 되어야 합니다
|
|
588
|
+
const workflowRun = await backend.getWorkflowRun({
|
|
589
|
+
workflowRunId: handle.workflowRun.id
|
|
590
|
+
});
|
|
591
|
+
expect(workflowRun?.status).toBe("failed");
|
|
592
|
+
expect(workflowRun?.attempts).toBe(1); // 한 번만 시도하고 종료
|
|
593
|
+
});
|
|
594
|
+
test("calls failWorkflowRun with forceComplete=false when shouldRetry returns true", async ()=>{
|
|
595
|
+
const client = new OpenWorkflow({
|
|
596
|
+
backend
|
|
597
|
+
});
|
|
598
|
+
// shouldRetry가 true를 반환하는 retryPolicy
|
|
599
|
+
const workflow = client.defineWorkflow({
|
|
600
|
+
name: "dynamic-retry-true",
|
|
601
|
+
retryPolicy: {
|
|
602
|
+
maxAttempts: 2,
|
|
603
|
+
shouldRetry: ()=>({
|
|
604
|
+
shouldRetry: true,
|
|
605
|
+
delayMs: 1000
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
}, ()=>{
|
|
609
|
+
throw new Error("Intentional failure");
|
|
610
|
+
});
|
|
611
|
+
const worker = client.newWorker();
|
|
612
|
+
const handle = await workflow.run();
|
|
613
|
+
await worker.tick();
|
|
614
|
+
await sleep(100);
|
|
615
|
+
// shouldRetry가 true를 반환했으므로 pending 상태로 재시도 대기
|
|
616
|
+
const workflowRun = await backend.getWorkflowRun({
|
|
617
|
+
workflowRunId: handle.workflowRun.id
|
|
618
|
+
});
|
|
619
|
+
expect(workflowRun?.status).toBe("pending");
|
|
620
|
+
expect(workflowRun?.attempts).toBe(1);
|
|
621
|
+
});
|
|
622
|
+
test("receives correct error and attempt number in shouldRetry function", async ()=>{
|
|
623
|
+
const client = new OpenWorkflow({
|
|
624
|
+
backend
|
|
625
|
+
});
|
|
626
|
+
let receivedError = null;
|
|
627
|
+
let receivedAttempt = null;
|
|
628
|
+
const workflow = client.defineWorkflow({
|
|
629
|
+
name: "dynamic-retry-params",
|
|
630
|
+
retryPolicy: {
|
|
631
|
+
maxAttempts: 10,
|
|
632
|
+
shouldRetry: (error, attempt)=>{
|
|
633
|
+
receivedError = error;
|
|
634
|
+
receivedAttempt = attempt;
|
|
635
|
+
return {
|
|
636
|
+
shouldRetry: false,
|
|
637
|
+
delayMs: 0
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}, ()=>{
|
|
642
|
+
throw new Error("Test error message");
|
|
643
|
+
});
|
|
644
|
+
const worker = client.newWorker();
|
|
645
|
+
const handle = await workflow.run();
|
|
646
|
+
await worker.tick();
|
|
647
|
+
await sleep(100);
|
|
648
|
+
// shouldRetry 함수가 올바른 파라미터를 받았는지 확인
|
|
649
|
+
expect(receivedError).not.toBeNull();
|
|
650
|
+
expect(receivedError.message).toBe("Test error message");
|
|
651
|
+
expect(receivedAttempt).toBe(1); // 첫 번째 시도 후이므로 1
|
|
652
|
+
const workflowRun = await backend.getWorkflowRun({
|
|
653
|
+
workflowRunId: handle.workflowRun.id
|
|
654
|
+
});
|
|
655
|
+
expect(workflowRun?.status).toBe("failed");
|
|
656
|
+
});
|
|
657
|
+
});
|
|
554
658
|
function sleep(ms) {
|
|
555
659
|
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
556
660
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/execution.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterAll, beforeAll, describe, expect, test } from \"vitest\";\nimport { BackendPostgres } from \".\";\nimport { OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"StepExecutor\", () => {\n let backend: BackendPostgres;\n\n beforeAll(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterAll(async () => {\n await backend.stop();\n });\n\n test(\"executes step and returns result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-basic\" }, async ({ step }) => {\n const result = await step.run({ name: \"add\" }, () => 5 + 3);\n return result;\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(8);\n });\n\n test(\"caches step results for same step name\", async () => {\n const client = new OpenWorkflow({ backend });\n\n let executionCount = 0;\n const workflow = client.defineWorkflow({ name: \"executor-cached\" }, async ({ step }) => {\n const first = await step.run({ name: \"cached-step\" }, () => {\n executionCount++;\n return \"first-execution\";\n });\n const second = await step.run({ name: \"cached-step\" }, () => {\n executionCount++;\n return \"second-execution\";\n });\n return { first, second };\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({\n first: \"first-execution\",\n second: \"first-execution\",\n });\n expect(executionCount).toBe(1);\n });\n\n test(\"different step names execute independently\", async () => {\n const client = new OpenWorkflow({ backend });\n\n let executionCount = 0;\n const workflow = client.defineWorkflow(\n { name: \"executor-different-steps\" },\n async ({ step }) => {\n const first = await step.run({ name: \"step-1\" }, () => {\n executionCount++;\n return \"a\";\n });\n const second = await step.run({ name: \"step-2\" }, () => {\n executionCount++;\n return \"b\";\n });\n return { first, second };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ first: \"a\", second: \"b\" });\n expect(executionCount).toBe(2);\n });\n\n test(\"propagates step errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-error\" }, async ({ step }) => {\n await step.run({ name: \"failing-step\" }, () => {\n throw new Error(\"Step failed intentionally\");\n });\n return \"should not reach\";\n });\n\n const worker = client.newWorker();\n // Use deadline to force immediate failure without retries\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"sleep puts workflow in sleeping status\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-sleep\" }, async ({ step }) => {\n await step.sleep(\"sleep-1\", \"5s\");\n return \"after sleep\";\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n await worker.tick();\n await sleep(50); // Wait for sleep step to complete\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"sleeping\");\n expect(workflowRun?.availableAt).not.toBeNull();\n });\n\n test(\"workflow resumes after sleep duration\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"resume-after-sleep\" }, async ({ step }) => {\n const value = await step.run({ name: \"before\" }, () => 5);\n await step.sleep(\"wait\", \"10ms\");\n return value + 10;\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n\n // First tick - hits sleep\n await worker.tick();\n await sleep(50); // Wait for tick to complete\n const sleeping = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(sleeping?.status).toBe(\"sleeping\");\n\n // Wait for sleep to elapse\n await sleep(50);\n\n // Second tick - completes\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n});\n\ndescribe(\"executeWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeAll(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterAll(async () => {\n await backend.stop();\n });\n\n describe(\"successful execution\", () => {\n test(\"executes a simple workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"simple-workflow\" },\n ({ input }: { input: { a: number; b: number } }) => {\n return input.a + input.b;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ a: 10, b: 5 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n\n test(\"executes a multi-step workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow<{ value: number }, number>(\n { name: \"multi-step-workflow\" },\n async ({ input, step }) => {\n const sum = await step.run({ name: \"add\" }, () => input.value + 5);\n const product = await step.run({ name: \"multiply\" }, () => sum * 2);\n return product;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ value: 10 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(30);\n });\n\n test(\"returns null for workflows without return\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"void-workflow\" }, () => null);\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBeNull();\n });\n\n test(\"returns null from workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"null-workflow\" }, () => null);\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBeNull();\n });\n });\n\n describe(\"error handling\", () => {\n test(\"handles workflow errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"failing-workflow\" }, () => {\n throw new Error(\"Workflow error\");\n });\n\n const worker = client.newWorker();\n // Use deadline to skip retries - fails with deadline exceeded\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"handles step errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"step-error-workflow\" }, async ({ step }) => {\n await step.run({ name: \"failing\" }, () => {\n throw new Error(\"Step error\");\n });\n return \"unreachable\";\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"serializes non-Error exceptions\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"non-error-workflow\" }, async ({ step }) => {\n await step.run({ name: \"throw-object\" }, () => {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw { custom: \"error\", code: 500 };\n });\n return \"nope\";\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow();\n });\n });\n\n describe(\"sleep handling\", () => {\n test(\"workflow enters sleeping status\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"sleep-workflow\" }, async ({ step }) => {\n await step.sleep(\"wait\", \"5s\");\n return \"after sleep\";\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n await worker.tick();\n await sleep(50); // Wait for sleep step to complete\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"sleeping\");\n });\n\n test(\"resumes workflow after sleep duration\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow<{ value: number }, number>(\n { name: \"resume-after-sleep\" },\n async ({ input, step }) => {\n const sum = await step.run({ name: \"add\" }, () => input.value + 1);\n await step.sleep(\"wait\", \"10ms\");\n return sum + 10;\n },\n );\n\n const handle = await workflow.run({ value: 5 });\n const worker = client.newWorker();\n\n // first tick - hits sleep\n await worker.tick();\n await sleep(50);\n\n const sleeping = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(sleeping?.status).toBe(\"sleeping\");\n\n // wait for sleep\n await sleep(50);\n\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(16);\n });\n });\n\n describe(\"workflow with complex data\", () => {\n test(\"handles objects as input and output\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"user-workflow\" },\n ({ input }: { input: { name: string; age: number } }) => {\n return {\n greeting: `Hello, ${input.name}! You are ${String(input.age)} years old.`,\n processed: true,\n };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ name: \"Alice\", age: 30 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({\n greeting: \"Hello, Alice! You are 30 years old.\",\n processed: true,\n });\n });\n\n test(\"handles arrays in workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"array-workflow\" },\n ({ input }: { input: { numbers: number[] } }) => {\n return input.numbers.reduce((a, b) => a + b, 0);\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ numbers: [1, 2, 3, 4, 5] });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n });\n\n describe(\"result type handling\", () => {\n test(\"returns success with numeric result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"numeric-result\" }, async ({ step }) => {\n return await step.run({ name: \"compute\" }, () => 100 + 200);\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(300);\n });\n\n test(\"returns success with string result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"string-result\" },\n ({ input }: { input: { text: string } }) => {\n return input.text.toUpperCase();\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ text: \"hello world\" });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(\"HELLO WORLD\");\n });\n\n test(\"returns success with boolean result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"bool-result\" },\n ({ input }: { input: { value: number } }) => {\n return input.value > 0;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ value: 42 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(true);\n });\n });\n\n describe(\"step execution order\", () => {\n test(\"executes steps in sequence\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const order: string[] = [];\n const workflow = client.defineWorkflow({ name: \"sequence-workflow\" }, async ({ step }) => {\n await step.run({ name: \"first\" }, () => order.push(\"first\"));\n await step.run({ name: \"second\" }, () => order.push(\"second\"));\n await step.run({ name: \"third\" }, () => order.push(\"third\"));\n return order;\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual([\"first\", \"second\", \"third\"]);\n });\n });\n\n describe(\"version handling\", () => {\n test(\"passes version to workflow function\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"version-workflow\", version: \"1.0.0\" },\n ({ version }) => {\n return { receivedVersion: version };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ receivedVersion: \"1.0.0\" });\n });\n\n test(\"passes null version when not specified\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"no-version-workflow\" }, ({ version }) => {\n return { receivedVersion: version };\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ receivedVersion: null });\n });\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["randomUUID","afterAll","beforeAll","describe","expect","test","BackendPostgres","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","namespaceId","runMigrations","initialize","stop","client","workflow","defineWorkflow","name","step","result","run","worker","newWorker","handle","tick","toBe","executionCount","first","second","toEqual","Error","deadlineAt","Date","sleep","rejects","toThrow","workflowRun","getWorkflowRun","workflowRunId","id","status","availableAt","not","toBeNull","value","sleeping","input","a","b","sum","product","custom","code","greeting","String","age","processed","numbers","reduce","text","toUpperCase","order","push","version","receivedVersion","ms","Promise","resolve","setTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/execution.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterAll, beforeAll, describe, expect, test } from \"vitest\";\nimport { BackendPostgres } from \".\";\nimport { OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"StepExecutor\", () => {\n let backend: BackendPostgres;\n\n beforeAll(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterAll(async () => {\n await backend.stop();\n });\n\n test(\"executes step and returns result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-basic\" }, async ({ step }) => {\n const result = await step.run({ name: \"add\" }, () => 5 + 3);\n return result;\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(8);\n });\n\n test(\"caches step results for same step name\", async () => {\n const client = new OpenWorkflow({ backend });\n\n let executionCount = 0;\n const workflow = client.defineWorkflow({ name: \"executor-cached\" }, async ({ step }) => {\n const first = await step.run({ name: \"cached-step\" }, () => {\n executionCount++;\n return \"first-execution\";\n });\n const second = await step.run({ name: \"cached-step\" }, () => {\n executionCount++;\n return \"second-execution\";\n });\n return { first, second };\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({\n first: \"first-execution\",\n second: \"first-execution\",\n });\n expect(executionCount).toBe(1);\n });\n\n test(\"different step names execute independently\", async () => {\n const client = new OpenWorkflow({ backend });\n\n let executionCount = 0;\n const workflow = client.defineWorkflow(\n { name: \"executor-different-steps\" },\n async ({ step }) => {\n const first = await step.run({ name: \"step-1\" }, () => {\n executionCount++;\n return \"a\";\n });\n const second = await step.run({ name: \"step-2\" }, () => {\n executionCount++;\n return \"b\";\n });\n return { first, second };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ first: \"a\", second: \"b\" });\n expect(executionCount).toBe(2);\n });\n\n test(\"propagates step errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-error\" }, async ({ step }) => {\n await step.run({ name: \"failing-step\" }, () => {\n throw new Error(\"Step failed intentionally\");\n });\n return \"should not reach\";\n });\n\n const worker = client.newWorker();\n // Use deadline to force immediate failure without retries\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"sleep puts workflow in sleeping status\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"executor-sleep\" }, async ({ step }) => {\n await step.sleep(\"sleep-1\", \"5s\");\n return \"after sleep\";\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n await worker.tick();\n await sleep(50); // Wait for sleep step to complete\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"sleeping\");\n expect(workflowRun?.availableAt).not.toBeNull();\n });\n\n test(\"workflow resumes after sleep duration\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"resume-after-sleep\" }, async ({ step }) => {\n const value = await step.run({ name: \"before\" }, () => 5);\n await step.sleep(\"wait\", \"10ms\");\n return value + 10;\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n\n // First tick - hits sleep\n await worker.tick();\n await sleep(50); // Wait for tick to complete\n const sleeping = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(sleeping?.status).toBe(\"sleeping\");\n\n // Wait for sleep to elapse\n await sleep(50);\n\n // Second tick - completes\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n});\n\ndescribe(\"executeWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeAll(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterAll(async () => {\n await backend.stop();\n });\n\n describe(\"successful execution\", () => {\n test(\"executes a simple workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"simple-workflow\" },\n ({ input }: { input: { a: number; b: number } }) => {\n return input.a + input.b;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ a: 10, b: 5 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n\n test(\"executes a multi-step workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow<{ value: number }, number>(\n { name: \"multi-step-workflow\" },\n async ({ input, step }) => {\n const sum = await step.run({ name: \"add\" }, () => input.value + 5);\n const product = await step.run({ name: \"multiply\" }, () => sum * 2);\n return product;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ value: 10 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(30);\n });\n\n test(\"returns null for workflows without return\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"void-workflow\" }, () => null);\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBeNull();\n });\n\n test(\"returns null from workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"null-workflow\" }, () => null);\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBeNull();\n });\n });\n\n describe(\"error handling\", () => {\n test(\"handles workflow errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"failing-workflow\" }, () => {\n throw new Error(\"Workflow error\");\n });\n\n const worker = client.newWorker();\n // Use deadline to skip retries - fails with deadline exceeded\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"handles step errors with deadline exceeded\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"step-error-workflow\" }, async ({ step }) => {\n await step.run({ name: \"failing\" }, () => {\n throw new Error(\"Step error\");\n });\n return \"unreachable\";\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow(/deadline exceeded/);\n });\n\n test(\"serializes non-Error exceptions\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"non-error-workflow\" }, async ({ step }) => {\n await step.run({ name: \"throw-object\" }, () => {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw { custom: \"error\", code: 500 };\n });\n return \"nope\";\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run({}, { deadlineAt: new Date() });\n await worker.tick();\n await sleep(100);\n\n await expect(handle.result()).rejects.toThrow();\n });\n });\n\n describe(\"sleep handling\", () => {\n test(\"workflow enters sleeping status\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"sleep-workflow\" }, async ({ step }) => {\n await step.sleep(\"wait\", \"5s\");\n return \"after sleep\";\n });\n\n const handle = await workflow.run();\n const worker = client.newWorker();\n await worker.tick();\n await sleep(50); // Wait for sleep step to complete\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"sleeping\");\n });\n\n test(\"resumes workflow after sleep duration\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow<{ value: number }, number>(\n { name: \"resume-after-sleep\" },\n async ({ input, step }) => {\n const sum = await step.run({ name: \"add\" }, () => input.value + 1);\n await step.sleep(\"wait\", \"10ms\");\n return sum + 10;\n },\n );\n\n const handle = await workflow.run({ value: 5 });\n const worker = client.newWorker();\n\n // first tick - hits sleep\n await worker.tick();\n await sleep(50);\n\n const sleeping = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(sleeping?.status).toBe(\"sleeping\");\n\n // wait for sleep\n await sleep(50);\n\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(16);\n });\n });\n\n describe(\"workflow with complex data\", () => {\n test(\"handles objects as input and output\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"user-workflow\" },\n ({ input }: { input: { name: string; age: number } }) => {\n return {\n greeting: `Hello, ${input.name}! You are ${String(input.age)} years old.`,\n processed: true,\n };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ name: \"Alice\", age: 30 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({\n greeting: \"Hello, Alice! You are 30 years old.\",\n processed: true,\n });\n });\n\n test(\"handles arrays in workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"array-workflow\" },\n ({ input }: { input: { numbers: number[] } }) => {\n return input.numbers.reduce((a, b) => a + b, 0);\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ numbers: [1, 2, 3, 4, 5] });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(15);\n });\n });\n\n describe(\"result type handling\", () => {\n test(\"returns success with numeric result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"numeric-result\" }, async ({ step }) => {\n return await step.run({ name: \"compute\" }, () => 100 + 200);\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(300);\n });\n\n test(\"returns success with string result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"string-result\" },\n ({ input }: { input: { text: string } }) => {\n return input.text.toUpperCase();\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ text: \"hello world\" });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(\"HELLO WORLD\");\n });\n\n test(\"returns success with boolean result\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"bool-result\" },\n ({ input }: { input: { value: number } }) => {\n return input.value > 0;\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run({ value: 42 });\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toBe(true);\n });\n });\n\n describe(\"step execution order\", () => {\n test(\"executes steps in sequence\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const order: string[] = [];\n const workflow = client.defineWorkflow({ name: \"sequence-workflow\" }, async ({ step }) => {\n await step.run({ name: \"first\" }, () => order.push(\"first\"));\n await step.run({ name: \"second\" }, () => order.push(\"second\"));\n await step.run({ name: \"third\" }, () => order.push(\"third\"));\n return order;\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual([\"first\", \"second\", \"third\"]);\n });\n });\n\n describe(\"version handling\", () => {\n test(\"passes version to workflow function\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow(\n { name: \"version-workflow\", version: \"1.0.0\" },\n ({ version }) => {\n return { receivedVersion: version };\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ receivedVersion: \"1.0.0\" });\n });\n\n test(\"passes null version when not specified\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"no-version-workflow\" }, ({ version }) => {\n return { receivedVersion: version };\n });\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n\n const result = await handle.result();\n expect(result).toEqual({ receivedVersion: null });\n });\n });\n});\n\ndescribe(\"executeWorkflow with dynamic retryPolicy\", () => {\n let backend: BackendPostgres;\n\n beforeAll(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterAll(async () => {\n await backend.stop();\n });\n\n test(\"calls failWorkflowRun with forceComplete=true when shouldRetry returns false\", async () => {\n const client = new OpenWorkflow({ backend });\n\n // shouldRetry가 false를 반환하는 retryPolicy\n const workflow = client.defineWorkflow(\n {\n name: \"dynamic-retry-false\",\n retryPolicy: {\n maxAttempts: 10, // 정적으로는 10번까지 허용하지만\n shouldRetry: () => ({ shouldRetry: false, delayMs: 0 }), // 동적으로 즉시 거부\n },\n },\n () => {\n throw new Error(\"Intentional failure\");\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n await sleep(100);\n\n // shouldRetry가 false를 반환했으므로 즉시 failed 상태가 되어야 합니다\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"failed\");\n expect(workflowRun?.attempts).toBe(1); // 한 번만 시도하고 종료\n });\n\n test(\"calls failWorkflowRun with forceComplete=false when shouldRetry returns true\", async () => {\n const client = new OpenWorkflow({ backend });\n\n // shouldRetry가 true를 반환하는 retryPolicy\n const workflow = client.defineWorkflow(\n {\n name: \"dynamic-retry-true\",\n retryPolicy: {\n maxAttempts: 2,\n shouldRetry: () => ({ shouldRetry: true, delayMs: 1000 }),\n },\n },\n () => {\n throw new Error(\"Intentional failure\");\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n await sleep(100);\n\n // shouldRetry가 true를 반환했으므로 pending 상태로 재시도 대기\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"pending\");\n expect(workflowRun?.attempts).toBe(1);\n });\n\n test(\"receives correct error and attempt number in shouldRetry function\", async () => {\n const client = new OpenWorkflow({ backend });\n\n let receivedError: unknown = null;\n let receivedAttempt: number | null = null;\n\n const workflow = client.defineWorkflow(\n {\n name: \"dynamic-retry-params\",\n retryPolicy: {\n maxAttempts: 10,\n shouldRetry: (error, attempt) => {\n receivedError = error;\n receivedAttempt = attempt;\n return { shouldRetry: false, delayMs: 0 };\n },\n },\n },\n () => {\n throw new Error(\"Test error message\");\n },\n );\n\n const worker = client.newWorker();\n const handle = await workflow.run();\n await worker.tick();\n await sleep(100);\n\n // shouldRetry 함수가 올바른 파라미터를 받았는지 확인\n expect(receivedError).not.toBeNull();\n expect((receivedError as { message?: string }).message).toBe(\"Test error message\");\n expect(receivedAttempt).toBe(1); // 첫 번째 시도 후이므로 1\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"failed\");\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["randomUUID","afterAll","beforeAll","describe","expect","test","BackendPostgres","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","namespaceId","runMigrations","initialize","stop","client","workflow","defineWorkflow","name","step","result","run","worker","newWorker","handle","tick","toBe","executionCount","first","second","toEqual","Error","deadlineAt","Date","sleep","rejects","toThrow","workflowRun","getWorkflowRun","workflowRunId","id","status","availableAt","not","toBeNull","value","sleeping","input","a","b","sum","product","custom","code","greeting","String","age","processed","numbers","reduce","text","toUpperCase","order","push","version","receivedVersion","retryPolicy","maxAttempts","shouldRetry","delayMs","attempts","receivedError","receivedAttempt","error","attempt","message","ms","Promise","resolve","setTimeout"],"mappings":"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"}
|
package/dist/internal.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export type * from "./client";
|
|
|
3
3
|
export { loadConfig } from "./config";
|
|
4
4
|
export type { DurationString } from "./core/duration";
|
|
5
5
|
export type { JsonValue } from "./core/json";
|
|
6
|
-
export { DEFAULT_RETRY_POLICY } from "./core/retry";
|
|
6
|
+
export { DEFAULT_RETRY_POLICY, mergeRetryPolicy, serializeRetryPolicy, shouldRetryByPolicy, calculateRetryDelayMs, shouldRetry, isDynamicRetryPolicy, isStaticRetryPolicy, } from "./core/retry";
|
|
7
|
+
export type { RetryPolicy, StaticRetryPolicy, DynamicRetryPolicy, SerializableRetryPolicy, RetryDecision, RetryDecisionFn, MergedStaticRetryPolicy, MergedDynamicRetryPolicy, MergedRetryPolicy, } from "./core/retry";
|
|
7
8
|
export type { StandardSchemaV1 } from "./core/schema";
|
|
8
9
|
export type { StepAttempt } from "./core/step";
|
|
9
10
|
export type { SchemaInput, SchemaOutput, WorkflowRun } from "./core/workflow";
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,mBAAmB,UAAU,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,mBAAmB,UAAU,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9E,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/internal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./backend.js";
|
|
2
2
|
export { loadConfig } from "./config.js";
|
|
3
|
-
export { DEFAULT_RETRY_POLICY } from "./core/retry.js";
|
|
3
|
+
export { DEFAULT_RETRY_POLICY, mergeRetryPolicy, serializeRetryPolicy, shouldRetryByPolicy, calculateRetryDelayMs, shouldRetry, isDynamicRetryPolicy, isStaticRetryPolicy } from "./core/retry.js";
|
|
4
4
|
|
|
5
5
|
//# sourceMappingURL=internal.js.map
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal.ts"],"sourcesContent":["export * from \"./backend\";\nexport type * from \"./client\";\nexport { loadConfig } from \"./config\";\nexport type { DurationString } from \"./core/duration\";\nexport type { JsonValue } from \"./core/json\";\nexport {
|
|
1
|
+
{"version":3,"sources":["../src/internal.ts"],"sourcesContent":["export * from \"./backend\";\nexport type * from \"./client\";\nexport { loadConfig } from \"./config\";\nexport type { DurationString } from \"./core/duration\";\nexport type { JsonValue } from \"./core/json\";\nexport {\n DEFAULT_RETRY_POLICY,\n mergeRetryPolicy,\n serializeRetryPolicy,\n shouldRetryByPolicy,\n calculateRetryDelayMs,\n shouldRetry,\n isDynamicRetryPolicy,\n isStaticRetryPolicy,\n} from \"./core/retry\";\nexport type {\n RetryPolicy,\n StaticRetryPolicy,\n DynamicRetryPolicy,\n SerializableRetryPolicy,\n RetryDecision,\n RetryDecisionFn,\n MergedStaticRetryPolicy,\n MergedDynamicRetryPolicy,\n MergedRetryPolicy,\n} from \"./core/retry\";\nexport type { StandardSchemaV1 } from \"./core/schema\";\nexport type { StepAttempt } from \"./core/step\";\nexport type { SchemaInput, SchemaOutput, WorkflowRun } from \"./core/workflow\";\nexport type { StepApi, WorkflowFunction } from \"./execution\";\nexport type { WorkflowSpec } from \"./workflow\";\n"],"names":["loadConfig","DEFAULT_RETRY_POLICY","mergeRetryPolicy","serializeRetryPolicy","shouldRetryByPolicy","calculateRetryDelayMs","shouldRetry","isDynamicRetryPolicy","isStaticRetryPolicy"],"mappings":"AAAA,cAAc,eAAY;AAE1B,SAASA,UAAU,QAAQ,cAAW;AAGtC,SACEC,oBAAoB,EACpBC,gBAAgB,EAChBC,oBAAoB,EACpBC,mBAAmB,EACnBC,qBAAqB,EACrBC,WAAW,EACXC,oBAAoB,EACpBC,mBAAmB,QACd,kBAAe"}
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgC;IACjE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,aAAa;IAYlC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAgB7B;;;OAGG;IACH,OAAO,KAAK,WAAW,GAEtB;YAMa,OAAO;YA+BP,sCAAsC;IA2CpD;;;;;;OAMG;YACW,4BAA4B;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgC;IACjE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,aAAa;IAYlC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAgB7B;;;OAGG;IACH,OAAO,KAAK,WAAW,GAEtB;YAMa,OAAO;YA+BP,sCAAsC;IA2CpD;;;;;;OAMG;YACW,4BAA4B;CAyB3C"}
|
package/dist/worker.js
CHANGED
|
@@ -148,7 +148,8 @@ const DEFAULT_CONCURRENCY = 1;
|
|
|
148
148
|
workflowRun: execution.workflowRun,
|
|
149
149
|
workflowFn: workflow.fn,
|
|
150
150
|
workflowVersion: execution.workflowRun.version,
|
|
151
|
-
workerId: execution.workerId
|
|
151
|
+
workerId: execution.workerId,
|
|
152
|
+
retryPolicy: workflow.spec.retryPolicy
|
|
152
153
|
});
|
|
153
154
|
} catch (error) {
|
|
154
155
|
// specifically for unexpected errors in the execution wrapper itself, not
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Backend } from \"./backend\";\nimport type { WorkflowRun } from \"./core/workflow\";\nimport { executeWorkflow } from \"./execution\";\nimport type { WorkflowRegistry } from \"./registry\";\nimport type { Workflow } from \"./workflow\";\n\nconst DEFAULT_LEASE_DURATION_MS = 30 * 1000; // 30s\nconst DEFAULT_POLL_INTERVAL_MS = 100; // 100ms\nconst DEFAULT_CONCURRENCY = 1;\n\n/**\n * Configures how a Worker polls the backend, leases workflow runs, and\n * registers workflows.\n */\nexport interface WorkerOptions {\n backend: Backend;\n registry: WorkflowRegistry;\n concurrency?: number | undefined;\n usePubSub?: boolean;\n listenDelay?: number;\n}\n\n/**\n * Runs workflows by polling the backend, dispatching runs across a concurrency\n * pool, and heartbeating/extending leases.\n */\nexport class Worker {\n private readonly backend: Backend;\n private readonly workerIds: string[];\n private readonly registry: WorkflowRegistry;\n private readonly activeExecutions = new Set<WorkflowExecution>();\n private running = false;\n private loopPromise: Promise<void> | null = null;\n\n private usePubSub: boolean;\n private listenDelay: number;\n\n constructor(options: WorkerOptions) {\n this.backend = options.backend;\n this.registry = options.registry;\n this.usePubSub = options.usePubSub ?? true;\n this.listenDelay = options.listenDelay ?? 500;\n\n const concurrency = Math.max(DEFAULT_CONCURRENCY, options.concurrency ?? DEFAULT_CONCURRENCY);\n\n // generate worker IDs for every concurrency slot\n this.workerIds = Array.from({ length: concurrency }, () => randomUUID());\n }\n\n /**\n * Start the worker. It will begin polling for and executing workflows.\n * @returns Promise resolved when started\n */\n async start(): Promise<void> {\n if (this.running) return;\n this.running = true;\n this.loopPromise = this.runLoop();\n await Promise.resolve();\n }\n\n /**\n * Stop the worker gracefully. Waits for all active workflow runs to complete\n * before returning.\n * @returns Promise resolved when stopped\n */\n async stop(): Promise<void> {\n this.running = false;\n\n // wait for the poll loop to stop\n if (this.loopPromise) await this.loopPromise;\n\n // wait for all active executions to finish\n while (this.activeExecutions.size > 0) await sleep(100);\n }\n\n /**\n * Processes one round of work claims and execution. Exposed for testing.\n * Returns the number of workflow runs claimed.\n * @returns Number of workflow runs claimed\n */\n async tick(): Promise<number> {\n const availableSlots = this.concurrency - this.activeExecutions.size;\n if (availableSlots <= 0) return 0;\n\n // claim work for each available slot\n const claims = Array.from({ length: availableSlots }, (_, i) => {\n const availableWorkerId = this.workerIds[i % this.workerIds.length];\n return availableWorkerId\n ? this.claimAndProcessWorkflowRunInBackground(availableWorkerId)\n : Promise.resolve(null);\n });\n\n const claimed = await Promise.all(claims);\n return claimed.filter((run) => run !== null).length;\n }\n\n /**\n * Get the configured concurrency limit.\n * @returns Concurrency limit\n */\n private get concurrency(): number {\n return this.workerIds.length;\n }\n\n /*\n * Main run loop that continuously ticks while the worker is running.\n * Only sleeps when no work was claimed to avoid busy-waiting.\n */\n private async runLoop(): Promise<void> {\n if (this.usePubSub) {\n this.backend.subscribe(async (result) => {\n if (!result.ok) {\n return;\n }\n\n await sleep(this.listenDelay);\n await this.tick();\n });\n }\n\n while (this.running) {\n try {\n const claimedCount = await this.tick();\n // only sleep if we didn't claim any work\n if (claimedCount === 0) {\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n } catch (error) {\n console.error(\"Worker tick failed:\", error);\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n }\n }\n\n /*\n * Cclaim and process a workflow run for the given worker ID. Do not await the\n * processing here to avoid blocking the caller.\n * Returns the claimed workflow run, or null if none was available.\n */\n private async claimAndProcessWorkflowRunInBackground(\n workerId: string,\n ): Promise<WorkflowRun | null> {\n // claim workflow run\n const workflowRun = await this.backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: DEFAULT_LEASE_DURATION_MS,\n });\n if (!workflowRun) return null;\n\n const workflow = this.registry.get(workflowRun.workflowName, workflowRun.version);\n if (!workflow) {\n const versionStr = workflowRun.version ? ` (version: ${workflowRun.version})` : \"\";\n await this.backend.failWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n error: {\n message: `Workflow \"${workflowRun.workflowName}\"${versionStr} is not registered`,\n },\n });\n return null;\n }\n\n // create execution and start processing *async* w/o blocking\n const execution = new WorkflowExecution({\n backend: this.backend,\n workflowRun,\n workerId,\n });\n this.activeExecutions.add(execution);\n\n this.processExecutionInBackground(execution, workflow)\n .catch(() => {\n // errors are already handled in processExecution\n })\n .finally(() => {\n execution.stopHeartbeat();\n this.activeExecutions.delete(execution);\n });\n\n return workflowRun;\n }\n\n /**\n * Process a workflow execution, handling heartbeats, step execution, and\n * marking success or failure.\n * @param execution - Workflow execution\n * @param workflow - Workflow to execute\n * @returns Promise resolved when processing completes\n */\n private async processExecutionInBackground(\n execution: WorkflowExecution,\n workflow: Workflow<unknown, unknown, unknown>,\n ): Promise<void> {\n // start heartbeating\n execution.startHeartbeat();\n\n try {\n await executeWorkflow({\n backend: this.backend,\n workflowRun: execution.workflowRun,\n workflowFn: workflow.fn,\n workflowVersion: execution.workflowRun.version,\n workerId: execution.workerId,\n });\n } catch (error) {\n // specifically for unexpected errors in the execution wrapper itself, not\n // for business logic errors (those are handled inside executeWorkflow)\n console.error(\n `Critical error during workflow execution for run ${execution.workflowRun.id}:`,\n error,\n );\n }\n }\n}\n\n/**\n * Configures the options for a WorkflowExecution.\n */\ninterface WorkflowExecutionOptions {\n backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n}\n\n/**\n * Tracks a claimed workflow run and maintains its heartbeat lease for the\n * worker.\n */\nclass WorkflowExecution {\n private backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n\n constructor(options: WorkflowExecutionOptions) {\n this.backend = options.backend;\n this.workflowRun = options.workflowRun;\n this.workerId = options.workerId;\n }\n\n /**\n * Start the heartbeat loop for this execution, heartbeating at half the lease\n * duration.\n */\n startHeartbeat(): void {\n const leaseDurationMs = DEFAULT_LEASE_DURATION_MS;\n const heartbeatIntervalMs = leaseDurationMs / 2;\n\n this.heartbeatTimer = setInterval(() => {\n this.backend\n .extendWorkflowRunLease({\n workflowRunId: this.workflowRun.id,\n workerId: this.workerId,\n leaseDurationMs,\n })\n .catch((error: unknown) => {\n console.error(\"Heartbeat failed:\", error);\n });\n }, heartbeatIntervalMs);\n }\n\n /**\n * Stop the heartbeat loop.\n */\n stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n}\n\n/**\n * Sleep for a given duration.\n * @param ms - Milliseconds to sleep\n * @returns Promise resolved after sleeping\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["randomUUID","executeWorkflow","DEFAULT_LEASE_DURATION_MS","DEFAULT_POLL_INTERVAL_MS","DEFAULT_CONCURRENCY","Worker","backend","workerIds","registry","activeExecutions","Set","running","loopPromise","usePubSub","listenDelay","options","concurrency","Math","max","Array","from","length","start","runLoop","Promise","resolve","stop","size","sleep","tick","availableSlots","claims","_","i","availableWorkerId","claimAndProcessWorkflowRunInBackground","claimed","all","filter","run","subscribe","result","ok","claimedCount","error","console","workerId","workflowRun","claimWorkflowRun","leaseDurationMs","workflow","get","workflowName","version","versionStr","failWorkflowRun","workflowRunId","id","message","execution","WorkflowExecution","add","processExecutionInBackground","catch","finally","stopHeartbeat","delete","startHeartbeat","workflowFn","fn","workflowVersion","heartbeatTimer","heartbeatIntervalMs","setInterval","extendWorkflowRunLease","clearInterval","ms","setTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Backend } from \"./backend\";\nimport type { WorkflowRun } from \"./core/workflow\";\nimport { executeWorkflow } from \"./execution\";\nimport type { WorkflowRegistry } from \"./registry\";\nimport type { Workflow } from \"./workflow\";\n\nconst DEFAULT_LEASE_DURATION_MS = 30 * 1000; // 30s\nconst DEFAULT_POLL_INTERVAL_MS = 100; // 100ms\nconst DEFAULT_CONCURRENCY = 1;\n\n/**\n * Configures how a Worker polls the backend, leases workflow runs, and\n * registers workflows.\n */\nexport interface WorkerOptions {\n backend: Backend;\n registry: WorkflowRegistry;\n concurrency?: number | undefined;\n usePubSub?: boolean;\n listenDelay?: number;\n}\n\n/**\n * Runs workflows by polling the backend, dispatching runs across a concurrency\n * pool, and heartbeating/extending leases.\n */\nexport class Worker {\n private readonly backend: Backend;\n private readonly workerIds: string[];\n private readonly registry: WorkflowRegistry;\n private readonly activeExecutions = new Set<WorkflowExecution>();\n private running = false;\n private loopPromise: Promise<void> | null = null;\n\n private usePubSub: boolean;\n private listenDelay: number;\n\n constructor(options: WorkerOptions) {\n this.backend = options.backend;\n this.registry = options.registry;\n this.usePubSub = options.usePubSub ?? true;\n this.listenDelay = options.listenDelay ?? 500;\n\n const concurrency = Math.max(DEFAULT_CONCURRENCY, options.concurrency ?? DEFAULT_CONCURRENCY);\n\n // generate worker IDs for every concurrency slot\n this.workerIds = Array.from({ length: concurrency }, () => randomUUID());\n }\n\n /**\n * Start the worker. It will begin polling for and executing workflows.\n * @returns Promise resolved when started\n */\n async start(): Promise<void> {\n if (this.running) return;\n this.running = true;\n this.loopPromise = this.runLoop();\n await Promise.resolve();\n }\n\n /**\n * Stop the worker gracefully. Waits for all active workflow runs to complete\n * before returning.\n * @returns Promise resolved when stopped\n */\n async stop(): Promise<void> {\n this.running = false;\n\n // wait for the poll loop to stop\n if (this.loopPromise) await this.loopPromise;\n\n // wait for all active executions to finish\n while (this.activeExecutions.size > 0) await sleep(100);\n }\n\n /**\n * Processes one round of work claims and execution. Exposed for testing.\n * Returns the number of workflow runs claimed.\n * @returns Number of workflow runs claimed\n */\n async tick(): Promise<number> {\n const availableSlots = this.concurrency - this.activeExecutions.size;\n if (availableSlots <= 0) return 0;\n\n // claim work for each available slot\n const claims = Array.from({ length: availableSlots }, (_, i) => {\n const availableWorkerId = this.workerIds[i % this.workerIds.length];\n return availableWorkerId\n ? this.claimAndProcessWorkflowRunInBackground(availableWorkerId)\n : Promise.resolve(null);\n });\n\n const claimed = await Promise.all(claims);\n return claimed.filter((run) => run !== null).length;\n }\n\n /**\n * Get the configured concurrency limit.\n * @returns Concurrency limit\n */\n private get concurrency(): number {\n return this.workerIds.length;\n }\n\n /*\n * Main run loop that continuously ticks while the worker is running.\n * Only sleeps when no work was claimed to avoid busy-waiting.\n */\n private async runLoop(): Promise<void> {\n if (this.usePubSub) {\n this.backend.subscribe(async (result) => {\n if (!result.ok) {\n return;\n }\n\n await sleep(this.listenDelay);\n await this.tick();\n });\n }\n\n while (this.running) {\n try {\n const claimedCount = await this.tick();\n // only sleep if we didn't claim any work\n if (claimedCount === 0) {\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n } catch (error) {\n console.error(\"Worker tick failed:\", error);\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n }\n }\n\n /*\n * Cclaim and process a workflow run for the given worker ID. Do not await the\n * processing here to avoid blocking the caller.\n * Returns the claimed workflow run, or null if none was available.\n */\n private async claimAndProcessWorkflowRunInBackground(\n workerId: string,\n ): Promise<WorkflowRun | null> {\n // claim workflow run\n const workflowRun = await this.backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: DEFAULT_LEASE_DURATION_MS,\n });\n if (!workflowRun) return null;\n\n const workflow = this.registry.get(workflowRun.workflowName, workflowRun.version);\n if (!workflow) {\n const versionStr = workflowRun.version ? ` (version: ${workflowRun.version})` : \"\";\n await this.backend.failWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n error: {\n message: `Workflow \"${workflowRun.workflowName}\"${versionStr} is not registered`,\n },\n });\n return null;\n }\n\n // create execution and start processing *async* w/o blocking\n const execution = new WorkflowExecution({\n backend: this.backend,\n workflowRun,\n workerId,\n });\n this.activeExecutions.add(execution);\n\n this.processExecutionInBackground(execution, workflow)\n .catch(() => {\n // errors are already handled in processExecution\n })\n .finally(() => {\n execution.stopHeartbeat();\n this.activeExecutions.delete(execution);\n });\n\n return workflowRun;\n }\n\n /**\n * Process a workflow execution, handling heartbeats, step execution, and\n * marking success or failure.\n * @param execution - Workflow execution\n * @param workflow - Workflow to execute\n * @returns Promise resolved when processing completes\n */\n private async processExecutionInBackground(\n execution: WorkflowExecution,\n workflow: Workflow<unknown, unknown, unknown>,\n ): Promise<void> {\n // start heartbeating\n execution.startHeartbeat();\n\n try {\n await executeWorkflow({\n backend: this.backend,\n workflowRun: execution.workflowRun,\n workflowFn: workflow.fn,\n workflowVersion: execution.workflowRun.version,\n workerId: execution.workerId,\n retryPolicy: workflow.spec.retryPolicy,\n });\n } catch (error) {\n // specifically for unexpected errors in the execution wrapper itself, not\n // for business logic errors (those are handled inside executeWorkflow)\n console.error(\n `Critical error during workflow execution for run ${execution.workflowRun.id}:`,\n error,\n );\n }\n }\n}\n\n/**\n * Configures the options for a WorkflowExecution.\n */\ninterface WorkflowExecutionOptions {\n backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n}\n\n/**\n * Tracks a claimed workflow run and maintains its heartbeat lease for the\n * worker.\n */\nclass WorkflowExecution {\n private backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n\n constructor(options: WorkflowExecutionOptions) {\n this.backend = options.backend;\n this.workflowRun = options.workflowRun;\n this.workerId = options.workerId;\n }\n\n /**\n * Start the heartbeat loop for this execution, heartbeating at half the lease\n * duration.\n */\n startHeartbeat(): void {\n const leaseDurationMs = DEFAULT_LEASE_DURATION_MS;\n const heartbeatIntervalMs = leaseDurationMs / 2;\n\n this.heartbeatTimer = setInterval(() => {\n this.backend\n .extendWorkflowRunLease({\n workflowRunId: this.workflowRun.id,\n workerId: this.workerId,\n leaseDurationMs,\n })\n .catch((error: unknown) => {\n console.error(\"Heartbeat failed:\", error);\n });\n }, heartbeatIntervalMs);\n }\n\n /**\n * Stop the heartbeat loop.\n */\n stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n}\n\n/**\n * Sleep for a given duration.\n * @param ms - Milliseconds to sleep\n * @returns Promise resolved after sleeping\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["randomUUID","executeWorkflow","DEFAULT_LEASE_DURATION_MS","DEFAULT_POLL_INTERVAL_MS","DEFAULT_CONCURRENCY","Worker","backend","workerIds","registry","activeExecutions","Set","running","loopPromise","usePubSub","listenDelay","options","concurrency","Math","max","Array","from","length","start","runLoop","Promise","resolve","stop","size","sleep","tick","availableSlots","claims","_","i","availableWorkerId","claimAndProcessWorkflowRunInBackground","claimed","all","filter","run","subscribe","result","ok","claimedCount","error","console","workerId","workflowRun","claimWorkflowRun","leaseDurationMs","workflow","get","workflowName","version","versionStr","failWorkflowRun","workflowRunId","id","message","execution","WorkflowExecution","add","processExecutionInBackground","catch","finally","stopHeartbeat","delete","startHeartbeat","workflowFn","fn","workflowVersion","retryPolicy","spec","heartbeatTimer","heartbeatIntervalMs","setInterval","extendWorkflowRunLease","clearInterval","ms","setTimeout"],"mappings":"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"}
|
package/dist/workflow.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RetryPolicy } from "./core/retry";
|
|
1
2
|
import type { StandardSchemaV1 } from "./core/schema";
|
|
2
3
|
import type { WorkflowFunction } from "./execution";
|
|
3
4
|
export interface WorkflowSpec<Input, Output, RawInput> {
|
|
@@ -7,6 +8,8 @@ export interface WorkflowSpec<Input, Output, RawInput> {
|
|
|
7
8
|
readonly version?: string;
|
|
8
9
|
/** The schema used to validate inputs. */
|
|
9
10
|
readonly schema?: StandardSchemaV1<RawInput, Input>;
|
|
11
|
+
/** The retry policy for the workflow. */
|
|
12
|
+
readonly retryPolicy?: RetryPolicy;
|
|
10
13
|
/** Phantom type carrier - won't exist at runtime. */
|
|
11
14
|
readonly __types?: {
|
|
12
15
|
output: Output;
|
package/dist/workflow.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ;IACnD,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpD,qDAAqD;IACrD,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,QAAQ,GAAG,KAAK,EAC1E,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,GAC1C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAEvC;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ;IAC/C,yBAAyB;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;;;;GAKG;AAIH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAG,KAAK,EAC5D,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAC3C,EAAE,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAErC;;;;;GAKG;AAGH,wBAAgB,cAAc,CAC5B,KAAK,EACL,UAAU,SAAS,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,EACtF,QAAQ,GAAG,KAAK,EAEhB,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,EACpE,EAAE,EAAE,UAAU,GACb,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ;IACnD,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpD,yCAAyC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,QAAQ,GAAG,KAAK,EAC1E,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,GAC1C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAEvC;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ;IAC/C,yBAAyB;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;;;;GAKG;AAIH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAG,KAAK,EAC5D,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAC3C,EAAE,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAErC;;;;;GAKG;AAGH,wBAAgB,cAAc,CAC5B,KAAK,EACL,UAAU,SAAS,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,EACtF,QAAQ,GAAG,KAAK,EAEhB,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,EACpE,EAAE,EAAE,UAAU,GACb,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC"}
|
package/dist/workflow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workflow.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"./core/schema\";\nimport type { WorkflowFunction } from \"./execution\";\n\nexport interface WorkflowSpec<Input, Output, RawInput> {\n /** The name of the workflow. */\n readonly name: string;\n /** The version of the workflow. */\n readonly version?: string;\n /** The schema used to validate inputs. */\n readonly schema?: StandardSchemaV1<RawInput, Input>;\n /** Phantom type carrier - won't exist at runtime. */\n readonly __types?: {\n output: Output;\n };\n}\n\n/**\n * Define a workflow spec.\n * @param spec - The workflow spec\n * @returns The workflow spec\n */\nexport function defineWorkflowSpec<Input, Output = unknown, RawInput = Input>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n): WorkflowSpec<Input, Output, RawInput> {\n return spec;\n}\n\n/**\n * A workflow spec and implementation.\n */\nexport interface Workflow<Input, Output, RawInput> {\n /** The workflow spec. */\n readonly spec: WorkflowSpec<Input, Output, RawInput>;\n /** The workflow implementation function. */\n readonly fn: WorkflowFunction<Input, Output>;\n}\n\n/**\n * Define a workflow.\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\n// Handles:\n// - `defineWorkflow(spec, fn)` (0 generics)\n// - `defineWorkflow<Input, Output>(spec, fn)` (2 generics)\nexport function defineWorkflow<Input, Output, RawInput = Input>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n fn: WorkflowFunction<Input, Output>,\n): Workflow<Input, Output, RawInput>;\n\n/**\n * Define a workflow.\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\n// Handles:\n// - `defineWorkflow<Input>(spec, fn)` (1 generic)\nexport function defineWorkflow<\n Input,\n WorkflowFn extends WorkflowFunction<Input, unknown> = WorkflowFunction<Input, unknown>,\n RawInput = Input,\n>(\n spec: WorkflowSpec<Input, Awaited<ReturnType<WorkflowFn>>, RawInput>,\n fn: WorkflowFn,\n): Workflow<Input, Awaited<ReturnType<WorkflowFn>>, RawInput>;\n\n/**\n * Define a workflow.\n * @internal\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\nexport function defineWorkflow<Input, Output, RawInput>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n fn: WorkflowFunction<Input, Output>,\n): Workflow<Input, Output, RawInput> {\n return {\n spec,\n fn,\n };\n}\n"],"names":["defineWorkflowSpec","spec","defineWorkflow","fn"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/workflow.ts"],"sourcesContent":["import type { RetryPolicy } from \"./core/retry\";\nimport type { StandardSchemaV1 } from \"./core/schema\";\nimport type { WorkflowFunction } from \"./execution\";\n\nexport interface WorkflowSpec<Input, Output, RawInput> {\n /** The name of the workflow. */\n readonly name: string;\n /** The version of the workflow. */\n readonly version?: string;\n /** The schema used to validate inputs. */\n readonly schema?: StandardSchemaV1<RawInput, Input>;\n /** The retry policy for the workflow. */\n readonly retryPolicy?: RetryPolicy;\n /** Phantom type carrier - won't exist at runtime. */\n readonly __types?: {\n output: Output;\n };\n}\n\n/**\n * Define a workflow spec.\n * @param spec - The workflow spec\n * @returns The workflow spec\n */\nexport function defineWorkflowSpec<Input, Output = unknown, RawInput = Input>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n): WorkflowSpec<Input, Output, RawInput> {\n return spec;\n}\n\n/**\n * A workflow spec and implementation.\n */\nexport interface Workflow<Input, Output, RawInput> {\n /** The workflow spec. */\n readonly spec: WorkflowSpec<Input, Output, RawInput>;\n /** The workflow implementation function. */\n readonly fn: WorkflowFunction<Input, Output>;\n}\n\n/**\n * Define a workflow.\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\n// Handles:\n// - `defineWorkflow(spec, fn)` (0 generics)\n// - `defineWorkflow<Input, Output>(spec, fn)` (2 generics)\nexport function defineWorkflow<Input, Output, RawInput = Input>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n fn: WorkflowFunction<Input, Output>,\n): Workflow<Input, Output, RawInput>;\n\n/**\n * Define a workflow.\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\n// Handles:\n// - `defineWorkflow<Input>(spec, fn)` (1 generic)\nexport function defineWorkflow<\n Input,\n WorkflowFn extends WorkflowFunction<Input, unknown> = WorkflowFunction<Input, unknown>,\n RawInput = Input,\n>(\n spec: WorkflowSpec<Input, Awaited<ReturnType<WorkflowFn>>, RawInput>,\n fn: WorkflowFn,\n): Workflow<Input, Awaited<ReturnType<WorkflowFn>>, RawInput>;\n\n/**\n * Define a workflow.\n * @internal\n * @param spec - The workflow spec\n * @param fn - The workflow implementation function\n * @returns The workflow\n */\nexport function defineWorkflow<Input, Output, RawInput>(\n spec: WorkflowSpec<Input, Output, RawInput>,\n fn: WorkflowFunction<Input, Output>,\n): Workflow<Input, Output, RawInput> {\n return {\n spec,\n fn,\n };\n}\n"],"names":["defineWorkflowSpec","spec","defineWorkflow","fn"],"mappings":"AAmBA;;;;CAIC,GACD,OAAO,SAASA,mBACdC,IAA2C;IAE3C,OAAOA;AACT;AA2CA;;;;;;CAMC,GACD,OAAO,SAASC,eACdD,IAA2C,EAC3CE,EAAmC;IAEnC,OAAO;QACLF;QACAE;IACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sonamu-kit/tasks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Sonamu Task - Simple & Distributed Task Queue",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@biomejs/biome": "^2.3.10",
|
|
39
39
|
"@swc/cli": "^0.7.8",
|
|
40
40
|
"@swc/core": "^1.13.5",
|
|
41
|
-
"@types/node": "25.0.
|
|
41
|
+
"@types/node": "25.0.7",
|
|
42
42
|
"nodemon": "^3.1.10",
|
|
43
43
|
"tsx": "^4.20.6",
|
|
44
44
|
"typescript": "^5.9.3",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"knex": "^3.1.0",
|
|
49
|
-
"@logtape/logtape": "
|
|
49
|
+
"@logtape/logtape": "2.0.0"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
52
|
"pg-native": "^3.5.2"
|
package/src/backend.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SerializedError } from "./core/error";
|
|
2
2
|
import type { JsonValue } from "./core/json";
|
|
3
|
+
import type { SerializableRetryPolicy } from "./core/retry";
|
|
3
4
|
import type { StepAttempt, StepAttemptContext, StepKind } from "./core/step";
|
|
4
5
|
import type { WorkflowRun } from "./core/workflow";
|
|
5
6
|
import type { OnSubscribed } from "./database/pubsub";
|
|
@@ -46,6 +47,7 @@ export interface CreateWorkflowRunParams {
|
|
|
46
47
|
input: JsonValue | null;
|
|
47
48
|
availableAt: Date | null; // null = immediately
|
|
48
49
|
deadlineAt: Date | null; // null = no deadline
|
|
50
|
+
retryPolicy?: SerializableRetryPolicy;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
export interface GetWorkflowRunParams {
|
|
@@ -81,6 +83,8 @@ export interface FailWorkflowRunParams {
|
|
|
81
83
|
workflowRunId: string;
|
|
82
84
|
workerId: string;
|
|
83
85
|
error: SerializedError;
|
|
86
|
+
forceComplete?: boolean;
|
|
87
|
+
customDelayMs?: number;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
export interface CancelWorkflowRunParams {
|
package/src/client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Backend } from "./backend";
|
|
2
|
+
import { serializeRetryPolicy } from "./core/retry";
|
|
2
3
|
import type { StandardSchemaV1 } from "./core/schema";
|
|
3
4
|
import type { SchemaInput, SchemaOutput, WorkflowRun } from "./core/workflow";
|
|
4
5
|
import { validateInput } from "./core/workflow";
|
|
@@ -101,6 +102,7 @@ export class OpenWorkflow {
|
|
|
101
102
|
input: parsedInput ?? null,
|
|
102
103
|
availableAt: null,
|
|
103
104
|
deadlineAt: options?.deadlineAt ?? null,
|
|
105
|
+
retryPolicy: spec.retryPolicy ? serializeRetryPolicy(spec.retryPolicy) : undefined,
|
|
104
106
|
});
|
|
105
107
|
|
|
106
108
|
if (options?.publishToChannel) {
|