@smthrs/triggers 0.0.0-stage → 1.0.0-rc.3

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 (226) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/LICENSE +21 -0
  3. package/README.md +109 -2
  4. package/dist/cjs/CatchUp.d.ts +25 -0
  5. package/dist/cjs/CatchUp.d.ts.map +1 -0
  6. package/dist/cjs/CatchUp.js +72 -0
  7. package/dist/cjs/CatchUp.js.map +7 -0
  8. package/dist/cjs/Channel.d.ts +108 -0
  9. package/dist/cjs/Channel.d.ts.map +1 -0
  10. package/dist/cjs/Channel.js +25 -0
  11. package/dist/cjs/Channel.js.map +7 -0
  12. package/dist/cjs/ClaimDecision.d.ts +183 -0
  13. package/dist/cjs/ClaimDecision.d.ts.map +1 -0
  14. package/dist/cjs/ClaimDecision.js +144 -0
  15. package/dist/cjs/ClaimDecision.js.map +7 -0
  16. package/dist/cjs/Cron.d.ts +91 -0
  17. package/dist/cjs/Cron.d.ts.map +1 -0
  18. package/dist/cjs/Cron.js +108 -0
  19. package/dist/cjs/Cron.js.map +7 -0
  20. package/dist/cjs/DispatchReader.d.ts +77 -0
  21. package/dist/cjs/DispatchReader.d.ts.map +1 -0
  22. package/dist/cjs/DispatchReader.js +121 -0
  23. package/dist/cjs/DispatchReader.js.map +7 -0
  24. package/dist/cjs/Overlap.d.ts +40 -0
  25. package/dist/cjs/Overlap.d.ts.map +1 -0
  26. package/dist/cjs/Overlap.js +37 -0
  27. package/dist/cjs/Overlap.js.map +7 -0
  28. package/dist/cjs/Schedule.d.ts +96 -0
  29. package/dist/cjs/Schedule.d.ts.map +1 -0
  30. package/dist/cjs/Schedule.js +61 -0
  31. package/dist/cjs/Schedule.js.map +7 -0
  32. package/dist/cjs/Scheduler.d.ts +248 -0
  33. package/dist/cjs/Scheduler.d.ts.map +1 -0
  34. package/dist/cjs/Scheduler.js +647 -0
  35. package/dist/cjs/Scheduler.js.map +7 -0
  36. package/dist/cjs/SqlTriggerStore.d.ts +29 -0
  37. package/dist/cjs/SqlTriggerStore.d.ts.map +1 -0
  38. package/dist/cjs/SqlTriggerStore.js +483 -0
  39. package/dist/cjs/SqlTriggerStore.js.map +7 -0
  40. package/dist/cjs/Trigger.d.ts +81 -0
  41. package/dist/cjs/Trigger.d.ts.map +1 -0
  42. package/dist/cjs/Trigger.js +54 -0
  43. package/dist/cjs/Trigger.js.map +7 -0
  44. package/dist/cjs/TriggerError.d.ts +58 -0
  45. package/dist/cjs/TriggerError.d.ts.map +1 -0
  46. package/dist/cjs/TriggerError.js +83 -0
  47. package/dist/cjs/TriggerError.js.map +7 -0
  48. package/dist/cjs/TriggerStore.d.ts +411 -0
  49. package/dist/cjs/TriggerStore.d.ts.map +1 -0
  50. package/dist/cjs/TriggerStore.js +127 -0
  51. package/dist/cjs/TriggerStore.js.map +7 -0
  52. package/dist/cjs/Webhook.d.ts +118 -0
  53. package/dist/cjs/Webhook.d.ts.map +1 -0
  54. package/dist/cjs/Webhook.js +153 -0
  55. package/dist/cjs/Webhook.js.map +7 -0
  56. package/dist/cjs/index.d.ts +30 -0
  57. package/dist/cjs/index.d.ts.map +1 -0
  58. package/dist/cjs/index.js +57 -0
  59. package/dist/cjs/index.js.map +7 -0
  60. package/dist/cjs/internal/ActiveRuns.d.ts +51 -0
  61. package/dist/cjs/internal/ActiveRuns.d.ts.map +1 -0
  62. package/dist/cjs/internal/ActiveRuns.js +56 -0
  63. package/dist/cjs/internal/ActiveRuns.js.map +7 -0
  64. package/dist/cjs/internal/DueOccurrences.d.ts +32 -0
  65. package/dist/cjs/internal/DueOccurrences.d.ts.map +1 -0
  66. package/dist/cjs/internal/DueOccurrences.js +80 -0
  67. package/dist/cjs/internal/DueOccurrences.js.map +7 -0
  68. package/dist/cjs/internal/WallClock.d.ts +59 -0
  69. package/dist/cjs/internal/WallClock.d.ts.map +1 -0
  70. package/dist/cjs/internal/WallClock.js +132 -0
  71. package/dist/cjs/internal/WallClock.js.map +7 -0
  72. package/dist/cjs/migrations/0001_triggers.d.ts +15 -0
  73. package/dist/cjs/migrations/0001_triggers.d.ts.map +1 -0
  74. package/dist/cjs/migrations/0001_triggers.js +64 -0
  75. package/dist/cjs/migrations/0001_triggers.js.map +7 -0
  76. package/dist/cjs/migrations/0002_reservation_lease.d.ts +15 -0
  77. package/dist/cjs/migrations/0002_reservation_lease.d.ts.map +1 -0
  78. package/dist/cjs/migrations/0002_reservation_lease.js +41 -0
  79. package/dist/cjs/migrations/0002_reservation_lease.js.map +7 -0
  80. package/dist/cjs/migrations/0003_heartbeat.d.ts +16 -0
  81. package/dist/cjs/migrations/0003_heartbeat.d.ts.map +1 -0
  82. package/dist/cjs/migrations/0003_heartbeat.js +44 -0
  83. package/dist/cjs/migrations/0003_heartbeat.js.map +7 -0
  84. package/dist/cjs/migrations/0004_fire_run_index.d.ts +17 -0
  85. package/dist/cjs/migrations/0004_fire_run_index.d.ts.map +1 -0
  86. package/dist/cjs/migrations/0004_fire_run_index.js +40 -0
  87. package/dist/cjs/migrations/0004_fire_run_index.js.map +7 -0
  88. package/dist/cjs/migrations/index.d.ts +33 -0
  89. package/dist/cjs/migrations/index.d.ts.map +1 -0
  90. package/dist/cjs/migrations/index.js +50 -0
  91. package/dist/cjs/migrations/index.js.map +7 -0
  92. package/dist/cjs/package.json +1 -0
  93. package/dist/cjs/test/TestTriggers.d.ts +12 -0
  94. package/dist/cjs/test/TestTriggers.d.ts.map +1 -0
  95. package/dist/cjs/test/TestTriggers.js +421 -0
  96. package/dist/cjs/test/TestTriggers.js.map +7 -0
  97. package/dist/esm/CatchUp.d.ts +25 -0
  98. package/dist/esm/CatchUp.d.ts.map +1 -0
  99. package/dist/esm/CatchUp.js +52 -0
  100. package/dist/esm/CatchUp.js.map +1 -0
  101. package/dist/esm/Channel.d.ts +108 -0
  102. package/dist/esm/Channel.d.ts.map +1 -0
  103. package/dist/esm/Channel.js +19 -0
  104. package/dist/esm/Channel.js.map +1 -0
  105. package/dist/esm/ClaimDecision.d.ts +183 -0
  106. package/dist/esm/ClaimDecision.d.ts.map +1 -0
  107. package/dist/esm/ClaimDecision.js +172 -0
  108. package/dist/esm/ClaimDecision.js.map +1 -0
  109. package/dist/esm/Cron.d.ts +91 -0
  110. package/dist/esm/Cron.d.ts.map +1 -0
  111. package/dist/esm/Cron.js +132 -0
  112. package/dist/esm/Cron.js.map +1 -0
  113. package/dist/esm/DispatchReader.d.ts +77 -0
  114. package/dist/esm/DispatchReader.d.ts.map +1 -0
  115. package/dist/esm/DispatchReader.js +152 -0
  116. package/dist/esm/DispatchReader.js.map +1 -0
  117. package/dist/esm/Overlap.d.ts +40 -0
  118. package/dist/esm/Overlap.d.ts.map +1 -0
  119. package/dist/esm/Overlap.js +31 -0
  120. package/dist/esm/Overlap.js.map +1 -0
  121. package/dist/esm/Schedule.d.ts +96 -0
  122. package/dist/esm/Schedule.d.ts.map +1 -0
  123. package/dist/esm/Schedule.js +73 -0
  124. package/dist/esm/Schedule.js.map +1 -0
  125. package/dist/esm/Scheduler.d.ts +248 -0
  126. package/dist/esm/Scheduler.d.ts.map +1 -0
  127. package/dist/esm/Scheduler.js +734 -0
  128. package/dist/esm/Scheduler.js.map +1 -0
  129. package/dist/esm/SqlTriggerStore.d.ts +29 -0
  130. package/dist/esm/SqlTriggerStore.d.ts.map +1 -0
  131. package/dist/esm/SqlTriggerStore.js +483 -0
  132. package/dist/esm/SqlTriggerStore.js.map +1 -0
  133. package/dist/esm/Trigger.d.ts +81 -0
  134. package/dist/esm/Trigger.d.ts.map +1 -0
  135. package/dist/esm/Trigger.js +56 -0
  136. package/dist/esm/Trigger.js.map +1 -0
  137. package/dist/esm/TriggerError.d.ts +58 -0
  138. package/dist/esm/TriggerError.d.ts.map +1 -0
  139. package/dist/esm/TriggerError.js +110 -0
  140. package/dist/esm/TriggerError.js.map +1 -0
  141. package/dist/esm/TriggerStore.d.ts +411 -0
  142. package/dist/esm/TriggerStore.d.ts.map +1 -0
  143. package/dist/esm/TriggerStore.js +209 -0
  144. package/dist/esm/TriggerStore.js.map +1 -0
  145. package/dist/esm/Webhook.d.ts +118 -0
  146. package/dist/esm/Webhook.d.ts.map +1 -0
  147. package/dist/esm/Webhook.js +186 -0
  148. package/dist/esm/Webhook.js.map +1 -0
  149. package/dist/esm/index.d.ts +30 -0
  150. package/dist/esm/index.d.ts.map +1 -0
  151. package/dist/esm/index.js +30 -0
  152. package/dist/esm/index.js.map +1 -0
  153. package/dist/esm/internal/ActiveRuns.d.ts +51 -0
  154. package/dist/esm/internal/ActiveRuns.d.ts.map +1 -0
  155. package/dist/esm/internal/ActiveRuns.js +34 -0
  156. package/dist/esm/internal/ActiveRuns.js.map +1 -0
  157. package/dist/esm/internal/DueOccurrences.d.ts +32 -0
  158. package/dist/esm/internal/DueOccurrences.d.ts.map +1 -0
  159. package/dist/esm/internal/DueOccurrences.js +55 -0
  160. package/dist/esm/internal/DueOccurrences.js.map +1 -0
  161. package/dist/esm/internal/WallClock.d.ts +59 -0
  162. package/dist/esm/internal/WallClock.d.ts.map +1 -0
  163. package/dist/esm/internal/WallClock.js +155 -0
  164. package/dist/esm/internal/WallClock.js.map +1 -0
  165. package/dist/esm/migrations/0001_triggers.d.ts +15 -0
  166. package/dist/esm/migrations/0001_triggers.d.ts.map +1 -0
  167. package/dist/esm/migrations/0001_triggers.js +42 -0
  168. package/dist/esm/migrations/0001_triggers.js.map +1 -0
  169. package/dist/esm/migrations/0002_reservation_lease.d.ts +15 -0
  170. package/dist/esm/migrations/0002_reservation_lease.d.ts.map +1 -0
  171. package/dist/esm/migrations/0002_reservation_lease.js +19 -0
  172. package/dist/esm/migrations/0002_reservation_lease.js.map +1 -0
  173. package/dist/esm/migrations/0003_heartbeat.d.ts +16 -0
  174. package/dist/esm/migrations/0003_heartbeat.d.ts.map +1 -0
  175. package/dist/esm/migrations/0003_heartbeat.js +23 -0
  176. package/dist/esm/migrations/0003_heartbeat.js.map +1 -0
  177. package/dist/esm/migrations/0004_fire_run_index.d.ts +17 -0
  178. package/dist/esm/migrations/0004_fire_run_index.d.ts.map +1 -0
  179. package/dist/esm/migrations/0004_fire_run_index.js +20 -0
  180. package/dist/esm/migrations/0004_fire_run_index.js.map +1 -0
  181. package/dist/esm/migrations/index.d.ts +33 -0
  182. package/dist/esm/migrations/index.d.ts.map +1 -0
  183. package/dist/esm/migrations/index.js +37 -0
  184. package/dist/esm/migrations/index.js.map +1 -0
  185. package/dist/esm/test/TestTriggers.d.ts +12 -0
  186. package/dist/esm/test/TestTriggers.d.ts.map +1 -0
  187. package/dist/esm/test/TestTriggers.js +439 -0
  188. package/dist/esm/test/TestTriggers.js.map +1 -0
  189. package/docs/README.md +140 -0
  190. package/docs/api.md +1186 -0
  191. package/docs/concepts/channels.md +103 -0
  192. package/docs/concepts/claim-protocol.md +229 -0
  193. package/docs/concepts/policies.md +124 -0
  194. package/docs/concepts/schedules.md +134 -0
  195. package/docs/guides/choose-a-policy.md +113 -0
  196. package/docs/guides/declare-a-trigger.md +136 -0
  197. package/docs/guides/ingest-a-webhook.md +164 -0
  198. package/docs/guides/run-the-scheduler.md +173 -0
  199. package/docs/guides/testing.md +157 -0
  200. package/docs/installation.md +81 -0
  201. package/docs/quickstart.md +163 -0
  202. package/docs/troubleshooting.md +281 -0
  203. package/package.json +241 -3
  204. package/src/CatchUp.ts +73 -0
  205. package/src/Channel.ts +121 -0
  206. package/src/ClaimDecision.ts +303 -0
  207. package/src/Cron.ts +173 -0
  208. package/src/DispatchReader.ts +179 -0
  209. package/src/Overlap.ts +53 -0
  210. package/src/Schedule.ts +112 -0
  211. package/src/Scheduler.ts +1154 -0
  212. package/src/SqlTriggerStore.ts +638 -0
  213. package/src/Trigger.ts +84 -0
  214. package/src/TriggerError.ts +129 -0
  215. package/src/TriggerStore.ts +514 -0
  216. package/src/Webhook.ts +270 -0
  217. package/src/index.ts +30 -0
  218. package/src/internal/ActiveRuns.ts +83 -0
  219. package/src/internal/DueOccurrences.ts +101 -0
  220. package/src/internal/WallClock.ts +172 -0
  221. package/src/migrations/0001_triggers.ts +43 -0
  222. package/src/migrations/0002_reservation_lease.ts +20 -0
  223. package/src/migrations/0003_heartbeat.ts +24 -0
  224. package/src/migrations/0004_fire_run_index.ts +21 -0
  225. package/src/migrations/index.ts +40 -0
  226. package/src/test/TestTriggers.ts +563 -0
@@ -0,0 +1,483 @@
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 SqlTriggerStore_exports = {};
30
+ __export(SqlTriggerStore_exports, {
31
+ layer: () => layer,
32
+ make: () => make
33
+ });
34
+ module.exports = __toCommonJS(SqlTriggerStore_exports);
35
+ var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
36
+ var import_DurableWriter = require("@smthrs/database/DurableWriter");
37
+ var Clock = __toESM(require("effect/Clock"), 1);
38
+ var Effect = __toESM(require("effect/Effect"), 1);
39
+ var Layer = __toESM(require("effect/Layer"), 1);
40
+ var Option = __toESM(require("effect/Option"), 1);
41
+ var Result = __toESM(require("effect/Result"), 1);
42
+ var Schema = __toESM(require("effect/Schema"), 1);
43
+ var SqlClient = __toESM(require("effect/unstable/sql/SqlClient"), 1);
44
+ var ClaimDecision = __toESM(require("./ClaimDecision.js"), 1);
45
+ var Migrations = __toESM(require("./migrations/index.js"), 1);
46
+ var Overlap = __toESM(require("./Overlap.js"), 1);
47
+ var Schedule = __toESM(require("./Schedule.js"), 1);
48
+ var Trigger = __toESM(require("./Trigger.js"), 1);
49
+ var import_TriggerError = require("./TriggerError.js");
50
+ var import_TriggerStore = require("./TriggerStore.js");
51
+ const fireRecord = (row) => ({
52
+ triggerId: row.trigger_id,
53
+ occurrence: row.occurrence_at_ms,
54
+ outcome: row.outcome,
55
+ ...row.run_id === null ? {} : { runId: row.run_id },
56
+ ...row.error === null ? {} : { error: row.error }
57
+ });
58
+ const storeError = (message, cause) => new import_TriggerError.TriggerError({
59
+ code: "store",
60
+ message,
61
+ ...cause === void 0 ? {} : { cause }
62
+ });
63
+ const unknownTrigger = (triggerId) => new import_TriggerError.TriggerError({ code: "unknown_trigger", message: `unknown trigger ${triggerId}` });
64
+ const StoredDeclaration = Schema.Struct({
65
+ ...Trigger.Trigger.fields,
66
+ revision: Schema.Int,
67
+ lastFiredAt: Schema.optional(Schema.Number)
68
+ });
69
+ const decodeDeclaration = Schema.decodeUnknownSync(StoredDeclaration);
70
+ const declaration = (row) => decodeDeclaration({
71
+ id: row.trigger_id,
72
+ flowId: row.flow_id,
73
+ input: JSON.parse(row.input_json),
74
+ cron: row.cron,
75
+ ...row.timezone === null ? {} : { timezone: row.timezone },
76
+ overlap: row.overlap,
77
+ catchUp: row.catch_up,
78
+ maxCatchUp: row.max_catch_up,
79
+ enabled: row.enabled === 1,
80
+ revision: row.revision,
81
+ ...row.last_fired_at_ms === null ? {} : { lastFiredAt: row.last_fired_at_ms }
82
+ });
83
+ const decodeFailure = (row, cause) => storeError(`could not decode trigger row ${row.trigger_id}`, cause);
84
+ const decode = (row) => Effect.try({ try: () => declaration(row), catch: (cause) => decodeFailure(row, cause) });
85
+ const listedRow = (row) => ({
86
+ triggerId: row.trigger_id,
87
+ trigger: Result.try({ try: () => declaration(row), catch: (cause) => decodeFailure(row, cause) }),
88
+ ...row.active_run_id === null ? {} : { activeRunId: row.active_run_id },
89
+ ...row.pending_at_ms === null ? {} : { pendingAt: row.pending_at_ms }
90
+ });
91
+ const make = Effect.gen(function* () {
92
+ const sql = yield* Effect.service(SqlClient.SqlClient);
93
+ const writer = yield* import_DurableWriter.DurableWriter;
94
+ const migrationFailed = (cause) => storeError("could not run trigger migrations", cause);
95
+ yield* Migrations.run.pipe(
96
+ Effect.mapError(migrationFailed),
97
+ Effect.catchDefect((defect) => Effect.fail(migrationFailed(defect)))
98
+ );
99
+ const asTriggerError = (message) => (cause) => cause instanceof import_TriggerError.TriggerError ? cause : storeError(message, cause);
100
+ const read = (effect) => effect.pipe(Effect.mapError(asTriggerError("trigger store read failed")));
101
+ const write = (effect) => writer.write(effect).pipe(Effect.mapError(asTriggerError("trigger store write failed")));
102
+ const requireTrigger = (triggerId) => sql`SELECT trigger_id FROM flows_triggers WHERE trigger_id = ${triggerId}`.pipe(
103
+ Effect.flatMap((rows) => rows[0] === void 0 ? Effect.fail(unknownTrigger(triggerId)) : Effect.void)
104
+ );
105
+ const get = (triggerId) => read(sql`SELECT * FROM flows_triggers WHERE trigger_id = ${triggerId}`).pipe(
106
+ Effect.flatMap(
107
+ (rows) => rows[0] === void 0 ? Effect.succeed(Option.none()) : decode(rows[0]).pipe(Effect.map(Option.some))
108
+ )
109
+ );
110
+ const fireSnapshot = (row) => row === void 0 ? void 0 : {
111
+ outcome: row.outcome,
112
+ ...row.run_id === null ? {} : { runId: row.run_id }
113
+ };
114
+ const claimInTransaction = (fire, claimedAt, suppliedRow) => Effect.gen(function* () {
115
+ const row = suppliedRow ?? (yield* sql`
116
+ SELECT enabled, revision, overlap, active_run_id, active_claimed_at_ms, pending_at_ms
117
+ FROM flows_triggers WHERE trigger_id = ${fire.triggerId}
118
+ `)[0];
119
+ if (row === void 0) return yield* Effect.fail(unknownTrigger(fire.triggerId));
120
+ const activeRunId = row.active_run_id ?? void 0;
121
+ const refusal = ClaimDecision.refuse({ enabled: row.enabled === 1, revision: row.revision }, fire);
122
+ if (refusal !== void 0) return yield* Effect.fail(refusal);
123
+ const insertResult = yield* sql`
124
+ INSERT INTO flows_trigger_fires (trigger_id, occurrence_at_ms)
125
+ VALUES (${fire.triggerId}, ${fire.occurrence})
126
+ ON CONFLICT (trigger_id, occurrence_at_ms) DO NOTHING
127
+ `.raw;
128
+ const inserted = yield* (0, import_DurableWriter.affectedRows)(insertResult);
129
+ const existing = inserted === 0 ? yield* sql`
130
+ SELECT outcome, run_id FROM flows_trigger_fires
131
+ WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${fire.occurrence}
132
+ ` : [];
133
+ const activeOccurrence = activeRunId !== void 0 && (0, import_TriggerStore.isReservation)(activeRunId) ? (0, import_TriggerStore.reservationOccurrence)(activeRunId) : void 0;
134
+ const active = activeOccurrence === void 0 ? [] : yield* sql`
135
+ SELECT outcome, run_id FROM flows_trigger_fires
136
+ WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${activeOccurrence}
137
+ `;
138
+ const existingFire = fireSnapshot(existing[0]);
139
+ const activeFire = fireSnapshot(active[0]);
140
+ const decision = ClaimDecision.decideFenced({
141
+ fire,
142
+ claimedAt,
143
+ reservationId: (0, import_TriggerStore.reservationId)(fire.triggerId, fire.occurrence, globalThis.crypto.randomUUID()),
144
+ snapshot: {
145
+ revision: row.revision,
146
+ enabled: row.enabled === 1,
147
+ overlap: row.overlap,
148
+ ...activeRunId === void 0 ? {} : { activeRunId },
149
+ ...row.active_claimed_at_ms === null ? {} : { activeClaimedAt: row.active_claimed_at_ms },
150
+ ...row.pending_at_ms === null ? {} : { pending: row.pending_at_ms },
151
+ ...existingFire === void 0 ? {} : { existingFire },
152
+ ...activeFire === void 0 ? {} : { activeFire }
153
+ }
154
+ });
155
+ for (const write2 of decision.writes) {
156
+ switch (write2._tag) {
157
+ case "SetOutcome": {
158
+ yield* write2.whileOpen ? sql`UPDATE flows_trigger_fires SET outcome = ${write2.outcome}
159
+ WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write2.occurrence}
160
+ AND (outcome IS NULL OR outcome = 'buffered')` : sql`UPDATE flows_trigger_fires SET outcome = ${write2.outcome}
161
+ WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write2.occurrence}`;
162
+ break;
163
+ }
164
+ case "SetRunId": {
165
+ yield* sql`UPDATE flows_trigger_fires SET run_id = ${write2.runId}
166
+ WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write2.occurrence}`;
167
+ break;
168
+ }
169
+ case "ReleaseReservation": {
170
+ yield* sql`UPDATE flows_triggers
171
+ SET active_run_id = ${write2.activeRunId ?? null}, active_claimed_at_ms = NULL,
172
+ pending_at_ms = ${write2.pending ?? null}
173
+ WHERE trigger_id = ${fire.triggerId} AND active_run_id = ${write2.expected}`;
174
+ break;
175
+ }
176
+ case "AdvanceCursor": {
177
+ yield* sql`UPDATE flows_triggers
178
+ SET last_fired_at_ms = ${Dialect.greatest(sql)}(COALESCE(last_fired_at_ms, ${write2.occurrence}), ${write2.occurrence})
179
+ WHERE trigger_id = ${fire.triggerId}`;
180
+ break;
181
+ }
182
+ case "SetPending": {
183
+ yield* sql`UPDATE flows_triggers SET pending_at_ms = ${write2.occurrence}
184
+ WHERE trigger_id = ${fire.triggerId}`;
185
+ break;
186
+ }
187
+ case "Reserve": {
188
+ yield* sql`UPDATE flows_triggers
189
+ SET active_run_id = ${write2.reservationId}, active_claimed_at_ms = ${write2.claimedAt}
190
+ WHERE trigger_id = ${fire.triggerId}`;
191
+ break;
192
+ }
193
+ }
194
+ }
195
+ return decision.claim;
196
+ });
197
+ return {
198
+ register: (trigger) => {
199
+ const decoded = Schema.decodeUnknownResult(Trigger.Trigger)(trigger);
200
+ if (Result.isFailure(decoded)) {
201
+ return Effect.fail(
202
+ (0, import_TriggerError.fromSchemaError)("invalid_trigger", "Trigger declaration is invalid", decoded.failure)
203
+ );
204
+ }
205
+ const snapshot = decoded.success;
206
+ let input;
207
+ try {
208
+ input = JSON.stringify(snapshot.input);
209
+ } catch (cause) {
210
+ return Effect.fail(storeError("trigger input is not JSON-serializable", cause));
211
+ }
212
+ if (input === void 0) {
213
+ return Effect.fail(
214
+ new import_TriggerError.TriggerError({
215
+ code: "invalid_trigger",
216
+ message: "trigger input has no JSON representation",
217
+ path: "input"
218
+ })
219
+ );
220
+ }
221
+ return Effect.suspend(
222
+ () => Schedule.validate(snapshot).pipe(
223
+ Effect.andThen(
224
+ write(sql`
225
+ INSERT INTO flows_triggers (trigger_id, flow_id, input_json, cron, timezone, overlap, catch_up, max_catch_up, enabled, revision)
226
+ VALUES (${snapshot.id}, ${snapshot.flowId}, ${input}, ${snapshot.cron}, ${snapshot.timezone ?? null}, ${snapshot.overlap}, ${snapshot.catchUp}, ${snapshot.maxCatchUp}, ${snapshot.enabled ? 1 : 0}, 1)
227
+ ON CONFLICT (trigger_id) DO UPDATE SET flow_id = excluded.flow_id, input_json = excluded.input_json, cron = excluded.cron,
228
+ timezone = excluded.timezone, overlap = excluded.overlap, catch_up = excluded.catch_up, max_catch_up = excluded.max_catch_up,
229
+ enabled = excluded.enabled, revision = flows_triggers.revision + 1
230
+ `.pipe(
231
+ Effect.flatMap(() => get(snapshot.id)),
232
+ Effect.flatMap(
233
+ (registered) => Option.isSome(registered) ? Effect.succeed(registered.value) : Effect.fail(storeError("registered trigger disappeared"))
234
+ )
235
+ ))
236
+ )
237
+ )
238
+ );
239
+ },
240
+ get,
241
+ list: () => read(sql`SELECT * FROM flows_triggers ORDER BY trigger_id`).pipe(
242
+ Effect.map((rows) => rows.map(listedRow))
243
+ ),
244
+ listEnabled: () => read(sql`SELECT * FROM flows_triggers WHERE enabled = 1 ORDER BY trigger_id`).pipe(
245
+ Effect.flatMap((rows) => Effect.all(rows.map(decode)))
246
+ ),
247
+ claimFire: (fire) => Effect.gen(function* () {
248
+ const claimedAt = yield* Clock.currentTimeMillis;
249
+ return yield* write(claimInTransaction(fire, claimedAt));
250
+ }),
251
+ claimPending: (fire) => Effect.gen(function* () {
252
+ const claimedAt = yield* Clock.currentTimeMillis;
253
+ return yield* write(Effect.gen(function* () {
254
+ const rows = yield* sql`
255
+ SELECT enabled, revision, overlap, active_run_id, active_claimed_at_ms, pending_at_ms
256
+ FROM flows_triggers WHERE trigger_id = ${fire.triggerId}
257
+ `;
258
+ const row = rows[0];
259
+ if (row === void 0) return yield* Effect.fail(unknownTrigger(fire.triggerId));
260
+ if (row.pending_at_ms === null) return Option.none();
261
+ const occurrence = row.pending_at_ms;
262
+ const claim = yield* claimInTransaction(
263
+ {
264
+ triggerId: fire.triggerId,
265
+ occurrence,
266
+ expectedRevision: fire.expectedRevision,
267
+ resumeBuffered: true
268
+ },
269
+ claimedAt,
270
+ row
271
+ );
272
+ if (claim.claimed && claim.action !== "buffer") {
273
+ yield* sql`UPDATE flows_triggers SET pending_at_ms = NULL WHERE trigger_id = ${fire.triggerId}`;
274
+ }
275
+ return Option.some({ occurrence, claim });
276
+ }));
277
+ }),
278
+ recordResult: (result) => write(
279
+ Effect.gen(function* () {
280
+ yield* requireTrigger(result.triggerId);
281
+ const rows = yield* sql`SELECT * FROM flows_trigger_fires
282
+ WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`;
283
+ const held = yield* sql`
284
+ SELECT active_run_id FROM flows_triggers WHERE trigger_id = ${result.triggerId}`;
285
+ const existing = rows[0] === void 0 ? void 0 : fireRecord(rows[0]);
286
+ const active = held[0].active_run_id ?? void 0;
287
+ const refusal = (0, import_TriggerStore.resultRefusal)(result, existing, active);
288
+ if (refusal !== void 0) return yield* Effect.fail(refusal);
289
+ if (result.outcome !== "launched" && existing?.outcome === result.outcome) return;
290
+ const terminal = result.outcome === "completed" || result.outcome === "failed" || result.outcome === "superseded";
291
+ const resultOwner = (existing?.outcome === "launched" ? existing.runId : result.reservationId ?? result.runId ?? active) ?? null;
292
+ if (terminal && result.runId === void 0) {
293
+ yield* sql`UPDATE flows_trigger_fires
294
+ SET outcome = ${result.outcome}, error = ${result.error ?? null}
295
+ WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`;
296
+ } else {
297
+ yield* sql`UPDATE flows_trigger_fires
298
+ SET outcome = ${result.outcome}, run_id = ${result.runId ?? null}, error = ${result.error ?? null}
299
+ WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`;
300
+ }
301
+ if (result.outcome === "launched") {
302
+ yield* sql`UPDATE flows_triggers
303
+ SET last_fired_at_ms = ${Dialect.greatest(sql)}(COALESCE(last_fired_at_ms, ${result.occurrence}), ${result.occurrence}),
304
+ active_run_id = ${result.runId},
305
+ active_claimed_at_ms = NULL
306
+ WHERE trigger_id = ${result.triggerId} AND active_run_id = ${result.reservationId}`;
307
+ return;
308
+ }
309
+ yield* sql`UPDATE flows_triggers
310
+ SET last_fired_at_ms = ${Dialect.greatest(sql)}(COALESCE(last_fired_at_ms, ${result.occurrence}), ${result.occurrence}),
311
+ active_run_id = CASE
312
+ WHEN active_run_id = ${resultOwner} THEN NULL
313
+ ELSE active_run_id
314
+ END,
315
+ active_claimed_at_ms = CASE
316
+ WHEN active_run_id = ${resultOwner} THEN NULL
317
+ ELSE active_claimed_at_ms
318
+ END
319
+ WHERE trigger_id = ${result.triggerId}`;
320
+ })
321
+ ).pipe(
322
+ Effect.asVoid
323
+ ),
324
+ restorePending: (fire) => write(Effect.gen(function* () {
325
+ yield* requireTrigger(fire.triggerId);
326
+ const rows = yield* sql`
327
+ SELECT t.pending_at_ms, f.run_id FROM flows_triggers t JOIN flows_trigger_fires f
328
+ ON f.trigger_id = t.trigger_id
329
+ WHERE t.trigger_id = ${fire.triggerId} AND t.active_run_id = ${fire.reservationId}
330
+ AND f.occurrence_at_ms = ${fire.occurrence} AND (f.outcome IS NULL OR f.outcome = 'buffered')`;
331
+ const row = rows[0];
332
+ if (row === void 0 || (0, import_TriggerStore.reservationOccurrence)(fire.reservationId) !== fire.occurrence) {
333
+ return yield* Effect.fail(
334
+ new import_TriggerError.TriggerError({ code: "stale_owner", message: "pending restoration no longer owns reservation" })
335
+ );
336
+ }
337
+ const pending = Overlap.pendingAfter({
338
+ running: false,
339
+ pending: row.pending_at_ms ?? void 0,
340
+ due: fire.occurrence
341
+ });
342
+ const predecessors = row.run_id === null || (0, import_TriggerStore.isReservation)(row.run_id) ? [] : yield* sql`
343
+ SELECT run_id FROM flows_trigger_fires
344
+ WHERE trigger_id = ${fire.triggerId} AND run_id = ${row.run_id} AND outcome = 'launched'`;
345
+ const predecessor = predecessors[0]?.run_id ?? null;
346
+ yield* sql`UPDATE flows_triggers
347
+ SET pending_at_ms = ${pending}, active_run_id = ${predecessor}, active_claimed_at_ms = NULL
348
+ WHERE trigger_id = ${fire.triggerId} AND active_run_id = ${fire.reservationId}`;
349
+ })).pipe(Effect.asVoid),
350
+ setPending: (fire) => write(Effect.gen(function* () {
351
+ const rows = yield* sql`
352
+ SELECT pending_at_ms FROM flows_triggers WHERE trigger_id = ${fire.triggerId}
353
+ `;
354
+ const row = rows[0];
355
+ if (row === void 0) return yield* Effect.fail(unknownTrigger(fire.triggerId));
356
+ const pending = Overlap.pendingAfter({
357
+ running: true,
358
+ pending: row.pending_at_ms ?? void 0,
359
+ due: fire.occurrence
360
+ });
361
+ yield* sql`UPDATE flows_triggers SET pending_at_ms = ${pending} WHERE trigger_id = ${fire.triggerId}`;
362
+ })).pipe(Effect.asVoid),
363
+ activeRun: (triggerId) => Effect.gen(function* () {
364
+ const now = yield* Clock.currentTimeMillis;
365
+ return yield* write(Effect.gen(function* () {
366
+ const rows = yield* sql`SELECT active_run_id, active_claimed_at_ms, pending_at_ms
367
+ FROM flows_triggers WHERE trigger_id = ${triggerId}`;
368
+ const row = rows[0];
369
+ if (row === void 0) return yield* Effect.fail(unknownTrigger(triggerId));
370
+ const activeRunId = row.active_run_id ?? void 0;
371
+ const occurrence = activeRunId !== void 0 && (0, import_TriggerStore.isReservation)(activeRunId) ? (0, import_TriggerStore.reservationOccurrence)(activeRunId) : void 0;
372
+ const unfinished = occurrence === void 0 ? [] : yield* sql`
373
+ SELECT outcome, run_id FROM flows_trigger_fires
374
+ WHERE trigger_id = ${triggerId} AND occurrence_at_ms = ${occurrence}
375
+ AND (outcome IS NULL OR outcome = 'buffered')
376
+ `;
377
+ const unfinishedFire = fireSnapshot(unfinished[0]);
378
+ const lease = ClaimDecision.lease({
379
+ now,
380
+ ...activeRunId === void 0 ? {} : { activeRunId },
381
+ ...row.active_claimed_at_ms === null ? {} : { activeClaimedAt: row.active_claimed_at_ms },
382
+ ...row.pending_at_ms === null ? {} : { pending: row.pending_at_ms },
383
+ ...unfinishedFire === void 0 ? {} : { unfinished: unfinishedFire }
384
+ });
385
+ if (lease._tag === "Idle") return Option.none();
386
+ if (lease._tag === "Held") return Option.some(lease.runId);
387
+ yield* lease.pending === void 0 ? sql`UPDATE flows_triggers SET active_run_id = NULL, active_claimed_at_ms = NULL
388
+ WHERE trigger_id = ${triggerId} AND active_run_id = ${lease.expected}` : sql`UPDATE flows_triggers
389
+ SET active_run_id = ${lease.recovered ?? null}, active_claimed_at_ms = NULL,
390
+ pending_at_ms = ${lease.pending}
391
+ WHERE trigger_id = ${triggerId} AND active_run_id = ${lease.expected}`;
392
+ return lease.recovered === void 0 ? Option.none() : Option.some(lease.recovered);
393
+ }));
394
+ }),
395
+ activeOccurrence: (triggerId, runId) => read(Effect.gen(function* () {
396
+ yield* requireTrigger(triggerId);
397
+ const reserved = (0, import_TriggerStore.reservationOccurrence)(runId);
398
+ if (reserved !== void 0) return Option.some(reserved);
399
+ if ((0, import_TriggerStore.isReservation)(runId)) return Option.none();
400
+ const rows = yield* sql`
401
+ SELECT occurrence_at_ms FROM flows_trigger_fires
402
+ WHERE trigger_id = ${triggerId} AND run_id = ${runId} AND outcome = 'launched'
403
+ ORDER BY occurrence_at_ms DESC LIMIT 1
404
+ `;
405
+ return rows[0] === void 0 ? Option.none() : Option.some(rows[0].occurrence_at_ms);
406
+ })),
407
+ clearActive: (triggerId, runId) => write(sql`UPDATE flows_triggers SET active_run_id = NULL, active_claimed_at_ms = NULL
408
+ WHERE trigger_id = ${triggerId} AND active_run_id = ${runId}`).pipe(
409
+ Effect.asVoid
410
+ ),
411
+ history: (query = {}) => (0, import_TriggerStore.historyLimit)(query.limit).pipe(
412
+ Effect.flatMap(
413
+ (limit) => (
414
+ // One statement for every query shape: a null parameter disables
415
+ // its predicate, and SQLite reads `LIMIT -1` as no limit. One row
416
+ // past the limit is fetched so the page knows whether a next page
417
+ // exists without a second count query.
418
+ read(sql`
419
+ SELECT trigger_id, occurrence_at_ms, outcome, run_id, error FROM flows_trigger_fires
420
+ WHERE ${query.triggerId === void 0 ? sql`1 = 1` : sql`trigger_id = ${query.triggerId}`}
421
+ AND ${query.runId === void 0 ? sql`1 = 1` : sql`run_id = ${query.runId}`}
422
+ AND ${query.outcome === void 0 ? sql`1 = 1` : sql`outcome = ${query.outcome}`}
423
+ AND (${query.cursor === void 0 ? 1 : 0} = 1
424
+ OR occurrence_at_ms < ${query.cursor?.occurrence ?? null}
425
+ OR (occurrence_at_ms = ${query.cursor?.occurrence ?? null} AND trigger_id < ${query.cursor?.triggerId ?? null}))
426
+ ORDER BY occurrence_at_ms DESC, trigger_id DESC
427
+ LIMIT ${limit === void 0 ? Dialect.isPostgres(sql) ? null : -1 : limit + 1}
428
+ `).pipe(Effect.map((rows) => (0, import_TriggerStore.historyPage)(rows.map(fireRecord), limit)))
429
+ )
430
+ )
431
+ ),
432
+ pruneFires: ({ olderThan }) => (0, import_TriggerStore.pruneCutoff)(olderThan).pipe(
433
+ Effect.flatMap(
434
+ (cutoff) => (
435
+ // The trigger row names the state the scheduler still reads: the
436
+ // buffered occurrence and the run or reservation on `active_run_id`.
437
+ // An unsettled row carries no outcome, so the outcome filter already
438
+ // spares a reserved occurrence.
439
+ write(Effect.gen(function* () {
440
+ const deleted = yield* sql`
441
+ DELETE FROM flows_trigger_fires
442
+ WHERE occurrence_at_ms < ${cutoff}
443
+ AND outcome IN ('completed', 'failed', 'skipped', 'superseded')
444
+ AND NOT EXISTS (
445
+ SELECT 1 FROM flows_triggers t
446
+ WHERE t.trigger_id = flows_trigger_fires.trigger_id
447
+ AND (t.pending_at_ms = flows_trigger_fires.occurrence_at_ms
448
+ OR t.active_run_id = flows_trigger_fires.run_id)
449
+ )
450
+ `.raw;
451
+ return yield* (0, import_DurableWriter.affectedRows)(deleted);
452
+ }))
453
+ )
454
+ )
455
+ ),
456
+ inspect: (triggerId) => read(sql`
457
+ SELECT active_run_id, pending_at_ms FROM flows_triggers WHERE trigger_id = ${triggerId}
458
+ `).pipe(
459
+ Effect.flatMap((rows) => {
460
+ const row = rows[0];
461
+ if (row === void 0) return Effect.fail(unknownTrigger(triggerId));
462
+ return Effect.succeed({
463
+ ...row.active_run_id === null ? {} : { activeRunId: row.active_run_id },
464
+ ...row.pending_at_ms === null ? {} : { pendingAt: row.pending_at_ms }
465
+ });
466
+ })
467
+ ),
468
+ heartbeat: (host) => Effect.flatMap(
469
+ Clock.currentTimeMillis,
470
+ (tickedAt) => write(sql`INSERT INTO flows_scheduler_heartbeat (host, ticked_at_ms) VALUES (${host}, ${tickedAt})
471
+ ON CONFLICT (host) DO UPDATE SET ticked_at_ms = excluded.ticked_at_ms`)
472
+ ).pipe(Effect.asVoid),
473
+ lastHeartbeat: () => read(sql`
474
+ SELECT host, ticked_at_ms FROM flows_scheduler_heartbeat ORDER BY ticked_at_ms DESC, host ASC LIMIT 1
475
+ `).pipe(
476
+ Effect.map(
477
+ (rows) => rows[0] === void 0 ? Option.none() : Option.some({ host: rows[0].host, tickedAt: rows[0].ticked_at_ms })
478
+ )
479
+ )
480
+ };
481
+ });
482
+ const layer = Layer.effect(import_TriggerStore.TriggerStore)(make);
483
+ //# sourceMappingURL=SqlTriggerStore.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/SqlTriggerStore.ts"],
4
+ "sourcesContent": ["/**\n * SQLite-backed trigger store.\n *\n * @see packages/smithers/agent/triggers/docs/api.md\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport { affectedRows, DurableWriter } from \"@smthrs/database/DurableWriter\"\nimport * as Clock from \"effect/Clock\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\nimport * as ClaimDecision from \"./ClaimDecision.ts\"\nimport * as Migrations from \"./migrations/index.ts\"\nimport * as Overlap from \"./Overlap.ts\"\nimport * as Schedule from \"./Schedule.ts\"\nimport * as Trigger from \"./Trigger.ts\"\nimport { fromSchemaError, TriggerError } from \"./TriggerError.ts\"\nimport {\n type ClaimFire,\n type FireRecord,\n type Heartbeat,\n historyLimit,\n historyPage,\n isReservation,\n type Listed,\n type Outcome,\n pruneCutoff,\n type Registered,\n reservationId,\n reservationOccurrence,\n resultRefusal,\n type Service,\n TriggerStore\n} from \"./TriggerStore.ts\"\n\ninterface Row {\n readonly trigger_id: string\n readonly flow_id: string\n readonly input_json: string\n readonly cron: string\n readonly timezone: string | null\n readonly overlap: string\n readonly catch_up: string\n readonly max_catch_up: number\n readonly enabled: number\n readonly revision: number\n readonly last_fired_at_ms: number | null\n readonly active_run_id: string | null\n readonly pending_at_ms: number | null\n}\n\ninterface FireRow {\n readonly trigger_id: string\n readonly occurrence_at_ms: number\n readonly outcome: Outcome | null\n readonly run_id: string | null\n readonly error: string | null\n}\n\nconst fireRecord = (row: FireRow): FireRecord => ({\n triggerId: row.trigger_id,\n occurrence: row.occurrence_at_ms,\n outcome: row.outcome,\n ...(row.run_id === null ? {} : { runId: row.run_id }),\n ...(row.error === null ? {} : { error: row.error })\n})\n\ninterface ClaimRow {\n readonly enabled: number\n readonly revision: number\n readonly overlap: Registered[\"overlap\"]\n readonly active_run_id: string | null\n readonly active_claimed_at_ms: number | null\n readonly pending_at_ms: number | null\n}\n\nconst storeError = (message: string, cause?: unknown) =>\n new TriggerError({\n code: \"store\",\n message,\n ...(cause === undefined ? {} : { cause })\n })\n\nconst unknownTrigger = (triggerId: string) =>\n new TriggerError({ code: \"unknown_trigger\", message: `unknown trigger ${triggerId}` })\n\n// A stored row is decoded through the same schema `register` accepts, plus the\n// store-owned fields, rather than cast. The table's CHECKs cover the policy\n// literals but not the catch-up bound or the non-empty strings, and a row\n// anyone rewrote must not reach the scheduler as a declaration `register`\n// would have refused.\nconst StoredDeclaration = Schema.Struct({\n ...Trigger.Trigger.fields,\n revision: Schema.Int,\n lastFiredAt: Schema.optional(Schema.Number)\n})\nconst decodeDeclaration = Schema.decodeUnknownSync(StoredDeclaration)\n\nconst declaration = (row: Row): Registered =>\n decodeDeclaration({\n id: row.trigger_id,\n flowId: row.flow_id,\n input: JSON.parse(row.input_json),\n cron: row.cron,\n ...(row.timezone === null ? {} : { timezone: row.timezone }),\n overlap: row.overlap,\n catchUp: row.catch_up,\n maxCatchUp: row.max_catch_up,\n enabled: row.enabled === 1,\n revision: row.revision,\n ...(row.last_fired_at_ms === null ? {} : { lastFiredAt: row.last_fired_at_ms })\n })\n\n// The offending id belongs in the message: a store failure that only says a\n// row would not decode leaves an operator grepping a shared database for it.\nconst decodeFailure = (row: Row, cause: unknown) => storeError(`could not decode trigger row ${row.trigger_id}`, cause)\n\nconst decode = (row: Row): Effect.Effect<Registered, TriggerError> =>\n Effect.try({ try: () => declaration(row), catch: (cause) => decodeFailure(row, cause) })\n\n// A listing decodes row by row. One corrupt `input_json` used to fail the\n// whole read, and because a scheduler tick lists every trigger before it\n// isolates them, that one row stopped every healthy schedule in the store.\nconst listedRow = (row: Row): Listed => ({\n triggerId: row.trigger_id,\n trigger: Result.try({ try: () => declaration(row), catch: (cause) => decodeFailure(row, cause) }),\n ...(row.active_run_id === null ? {} : { activeRunId: row.active_run_id }),\n ...(row.pending_at_ms === null ? {} : { pendingAt: row.pending_at_ms })\n})\n\n/**\n * Builds a {@link TriggerStore.Service} over the ambient SQL client, with\n * every write going through the durable writer.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make: Effect.Effect<\n Service,\n TriggerError,\n DurableWriter | SqlClient.SqlClient\n> = Effect.gen(function*() {\n const sql = yield* Effect.service(SqlClient.SqlClient)\n const writer = yield* DurableWriter\n // The migrator raises a schema it cannot apply as a defect, not as a typed\n // failure, so mapping the error channel alone left a defect escaping a\n // constructor whose signature promises `TriggerError`. Defects are caught\n // separately from failures, which leaves interruption alone.\n const migrationFailed = (cause: unknown) => storeError(\"could not run trigger migrations\", cause)\n yield* Migrations.run.pipe(\n Effect.mapError(migrationFailed),\n Effect.catchDefect((defect) => Effect.fail(migrationFailed(defect)))\n )\n // A failure the store itself typed already says which refusal it is, so it\n // travels out unchanged. Re-wrapping it turned `unknown trigger x` into the\n // generic write failure and erased the one code a caller could branch on.\n const asTriggerError = (message: string) => (cause: unknown): TriggerError =>\n cause instanceof TriggerError ? cause : storeError(message, cause)\n const read = <A>(effect: Effect.Effect<A, unknown>): Effect.Effect<A, TriggerError> =>\n effect.pipe(Effect.mapError(asTriggerError(\"trigger store read failed\")))\n const write = <A>(effect: Effect.Effect<A, unknown>): Effect.Effect<A, TriggerError> =>\n writer.write(effect).pipe(Effect.mapError(asTriggerError(\"trigger store write failed\")))\n const requireTrigger = (triggerId: string) =>\n sql<{ readonly trigger_id: string }>`SELECT trigger_id FROM flows_triggers WHERE trigger_id = ${triggerId}`.pipe(\n Effect.flatMap((rows) => rows[0] === undefined ? Effect.fail(unknownTrigger(triggerId)) : Effect.void)\n )\n const get: Service[\"get\"] = (triggerId) =>\n read(sql<Row>`SELECT * FROM flows_triggers WHERE trigger_id = ${triggerId}`).pipe(\n Effect.flatMap((rows) =>\n rows[0] === undefined ? Effect.succeed(Option.none()) : decode(rows[0]).pipe(Effect.map(Option.some))\n )\n )\n const fireSnapshot = (\n row: { readonly outcome: string | null; readonly run_id: string | null } | undefined\n ): ClaimDecision.Fire | undefined =>\n row === undefined ? undefined : {\n outcome: row.outcome as Outcome | null,\n ...(row.run_id === null ? {} : { runId: row.run_id })\n }\n const claimInTransaction = (\n fire: ClaimFire,\n claimedAt: number,\n suppliedRow?: ClaimRow\n ) =>\n Effect.gen(function*() {\n // The declaration is read first and inside the transaction, so the\n // policy applied is the stored one and a caller holding a snapshot from\n // before an edit is refused rather than obeyed.\n const row = suppliedRow ?? (yield* sql<ClaimRow>`\n SELECT enabled, revision, overlap, active_run_id, active_claimed_at_ms, pending_at_ms\n FROM flows_triggers WHERE trigger_id = ${fire.triggerId}\n `)[0]\n if (row === undefined) return yield* Effect.fail(unknownTrigger(fire.triggerId))\n const activeRunId = row.active_run_id ?? undefined\n // The fences run before the insert below, so a refused claim leaves no\n // ledger row behind.\n const refusal = ClaimDecision.refuse({ enabled: row.enabled === 1, revision: row.revision }, fire)\n if (refusal !== undefined) return yield* Effect.fail(refusal)\n const insertResult = yield* sql`\n INSERT INTO flows_trigger_fires (trigger_id, occurrence_at_ms)\n VALUES (${fire.triggerId}, ${fire.occurrence})\n ON CONFLICT (trigger_id, occurrence_at_ms) DO NOTHING\n `.raw\n const inserted = yield* affectedRows(insertResult)\n const existing = inserted === 0\n ? yield* sql<{ readonly outcome: string | null; readonly run_id: string | null }>`\n SELECT outcome, run_id FROM flows_trigger_fires\n WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${fire.occurrence}\n `\n : []\n // Both the expired-reservation reclaim and the supersede predecessor\n // lookup read the row the reservation holds, so one read serves both.\n const activeOccurrence = activeRunId !== undefined && isReservation(activeRunId)\n ? reservationOccurrence(activeRunId)\n : undefined\n const active = activeOccurrence === undefined\n ? []\n : yield* sql<{ readonly outcome: string | null; readonly run_id: string | null }>`\n SELECT outcome, run_id FROM flows_trigger_fires\n WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${activeOccurrence}\n `\n const existingFire = fireSnapshot(existing[0])\n const activeFire = fireSnapshot(active[0])\n const decision = ClaimDecision.decideFenced({\n fire,\n claimedAt,\n reservationId: reservationId(fire.triggerId, fire.occurrence, globalThis.crypto.randomUUID()),\n snapshot: {\n revision: row.revision,\n enabled: row.enabled === 1,\n overlap: row.overlap,\n ...(activeRunId === undefined ? {} : { activeRunId }),\n ...(row.active_claimed_at_ms === null ? {} : { activeClaimedAt: row.active_claimed_at_ms }),\n ...(row.pending_at_ms === null ? {} : { pending: row.pending_at_ms }),\n ...(existingFire === undefined ? {} : { existingFire }),\n ...(activeFire === undefined ? {} : { activeFire })\n }\n })\n for (const write of decision.writes) {\n switch (write._tag) {\n case \"SetOutcome\": {\n yield* write.whileOpen\n ? sql`UPDATE flows_trigger_fires SET outcome = ${write.outcome}\n WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write.occurrence}\n AND (outcome IS NULL OR outcome = 'buffered')`\n : sql`UPDATE flows_trigger_fires SET outcome = ${write.outcome}\n WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write.occurrence}`\n break\n }\n case \"SetRunId\": {\n yield* sql`UPDATE flows_trigger_fires SET run_id = ${write.runId}\n WHERE trigger_id = ${fire.triggerId} AND occurrence_at_ms = ${write.occurrence}`\n break\n }\n case \"ReleaseReservation\": {\n yield* sql`UPDATE flows_triggers\n SET active_run_id = ${write.activeRunId ?? null}, active_claimed_at_ms = NULL,\n pending_at_ms = ${write.pending ?? null}\n WHERE trigger_id = ${fire.triggerId} AND active_run_id = ${write.expected}`\n break\n }\n case \"AdvanceCursor\": {\n yield* sql`UPDATE flows_triggers\n SET last_fired_at_ms = ${\n Dialect.greatest(sql)\n }(COALESCE(last_fired_at_ms, ${write.occurrence}), ${write.occurrence})\n WHERE trigger_id = ${fire.triggerId}`\n break\n }\n case \"SetPending\": {\n yield* sql`UPDATE flows_triggers SET pending_at_ms = ${write.occurrence}\n WHERE trigger_id = ${fire.triggerId}`\n break\n }\n case \"Reserve\": {\n yield* sql`UPDATE flows_triggers\n SET active_run_id = ${write.reservationId}, active_claimed_at_ms = ${write.claimedAt}\n WHERE trigger_id = ${fire.triggerId}`\n break\n }\n }\n }\n return decision.claim\n })\n\n return {\n register: (trigger) => {\n const decoded = Schema.decodeUnknownResult(Trigger.Trigger)(trigger)\n if (Result.isFailure(decoded)) {\n return Effect.fail(\n fromSchemaError(\"invalid_trigger\", \"Trigger declaration is invalid\", decoded.failure)\n )\n }\n const snapshot = decoded.success\n let input: string | undefined\n try {\n input = JSON.stringify(snapshot.input)\n } catch (cause) {\n return Effect.fail(storeError(\"trigger input is not JSON-serializable\", cause))\n }\n // `JSON.stringify` answers `undefined` rather than throwing for an input\n // it cannot represent. Keep this guard even though Schema.Json currently\n // refuses every such input before serialization.\n if (input === undefined) {\n return Effect.fail(\n new TriggerError({\n code: \"invalid_trigger\",\n message: \"trigger input has no JSON representation\",\n path: \"input\"\n })\n )\n }\n // Registration is the last point where an unsatisfiable expression can\n // still be refused. The decoded declaration and its input string were\n // already copied at the call boundary, before this lazy work begins.\n return Effect.suspend(() =>\n Schedule.validate(snapshot).pipe(\n Effect.andThen(\n write(sql`\n INSERT INTO flows_triggers (trigger_id, flow_id, input_json, cron, timezone, overlap, catch_up, max_catch_up, enabled, revision)\n VALUES (${snapshot.id}, ${snapshot.flowId}, ${input}, ${snapshot.cron}, ${\n snapshot.timezone ?? null\n }, ${snapshot.overlap}, ${snapshot.catchUp}, ${snapshot.maxCatchUp}, ${snapshot.enabled ? 1 : 0}, 1)\n ON CONFLICT (trigger_id) DO UPDATE SET flow_id = excluded.flow_id, input_json = excluded.input_json, cron = excluded.cron,\n timezone = excluded.timezone, overlap = excluded.overlap, catch_up = excluded.catch_up, max_catch_up = excluded.max_catch_up,\n enabled = excluded.enabled, revision = flows_triggers.revision + 1\n `.pipe(\n Effect.flatMap(() => get(snapshot.id)),\n Effect.flatMap((registered) =>\n Option.isSome(registered)\n ? Effect.succeed(registered.value)\n : Effect.fail(storeError(\"registered trigger disappeared\"))\n )\n ))\n )\n )\n )\n },\n get,\n list: () =>\n read(sql<Row>`SELECT * FROM flows_triggers ORDER BY trigger_id`).pipe(\n Effect.map((rows) => rows.map(listedRow))\n ),\n listEnabled: () =>\n read(sql<Row>`SELECT * FROM flows_triggers WHERE enabled = 1 ORDER BY trigger_id`).pipe(\n Effect.flatMap((rows) => Effect.all(rows.map(decode)))\n ),\n claimFire: (fire) =>\n Effect.gen(function*() {\n const claimedAt = yield* Clock.currentTimeMillis\n return yield* write(claimInTransaction(fire, claimedAt))\n }),\n claimPending: (fire) =>\n Effect.gen(function*() {\n const claimedAt = yield* Clock.currentTimeMillis\n return yield* write(Effect.gen(function*() {\n const rows = yield* sql<ClaimRow>`\n SELECT enabled, revision, overlap, active_run_id, active_claimed_at_ms, pending_at_ms\n FROM flows_triggers WHERE trigger_id = ${fire.triggerId}\n `\n const row = rows[0]\n if (row === undefined) return yield* Effect.fail(unknownTrigger(fire.triggerId))\n if (row.pending_at_ms === null) return Option.none()\n const occurrence = row.pending_at_ms\n const claim = yield* claimInTransaction(\n {\n triggerId: fire.triggerId,\n occurrence,\n expectedRevision: fire.expectedRevision,\n resumeBuffered: true\n },\n claimedAt,\n row\n )\n if (claim.claimed && claim.action !== \"buffer\") {\n yield* sql`UPDATE flows_triggers SET pending_at_ms = NULL WHERE trigger_id = ${fire.triggerId}`\n }\n return Option.some({ occurrence, claim })\n }))\n }),\n recordResult: (result) =>\n write(\n Effect.gen(function*() {\n yield* requireTrigger(result.triggerId)\n const rows = yield* sql<FireRow>`SELECT * FROM flows_trigger_fires\n WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`\n const held = yield* sql<{ readonly active_run_id: string | null }>`\n SELECT active_run_id FROM flows_triggers WHERE trigger_id = ${result.triggerId}`\n const existing = rows[0] === undefined ? undefined : fireRecord(rows[0])\n const active = held[0]!.active_run_id ?? undefined\n const refusal = resultRefusal(result, existing, active)\n if (refusal !== undefined) return yield* Effect.fail(refusal)\n if (result.outcome !== \"launched\" && existing?.outcome === result.outcome) return\n const terminal = result.outcome === \"completed\" || result.outcome === \"failed\" ||\n result.outcome === \"superseded\"\n const resultOwner = (existing?.outcome === \"launched\"\n ? existing.runId\n : result.reservationId ?? result.runId ?? active) ?? null\n if (terminal && result.runId === undefined) {\n // Keep the recorded owner so an unqualified late result can only\n // clear the run launched by this occurrence, never a newer one.\n yield* sql`UPDATE flows_trigger_fires\n SET outcome = ${result.outcome}, error = ${result.error ?? null}\n WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`\n } else {\n yield* sql`UPDATE flows_trigger_fires\n SET outcome = ${result.outcome}, run_id = ${result.runId ?? null}, error = ${result.error ?? null}\n WHERE trigger_id = ${result.triggerId} AND occurrence_at_ms = ${result.occurrence}`\n }\n // `last_fired_at_ms` is the cursor catch-up resumes from, so it only\n // ever moves forward. An older run settling after a newer occurrence\n // was skipped used to drag it backwards and replay settled work.\n if (result.outcome === \"launched\") {\n yield* sql`UPDATE flows_triggers\n SET last_fired_at_ms = ${\n Dialect.greatest(sql)\n }(COALESCE(last_fired_at_ms, ${result.occurrence}), ${result.occurrence}),\n active_run_id = ${result.runId},\n active_claimed_at_ms = NULL\n WHERE trigger_id = ${result.triggerId} AND active_run_id = ${result.reservationId}`\n return\n }\n yield* sql`UPDATE flows_triggers\n SET last_fired_at_ms = ${\n Dialect.greatest(sql)\n }(COALESCE(last_fired_at_ms, ${result.occurrence}), ${result.occurrence}),\n active_run_id = CASE\n WHEN active_run_id = ${resultOwner} THEN NULL\n ELSE active_run_id\n END,\n active_claimed_at_ms = CASE\n WHEN active_run_id = ${resultOwner} THEN NULL\n ELSE active_claimed_at_ms\n END\n WHERE trigger_id = ${result.triggerId}`\n })\n ).pipe(\n Effect.asVoid\n ),\n restorePending: (fire) =>\n write(Effect.gen(function*() {\n yield* requireTrigger(fire.triggerId)\n const rows = yield* sql<{ readonly pending_at_ms: number | null; readonly run_id: string | null }>`\n SELECT t.pending_at_ms, f.run_id FROM flows_triggers t JOIN flows_trigger_fires f\n ON f.trigger_id = t.trigger_id\n WHERE t.trigger_id = ${fire.triggerId} AND t.active_run_id = ${fire.reservationId}\n AND f.occurrence_at_ms = ${fire.occurrence} AND (f.outcome IS NULL OR f.outcome = 'buffered')`\n const row = rows[0]\n if (row === undefined || reservationOccurrence(fire.reservationId) !== fire.occurrence) {\n return yield* Effect.fail(\n new TriggerError({ code: \"stale_owner\", message: \"pending restoration no longer owns reservation\" })\n )\n }\n const pending = Overlap.pendingAfter({\n running: false,\n pending: row.pending_at_ms ?? undefined,\n due: fire.occurrence\n })\n const predecessors = row.run_id === null || isReservation(row.run_id) ?\n [] :\n yield* sql<{ readonly run_id: string }>`\n SELECT run_id FROM flows_trigger_fires\n WHERE trigger_id = ${fire.triggerId} AND run_id = ${row.run_id} AND outcome = 'launched'`\n const predecessor = predecessors[0]?.run_id ?? null\n yield* sql`UPDATE flows_triggers\n SET pending_at_ms = ${pending}, active_run_id = ${predecessor}, active_claimed_at_ms = NULL\n WHERE trigger_id = ${fire.triggerId} AND active_run_id = ${fire.reservationId}`\n })).pipe(Effect.asVoid),\n setPending: (fire) =>\n write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly pending_at_ms: number | null }>`\n SELECT pending_at_ms FROM flows_triggers WHERE trigger_id = ${fire.triggerId}\n `\n const row = rows[0]\n if (row === undefined) return yield* Effect.fail(unknownTrigger(fire.triggerId))\n const pending = Overlap.pendingAfter({\n running: true,\n pending: row.pending_at_ms ?? undefined,\n due: fire.occurrence\n })\n yield* sql`UPDATE flows_triggers SET pending_at_ms = ${pending} WHERE trigger_id = ${fire.triggerId}`\n })).pipe(Effect.asVoid),\n activeRun: (triggerId) =>\n Effect.gen(function*() {\n const now = yield* Clock.currentTimeMillis\n return yield* write(Effect.gen(function*() {\n const rows = yield* sql<{\n readonly active_run_id: string | null\n readonly active_claimed_at_ms: number | null\n readonly pending_at_ms: number | null\n }>`SELECT active_run_id, active_claimed_at_ms, pending_at_ms\n FROM flows_triggers WHERE trigger_id = ${triggerId}`\n const row = rows[0]\n if (row === undefined) return yield* Effect.fail(unknownTrigger(triggerId))\n const activeRunId = row.active_run_id ?? undefined\n const occurrence = activeRunId !== undefined && isReservation(activeRunId)\n ? reservationOccurrence(activeRunId)\n : undefined\n const unfinished = occurrence === undefined\n ? []\n : yield* sql<{ readonly outcome: string | null; readonly run_id: string | null }>`\n SELECT outcome, run_id FROM flows_trigger_fires\n WHERE trigger_id = ${triggerId} AND occurrence_at_ms = ${occurrence}\n AND (outcome IS NULL OR outcome = 'buffered')\n `\n const unfinishedFire = fireSnapshot(unfinished[0])\n const lease = ClaimDecision.lease({\n now,\n ...(activeRunId === undefined ? {} : { activeRunId }),\n ...(row.active_claimed_at_ms === null ? {} : { activeClaimedAt: row.active_claimed_at_ms }),\n ...(row.pending_at_ms === null ? {} : { pending: row.pending_at_ms }),\n ...(unfinishedFire === undefined ? {} : { unfinished: unfinishedFire })\n })\n if (lease._tag === \"Idle\") return Option.none()\n if (lease._tag === \"Held\") return Option.some(lease.runId)\n yield* lease.pending === undefined\n ? sql`UPDATE flows_triggers SET active_run_id = NULL, active_claimed_at_ms = NULL\n WHERE trigger_id = ${triggerId} AND active_run_id = ${lease.expected}`\n : sql`UPDATE flows_triggers\n SET active_run_id = ${lease.recovered ?? null}, active_claimed_at_ms = NULL,\n pending_at_ms = ${lease.pending}\n WHERE trigger_id = ${triggerId} AND active_run_id = ${lease.expected}`\n return lease.recovered === undefined ? Option.none() : Option.some(lease.recovered)\n }))\n }),\n activeOccurrence: (triggerId, runId) =>\n read(Effect.gen(function*() {\n yield* requireTrigger(triggerId)\n const reserved = reservationOccurrence(runId)\n if (reserved !== undefined) return Option.some(reserved)\n if (isReservation(runId)) return Option.none()\n const rows = yield* sql<{ readonly occurrence_at_ms: number }>`\n SELECT occurrence_at_ms FROM flows_trigger_fires\n WHERE trigger_id = ${triggerId} AND run_id = ${runId} AND outcome = 'launched'\n ORDER BY occurrence_at_ms DESC LIMIT 1\n `\n return rows[0] === undefined ? Option.none() : Option.some(rows[0].occurrence_at_ms)\n })),\n clearActive: (triggerId, runId) =>\n write(sql`UPDATE flows_triggers SET active_run_id = NULL, active_claimed_at_ms = NULL\n WHERE trigger_id = ${triggerId} AND active_run_id = ${runId}`).pipe(\n Effect.asVoid\n ),\n history: (query = {}) =>\n historyLimit(query.limit).pipe(\n Effect.flatMap((limit) =>\n // One statement for every query shape: a null parameter disables\n // its predicate, and SQLite reads `LIMIT -1` as no limit. One row\n // past the limit is fetched so the page knows whether a next page\n // exists without a second count query.\n read(sql<FireRow>`\n SELECT trigger_id, occurrence_at_ms, outcome, run_id, error FROM flows_trigger_fires\n WHERE ${query.triggerId === undefined ? sql`1 = 1` : sql`trigger_id = ${query.triggerId}`}\n AND ${query.runId === undefined ? sql`1 = 1` : sql`run_id = ${query.runId}`}\n AND ${query.outcome === undefined ? sql`1 = 1` : sql`outcome = ${query.outcome}`}\n AND (${query.cursor === undefined ? 1 : 0} = 1\n OR occurrence_at_ms < ${query.cursor?.occurrence ?? null}\n OR (occurrence_at_ms = ${query.cursor?.occurrence ?? null} AND trigger_id < ${\n query.cursor?.triggerId ?? null\n }))\n ORDER BY occurrence_at_ms DESC, trigger_id DESC\n LIMIT ${limit === undefined ? (Dialect.isPostgres(sql) ? null : -1) : limit + 1}\n `).pipe(Effect.map((rows) => historyPage(rows.map(fireRecord), limit)))\n )\n ),\n pruneFires: ({ olderThan }) =>\n pruneCutoff(olderThan).pipe(\n Effect.flatMap((cutoff) =>\n // The trigger row names the state the scheduler still reads: the\n // buffered occurrence and the run or reservation on `active_run_id`.\n // An unsettled row carries no outcome, so the outcome filter already\n // spares a reserved occurrence.\n write(Effect.gen(function*() {\n const deleted = yield* sql`\n DELETE FROM flows_trigger_fires\n WHERE occurrence_at_ms < ${cutoff}\n AND outcome IN ('completed', 'failed', 'skipped', 'superseded')\n AND NOT EXISTS (\n SELECT 1 FROM flows_triggers t\n WHERE t.trigger_id = flows_trigger_fires.trigger_id\n AND (t.pending_at_ms = flows_trigger_fires.occurrence_at_ms\n OR t.active_run_id = flows_trigger_fires.run_id)\n )\n `.raw\n return yield* affectedRows(deleted)\n }))\n )\n ),\n inspect: (triggerId) =>\n read(sql<{ readonly active_run_id: string | null; readonly pending_at_ms: number | null }>`\n SELECT active_run_id, pending_at_ms FROM flows_triggers WHERE trigger_id = ${triggerId}\n `).pipe(\n Effect.flatMap((rows) => {\n const row = rows[0]\n if (row === undefined) return Effect.fail(unknownTrigger(triggerId))\n return Effect.succeed({\n ...(row.active_run_id === null ? {} : { activeRunId: row.active_run_id }),\n ...(row.pending_at_ms === null ? {} : { pendingAt: row.pending_at_ms })\n })\n })\n ),\n heartbeat: (host) =>\n Effect.flatMap(\n Clock.currentTimeMillis,\n (tickedAt) =>\n write(sql`INSERT INTO flows_scheduler_heartbeat (host, ticked_at_ms) VALUES (${host}, ${tickedAt})\n ON CONFLICT (host) DO UPDATE SET ticked_at_ms = excluded.ticked_at_ms`)\n ).pipe(Effect.asVoid),\n lastHeartbeat: () =>\n read(sql<{ readonly host: string; readonly ticked_at_ms: number }>`\n SELECT host, ticked_at_ms FROM flows_scheduler_heartbeat ORDER BY ticked_at_ms DESC, host ASC LIMIT 1\n `).pipe(\n Effect.map((rows) =>\n rows[0] === undefined\n ? Option.none<Heartbeat>()\n : Option.some({ host: rows[0].host, tickedAt: rows[0].ticked_at_ms })\n )\n )\n }\n})\n\n/**\n * Provides {@link TriggerStore.TriggerStore} backed by SQL.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<\n TriggerStore,\n TriggerError,\n DurableWriter | SqlClient.SqlClient\n> = Layer.effect(TriggerStore)(make)\n"],
5
+ "mappings": "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",
6
+ "names": ["write"]
7
+ }