@smthrs/run-store 0.0.0-stage → 1.0.0-rc.4
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/CHANGELOG.md +112 -0
- package/LICENSE +21 -0
- package/README.md +195 -2
- package/dist/cjs/AttemptStore.d.ts +359 -0
- package/dist/cjs/AttemptStore.d.ts.map +1 -0
- package/dist/cjs/AttemptStore.js +731 -0
- package/dist/cjs/AttemptStore.js.map +7 -0
- package/dist/cjs/Heartbeat.d.ts +46 -0
- package/dist/cjs/Heartbeat.d.ts.map +1 -0
- package/dist/cjs/Heartbeat.js +28 -0
- package/dist/cjs/Heartbeat.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +42 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +55 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/Ownership.d.ts +275 -0
- package/dist/cjs/Ownership.d.ts.map +1 -0
- package/dist/cjs/Ownership.js +129 -0
- package/dist/cjs/Ownership.js.map +7 -0
- package/dist/cjs/RunStore.d.ts +571 -0
- package/dist/cjs/RunStore.d.ts.map +1 -0
- package/dist/cjs/RunStore.js +1079 -0
- package/dist/cjs/RunStore.js.map +7 -0
- package/dist/cjs/RunStoreMetrics.d.ts +151 -0
- package/dist/cjs/RunStoreMetrics.d.ts.map +1 -0
- package/dist/cjs/RunStoreMetrics.js +91 -0
- package/dist/cjs/RunStoreMetrics.js.map +7 -0
- package/dist/cjs/index.d.ts +47 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Boundary.d.ts +80 -0
- package/dist/cjs/internal/Boundary.d.ts.map +1 -0
- package/dist/cjs/internal/Boundary.js +92 -0
- package/dist/cjs/internal/Boundary.js.map +7 -0
- package/dist/cjs/internal/Companion.d.ts +44 -0
- package/dist/cjs/internal/Companion.d.ts.map +1 -0
- package/dist/cjs/internal/Companion.js +50 -0
- package/dist/cjs/internal/Companion.js.map +7 -0
- package/dist/cjs/internal/SpanOutcome.d.ts +37 -0
- package/dist/cjs/internal/SpanOutcome.d.ts.map +1 -0
- package/dist/cjs/internal/SpanOutcome.js +62 -0
- package/dist/cjs/internal/SpanOutcome.js.map +7 -0
- package/dist/cjs/migrations/0001_initial.d.ts +15 -0
- package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_initial.js +110 -0
- package/dist/cjs/migrations/0001_initial.js.map +7 -0
- package/dist/cjs/migrations/0002_lineage.d.ts +27 -0
- package/dist/cjs/migrations/0002_lineage.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_lineage.js +43 -0
- package/dist/cjs/migrations/0002_lineage.js.map +7 -0
- package/dist/cjs/migrations/0003_execution_revisions.d.ts +15 -0
- package/dist/cjs/migrations/0003_execution_revisions.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_execution_revisions.js +77 -0
- package/dist/cjs/migrations/0003_execution_revisions.js.map +7 -0
- package/dist/cjs/migrations/0004_waiting_request.d.ts +32 -0
- package/dist/cjs/migrations/0004_waiting_request.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_waiting_request.js +42 -0
- package/dist/cjs/migrations/0004_waiting_request.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestRunStore.d.ts +17 -0
- package/dist/cjs/test/TestRunStore.d.ts.map +1 -0
- package/dist/cjs/test/TestRunStore.js +42 -0
- package/dist/cjs/test/TestRunStore.js.map +7 -0
- package/dist/esm/AttemptStore.d.ts +359 -0
- package/dist/esm/AttemptStore.d.ts.map +1 -0
- package/dist/esm/AttemptStore.js +835 -0
- package/dist/esm/AttemptStore.js.map +1 -0
- package/dist/esm/Heartbeat.d.ts +46 -0
- package/dist/esm/Heartbeat.d.ts.map +1 -0
- package/dist/esm/Heartbeat.js +46 -0
- package/dist/esm/Heartbeat.js.map +1 -0
- package/dist/esm/Migrations.d.ts +42 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +56 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/Ownership.d.ts +275 -0
- package/dist/esm/Ownership.d.ts.map +1 -0
- package/dist/esm/Ownership.js +299 -0
- package/dist/esm/Ownership.js.map +1 -0
- package/dist/esm/RunStore.d.ts +571 -0
- package/dist/esm/RunStore.d.ts.map +1 -0
- package/dist/esm/RunStore.js +1287 -0
- package/dist/esm/RunStore.js.map +1 -0
- package/dist/esm/RunStoreMetrics.d.ts +151 -0
- package/dist/esm/RunStoreMetrics.d.ts.map +1 -0
- package/dist/esm/RunStoreMetrics.js +120 -0
- package/dist/esm/RunStoreMetrics.js.map +1 -0
- package/dist/esm/index.d.ts +47 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +47 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Boundary.d.ts +80 -0
- package/dist/esm/internal/Boundary.d.ts.map +1 -0
- package/dist/esm/internal/Boundary.js +109 -0
- package/dist/esm/internal/Boundary.js.map +1 -0
- package/dist/esm/internal/Companion.d.ts +44 -0
- package/dist/esm/internal/Companion.d.ts.map +1 -0
- package/dist/esm/internal/Companion.js +53 -0
- package/dist/esm/internal/Companion.js.map +1 -0
- package/dist/esm/internal/SpanOutcome.d.ts +37 -0
- package/dist/esm/internal/SpanOutcome.d.ts.map +1 -0
- package/dist/esm/internal/SpanOutcome.js +48 -0
- package/dist/esm/internal/SpanOutcome.js.map +1 -0
- package/dist/esm/migrations/0001_initial.d.ts +15 -0
- package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
- package/dist/esm/migrations/0001_initial.js +88 -0
- package/dist/esm/migrations/0001_initial.js.map +1 -0
- package/dist/esm/migrations/0002_lineage.d.ts +27 -0
- package/dist/esm/migrations/0002_lineage.d.ts.map +1 -0
- package/dist/esm/migrations/0002_lineage.js +33 -0
- package/dist/esm/migrations/0002_lineage.js.map +1 -0
- package/dist/esm/migrations/0003_execution_revisions.d.ts +15 -0
- package/dist/esm/migrations/0003_execution_revisions.d.ts.map +1 -0
- package/dist/esm/migrations/0003_execution_revisions.js +58 -0
- package/dist/esm/migrations/0003_execution_revisions.js.map +1 -0
- package/dist/esm/migrations/0004_waiting_request.d.ts +32 -0
- package/dist/esm/migrations/0004_waiting_request.d.ts.map +1 -0
- package/dist/esm/migrations/0004_waiting_request.js +37 -0
- package/dist/esm/migrations/0004_waiting_request.js.map +1 -0
- package/dist/esm/test/TestRunStore.d.ts +17 -0
- package/dist/esm/test/TestRunStore.d.ts.map +1 -0
- package/dist/esm/test/TestRunStore.js +19 -0
- package/dist/esm/test/TestRunStore.js.map +1 -0
- package/docs/README.md +142 -0
- package/docs/api.md +901 -0
- package/docs/concepts/durable-values.md +140 -0
- package/docs/concepts/execution-revisions.md +53 -0
- package/docs/concepts/fencing.md +146 -0
- package/docs/concepts/leases.md +154 -0
- package/docs/concepts/liveness-evidence.md +142 -0
- package/docs/guides/cancel-a-run.md +156 -0
- package/docs/guides/claim-and-finish-a-run.md +175 -0
- package/docs/guides/compose-the-stores.md +142 -0
- package/docs/guides/observe-outcomes.md +182 -0
- package/docs/guides/record-step-attempts.md +170 -0
- package/docs/guides/recover-a-stalled-run.md +173 -0
- package/docs/guides/testing.md +141 -0
- package/docs/installation.md +109 -0
- package/docs/quickstart.md +201 -0
- package/docs/troubleshooting.md +255 -0
- package/package.json +183 -3
- package/src/AttemptStore.ts +1195 -0
- package/src/Heartbeat.ts +47 -0
- package/src/Migrations.ts +59 -0
- package/src/Ownership.ts +407 -0
- package/src/RunStore.ts +2063 -0
- package/src/RunStoreMetrics.ts +165 -0
- package/src/index.ts +51 -0
- package/src/internal/Boundary.ts +130 -0
- package/src/internal/Companion.ts +64 -0
- package/src/internal/SpanOutcome.ts +71 -0
- package/src/migrations/0001_initial.ts +114 -0
- package/src/migrations/0002_lineage.ts +38 -0
- package/src/migrations/0003_execution_revisions.ts +69 -0
- package/src/migrations/0004_waiting_request.ts +39 -0
- package/src/test/TestRunStore.ts +22 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable database identity and coalesced execution change tracking.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Dialect from "@smthrs/database/Dialect"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as SqlClient from "effect/unstable/sql/SqlClient"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Retains one current revision per run ID, including deleted IDs forever.
|
|
13
|
+
* Triggers participate in the mutating transaction, including raw SQL writes.
|
|
14
|
+
* @category migrations
|
|
15
|
+
* @since 1.0.0
|
|
16
|
+
*/
|
|
17
|
+
export const executionRevisions: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {
|
|
18
|
+
const sql = yield* SqlClient.SqlClient
|
|
19
|
+
yield* sql`CREATE TABLE flows_run_source (
|
|
20
|
+
singleton ${Dialect.integer(sql)} PRIMARY KEY CHECK (singleton = 1),
|
|
21
|
+
source TEXT NOT NULL CHECK (length(source) = 32 AND ${
|
|
22
|
+
sql.literal(Dialect.isPostgres(sql) ? "source !~ '[^0-9a-f]'" : "source NOT GLOB '*[^0-9a-f]*'")
|
|
23
|
+
}),
|
|
24
|
+
revision ${Dialect.integer(sql)} NOT NULL CHECK (${
|
|
25
|
+
Dialect.isInteger(sql, sql`revision`)
|
|
26
|
+
} AND revision >= 0 AND revision <= 9007199254740991)
|
|
27
|
+
)`
|
|
28
|
+
yield* sql`INSERT INTO flows_run_source VALUES (1, ${
|
|
29
|
+
sql.literal(Dialect.isPostgres(sql) ? "replace(gen_random_uuid()::text, '-', '')" : "lower(hex(randomblob(16)))")
|
|
30
|
+
}, 0)`
|
|
31
|
+
yield* sql`CREATE TABLE flows_run_changes (
|
|
32
|
+
run_id TEXT PRIMARY KEY NOT NULL CHECK (length(run_id) > 0),
|
|
33
|
+
revision ${Dialect.integer(sql)} NOT NULL UNIQUE CHECK (${
|
|
34
|
+
Dialect.isInteger(sql, sql`revision`)
|
|
35
|
+
} AND revision > 0 AND revision <= 9007199254740991),
|
|
36
|
+
deleted ${Dialect.integer(sql)} NOT NULL CHECK (deleted IN (0, 1))
|
|
37
|
+
)`
|
|
38
|
+
yield* sql`ALTER TABLE flows_runs ADD COLUMN cancel_acknowledgement_json TEXT
|
|
39
|
+
CHECK (cancel_acknowledgement_json IS NULL OR ${Dialect.jsonValid(sql, sql`cancel_acknowledgement_json`)})`
|
|
40
|
+
// Old rows receive a real baseline revision; their prior mutation order is unknown.
|
|
41
|
+
yield* sql`INSERT INTO flows_run_changes (run_id, revision, deleted)
|
|
42
|
+
SELECT run_id, ROW_NUMBER() OVER (ORDER BY created_at_ms, run_id), 0 FROM flows_runs`
|
|
43
|
+
yield* sql`UPDATE flows_run_source SET revision = (SELECT COUNT(*) FROM flows_run_changes)`
|
|
44
|
+
for (const [operation, row, deleted] of [["INSERT", "NEW", 0], ["UPDATE", "NEW", 0], ["DELETE", "OLD", 1]] as const) {
|
|
45
|
+
yield* Dialect.trigger(sql, {
|
|
46
|
+
name: `flows_runs_revision_${operation.toLowerCase()}`,
|
|
47
|
+
table: "flows_runs",
|
|
48
|
+
event: `AFTER ${operation}`,
|
|
49
|
+
body: `
|
|
50
|
+
${
|
|
51
|
+
Dialect.isPostgres(sql)
|
|
52
|
+
? "IF NOT EXISTS (SELECT 1 FROM flows_run_source WHERE singleton = 1) THEN RAISE EXCEPTION 'execution source identity is missing' USING ERRCODE = '23514'; END IF;"
|
|
53
|
+
: "SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM flows_run_source WHERE singleton = 1) THEN RAISE(ABORT, 'execution source identity is missing') END;"
|
|
54
|
+
}
|
|
55
|
+
UPDATE flows_run_source SET revision = revision + 1 WHERE singleton = 1;
|
|
56
|
+
INSERT INTO flows_run_changes (run_id, revision, deleted)
|
|
57
|
+
SELECT ${row}.run_id, revision, ${deleted} FROM flows_run_source WHERE singleton = 1
|
|
58
|
+
ON CONFLICT (run_id) DO UPDATE SET revision = excluded.revision, deleted = excluded.deleted;
|
|
59
|
+
`
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
yield* Dialect.trigger(sql, {
|
|
63
|
+
name: `flows_runs_identity_immutable`,
|
|
64
|
+
table: `flows_runs`,
|
|
65
|
+
event: "BEFORE UPDATE OF run_id",
|
|
66
|
+
when: `NEW.run_id <> OLD.run_id`,
|
|
67
|
+
reject: "run identity is immutable"
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The question a parked run is waiting on an answer to.
|
|
3
|
+
*
|
|
4
|
+
* `waiting_reason` and `waiting_token` say THAT a run is parked on a human
|
|
5
|
+
* decision and which wait point holds it; neither says what was asked. A
|
|
6
|
+
* `HumanTask` park therefore left the prompt, the kind of answer it wants, and
|
|
7
|
+
* the attempt budget nowhere a reader could find them: the question lived only
|
|
8
|
+
* in the action payload of an execution that had already released its owner,
|
|
9
|
+
* so the approvals inbox could describe the gate as "something is waiting" and
|
|
10
|
+
* no more (run-3, `coding-clarification`).
|
|
11
|
+
*
|
|
12
|
+
* This column is that missing half. It is the JSON the wait declared about
|
|
13
|
+
* itself — for `HumanTask`, `{kind, name, prompt, attempt, maxAttempts}` — and
|
|
14
|
+
* it is written and cleared with the other three waiting columns, so it can
|
|
15
|
+
* never describe a wait the run is no longer holding.
|
|
16
|
+
*
|
|
17
|
+
* Append-only, as every migration after the first must be: the column is
|
|
18
|
+
* nullable, so every row an earlier build wrote stays valid and reads back as
|
|
19
|
+
* a park that declared no question.
|
|
20
|
+
*
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as Dialect from "@smthrs/database/Dialect"
|
|
25
|
+
import * as Effect from "effect/Effect"
|
|
26
|
+
import * as SqlClient from "effect/unstable/sql/SqlClient"
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Adds `waiting_request` to `flows_runs`.
|
|
30
|
+
*
|
|
31
|
+
* @category migrations
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
export const waitingRequest: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {
|
|
35
|
+
const sql = yield* SqlClient.SqlClient
|
|
36
|
+
|
|
37
|
+
yield* sql`ALTER TABLE flows_runs ADD COLUMN waiting_request TEXT
|
|
38
|
+
CHECK (waiting_request IS NULL OR ${Dialect.jsonValid(sql, sql`waiting_request`)})`
|
|
39
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic bundle of the production run and attempt services.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as TestDatabase from "@smthrs/database/test/TestDatabase"
|
|
8
|
+
import * as Layer from "effect/Layer"
|
|
9
|
+
import * as AttemptStore from "../AttemptStore.ts"
|
|
10
|
+
import * as Migrations from "../Migrations.ts"
|
|
11
|
+
import * as RunStore from "../RunStore.ts"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Provides the production SQLite run and attempt stores over an in-memory
|
|
15
|
+
* database. Migrations run before either service is exposed.
|
|
16
|
+
*
|
|
17
|
+
* @category layers
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
*/
|
|
20
|
+
export const layer = Layer.mergeAll(RunStore.layer, AttemptStore.layer).pipe(
|
|
21
|
+
Layer.provide(Layer.provideMerge(Migrations.layer, TestDatabase.layer))
|
|
22
|
+
)
|