@strav/durable 0.4.31 → 1.0.0-alpha.9
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/package.json +18 -37
- package/src/define_durable.ts +29 -0
- package/src/durable_advance_job.ts +38 -0
- package/src/durable_compensate_job.ts +33 -0
- package/src/durable_error.ts +38 -0
- package/src/durable_provider.ts +91 -0
- package/src/durable_runner.ts +395 -0
- package/src/durable_workflow.ts +97 -0
- package/src/index.ts +25 -26
- package/src/journal_schema.ts +53 -0
- package/src/runs_schema.ts +38 -0
- package/src/types.ts +58 -198
- package/src/workflow_registry.ts +49 -0
- package/CHANGELOG.md +0 -26
- package/src/builder.ts +0 -158
- package/src/config.ts +0 -36
- package/src/durable.ts +0 -268
- package/src/engine/advance_handler.ts +0 -154
- package/src/engine/compensate_handler.ts +0 -70
- package/src/engine/compensation_driver.ts +0 -61
- package/src/engine/context.ts +0 -36
- package/src/engine/enqueue.ts +0 -62
- package/src/engine/finalize.ts +0 -111
- package/src/engine/index.ts +0 -20
- package/src/engine/run_store.ts +0 -42
- package/src/engine/step_driver.ts +0 -291
- package/src/engine/suspended_run.ts +0 -24
- package/src/errors.ts +0 -21
- package/src/helpers.ts +0 -16
- package/src/models/index.ts +0 -3
- package/src/models/journal.ts +0 -54
- package/src/models/run_machine.ts +0 -39
- package/src/models/workflow_run.ts +0 -36
- package/src/providers/durable_provider.ts +0 -31
- package/src/providers/index.ts +0 -2
- package/src/registry.ts +0 -35
- package/src/schema.ts +0 -70
- package/src/util.ts +0 -25
- package/tsconfig.json +0 -5
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { ServiceProvider } from '@strav/kernel'
|
|
2
|
-
import type { Application } from '@strav/kernel'
|
|
3
|
-
import { Durable } from '../durable.ts'
|
|
4
|
-
|
|
5
|
-
export interface DurableProviderOptions {
|
|
6
|
-
/** Whether to auto-create the durable engine tables. Default: `true`. */
|
|
7
|
-
ensureTables?: boolean
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Registers the durable execution engine.
|
|
12
|
-
*
|
|
13
|
-
* On boot it creates the engine tables and registers the `durable:advance` /
|
|
14
|
-
* `durable:compensate` queue handlers. Run a `@strav/queue` `Worker` on the
|
|
15
|
-
* durable queue (default name `'durable'`) to actually process runs.
|
|
16
|
-
*/
|
|
17
|
-
export default class DurableProvider extends ServiceProvider {
|
|
18
|
-
readonly name = 'durable'
|
|
19
|
-
override readonly dependencies = ['database', 'queue']
|
|
20
|
-
|
|
21
|
-
constructor(private readonly options?: DurableProviderOptions) {
|
|
22
|
-
super()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
override async boot(_app: Application): Promise<void> {
|
|
26
|
-
if (this.options?.ensureTables !== false) {
|
|
27
|
-
await Durable.ensureTables()
|
|
28
|
-
}
|
|
29
|
-
Durable.registerHandlers()
|
|
30
|
-
}
|
|
31
|
-
}
|
package/src/providers/index.ts
DELETED
package/src/registry.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { DurableWorkflow } from './builder.ts'
|
|
2
|
-
import { WorkflowNotRegisteredError } from './errors.ts'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Process-wide registry of durable workflow definitions.
|
|
6
|
-
*
|
|
7
|
-
* A `durable:advance` queue job carries only a workflow *name* (not a
|
|
8
|
-
* closure), so the worker process must be able to look the definition up
|
|
9
|
-
* by name. Importing the module that calls `durable(...)` registers it.
|
|
10
|
-
*/
|
|
11
|
-
const workflows = new Map<string, DurableWorkflow>()
|
|
12
|
-
|
|
13
|
-
export const registry = {
|
|
14
|
-
/** Register (or replace) a workflow definition by name. */
|
|
15
|
-
register(workflow: DurableWorkflow): void {
|
|
16
|
-
workflows.set(workflow.name, workflow)
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
/** Look up a workflow definition, throwing if it is not registered. */
|
|
20
|
-
get(name: string): DurableWorkflow {
|
|
21
|
-
const wf = workflows.get(name)
|
|
22
|
-
if (!wf) throw new WorkflowNotRegisteredError(name)
|
|
23
|
-
return wf
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
/** Whether a workflow name is registered. */
|
|
27
|
-
has(name: string): boolean {
|
|
28
|
-
return workflows.has(name)
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
/** Clear all registrations. For testing only. */
|
|
32
|
-
reset(): void {
|
|
33
|
-
workflows.clear()
|
|
34
|
-
},
|
|
35
|
-
}
|
package/src/schema.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { sql } from '@strav/database'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Create the durable engine's two tables if they do not exist.
|
|
5
|
-
*
|
|
6
|
-
* Follows the `@strav/queue` precedent — inline, idempotent DDL rather than
|
|
7
|
-
* a migration file (apps own their migrations directory). Safe to call on
|
|
8
|
-
* every boot. Post-1.0 schema changes must be additive
|
|
9
|
-
* (`ALTER TABLE ... ADD COLUMN IF NOT EXISTS`).
|
|
10
|
-
*
|
|
11
|
-
* - `_strav_workflow_runs` — the durable record of a run; the pollable row.
|
|
12
|
-
* - `_strav_workflow_journal` — the per-step checkpoint log;
|
|
13
|
-
* `UNIQUE (run_id, step_id)` is what makes redelivery idempotent.
|
|
14
|
-
*/
|
|
15
|
-
export async function ensureTables(): Promise<void> {
|
|
16
|
-
await sql`
|
|
17
|
-
CREATE TABLE IF NOT EXISTS "_strav_workflow_runs" (
|
|
18
|
-
"id" BIGSERIAL PRIMARY KEY,
|
|
19
|
-
"workflow_name" VARCHAR(255) NOT NULL,
|
|
20
|
-
"input" JSONB NOT NULL DEFAULT '{}',
|
|
21
|
-
"status" VARCHAR(32) NOT NULL DEFAULT 'pending',
|
|
22
|
-
"state" JSONB NOT NULL DEFAULT '{}',
|
|
23
|
-
"current_step" INT NOT NULL DEFAULT 0,
|
|
24
|
-
"compensation_cursor" INT,
|
|
25
|
-
"parent_run_id" BIGINT REFERENCES "_strav_workflow_runs"("id") ON DELETE CASCADE,
|
|
26
|
-
"parent_step_id" VARCHAR(512),
|
|
27
|
-
"awaiting_signal" VARCHAR(255),
|
|
28
|
-
"wake_at" TIMESTAMPTZ,
|
|
29
|
-
"error" TEXT,
|
|
30
|
-
"result" JSONB,
|
|
31
|
-
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
32
|
-
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
33
|
-
)
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
await sql`
|
|
37
|
-
CREATE INDEX IF NOT EXISTS "idx_strav_wf_runs_status"
|
|
38
|
-
ON "_strav_workflow_runs" ("status")
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
await sql`
|
|
42
|
-
CREATE INDEX IF NOT EXISTS "idx_strav_wf_runs_parent"
|
|
43
|
-
ON "_strav_workflow_runs" ("parent_run_id")
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
await sql`
|
|
47
|
-
CREATE TABLE IF NOT EXISTS "_strav_workflow_journal" (
|
|
48
|
-
"id" BIGSERIAL PRIMARY KEY,
|
|
49
|
-
"run_id" BIGINT NOT NULL REFERENCES "_strav_workflow_runs"("id") ON DELETE CASCADE,
|
|
50
|
-
"step_id" VARCHAR(512) NOT NULL,
|
|
51
|
-
"status" VARCHAR(16) NOT NULL DEFAULT 'completed',
|
|
52
|
-
"result" JSONB,
|
|
53
|
-
"error" TEXT,
|
|
54
|
-
"attempt" INT NOT NULL DEFAULT 1,
|
|
55
|
-
"completed_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
56
|
-
UNIQUE ("run_id", "step_id")
|
|
57
|
-
)
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
await sql`
|
|
61
|
-
CREATE INDEX IF NOT EXISTS "idx_strav_wf_journal_run"
|
|
62
|
-
ON "_strav_workflow_journal" ("run_id")
|
|
63
|
-
`
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Drop the durable engine's tables. For testing only. */
|
|
67
|
-
export async function dropTables(): Promise<void> {
|
|
68
|
-
await sql`DROP TABLE IF EXISTS "_strav_workflow_journal"`
|
|
69
|
-
await sql`DROP TABLE IF EXISTS "_strav_workflow_runs"`
|
|
70
|
-
}
|
package/src/util.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse a JSONB column value. Bun's SQL driver may return a JSONB column as
|
|
3
|
-
* either an already-parsed object or a raw string depending on context — this
|
|
4
|
-
* normalizes both. `null`/`undefined` pass through unchanged.
|
|
5
|
-
*/
|
|
6
|
-
export function parseJson<T = unknown>(value: unknown): T {
|
|
7
|
-
if (value == null) return value as T
|
|
8
|
-
if (typeof value === 'string') {
|
|
9
|
-
try {
|
|
10
|
-
return JSON.parse(value) as T
|
|
11
|
-
} catch {
|
|
12
|
-
return value as T
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return value as T
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Compute the retry backoff delay (ms) for a given attempt number. */
|
|
19
|
-
export function backoffDelay(
|
|
20
|
-
attempt: number,
|
|
21
|
-
strategy: 'exponential' | 'linear'
|
|
22
|
-
): number {
|
|
23
|
-
if (strategy === 'linear') return attempt * 5_000
|
|
24
|
-
return Math.pow(2, attempt) * 1_000 + Math.random() * 1_000
|
|
25
|
-
}
|