@sonamu-kit/tasks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.swcrc +17 -0
- package/README.md +7 -0
- package/dist/backend.d.ts +107 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -0
- package/dist/chaos.test.d.ts +2 -0
- package/dist/chaos.test.d.ts.map +1 -0
- package/dist/chaos.test.js +92 -0
- package/dist/chaos.test.js.map +1 -0
- package/dist/client.d.ts +178 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +223 -0
- package/dist/client.js.map +1 -0
- package/dist/client.test.d.ts +2 -0
- package/dist/client.test.d.ts.map +1 -0
- package/dist/client.test.js +339 -0
- package/dist/client.test.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +24 -0
- package/dist/config.test.js.map +1 -0
- package/dist/core/duration.d.ts +22 -0
- package/dist/core/duration.d.ts.map +1 -0
- package/dist/core/duration.js +64 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/duration.test.d.ts +2 -0
- package/dist/core/duration.test.d.ts.map +1 -0
- package/dist/core/duration.test.js +265 -0
- package/dist/core/duration.test.js.map +1 -0
- package/dist/core/error.d.ts +15 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +25 -0
- package/dist/core/error.js.map +1 -0
- package/dist/core/error.test.d.ts +2 -0
- package/dist/core/error.test.d.ts.map +1 -0
- package/dist/core/error.test.js +63 -0
- package/dist/core/error.test.js.map +1 -0
- package/dist/core/json.d.ts +5 -0
- package/dist/core/json.d.ts.map +1 -0
- package/dist/core/json.js +3 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/result.d.ts +22 -0
- package/dist/core/result.d.ts.map +1 -0
- package/dist/core/result.js +22 -0
- package/dist/core/result.js.map +1 -0
- package/dist/core/result.test.d.ts +2 -0
- package/dist/core/result.test.d.ts.map +1 -0
- package/dist/core/result.test.js +19 -0
- package/dist/core/result.test.js.map +1 -0
- package/dist/core/retry.d.ts +21 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +25 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/retry.test.d.ts +2 -0
- package/dist/core/retry.test.d.ts.map +1 -0
- package/dist/core/retry.test.js +37 -0
- package/dist/core/retry.test.js.map +1 -0
- package/dist/core/schema.d.ts +57 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +4 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/step.d.ts +96 -0
- package/dist/core/step.d.ts.map +1 -0
- package/dist/core/step.js +78 -0
- package/dist/core/step.js.map +1 -0
- package/dist/core/step.test.d.ts +2 -0
- package/dist/core/step.test.d.ts.map +1 -0
- package/dist/core/step.test.js +356 -0
- package/dist/core/step.test.js.map +1 -0
- package/dist/core/workflow.d.ts +78 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +46 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/core/workflow.test.d.ts +2 -0
- package/dist/core/workflow.test.d.ts.map +1 -0
- package/dist/core/workflow.test.js +172 -0
- package/dist/core/workflow.test.js.map +1 -0
- package/dist/database/backend.d.ts +60 -0
- package/dist/database/backend.d.ts.map +1 -0
- package/dist/database/backend.js +387 -0
- package/dist/database/backend.js.map +1 -0
- package/dist/database/backend.test.d.ts +2 -0
- package/dist/database/backend.test.d.ts.map +1 -0
- package/dist/database/backend.test.js +17 -0
- package/dist/database/backend.test.js.map +1 -0
- package/dist/database/backend.testsuite.d.ts +20 -0
- package/dist/database/backend.testsuite.d.ts.map +1 -0
- package/dist/database/backend.testsuite.js +1174 -0
- package/dist/database/backend.testsuite.js.map +1 -0
- package/dist/database/base.d.ts +12 -0
- package/dist/database/base.d.ts.map +1 -0
- package/dist/database/base.js +19 -0
- package/dist/database/base.js.map +1 -0
- package/dist/database/migrations/20251212000000_0_init.js +9 -0
- package/dist/database/migrations/20251212000000_0_init.js.map +1 -0
- package/dist/database/migrations/20251212000000_1_tables.js +88 -0
- package/dist/database/migrations/20251212000000_1_tables.js.map +1 -0
- package/dist/database/migrations/20251212000000_2_fk.js +48 -0
- package/dist/database/migrations/20251212000000_2_fk.js.map +1 -0
- package/dist/database/migrations/20251212000000_3_indexes.js +107 -0
- package/dist/database/migrations/20251212000000_3_indexes.js.map +1 -0
- package/dist/database/pubsub.d.ts +17 -0
- package/dist/database/pubsub.d.ts.map +1 -0
- package/dist/database/pubsub.js +70 -0
- package/dist/database/pubsub.js.map +1 -0
- package/dist/database/pubsub.test.d.ts +2 -0
- package/dist/database/pubsub.test.d.ts.map +1 -0
- package/dist/database/pubsub.test.js +86 -0
- package/dist/database/pubsub.test.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/execution.d.ts +82 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +182 -0
- package/dist/execution.js.map +1 -0
- package/dist/execution.test.d.ts +2 -0
- package/dist/execution.test.d.ts.map +1 -0
- package/dist/execution.test.js +556 -0
- package/dist/execution.test.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +12 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +5 -0
- package/dist/internal.js.map +1 -0
- package/dist/practices/01-remote-workflow.d.ts +2 -0
- package/dist/practices/01-remote-workflow.d.ts.map +1 -0
- package/dist/practices/01-remote-workflow.js +69 -0
- package/dist/practices/01-remote-workflow.js.map +1 -0
- package/dist/practices/01-remote.d.ts +2 -0
- package/dist/practices/01-remote.d.ts.map +1 -0
- package/dist/practices/01-remote.js +87 -0
- package/dist/practices/01-remote.js.map +1 -0
- package/dist/practices/02-local.d.ts +2 -0
- package/dist/practices/02-local.d.ts.map +1 -0
- package/dist/practices/02-local.js +84 -0
- package/dist/practices/02-local.js.map +1 -0
- package/dist/practices/03-local-retry.d.ts +2 -0
- package/dist/practices/03-local-retry.d.ts.map +1 -0
- package/dist/practices/03-local-retry.js +85 -0
- package/dist/practices/03-local-retry.js.map +1 -0
- package/dist/practices/04-scheduler-dispose.d.ts +2 -0
- package/dist/practices/04-scheduler-dispose.d.ts.map +1 -0
- package/dist/practices/04-scheduler-dispose.js +65 -0
- package/dist/practices/04-scheduler-dispose.js.map +1 -0
- package/dist/practices/05-router.d.ts +2 -0
- package/dist/practices/05-router.d.ts.map +1 -0
- package/dist/practices/05-router.js +80 -0
- package/dist/practices/05-router.js.map +1 -0
- package/dist/registry.d.ts +33 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +54 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +95 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/scheduler.d.ts +22 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +117 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/tasks/index.d.ts +4 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +5 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/local-task.d.ts +6 -0
- package/dist/tasks/local-task.d.ts.map +1 -0
- package/dist/tasks/local-task.js +95 -0
- package/dist/tasks/local-task.js.map +1 -0
- package/dist/tasks/remote-task.d.ts +11 -0
- package/dist/tasks/remote-task.d.ts.map +1 -0
- package/dist/tasks/remote-task.js +213 -0
- package/dist/tasks/remote-task.js.map +1 -0
- package/dist/tasks/shared.d.ts +8 -0
- package/dist/tasks/shared.d.ts.map +1 -0
- package/dist/tasks/shared.js +41 -0
- package/dist/tasks/shared.js.map +1 -0
- package/dist/testing/connection.d.ts +7 -0
- package/dist/testing/connection.d.ts.map +1 -0
- package/dist/testing/connection.js +38 -0
- package/dist/testing/connection.js.map +1 -0
- package/dist/types/config.d.ts +44 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +18 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +4 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/events.d.ts +43 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +3 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/task-items.d.ts +12 -0
- package/dist/types/task-items.d.ts.map +1 -0
- package/dist/types/task-items.js +3 -0
- package/dist/types/task-items.js.map +1 -0
- package/dist/types/utils.d.ts +4 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +8 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/worker.d.ts +61 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +206 -0
- package/dist/worker.js.map +1 -0
- package/dist/worker.test.d.ts +2 -0
- package/dist/worker.test.d.ts.map +1 -0
- package/dist/worker.test.js +1163 -0
- package/dist/worker.test.js.map +1 -0
- package/dist/workflow.d.ts +44 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +21 -0
- package/dist/workflow.js.map +1 -0
- package/dist/workflow.test.d.ts +2 -0
- package/dist/workflow.test.d.ts.map +1 -0
- package/dist/workflow.test.js +73 -0
- package/dist/workflow.test.js.map +1 -0
- package/nodemon.json +6 -0
- package/package.json +63 -0
- package/scripts/migrate.ts +11 -0
- package/src/backend.ts +133 -0
- package/src/chaos.test.ts +108 -0
- package/src/client.test.ts +297 -0
- package/src/client.ts +331 -0
- package/src/config.test.ts +23 -0
- package/src/config.ts +35 -0
- package/src/core/duration.test.ts +326 -0
- package/src/core/duration.ts +86 -0
- package/src/core/error.test.ts +77 -0
- package/src/core/error.ts +30 -0
- package/src/core/json.ts +2 -0
- package/src/core/result.test.ts +13 -0
- package/src/core/result.ts +29 -0
- package/src/core/retry.test.ts +41 -0
- package/src/core/retry.ts +29 -0
- package/src/core/schema.ts +74 -0
- package/src/core/step.test.ts +362 -0
- package/src/core/step.ts +152 -0
- package/src/core/workflow.test.ts +184 -0
- package/src/core/workflow.ts +127 -0
- package/src/database/backend.test.ts +16 -0
- package/src/database/backend.testsuite.ts +1376 -0
- package/src/database/backend.ts +655 -0
- package/src/database/base.ts +23 -0
- package/src/database/migrations/20251212000000_0_init.ts +10 -0
- package/src/database/migrations/20251212000000_1_tables.ts +54 -0
- package/src/database/migrations/20251212000000_2_fk.ts +46 -0
- package/src/database/migrations/20251212000000_3_indexes.ts +82 -0
- package/src/database/pubsub.test.ts +92 -0
- package/src/database/pubsub.ts +92 -0
- package/src/execution.test.ts +508 -0
- package/src/execution.ts +291 -0
- package/src/index.ts +7 -0
- package/src/internal.ts +11 -0
- package/src/practices/01-remote-workflow.ts +61 -0
- package/src/registry.test.ts +122 -0
- package/src/registry.ts +65 -0
- package/src/testing/connection.ts +44 -0
- package/src/worker.test.ts +1138 -0
- package/src/worker.ts +281 -0
- package/src/workflow.test.ts +68 -0
- package/src/workflow.ts +84 -0
- package/table_ddl.sql +60 -0
- package/templates/openworkflow.config.ts +22 -0
- package/tsconfig.json +40 -0
- package/tsconfig.test.json +4 -0
- package/vite.config.ts +13 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/database/pubsub.ts"],"sourcesContent":["import assert from \"assert\";\nimport type { Knex } from \"knex\";\nimport { err, ok, type Result } from \"../core/result\";\n\nexport type OnSubscribed = (result: Result<string | null>) => void | Promise<void>;\n\nexport class PostgresPubSub {\n private _destroyed = false;\n private _onClosed: () => Promise<void>;\n private _listeners = new Map<string, Set<OnSubscribed>>();\n\n // biome-ignore lint/suspicious/noExplicitAny: Knex exposes a connection as any\n private _connection: any | null = null;\n\n private constructor(private readonly knex: Knex) {\n // Re-connect to the database when the connection is closed and not destroyed manually\n this._onClosed = (async () => {\n if (this._destroyed) {\n return;\n }\n\n await this.connect();\n }).bind(this);\n }\n\n get destroyed() {\n return this._destroyed;\n }\n\n // acquire new raw connection and set up listeners\n async connect() {\n const connection = await this.knex.client.acquireRawConnection();\n connection.on(\"close\", this._onClosed);\n connection.on(\n \"notification\",\n async ({ channel, payload: rawPayload }: { channel: string; payload: unknown }) => {\n const payload =\n typeof rawPayload === \"string\" && rawPayload.length !== 0 ? rawPayload : null;\n const listeners = this._listeners.get(channel);\n if (!listeners) {\n return;\n }\n\n const result = ok(payload);\n await Promise.allSettled(\n Array.from(listeners.values()).map((listener) => Promise.resolve(listener(result))),\n );\n },\n );\n connection.on(\"error\", async (error: Error) => {\n const result = err(error);\n await Promise.allSettled(\n Array.from(this._listeners.values())\n .flatMap((listeners) => Array.from(listeners))\n .map((listener) => Promise.resolve(listener(result))),\n );\n });\n\n for (const channel of this._listeners.keys()) {\n connection.query(`LISTEN ${channel}`);\n }\n\n this._connection = connection;\n }\n\n // destroy the listener and close the connection, do not destroy the knex connection\n async destroy() {\n this._destroyed = true;\n this._connection.off(\"close\", this._onClosed);\n await this.knex.client.destroyRawConnection(this._connection);\n }\n\n // create a new listener and connect to the database\n static async create(knex: Knex) {\n const listener = new PostgresPubSub(knex);\n await listener.connect();\n return listener;\n }\n\n // add a new listener to the channel\n listenEvent(channel: string, callback: OnSubscribed) {\n if (!this._listeners.has(channel)) {\n this._connection?.query(`LISTEN ${channel}`);\n this._listeners.set(channel, new Set<OnSubscribed>().add(callback));\n return;\n }\n\n const listeners = this._listeners.get(channel);\n assert(listeners, \"Listener channel not found\");\n listeners.add(callback);\n }\n}\n"],"names":["assert","err","ok","PostgresPubSub","_destroyed","_onClosed","_listeners","Map","_connection","knex","connect","bind","destroyed","connection","client","acquireRawConnection","on","channel","payload","rawPayload","length","listeners","get","result","Promise","allSettled","Array","from","values","map","listener","resolve","error","flatMap","keys","query","destroy","off","destroyRawConnection","create","listenEvent","callback","has","set","Set","add"],"mappings":"AAAA,OAAOA,YAAY,SAAS;AAE5B,SAASC,GAAG,EAAEC,EAAE,QAAqB,oBAAiB;AAItD,OAAO,MAAMC;;IACHC,aAAa,MAAM;IACnBC,UAA+B;IAC/BC,aAAa,IAAIC,MAAiC;IAE1D,+EAA+E;IACvEC,cAA0B,KAAK;IAEvC,YAAoB,AAAiBC,IAAU,CAAE;aAAZA,OAAAA;QACnC,sFAAsF;QACtF,IAAI,CAACJ,SAAS,GAAG,AAAC,CAAA;YAChB,IAAI,IAAI,CAACD,UAAU,EAAE;gBACnB;YACF;YAEA,MAAM,IAAI,CAACM,OAAO;QACpB,CAAA,EAAGC,IAAI,CAAC,IAAI;IACd;IAEA,IAAIC,YAAY;QACd,OAAO,IAAI,CAACR,UAAU;IACxB;IAEA,kDAAkD;IAClD,MAAMM,UAAU;QACd,MAAMG,aAAa,MAAM,IAAI,CAACJ,IAAI,CAACK,MAAM,CAACC,oBAAoB;QAC9DF,WAAWG,EAAE,CAAC,SAAS,IAAI,CAACX,SAAS;QACrCQ,WAAWG,EAAE,CACX,gBACA,OAAO,EAAEC,OAAO,EAAEC,SAASC,UAAU,EAAyC;YAC5E,MAAMD,UACJ,OAAOC,eAAe,YAAYA,WAAWC,MAAM,KAAK,IAAID,aAAa;YAC3E,MAAME,YAAY,IAAI,CAACf,UAAU,CAACgB,GAAG,CAACL;YACtC,IAAI,CAACI,WAAW;gBACd;YACF;YAEA,MAAME,SAASrB,GAAGgB;YAClB,MAAMM,QAAQC,UAAU,CACtBC,MAAMC,IAAI,CAACN,UAAUO,MAAM,IAAIC,GAAG,CAAC,CAACC,WAAaN,QAAQO,OAAO,CAACD,SAASP;QAE9E;QAEFV,WAAWG,EAAE,CAAC,SAAS,OAAOgB;YAC5B,MAAMT,SAAStB,IAAI+B;YACnB,MAAMR,QAAQC,UAAU,CACtBC,MAAMC,IAAI,CAAC,IAAI,CAACrB,UAAU,CAACsB,MAAM,IAC9BK,OAAO,CAAC,CAACZ,YAAcK,MAAMC,IAAI,CAACN,YAClCQ,GAAG,CAAC,CAACC,WAAaN,QAAQO,OAAO,CAACD,SAASP;QAElD;QAEA,KAAK,MAAMN,WAAW,IAAI,CAACX,UAAU,CAAC4B,IAAI,GAAI;YAC5CrB,WAAWsB,KAAK,CAAC,CAAC,OAAO,EAAElB,SAAS;QACtC;QAEA,IAAI,CAACT,WAAW,GAAGK;IACrB;IAEA,oFAAoF;IACpF,MAAMuB,UAAU;QACd,IAAI,CAAChC,UAAU,GAAG;QAClB,IAAI,CAACI,WAAW,CAAC6B,GAAG,CAAC,SAAS,IAAI,CAAChC,SAAS;QAC5C,MAAM,IAAI,CAACI,IAAI,CAACK,MAAM,CAACwB,oBAAoB,CAAC,IAAI,CAAC9B,WAAW;IAC9D;IAEA,oDAAoD;IACpD,aAAa+B,OAAO9B,IAAU,EAAE;QAC9B,MAAMqB,WAAW,IAAI3B,eAAeM;QACpC,MAAMqB,SAASpB,OAAO;QACtB,OAAOoB;IACT;IAEA,oCAAoC;IACpCU,YAAYvB,OAAe,EAAEwB,QAAsB,EAAE;QACnD,IAAI,CAAC,IAAI,CAACnC,UAAU,CAACoC,GAAG,CAACzB,UAAU;YACjC,IAAI,CAACT,WAAW,EAAE2B,MAAM,CAAC,OAAO,EAAElB,SAAS;YAC3C,IAAI,CAACX,UAAU,CAACqC,GAAG,CAAC1B,SAAS,IAAI2B,MAAoBC,GAAG,CAACJ;YACzD;QACF;QAEA,MAAMpB,YAAY,IAAI,CAACf,UAAU,CAACgB,GAAG,CAACL;QACtCjB,OAAOqB,WAAW;QAClBA,UAAUwB,GAAG,CAACJ;IAChB;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pubsub.test.d.ts","sourceRoot":"","sources":["../../src/database/pubsub.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import knex from "knex";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { KNEX_GLOBAL_CONFIG } from "../testing/connection.js";
|
|
4
|
+
import { PostgresPubSub } from "./pubsub.js";
|
|
5
|
+
describe("PostgresPubSub", ()=>{
|
|
6
|
+
let knexInstance;
|
|
7
|
+
beforeEach(async ()=>{
|
|
8
|
+
knexInstance = knex(KNEX_GLOBAL_CONFIG);
|
|
9
|
+
});
|
|
10
|
+
afterEach(async ()=>{
|
|
11
|
+
await knexInstance.destroy();
|
|
12
|
+
});
|
|
13
|
+
it("should create a new pubsub and connect to the database", async ()=>{
|
|
14
|
+
const pubsub = await PostgresPubSub.create(knexInstance);
|
|
15
|
+
expect(pubsub.destroyed).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it("should destroy the pubsub and close the connection", async ()=>{
|
|
18
|
+
const pubsub = await PostgresPubSub.create(knexInstance);
|
|
19
|
+
expect(pubsub.destroyed).toBe(false);
|
|
20
|
+
await pubsub.destroy();
|
|
21
|
+
expect(pubsub.destroyed).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("should not add the same pubsub multiple times", async ()=>{
|
|
24
|
+
const pubsub = await PostgresPubSub.create(knexInstance);
|
|
25
|
+
const results = [];
|
|
26
|
+
const callback = async (result)=>{
|
|
27
|
+
results.push(result);
|
|
28
|
+
};
|
|
29
|
+
pubsub.listenEvent("test", callback);
|
|
30
|
+
pubsub.listenEvent("test", callback);
|
|
31
|
+
await knexInstance.raw("NOTIFY test, 'test'");
|
|
32
|
+
await sleep(100);
|
|
33
|
+
expect(results.length).toBe(1);
|
|
34
|
+
expect(results[0]).toStrictEqual({
|
|
35
|
+
ok: true,
|
|
36
|
+
value: "test"
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it("should route notifications to the correct pubsubs by channel", async ()=>{
|
|
40
|
+
const pubsub = await PostgresPubSub.create(knexInstance);
|
|
41
|
+
const results1 = [];
|
|
42
|
+
const results2 = [];
|
|
43
|
+
pubsub.listenEvent("test1", (result)=>{
|
|
44
|
+
results1.push(result);
|
|
45
|
+
});
|
|
46
|
+
pubsub.listenEvent("test2", (result)=>{
|
|
47
|
+
results2.push(result);
|
|
48
|
+
});
|
|
49
|
+
await knexInstance.raw("NOTIFY test1, '!!!'");
|
|
50
|
+
await sleep(100);
|
|
51
|
+
expect(results1.length).toBe(1);
|
|
52
|
+
expect(results1[0]).toStrictEqual({
|
|
53
|
+
ok: true,
|
|
54
|
+
value: "!!!"
|
|
55
|
+
});
|
|
56
|
+
expect(results2.length).toBe(0);
|
|
57
|
+
await knexInstance.raw("NOTIFY test2, '###'");
|
|
58
|
+
await sleep(100);
|
|
59
|
+
expect(results1.length).toBe(1);
|
|
60
|
+
expect(results2.length).toBe(1);
|
|
61
|
+
expect(results2[0]).toStrictEqual({
|
|
62
|
+
ok: true,
|
|
63
|
+
value: "###"
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it("should payload be null if the payload is empty string", async ()=>{
|
|
67
|
+
const pubsub = await PostgresPubSub.create(knexInstance);
|
|
68
|
+
const results = [];
|
|
69
|
+
const callback = async (result)=>{
|
|
70
|
+
results.push(result);
|
|
71
|
+
};
|
|
72
|
+
pubsub.listenEvent("test", callback);
|
|
73
|
+
await knexInstance.raw(`NOTIFY test`);
|
|
74
|
+
await sleep(100);
|
|
75
|
+
expect(results.length).toBe(1);
|
|
76
|
+
expect(results[0]).toStrictEqual({
|
|
77
|
+
ok: true,
|
|
78
|
+
value: null
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
function sleep(ms) {
|
|
83
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=pubsub.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/database/pubsub.test.ts"],"sourcesContent":["import knex, { type Knex } from \"knex\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport type { Result } from \"../core/result\";\nimport { KNEX_GLOBAL_CONFIG } from \"../testing/connection\";\nimport { type OnSubscribed, PostgresPubSub } from \"./pubsub\";\n\ndescribe(\"PostgresPubSub\", () => {\n let knexInstance: Knex;\n\n beforeEach(async () => {\n knexInstance = knex(KNEX_GLOBAL_CONFIG);\n });\n\n afterEach(async () => {\n await knexInstance.destroy();\n });\n\n it(\"should create a new pubsub and connect to the database\", async () => {\n const pubsub = await PostgresPubSub.create(knexInstance);\n expect(pubsub.destroyed).toBe(false);\n });\n\n it(\"should destroy the pubsub and close the connection\", async () => {\n const pubsub = await PostgresPubSub.create(knexInstance);\n expect(pubsub.destroyed).toBe(false);\n await pubsub.destroy();\n expect(pubsub.destroyed).toBe(true);\n });\n\n it(\"should not add the same pubsub multiple times\", async () => {\n const pubsub = await PostgresPubSub.create(knexInstance);\n const results: Result<string | null>[] = [];\n const callback: OnSubscribed = async (result) => {\n results.push(result);\n };\n\n pubsub.listenEvent(\"test\", callback);\n pubsub.listenEvent(\"test\", callback);\n\n await knexInstance.raw(\"NOTIFY test, 'test'\");\n await sleep(100);\n\n expect(results.length).toBe(1);\n expect(results[0]).toStrictEqual({ ok: true, value: \"test\" });\n });\n\n it(\"should route notifications to the correct pubsubs by channel\", async () => {\n const pubsub = await PostgresPubSub.create(knexInstance);\n const results1: Result<string | null>[] = [];\n const results2: Result<string | null>[] = [];\n\n pubsub.listenEvent(\"test1\", (result) => {\n results1.push(result);\n });\n pubsub.listenEvent(\"test2\", (result) => {\n results2.push(result);\n });\n\n await knexInstance.raw(\"NOTIFY test1, '!!!'\");\n await sleep(100);\n\n expect(results1.length).toBe(1);\n expect(results1[0]).toStrictEqual({ ok: true, value: \"!!!\" });\n expect(results2.length).toBe(0);\n\n await knexInstance.raw(\"NOTIFY test2, '###'\");\n await sleep(100);\n\n expect(results1.length).toBe(1);\n expect(results2.length).toBe(1);\n expect(results2[0]).toStrictEqual({ ok: true, value: \"###\" });\n });\n\n it(\"should payload be null if the payload is empty string\", async () => {\n const pubsub = await PostgresPubSub.create(knexInstance);\n const results: Result<string | null>[] = [];\n const callback: OnSubscribed = async (result) => {\n results.push(result);\n };\n\n pubsub.listenEvent(\"test\", callback);\n await knexInstance.raw(`NOTIFY test`);\n await sleep(100);\n\n expect(results.length).toBe(1);\n expect(results[0]).toStrictEqual({ ok: true, value: null });\n });\n});\n\nfunction sleep(ms: number) {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["knex","afterEach","beforeEach","describe","expect","it","KNEX_GLOBAL_CONFIG","PostgresPubSub","knexInstance","destroy","pubsub","create","destroyed","toBe","results","callback","result","push","listenEvent","raw","sleep","length","toStrictEqual","ok","value","results1","results2","ms","Promise","resolve","setTimeout"],"mappings":"AAAA,OAAOA,UAAyB,OAAO;AACvC,SAASC,SAAS,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAErE,SAASC,kBAAkB,QAAQ,2BAAwB;AAC3D,SAA4BC,cAAc,QAAQ,cAAW;AAE7DJ,SAAS,kBAAkB;IACzB,IAAIK;IAEJN,WAAW;QACTM,eAAeR,KAAKM;IACtB;IAEAL,UAAU;QACR,MAAMO,aAAaC,OAAO;IAC5B;IAEAJ,GAAG,0DAA0D;QAC3D,MAAMK,SAAS,MAAMH,eAAeI,MAAM,CAACH;QAC3CJ,OAAOM,OAAOE,SAAS,EAAEC,IAAI,CAAC;IAChC;IAEAR,GAAG,sDAAsD;QACvD,MAAMK,SAAS,MAAMH,eAAeI,MAAM,CAACH;QAC3CJ,OAAOM,OAAOE,SAAS,EAAEC,IAAI,CAAC;QAC9B,MAAMH,OAAOD,OAAO;QACpBL,OAAOM,OAAOE,SAAS,EAAEC,IAAI,CAAC;IAChC;IAEAR,GAAG,iDAAiD;QAClD,MAAMK,SAAS,MAAMH,eAAeI,MAAM,CAACH;QAC3C,MAAMM,UAAmC,EAAE;QAC3C,MAAMC,WAAyB,OAAOC;YACpCF,QAAQG,IAAI,CAACD;QACf;QAEAN,OAAOQ,WAAW,CAAC,QAAQH;QAC3BL,OAAOQ,WAAW,CAAC,QAAQH;QAE3B,MAAMP,aAAaW,GAAG,CAAC;QACvB,MAAMC,MAAM;QAEZhB,OAAOU,QAAQO,MAAM,EAAER,IAAI,CAAC;QAC5BT,OAAOU,OAAO,CAAC,EAAE,EAAEQ,aAAa,CAAC;YAAEC,IAAI;YAAMC,OAAO;QAAO;IAC7D;IAEAnB,GAAG,gEAAgE;QACjE,MAAMK,SAAS,MAAMH,eAAeI,MAAM,CAACH;QAC3C,MAAMiB,WAAoC,EAAE;QAC5C,MAAMC,WAAoC,EAAE;QAE5ChB,OAAOQ,WAAW,CAAC,SAAS,CAACF;YAC3BS,SAASR,IAAI,CAACD;QAChB;QACAN,OAAOQ,WAAW,CAAC,SAAS,CAACF;YAC3BU,SAAST,IAAI,CAACD;QAChB;QAEA,MAAMR,aAAaW,GAAG,CAAC;QACvB,MAAMC,MAAM;QAEZhB,OAAOqB,SAASJ,MAAM,EAAER,IAAI,CAAC;QAC7BT,OAAOqB,QAAQ,CAAC,EAAE,EAAEH,aAAa,CAAC;YAAEC,IAAI;YAAMC,OAAO;QAAM;QAC3DpB,OAAOsB,SAASL,MAAM,EAAER,IAAI,CAAC;QAE7B,MAAML,aAAaW,GAAG,CAAC;QACvB,MAAMC,MAAM;QAEZhB,OAAOqB,SAASJ,MAAM,EAAER,IAAI,CAAC;QAC7BT,OAAOsB,SAASL,MAAM,EAAER,IAAI,CAAC;QAC7BT,OAAOsB,QAAQ,CAAC,EAAE,EAAEJ,aAAa,CAAC;YAAEC,IAAI;YAAMC,OAAO;QAAM;IAC7D;IAEAnB,GAAG,yDAAyD;QAC1D,MAAMK,SAAS,MAAMH,eAAeI,MAAM,CAACH;QAC3C,MAAMM,UAAmC,EAAE;QAC3C,MAAMC,WAAyB,OAAOC;YACpCF,QAAQG,IAAI,CAACD;QACf;QAEAN,OAAOQ,WAAW,CAAC,QAAQH;QAC3B,MAAMP,aAAaW,GAAG,CAAC,CAAC,WAAW,CAAC;QACpC,MAAMC,MAAM;QAEZhB,OAAOU,QAAQO,MAAM,EAAER,IAAI,CAAC;QAC5BT,OAAOU,OAAO,CAAC,EAAE,EAAEQ,aAAa,CAAC;YAAEC,IAAI;YAAMC,OAAO;QAAK;IAC3D;AACF;AAEA,SAASJ,MAAMO,EAAU;IACvB,OAAO,IAAIC,QAAc,CAACC,UAAYC,WAAWD,SAASF;AAC5D"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ProcessErrorReasonEnum } from "./types";
|
|
2
|
+
export declare class SonamuTaskError extends Error {
|
|
3
|
+
cause: Error | undefined;
|
|
4
|
+
type: ProcessErrorReasonEnum;
|
|
5
|
+
constructor(type?: ProcessErrorReasonEnum, cause?: Error);
|
|
6
|
+
}
|
|
7
|
+
export declare function isSonamuTaskError(error: unknown): error is SonamuTaskError;
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,qBAAa,eAAgB,SAAQ,KAAK;IACxC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,sBAAsB,CAAC;gBAEjB,IAAI,CAAC,EAAE,sBAAsB,EAAE,KAAK,CAAC,EAAE,KAAK;CAazD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class SonamuTaskError extends Error {
|
|
2
|
+
cause;
|
|
3
|
+
type;
|
|
4
|
+
constructor(type, cause){
|
|
5
|
+
if (!type) {
|
|
6
|
+
type = "exception";
|
|
7
|
+
}
|
|
8
|
+
super(`SonamuTaskError: ${type}`);
|
|
9
|
+
this.name = "SonamuTaskError";
|
|
10
|
+
this.type = type;
|
|
11
|
+
if (cause) {
|
|
12
|
+
this.cause = cause;
|
|
13
|
+
this.stack = cause.stack;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isSonamuTaskError(error) {
|
|
18
|
+
return error instanceof SonamuTaskError;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import { ProcessErrorReasonEnum } from \"./types\";\n\nexport class SonamuTaskError extends Error {\n cause: Error | undefined;\n type: ProcessErrorReasonEnum;\n\n constructor(type?: ProcessErrorReasonEnum, cause?: Error) {\n if (!type) {\n type = \"exception\";\n }\n\n super(`SonamuTaskError: ${type}`);\n this.name = \"SonamuTaskError\";\n this.type = type;\n if (cause) {\n this.cause = cause;\n this.stack = cause.stack;\n }\n }\n}\n\nexport function isSonamuTaskError(error: unknown): error is SonamuTaskError {\n return error instanceof SonamuTaskError;\n}\n"],"names":["SonamuTaskError","Error","cause","type","name","stack","isSonamuTaskError","error"],"mappings":"AAEA,OAAO,MAAMA,wBAAwBC;IACnCC,MAAyB;IACzBC,KAA6B;IAE7B,YAAYA,IAA6B,EAAED,KAAa,CAAE;QACxD,IAAI,CAACC,MAAM;YACTA,OAAO;QACT;QAEA,KAAK,CAAC,CAAC,iBAAiB,EAAEA,MAAM;QAChC,IAAI,CAACC,IAAI,GAAG;QACZ,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAID,OAAO;YACT,IAAI,CAACA,KAAK,GAAGA;YACb,IAAI,CAACG,KAAK,GAAGH,MAAMG,KAAK;QAC1B;IACF;AACF;AAEA,OAAO,SAASC,kBAAkBC,KAAc;IAC9C,OAAOA,iBAAiBP;AAC1B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Backend } from "./backend";
|
|
2
|
+
import type { DurationString } from "./core/duration";
|
|
3
|
+
import type { StepAttempt } from "./core/step";
|
|
4
|
+
import type { WorkflowRun } from "./core/workflow";
|
|
5
|
+
/**
|
|
6
|
+
* Config for an individual step defined with `step.run()`.
|
|
7
|
+
*/
|
|
8
|
+
export interface StepFunctionConfig {
|
|
9
|
+
/**
|
|
10
|
+
* The name of the step.
|
|
11
|
+
*/
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Represents the API for defining steps within a workflow. Used within a
|
|
16
|
+
* workflow handler to define steps by calling `step.run()`.
|
|
17
|
+
*/
|
|
18
|
+
export interface StepApi {
|
|
19
|
+
run<Output>(config: Readonly<StepFunctionConfig>, fn: StepFunction<Output>): Promise<Output>;
|
|
20
|
+
sleep(name: string, duration: DurationString): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The step definition (defined by the user) that executes user code. Can return
|
|
24
|
+
* undefined (e.g., when using `return;`) which will be converted to null.
|
|
25
|
+
*/
|
|
26
|
+
export type StepFunction<Output> = () => Promise<Output | undefined> | Output | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Params passed to a workflow function for the user to use when defining steps.
|
|
29
|
+
*/
|
|
30
|
+
export interface WorkflowFunctionParams<Input> {
|
|
31
|
+
input: Input;
|
|
32
|
+
step: StepApi;
|
|
33
|
+
version: string | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The workflow definition's function (defined by the user) that the user uses
|
|
37
|
+
* to define the workflow's steps.
|
|
38
|
+
*/
|
|
39
|
+
export type WorkflowFunction<Input, Output> = (params: Readonly<WorkflowFunctionParams<Input>>) => Promise<Output> | Output;
|
|
40
|
+
/**
|
|
41
|
+
* Configures the options for a StepExecutor.
|
|
42
|
+
*/
|
|
43
|
+
export interface StepExecutorOptions {
|
|
44
|
+
backend: Backend;
|
|
45
|
+
workflowRunId: string;
|
|
46
|
+
workerId: string;
|
|
47
|
+
attempts: StepAttempt[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Replays prior step attempts and persists new ones while memoizing
|
|
51
|
+
* deterministic step outputs.
|
|
52
|
+
*/
|
|
53
|
+
export declare class StepExecutor implements StepApi {
|
|
54
|
+
private readonly backend;
|
|
55
|
+
private readonly workflowRunId;
|
|
56
|
+
private readonly workerId;
|
|
57
|
+
private cache;
|
|
58
|
+
constructor(options: Readonly<StepExecutorOptions>);
|
|
59
|
+
run<Output>(config: Readonly<StepFunctionConfig>, fn: StepFunction<Output>): Promise<Output>;
|
|
60
|
+
sleep(name: string, duration: DurationString): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parameters for the workflow execution use case.
|
|
64
|
+
*/
|
|
65
|
+
export interface ExecuteWorkflowParams {
|
|
66
|
+
backend: Backend;
|
|
67
|
+
workflowRun: WorkflowRun;
|
|
68
|
+
workflowFn: WorkflowFunction<unknown, unknown>;
|
|
69
|
+
workflowVersion: string | null;
|
|
70
|
+
workerId: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Execute a workflow run. This is the core application use case that handles:
|
|
74
|
+
* - Loading step history
|
|
75
|
+
* - Handling sleeping steps
|
|
76
|
+
* - Creating the step executor
|
|
77
|
+
* - Executing the workflow function
|
|
78
|
+
* - Completing, failing, or sleeping the workflow run based on the outcome
|
|
79
|
+
* @param params - The execution parameters
|
|
80
|
+
*/
|
|
81
|
+
export declare function executeWorkflow(params: Readonly<ExecuteWorkflowParams>): Promise<void>;
|
|
82
|
+
//# sourceMappingURL=execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,aAAa,CAAC;AASjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7F,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,IAAI,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,KAAK,EAAE,MAAM,IAAI,CAC5C,MAAM,EAAE,QAAQ,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,KAC5C,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AAgB9B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,OAAO;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,KAAK,CAAmB;gBAEpB,OAAO,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IAQ5C,GAAG,CAAC,MAAM,EACd,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACpC,EAAE,EAAE,YAAY,CAAC,MAAM,CAAC,GACvB,OAAO,CAAC,MAAM,CAAC;IAgDZ,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BnE;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2F5F"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { serializeError } from "./core/error.js";
|
|
2
|
+
import { addToStepAttemptCache, calculateSleepResumeAt, createSleepContext, createStepAttemptCacheFromAttempts, getCachedStepAttempt, normalizeStepOutput } from "./core/step.js";
|
|
3
|
+
/**
|
|
4
|
+
* Signal thrown when a workflow needs to sleep. Contains the time when the
|
|
5
|
+
* workflow should resume.
|
|
6
|
+
*/ class SleepSignal extends Error {
|
|
7
|
+
resumeAt;
|
|
8
|
+
constructor(resumeAt){
|
|
9
|
+
super("SleepSignal");
|
|
10
|
+
this.name = "SleepSignal";
|
|
11
|
+
this.resumeAt = resumeAt;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Replays prior step attempts and persists new ones while memoizing
|
|
16
|
+
* deterministic step outputs.
|
|
17
|
+
*/ export class StepExecutor {
|
|
18
|
+
backend;
|
|
19
|
+
workflowRunId;
|
|
20
|
+
workerId;
|
|
21
|
+
cache;
|
|
22
|
+
constructor(options){
|
|
23
|
+
this.backend = options.backend;
|
|
24
|
+
this.workflowRunId = options.workflowRunId;
|
|
25
|
+
this.workerId = options.workerId;
|
|
26
|
+
this.cache = createStepAttemptCacheFromAttempts(options.attempts);
|
|
27
|
+
}
|
|
28
|
+
async run(config, fn) {
|
|
29
|
+
const { name } = config;
|
|
30
|
+
// return cached result if available
|
|
31
|
+
const existingAttempt = getCachedStepAttempt(this.cache, name);
|
|
32
|
+
if (existingAttempt) {
|
|
33
|
+
return existingAttempt.output;
|
|
34
|
+
}
|
|
35
|
+
// not in cache, create new step attempt
|
|
36
|
+
const attempt = await this.backend.createStepAttempt({
|
|
37
|
+
workflowRunId: this.workflowRunId,
|
|
38
|
+
workerId: this.workerId,
|
|
39
|
+
stepName: name,
|
|
40
|
+
kind: "function",
|
|
41
|
+
config: {},
|
|
42
|
+
context: null
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
// execute step function
|
|
46
|
+
const result = await fn();
|
|
47
|
+
const output = normalizeStepOutput(result);
|
|
48
|
+
// mark success
|
|
49
|
+
const savedAttempt = await this.backend.completeStepAttempt({
|
|
50
|
+
workflowRunId: this.workflowRunId,
|
|
51
|
+
stepAttemptId: attempt.id,
|
|
52
|
+
workerId: this.workerId,
|
|
53
|
+
output
|
|
54
|
+
});
|
|
55
|
+
// cache result
|
|
56
|
+
this.cache = addToStepAttemptCache(this.cache, savedAttempt);
|
|
57
|
+
return savedAttempt.output;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// mark failure
|
|
60
|
+
await this.backend.failStepAttempt({
|
|
61
|
+
workflowRunId: this.workflowRunId,
|
|
62
|
+
stepAttemptId: attempt.id,
|
|
63
|
+
workerId: this.workerId,
|
|
64
|
+
error: serializeError(error)
|
|
65
|
+
});
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async sleep(name, duration) {
|
|
70
|
+
// return cached result if this sleep already completed
|
|
71
|
+
const existingAttempt = getCachedStepAttempt(this.cache, name);
|
|
72
|
+
if (existingAttempt) return;
|
|
73
|
+
// create new step attempt for the sleep
|
|
74
|
+
const result = calculateSleepResumeAt(duration);
|
|
75
|
+
if (!result.ok) {
|
|
76
|
+
throw result.error;
|
|
77
|
+
}
|
|
78
|
+
const resumeAt = result.value;
|
|
79
|
+
const context = createSleepContext(resumeAt);
|
|
80
|
+
await this.backend.createStepAttempt({
|
|
81
|
+
workflowRunId: this.workflowRunId,
|
|
82
|
+
workerId: this.workerId,
|
|
83
|
+
stepName: name,
|
|
84
|
+
kind: "sleep",
|
|
85
|
+
config: {},
|
|
86
|
+
context
|
|
87
|
+
});
|
|
88
|
+
// throw sleep signal to trigger postponement
|
|
89
|
+
// we do not mark the step as completed here; it will be updated
|
|
90
|
+
// when the workflow resumes
|
|
91
|
+
throw new SleepSignal(resumeAt);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Execute a workflow run. This is the core application use case that handles:
|
|
96
|
+
* - Loading step history
|
|
97
|
+
* - Handling sleeping steps
|
|
98
|
+
* - Creating the step executor
|
|
99
|
+
* - Executing the workflow function
|
|
100
|
+
* - Completing, failing, or sleeping the workflow run based on the outcome
|
|
101
|
+
* @param params - The execution parameters
|
|
102
|
+
*/ export async function executeWorkflow(params) {
|
|
103
|
+
const { backend, workflowRun, workflowFn, workflowVersion, workerId } = params;
|
|
104
|
+
try {
|
|
105
|
+
// load all pages of step history
|
|
106
|
+
const attempts = [];
|
|
107
|
+
let cursor;
|
|
108
|
+
do {
|
|
109
|
+
const response = await backend.listStepAttempts({
|
|
110
|
+
workflowRunId: workflowRun.id,
|
|
111
|
+
...cursor ? {
|
|
112
|
+
after: cursor
|
|
113
|
+
} : {},
|
|
114
|
+
limit: 1000
|
|
115
|
+
});
|
|
116
|
+
attempts.push(...response.data);
|
|
117
|
+
cursor = response.pagination.next ?? undefined;
|
|
118
|
+
}while (cursor)
|
|
119
|
+
// mark any sleep steps as completed if their sleep duration has elapsed,
|
|
120
|
+
// or rethrow SleepSignal if still sleeping
|
|
121
|
+
for(let i = 0; i < attempts.length; i++){
|
|
122
|
+
const attempt = attempts[i];
|
|
123
|
+
if (!attempt) continue;
|
|
124
|
+
if (attempt.status === "running" && attempt.kind === "sleep" && attempt.context?.kind === "sleep") {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
const resumeAt = new Date(attempt.context.resumeAt);
|
|
127
|
+
const resumeAtMs = resumeAt.getTime();
|
|
128
|
+
if (now < resumeAtMs) {
|
|
129
|
+
// sleep duration HAS NOT elapsed yet, throw signal to put workflow
|
|
130
|
+
// back to sleep
|
|
131
|
+
throw new SleepSignal(resumeAt);
|
|
132
|
+
}
|
|
133
|
+
// sleep duration HAS elapsed, mark the step as completed and continue
|
|
134
|
+
const completed = await backend.completeStepAttempt({
|
|
135
|
+
workflowRunId: workflowRun.id,
|
|
136
|
+
stepAttemptId: attempt.id,
|
|
137
|
+
workerId,
|
|
138
|
+
output: null
|
|
139
|
+
});
|
|
140
|
+
// update cache w/ completed attempt
|
|
141
|
+
attempts[i] = completed;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// create step executor
|
|
145
|
+
const executor = new StepExecutor({
|
|
146
|
+
backend,
|
|
147
|
+
workflowRunId: workflowRun.id,
|
|
148
|
+
workerId,
|
|
149
|
+
attempts
|
|
150
|
+
});
|
|
151
|
+
// execute workflow
|
|
152
|
+
const output = await workflowFn({
|
|
153
|
+
input: workflowRun.input,
|
|
154
|
+
step: executor,
|
|
155
|
+
version: workflowVersion
|
|
156
|
+
});
|
|
157
|
+
// mark success
|
|
158
|
+
await backend.completeWorkflowRun({
|
|
159
|
+
workflowRunId: workflowRun.id,
|
|
160
|
+
workerId,
|
|
161
|
+
output: output ?? null
|
|
162
|
+
});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
// handle sleep signal by setting workflow to sleeping status
|
|
165
|
+
if (error instanceof SleepSignal) {
|
|
166
|
+
await backend.sleepWorkflowRun({
|
|
167
|
+
workflowRunId: workflowRun.id,
|
|
168
|
+
workerId,
|
|
169
|
+
availableAt: error.resumeAt
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// mark failure
|
|
174
|
+
await backend.failWorkflowRun({
|
|
175
|
+
workflowRunId: workflowRun.id,
|
|
176
|
+
workerId,
|
|
177
|
+
error: serializeError(error)
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=execution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/execution.ts"],"sourcesContent":["import type { Backend } from \"./backend\";\nimport type { DurationString } from \"./core/duration\";\nimport { serializeError } from \"./core/error\";\nimport type { JsonValue } from \"./core/json\";\nimport type { StepAttempt, StepAttemptCache } from \"./core/step\";\nimport {\n addToStepAttemptCache,\n calculateSleepResumeAt,\n createSleepContext,\n createStepAttemptCacheFromAttempts,\n getCachedStepAttempt,\n normalizeStepOutput,\n} from \"./core/step\";\nimport type { WorkflowRun } from \"./core/workflow\";\n\n/**\n * Config for an individual step defined with `step.run()`.\n */\nexport interface StepFunctionConfig {\n /**\n * The name of the step.\n */\n name: string;\n}\n\n/**\n * Represents the API for defining steps within a workflow. Used within a\n * workflow handler to define steps by calling `step.run()`.\n */\nexport interface StepApi {\n run<Output>(config: Readonly<StepFunctionConfig>, fn: StepFunction<Output>): Promise<Output>;\n sleep(name: string, duration: DurationString): Promise<void>;\n}\n\n/**\n * The step definition (defined by the user) that executes user code. Can return\n * undefined (e.g., when using `return;`) which will be converted to null.\n */\nexport type StepFunction<Output> = () => Promise<Output | undefined> | Output | undefined;\n\n/**\n * Params passed to a workflow function for the user to use when defining steps.\n */\nexport interface WorkflowFunctionParams<Input> {\n input: Input;\n step: StepApi;\n version: string | null;\n}\n\n/**\n * The workflow definition's function (defined by the user) that the user uses\n * to define the workflow's steps.\n */\nexport type WorkflowFunction<Input, Output> = (\n params: Readonly<WorkflowFunctionParams<Input>>,\n) => Promise<Output> | Output;\n\n/**\n * Signal thrown when a workflow needs to sleep. Contains the time when the\n * workflow should resume.\n */\nclass SleepSignal extends Error {\n readonly resumeAt: Date;\n\n constructor(resumeAt: Readonly<Date>) {\n super(\"SleepSignal\");\n this.name = \"SleepSignal\";\n this.resumeAt = resumeAt;\n }\n}\n\n/**\n * Configures the options for a StepExecutor.\n */\nexport interface StepExecutorOptions {\n backend: Backend;\n workflowRunId: string;\n workerId: string;\n attempts: StepAttempt[];\n}\n\n/**\n * Replays prior step attempts and persists new ones while memoizing\n * deterministic step outputs.\n */\nexport class StepExecutor implements StepApi {\n private readonly backend: Backend;\n private readonly workflowRunId: string;\n private readonly workerId: string;\n private cache: StepAttemptCache;\n\n constructor(options: Readonly<StepExecutorOptions>) {\n this.backend = options.backend;\n this.workflowRunId = options.workflowRunId;\n this.workerId = options.workerId;\n\n this.cache = createStepAttemptCacheFromAttempts(options.attempts);\n }\n\n async run<Output>(\n config: Readonly<StepFunctionConfig>,\n fn: StepFunction<Output>,\n ): Promise<Output> {\n const { name } = config;\n\n // return cached result if available\n const existingAttempt = getCachedStepAttempt(this.cache, name);\n if (existingAttempt) {\n return existingAttempt.output as Output;\n }\n\n // not in cache, create new step attempt\n const attempt = await this.backend.createStepAttempt({\n workflowRunId: this.workflowRunId,\n workerId: this.workerId,\n stepName: name,\n kind: \"function\",\n config: {},\n context: null,\n });\n\n try {\n // execute step function\n const result = await fn();\n const output = normalizeStepOutput(result);\n\n // mark success\n const savedAttempt = await this.backend.completeStepAttempt({\n workflowRunId: this.workflowRunId,\n stepAttemptId: attempt.id,\n workerId: this.workerId,\n output,\n });\n\n // cache result\n this.cache = addToStepAttemptCache(this.cache, savedAttempt);\n\n return savedAttempt.output as Output;\n } catch (error) {\n // mark failure\n await this.backend.failStepAttempt({\n workflowRunId: this.workflowRunId,\n stepAttemptId: attempt.id,\n workerId: this.workerId,\n error: serializeError(error),\n });\n throw error;\n }\n }\n\n async sleep(name: string, duration: DurationString): Promise<void> {\n // return cached result if this sleep already completed\n const existingAttempt = getCachedStepAttempt(this.cache, name);\n if (existingAttempt) return;\n\n // create new step attempt for the sleep\n const result = calculateSleepResumeAt(duration);\n if (!result.ok) {\n throw result.error;\n }\n const resumeAt = result.value;\n const context = createSleepContext(resumeAt);\n\n await this.backend.createStepAttempt({\n workflowRunId: this.workflowRunId,\n workerId: this.workerId,\n stepName: name,\n kind: \"sleep\",\n config: {},\n context,\n });\n\n // throw sleep signal to trigger postponement\n // we do not mark the step as completed here; it will be updated\n // when the workflow resumes\n throw new SleepSignal(resumeAt);\n }\n}\n\n/**\n * Parameters for the workflow execution use case.\n */\nexport interface ExecuteWorkflowParams {\n backend: Backend;\n workflowRun: WorkflowRun;\n workflowFn: WorkflowFunction<unknown, unknown>;\n workflowVersion: string | null;\n workerId: string;\n}\n\n/**\n * Execute a workflow run. This is the core application use case that handles:\n * - Loading step history\n * - Handling sleeping steps\n * - Creating the step executor\n * - Executing the workflow function\n * - Completing, failing, or sleeping the workflow run based on the outcome\n * @param params - The execution parameters\n */\nexport async function executeWorkflow(params: Readonly<ExecuteWorkflowParams>): Promise<void> {\n const { backend, workflowRun, workflowFn, workflowVersion, workerId } = params;\n\n try {\n // load all pages of step history\n const attempts: StepAttempt[] = [];\n let cursor: string | undefined;\n do {\n const response = await backend.listStepAttempts({\n workflowRunId: workflowRun.id,\n ...(cursor ? { after: cursor } : {}),\n limit: 1000,\n });\n attempts.push(...response.data);\n cursor = response.pagination.next ?? undefined;\n } while (cursor);\n\n // mark any sleep steps as completed if their sleep duration has elapsed,\n // or rethrow SleepSignal if still sleeping\n for (let i = 0; i < attempts.length; i++) {\n const attempt = attempts[i];\n if (!attempt) continue;\n\n if (\n attempt.status === \"running\" &&\n attempt.kind === \"sleep\" &&\n attempt.context?.kind === \"sleep\"\n ) {\n const now = Date.now();\n const resumeAt = new Date(attempt.context.resumeAt);\n const resumeAtMs = resumeAt.getTime();\n\n if (now < resumeAtMs) {\n // sleep duration HAS NOT elapsed yet, throw signal to put workflow\n // back to sleep\n throw new SleepSignal(resumeAt);\n }\n\n // sleep duration HAS elapsed, mark the step as completed and continue\n const completed = await backend.completeStepAttempt({\n workflowRunId: workflowRun.id,\n stepAttemptId: attempt.id,\n workerId,\n output: null,\n });\n\n // update cache w/ completed attempt\n attempts[i] = completed;\n }\n }\n\n // create step executor\n const executor = new StepExecutor({\n backend,\n workflowRunId: workflowRun.id,\n workerId,\n attempts,\n });\n\n // execute workflow\n const output = await workflowFn({\n input: workflowRun.input as unknown,\n step: executor,\n version: workflowVersion,\n });\n\n // mark success\n await backend.completeWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n output: (output ?? null) as JsonValue,\n });\n } catch (error) {\n // handle sleep signal by setting workflow to sleeping status\n if (error instanceof SleepSignal) {\n await backend.sleepWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n availableAt: error.resumeAt,\n });\n\n return;\n }\n\n // mark failure\n await backend.failWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n error: serializeError(error),\n });\n }\n}\n"],"names":["serializeError","addToStepAttemptCache","calculateSleepResumeAt","createSleepContext","createStepAttemptCacheFromAttempts","getCachedStepAttempt","normalizeStepOutput","SleepSignal","Error","resumeAt","name","StepExecutor","backend","workflowRunId","workerId","cache","options","attempts","run","config","fn","existingAttempt","output","attempt","createStepAttempt","stepName","kind","context","result","savedAttempt","completeStepAttempt","stepAttemptId","id","error","failStepAttempt","sleep","duration","ok","value","executeWorkflow","params","workflowRun","workflowFn","workflowVersion","cursor","response","listStepAttempts","after","limit","push","data","pagination","next","undefined","i","length","status","now","Date","resumeAtMs","getTime","completed","executor","input","step","version","completeWorkflowRun","sleepWorkflowRun","availableAt","failWorkflowRun"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.test.d.ts","sourceRoot":"","sources":["../src/execution.test.ts"],"names":[],"mappings":""}
|