@sonamu-kit/tasks 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,10 +7,11 @@ import { KNEX_GLOBAL_CONFIG } from "./testing/connection.js";
7
7
  describe("OpenWorkflow", ()=>{
8
8
  let backend;
9
9
  beforeEach(async ()=>{
10
- backend = await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {
10
+ backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
11
11
  namespaceId: randomUUID(),
12
12
  runMigrations: false
13
13
  });
14
+ await backend.initialize();
14
15
  });
15
16
  afterEach(async ()=>{
16
17
  await backend.stop();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterEach, beforeEach, describe, expect, test } from \"vitest\";\nimport { z } from \"zod\";\nimport { BackendPostgres } from \".\";\nimport { declareWorkflow, OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"OpenWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeEach(async () => {\n backend = await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n });\n\n afterEach(async () => {\n await backend.stop();\n });\n\n test(\"enqueues workflow runs via backend\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"enqueue-test\" }, noopFn);\n await workflow.run({ docUrl: \"https://example.com\" });\n\n const workerId = \"enqueue-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n\n expect(claimed?.workflowName).toBe(\"enqueue-test\");\n expect(claimed?.workerId).toBe(workerId);\n expect(claimed?.input).toEqual({ docUrl: \"https://example.com\" });\n });\n\n describe(\"schema validation\", () => {\n describe(\"Zod schema\", () => {\n const schema = z.object({\n userId: z.uuid(),\n count: z.number().int().positive(),\n });\n\n test(\"accepts valid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-valid\", schema }, noopFn);\n\n const handle = await workflow.run({\n userId: randomUUID(),\n count: 3,\n });\n\n await handle.cancel();\n });\n\n test(\"rejects invalid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-invalid\", schema }, noopFn);\n\n await expect(workflow.run({ userId: \"not-a-uuid\", count: 0 } as never)).rejects.toThrow();\n });\n });\n });\n\n test(\"result resolves when workflow succeeds\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-success\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n await backend.completeWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n output: { ok: true },\n });\n\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression\n const result = await handle.result();\n expect(result).toEqual({ ok: true });\n });\n\n test(\"result rejects when workflow fails\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-failure\" }, noopFn);\n await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n // mark as failed (should reschedule))\n await backend.failWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n error: { message: \"boom\" },\n });\n\n const rescheduled = await backend.getWorkflowRun({\n workflowRunId: claimed.id,\n });\n expect(rescheduled?.status).toBe(\"pending\");\n expect(rescheduled?.error).toEqual({ message: \"boom\" });\n });\n\n test(\"creates workflow run with deadline\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"deadline-test\" }, noopFn);\n const deadline = new Date(Date.now() + 60_000); // in 1 minute\n const handle = await workflow.run({ value: 1 }, { deadlineAt: deadline });\n\n expect(handle.workflowRun.deadlineAt).not.toBeNull();\n expect(handle.workflowRun.deadlineAt?.getTime()).toBe(deadline.getTime());\n });\n\n test(\"creates workflow run with version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"versioned-test\", version: \"v2.0\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBe(\"v2.0\");\n });\n\n test(\"creates workflow run without version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"unversioned-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBeNull();\n });\n\n test(\"cancels workflow run via handle\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"cancel-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n await handle.cancel();\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"canceled\");\n expect(workflowRun?.finishedAt).not.toBeNull();\n });\n\n describe(\"declareWorkflow / implementWorkflow API\", () => {\n test(\"declareWorkflow returns a spec that can be used to schedule runs\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"declare-test\" });\n\n const handle = await client.runWorkflow(spec, { message: \"hello\" });\n expect(handle.workflowRun.workflowName).toBe(\"declare-test\");\n\n await handle.cancel();\n });\n\n test(\"implementWorkflow registers the workflow for worker execution\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"implement-test\" });\n client.implementWorkflow(spec, ({ input }) => {\n return { received: input };\n });\n\n const handle = await client.runWorkflow(spec, { data: 42 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ received: { data: 42 } });\n });\n\n test(\"implementWorkflow throws when workflow is already registered\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"duplicate-test\" });\n client.implementWorkflow(spec, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec, noopFn);\n }).toThrow('Workflow \"duplicate-test\" is already registered');\n });\n\n test(\"implementWorkflow allows registering different versions of the same workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const specV1 = declareWorkflow({\n name: \"multi-version\",\n version: \"v1\",\n });\n const specV2 = declareWorkflow({\n name: \"multi-version\",\n version: \"v2\",\n });\n\n // no throwing...\n client.implementWorkflow(specV1, noopFn);\n client.implementWorkflow(specV2, noopFn);\n });\n\n test(\"implementWorkflow throws for same name+version combination\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec1 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n const spec2 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n\n client.implementWorkflow(spec1, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec2, noopFn);\n }).toThrow('Workflow \"version-duplicate\" (version: v1) is already registered');\n });\n\n test(\"declareWorkflow with schema validates input on runWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const schema = z.object({\n email: z.email(),\n });\n const spec = declareWorkflow({\n name: \"declare-schema-test\",\n schema,\n });\n\n const handle = await client.runWorkflow(spec, {\n email: \"test@example.com\",\n });\n await handle.cancel();\n\n await expect(client.runWorkflow(spec, { email: \"not-an-email\" })).rejects.toThrow();\n });\n\n test(\"declareWorkflow with version sets version on workflow run\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({\n name: \"declare-version-test\",\n version: \"v1.2.3\",\n });\n\n const handle = await client.runWorkflow(spec);\n expect(handle.workflowRun.version).toBe(\"v1.2.3\");\n\n await handle.cancel();\n });\n\n test(\"defineWorkflow wraps declareWorkflow and implementWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"define-wrap-test\" }, ({ input }) => ({\n doubled: (input as { n: number }).n * 2,\n }));\n\n const handle = await workflow.run({ n: 21 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ doubled: 42 });\n });\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function noopFn() {\n // no-op\n}\n"],"names":["randomUUID","afterEach","beforeEach","describe","expect","test","z","BackendPostgres","declareWorkflow","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","connect","namespaceId","runMigrations","stop","client","workflow","defineWorkflow","name","noopFn","run","docUrl","workerId","claimed","claimWorkflowRun","leaseDurationMs","workflowName","toBe","input","toEqual","schema","object","userId","uuid","count","number","int","positive","handle","cancel","rejects","toThrow","value","not","toBeNull","Error","completeWorkflowRun","workflowRunId","id","output","ok","result","failWorkflowRun","error","message","rescheduled","getWorkflowRun","status","deadline","Date","now","deadlineAt","workflowRun","getTime","version","finishedAt","spec","runWorkflow","implementWorkflow","received","data","worker","newWorker","tick","sleep","specV1","specV2","spec1","spec2","email","doubled","n","ms","Promise","resolve","setTimeout"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/client.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterEach, beforeEach, describe, expect, test } from \"vitest\";\nimport { z } from \"zod\";\nimport { BackendPostgres } from \".\";\nimport { declareWorkflow, OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"OpenWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeEach(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterEach(async () => {\n await backend.stop();\n });\n\n test(\"enqueues workflow runs via backend\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"enqueue-test\" }, noopFn);\n await workflow.run({ docUrl: \"https://example.com\" });\n\n const workerId = \"enqueue-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n\n expect(claimed?.workflowName).toBe(\"enqueue-test\");\n expect(claimed?.workerId).toBe(workerId);\n expect(claimed?.input).toEqual({ docUrl: \"https://example.com\" });\n });\n\n describe(\"schema validation\", () => {\n describe(\"Zod schema\", () => {\n const schema = z.object({\n userId: z.uuid(),\n count: z.number().int().positive(),\n });\n\n test(\"accepts valid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-valid\", schema }, noopFn);\n\n const handle = await workflow.run({\n userId: randomUUID(),\n count: 3,\n });\n\n await handle.cancel();\n });\n\n test(\"rejects invalid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-invalid\", schema }, noopFn);\n\n await expect(workflow.run({ userId: \"not-a-uuid\", count: 0 } as never)).rejects.toThrow();\n });\n });\n });\n\n test(\"result resolves when workflow succeeds\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-success\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n await backend.completeWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n output: { ok: true },\n });\n\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression\n const result = await handle.result();\n expect(result).toEqual({ ok: true });\n });\n\n test(\"result rejects when workflow fails\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-failure\" }, noopFn);\n await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n // mark as failed (should reschedule))\n await backend.failWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n error: { message: \"boom\" },\n });\n\n const rescheduled = await backend.getWorkflowRun({\n workflowRunId: claimed.id,\n });\n expect(rescheduled?.status).toBe(\"pending\");\n expect(rescheduled?.error).toEqual({ message: \"boom\" });\n });\n\n test(\"creates workflow run with deadline\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"deadline-test\" }, noopFn);\n const deadline = new Date(Date.now() + 60_000); // in 1 minute\n const handle = await workflow.run({ value: 1 }, { deadlineAt: deadline });\n\n expect(handle.workflowRun.deadlineAt).not.toBeNull();\n expect(handle.workflowRun.deadlineAt?.getTime()).toBe(deadline.getTime());\n });\n\n test(\"creates workflow run with version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"versioned-test\", version: \"v2.0\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBe(\"v2.0\");\n });\n\n test(\"creates workflow run without version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"unversioned-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBeNull();\n });\n\n test(\"cancels workflow run via handle\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"cancel-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n await handle.cancel();\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"canceled\");\n expect(workflowRun?.finishedAt).not.toBeNull();\n });\n\n describe(\"declareWorkflow / implementWorkflow API\", () => {\n test(\"declareWorkflow returns a spec that can be used to schedule runs\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"declare-test\" });\n\n const handle = await client.runWorkflow(spec, { message: \"hello\" });\n expect(handle.workflowRun.workflowName).toBe(\"declare-test\");\n\n await handle.cancel();\n });\n\n test(\"implementWorkflow registers the workflow for worker execution\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"implement-test\" });\n client.implementWorkflow(spec, ({ input }) => {\n return { received: input };\n });\n\n const handle = await client.runWorkflow(spec, { data: 42 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ received: { data: 42 } });\n });\n\n test(\"implementWorkflow throws when workflow is already registered\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"duplicate-test\" });\n client.implementWorkflow(spec, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec, noopFn);\n }).toThrow('Workflow \"duplicate-test\" is already registered');\n });\n\n test(\"implementWorkflow allows registering different versions of the same workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const specV1 = declareWorkflow({\n name: \"multi-version\",\n version: \"v1\",\n });\n const specV2 = declareWorkflow({\n name: \"multi-version\",\n version: \"v2\",\n });\n\n // no throwing...\n client.implementWorkflow(specV1, noopFn);\n client.implementWorkflow(specV2, noopFn);\n });\n\n test(\"implementWorkflow throws for same name+version combination\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec1 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n const spec2 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n\n client.implementWorkflow(spec1, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec2, noopFn);\n }).toThrow('Workflow \"version-duplicate\" (version: v1) is already registered');\n });\n\n test(\"declareWorkflow with schema validates input on runWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const schema = z.object({\n email: z.email(),\n });\n const spec = declareWorkflow({\n name: \"declare-schema-test\",\n schema,\n });\n\n const handle = await client.runWorkflow(spec, {\n email: \"test@example.com\",\n });\n await handle.cancel();\n\n await expect(client.runWorkflow(spec, { email: \"not-an-email\" })).rejects.toThrow();\n });\n\n test(\"declareWorkflow with version sets version on workflow run\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({\n name: \"declare-version-test\",\n version: \"v1.2.3\",\n });\n\n const handle = await client.runWorkflow(spec);\n expect(handle.workflowRun.version).toBe(\"v1.2.3\");\n\n await handle.cancel();\n });\n\n test(\"defineWorkflow wraps declareWorkflow and implementWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"define-wrap-test\" }, ({ input }) => ({\n doubled: (input as { n: number }).n * 2,\n }));\n\n const handle = await workflow.run({ n: 21 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ doubled: 42 });\n });\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function noopFn() {\n // no-op\n}\n"],"names":["randomUUID","afterEach","beforeEach","describe","expect","test","z","BackendPostgres","declareWorkflow","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","namespaceId","runMigrations","initialize","stop","client","workflow","defineWorkflow","name","noopFn","run","docUrl","workerId","claimed","claimWorkflowRun","leaseDurationMs","workflowName","toBe","input","toEqual","schema","object","userId","uuid","count","number","int","positive","handle","cancel","rejects","toThrow","value","not","toBeNull","Error","completeWorkflowRun","workflowRunId","id","output","ok","result","failWorkflowRun","error","message","rescheduled","getWorkflowRun","status","deadline","Date","now","deadlineAt","workflowRun","getTime","version","finishedAt","spec","runWorkflow","implementWorkflow","received","data","worker","newWorker","tick","sleep","specV1","specV2","spec1","spec2","email","doubled","n","ms","Promise","resolve","setTimeout"],"mappings":"AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,SAASC,SAAS,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,SAAS;AACvE,SAASC,CAAC,QAAQ,MAAM;AACxB,SAASC,eAAe,QAAQ,aAAI;AACpC,SAASC,eAAe,EAAEC,YAAY,QAAQ,cAAW;AACzD,SAASC,kBAAkB,QAAQ,0BAAuB;AAE1DP,SAAS,gBAAgB;IACvB,IAAIQ;IAEJT,WAAW;QACTS,UAAU,IAAIJ,gBAAgBG,oBAAoB;YAChDE,aAAaZ;YACba,eAAe;QACjB;QACA,MAAMF,QAAQG,UAAU;IAC1B;IAEAb,UAAU;QACR,MAAMU,QAAQI,IAAI;IACpB;IAEAV,KAAK,sCAAsC;QACzC,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAe,GAAGC;QACjE,MAAMH,SAASI,GAAG,CAAC;YAAEC,QAAQ;QAAsB;QAEnD,MAAMC,WAAW;QACjB,MAAMC,UAAU,MAAMb,QAAQc,gBAAgB,CAAC;YAC7CF;YACAG,iBAAiB;QACnB;QAEAtB,OAAOoB,SAASG,cAAcC,IAAI,CAAC;QACnCxB,OAAOoB,SAASD,UAAUK,IAAI,CAACL;QAC/BnB,OAAOoB,SAASK,OAAOC,OAAO,CAAC;YAAER,QAAQ;QAAsB;IACjE;IAEAnB,SAAS,qBAAqB;QAC5BA,SAAS,cAAc;YACrB,MAAM4B,SAASzB,EAAE0B,MAAM,CAAC;gBACtBC,QAAQ3B,EAAE4B,IAAI;gBACdC,OAAO7B,EAAE8B,MAAM,GAAGC,GAAG,GAAGC,QAAQ;YAClC;YAEAjC,KAAK,uBAAuB;gBAC1B,MAAMW,SAAS,IAAIP,aAAa;oBAAEE;gBAAQ;gBAC1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;oBAAEC,MAAM;oBAAoBY;gBAAO,GAAGX;gBAE7E,MAAMmB,SAAS,MAAMtB,SAASI,GAAG,CAAC;oBAChCY,QAAQjC;oBACRmC,OAAO;gBACT;gBAEA,MAAMI,OAAOC,MAAM;YACrB;YAEAnC,KAAK,yBAAyB;gBAC5B,MAAMW,SAAS,IAAIP,aAAa;oBAAEE;gBAAQ;gBAC1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;oBAAEC,MAAM;oBAAsBY;gBAAO,GAAGX;gBAE/E,MAAMhB,OAAOa,SAASI,GAAG,CAAC;oBAAEY,QAAQ;oBAAcE,OAAO;gBAAE,IAAaM,OAAO,CAACC,OAAO;YACzF;QACF;IACF;IAEArC,KAAK,0CAA0C;QAC7C,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAiB,GAAGC;QACnE,MAAMmB,SAAS,MAAMtB,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE;QAE7C,MAAMpB,WAAW;QACjB,MAAMC,UAAU,MAAMb,QAAQc,gBAAgB,CAAC;YAC7CF;YACAG,iBAAiB;QACnB;QACAtB,OAAOoB,SAASoB,GAAG,CAACC,QAAQ;QAC5B,IAAI,CAACrB,SAAS,MAAM,IAAIsB,MAAM;QAE9B,MAAMnC,QAAQoC,mBAAmB,CAAC;YAChCC,eAAexB,QAAQyB,EAAE;YACzB1B;YACA2B,QAAQ;gBAAEC,IAAI;YAAK;QACrB;QAEA,2EAA2E;QAC3E,MAAMC,SAAS,MAAMb,OAAOa,MAAM;QAClChD,OAAOgD,QAAQtB,OAAO,CAAC;YAAEqB,IAAI;QAAK;IACpC;IAEA9C,KAAK,sCAAsC;QACzC,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAiB,GAAGC;QACnE,MAAMH,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE;QAE9B,MAAMpB,WAAW;QACjB,MAAMC,UAAU,MAAMb,QAAQc,gBAAgB,CAAC;YAC7CF;YACAG,iBAAiB;QACnB;QACAtB,OAAOoB,SAASoB,GAAG,CAACC,QAAQ;QAC5B,IAAI,CAACrB,SAAS,MAAM,IAAIsB,MAAM;QAE9B,sCAAsC;QACtC,MAAMnC,QAAQ0C,eAAe,CAAC;YAC5BL,eAAexB,QAAQyB,EAAE;YACzB1B;YACA+B,OAAO;gBAAEC,SAAS;YAAO;QAC3B;QAEA,MAAMC,cAAc,MAAM7C,QAAQ8C,cAAc,CAAC;YAC/CT,eAAexB,QAAQyB,EAAE;QAC3B;QACA7C,OAAOoD,aAAaE,QAAQ9B,IAAI,CAAC;QACjCxB,OAAOoD,aAAaF,OAAOxB,OAAO,CAAC;YAAEyB,SAAS;QAAO;IACvD;IAEAlD,KAAK,sCAAsC;QACzC,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAgB,GAAGC;QAClE,MAAMuC,WAAW,IAAIC,KAAKA,KAAKC,GAAG,KAAK,SAAS,cAAc;QAC9D,MAAMtB,SAAS,MAAMtB,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE,GAAG;YAAEmB,YAAYH;QAAS;QAEvEvD,OAAOmC,OAAOwB,WAAW,CAACD,UAAU,EAAElB,GAAG,CAACC,QAAQ;QAClDzC,OAAOmC,OAAOwB,WAAW,CAACD,UAAU,EAAEE,WAAWpC,IAAI,CAAC+B,SAASK,OAAO;IACxE;IAEA3D,KAAK,qCAAqC;QACxC,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;YAAkB8C,SAAS;QAAO,GAAG7C;QACpF,MAAMmB,SAAS,MAAMtB,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE;QAE7CvC,OAAOmC,OAAOwB,WAAW,CAACE,OAAO,EAAErC,IAAI,CAAC;IAC1C;IAEAvB,KAAK,wCAAwC;QAC3C,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAmB,GAAGC;QACrE,MAAMmB,SAAS,MAAMtB,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE;QAE7CvC,OAAOmC,OAAOwB,WAAW,CAACE,OAAO,EAAEpB,QAAQ;IAC7C;IAEAxC,KAAK,mCAAmC;QACtC,MAAMW,SAAS,IAAIP,aAAa;YAAEE;QAAQ;QAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;YAAEC,MAAM;QAAc,GAAGC;QAChE,MAAMmB,SAAS,MAAMtB,SAASI,GAAG,CAAC;YAAEsB,OAAO;QAAE;QAE7C,MAAMJ,OAAOC,MAAM;QAEnB,MAAMuB,cAAc,MAAMpD,QAAQ8C,cAAc,CAAC;YAC/CT,eAAeT,OAAOwB,WAAW,CAACd,EAAE;QACtC;QACA7C,OAAO2D,aAAaL,QAAQ9B,IAAI,CAAC;QACjCxB,OAAO2D,aAAaG,YAAYtB,GAAG,CAACC,QAAQ;IAC9C;IAEA1C,SAAS,2CAA2C;QAClDE,KAAK,oEAAoE;YACvE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMwD,OAAO3D,gBAAgB;gBAAEW,MAAM;YAAe;YAEpD,MAAMoB,SAAS,MAAMvB,OAAOoD,WAAW,CAACD,MAAM;gBAAEZ,SAAS;YAAQ;YACjEnD,OAAOmC,OAAOwB,WAAW,CAACpC,YAAY,EAAEC,IAAI,CAAC;YAE7C,MAAMW,OAAOC,MAAM;QACrB;QAEAnC,KAAK,iEAAiE;YACpE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMwD,OAAO3D,gBAAgB;gBAAEW,MAAM;YAAiB;YACtDH,OAAOqD,iBAAiB,CAACF,MAAM,CAAC,EAAEtC,KAAK,EAAE;gBACvC,OAAO;oBAAEyC,UAAUzC;gBAAM;YAC3B;YAEA,MAAMU,SAAS,MAAMvB,OAAOoD,WAAW,CAACD,MAAM;gBAAEI,MAAM;YAAG;YACzD,MAAMC,SAASxD,OAAOyD,SAAS;YAC/B,MAAMD,OAAOE,IAAI;YACjB,MAAMC,MAAM,MAAM,gCAAgC;YAElD,MAAMvB,SAAS,MAAMb,OAAOa,MAAM;YAClChD,OAAOgD,QAAQtB,OAAO,CAAC;gBAAEwC,UAAU;oBAAEC,MAAM;gBAAG;YAAE;QAClD;QAEAlE,KAAK,gEAAgE;YACnE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMwD,OAAO3D,gBAAgB;gBAAEW,MAAM;YAAiB;YACtDH,OAAOqD,iBAAiB,CAACF,MAAM/C;YAE/BhB,OAAO;gBACLY,OAAOqD,iBAAiB,CAACF,MAAM/C;YACjC,GAAGsB,OAAO,CAAC;QACb;QAEArC,KAAK,gFAAgF;YACnF,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMiE,SAASpE,gBAAgB;gBAC7BW,MAAM;gBACN8C,SAAS;YACX;YACA,MAAMY,SAASrE,gBAAgB;gBAC7BW,MAAM;gBACN8C,SAAS;YACX;YAEA,iBAAiB;YACjBjD,OAAOqD,iBAAiB,CAACO,QAAQxD;YACjCJ,OAAOqD,iBAAiB,CAACQ,QAAQzD;QACnC;QAEAf,KAAK,8DAA8D;YACjE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMmE,QAAQtE,gBAAgB;gBAC5BW,MAAM;gBACN8C,SAAS;YACX;YACA,MAAMc,QAAQvE,gBAAgB;gBAC5BW,MAAM;gBACN8C,SAAS;YACX;YAEAjD,OAAOqD,iBAAiB,CAACS,OAAO1D;YAEhChB,OAAO;gBACLY,OAAOqD,iBAAiB,CAACU,OAAO3D;YAClC,GAAGsB,OAAO,CAAC;QACb;QAEArC,KAAK,8DAA8D;YACjE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMoB,SAASzB,EAAE0B,MAAM,CAAC;gBACtBgD,OAAO1E,EAAE0E,KAAK;YAChB;YACA,MAAMb,OAAO3D,gBAAgB;gBAC3BW,MAAM;gBACNY;YACF;YAEA,MAAMQ,SAAS,MAAMvB,OAAOoD,WAAW,CAACD,MAAM;gBAC5Ca,OAAO;YACT;YACA,MAAMzC,OAAOC,MAAM;YAEnB,MAAMpC,OAAOY,OAAOoD,WAAW,CAACD,MAAM;gBAAEa,OAAO;YAAe,IAAIvC,OAAO,CAACC,OAAO;QACnF;QAEArC,KAAK,6DAA6D;YAChE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMwD,OAAO3D,gBAAgB;gBAC3BW,MAAM;gBACN8C,SAAS;YACX;YAEA,MAAM1B,SAAS,MAAMvB,OAAOoD,WAAW,CAACD;YACxC/D,OAAOmC,OAAOwB,WAAW,CAACE,OAAO,EAAErC,IAAI,CAAC;YAExC,MAAMW,OAAOC,MAAM;QACrB;QAEAnC,KAAK,8DAA8D;YACjE,MAAMW,SAAS,IAAIP,aAAa;gBAAEE;YAAQ;YAE1C,MAAMM,WAAWD,OAAOE,cAAc,CAAC;gBAAEC,MAAM;YAAmB,GAAG,CAAC,EAAEU,KAAK,EAAE,GAAM,CAAA;oBACnFoD,SAAS,AAACpD,MAAwBqD,CAAC,GAAG;gBACxC,CAAA;YAEA,MAAM3C,SAAS,MAAMtB,SAASI,GAAG,CAAC;gBAAE6D,GAAG;YAAG;YAC1C,MAAMV,SAASxD,OAAOyD,SAAS;YAC/B,MAAMD,OAAOE,IAAI;YACjB,MAAMC,MAAM,MAAM,gCAAgC;YAElD,MAAMvB,SAAS,MAAMb,OAAOa,MAAM;YAClChD,OAAOgD,QAAQtB,OAAO,CAAC;gBAAEmD,SAAS;YAAG;QACvC;IACF;AACF;AAEA,SAASN,MAAMQ,EAAU;IACvB,OAAO,IAAIC,QAAQ,CAACC,UAAYC,WAAWD,SAASF;AACtD;AAEA,eAAe/D;AACb,QAAQ;AACV"}
@@ -13,19 +13,18 @@ interface BackendPostgresOptions {
13
13
  * Manages a connection to a Postgres database for workflow operations.
14
14
  */
15
15
  export declare class BackendPostgres implements Backend {
16
- private knex;
16
+ private config;
17
17
  private namespaceId;
18
18
  private usePubSub;
19
19
  private pubsub;
20
- private constructor();
20
+ private initialized;
21
+ private runMigrations;
22
+ private _knex;
23
+ private get knex();
24
+ constructor(config: Knex.Config, options?: BackendPostgresOptions);
25
+ initialize(): Promise<void>;
21
26
  subscribe(callback: OnSubscribed): Promise<void>;
22
27
  publish(payload?: string): Promise<void>;
23
- /**
24
- * Create and initialize a new BackendPostgres instance. This will
25
- * automatically run migrations on startup unless `runMigrations` is set to
26
- * false.
27
- */
28
- static connect(dbConf: Knex.Config, options?: BackendPostgresOptions): Promise<BackendPostgres>;
29
28
  stop(): Promise<void>;
30
29
  createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun>;
31
30
  getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/database/backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/database/backend.ts"],"names":[],"mappings":"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"}
@@ -9,16 +9,62 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
9
9
  /**
10
10
  * Manages a connection to a Postgres database for workflow operations.
11
11
  */ export class BackendPostgres {
12
- knex;
12
+ config;
13
13
  namespaceId;
14
14
  usePubSub;
15
15
  pubsub = null;
16
- constructor(knex, namespaceId, usePubSub){
17
- this.knex = knex;
16
+ initialized = false;
17
+ runMigrations;
18
+ _knex = null;
19
+ get knex() {
20
+ if (!this._knex) {
21
+ this._knex = knex(this.config);
22
+ }
23
+ return this._knex;
24
+ }
25
+ constructor(config, options){
26
+ this.config = {
27
+ ...config,
28
+ postProcessResponse: (result, _queryContext)=>{
29
+ if (result === null || result === undefined) {
30
+ return result;
31
+ }
32
+ if (config?.postProcessResponse) {
33
+ result = config.postProcessResponse(result, _queryContext);
34
+ }
35
+ const camelizeRow = (row)=>Object.fromEntries(Object.entries(row).map(([key, value])=>[
36
+ camelize(key, true),
37
+ value
38
+ ]));
39
+ if (Array.isArray(result)) {
40
+ return result.map(camelizeRow);
41
+ }
42
+ return camelizeRow(result);
43
+ }
44
+ };
45
+ const { namespaceId, usePubSub, runMigrations } = {
46
+ namespaceId: DEFAULT_NAMESPACE_ID,
47
+ usePubSub: true,
48
+ runMigrations: true,
49
+ ...options
50
+ };
18
51
  this.namespaceId = namespaceId;
19
52
  this.usePubSub = usePubSub;
53
+ this.runMigrations = runMigrations;
54
+ }
55
+ async initialize() {
56
+ if (this.initialized) {
57
+ return;
58
+ }
59
+ if (this.runMigrations) {
60
+ await migrate(this.config, DEFAULT_SCHEMA);
61
+ }
62
+ this.initialized = true;
20
63
  }
21
64
  async subscribe(callback) {
65
+ if (!this.initialized) {
66
+ throw new Error("Backend not initialized");
67
+ }
22
68
  if (!this.usePubSub) {
23
69
  return;
24
70
  }
@@ -28,53 +74,26 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
28
74
  this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);
29
75
  }
30
76
  async publish(payload) {
77
+ if (!this.initialized) {
78
+ throw new Error("Backend not initialized");
79
+ }
31
80
  if (!this.usePubSub) {
32
81
  return;
33
82
  }
34
83
  await this.knex.raw(payload ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'` : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`);
35
84
  }
36
- /**
37
- * Create and initialize a new BackendPostgres instance. This will
38
- * automatically run migrations on startup unless `runMigrations` is set to
39
- * false.
40
- */ static async connect(dbConf, options) {
41
- const postProcessResponse = (result, _queryContext)=>{
42
- if (result === null || result === undefined) {
43
- return result;
44
- }
45
- if (dbConf?.postProcessResponse) {
46
- result = dbConf.postProcessResponse(result, _queryContext);
47
- }
48
- const camelizeRow = (row)=>Object.fromEntries(Object.entries(row).map(([key, value])=>[
49
- camelize(key, true),
50
- value
51
- ]));
52
- if (Array.isArray(result)) {
53
- return result.map(camelizeRow);
54
- }
55
- return camelizeRow(result);
56
- };
57
- const { namespaceId, runMigrations, usePubSub } = {
58
- namespaceId: DEFAULT_NAMESPACE_ID,
59
- runMigrations: true,
60
- usePubSub: true,
61
- ...options
62
- };
63
- const knexInstance = knex({
64
- ...dbConf,
65
- postProcessResponse
66
- });
67
- if (runMigrations) {
68
- await migrate(knexInstance, DEFAULT_SCHEMA);
69
- }
70
- return new BackendPostgres(knexInstance, namespaceId, usePubSub);
71
- }
72
85
  async stop() {
86
+ if (!this.initialized) {
87
+ return;
88
+ }
73
89
  await this.pubsub?.destroy();
74
90
  this.pubsub = null;
75
91
  await this.knex.destroy();
76
92
  }
77
93
  async createWorkflowRun(params) {
94
+ if (!this.initialized) {
95
+ throw new Error("Backend not initialized");
96
+ }
78
97
  const qb = this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").insert({
79
98
  namespace_id: this.namespaceId,
80
99
  id: crypto.randomUUID(),
@@ -98,10 +117,16 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
98
117
  return workflowRun[0];
99
118
  }
100
119
  async getWorkflowRun(params) {
120
+ if (!this.initialized) {
121
+ throw new Error("Backend not initialized");
122
+ }
101
123
  const workflowRun = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).select("namespace_id", "id", "workflow_name", "version", "status", "idempotency_key", "config", "context", "input", "output", "error", "attempts", "parent_step_attempt_namespace_id", "parent_step_attempt_id", "worker_id", "available_at", "deadline_at", "started_at", "finished_at", "created_at", "updated_at").first();
102
124
  return workflowRun ?? null;
103
125
  }
104
126
  async listWorkflowRuns(params) {
127
+ if (!this.initialized) {
128
+ throw new Error("Backend not initialized");
129
+ }
105
130
  const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
106
131
  const { after, before } = params;
107
132
  let cursor = null;
@@ -127,6 +152,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
127
152
  return qb;
128
153
  }
129
154
  async claimWorkflowRun(params) {
155
+ if (!this.initialized) {
156
+ throw new Error("Backend not initialized");
157
+ }
130
158
  const claimed = await this.knex.with("expired", (qb)=>qb.withSchema(DEFAULT_SCHEMA).table("workflow_runs").update({
131
159
  status: "failed",
132
160
  error: JSON.stringify({
@@ -157,6 +185,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
157
185
  return claimed[0] ?? null;
158
186
  }
159
187
  async extendWorkflowRunLease(params) {
188
+ if (!this.initialized) {
189
+ throw new Error("Backend not initialized");
190
+ }
160
191
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).where("status", "running").where("worker_id", params.workerId).update({
161
192
  available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),
162
193
  updated_at: this.knex.fn.now()
@@ -167,6 +198,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
167
198
  return updated;
168
199
  }
169
200
  async sleepWorkflowRun(params) {
201
+ if (!this.initialized) {
202
+ throw new Error("Backend not initialized");
203
+ }
170
204
  // 'succeeded' status is deprecated
171
205
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).whereNotIn("status", [
172
206
  "succeeded",
@@ -185,6 +219,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
185
219
  return updated;
186
220
  }
187
221
  async completeWorkflowRun(params) {
222
+ if (!this.initialized) {
223
+ throw new Error("Backend not initialized");
224
+ }
188
225
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).where("status", "running").where("worker_id", params.workerId).update({
189
226
  status: "completed",
190
227
  output: JSON.stringify(params.output),
@@ -200,6 +237,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
200
237
  return updated;
201
238
  }
202
239
  async failWorkflowRun(params) {
240
+ if (!this.initialized) {
241
+ throw new Error("Backend not initialized");
242
+ }
203
243
  const { workflowRunId, error } = params;
204
244
  const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;
205
245
  // this beefy query updates a workflow's status, available_at, and
@@ -225,6 +265,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
225
265
  return updated;
226
266
  }
227
267
  async cancelWorkflowRun(params) {
268
+ if (!this.initialized) {
269
+ throw new Error("Backend not initialized");
270
+ }
228
271
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).whereIn("status", [
229
272
  "pending",
230
273
  "running",
@@ -262,6 +305,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
262
305
  return updated;
263
306
  }
264
307
  async createStepAttempt(params) {
308
+ if (!this.initialized) {
309
+ throw new Error("Backend not initialized");
310
+ }
265
311
  const [stepAttempt] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts").insert({
266
312
  namespace_id: this.namespaceId,
267
313
  id: crypto.randomUUID(),
@@ -281,10 +327,16 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
281
327
  return stepAttempt;
282
328
  }
283
329
  async getStepAttempt(params) {
330
+ if (!this.initialized) {
331
+ throw new Error("Backend not initialized");
332
+ }
284
333
  const stepAttempt = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts").where("namespace_id", this.namespaceId).where("id", params.stepAttemptId).first();
285
334
  return stepAttempt ?? null;
286
335
  }
287
336
  async listStepAttempts(params) {
337
+ if (!this.initialized) {
338
+ throw new Error("Backend not initialized");
339
+ }
288
340
  const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
289
341
  const { after, before } = params;
290
342
  let cursor = null;
@@ -342,6 +394,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
342
394
  };
343
395
  }
344
396
  async completeStepAttempt(params) {
397
+ if (!this.initialized) {
398
+ throw new Error("Backend not initialized");
399
+ }
345
400
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts as sa").update({
346
401
  status: "completed",
347
402
  output: JSON.stringify(params.output),
@@ -355,6 +410,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
355
410
  return updated;
356
411
  }
357
412
  async failStepAttempt(params) {
413
+ if (!this.initialized) {
414
+ throw new Error("Backend not initialized");
415
+ }
358
416
  const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts as sa").update({
359
417
  status: "failed",
360
418
  output: null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/database/backend.ts"],"sourcesContent":["import { camelize } from \"inflection\";\nimport knex, { type Knex } from \"knex\";\nimport {\n type Backend,\n type CancelWorkflowRunParams,\n type ClaimWorkflowRunParams,\n type CompleteStepAttemptParams,\n type CompleteWorkflowRunParams,\n type CreateStepAttemptParams,\n type CreateWorkflowRunParams,\n DEFAULT_NAMESPACE_ID,\n type ExtendWorkflowRunLeaseParams,\n type FailStepAttemptParams,\n type FailWorkflowRunParams,\n type GetStepAttemptParams,\n type GetWorkflowRunParams,\n type ListStepAttemptsParams,\n type ListWorkflowRunsParams,\n type PaginatedResponse,\n type SleepWorkflowRunParams,\n} from \"../backend\";\nimport { DEFAULT_RETRY_POLICY } from \"../core/retry\";\nimport type { StepAttempt } from \"../core/step\";\nimport type { WorkflowRun } from \"../core/workflow\";\nimport { DEFAULT_SCHEMA, migrate } from \"./base\";\nimport { type OnSubscribed, PostgresPubSub } from \"./pubsub\";\n\nexport const DEFAULT_LISTEN_CHANNEL = \"new_tasks\" as const;\nconst DEFAULT_PAGINATION_PAGE_SIZE = 100 as const;\n\ninterface BackendPostgresOptions {\n namespaceId?: string;\n runMigrations?: boolean;\n\n // default: true\n usePubSub?: boolean;\n}\n\n/**\n * Manages a connection to a Postgres database for workflow operations.\n */\nexport class BackendPostgres implements Backend {\n private knex: Knex;\n private namespaceId: string;\n private usePubSub: boolean;\n private pubsub: PostgresPubSub | null = null;\n\n private constructor(knex: Knex, namespaceId: string, usePubSub: boolean) {\n this.knex = knex;\n this.namespaceId = namespaceId;\n this.usePubSub = usePubSub;\n }\n\n async subscribe(callback: OnSubscribed) {\n if (!this.usePubSub) {\n return;\n }\n\n if (!this.pubsub) {\n this.pubsub = await PostgresPubSub.create(this.knex);\n }\n\n this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);\n }\n\n async publish(payload?: string): Promise<void> {\n if (!this.usePubSub) {\n return;\n }\n\n await this.knex.raw(\n payload\n ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'`\n : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`,\n );\n }\n\n /**\n * Create and initialize a new BackendPostgres instance. This will\n * automatically run migrations on startup unless `runMigrations` is set to\n * false.\n */\n static async connect(\n dbConf: Knex.Config,\n options?: BackendPostgresOptions,\n ): Promise<BackendPostgres> {\n const postProcessResponse: Knex.Config[\"postProcessResponse\"] = (result, _queryContext) => {\n if (result === null || result === undefined) {\n return result;\n }\n\n if (dbConf?.postProcessResponse) {\n result = dbConf.postProcessResponse(result, _queryContext);\n }\n\n const camelizeRow = (row: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(row).map(([key, value]) => [camelize(key, true), value]));\n\n if (Array.isArray(result)) {\n return result.map(camelizeRow);\n }\n\n return camelizeRow(result);\n };\n\n const { namespaceId, runMigrations, usePubSub } = {\n namespaceId: DEFAULT_NAMESPACE_ID,\n runMigrations: true,\n usePubSub: true,\n ...options,\n };\n\n const knexInstance = knex({ ...dbConf, postProcessResponse });\n if (runMigrations) {\n await migrate(knexInstance, DEFAULT_SCHEMA);\n }\n\n return new BackendPostgres(knexInstance, namespaceId, usePubSub);\n }\n\n async stop(): Promise<void> {\n await this.pubsub?.destroy();\n this.pubsub = null;\n await this.knex.destroy();\n }\n\n async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_name: params.workflowName,\n version: params.version,\n status: \"pending\",\n idempotency_key: params.idempotencyKey,\n config: params.config,\n context: params.context,\n input: params.input,\n attempts: 0,\n available_at: params.availableAt ?? this.knex.fn.now(),\n deadline_at: params.deadlineAt,\n created_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n const workflowRun = await qb;\n if (!workflowRun[0]) {\n throw new Error(\"Failed to create workflow run\");\n }\n\n return workflowRun[0];\n }\n\n async getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null> {\n const workflowRun = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .select(\n \"namespace_id\",\n \"id\",\n \"workflow_name\",\n \"version\",\n \"status\",\n \"idempotency_key\",\n \"config\",\n \"context\",\n \"input\",\n \"output\",\n \"error\",\n \"attempts\",\n \"parent_step_attempt_namespace_id\",\n \"parent_step_attempt_id\",\n \"worker_id\",\n \"available_at\",\n \"deadline_at\",\n \"started_at\",\n \"finished_at\",\n \"created_at\",\n \"updated_at\",\n )\n .first();\n\n return workflowRun ?? null;\n }\n\n async listWorkflowRuns(params: ListWorkflowRunsParams): Promise<PaginatedResponse<WorkflowRun>> {\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListWorkflowRunsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListWorkflowRunsWhere(params: ListWorkflowRunsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n async claimWorkflowRun(params: ClaimWorkflowRunParams): Promise<WorkflowRun | null> {\n const claimed = await this.knex\n .with(\"expired\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .update({\n status: \"failed\",\n error: JSON.stringify({ message: \"Workflow run deadline exceeded\" }),\n worker_id: null,\n available_at: null,\n finished_at: this.knex.raw(\"NOW()\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .whereNotNull(\"deadline_at\")\n .where(\"deadline_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .returning(\"id\"),\n )\n .with(\"candidate\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .select(\"id\")\n .from(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .where(\"available_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .where((qb2) => {\n qb2.whereNull(\"deadline_at\").orWhere(\"deadline_at\", \">\", this.knex.raw(\"NOW()\"));\n })\n .orderByRaw(\"CASE WHEN status = 'pending' THEN 0 ELSE 1 END\")\n .orderBy(\"available_at\", \"asc\")\n .orderBy(\"created_at\", \"asc\")\n .limit(1)\n .forUpdate()\n .skipLocked(),\n )\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs as wr\")\n .where(\"wr.namespace_id\", this.namespaceId)\n .where(\"wr.id\", this.knex.ref(\"candidate.id\"))\n .update({\n status: \"running\",\n attempts: this.knex.raw(\"wr.attempts + 1\"),\n worker_id: params.workerId,\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n started_at: this.knex.raw(\"COALESCE(wr.started_at, NOW())\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .updateFrom(\"candidate\")\n .returning(\"wr.*\");\n\n return claimed[0] ?? null;\n }\n\n async extendWorkflowRunLease(params: ExtendWorkflowRunLeaseParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to extend lease for workflow run\");\n }\n\n return updated;\n }\n\n async sleepWorkflowRun(params: SleepWorkflowRunParams): Promise<WorkflowRun> {\n // 'succeeded' status is deprecated\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereNotIn(\"status\", [\"succeeded\", \"completed\", \"failed\", \"canceled\"])\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"sleeping\",\n available_at: params.availableAt,\n worker_id: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to sleep workflow run\");\n }\n\n return updated;\n }\n\n async completeWorkflowRun(params: CompleteWorkflowRunParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n worker_id: params.workerId,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to complete workflow run\");\n }\n\n return updated;\n }\n\n async failWorkflowRun(params: FailWorkflowRunParams): Promise<WorkflowRun> {\n const { workflowRunId, error } = params;\n const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;\n\n // this beefy query updates a workflow's status, available_at, and\n // finished_at based on the workflow's deadline and retry policy\n //\n // if the next retry would exceed the deadline, the run is marked as\n // 'failed' and finalized, otherwise, the run is rescheduled with an updated\n // 'available_at' timestamp for the next retry\n const retryIntervalExpr = `LEAST(${initialIntervalMs} * POWER(${backoffCoefficient}, \"attempts\" - 1), ${maximumIntervalMs}) * INTERVAL '1 millisecond'`;\n const deadlineExceededCondition = `\"deadline_at\" IS NOT NULL AND NOW() + (${retryIntervalExpr}) >= \"deadline_at\"`;\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN 'failed' ELSE 'pending' END`,\n ),\n available_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NULL ELSE NOW() + (${retryIntervalExpr}) END`,\n ),\n finished_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NOW() ELSE NULL END`,\n ),\n error: JSON.stringify(error),\n worker_id: null,\n started_at: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark workflow run failed\");\n }\n\n return updated;\n }\n\n async cancelWorkflowRun(params: CancelWorkflowRunParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .update({\n status: \"canceled\",\n worker_id: null,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n // workflow may already be in a terminal state\n const existing = await this.getWorkflowRun({\n workflowRunId: params.workflowRunId,\n });\n if (!existing) {\n throw new Error(`Workflow run ${params.workflowRunId} does not exist`);\n }\n\n // if already canceled, just return it\n if (existing.status === \"canceled\") {\n return existing;\n }\n\n // throw error for completed/failed workflows\n // 'succeeded' status is deprecated\n if ([\"succeeded\", \"completed\", \"failed\"].includes(existing.status)) {\n throw new Error(\n `Cannot cancel workflow run ${params.workflowRunId} with status ${existing.status}`,\n );\n }\n\n throw new Error(\"Failed to cancel workflow run\");\n }\n\n return updated;\n }\n\n async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {\n const [stepAttempt] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_run_id: params.workflowRunId,\n step_name: params.stepName,\n kind: params.kind,\n status: \"running\",\n config: JSON.stringify(params.config),\n context: JSON.stringify(params.context),\n started_at: this.knex.fn.now(),\n created_at: this.knex.raw(\"date_trunc('milliseconds', NOW())\"),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!stepAttempt) {\n throw new Error(\"Failed to create step attempt\");\n }\n\n return stepAttempt;\n }\n\n async getStepAttempt(params: GetStepAttemptParams): Promise<StepAttempt | null> {\n const stepAttempt = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.stepAttemptId)\n .first();\n\n return stepAttempt ?? null;\n }\n\n async listStepAttempts(params: ListStepAttemptsParams): Promise<PaginatedResponse<StepAttempt>> {\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListStepAttemptsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListStepAttemptsWhere(params: ListStepAttemptsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"workflow_run_id\", params.workflowRunId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n private processPaginationResults<T extends Cursor>(\n rows: T[],\n limit: number,\n hasAfter: boolean,\n hasBefore: boolean,\n ): PaginatedResponse<T> {\n const data = rows;\n let hasNext = false;\n let hasPrev = false;\n\n if (hasBefore) {\n data.reverse();\n if (data.length > limit) {\n hasPrev = true;\n data.shift();\n }\n hasNext = true;\n } else {\n if (data.length > limit) {\n hasNext = true;\n data.pop();\n }\n if (hasAfter) {\n hasPrev = true;\n }\n }\n\n const lastItem = data.at(-1);\n const nextCursor = hasNext && lastItem ? encodeCursor(lastItem) : null;\n const firstItem = data[0];\n const prevCursor = hasPrev && firstItem ? encodeCursor(firstItem) : null;\n\n return {\n data,\n pagination: {\n next: nextCursor,\n prev: prevCursor,\n },\n };\n }\n\n async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt completed\");\n }\n\n return updated;\n }\n\n async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"failed\",\n output: null,\n error: JSON.stringify(params.error),\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt failed\");\n }\n\n return updated;\n }\n}\n\n/**\n * Cursor used for pagination. Requires created_at and id fields. Because JS\n * Date does not natively support microsecond precision dates, created_at should\n * be stored with millisecond precision in paginated tables to avoid issues with\n * cursor comparisons.\n */\ninterface Cursor {\n createdAt: Date;\n id: string;\n}\n\nfunction encodeCursor(item: Cursor): string {\n const encoded = Buffer.from(\n JSON.stringify({ createdAt: item.createdAt.toISOString(), id: item.id }),\n ).toString(\"base64\");\n return encoded;\n}\n\nexport function decodeCursor(cursor: string): Cursor {\n const decoded = Buffer.from(cursor, \"base64\").toString(\"utf8\");\n const parsed = JSON.parse(decoded) as { createdAt: string; id: string };\n return {\n createdAt: new Date(parsed.createdAt),\n id: parsed.id,\n };\n}\n"],"names":["camelize","knex","DEFAULT_NAMESPACE_ID","DEFAULT_RETRY_POLICY","DEFAULT_SCHEMA","migrate","PostgresPubSub","DEFAULT_LISTEN_CHANNEL","DEFAULT_PAGINATION_PAGE_SIZE","BackendPostgres","namespaceId","usePubSub","pubsub","subscribe","callback","create","listenEvent","publish","payload","raw","connect","dbConf","options","postProcessResponse","result","_queryContext","undefined","camelizeRow","row","Object","fromEntries","entries","map","key","value","Array","isArray","runMigrations","knexInstance","stop","destroy","createWorkflowRun","params","qb","withSchema","table","insert","namespace_id","id","crypto","randomUUID","workflow_name","workflowName","version","status","idempotency_key","idempotencyKey","config","context","input","attempts","available_at","availableAt","fn","now","deadline_at","deadlineAt","created_at","updated_at","returning","workflowRun","Error","getWorkflowRun","where","workflowRunId","select","first","listWorkflowRuns","limit","after","before","cursor","decodeCursor","buildListWorkflowRunsWhere","rows","orderBy","processPaginationResults","operator","whereRaw","createdAt","toISOString","claimWorkflowRun","claimed","with","update","error","JSON","stringify","message","worker_id","finished_at","whereIn","whereNotNull","from","qb2","whereNull","orWhere","orderByRaw","forUpdate","skipLocked","ref","workerId","leaseDurationMs","started_at","updateFrom","extendWorkflowRunLease","updated","sleepWorkflowRun","whereNotIn","completeWorkflowRun","output","failWorkflowRun","initialIntervalMs","backoffCoefficient","maximumIntervalMs","retryIntervalExpr","deadlineExceededCondition","cancelWorkflowRun","existing","includes","createStepAttempt","stepAttempt","workflow_run_id","step_name","stepName","kind","getStepAttempt","stepAttemptId","listStepAttempts","buildListStepAttemptsWhere","hasAfter","hasBefore","data","hasNext","hasPrev","reverse","length","shift","pop","lastItem","at","nextCursor","encodeCursor","firstItem","prevCursor","pagination","next","prev","completeStepAttempt","failStepAttempt","item","encoded","Buffer","toString","decoded","parsed","parse","Date"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/database/backend.ts"],"sourcesContent":["import { camelize } from \"inflection\";\nimport knex, { type Knex } from \"knex\";\nimport {\n type Backend,\n type CancelWorkflowRunParams,\n type ClaimWorkflowRunParams,\n type CompleteStepAttemptParams,\n type CompleteWorkflowRunParams,\n type CreateStepAttemptParams,\n type CreateWorkflowRunParams,\n DEFAULT_NAMESPACE_ID,\n type ExtendWorkflowRunLeaseParams,\n type FailStepAttemptParams,\n type FailWorkflowRunParams,\n type GetStepAttemptParams,\n type GetWorkflowRunParams,\n type ListStepAttemptsParams,\n type ListWorkflowRunsParams,\n type PaginatedResponse,\n type SleepWorkflowRunParams,\n} from \"../backend\";\nimport { DEFAULT_RETRY_POLICY } from \"../core/retry\";\nimport type { StepAttempt } from \"../core/step\";\nimport type { WorkflowRun } from \"../core/workflow\";\nimport { DEFAULT_SCHEMA, migrate } from \"./base\";\nimport { type OnSubscribed, PostgresPubSub } from \"./pubsub\";\n\nexport const DEFAULT_LISTEN_CHANNEL = \"new_tasks\" as const;\nconst DEFAULT_PAGINATION_PAGE_SIZE = 100 as const;\n\ninterface BackendPostgresOptions {\n namespaceId?: string;\n runMigrations?: boolean;\n\n // default: true\n usePubSub?: boolean;\n}\n\n/**\n * Manages a connection to a Postgres database for workflow operations.\n */\nexport class BackendPostgres implements Backend {\n private config: Knex.Config;\n private namespaceId: string;\n private usePubSub: boolean;\n private pubsub: PostgresPubSub | null = null;\n private initialized: boolean = false;\n private runMigrations: boolean;\n\n private _knex: Knex | null = null;\n private get knex(): Knex {\n if (!this._knex) {\n this._knex = knex(this.config);\n }\n\n return this._knex;\n }\n\n constructor(config: Knex.Config, options?: BackendPostgresOptions) {\n this.config = {\n ...config,\n postProcessResponse: (result, _queryContext) => {\n if (result === null || result === undefined) {\n return result;\n }\n\n if (config?.postProcessResponse) {\n result = config.postProcessResponse(result, _queryContext);\n }\n\n const camelizeRow = (row: Record<string, unknown>) =>\n Object.fromEntries(\n Object.entries(row).map(([key, value]) => [camelize(key, true), value]),\n );\n\n if (Array.isArray(result)) {\n return result.map(camelizeRow);\n }\n\n return camelizeRow(result);\n },\n };\n\n const { namespaceId, usePubSub, runMigrations } = {\n namespaceId: DEFAULT_NAMESPACE_ID,\n usePubSub: true,\n runMigrations: true,\n ...options,\n };\n\n this.namespaceId = namespaceId;\n this.usePubSub = usePubSub;\n this.runMigrations = runMigrations;\n }\n\n async initialize() {\n if (this.initialized) {\n return;\n }\n\n if (this.runMigrations) {\n await migrate(this.config, DEFAULT_SCHEMA);\n }\n\n this.initialized = true;\n }\n\n async subscribe(callback: OnSubscribed) {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n if (!this.usePubSub) {\n return;\n }\n\n if (!this.pubsub) {\n this.pubsub = await PostgresPubSub.create(this.knex);\n }\n\n this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);\n }\n\n async publish(payload?: string): Promise<void> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n if (!this.usePubSub) {\n return;\n }\n\n await this.knex.raw(\n payload\n ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'`\n : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`,\n );\n }\n\n async stop(): Promise<void> {\n if (!this.initialized) {\n return;\n }\n\n await this.pubsub?.destroy();\n this.pubsub = null;\n await this.knex.destroy();\n }\n\n async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_name: params.workflowName,\n version: params.version,\n status: \"pending\",\n idempotency_key: params.idempotencyKey,\n config: params.config,\n context: params.context,\n input: params.input,\n attempts: 0,\n available_at: params.availableAt ?? this.knex.fn.now(),\n deadline_at: params.deadlineAt,\n created_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n const workflowRun = await qb;\n if (!workflowRun[0]) {\n throw new Error(\"Failed to create workflow run\");\n }\n\n return workflowRun[0];\n }\n\n async getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const workflowRun = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .select(\n \"namespace_id\",\n \"id\",\n \"workflow_name\",\n \"version\",\n \"status\",\n \"idempotency_key\",\n \"config\",\n \"context\",\n \"input\",\n \"output\",\n \"error\",\n \"attempts\",\n \"parent_step_attempt_namespace_id\",\n \"parent_step_attempt_id\",\n \"worker_id\",\n \"available_at\",\n \"deadline_at\",\n \"started_at\",\n \"finished_at\",\n \"created_at\",\n \"updated_at\",\n )\n .first();\n\n return workflowRun ?? null;\n }\n\n async listWorkflowRuns(params: ListWorkflowRunsParams): Promise<PaginatedResponse<WorkflowRun>> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListWorkflowRunsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListWorkflowRunsWhere(params: ListWorkflowRunsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n async claimWorkflowRun(params: ClaimWorkflowRunParams): Promise<WorkflowRun | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const claimed = await this.knex\n .with(\"expired\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .update({\n status: \"failed\",\n error: JSON.stringify({ message: \"Workflow run deadline exceeded\" }),\n worker_id: null,\n available_at: null,\n finished_at: this.knex.raw(\"NOW()\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .whereNotNull(\"deadline_at\")\n .where(\"deadline_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .returning(\"id\"),\n )\n .with(\"candidate\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .select(\"id\")\n .from(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .where(\"available_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .where((qb2) => {\n qb2.whereNull(\"deadline_at\").orWhere(\"deadline_at\", \">\", this.knex.raw(\"NOW()\"));\n })\n .orderByRaw(\"CASE WHEN status = 'pending' THEN 0 ELSE 1 END\")\n .orderBy(\"available_at\", \"asc\")\n .orderBy(\"created_at\", \"asc\")\n .limit(1)\n .forUpdate()\n .skipLocked(),\n )\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs as wr\")\n .where(\"wr.namespace_id\", this.namespaceId)\n .where(\"wr.id\", this.knex.ref(\"candidate.id\"))\n .update({\n status: \"running\",\n attempts: this.knex.raw(\"wr.attempts + 1\"),\n worker_id: params.workerId,\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n started_at: this.knex.raw(\"COALESCE(wr.started_at, NOW())\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .updateFrom(\"candidate\")\n .returning(\"wr.*\");\n\n return claimed[0] ?? null;\n }\n\n async extendWorkflowRunLease(params: ExtendWorkflowRunLeaseParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to extend lease for workflow run\");\n }\n\n return updated;\n }\n\n async sleepWorkflowRun(params: SleepWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n // 'succeeded' status is deprecated\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereNotIn(\"status\", [\"succeeded\", \"completed\", \"failed\", \"canceled\"])\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"sleeping\",\n available_at: params.availableAt,\n worker_id: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to sleep workflow run\");\n }\n\n return updated;\n }\n\n async completeWorkflowRun(params: CompleteWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n worker_id: params.workerId,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to complete workflow run\");\n }\n\n return updated;\n }\n\n async failWorkflowRun(params: FailWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const { workflowRunId, error } = params;\n const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;\n\n // this beefy query updates a workflow's status, available_at, and\n // finished_at based on the workflow's deadline and retry policy\n //\n // if the next retry would exceed the deadline, the run is marked as\n // 'failed' and finalized, otherwise, the run is rescheduled with an updated\n // 'available_at' timestamp for the next retry\n const retryIntervalExpr = `LEAST(${initialIntervalMs} * POWER(${backoffCoefficient}, \"attempts\" - 1), ${maximumIntervalMs}) * INTERVAL '1 millisecond'`;\n const deadlineExceededCondition = `\"deadline_at\" IS NOT NULL AND NOW() + (${retryIntervalExpr}) >= \"deadline_at\"`;\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN 'failed' ELSE 'pending' END`,\n ),\n available_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NULL ELSE NOW() + (${retryIntervalExpr}) END`,\n ),\n finished_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NOW() ELSE NULL END`,\n ),\n error: JSON.stringify(error),\n worker_id: null,\n started_at: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark workflow run failed\");\n }\n\n return updated;\n }\n\n async cancelWorkflowRun(params: CancelWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .update({\n status: \"canceled\",\n worker_id: null,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n // workflow may already be in a terminal state\n const existing = await this.getWorkflowRun({\n workflowRunId: params.workflowRunId,\n });\n if (!existing) {\n throw new Error(`Workflow run ${params.workflowRunId} does not exist`);\n }\n\n // if already canceled, just return it\n if (existing.status === \"canceled\") {\n return existing;\n }\n\n // throw error for completed/failed workflows\n // 'succeeded' status is deprecated\n if ([\"succeeded\", \"completed\", \"failed\"].includes(existing.status)) {\n throw new Error(\n `Cannot cancel workflow run ${params.workflowRunId} with status ${existing.status}`,\n );\n }\n\n throw new Error(\"Failed to cancel workflow run\");\n }\n\n return updated;\n }\n\n async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [stepAttempt] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_run_id: params.workflowRunId,\n step_name: params.stepName,\n kind: params.kind,\n status: \"running\",\n config: JSON.stringify(params.config),\n context: JSON.stringify(params.context),\n started_at: this.knex.fn.now(),\n created_at: this.knex.raw(\"date_trunc('milliseconds', NOW())\"),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!stepAttempt) {\n throw new Error(\"Failed to create step attempt\");\n }\n\n return stepAttempt;\n }\n\n async getStepAttempt(params: GetStepAttemptParams): Promise<StepAttempt | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const stepAttempt = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.stepAttemptId)\n .first();\n\n return stepAttempt ?? null;\n }\n\n async listStepAttempts(params: ListStepAttemptsParams): Promise<PaginatedResponse<StepAttempt>> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListStepAttemptsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListStepAttemptsWhere(params: ListStepAttemptsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"workflow_run_id\", params.workflowRunId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n private processPaginationResults<T extends Cursor>(\n rows: T[],\n limit: number,\n hasAfter: boolean,\n hasBefore: boolean,\n ): PaginatedResponse<T> {\n const data = rows;\n let hasNext = false;\n let hasPrev = false;\n\n if (hasBefore) {\n data.reverse();\n if (data.length > limit) {\n hasPrev = true;\n data.shift();\n }\n hasNext = true;\n } else {\n if (data.length > limit) {\n hasNext = true;\n data.pop();\n }\n if (hasAfter) {\n hasPrev = true;\n }\n }\n\n const lastItem = data.at(-1);\n const nextCursor = hasNext && lastItem ? encodeCursor(lastItem) : null;\n const firstItem = data[0];\n const prevCursor = hasPrev && firstItem ? encodeCursor(firstItem) : null;\n\n return {\n data,\n pagination: {\n next: nextCursor,\n prev: prevCursor,\n },\n };\n }\n\n async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt completed\");\n }\n\n return updated;\n }\n\n async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"failed\",\n output: null,\n error: JSON.stringify(params.error),\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt failed\");\n }\n\n return updated;\n }\n}\n\n/**\n * Cursor used for pagination. Requires created_at and id fields. Because JS\n * Date does not natively support microsecond precision dates, created_at should\n * be stored with millisecond precision in paginated tables to avoid issues with\n * cursor comparisons.\n */\ninterface Cursor {\n createdAt: Date;\n id: string;\n}\n\nfunction encodeCursor(item: Cursor): string {\n const encoded = Buffer.from(\n JSON.stringify({ createdAt: item.createdAt.toISOString(), id: item.id }),\n ).toString(\"base64\");\n return encoded;\n}\n\nexport function decodeCursor(cursor: string): Cursor {\n const decoded = Buffer.from(cursor, \"base64\").toString(\"utf8\");\n const parsed = JSON.parse(decoded) as { createdAt: string; id: string };\n return {\n createdAt: new Date(parsed.createdAt),\n id: parsed.id,\n };\n}\n"],"names":["camelize","knex","DEFAULT_NAMESPACE_ID","DEFAULT_RETRY_POLICY","DEFAULT_SCHEMA","migrate","PostgresPubSub","DEFAULT_LISTEN_CHANNEL","DEFAULT_PAGINATION_PAGE_SIZE","BackendPostgres","config","namespaceId","usePubSub","pubsub","initialized","runMigrations","_knex","options","postProcessResponse","result","_queryContext","undefined","camelizeRow","row","Object","fromEntries","entries","map","key","value","Array","isArray","initialize","subscribe","callback","Error","create","listenEvent","publish","payload","raw","stop","destroy","createWorkflowRun","params","qb","withSchema","table","insert","namespace_id","id","crypto","randomUUID","workflow_name","workflowName","version","status","idempotency_key","idempotencyKey","context","input","attempts","available_at","availableAt","fn","now","deadline_at","deadlineAt","created_at","updated_at","returning","workflowRun","getWorkflowRun","where","workflowRunId","select","first","listWorkflowRuns","limit","after","before","cursor","decodeCursor","buildListWorkflowRunsWhere","rows","orderBy","processPaginationResults","operator","whereRaw","createdAt","toISOString","claimWorkflowRun","claimed","with","update","error","JSON","stringify","message","worker_id","finished_at","whereIn","whereNotNull","from","qb2","whereNull","orWhere","orderByRaw","forUpdate","skipLocked","ref","workerId","leaseDurationMs","started_at","updateFrom","extendWorkflowRunLease","updated","sleepWorkflowRun","whereNotIn","completeWorkflowRun","output","failWorkflowRun","initialIntervalMs","backoffCoefficient","maximumIntervalMs","retryIntervalExpr","deadlineExceededCondition","cancelWorkflowRun","existing","includes","createStepAttempt","stepAttempt","workflow_run_id","step_name","stepName","kind","getStepAttempt","stepAttemptId","listStepAttempts","buildListStepAttemptsWhere","hasAfter","hasBefore","data","hasNext","hasPrev","reverse","length","shift","pop","lastItem","at","nextCursor","encodeCursor","firstItem","prevCursor","pagination","next","prev","completeStepAttempt","failStepAttempt","item","encoded","Buffer","toString","decoded","parsed","parse","Date"],"mappings":"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"}
@@ -4,10 +4,12 @@ import { BackendPostgres } from "./backend.js";
4
4
  import { testBackend } from "./backend.testsuite.js";
5
5
  testBackend({
6
6
  setup: async ()=>{
7
- return await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {
7
+ const backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
8
8
  namespaceId: randomUUID(),
9
9
  runMigrations: false
10
10
  });
11
+ await backend.initialize();
12
+ return backend;
11
13
  },
12
14
  teardown: async (backend)=>{
13
15
  await backend.stop();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/database/backend.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { KNEX_GLOBAL_CONFIG } from \"../testing/connection\";\nimport { BackendPostgres } from \"./backend\";\nimport { testBackend } from \"./backend.testsuite\";\n\ntestBackend({\n setup: async () => {\n return await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n },\n teardown: async (backend) => {\n await (backend as BackendPostgres).stop();\n },\n});\n"],"names":["randomUUID","KNEX_GLOBAL_CONFIG","BackendPostgres","testBackend","setup","connect","namespaceId","runMigrations","teardown","backend","stop"],"mappings":"AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,SAASC,kBAAkB,QAAQ,2BAAwB;AAC3D,SAASC,eAAe,QAAQ,eAAY;AAC5C,SAASC,WAAW,QAAQ,yBAAsB;AAElDA,YAAY;IACVC,OAAO;QACL,OAAO,MAAMF,gBAAgBG,OAAO,CAACJ,oBAAoB;YACvDK,aAAaN;YACbO,eAAe;QACjB;IACF;IACAC,UAAU,OAAOC;QACf,MAAM,AAACA,QAA4BC,IAAI;IACzC;AACF"}
1
+ {"version":3,"sources":["../../src/database/backend.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { KNEX_GLOBAL_CONFIG } from \"../testing/connection\";\nimport { BackendPostgres } from \"./backend\";\nimport { testBackend } from \"./backend.testsuite\";\n\ntestBackend({\n setup: async () => {\n const backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n return backend;\n },\n teardown: async (backend) => {\n await (backend as BackendPostgres).stop();\n },\n});\n"],"names":["randomUUID","KNEX_GLOBAL_CONFIG","BackendPostgres","testBackend","setup","backend","namespaceId","runMigrations","initialize","teardown","stop"],"mappings":"AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,SAASC,kBAAkB,QAAQ,2BAAwB;AAC3D,SAASC,eAAe,QAAQ,eAAY;AAC5C,SAASC,WAAW,QAAQ,yBAAsB;AAElDA,YAAY;IACVC,OAAO;QACL,MAAMC,UAAU,IAAIH,gBAAgBD,oBAAoB;YACtDK,aAAaN;YACbO,eAAe;QACjB;QACA,MAAMF,QAAQG,UAAU;QACxB,OAAOH;IACT;IACAI,UAAU,OAAOJ;QACf,MAAM,AAACA,QAA4BK,IAAI;IACzC;AACF"}
@@ -1,10 +1,10 @@
1
- import type { Knex } from "knex";
1
+ import { type Knex } from "knex";
2
2
  export declare const DEFAULT_SCHEMA = "sonamu_tasks";
3
3
  /**
4
4
  * migrate applies pending migrations to the database. Does nothing if the
5
5
  * database is already up to date.
6
6
  */
7
- export declare function migrate(knex: Knex, schema: string): Promise<void>;
7
+ export declare function migrate(config: Knex.Config, schema: string): Promise<void>;
8
8
  /**
9
9
  * dropSchema drops the specified schema from the database.
10
10
  */