@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.
Files changed (157) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -2
  4. package/dist/cjs/AttemptStore.d.ts +359 -0
  5. package/dist/cjs/AttemptStore.d.ts.map +1 -0
  6. package/dist/cjs/AttemptStore.js +731 -0
  7. package/dist/cjs/AttemptStore.js.map +7 -0
  8. package/dist/cjs/Heartbeat.d.ts +46 -0
  9. package/dist/cjs/Heartbeat.d.ts.map +1 -0
  10. package/dist/cjs/Heartbeat.js +28 -0
  11. package/dist/cjs/Heartbeat.js.map +7 -0
  12. package/dist/cjs/Migrations.d.ts +42 -0
  13. package/dist/cjs/Migrations.d.ts.map +1 -0
  14. package/dist/cjs/Migrations.js +55 -0
  15. package/dist/cjs/Migrations.js.map +7 -0
  16. package/dist/cjs/Ownership.d.ts +275 -0
  17. package/dist/cjs/Ownership.d.ts.map +1 -0
  18. package/dist/cjs/Ownership.js +129 -0
  19. package/dist/cjs/Ownership.js.map +7 -0
  20. package/dist/cjs/RunStore.d.ts +571 -0
  21. package/dist/cjs/RunStore.d.ts.map +1 -0
  22. package/dist/cjs/RunStore.js +1079 -0
  23. package/dist/cjs/RunStore.js.map +7 -0
  24. package/dist/cjs/RunStoreMetrics.d.ts +151 -0
  25. package/dist/cjs/RunStoreMetrics.d.ts.map +1 -0
  26. package/dist/cjs/RunStoreMetrics.js +91 -0
  27. package/dist/cjs/RunStoreMetrics.js.map +7 -0
  28. package/dist/cjs/index.d.ts +47 -0
  29. package/dist/cjs/index.d.ts.map +1 -0
  30. package/dist/cjs/index.js +43 -0
  31. package/dist/cjs/index.js.map +7 -0
  32. package/dist/cjs/internal/Boundary.d.ts +80 -0
  33. package/dist/cjs/internal/Boundary.d.ts.map +1 -0
  34. package/dist/cjs/internal/Boundary.js +92 -0
  35. package/dist/cjs/internal/Boundary.js.map +7 -0
  36. package/dist/cjs/internal/Companion.d.ts +44 -0
  37. package/dist/cjs/internal/Companion.d.ts.map +1 -0
  38. package/dist/cjs/internal/Companion.js +50 -0
  39. package/dist/cjs/internal/Companion.js.map +7 -0
  40. package/dist/cjs/internal/SpanOutcome.d.ts +37 -0
  41. package/dist/cjs/internal/SpanOutcome.d.ts.map +1 -0
  42. package/dist/cjs/internal/SpanOutcome.js +62 -0
  43. package/dist/cjs/internal/SpanOutcome.js.map +7 -0
  44. package/dist/cjs/migrations/0001_initial.d.ts +15 -0
  45. package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
  46. package/dist/cjs/migrations/0001_initial.js +110 -0
  47. package/dist/cjs/migrations/0001_initial.js.map +7 -0
  48. package/dist/cjs/migrations/0002_lineage.d.ts +27 -0
  49. package/dist/cjs/migrations/0002_lineage.d.ts.map +1 -0
  50. package/dist/cjs/migrations/0002_lineage.js +43 -0
  51. package/dist/cjs/migrations/0002_lineage.js.map +7 -0
  52. package/dist/cjs/migrations/0003_execution_revisions.d.ts +15 -0
  53. package/dist/cjs/migrations/0003_execution_revisions.d.ts.map +1 -0
  54. package/dist/cjs/migrations/0003_execution_revisions.js +77 -0
  55. package/dist/cjs/migrations/0003_execution_revisions.js.map +7 -0
  56. package/dist/cjs/migrations/0004_waiting_request.d.ts +32 -0
  57. package/dist/cjs/migrations/0004_waiting_request.d.ts.map +1 -0
  58. package/dist/cjs/migrations/0004_waiting_request.js +42 -0
  59. package/dist/cjs/migrations/0004_waiting_request.js.map +7 -0
  60. package/dist/cjs/package.json +1 -0
  61. package/dist/cjs/test/TestRunStore.d.ts +17 -0
  62. package/dist/cjs/test/TestRunStore.d.ts.map +1 -0
  63. package/dist/cjs/test/TestRunStore.js +42 -0
  64. package/dist/cjs/test/TestRunStore.js.map +7 -0
  65. package/dist/esm/AttemptStore.d.ts +359 -0
  66. package/dist/esm/AttemptStore.d.ts.map +1 -0
  67. package/dist/esm/AttemptStore.js +835 -0
  68. package/dist/esm/AttemptStore.js.map +1 -0
  69. package/dist/esm/Heartbeat.d.ts +46 -0
  70. package/dist/esm/Heartbeat.d.ts.map +1 -0
  71. package/dist/esm/Heartbeat.js +46 -0
  72. package/dist/esm/Heartbeat.js.map +1 -0
  73. package/dist/esm/Migrations.d.ts +42 -0
  74. package/dist/esm/Migrations.d.ts.map +1 -0
  75. package/dist/esm/Migrations.js +56 -0
  76. package/dist/esm/Migrations.js.map +1 -0
  77. package/dist/esm/Ownership.d.ts +275 -0
  78. package/dist/esm/Ownership.d.ts.map +1 -0
  79. package/dist/esm/Ownership.js +299 -0
  80. package/dist/esm/Ownership.js.map +1 -0
  81. package/dist/esm/RunStore.d.ts +571 -0
  82. package/dist/esm/RunStore.d.ts.map +1 -0
  83. package/dist/esm/RunStore.js +1287 -0
  84. package/dist/esm/RunStore.js.map +1 -0
  85. package/dist/esm/RunStoreMetrics.d.ts +151 -0
  86. package/dist/esm/RunStoreMetrics.d.ts.map +1 -0
  87. package/dist/esm/RunStoreMetrics.js +120 -0
  88. package/dist/esm/RunStoreMetrics.js.map +1 -0
  89. package/dist/esm/index.d.ts +47 -0
  90. package/dist/esm/index.d.ts.map +1 -0
  91. package/dist/esm/index.js +47 -0
  92. package/dist/esm/index.js.map +1 -0
  93. package/dist/esm/internal/Boundary.d.ts +80 -0
  94. package/dist/esm/internal/Boundary.d.ts.map +1 -0
  95. package/dist/esm/internal/Boundary.js +109 -0
  96. package/dist/esm/internal/Boundary.js.map +1 -0
  97. package/dist/esm/internal/Companion.d.ts +44 -0
  98. package/dist/esm/internal/Companion.d.ts.map +1 -0
  99. package/dist/esm/internal/Companion.js +53 -0
  100. package/dist/esm/internal/Companion.js.map +1 -0
  101. package/dist/esm/internal/SpanOutcome.d.ts +37 -0
  102. package/dist/esm/internal/SpanOutcome.d.ts.map +1 -0
  103. package/dist/esm/internal/SpanOutcome.js +48 -0
  104. package/dist/esm/internal/SpanOutcome.js.map +1 -0
  105. package/dist/esm/migrations/0001_initial.d.ts +15 -0
  106. package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
  107. package/dist/esm/migrations/0001_initial.js +88 -0
  108. package/dist/esm/migrations/0001_initial.js.map +1 -0
  109. package/dist/esm/migrations/0002_lineage.d.ts +27 -0
  110. package/dist/esm/migrations/0002_lineage.d.ts.map +1 -0
  111. package/dist/esm/migrations/0002_lineage.js +33 -0
  112. package/dist/esm/migrations/0002_lineage.js.map +1 -0
  113. package/dist/esm/migrations/0003_execution_revisions.d.ts +15 -0
  114. package/dist/esm/migrations/0003_execution_revisions.d.ts.map +1 -0
  115. package/dist/esm/migrations/0003_execution_revisions.js +58 -0
  116. package/dist/esm/migrations/0003_execution_revisions.js.map +1 -0
  117. package/dist/esm/migrations/0004_waiting_request.d.ts +32 -0
  118. package/dist/esm/migrations/0004_waiting_request.d.ts.map +1 -0
  119. package/dist/esm/migrations/0004_waiting_request.js +37 -0
  120. package/dist/esm/migrations/0004_waiting_request.js.map +1 -0
  121. package/dist/esm/test/TestRunStore.d.ts +17 -0
  122. package/dist/esm/test/TestRunStore.d.ts.map +1 -0
  123. package/dist/esm/test/TestRunStore.js +19 -0
  124. package/dist/esm/test/TestRunStore.js.map +1 -0
  125. package/docs/README.md +142 -0
  126. package/docs/api.md +901 -0
  127. package/docs/concepts/durable-values.md +140 -0
  128. package/docs/concepts/execution-revisions.md +53 -0
  129. package/docs/concepts/fencing.md +146 -0
  130. package/docs/concepts/leases.md +154 -0
  131. package/docs/concepts/liveness-evidence.md +142 -0
  132. package/docs/guides/cancel-a-run.md +156 -0
  133. package/docs/guides/claim-and-finish-a-run.md +175 -0
  134. package/docs/guides/compose-the-stores.md +142 -0
  135. package/docs/guides/observe-outcomes.md +182 -0
  136. package/docs/guides/record-step-attempts.md +170 -0
  137. package/docs/guides/recover-a-stalled-run.md +173 -0
  138. package/docs/guides/testing.md +141 -0
  139. package/docs/installation.md +109 -0
  140. package/docs/quickstart.md +201 -0
  141. package/docs/troubleshooting.md +255 -0
  142. package/package.json +183 -3
  143. package/src/AttemptStore.ts +1195 -0
  144. package/src/Heartbeat.ts +47 -0
  145. package/src/Migrations.ts +59 -0
  146. package/src/Ownership.ts +407 -0
  147. package/src/RunStore.ts +2063 -0
  148. package/src/RunStoreMetrics.ts +165 -0
  149. package/src/index.ts +51 -0
  150. package/src/internal/Boundary.ts +130 -0
  151. package/src/internal/Companion.ts +64 -0
  152. package/src/internal/SpanOutcome.ts +71 -0
  153. package/src/migrations/0001_initial.ts +114 -0
  154. package/src/migrations/0002_lineage.ts +38 -0
  155. package/src/migrations/0003_execution_revisions.ts +69 -0
  156. package/src/migrations/0004_waiting_request.ts +39 -0
  157. package/src/test/TestRunStore.ts +22 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Trampoline lineage columns on `flows_runs`.
3
+ *
4
+ * A round is its own run row, chained to the previous round through the `parent_run_id`
5
+ * column this table already reserved for continue-as-new, and these two
6
+ * columns are what make the chain readable as a lineage rather than as an
7
+ * anonymous ancestry: which lineage a round belongs to, and which round of it
8
+ * this is. They are NOT `flows_run_parents` edges — that table is the subflow
9
+ * DAG cycle detection walks, and a lineage is a chain of successive
10
+ * executions of one run rather than a parent spawning a child.
11
+ *
12
+ * Append-only, as every migration after the first must be: both columns are
13
+ * nullable, so every row an earlier build wrote stays valid and reads back as
14
+ * "round 0 of its own lineage".
15
+ *
16
+ * @since 0.1.0
17
+ */
18
+ import * as Effect from "effect/Effect";
19
+ import * as SqlClient from "effect/unstable/sql/SqlClient";
20
+ /**
21
+ * Adds `lineage_id` and `round_ordinal` to `flows_runs` and indexes the pair.
22
+ *
23
+ * @category migrations
24
+ * @since 0.1.0
25
+ */
26
+ export declare const lineage: Effect.Effect<void, unknown, SqlClient.SqlClient>;
27
+ //# sourceMappingURL=0002_lineage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0002_lineage.d.ts","sourceRoot":"","sources":["../../../src/migrations/0002_lineage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,+BAA+B,CAAA;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CASpE,CAAA"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var lineage_exports = {};
30
+ __export(lineage_exports, {
31
+ lineage: () => lineage
32
+ });
33
+ module.exports = __toCommonJS(lineage_exports);
34
+ var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
35
+ var Effect = __toESM(require("effect/Effect"), 1);
36
+ var SqlClient = __toESM(require("effect/unstable/sql/SqlClient"), 1);
37
+ const lineage = Effect.gen(function* () {
38
+ const sql = yield* SqlClient.SqlClient;
39
+ yield* sql`ALTER TABLE flows_runs ADD COLUMN lineage_id TEXT CHECK (lineage_id IS NULL OR length(lineage_id) > 0)`;
40
+ yield* sql`ALTER TABLE flows_runs ADD COLUMN round_ordinal ${Dialect.integer(sql)} CHECK (round_ordinal IS NULL OR (${Dialect.isInteger(sql, sql`round_ordinal`)} AND round_ordinal >= 0 AND round_ordinal <= 9007199254740991))`;
41
+ yield* sql`CREATE UNIQUE INDEX flows_runs_lineage_idx ON flows_runs (lineage_id, round_ordinal)`;
42
+ });
43
+ //# sourceMappingURL=0002_lineage.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/migrations/0002_lineage.ts"],
4
+ "sourcesContent": ["/**\n * Trampoline lineage columns on `flows_runs`.\n *\n * A round is its own run row, chained to the previous round through the `parent_run_id`\n * column this table already reserved for continue-as-new, and these two\n * columns are what make the chain readable as a lineage rather than as an\n * anonymous ancestry: which lineage a round belongs to, and which round of it\n * this is. They are NOT `flows_run_parents` edges \u2014 that table is the subflow\n * DAG cycle detection walks, and a lineage is a chain of successive\n * executions of one run rather than a parent spawning a child.\n *\n * Append-only, as every migration after the first must be: both columns are\n * nullable, so every row an earlier build wrote stays valid and reads back as\n * \"round 0 of its own lineage\".\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\n\n/**\n * Adds `lineage_id` and `round_ordinal` to `flows_runs` and indexes the pair.\n *\n * @category migrations\n * @since 0.1.0\n */\nexport const lineage: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n\n yield* sql`ALTER TABLE flows_runs ADD COLUMN lineage_id TEXT CHECK (lineage_id IS NULL OR length(lineage_id) > 0)`\n yield* sql`ALTER TABLE flows_runs ADD COLUMN round_ordinal ${Dialect.integer(sql)} CHECK (round_ordinal IS NULL OR (${\n Dialect.isInteger(sql, sql`round_ordinal`)\n } AND round_ordinal >= 0 AND round_ordinal <= 9007199254740991))`\n\n yield* sql`CREATE UNIQUE INDEX flows_runs_lineage_idx ON flows_runs (lineage_id, round_ordinal)`\n})\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,UAA6D,OAAO,IAAI,aAAY;AAC/F,QAAM,MAAM,OAAO,UAAU;AAE7B,SAAO;AACP,SAAO,sDAAsD,QAAQ,QAAQ,GAAG,CAAC,qCAC/E,QAAQ,UAAU,KAAK,kBAAkB,CAC3C;AAEA,SAAO;AACT,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Durable database identity and coalesced execution change tracking.
3
+ *
4
+ * @since 1.0.0
5
+ */
6
+ import * as Effect from "effect/Effect";
7
+ import * as SqlClient from "effect/unstable/sql/SqlClient";
8
+ /**
9
+ * Retains one current revision per run ID, including deleted IDs forever.
10
+ * Triggers participate in the mutating transaction, including raw SQL writes.
11
+ * @category migrations
12
+ * @since 1.0.0
13
+ */
14
+ export declare const executionRevisions: Effect.Effect<void, unknown, SqlClient.SqlClient>;
15
+ //# sourceMappingURL=0003_execution_revisions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0003_execution_revisions.d.ts","sourceRoot":"","sources":["../../../src/migrations/0003_execution_revisions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,+BAA+B,CAAA;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAoD/E,CAAA"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var execution_revisions_exports = {};
30
+ __export(execution_revisions_exports, {
31
+ executionRevisions: () => executionRevisions
32
+ });
33
+ module.exports = __toCommonJS(execution_revisions_exports);
34
+ var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
35
+ var Effect = __toESM(require("effect/Effect"), 1);
36
+ var SqlClient = __toESM(require("effect/unstable/sql/SqlClient"), 1);
37
+ const executionRevisions = Effect.gen(function* () {
38
+ const sql = yield* SqlClient.SqlClient;
39
+ yield* sql`CREATE TABLE flows_run_source (
40
+ singleton ${Dialect.integer(sql)} PRIMARY KEY CHECK (singleton = 1),
41
+ source TEXT NOT NULL CHECK (length(source) = 32 AND ${sql.literal(Dialect.isPostgres(sql) ? "source !~ '[^0-9a-f]'" : "source NOT GLOB '*[^0-9a-f]*'")}),
42
+ revision ${Dialect.integer(sql)} NOT NULL CHECK (${Dialect.isInteger(sql, sql`revision`)} AND revision >= 0 AND revision <= 9007199254740991)
43
+ )`;
44
+ yield* sql`INSERT INTO flows_run_source VALUES (1, ${sql.literal(Dialect.isPostgres(sql) ? "replace(gen_random_uuid()::text, '-', '')" : "lower(hex(randomblob(16)))")}, 0)`;
45
+ yield* sql`CREATE TABLE flows_run_changes (
46
+ run_id TEXT PRIMARY KEY NOT NULL CHECK (length(run_id) > 0),
47
+ revision ${Dialect.integer(sql)} NOT NULL UNIQUE CHECK (${Dialect.isInteger(sql, sql`revision`)} AND revision > 0 AND revision <= 9007199254740991),
48
+ deleted ${Dialect.integer(sql)} NOT NULL CHECK (deleted IN (0, 1))
49
+ )`;
50
+ yield* sql`ALTER TABLE flows_runs ADD COLUMN cancel_acknowledgement_json TEXT
51
+ CHECK (cancel_acknowledgement_json IS NULL OR ${Dialect.jsonValid(sql, sql`cancel_acknowledgement_json`)})`;
52
+ yield* sql`INSERT INTO flows_run_changes (run_id, revision, deleted)
53
+ SELECT run_id, ROW_NUMBER() OVER (ORDER BY created_at_ms, run_id), 0 FROM flows_runs`;
54
+ yield* sql`UPDATE flows_run_source SET revision = (SELECT COUNT(*) FROM flows_run_changes)`;
55
+ for (const [operation, row, deleted] of [["INSERT", "NEW", 0], ["UPDATE", "NEW", 0], ["DELETE", "OLD", 1]]) {
56
+ yield* Dialect.trigger(sql, {
57
+ name: `flows_runs_revision_${operation.toLowerCase()}`,
58
+ table: "flows_runs",
59
+ event: `AFTER ${operation}`,
60
+ body: `
61
+ ${Dialect.isPostgres(sql) ? "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;" : "SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM flows_run_source WHERE singleton = 1) THEN RAISE(ABORT, 'execution source identity is missing') END;"}
62
+ UPDATE flows_run_source SET revision = revision + 1 WHERE singleton = 1;
63
+ INSERT INTO flows_run_changes (run_id, revision, deleted)
64
+ SELECT ${row}.run_id, revision, ${deleted} FROM flows_run_source WHERE singleton = 1
65
+ ON CONFLICT (run_id) DO UPDATE SET revision = excluded.revision, deleted = excluded.deleted;
66
+ `
67
+ });
68
+ }
69
+ yield* Dialect.trigger(sql, {
70
+ name: `flows_runs_identity_immutable`,
71
+ table: `flows_runs`,
72
+ event: "BEFORE UPDATE OF run_id",
73
+ when: `NEW.run_id <> OLD.run_id`,
74
+ reject: "run identity is immutable"
75
+ });
76
+ });
77
+ //# sourceMappingURL=0003_execution_revisions.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/migrations/0003_execution_revisions.ts"],
4
+ "sourcesContent": ["/**\n * Durable database identity and coalesced execution change tracking.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\n\n/**\n * Retains one current revision per run ID, including deleted IDs forever.\n * Triggers participate in the mutating transaction, including raw SQL writes.\n * @category migrations\n * @since 1.0.0\n */\nexport const executionRevisions: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n yield* sql`CREATE TABLE flows_run_source (\n singleton ${Dialect.integer(sql)} PRIMARY KEY CHECK (singleton = 1),\n source TEXT NOT NULL CHECK (length(source) = 32 AND ${\n sql.literal(Dialect.isPostgres(sql) ? \"source !~ '[^0-9a-f]'\" : \"source NOT GLOB '*[^0-9a-f]*'\")\n }),\n revision ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`revision`)\n } AND revision >= 0 AND revision <= 9007199254740991)\n )`\n yield* sql`INSERT INTO flows_run_source VALUES (1, ${\n sql.literal(Dialect.isPostgres(sql) ? \"replace(gen_random_uuid()::text, '-', '')\" : \"lower(hex(randomblob(16)))\")\n }, 0)`\n yield* sql`CREATE TABLE flows_run_changes (\n run_id TEXT PRIMARY KEY NOT NULL CHECK (length(run_id) > 0),\n revision ${Dialect.integer(sql)} NOT NULL UNIQUE CHECK (${\n Dialect.isInteger(sql, sql`revision`)\n } AND revision > 0 AND revision <= 9007199254740991),\n deleted ${Dialect.integer(sql)} NOT NULL CHECK (deleted IN (0, 1))\n )`\n yield* sql`ALTER TABLE flows_runs ADD COLUMN cancel_acknowledgement_json TEXT\n CHECK (cancel_acknowledgement_json IS NULL OR ${Dialect.jsonValid(sql, sql`cancel_acknowledgement_json`)})`\n // Old rows receive a real baseline revision; their prior mutation order is unknown.\n yield* sql`INSERT INTO flows_run_changes (run_id, revision, deleted)\n SELECT run_id, ROW_NUMBER() OVER (ORDER BY created_at_ms, run_id), 0 FROM flows_runs`\n yield* sql`UPDATE flows_run_source SET revision = (SELECT COUNT(*) FROM flows_run_changes)`\n for (const [operation, row, deleted] of [[\"INSERT\", \"NEW\", 0], [\"UPDATE\", \"NEW\", 0], [\"DELETE\", \"OLD\", 1]] as const) {\n yield* Dialect.trigger(sql, {\n name: `flows_runs_revision_${operation.toLowerCase()}`,\n table: \"flows_runs\",\n event: `AFTER ${operation}`,\n body: `\n ${\n Dialect.isPostgres(sql)\n ? \"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;\"\n : \"SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM flows_run_source WHERE singleton = 1) THEN RAISE(ABORT, 'execution source identity is missing') END;\"\n }\n UPDATE flows_run_source SET revision = revision + 1 WHERE singleton = 1;\n INSERT INTO flows_run_changes (run_id, revision, deleted)\n SELECT ${row}.run_id, revision, ${deleted} FROM flows_run_source WHERE singleton = 1\n ON CONFLICT (run_id) DO UPDATE SET revision = excluded.revision, deleted = excluded.deleted;\n `\n })\n }\n yield* Dialect.trigger(sql, {\n name: `flows_runs_identity_immutable`,\n table: `flows_runs`,\n event: \"BEFORE UPDATE OF run_id\",\n when: `NEW.run_id <> OLD.run_id`,\n reject: \"run identity is immutable\"\n })\n})\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,qBAAwE,OAAO,IAAI,aAAY;AAC1G,QAAM,MAAM,OAAO,UAAU;AAC7B,SAAO;AAAA,gBACO,QAAQ,QAAQ,GAAG,CAAC;AAAA,0DAEhC,IAAI,QAAQ,QAAQ,WAAW,GAAG,IAAI,0BAA0B,+BAA+B,CACjG;AAAA,eACa,QAAQ,QAAQ,GAAG,CAAC,oBAC/B,QAAQ,UAAU,KAAK,aAAa,CACtC;AAAA;AAEA,SAAO,8CACL,IAAI,QAAQ,QAAQ,WAAW,GAAG,IAAI,8CAA8C,4BAA4B,CAClH;AACA,SAAO;AAAA;AAAA,eAEM,QAAQ,QAAQ,GAAG,CAAC,2BAC/B,QAAQ,UAAU,KAAK,aAAa,CACtC;AAAA,cACY,QAAQ,QAAQ,GAAG,CAAC;AAAA;AAEhC,SAAO;AAAA,oDAC2C,QAAQ,UAAU,KAAK,gCAAgC,CAAC;AAE1G,SAAO;AAAA;AAEP,SAAO;AACP,aAAW,CAAC,WAAW,KAAK,OAAO,KAAK,CAAC,CAAC,UAAU,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,CAAC,CAAC,GAAY;AACnH,WAAO,QAAQ,QAAQ,KAAK;AAAA,MAC1B,MAAM,uBAAuB,UAAU,YAAY,CAAC;AAAA,MACpD,OAAO;AAAA,MACP,OAAO,SAAS,SAAS;AAAA,MACzB,MAAM;AAAA,UAEJ,QAAQ,WAAW,GAAG,IAClB,oKACA,iJACN;AAAA;AAAA;AAAA,mBAGa,GAAG,sBAAsB,OAAO;AAAA;AAAA;AAAA,IAG/C,CAAC;AAAA,EACH;AACA,SAAO,QAAQ,QAAQ,KAAK;AAAA,IAC1B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,32 @@
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
+ import * as Effect from "effect/Effect";
24
+ import * as SqlClient from "effect/unstable/sql/SqlClient";
25
+ /**
26
+ * Adds `waiting_request` to `flows_runs`.
27
+ *
28
+ * @category migrations
29
+ * @since 1.0.0
30
+ */
31
+ export declare const waitingRequest: Effect.Effect<void, unknown, SqlClient.SqlClient>;
32
+ //# sourceMappingURL=0004_waiting_request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0004_waiting_request.d.ts","sourceRoot":"","sources":["../../../src/migrations/0004_waiting_request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,+BAA+B,CAAA;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAK3E,CAAA"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var waiting_request_exports = {};
30
+ __export(waiting_request_exports, {
31
+ waitingRequest: () => waitingRequest
32
+ });
33
+ module.exports = __toCommonJS(waiting_request_exports);
34
+ var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
35
+ var Effect = __toESM(require("effect/Effect"), 1);
36
+ var SqlClient = __toESM(require("effect/unstable/sql/SqlClient"), 1);
37
+ const waitingRequest = Effect.gen(function* () {
38
+ const sql = yield* SqlClient.SqlClient;
39
+ yield* sql`ALTER TABLE flows_runs ADD COLUMN waiting_request TEXT
40
+ CHECK (waiting_request IS NULL OR ${Dialect.jsonValid(sql, sql`waiting_request`)})`;
41
+ });
42
+ //# sourceMappingURL=0004_waiting_request.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/migrations/0004_waiting_request.ts"],
4
+ "sourcesContent": ["/**\n * The question a parked run is waiting on an answer to.\n *\n * `waiting_reason` and `waiting_token` say THAT a run is parked on a human\n * decision and which wait point holds it; neither says what was asked. A\n * `HumanTask` park therefore left the prompt, the kind of answer it wants, and\n * the attempt budget nowhere a reader could find them: the question lived only\n * in the action payload of an execution that had already released its owner,\n * so the approvals inbox could describe the gate as \"something is waiting\" and\n * no more (run-3, `coding-clarification`).\n *\n * This column is that missing half. It is the JSON the wait declared about\n * itself \u2014 for `HumanTask`, `{kind, name, prompt, attempt, maxAttempts}` \u2014 and\n * it is written and cleared with the other three waiting columns, so it can\n * never describe a wait the run is no longer holding.\n *\n * Append-only, as every migration after the first must be: the column is\n * nullable, so every row an earlier build wrote stays valid and reads back as\n * a park that declared no question.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\n\n/**\n * Adds `waiting_request` to `flows_runs`.\n *\n * @category migrations\n * @since 1.0.0\n */\nexport const waitingRequest: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n\n yield* sql`ALTER TABLE flows_runs ADD COLUMN waiting_request TEXT\n CHECK (waiting_request IS NULL OR ${Dialect.jsonValid(sql, sql`waiting_request`)})`\n})\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,iBAAoE,OAAO,IAAI,aAAY;AACtG,QAAM,MAAM,OAAO,UAAU;AAE7B,SAAO;AAAA,wCAC+B,QAAQ,UAAU,KAAK,oBAAoB,CAAC;AACpF,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Deterministic bundle of the production run and attempt services.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import * as Layer from "effect/Layer";
7
+ import * as AttemptStore from "../AttemptStore.ts";
8
+ import * as RunStore from "../RunStore.ts";
9
+ /**
10
+ * Provides the production SQLite run and attempt stores over an in-memory
11
+ * database. Migrations run before either service is exposed.
12
+ *
13
+ * @category layers
14
+ * @since 0.1.0
15
+ */
16
+ export declare const layer: Layer.Layer<AttemptStore.AttemptStore | RunStore.RunStore, import("effect/unstable/sql/SqlError").SqlError | import("effect/unstable/sql/Migrator").MigrationError, never>;
17
+ //# sourceMappingURL=TestRunStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestRunStore.d.ts","sourceRoot":"","sources":["../../../src/test/TestRunStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAElD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,4KAEjB,CAAA"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var TestRunStore_exports = {};
30
+ __export(TestRunStore_exports, {
31
+ layer: () => layer
32
+ });
33
+ module.exports = __toCommonJS(TestRunStore_exports);
34
+ var TestDatabase = __toESM(require("@smthrs/database/test/TestDatabase"), 1);
35
+ var Layer = __toESM(require("effect/Layer"), 1);
36
+ var AttemptStore = __toESM(require("../AttemptStore.js"), 1);
37
+ var Migrations = __toESM(require("../Migrations.js"), 1);
38
+ var RunStore = __toESM(require("../RunStore.js"), 1);
39
+ const layer = Layer.mergeAll(RunStore.layer, AttemptStore.layer).pipe(
40
+ Layer.provide(Layer.provideMerge(Migrations.layer, TestDatabase.layer))
41
+ );
42
+ //# sourceMappingURL=TestRunStore.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/test/TestRunStore.ts"],
4
+ "sourcesContent": ["/**\n * Deterministic bundle of the production run and attempt services.\n *\n * @since 0.1.0\n */\n\nimport * as TestDatabase from \"@smthrs/database/test/TestDatabase\"\nimport * as Layer from \"effect/Layer\"\nimport * as AttemptStore from \"../AttemptStore.ts\"\nimport * as Migrations from \"../Migrations.ts\"\nimport * as RunStore from \"../RunStore.ts\"\n\n/**\n * Provides the production SQLite run and attempt stores over an in-memory\n * database. Migrations run before either service is exposed.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer = Layer.mergeAll(RunStore.layer, AttemptStore.layer).pipe(\n Layer.provide(Layer.provideMerge(Migrations.layer, TestDatabase.layer))\n)\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,mBAA8B;AAC9B,YAAuB;AACvB,mBAA8B;AAC9B,iBAA4B;AAC5B,eAA0B;AASnB,MAAM,QAAQ,MAAM,SAAS,SAAS,OAAO,aAAa,KAAK,EAAE;AAAA,EACtE,MAAM,QAAQ,MAAM,aAAa,WAAW,OAAO,aAAa,KAAK,CAAC;AACxE;",
6
+ "names": []
7
+ }