@swirls/looms 0.3.0 → 0.5.0

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 (253) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/actor/cell.d.ts +21 -2
  3. package/dist/actor/cell.js +15 -3
  4. package/dist/actor/host.d.ts +10 -2
  5. package/dist/actor/host.js +27 -4
  6. package/dist/actor/index.d.ts +2 -1
  7. package/dist/actor/index.js +1 -1
  8. package/dist/actor/routing.d.ts +3 -0
  9. package/dist/actor/routing.js +13 -2
  10. package/dist/agent/debugger/agent-chat.js +3 -30
  11. package/dist/agent/debugger/streaming-text.d.ts +6 -0
  12. package/dist/agent/debugger/streaming-text.js +33 -0
  13. package/dist/agent/definitions-store.d.ts +1 -1
  14. package/dist/agent/definitions-store.js +1 -1
  15. package/dist/agent/definitions.d.ts +48 -0
  16. package/dist/agent/definitions.js +44 -0
  17. package/dist/agent/effects.d.ts +184 -0
  18. package/dist/agent/effects.js +172 -40
  19. package/dist/agent/events.d.ts +89 -0
  20. package/dist/agent/events.js +25 -0
  21. package/dist/agent/module.d.ts +819 -407
  22. package/dist/agent/module.js +5 -1
  23. package/dist/agent/scope.d.ts +88 -0
  24. package/dist/agent/threads.js +39 -3
  25. package/dist/agent/types.d.ts +4 -0
  26. package/dist/approval/effects.d.ts +40 -0
  27. package/dist/approval/module.d.ts +80 -0
  28. package/dist/client/client.d.ts +468 -119
  29. package/dist/client/client.js +214 -21
  30. package/dist/client/index.d.ts +1 -1
  31. package/dist/client/sse.d.ts +1 -1
  32. package/dist/client/sse.js +2 -2
  33. package/dist/cloudflare/durable-object.d.ts +66 -2
  34. package/dist/cloudflare/durable-object.js +96 -1
  35. package/dist/cloudflare/event-store.d.ts +7 -3
  36. package/dist/cloudflare/event-store.js +16 -3
  37. package/dist/cloudflare/index.d.ts +7 -1
  38. package/dist/cloudflare/index.js +7 -1
  39. package/dist/cloudflare/memory-queue.d.ts +18 -0
  40. package/dist/cloudflare/memory-queue.js +53 -0
  41. package/dist/cloudflare/queue-consumer.d.ts +52 -0
  42. package/dist/cloudflare/queue-consumer.js +102 -0
  43. package/dist/cloudflare/queue-message.d.ts +31 -0
  44. package/dist/cloudflare/queue-message.js +33 -0
  45. package/dist/cloudflare/queue-pull.d.ts +62 -0
  46. package/dist/cloudflare/queue-pull.js +130 -0
  47. package/dist/cloudflare/queue-worker.d.ts +39 -0
  48. package/dist/cloudflare/queue-worker.js +62 -0
  49. package/dist/cloudflare/r2-blob-store.d.ts +33 -0
  50. package/dist/cloudflare/r2-blob-store.js +60 -0
  51. package/dist/cloudflare/router.d.ts +4 -1
  52. package/dist/cloudflare/router.js +8 -3
  53. package/dist/cloudflare/websocket.d.ts +27 -0
  54. package/dist/cloudflare/websocket.js +215 -0
  55. package/dist/core/attempts.d.ts +8 -0
  56. package/dist/core/attempts.js +27 -0
  57. package/dist/core/blob-offload.d.ts +21 -0
  58. package/dist/core/blob-offload.js +143 -0
  59. package/dist/core/blob-store.d.ts +45 -0
  60. package/dist/core/blob-store.js +164 -0
  61. package/dist/core/blobs.d.ts +37 -0
  62. package/dist/core/blobs.js +160 -0
  63. package/dist/core/definition-resolver.d.ts +40 -0
  64. package/dist/core/definition-resolver.js +129 -0
  65. package/dist/core/definition-store.d.ts +6 -0
  66. package/dist/core/definition-store.js +37 -1
  67. package/dist/core/effects.d.ts +86 -3
  68. package/dist/core/effects.js +99 -3
  69. package/dist/core/envelope.d.ts +9 -0
  70. package/dist/core/envelope.js +4 -0
  71. package/dist/core/event-codec.js +3 -0
  72. package/dist/core/fold.js +83 -1
  73. package/dist/core/ids.d.ts +2 -0
  74. package/dist/core/ids.js +4 -0
  75. package/dist/core/index.d.ts +10 -0
  76. package/dist/core/index.js +9 -0
  77. package/dist/core/launcher.d.ts +97 -0
  78. package/dist/core/launcher.js +67 -0
  79. package/dist/core/module-scope.d.ts +7 -3
  80. package/dist/core/module-scope.js +3 -0
  81. package/dist/core/module.d.ts +10 -0
  82. package/dist/core/projection.d.ts +5 -4
  83. package/dist/core/projection.js +4 -0
  84. package/dist/core/protocol.d.ts +36 -1
  85. package/dist/core/protocol.js +35 -0
  86. package/dist/core/published.d.ts +2 -0
  87. package/dist/core/published.js +4 -1
  88. package/dist/{react → core}/register.d.ts +9 -8
  89. package/dist/core/replay.d.ts +96 -0
  90. package/dist/core/run-context.d.ts +33 -0
  91. package/dist/core/run-context.js +23 -0
  92. package/dist/core/sqlite-store.js +57 -5
  93. package/dist/core/state.d.ts +60 -0
  94. package/dist/core/state.js +19 -0
  95. package/dist/core/store.d.ts +6 -0
  96. package/dist/core/store.js +7 -0
  97. package/dist/core/subscription.d.ts +97 -0
  98. package/dist/core/subscription.js +224 -0
  99. package/dist/core/thread.d.ts +7 -0
  100. package/dist/core/thread.js +1 -0
  101. package/dist/core/trace.d.ts +21 -0
  102. package/dist/core/trace.js +45 -0
  103. package/dist/core/worker-callback.d.ts +25 -1
  104. package/dist/debugger/app/assets/{index-CgDzWJg7.css → index-B7GdT1UA.css} +1 -1
  105. package/dist/debugger/app/assets/index-C31SnCuN.js +22 -0
  106. package/dist/debugger/app/assets/routes-DPA3O5AF.js +47 -0
  107. package/dist/debugger/app/index.html +2 -2
  108. package/dist/debugger/components/event-inspector.d.ts +3 -1
  109. package/dist/debugger/components/event-inspector.js +25 -4
  110. package/dist/debugger/components/event-stream.d.ts +4 -1
  111. package/dist/debugger/components/event-stream.js +2 -2
  112. package/dist/debugger/components/run-tree.d.ts +5 -2
  113. package/dist/debugger/components/run-tree.js +10 -4
  114. package/dist/debugger/context.d.ts +8 -1
  115. package/dist/debugger/context.js +15 -3
  116. package/dist/debugger/events/protocol-summarize.js +18 -3
  117. package/dist/debugger/index.d.ts +1 -1
  118. package/dist/debugger/index.js +1 -1
  119. package/dist/debugger/shell/debugger-shell.js +1 -1
  120. package/dist/debugger/shell/events-pane.js +4 -3
  121. package/dist/debugger/styles.css +1 -0
  122. package/dist/evaluator/definitions-store.d.ts +3 -4
  123. package/dist/evaluator/definitions-store.js +3 -3
  124. package/dist/evaluator/definitions.d.ts +4 -1
  125. package/dist/evaluator/definitions.js +5 -2
  126. package/dist/evaluator/effects.d.ts +41 -0
  127. package/dist/evaluator/effects.js +6 -2
  128. package/dist/evaluator/module.d.ts +84 -0
  129. package/dist/evaluator/module.js +7 -3
  130. package/dist/evaluator/threads.d.ts +2 -0
  131. package/dist/evaluator/threads.js +4 -0
  132. package/dist/projectors/event-log.d.ts +87 -0
  133. package/dist/projectors/event-log.js +396 -0
  134. package/dist/projectors/index-model.d.ts +12 -0
  135. package/dist/projectors/index-model.js +12 -3
  136. package/dist/projectors/memory.js +7 -0
  137. package/dist/projectors/postgres.d.ts +1 -0
  138. package/dist/projectors/postgres.js +32 -15
  139. package/dist/projectors/schema.d.ts +5 -0
  140. package/dist/projectors/schema.js +26 -1
  141. package/dist/projectors/sqlite.js +47 -19
  142. package/dist/projectors/with-projectors.d.ts +7 -1
  143. package/dist/projectors/with-projectors.js +13 -3
  144. package/dist/react/derived.d.ts +26 -8
  145. package/dist/react/derived.js +23 -26
  146. package/dist/react/index.d.ts +3 -3
  147. package/dist/react/index.js +2 -2
  148. package/dist/react/materialize.js +5 -1
  149. package/dist/react/react.d.ts +16 -9
  150. package/dist/react/react.js +47 -23
  151. package/dist/react/store.d.ts +14 -6
  152. package/dist/react/store.js +104 -12
  153. package/dist/react/tables.d.ts +4 -1
  154. package/dist/runtime/cancellation.d.ts +2 -1
  155. package/dist/runtime/cancellation.js +35 -2
  156. package/dist/runtime/child-run.d.ts +10 -0
  157. package/dist/runtime/child-run.js +58 -0
  158. package/dist/runtime/deadline-transition-planner.js +11 -2
  159. package/dist/runtime/dispatch-effect.d.ts +2 -2
  160. package/dist/runtime/dispatch-effect.js +43 -19
  161. package/dist/runtime/errors.d.ts +14 -1
  162. package/dist/runtime/errors.js +10 -0
  163. package/dist/runtime/filesystem-blob-store.d.ts +6 -0
  164. package/dist/runtime/filesystem-blob-store.js +59 -0
  165. package/dist/runtime/helpers.d.ts +15 -6
  166. package/dist/runtime/helpers.js +46 -6
  167. package/dist/runtime/index.d.ts +4 -2
  168. package/dist/runtime/index.js +3 -1
  169. package/dist/runtime/local-effect-executor.d.ts +11 -2
  170. package/dist/runtime/local-effect-executor.js +80 -4
  171. package/dist/runtime/looms.d.ts +38 -4
  172. package/dist/runtime/looms.js +17 -3
  173. package/dist/runtime/repository.d.ts +5 -4
  174. package/dist/runtime/repository.js +5 -2
  175. package/dist/runtime/retry-policy.d.ts +5 -0
  176. package/dist/runtime/retry-policy.js +10 -2
  177. package/dist/runtime/run-start.d.ts +1 -1
  178. package/dist/runtime/run-start.js +10 -0
  179. package/dist/runtime/runtime.d.ts +2 -2
  180. package/dist/runtime/runtime.js +191 -68
  181. package/dist/runtime/server/auth.d.ts +3 -0
  182. package/dist/runtime/server/fetch-handler.d.ts +2 -2
  183. package/dist/runtime/server/fetch-handler.js +35 -4
  184. package/dist/runtime/server/grant.d.ts +49 -0
  185. package/dist/runtime/server/grant.js +177 -0
  186. package/dist/runtime/server/routes/effects.js +47 -5
  187. package/dist/runtime/server/routes/runs.js +6 -1
  188. package/dist/runtime/server/run-route.d.ts +18 -0
  189. package/dist/runtime/server/run-route.js +26 -0
  190. package/dist/runtime/server.d.ts +3 -1
  191. package/dist/runtime/server.js +3 -1
  192. package/dist/runtime/sse.js +26 -4
  193. package/dist/runtime/subscription.d.ts +8 -0
  194. package/dist/runtime/subscription.js +23 -0
  195. package/dist/runtime/types.d.ts +87 -8
  196. package/dist/runtime/wake-engine.d.ts +4 -2
  197. package/dist/runtime/wake-engine.js +57 -6
  198. package/dist/runtime/worker-callbacks.d.ts +12 -2
  199. package/dist/runtime/worker-callbacks.js +102 -72
  200. package/dist/runtime/worker-effect-dispatch.d.ts +14 -5
  201. package/dist/runtime/worker-effect-dispatch.js +84 -18
  202. package/dist/schedule/debugger/index.d.ts +4 -0
  203. package/dist/schedule/debugger/index.js +11 -0
  204. package/dist/schedule/debugger/schedule-view.d.ts +3 -0
  205. package/dist/schedule/debugger/schedule-view.js +68 -0
  206. package/dist/schedule/debugger/summarize.d.ts +2 -0
  207. package/dist/schedule/debugger/summarize.js +43 -0
  208. package/dist/schedule/definitions-store.d.ts +10 -0
  209. package/dist/schedule/definitions-store.js +8 -0
  210. package/dist/schedule/definitions.d.ts +57 -0
  211. package/dist/schedule/definitions.js +30 -0
  212. package/dist/schedule/effects.d.ts +756 -0
  213. package/dist/schedule/effects.js +244 -0
  214. package/dist/schedule/ensure.d.ts +55 -0
  215. package/dist/schedule/ensure.js +109 -0
  216. package/dist/schedule/events.d.ts +116 -0
  217. package/dist/schedule/events.js +49 -0
  218. package/dist/schedule/index.d.ts +11 -0
  219. package/dist/schedule/index.js +11 -0
  220. package/dist/schedule/module.d.ts +1474 -0
  221. package/dist/schedule/module.js +22 -0
  222. package/dist/schedule/projections.d.ts +55 -0
  223. package/dist/schedule/projections.js +88 -0
  224. package/dist/schedule/scope.d.ts +121 -0
  225. package/dist/schedule/scope.js +7 -0
  226. package/dist/schedule/signals.d.ts +25 -0
  227. package/dist/schedule/signals.js +33 -0
  228. package/dist/schedule/specs.d.ts +59 -0
  229. package/dist/schedule/specs.js +231 -0
  230. package/dist/schedule/threads.d.ts +25 -0
  231. package/dist/schedule/threads.js +259 -0
  232. package/dist/testing/corpus-module.d.ts +34 -0
  233. package/dist/testing/corpus-module.js +87 -0
  234. package/dist/testing/index.d.ts +2 -0
  235. package/dist/testing/index.js +5 -6
  236. package/dist/testing/replay-corpus.d.ts +22 -0
  237. package/dist/testing/replay-corpus.js +50 -0
  238. package/dist/workflow/definitions-store.d.ts +1 -1
  239. package/dist/workflow/definitions-store.js +1 -1
  240. package/dist/workflow/definitions.d.ts +14 -0
  241. package/dist/workflow/effects.d.ts +122 -0
  242. package/dist/workflow/effects.js +16 -6
  243. package/dist/workflow/events.d.ts +42 -0
  244. package/dist/workflow/events.js +4 -0
  245. package/dist/workflow/module.d.ts +345 -32
  246. package/dist/workflow/module.js +7 -3
  247. package/dist/workflow/scope.d.ts +42 -0
  248. package/dist/workflow/threads.d.ts +72 -0
  249. package/dist/workflow/threads.js +106 -38
  250. package/package.json +11 -2
  251. package/dist/debugger/app/assets/index-BevQYa-U.js +0 -22
  252. package/dist/debugger/app/assets/routes-B17Om93i.js +0 -47
  253. /package/dist/{react → core}/register.js +0 -0
@@ -0,0 +1,244 @@
1
+ import { Clock, Data, Effect, Schema } from 'effect';
2
+ import { DEFAULT_DEFINITION_VERSION, RunLauncherTag } from '@swirls/looms/core';
3
+ import { ScheduleMisfireSchema, ScheduleOverlapSchema, } from './definitions.js';
4
+ import { ScheduleDefinitionsTag } from './definitions-store.js';
5
+ import { scheduleModule } from './scope.js';
6
+ import { MISFIRE_GRACE_MS, ScheduleSpecSchema, nextFireAt } from './specs.js';
7
+ const NullableFinite = Schema.NullOr(Schema.Finite);
8
+ const NullableJson = Schema.NullOr(Schema.Json);
9
+ const PlanInputSchema = Schema.Struct({
10
+ spec: ScheduleSpecSchema,
11
+ generation: Schema.Finite,
12
+ fireCount: Schema.Finite,
13
+ startAt: NullableFinite,
14
+ endAt: NullableFinite,
15
+ maxFires: NullableFinite,
16
+ misfire: ScheduleMisfireSchema,
17
+ });
18
+ const FireInputSchema = Schema.Struct({
19
+ definitionName: Schema.String,
20
+ definitionVersion: Schema.String,
21
+ spec: ScheduleSpecSchema,
22
+ generation: Schema.Finite,
23
+ at: Schema.Finite,
24
+ nextAt: NullableFinite,
25
+ misfire: ScheduleMisfireSchema,
26
+ overlap: ScheduleOverlapSchema,
27
+ storedInput: NullableJson,
28
+ fireCount: Schema.Finite,
29
+ startAt: NullableFinite,
30
+ endAt: NullableFinite,
31
+ maxFires: NullableFinite,
32
+ lastFiredRunId: Schema.NullOr(Schema.String),
33
+ manual: Schema.Boolean,
34
+ triggerId: Schema.optional(Schema.String),
35
+ });
36
+ const RETRY = { maxAttempts: 5, backoffMs: 1_000, maxBackoffMs: 60_000 };
37
+ class ScheduleEffectError extends Data.TaggedError('ScheduleEffectError') {
38
+ }
39
+ export const planEffect = scheduleModule.effect({
40
+ type: 'schedule.plan',
41
+ input: PlanInputSchema,
42
+ retry: RETRY,
43
+ execute: (input, ctx) => Effect.gen(function* () {
44
+ const now = yield* Clock.currentTimeMillis;
45
+ const nextAt = planNextAt(input, now);
46
+ return [
47
+ {
48
+ type: 'schedule.planned',
49
+ payload: { nextAt, generation: input.generation, spec: input.spec },
50
+ threadId: ctx.threadId,
51
+ },
52
+ ];
53
+ }),
54
+ });
55
+ export const fireEffect = scheduleModule.effect({
56
+ type: 'schedule.fire',
57
+ input: FireInputSchema,
58
+ retry: RETRY,
59
+ execute: (input, ctx) => Effect.gen(function* () {
60
+ const definitions = yield* ScheduleDefinitionsTag;
61
+ const launcher = yield* RunLauncherTag;
62
+ const now = yield* Clock.currentTimeMillis;
63
+ const definition = yield* definitions.resolve(input.definitionName, input.definitionVersion);
64
+ if (!definition) {
65
+ return yield* new ScheduleEffectError({
66
+ message: `Unknown schedule definition "${input.definitionName}@${input.definitionVersion}"`,
67
+ });
68
+ }
69
+ if (!input.manual && input.misfire === 'skip' && now > input.at + MISFIRE_GRACE_MS) {
70
+ return skipped(ctx.threadId, input, input.at, 'misfire', scheduledNext(input, now, now));
71
+ }
72
+ const fireCtx = {
73
+ at: input.manual ? now : input.at,
74
+ input: input.storedInput,
75
+ runId: ctx.runId,
76
+ manual: input.manual,
77
+ };
78
+ const gate = yield* resolveBeforeFire(definition, fireCtx);
79
+ if (gate !== 'fire') {
80
+ return skipped(ctx.threadId, input, fireCtx.at, gate.skip, input.manual ? input.nextAt : scheduledNext(input, input.at, now));
81
+ }
82
+ if (input.overlap === 'skip' && input.lastFiredRunId) {
83
+ const previous = yield* launcher.status(input.lastFiredRunId);
84
+ if (previous &&
85
+ previous.status !== 'completed' &&
86
+ previous.status !== 'failed' &&
87
+ previous.status !== 'cancelled') {
88
+ return skipped(ctx.threadId, input, fireCtx.at, 'overlap', input.manual ? input.nextAt : scheduledNext(input, input.at, now));
89
+ }
90
+ }
91
+ const produced = yield* resolveFireInput(definition, fireCtx);
92
+ const targetRunId = input.manual && input.triggerId
93
+ ? `${ctx.runId}:manual:${input.triggerId}`
94
+ : `${ctx.runId}:fire:${input.at}`;
95
+ yield* launcher.launch({
96
+ runId: targetRunId,
97
+ kind: definition.target.kind,
98
+ definitionName: definition.target.name,
99
+ definitionVersion: definition.target.version ?? DEFAULT_DEFINITION_VERSION,
100
+ input: produced,
101
+ idempotencyKey: targetRunId,
102
+ });
103
+ const nextFireCount = input.fireCount + 1;
104
+ const nextAt = input.manual
105
+ ? input.maxFires !== null && nextFireCount >= input.maxFires
106
+ ? null
107
+ : input.nextAt
108
+ : scheduledNext(input, input.at, now, nextFireCount);
109
+ const payload = {
110
+ at: fireCtx.at,
111
+ runId: targetRunId,
112
+ nextAt,
113
+ generation: input.generation,
114
+ manual: input.manual,
115
+ };
116
+ return [
117
+ {
118
+ type: 'schedule.fired',
119
+ payload: input.triggerId === undefined ? payload : { ...payload, triggerId: input.triggerId },
120
+ threadId: ctx.threadId,
121
+ },
122
+ ];
123
+ }),
124
+ });
125
+ export function planInputOf(state) {
126
+ return {
127
+ spec: state.spec,
128
+ generation: state.generation,
129
+ fireCount: state.fireCount,
130
+ startAt: state.startAt,
131
+ endAt: state.endAt,
132
+ maxFires: state.maxFires,
133
+ misfire: state.misfire,
134
+ };
135
+ }
136
+ export function fireInputOf(state, at, manual, triggerId) {
137
+ const input = {
138
+ definitionName: state.definitionName,
139
+ definitionVersion: state.definitionVersion,
140
+ spec: state.spec,
141
+ generation: state.generation,
142
+ at,
143
+ nextAt: state.nextAt,
144
+ misfire: state.misfire,
145
+ overlap: state.overlap,
146
+ storedInput: state.input,
147
+ fireCount: state.fireCount,
148
+ startAt: state.startAt,
149
+ endAt: state.endAt,
150
+ maxFires: state.maxFires,
151
+ lastFiredRunId: state.lastFiredRunId,
152
+ manual,
153
+ };
154
+ if (triggerId === undefined) {
155
+ return input;
156
+ }
157
+ return { ...input, triggerId };
158
+ }
159
+ function planNextAt(input, now) {
160
+ if (input.maxFires !== null && input.fireCount >= input.maxFires) {
161
+ return null;
162
+ }
163
+ const spec = input.spec;
164
+ let next;
165
+ if (spec.type === 'once') {
166
+ if (input.startAt !== null && spec.at < input.startAt) {
167
+ next = null;
168
+ }
169
+ else if (spec.at > now || input.misfire === 'fire-once') {
170
+ next = spec.at;
171
+ }
172
+ else {
173
+ next = null;
174
+ }
175
+ }
176
+ else {
177
+ const notBefore = input.startAt !== null ? Math.max(now, input.startAt) : now;
178
+ next = nextFireAt(spec, notBefore - 1);
179
+ if (next !== null && next < notBefore) {
180
+ next = nextFireAt(spec, notBefore);
181
+ }
182
+ }
183
+ return withinEndAt(next, input.endAt);
184
+ }
185
+ function scheduledNext(input, after, now, firesAfter = input.fireCount) {
186
+ if (input.maxFires !== null && firesAfter >= input.maxFires) {
187
+ return null;
188
+ }
189
+ const floor = input.startAt !== null ? Math.max(now, input.startAt) : now;
190
+ let next = nextFireAt(input.spec, after);
191
+ if (next !== null && next <= floor) {
192
+ next = nextFireAt(input.spec, floor);
193
+ }
194
+ return withinEndAt(next, input.endAt);
195
+ }
196
+ function withinEndAt(next, endAt) {
197
+ if (next !== null && endAt !== null && next > endAt) {
198
+ return null;
199
+ }
200
+ return next;
201
+ }
202
+ function skipped(threadId, input, at, reason, nextAt) {
203
+ const payload = {
204
+ at,
205
+ reason,
206
+ nextAt,
207
+ generation: input.generation,
208
+ manual: input.manual,
209
+ triggerId: input.triggerId,
210
+ };
211
+ return [
212
+ {
213
+ type: 'schedule.skipped',
214
+ payload,
215
+ threadId,
216
+ },
217
+ ];
218
+ }
219
+ function resolveBeforeFire(definition, ctx) {
220
+ const decide = definition.beforeFire;
221
+ if (!decide) {
222
+ return Effect.succeed('fire');
223
+ }
224
+ return Effect.tryPromise({
225
+ try: () => Promise.resolve(decide(ctx)),
226
+ catch: (cause) => new ScheduleEffectError({
227
+ message: cause instanceof Error ? cause.message : 'beforeFire failed',
228
+ cause,
229
+ }),
230
+ });
231
+ }
232
+ function resolveFireInput(definition, ctx) {
233
+ const produce = definition.fireInput;
234
+ if (!produce) {
235
+ return Effect.succeed(ctx.input);
236
+ }
237
+ return Effect.tryPromise({
238
+ try: () => Promise.resolve(produce(ctx)),
239
+ catch: (cause) => new ScheduleEffectError({
240
+ message: cause instanceof Error ? cause.message : 'schedule input failed',
241
+ cause,
242
+ }),
243
+ });
244
+ }
@@ -0,0 +1,55 @@
1
+ import { Schema } from 'effect';
2
+ import { type EventInput, type JsonValue, type LaunchRunArgs, type RunState } from '@swirls/looms/core';
3
+ import type { ScheduleDefinition } from './definitions';
4
+ import { type ScheduleSpec } from './specs';
5
+ declare const ScheduleSnapshotSchema: Schema.Struct<{
6
+ readonly spec: Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
7
+ readonly type: Schema.Literal<"cron">;
8
+ readonly expr: Schema.String;
9
+ readonly tz: Schema.optional<Schema.String>;
10
+ }>, Schema.Struct<{
11
+ readonly type: Schema.Literal<"every">;
12
+ readonly everyMs: Schema.Finite;
13
+ readonly anchor: Schema.optional<Schema.Finite>;
14
+ }>, Schema.Struct<{
15
+ readonly type: Schema.Literal<"once">;
16
+ readonly at: Schema.Finite;
17
+ }>]>>;
18
+ readonly input: Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
19
+ readonly endAt: Schema.NullOr<Schema.Finite>;
20
+ readonly maxFires: Schema.NullOr<Schema.Finite>;
21
+ readonly paused: Schema.Boolean;
22
+ readonly retired: Schema.Boolean;
23
+ readonly generation: Schema.Finite;
24
+ readonly definitionName: Schema.String;
25
+ readonly definitionVersion: Schema.String;
26
+ }>;
27
+ export type ScheduleSnapshot = Schema.Schema.Type<typeof ScheduleSnapshotSchema>;
28
+ /**
29
+ * The three operations a deploy reconciler needs. `createLooms` already has them.
30
+ * On Cloudflare, use `durableObjectScheduleHost`.
31
+ */
32
+ export interface ScheduleHost {
33
+ startRun(args: LaunchRunArgs): Promise<{
34
+ readonly runId: string;
35
+ }>;
36
+ signal(runId: string, events: ReadonlyArray<EventInput>): Promise<RunState>;
37
+ getRun(runId: string): Promise<RunState>;
38
+ }
39
+ export interface ScheduleEnsureConfig {
40
+ readonly spec?: ScheduleSpec;
41
+ readonly input?: JsonValue | null;
42
+ readonly startAt?: number | null;
43
+ readonly endAt?: number | null;
44
+ readonly maxFires?: number | null;
45
+ }
46
+ export type ScheduleEnsureResult = 'started' | 'updated' | 'resumed' | 'unchanged';
47
+ /**
48
+ * Start a schedule, or bring an existing one in line with `config`.
49
+ * A retired schedule is resumed in place. A cancelled, completed, or failed one throws:
50
+ * those run ids cannot be reused.
51
+ */
52
+ export declare function ensureSchedule(host: ScheduleHost, definition: ScheduleDefinition, runId: string, config?: ScheduleEnsureConfig): Promise<ScheduleEnsureResult>;
53
+ /** The schedule thread's config, or null when the run has not started. */
54
+ export declare function readSchedule(state: RunState): ScheduleSnapshot | null;
55
+ export {};
@@ -0,0 +1,109 @@
1
+ import { Option, Schema } from 'effect';
2
+ import { stringifyJson, } from '@swirls/looms/core';
3
+ import { resume, update } from './signals.js';
4
+ import { ScheduleSpecSchema } from './specs.js';
5
+ const ScheduleSnapshotSchema = Schema.Struct({
6
+ spec: Schema.NullOr(ScheduleSpecSchema),
7
+ input: Schema.NullOr(Schema.Json),
8
+ endAt: Schema.NullOr(Schema.Finite),
9
+ maxFires: Schema.NullOr(Schema.Finite),
10
+ paused: Schema.Boolean,
11
+ retired: Schema.Boolean,
12
+ generation: Schema.Finite,
13
+ definitionName: Schema.String,
14
+ definitionVersion: Schema.String,
15
+ });
16
+ const decodeSnapshot = Schema.decodeUnknownOption(ScheduleSnapshotSchema);
17
+ /**
18
+ * Start a schedule, or bring an existing one in line with `config`.
19
+ * A retired schedule is resumed in place. A cancelled, completed, or failed one throws:
20
+ * those run ids cannot be reused.
21
+ */
22
+ export function ensureSchedule(host, definition, runId, config = {}) {
23
+ return host.getRun(runId).then((state) => {
24
+ const current = readSchedule(state);
25
+ if (!current) {
26
+ return host
27
+ .startRun({
28
+ runId,
29
+ kind: definition.kind,
30
+ definitionName: definition.name,
31
+ definitionVersion: definition.version,
32
+ input: startInput(config),
33
+ })
34
+ .then(() => 'started');
35
+ }
36
+ if (state.status !== 'running') {
37
+ throw new Error(`Schedule "${runId}" is ${state.status}; cancelled schedules need a new runId`);
38
+ }
39
+ const desired = {
40
+ spec: config.spec ?? definition.spec,
41
+ input: config.input ?? null,
42
+ endAt: config.endAt ?? null,
43
+ maxFires: config.maxFires ?? null,
44
+ definitionName: definition.name,
45
+ definitionVersion: definition.version,
46
+ };
47
+ const changed = stringifyJson(current.spec) !== stringifyJson(desired.spec) ||
48
+ stringifyJson(current.input) !== stringifyJson(desired.input) ||
49
+ current.endAt !== desired.endAt ||
50
+ current.maxFires !== desired.maxFires ||
51
+ current.definitionName !== desired.definitionName ||
52
+ current.definitionVersion !== desired.definitionVersion;
53
+ const events = [];
54
+ if (changed) {
55
+ events.push(update({
56
+ spec: desired.spec,
57
+ input: desired.input,
58
+ endAt: desired.endAt,
59
+ maxFires: desired.maxFires,
60
+ definitionName: desired.definitionName,
61
+ definitionVersion: desired.definitionVersion,
62
+ }));
63
+ }
64
+ if (current.paused || current.retired) {
65
+ events.push(resume());
66
+ }
67
+ if (events.length === 0) {
68
+ return 'unchanged';
69
+ }
70
+ return host
71
+ .signal(runId, events)
72
+ .then(() => (changed ? 'updated' : 'resumed'));
73
+ });
74
+ }
75
+ /** The schedule thread's config, or null when the run has not started. */
76
+ export function readSchedule(state) {
77
+ const threadId = state.rootThreadId;
78
+ if (!threadId) {
79
+ return null;
80
+ }
81
+ const thread = state.threads[threadId];
82
+ if (!thread) {
83
+ return null;
84
+ }
85
+ const decoded = decodeSnapshot(thread.state);
86
+ if (Option.isNone(decoded)) {
87
+ throw new Error(`Schedule "${state.runId}" state is unreadable`);
88
+ }
89
+ return decoded.value;
90
+ }
91
+ function startInput(config) {
92
+ const input = {};
93
+ if (config.spec !== undefined) {
94
+ input.spec = config.spec;
95
+ }
96
+ if (config.input !== undefined) {
97
+ input.input = config.input;
98
+ }
99
+ if (config.startAt !== undefined) {
100
+ input.startAt = config.startAt;
101
+ }
102
+ if (config.endAt !== undefined) {
103
+ input.endAt = config.endAt;
104
+ }
105
+ if (config.maxFires !== undefined) {
106
+ input.maxFires = config.maxFires;
107
+ }
108
+ return input;
109
+ }
@@ -0,0 +1,116 @@
1
+ import { Schema } from 'effect';
2
+ export declare const SchedulePlannedPayloadSchema: Schema.Struct<{
3
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
4
+ readonly generation: Schema.Finite;
5
+ readonly spec: Schema.Union<readonly [Schema.Struct<{
6
+ readonly type: Schema.Literal<"cron">;
7
+ readonly expr: Schema.String;
8
+ readonly tz: Schema.optional<Schema.String>;
9
+ }>, Schema.Struct<{
10
+ readonly type: Schema.Literal<"every">;
11
+ readonly everyMs: Schema.Finite;
12
+ readonly anchor: Schema.optional<Schema.Finite>;
13
+ }>, Schema.Struct<{
14
+ readonly type: Schema.Literal<"once">;
15
+ readonly at: Schema.Finite;
16
+ }>]>;
17
+ }>;
18
+ export declare const ScheduleFiredPayloadSchema: Schema.Struct<{
19
+ readonly at: Schema.Finite;
20
+ readonly runId: Schema.String;
21
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
22
+ readonly generation: Schema.Finite;
23
+ readonly manual: Schema.Boolean;
24
+ readonly triggerId: Schema.optional<Schema.String>;
25
+ }>;
26
+ export declare const ScheduleSkippedPayloadSchema: Schema.Struct<{
27
+ readonly at: Schema.Finite;
28
+ readonly reason: Schema.String;
29
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
30
+ readonly generation: Schema.Finite;
31
+ readonly manual: Schema.Boolean;
32
+ readonly triggerId: Schema.optional<Schema.String>;
33
+ }>;
34
+ export declare const ScheduleUpdatedPayloadSchema: Schema.Struct<{
35
+ readonly spec: Schema.optional<Schema.Union<readonly [Schema.Struct<{
36
+ readonly type: Schema.Literal<"cron">;
37
+ readonly expr: Schema.String;
38
+ readonly tz: Schema.optional<Schema.String>;
39
+ }>, Schema.Struct<{
40
+ readonly type: Schema.Literal<"every">;
41
+ readonly everyMs: Schema.Finite;
42
+ readonly anchor: Schema.optional<Schema.Finite>;
43
+ }>, Schema.Struct<{
44
+ readonly type: Schema.Literal<"once">;
45
+ readonly at: Schema.Finite;
46
+ }>]>>;
47
+ readonly input: Schema.optional<Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
48
+ readonly endAt: Schema.optional<Schema.NullOr<Schema.Finite>>;
49
+ readonly maxFires: Schema.optional<Schema.NullOr<Schema.Finite>>;
50
+ readonly definitionName: Schema.optional<Schema.String>;
51
+ readonly definitionVersion: Schema.optional<Schema.String>;
52
+ }>;
53
+ export declare const ScheduleEmptyPayloadSchema: Schema.Struct<{}>;
54
+ export declare const ScheduleRejectedPayloadSchema: Schema.Struct<{
55
+ readonly reason: Schema.String;
56
+ }>;
57
+ export declare const scheduleCatalog: import("@swirls/looms/core").EventCatalog<"schedule", {
58
+ planned: Schema.Struct<{
59
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
60
+ readonly generation: Schema.Finite;
61
+ readonly spec: Schema.Union<readonly [Schema.Struct<{
62
+ readonly type: Schema.Literal<"cron">;
63
+ readonly expr: Schema.String;
64
+ readonly tz: Schema.optional<Schema.String>;
65
+ }>, Schema.Struct<{
66
+ readonly type: Schema.Literal<"every">;
67
+ readonly everyMs: Schema.Finite;
68
+ readonly anchor: Schema.optional<Schema.Finite>;
69
+ }>, Schema.Struct<{
70
+ readonly type: Schema.Literal<"once">;
71
+ readonly at: Schema.Finite;
72
+ }>]>;
73
+ }>;
74
+ fired: Schema.Struct<{
75
+ readonly at: Schema.Finite;
76
+ readonly runId: Schema.String;
77
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
78
+ readonly generation: Schema.Finite;
79
+ readonly manual: Schema.Boolean;
80
+ readonly triggerId: Schema.optional<Schema.String>;
81
+ }>;
82
+ skipped: Schema.Struct<{
83
+ readonly at: Schema.Finite;
84
+ readonly reason: Schema.String;
85
+ readonly nextAt: Schema.NullOr<Schema.Finite>;
86
+ readonly generation: Schema.Finite;
87
+ readonly manual: Schema.Boolean;
88
+ readonly triggerId: Schema.optional<Schema.String>;
89
+ }>;
90
+ updated: Schema.Struct<{
91
+ readonly spec: Schema.optional<Schema.Union<readonly [Schema.Struct<{
92
+ readonly type: Schema.Literal<"cron">;
93
+ readonly expr: Schema.String;
94
+ readonly tz: Schema.optional<Schema.String>;
95
+ }>, Schema.Struct<{
96
+ readonly type: Schema.Literal<"every">;
97
+ readonly everyMs: Schema.Finite;
98
+ readonly anchor: Schema.optional<Schema.Finite>;
99
+ }>, Schema.Struct<{
100
+ readonly type: Schema.Literal<"once">;
101
+ readonly at: Schema.Finite;
102
+ }>]>>;
103
+ readonly input: Schema.optional<Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
104
+ readonly endAt: Schema.optional<Schema.NullOr<Schema.Finite>>;
105
+ readonly maxFires: Schema.optional<Schema.NullOr<Schema.Finite>>;
106
+ readonly definitionName: Schema.optional<Schema.String>;
107
+ readonly definitionVersion: Schema.optional<Schema.String>;
108
+ }>;
109
+ paused: Schema.Struct<{}>;
110
+ resumed: Schema.Struct<{}>;
111
+ retired: Schema.Struct<{}>;
112
+ triggered: Schema.Struct<{}>;
113
+ rejected: Schema.Struct<{
114
+ readonly reason: Schema.String;
115
+ }>;
116
+ }>;
@@ -0,0 +1,49 @@
1
+ import { Schema } from 'effect';
2
+ import { defineEventCatalog } from '@swirls/looms/core';
3
+ import { ScheduleSpecSchema } from './specs.js';
4
+ const GenerationSchema = Schema.Finite;
5
+ const NextAtSchema = Schema.NullOr(Schema.Finite);
6
+ export const SchedulePlannedPayloadSchema = Schema.Struct({
7
+ nextAt: NextAtSchema,
8
+ generation: GenerationSchema,
9
+ spec: ScheduleSpecSchema,
10
+ });
11
+ export const ScheduleFiredPayloadSchema = Schema.Struct({
12
+ at: Schema.Finite,
13
+ runId: Schema.String,
14
+ nextAt: NextAtSchema,
15
+ generation: GenerationSchema,
16
+ manual: Schema.Boolean,
17
+ triggerId: Schema.optional(Schema.String),
18
+ });
19
+ export const ScheduleSkippedPayloadSchema = Schema.Struct({
20
+ at: Schema.Finite,
21
+ reason: Schema.String,
22
+ nextAt: NextAtSchema,
23
+ generation: GenerationSchema,
24
+ manual: Schema.Boolean,
25
+ triggerId: Schema.optional(Schema.String),
26
+ });
27
+ export const ScheduleUpdatedPayloadSchema = Schema.Struct({
28
+ spec: Schema.optional(ScheduleSpecSchema),
29
+ input: Schema.optional(Schema.NullOr(Schema.Json)),
30
+ endAt: Schema.optional(Schema.NullOr(Schema.Finite)),
31
+ maxFires: Schema.optional(Schema.NullOr(Schema.Finite)),
32
+ definitionName: Schema.optional(Schema.String),
33
+ definitionVersion: Schema.optional(Schema.String),
34
+ });
35
+ export const ScheduleEmptyPayloadSchema = Schema.Struct({});
36
+ export const ScheduleRejectedPayloadSchema = Schema.Struct({
37
+ reason: Schema.String,
38
+ });
39
+ export const scheduleCatalog = defineEventCatalog('schedule', {
40
+ planned: SchedulePlannedPayloadSchema,
41
+ fired: ScheduleFiredPayloadSchema,
42
+ skipped: ScheduleSkippedPayloadSchema,
43
+ updated: ScheduleUpdatedPayloadSchema,
44
+ paused: ScheduleEmptyPayloadSchema,
45
+ resumed: ScheduleEmptyPayloadSchema,
46
+ retired: ScheduleEmptyPayloadSchema,
47
+ triggered: ScheduleEmptyPayloadSchema,
48
+ rejected: ScheduleRejectedPayloadSchema,
49
+ });
@@ -0,0 +1,11 @@
1
+ export * from './definitions';
2
+ export * from './definitions-store';
3
+ export * from './effects';
4
+ export * from './ensure';
5
+ export * from './events';
6
+ export * from './module';
7
+ export * from './projections';
8
+ export * from './scope';
9
+ export * from './signals';
10
+ export * from './specs';
11
+ export * from './threads';
@@ -0,0 +1,11 @@
1
+ export * from './definitions.js';
2
+ export * from './definitions-store.js';
3
+ export * from './effects.js';
4
+ export * from './ensure.js';
5
+ export * from './events.js';
6
+ export * from './module.js';
7
+ export * from './projections.js';
8
+ export * from './scope.js';
9
+ export * from './signals.js';
10
+ export * from './specs.js';
11
+ export * from './threads.js';