@adonis-agora/durable 0.1.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 (316) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -0
  3. package/dist/assets/dashboard.html +446 -0
  4. package/dist/commands/main.d.ts +15 -0
  5. package/dist/commands/main.d.ts.map +1 -0
  6. package/dist/commands/main.js +15 -0
  7. package/dist/commands/main.js.map +1 -0
  8. package/dist/commands/retry.d.ts +16 -0
  9. package/dist/commands/retry.d.ts.map +1 -0
  10. package/dist/commands/retry.js +34 -0
  11. package/dist/commands/retry.js.map +1 -0
  12. package/dist/commands/runs.d.ts +17 -0
  13. package/dist/commands/runs.d.ts.map +1 -0
  14. package/dist/commands/runs.js +40 -0
  15. package/dist/commands/runs.js.map +1 -0
  16. package/dist/commands/work.d.ts +18 -0
  17. package/dist/commands/work.d.ts.map +1 -0
  18. package/dist/commands/work.js +58 -0
  19. package/dist/commands/work.js.map +1 -0
  20. package/dist/configure.d.ts +15 -0
  21. package/dist/configure.d.ts.map +1 -0
  22. package/dist/configure.js +26 -0
  23. package/dist/configure.js.map +1 -0
  24. package/dist/providers/dashboard_provider.d.ts +27 -0
  25. package/dist/providers/dashboard_provider.d.ts.map +1 -0
  26. package/dist/providers/dashboard_provider.js +94 -0
  27. package/dist/providers/dashboard_provider.js.map +1 -0
  28. package/dist/providers/durable_provider.d.ts +36 -0
  29. package/dist/providers/durable_provider.d.ts.map +1 -0
  30. package/dist/providers/durable_provider.js +113 -0
  31. package/dist/providers/durable_provider.js.map +1 -0
  32. package/dist/src/admission-redis/index.d.ts +2 -0
  33. package/dist/src/admission-redis/index.d.ts.map +1 -0
  34. package/dist/src/admission-redis/index.js +2 -0
  35. package/dist/src/admission-redis/index.js.map +1 -0
  36. package/dist/src/admission-redis/redis-admission-backend.d.ts +83 -0
  37. package/dist/src/admission-redis/redis-admission-backend.d.ts.map +1 -0
  38. package/dist/src/admission-redis/redis-admission-backend.js +200 -0
  39. package/dist/src/admission-redis/redis-admission-backend.js.map +1 -0
  40. package/dist/src/admission.d.ts +49 -0
  41. package/dist/src/admission.d.ts.map +1 -0
  42. package/dist/src/admission.js +30 -0
  43. package/dist/src/admission.js.map +1 -0
  44. package/dist/src/backoff.d.ts +5 -0
  45. package/dist/src/backoff.d.ts.map +1 -0
  46. package/dist/src/backoff.js +11 -0
  47. package/dist/src/backoff.js.map +1 -0
  48. package/dist/src/checkpoints.d.ts +42 -0
  49. package/dist/src/checkpoints.d.ts.map +1 -0
  50. package/dist/src/checkpoints.js +57 -0
  51. package/dist/src/checkpoints.js.map +1 -0
  52. package/dist/src/codec-state-store.d.ts +60 -0
  53. package/dist/src/codec-state-store.d.ts.map +1 -0
  54. package/dist/src/codec-state-store.js +129 -0
  55. package/dist/src/codec-state-store.js.map +1 -0
  56. package/dist/src/commands/index.d.ts +3 -0
  57. package/dist/src/commands/index.d.ts.map +1 -0
  58. package/dist/src/commands/index.js +3 -0
  59. package/dist/src/commands/index.js.map +1 -0
  60. package/dist/src/commands/runs.d.ts +24 -0
  61. package/dist/src/commands/runs.d.ts.map +1 -0
  62. package/dist/src/commands/runs.js +39 -0
  63. package/dist/src/commands/runs.js.map +1 -0
  64. package/dist/src/commands/worker.d.ts +61 -0
  65. package/dist/src/commands/worker.d.ts.map +1 -0
  66. package/dist/src/commands/worker.js +74 -0
  67. package/dist/src/commands/worker.js.map +1 -0
  68. package/dist/src/completion.d.ts +15 -0
  69. package/dist/src/completion.d.ts.map +1 -0
  70. package/dist/src/completion.js +10 -0
  71. package/dist/src/completion.js.map +1 -0
  72. package/dist/src/dashboard/define_config.d.ts +48 -0
  73. package/dist/src/dashboard/define_config.d.ts.map +1 -0
  74. package/dist/src/dashboard/define_config.js +57 -0
  75. package/dist/src/dashboard/define_config.js.map +1 -0
  76. package/dist/src/dashboard/handlers.d.ts +47 -0
  77. package/dist/src/dashboard/handlers.d.ts.map +1 -0
  78. package/dist/src/dashboard/handlers.js +163 -0
  79. package/dist/src/dashboard/handlers.js.map +1 -0
  80. package/dist/src/dashboard/html.d.ts +7 -0
  81. package/dist/src/dashboard/html.d.ts.map +1 -0
  82. package/dist/src/dashboard/html.js +24 -0
  83. package/dist/src/dashboard/html.js.map +1 -0
  84. package/dist/src/dashboard/index.d.ts +8 -0
  85. package/dist/src/dashboard/index.d.ts.map +1 -0
  86. package/dist/src/dashboard/index.js +6 -0
  87. package/dist/src/dashboard/index.js.map +1 -0
  88. package/dist/src/define_config.d.ts +72 -0
  89. package/dist/src/define_config.d.ts.map +1 -0
  90. package/dist/src/define_config.js +8 -0
  91. package/dist/src/define_config.js.map +1 -0
  92. package/dist/src/diagnostics-bridge.d.ts +22 -0
  93. package/dist/src/diagnostics-bridge.d.ts.map +1 -0
  94. package/dist/src/diagnostics-bridge.js +25 -0
  95. package/dist/src/diagnostics-bridge.js.map +1 -0
  96. package/dist/src/duration.d.ts +6 -0
  97. package/dist/src/duration.d.ts.map +1 -0
  98. package/dist/src/duration.js +39 -0
  99. package/dist/src/duration.js.map +1 -0
  100. package/dist/src/engine.d.ts +583 -0
  101. package/dist/src/engine.d.ts.map +1 -0
  102. package/dist/src/engine.js +1894 -0
  103. package/dist/src/engine.js.map +1 -0
  104. package/dist/src/entities.d.ts +44 -0
  105. package/dist/src/entities.d.ts.map +1 -0
  106. package/dist/src/entities.js +64 -0
  107. package/dist/src/entities.js.map +1 -0
  108. package/dist/src/errors.d.ts +68 -0
  109. package/dist/src/errors.d.ts.map +1 -0
  110. package/dist/src/errors.js +102 -0
  111. package/dist/src/errors.js.map +1 -0
  112. package/dist/src/event-accumulators.d.ts +39 -0
  113. package/dist/src/event-accumulators.d.ts.map +1 -0
  114. package/dist/src/event-accumulators.js +70 -0
  115. package/dist/src/event-accumulators.js.map +1 -0
  116. package/dist/src/events.d.ts +9 -0
  117. package/dist/src/events.d.ts.map +1 -0
  118. package/dist/src/events.js +35 -0
  119. package/dist/src/events.js.map +1 -0
  120. package/dist/src/index.d.ts +36 -0
  121. package/dist/src/index.d.ts.map +1 -0
  122. package/dist/src/index.js +37 -0
  123. package/dist/src/index.js.map +1 -0
  124. package/dist/src/interfaces.d.ts +909 -0
  125. package/dist/src/interfaces.d.ts.map +1 -0
  126. package/dist/src/interfaces.js +2 -0
  127. package/dist/src/interfaces.js.map +1 -0
  128. package/dist/src/metrics.d.ts +44 -0
  129. package/dist/src/metrics.d.ts.map +1 -0
  130. package/dist/src/metrics.js +78 -0
  131. package/dist/src/metrics.js.map +1 -0
  132. package/dist/src/otel/durable-metrics.d.ts +42 -0
  133. package/dist/src/otel/durable-metrics.d.ts.map +1 -0
  134. package/dist/src/otel/durable-metrics.js +75 -0
  135. package/dist/src/otel/durable-metrics.js.map +1 -0
  136. package/dist/src/otel/durable-otel.d.ts +20 -0
  137. package/dist/src/otel/durable-otel.d.ts.map +1 -0
  138. package/dist/src/otel/durable-otel.js +74 -0
  139. package/dist/src/otel/durable-otel.js.map +1 -0
  140. package/dist/src/otel/index.d.ts +4 -0
  141. package/dist/src/otel/index.d.ts.map +1 -0
  142. package/dist/src/otel/index.js +4 -0
  143. package/dist/src/otel/index.js.map +1 -0
  144. package/dist/src/otel/traceparent.d.ts +17 -0
  145. package/dist/src/otel/traceparent.d.ts.map +1 -0
  146. package/dist/src/otel/traceparent.js +21 -0
  147. package/dist/src/otel/traceparent.js.map +1 -0
  148. package/dist/src/pollers.d.ts +33 -0
  149. package/dist/src/pollers.d.ts.map +1 -0
  150. package/dist/src/pollers.js +76 -0
  151. package/dist/src/pollers.js.map +1 -0
  152. package/dist/src/protocol.d.ts +17 -0
  153. package/dist/src/protocol.d.ts.map +1 -0
  154. package/dist/src/protocol.js +47 -0
  155. package/dist/src/protocol.js.map +1 -0
  156. package/dist/src/queue.d.ts +113 -0
  157. package/dist/src/queue.d.ts.map +1 -0
  158. package/dist/src/queue.js +128 -0
  159. package/dist/src/queue.js.map +1 -0
  160. package/dist/src/remote-step-factory.d.ts +15 -0
  161. package/dist/src/remote-step-factory.d.ts.map +1 -0
  162. package/dist/src/remote-step-factory.js +12 -0
  163. package/dist/src/remote-step-factory.js.map +1 -0
  164. package/dist/src/remote-workflow-executor.d.ts +24 -0
  165. package/dist/src/remote-workflow-executor.d.ts.map +1 -0
  166. package/dist/src/remote-workflow-executor.js +70 -0
  167. package/dist/src/remote-workflow-executor.js.map +1 -0
  168. package/dist/src/scheduler.d.ts +67 -0
  169. package/dist/src/scheduler.d.ts.map +1 -0
  170. package/dist/src/scheduler.js +111 -0
  171. package/dist/src/scheduler.js.map +1 -0
  172. package/dist/src/search-attributes.d.ts +48 -0
  173. package/dist/src/search-attributes.d.ts.map +1 -0
  174. package/dist/src/search-attributes.js +98 -0
  175. package/dist/src/search-attributes.js.map +1 -0
  176. package/dist/src/singleton-gate.d.ts +44 -0
  177. package/dist/src/singleton-gate.d.ts.map +1 -0
  178. package/dist/src/singleton-gate.js +90 -0
  179. package/dist/src/singleton-gate.js.map +1 -0
  180. package/dist/src/step-logger.d.ts +9 -0
  181. package/dist/src/step-logger.d.ts.map +1 -0
  182. package/dist/src/step-logger.js +89 -0
  183. package/dist/src/step-logger.js.map +1 -0
  184. package/dist/src/stores/factory.d.ts +42 -0
  185. package/dist/src/stores/factory.d.ts.map +1 -0
  186. package/dist/src/stores/factory.js +29 -0
  187. package/dist/src/stores/factory.js.map +1 -0
  188. package/dist/src/stores/lucid-helpers.d.ts +11 -0
  189. package/dist/src/stores/lucid-helpers.d.ts.map +1 -0
  190. package/dist/src/stores/lucid-helpers.js +38 -0
  191. package/dist/src/stores/lucid-helpers.js.map +1 -0
  192. package/dist/src/stores/lucid-mappers.d.ts +58 -0
  193. package/dist/src/stores/lucid-mappers.d.ts.map +1 -0
  194. package/dist/src/stores/lucid-mappers.js +174 -0
  195. package/dist/src/stores/lucid-mappers.js.map +1 -0
  196. package/dist/src/stores/lucid-schema.d.ts +28 -0
  197. package/dist/src/stores/lucid-schema.d.ts.map +1 -0
  198. package/dist/src/stores/lucid-schema.js +115 -0
  199. package/dist/src/stores/lucid-schema.js.map +1 -0
  200. package/dist/src/stores/lucid.d.ts +69 -0
  201. package/dist/src/stores/lucid.d.ts.map +1 -0
  202. package/dist/src/stores/lucid.js +342 -0
  203. package/dist/src/stores/lucid.js.map +1 -0
  204. package/dist/src/telescope/dashboard.d.ts +15 -0
  205. package/dist/src/telescope/dashboard.d.ts.map +1 -0
  206. package/dist/src/telescope/dashboard.js +113 -0
  207. package/dist/src/telescope/dashboard.js.map +1 -0
  208. package/dist/src/telescope/data-providers.d.ts +20 -0
  209. package/dist/src/telescope/data-providers.d.ts.map +1 -0
  210. package/dist/src/telescope/data-providers.js +322 -0
  211. package/dist/src/telescope/data-providers.js.map +1 -0
  212. package/dist/src/telescope/extension.d.ts +22 -0
  213. package/dist/src/telescope/extension.d.ts.map +1 -0
  214. package/dist/src/telescope/extension.js +38 -0
  215. package/dist/src/telescope/extension.js.map +1 -0
  216. package/dist/src/telescope/index.d.ts +5 -0
  217. package/dist/src/telescope/index.d.ts.map +1 -0
  218. package/dist/src/telescope/index.js +4 -0
  219. package/dist/src/telescope/index.js.map +1 -0
  220. package/dist/src/telescope/telescope-sdk.d.ts +123 -0
  221. package/dist/src/telescope/telescope-sdk.d.ts.map +1 -0
  222. package/dist/src/telescope/telescope-sdk.js +12 -0
  223. package/dist/src/telescope/telescope-sdk.js.map +1 -0
  224. package/dist/src/test-helpers.d.ts +11 -0
  225. package/dist/src/test-helpers.d.ts.map +1 -0
  226. package/dist/src/test-helpers.js +12 -0
  227. package/dist/src/test-helpers.js.map +1 -0
  228. package/dist/src/testing/in-memory-state-store.d.ts +64 -0
  229. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -0
  230. package/dist/src/testing/in-memory-state-store.js +259 -0
  231. package/dist/src/testing/in-memory-state-store.js.map +1 -0
  232. package/dist/src/testing/in-memory-transport.d.ts +25 -0
  233. package/dist/src/testing/in-memory-transport.d.ts.map +1 -0
  234. package/dist/src/testing/in-memory-transport.js +49 -0
  235. package/dist/src/testing/in-memory-transport.js.map +1 -0
  236. package/dist/src/testing-kit/admission-backend-conformance.d.ts +18 -0
  237. package/dist/src/testing-kit/admission-backend-conformance.d.ts.map +1 -0
  238. package/dist/src/testing-kit/admission-backend-conformance.js +81 -0
  239. package/dist/src/testing-kit/admission-backend-conformance.js.map +1 -0
  240. package/dist/src/testing-kit/assertions.d.ts +10 -0
  241. package/dist/src/testing-kit/assertions.d.ts.map +1 -0
  242. package/dist/src/testing-kit/assertions.js +41 -0
  243. package/dist/src/testing-kit/assertions.js.map +1 -0
  244. package/dist/src/testing-kit/harness.d.ts +28 -0
  245. package/dist/src/testing-kit/harness.d.ts.map +1 -0
  246. package/dist/src/testing-kit/harness.js +42 -0
  247. package/dist/src/testing-kit/harness.js.map +1 -0
  248. package/dist/src/testing-kit/index.d.ts +8 -0
  249. package/dist/src/testing-kit/index.d.ts.map +1 -0
  250. package/dist/src/testing-kit/index.js +8 -0
  251. package/dist/src/testing-kit/index.js.map +1 -0
  252. package/dist/src/testing-kit/replay.d.ts +22 -0
  253. package/dist/src/testing-kit/replay.d.ts.map +1 -0
  254. package/dist/src/testing-kit/replay.js +38 -0
  255. package/dist/src/testing-kit/replay.js.map +1 -0
  256. package/dist/src/testing-kit/state-store-conformance.d.ts +51 -0
  257. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -0
  258. package/dist/src/testing-kit/state-store-conformance.js +430 -0
  259. package/dist/src/testing-kit/state-store-conformance.js.map +1 -0
  260. package/dist/src/testing-kit/steps.d.ts +5 -0
  261. package/dist/src/testing-kit/steps.d.ts.map +1 -0
  262. package/dist/src/testing-kit/steps.js +15 -0
  263. package/dist/src/testing-kit/steps.js.map +1 -0
  264. package/dist/src/testing-kit/transport-conformance.d.ts +16 -0
  265. package/dist/src/testing-kit/transport-conformance.d.ts.map +1 -0
  266. package/dist/src/testing-kit/transport-conformance.js +64 -0
  267. package/dist/src/testing-kit/transport-conformance.js.map +1 -0
  268. package/dist/src/tokens.d.ts +18 -0
  269. package/dist/src/tokens.d.ts.map +1 -0
  270. package/dist/src/tokens.js +18 -0
  271. package/dist/src/tokens.js.map +1 -0
  272. package/dist/src/transport-pool.d.ts +30 -0
  273. package/dist/src/transport-pool.d.ts.map +1 -0
  274. package/dist/src/transport-pool.js +82 -0
  275. package/dist/src/transport-pool.js.map +1 -0
  276. package/dist/src/transports/db-helpers.d.ts +10 -0
  277. package/dist/src/transports/db-helpers.d.ts.map +1 -0
  278. package/dist/src/transports/db-helpers.js +33 -0
  279. package/dist/src/transports/db-helpers.js.map +1 -0
  280. package/dist/src/transports/db-schema.d.ts +28 -0
  281. package/dist/src/transports/db-schema.d.ts.map +1 -0
  282. package/dist/src/transports/db-schema.js +98 -0
  283. package/dist/src/transports/db-schema.js.map +1 -0
  284. package/dist/src/transports/db.d.ts +81 -0
  285. package/dist/src/transports/db.d.ts.map +1 -0
  286. package/dist/src/transports/db.js +340 -0
  287. package/dist/src/transports/db.js.map +1 -0
  288. package/dist/src/transports/factory.d.ts +79 -0
  289. package/dist/src/transports/factory.d.ts.map +1 -0
  290. package/dist/src/transports/factory.js +57 -0
  291. package/dist/src/transports/factory.js.map +1 -0
  292. package/dist/src/transports/queue-mock-adapter.d.ts +39 -0
  293. package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -0
  294. package/dist/src/transports/queue-mock-adapter.js +86 -0
  295. package/dist/src/transports/queue-mock-adapter.js.map +1 -0
  296. package/dist/src/transports/queue.d.ts +84 -0
  297. package/dist/src/transports/queue.d.ts.map +1 -0
  298. package/dist/src/transports/queue.js +210 -0
  299. package/dist/src/transports/queue.js.map +1 -0
  300. package/dist/src/workflow-ctx.d.ts +64 -0
  301. package/dist/src/workflow-ctx.d.ts.map +1 -0
  302. package/dist/src/workflow-ctx.js +457 -0
  303. package/dist/src/workflow-ctx.js.map +1 -0
  304. package/dist/src/workflow-ref.d.ts +34 -0
  305. package/dist/src/workflow-ref.d.ts.map +1 -0
  306. package/dist/src/workflow-ref.js +28 -0
  307. package/dist/src/workflow-ref.js.map +1 -0
  308. package/dist/stubs/config/durable.stub +49 -0
  309. package/dist/stubs/config/durable_dashboard.stub +17 -0
  310. package/dist/stubs/database/migrations/create_durable_tables.stub +87 -0
  311. package/dist/stubs/database/migrations/create_durable_transport_tables.stub +75 -0
  312. package/dist/stubs/main.d.ts +3 -0
  313. package/dist/stubs/main.d.ts.map +1 -0
  314. package/dist/stubs/main.js +4 -0
  315. package/dist/stubs/main.js.map +1 -0
  316. package/package.json +154 -0
@@ -0,0 +1,200 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ // One atomic admit. Reclaims dead-instance slots, prunes stale waiters, enforces the rate window and
3
+ // the concurrency cap, then selects the rightful next waiter by (priority desc, fairness round-robin,
4
+ // arrival FIFO/LIFO) before taking a slot — all server-side so concurrent pods can't race past the cap.
5
+ // Returns {admitted (1|0), retryAt}.
6
+ // KEYS 1 slots(hash slotId→instanceId) 2 waiters(zset waiterId→-priority) 3 wmeta(hash waiterId→"seq|key")
7
+ // 4 wexpire(zset waiterId→expiry) 5 keyserved(hash key→tick) 6 rate(string)
8
+ // 7 seq(string) 8 servedSeq(string)
9
+ const ACQUIRE_LUA = `
10
+ local p=cjson.decode(ARGV[1])
11
+ local now=p.now; local instanceId=p.instanceId; local concurrency=p.concurrency
12
+ local rateLimit=p.rateLimit; local ratePeriodMs=p.ratePeriodMs; local waiterId=p.waiterId
13
+ local priority=p.priority; local retryAt=p.retryAt; local hasOrdering=p.hasOrdering
14
+ local fairnessOn=p.fairnessOn; local fairKey=p.fairKey; local instPrefix=p.instPrefix
15
+ local waiterTtl=p.waiterTtl; local instTtl=p.instTtl; local lifo=p.lifo
16
+
17
+ -- refresh own liveness atomically so this pod can never reclaim its own fresh slot
18
+ redis.call('SET', instPrefix..instanceId, '1', 'PX', instTtl)
19
+
20
+ -- reclaim slots whose owning instance is gone; count the live ones
21
+ local slotIds=redis.call('HKEYS', KEYS[1]); local inflight=0
22
+ for i=1,#slotIds do
23
+ local owner=redis.call('HGET', KEYS[1], slotIds[i])
24
+ if owner and redis.call('EXISTS', instPrefix..owner)==1 then inflight=inflight+1
25
+ else redis.call('HDEL', KEYS[1], slotIds[i]) end
26
+ end
27
+
28
+ -- prune waiters that stopped retrying (cancelled/abandoned)
29
+ local stale=redis.call('ZRANGEBYSCORE', KEYS[4], '-inf', now)
30
+ for i=1,#stale do
31
+ redis.call('ZREM', KEYS[2], stale[i]); redis.call('HDEL', KEYS[3], stale[i]); redis.call('ZREM', KEYS[4], stale[i])
32
+ end
33
+
34
+ if rateLimit>0 then
35
+ local used=tonumber(redis.call('GET', KEYS[6]) or '0')
36
+ if used>=rateLimit then return {0, retryAt} end
37
+ end
38
+
39
+ local function register()
40
+ if redis.call('ZSCORE', KEYS[2], waiterId)==false then
41
+ local seq=redis.call('INCR', KEYS[7])
42
+ redis.call('HSET', KEYS[3], waiterId, seq..'|'..fairKey)
43
+ end
44
+ redis.call('ZADD', KEYS[2], -priority, waiterId)
45
+ redis.call('ZADD', KEYS[4], now+waiterTtl, waiterId)
46
+ end
47
+
48
+ if concurrency>0 and inflight>=concurrency then
49
+ if hasOrdering==1 then register() end
50
+ return {0, retryAt}
51
+ end
52
+
53
+ if hasOrdering==1 then
54
+ register()
55
+ local firstWS=redis.call('ZRANGE', KEYS[2], 0, 0, 'WITHSCORES')
56
+ local topScore=firstWS[2]
57
+ local tier=redis.call('ZRANGEBYSCORE', KEYS[2], topScore, topScore)
58
+ local bestId=nil; local bestServed=nil; local bestSeq=nil
59
+ for i=1,#tier do
60
+ local wid=tier[i]; local meta=redis.call('HGET', KEYS[3], wid)
61
+ if meta then
62
+ local sep=string.find(meta, '|', 1, true)
63
+ local seq=tonumber(string.sub(meta, 1, sep-1))
64
+ local k=string.sub(meta, sep+1)
65
+ local served=0
66
+ if fairnessOn==1 and k~='' then served=tonumber(redis.call('HGET', KEYS[5], k) or '0') end
67
+ local better=false
68
+ if bestId==nil then better=true
69
+ elseif served<bestServed then better=true
70
+ elseif served==bestServed then
71
+ if lifo==1 then better=(seq>bestSeq) else better=(seq<bestSeq) end
72
+ end
73
+ if better then bestId=wid; bestServed=served; bestSeq=seq end
74
+ end
75
+ end
76
+ if bestId~=waiterId then return {0, retryAt} end
77
+ end
78
+
79
+ -- admit
80
+ redis.call('HSET', KEYS[1], waiterId, instanceId)
81
+ redis.call('ZREM', KEYS[2], waiterId); redis.call('HDEL', KEYS[3], waiterId); redis.call('ZREM', KEYS[4], waiterId)
82
+ if fairnessOn==1 and fairKey~='' then
83
+ local tick=redis.call('INCR', KEYS[8]); redis.call('HSET', KEYS[5], fairKey, tick)
84
+ end
85
+ if rateLimit>0 then redis.call('INCR', KEYS[6]); redis.call('PEXPIRE', KEYS[6], ratePeriodMs) end
86
+ return {1, 0}
87
+ `;
88
+ /**
89
+ * An {@link AdmissionBackend} whose state lives in Redis, so `concurrency` / `rateLimit` / priority +
90
+ * fairness ordering are enforced ACROSS every engine replica instead of per-process.
91
+ *
92
+ * - **Concurrency** is a hash of slot→owning-instance; a slot is reclaimed only when its owner's
93
+ * liveness heartbeat lapses, so a live pod holds its slot for the full step duration (no time-lease
94
+ * false purge) while a crashed pod's slots free within `instanceTtlMs`.
95
+ * - **Rate limit** is a fixed-window counter.
96
+ * - **Ordering** registers blocked callers and, when a slot frees, admits the rightful next under
97
+ * (priority desc → fairness round-robin by `key` → arrival FIFO/LIFO). Abandoned waiters are pruned
98
+ * so a cancelled run can't sit as a phantom best-waiter.
99
+ *
100
+ * Targets a single (non-cluster) Redis — the atomic Lua spans multiple keys.
101
+ */
102
+ export class RedisAdmissionBackend {
103
+ redis;
104
+ prefix;
105
+ instanceId;
106
+ instanceTtlMs;
107
+ waiterTtlMs;
108
+ retryMs;
109
+ clock;
110
+ configs = new Map();
111
+ heartbeat;
112
+ subscriber;
113
+ constructor(options) {
114
+ this.redis = options.connection;
115
+ this.prefix = options.prefix ?? 'durable';
116
+ this.instanceId = options.instanceId ?? randomUUID();
117
+ this.instanceTtlMs = options.instanceTtlMs ?? 30_000;
118
+ this.retryMs = options.retryMs ?? 1000;
119
+ this.waiterTtlMs = options.waiterTtlMs ?? this.retryMs * 3;
120
+ this.clock = options.clock ?? Date.now;
121
+ this.redis.defineCommand('admissionAcquire', { numberOfKeys: 8, lua: ACQUIRE_LUA });
122
+ // Keep this instance's liveness fresh even while idle, so slots it holds aren't reclaimed.
123
+ void this.refreshLiveness();
124
+ this.heartbeat = setInterval(() => void this.refreshLiveness().catch(() => undefined), Math.max(1000, Math.floor(this.instanceTtlMs / 3)));
125
+ this.heartbeat.unref?.();
126
+ }
127
+ register(config) {
128
+ this.configs.set(config.name, config);
129
+ }
130
+ handles(queue) {
131
+ return this.configs.has(queue);
132
+ }
133
+ async tryAdmit(queue, item) {
134
+ const config = this.configs.get(queue);
135
+ if (!config)
136
+ return { ok: true }; // unregistered queue → ungated
137
+ const now = this.clock();
138
+ const rate = config.rateLimit;
139
+ const fairnessOn = config.fairness === 'key' ? 1 : 0;
140
+ const lifo = config.order === 'lifo' ? 1 : 0;
141
+ const fairKey = fairnessOn === 1 ? (item.key ?? '') : '';
142
+ // Order-track every call that carries a stable waiterId (the engine always does), so even a plain
143
+ // concurrency queue admits blocked waiters by arrival FIFO — identical to the in-process backend.
144
+ const hasOrdering = item.waiterId != null || item.priority != null || fairnessOn === 1 || lifo === 1 ? 1 : 0;
145
+ const rateKey = rate
146
+ ? this.key(queue, 'rate', Math.floor(now / rate.periodMs))
147
+ : this.key(queue, 'rate', 0);
148
+ const params = {
149
+ now,
150
+ instanceId: this.instanceId,
151
+ concurrency: config.concurrency ?? 0,
152
+ rateLimit: rate?.limit ?? 0,
153
+ ratePeriodMs: rate?.periodMs ?? 0,
154
+ waiterId: item.waiterId ?? `anon:${now}`,
155
+ priority: item.priority ?? 0,
156
+ retryAt: now + this.retryMs,
157
+ hasOrdering,
158
+ fairnessOn,
159
+ fairKey,
160
+ instPrefix: this.instPrefix(),
161
+ waiterTtl: this.waiterTtlMs,
162
+ instTtl: this.instanceTtlMs,
163
+ lifo,
164
+ };
165
+ const [admitted, retryAt] = await this.redis.admissionAcquire(this.key(queue, 'slots'), this.key(queue, 'waiters'), this.key(queue, 'wmeta'), this.key(queue, 'wexpire'), this.key(queue, 'keyserved'), rateKey, this.key(queue, 'seq'), this.key(queue, 'servedseq'), JSON.stringify(params));
166
+ return admitted === 1 ? { ok: true } : { ok: false, retryAt };
167
+ }
168
+ async release(queue, slotId) {
169
+ await this.redis.hdel(this.key(queue, 'slots'), slotId);
170
+ // Tell the fleet a slot freed so blocked waiters can re-contend now (best-effort; the retry tick
171
+ // is the guarantee). One channel, queue name as payload.
172
+ await this.redis.publish(this.freedChannel(), queue);
173
+ }
174
+ onFreed(handler) {
175
+ // A subscriber connection can't run normal commands, so use a dedicated dup of the connection.
176
+ const sub = this.redis.duplicate();
177
+ this.subscriber = sub;
178
+ void sub.subscribe(this.freedChannel());
179
+ sub.on('message', (_channel, queue) => handler(queue));
180
+ }
181
+ /** Stop the liveness heartbeat + subscriber (held slots lapse within `instanceTtlMs`). */
182
+ async close() {
183
+ clearInterval(this.heartbeat);
184
+ this.subscriber?.disconnect();
185
+ }
186
+ freedChannel() {
187
+ return `${this.prefix}:adm:freed`;
188
+ }
189
+ async refreshLiveness() {
190
+ await this.redis.set(`${this.instPrefix()}${this.instanceId}`, '1', 'PX', this.instanceTtlMs);
191
+ }
192
+ instPrefix() {
193
+ return `${this.prefix}:adm:inst:`;
194
+ }
195
+ key(queue, part, window) {
196
+ const base = `${this.prefix}:adm:${queue}:${part}`;
197
+ return window === undefined ? base : `${base}:${window}`;
198
+ }
199
+ }
200
+ //# sourceMappingURL=redis-admission-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-admission-backend.js","sourceRoot":"","sources":["../../../src/admission-redis/redis-admission-backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA6CzC,qGAAqG;AACrG,sGAAsG;AACtG,wGAAwG;AACxG,qCAAqC;AACrC,+GAA+G;AAC/G,8FAA8F;AAC9F,+DAA+D;AAC/D,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8EnB,CAAC;AAqCF;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,qBAAqB;IACf,KAAK,CAAmB;IACxB,MAAM,CAAS;IACf,UAAU,CAAS;IACnB,aAAa,CAAS;IACtB,WAAW,CAAS;IACpB,OAAO,CAAS;IAChB,KAAK,CAAe;IACpB,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IACzC,SAAS,CAAiC;IACnD,UAAU,CAAa;IAE/B,YAAY,OAA8B;QACxC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,UAA8B,CAAC;QACpD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,UAAU,EAAE,CAAC;QACrD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,MAAM,CAAC;QACrD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,kBAAkB,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QACpF,2FAA2F;QAC3F,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS,GAAG,WAAW,CAC1B,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EACxD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CACnD,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IAC3B,CAAC;IAED,QAAQ,CAAC,MAAmB;QAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,IAAmB;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,+BAA+B;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,kGAAkG;QAClG,kGAAkG;QAClG,MAAM,WAAW,GACf,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,UAAU,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3F,MAAM,OAAO,GAAG,IAAI;YAClB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1D,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAkB;YAC5B,GAAG;YACH,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,CAAC;YACpC,SAAS,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;YAC3B,YAAY,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC;YACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG,EAAE;YACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC;YAC5B,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC,OAAO;YAC3B,WAAW;YACX,UAAU;YACV,OAAO;YACP,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;YAC7B,SAAS,EAAE,IAAI,CAAC,WAAW;YAC3B,OAAO,EAAE,IAAI,CAAC,aAAa;YAC3B,IAAI;SACL,CAAC;QACF,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAC3D,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,EACxB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,EACxB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,EAC5B,OAAO,EACP,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,EACtB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,EAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CACvB,CAAC;QACF,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,MAAc;QACzC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QACxD,iGAAiG;QACjG,yDAAyD;QACzD,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,CAAC,OAAgC;QACtC,+FAA+F;QAC/F,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QACtB,KAAK,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QACxC,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,0FAA0F;IAC1F,KAAK,CAAC,KAAK;QACT,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,OAAO,GAAG,IAAI,CAAC,MAAM,YAAY,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,eAAe;QAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAChG,CAAC;IAEO,UAAU;QAChB,OAAO,GAAG,IAAI,CAAC,MAAM,YAAY,CAAC;IACpC,CAAC;IAEO,GAAG,CAAC,KAAa,EAAE,IAAY,EAAE,MAAe;QACtD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,QAAQ,KAAK,IAAI,IAAI,EAAE,CAAC;QACnD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,49 @@
1
+ import { type Admission, type AdmissionItem, type QueueConfig } from './queue.js';
2
+ /**
3
+ * Pluggable backend for the remote-step flow-control gate (`ctx.call(step, input, { queue })`). The
4
+ * engine asks it whether a queued call may be admitted now (concurrency / rate / ordering) and tells
5
+ * it when an admitted slot is released.
6
+ *
7
+ * The default {@link InMemoryAdmissionBackend} keeps per-instance counts — correct for a single
8
+ * orchestrator. Swap in a store/Redis-backed backend to make the caps and ordering GLOBAL across
9
+ * engine replicas (so `concurrency: 5` means 5 in-flight across the whole fleet, not 5 per pod).
10
+ *
11
+ * `tryAdmit`/`release` are async so a global backend can do its atomic round-trip; the in-memory one
12
+ * resolves synchronously. An UNREGISTERED queue name is treated as ungated (admits immediately) — the
13
+ * engine only tracks/releases a slot for a registered queue.
14
+ */
15
+ export interface AdmissionBackend {
16
+ /** Register (or replace) a queue's config. Called from `engine.registerQueue`. */
17
+ register(config: QueueConfig): void;
18
+ /** Whether `queue` is registered (gated). The engine only tracks/releases a slot for a gated queue. */
19
+ handles(queue: string): boolean;
20
+ /** May a call on `queue` take a slot now? On `ok`, the caller holds a slot until {@link release}. */
21
+ tryAdmit(queue: string, item: AdmissionItem): Promise<Admission>;
22
+ /**
23
+ * Release the slot granted by {@link tryAdmit} for `queue`. `slotId` is the admitted item's
24
+ * `waiterId` (the engine's stepId), so a lease-based backend can drop the exact slot; an in-process
25
+ * counter ignores it.
26
+ */
27
+ release(queue: string, slotId: string): Promise<void>;
28
+ /**
29
+ * Optional: subscribe to "a slot just freed on `queue`" events (e.g. a fleet-wide pub/sub). The
30
+ * engine uses it to wake its admission-blocked runs early instead of waiting for their retry tick.
31
+ * A backend without it falls back to retry-tick polling. Fired best-effort across the fleet.
32
+ */
33
+ onFreed?(handler: (queue: string) => void): void;
34
+ }
35
+ /**
36
+ * Default admission backend: one in-process {@link QueueController} per registered queue. Caps and
37
+ * waiter ordering are per engine instance (see {@link QueueConfig}). Preserves the exact pre-backend
38
+ * behaviour — the engine used to hold these controllers directly.
39
+ */
40
+ export declare class InMemoryAdmissionBackend implements AdmissionBackend {
41
+ private readonly clock;
42
+ private readonly controllers;
43
+ constructor(clock: () => number);
44
+ register(config: QueueConfig): void;
45
+ handles(queue: string): boolean;
46
+ tryAdmit(queue: string, item: AdmissionItem): Promise<Admission>;
47
+ release(queue: string, _slotId: string): Promise<void>;
48
+ }
49
+ //# sourceMappingURL=admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admission.d.ts","sourceRoot":"","sources":["../../src/admission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAmB,MAAM,YAAY,CAAC;AAEnG;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,uGAAuG;IACvG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,qGAAqG;IACrG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD;;;;OAIG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;CAClD;AAED;;;;GAIG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAGnD,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;gBAErC,KAAK,EAAE,MAAM,MAAM;IAEhD,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAInC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIzB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAOhE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D"}
@@ -0,0 +1,30 @@
1
+ import { QueueController } from './queue.js';
2
+ /**
3
+ * Default admission backend: one in-process {@link QueueController} per registered queue. Caps and
4
+ * waiter ordering are per engine instance (see {@link QueueConfig}). Preserves the exact pre-backend
5
+ * behaviour — the engine used to hold these controllers directly.
6
+ */
7
+ export class InMemoryAdmissionBackend {
8
+ clock;
9
+ controllers = new Map();
10
+ constructor(clock) {
11
+ this.clock = clock;
12
+ }
13
+ register(config) {
14
+ this.controllers.set(config.name, new QueueController(config, this.clock));
15
+ }
16
+ handles(queue) {
17
+ return this.controllers.has(queue);
18
+ }
19
+ async tryAdmit(queue, item) {
20
+ const controller = this.controllers.get(queue);
21
+ // Unregistered queue → ungated (mirrors the old `this.queues.get(queue)` truthiness check).
22
+ if (!controller)
23
+ return { ok: true };
24
+ return controller.tryAdmit(item);
25
+ }
26
+ async release(queue, _slotId) {
27
+ this.controllers.get(queue)?.release();
28
+ }
29
+ }
30
+ //# sourceMappingURL=admission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admission.js","sourceRoot":"","sources":["../../src/admission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwD,eAAe,EAAE,MAAM,YAAY,CAAC;AAoCnG;;;;GAIG;AACH,MAAM,OAAO,wBAAwB;IAGN;IAFZ,WAAW,GAAG,IAAI,GAAG,EAA2B,CAAC;IAElE,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAEpD,QAAQ,CAAC,MAAmB;QAC1B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,IAAmB;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/C,4FAA4F;QAC5F,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,OAAe;QAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ import type { StepOptions } from './interfaces.js';
2
+ /** Delay in ms before the next retry attempt, per a step's `StepOptions` backoff config. Shared by
3
+ * the local-step retry loop and the durable remote-step retry, so they stay consistent. */
4
+ export declare function backoffDelay(attempt: number, options?: StepOptions): number;
5
+ //# sourceMappingURL=backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../src/backoff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;4FAC4F;AAC5F,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAM3E"}
@@ -0,0 +1,11 @@
1
+ /** Delay in ms before the next retry attempt, per a step's `StepOptions` backoff config. Shared by
2
+ * the local-step retry loop and the durable remote-step retry, so they stay consistent. */
3
+ export function backoffDelay(attempt, options) {
4
+ const base = options?.backoffMs ?? 0;
5
+ if (base <= 0)
6
+ return 0;
7
+ const raw = options?.backoff === 'exp' ? base * 2 ** (attempt - 1) : base;
8
+ const capped = options?.backoffMaxMs ? Math.min(raw, options.backoffMaxMs) : raw;
9
+ return options?.jitter ? Math.round(capped * (0.5 + Math.random() * 0.5)) : capped;
10
+ }
11
+ //# sourceMappingURL=backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../src/backoff.ts"],"names":[],"mappings":"AAEA;4FAC4F;AAC5F,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,OAAqB;IACjE,MAAM,IAAI,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACxB,MAAM,GAAG,GAAG,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1E,MAAM,MAAM,GAAG,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AACrF,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { StepCheckpoint, StepError, StepEvent, StepKind } from './interfaces.js';
2
+ /**
3
+ * Build a *phased* step checkpoint — one with distinct enqueue/run/finish timestamps (a local step
4
+ * that is running/completed/failed, or a pending remote/sleep step). Computes the deterministic
5
+ * `stepId` and normalizes empty event arrays, so the engine's running/completed/failed/pending
6
+ * checkpoint literals are constructed in exactly one place instead of ~8.
7
+ *
8
+ * For instantaneous checkpoints (sleep timers, markers, delivered signals) use {@link instantCheckpoint}.
9
+ */
10
+ export declare function stepCheckpoint(p: {
11
+ runId: string;
12
+ seq: number;
13
+ name: string;
14
+ kind: StepKind;
15
+ status: StepCheckpoint['status'];
16
+ attempts: number;
17
+ enqueuedAt: Date;
18
+ startedAt: Date;
19
+ finishedAt: Date;
20
+ input?: unknown;
21
+ output?: unknown;
22
+ error?: StepError | undefined;
23
+ events?: StepEvent[] | undefined;
24
+ workerGroup?: string | undefined;
25
+ wakeAt?: number | undefined;
26
+ }): StepCheckpoint;
27
+ /**
28
+ * Build an *instantaneous* checkpoint — one with no distinct enqueue/run/finish phases (a durable
29
+ * sleep timer, a breakpoint marker, a delivered signal, a published `setEvent` value, a `patched`
30
+ * marker). All three timestamps collapse to now and `attempts` is 1. Collapses what was five
31
+ * near-identical 10-field `saveCheckpoint({...})` literals into one builder.
32
+ */
33
+ export declare function instantCheckpoint(p: {
34
+ runId: string;
35
+ seq: number;
36
+ name: string;
37
+ kind: StepKind;
38
+ status?: StepCheckpoint['status'];
39
+ output?: unknown;
40
+ wakeAt?: number;
41
+ }): StepCheckpoint;
42
+ //# sourceMappingURL=checkpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoints.d.ts","sourceRoot":"","sources":["../../src/checkpoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAQtF;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,GAAG,cAAc,CAmBjB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,cAAc,CAgBjB"}
@@ -0,0 +1,57 @@
1
+ import { stepId } from './protocol.js';
2
+ /** Drop an empty events array to `undefined` (the repeated `events?.length ? events : undefined`). */
3
+ function nonEmptyEvents(events) {
4
+ return events && events.length > 0 ? events : undefined;
5
+ }
6
+ /**
7
+ * Build a *phased* step checkpoint — one with distinct enqueue/run/finish timestamps (a local step
8
+ * that is running/completed/failed, or a pending remote/sleep step). Computes the deterministic
9
+ * `stepId` and normalizes empty event arrays, so the engine's running/completed/failed/pending
10
+ * checkpoint literals are constructed in exactly one place instead of ~8.
11
+ *
12
+ * For instantaneous checkpoints (sleep timers, markers, delivered signals) use {@link instantCheckpoint}.
13
+ */
14
+ export function stepCheckpoint(p) {
15
+ return {
16
+ runId: p.runId,
17
+ seq: p.seq,
18
+ name: p.name,
19
+ kind: p.kind,
20
+ stepId: stepId(p.runId, p.seq),
21
+ status: p.status,
22
+ input: p.input,
23
+ output: p.output,
24
+ error: p.error,
25
+ events: nonEmptyEvents(p.events),
26
+ attempts: p.attempts,
27
+ workerGroup: p.workerGroup,
28
+ wakeAt: p.wakeAt,
29
+ enqueuedAt: p.enqueuedAt,
30
+ startedAt: p.startedAt,
31
+ finishedAt: p.finishedAt,
32
+ };
33
+ }
34
+ /**
35
+ * Build an *instantaneous* checkpoint — one with no distinct enqueue/run/finish phases (a durable
36
+ * sleep timer, a breakpoint marker, a delivered signal, a published `setEvent` value, a `patched`
37
+ * marker). All three timestamps collapse to now and `attempts` is 1. Collapses what was five
38
+ * near-identical 10-field `saveCheckpoint({...})` literals into one builder.
39
+ */
40
+ export function instantCheckpoint(p) {
41
+ const at = new Date();
42
+ return {
43
+ runId: p.runId,
44
+ seq: p.seq,
45
+ name: p.name,
46
+ kind: p.kind,
47
+ stepId: stepId(p.runId, p.seq),
48
+ status: p.status ?? 'completed',
49
+ output: p.output,
50
+ wakeAt: p.wakeAt,
51
+ attempts: 1,
52
+ enqueuedAt: at,
53
+ startedAt: at,
54
+ finishedAt: at,
55
+ };
56
+ }
57
+ //# sourceMappingURL=checkpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoints.js","sourceRoot":"","sources":["../../src/checkpoints.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,sGAAsG;AACtG,SAAS,cAAc,CAAC,MAA+B;IACrD,OAAO,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,CAgB9B;IACC,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC;QAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,UAAU,EAAE,CAAC,CAAC,UAAU;KACzB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAQjC;IACC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC;IACtB,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,WAAW;QAC/B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC;QACX,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,EAAE;KACf,CAAC;AACJ,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { RunQuery, SignalWaiter, StateStore, StepCheckpoint, WorkflowRun } from './interfaces.js';
2
+ /**
3
+ * Transforms a payload value as it crosses the store boundary — e.g. encrypt-at-rest, compress, or
4
+ * redact PII. `decode` must be the exact inverse of `encode`. Both are synchronous (use a local
5
+ * cipher / codec); wrap an async KMS yourself if you need one.
6
+ */
7
+ export interface PayloadCodec {
8
+ encode(value: unknown): unknown;
9
+ decode(value: unknown): unknown;
10
+ }
11
+ /**
12
+ * A `StateStore` decorator that runs run/step **payloads** (input + output) through a
13
+ * {@link PayloadCodec} — encoded on write, decoded on read — so they're never stored in the clear.
14
+ * Adapter-agnostic: wrap any store. Searchable metadata (id, status, workflow, tags, timestamps) and
15
+ * the structured `error` are left untouched so the dashboard, queries, and recovery still work.
16
+ *
17
+ * ```ts
18
+ * const store = new CodecStateStore(new TypeOrmStateStore(ds), aesCodec);
19
+ * ```
20
+ */
21
+ export declare class CodecStateStore implements StateStore {
22
+ private readonly inner;
23
+ private readonly codec;
24
+ constructor(inner: StateStore, codec: PayloadCodec);
25
+ private enc;
26
+ private dec;
27
+ private encRun;
28
+ private decRun;
29
+ private encCp;
30
+ private decCp;
31
+ ensureSchema(): Promise<void>;
32
+ createRun(run: WorkflowRun): Promise<void>;
33
+ updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
34
+ getRun(runId: string): Promise<WorkflowRun | null>;
35
+ deleteRun(runId: string): Promise<void>;
36
+ getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
37
+ saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
38
+ transaction<T>(work: (tx: {
39
+ raw: unknown;
40
+ saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
41
+ }) => Promise<T>): Promise<T>;
42
+ listIncompleteRuns(): Promise<WorkflowRun[]>;
43
+ listPendingRuns(limit: number): Promise<WorkflowRun[]>;
44
+ listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
45
+ tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
46
+ releaseRunLock(runId: string): Promise<void>;
47
+ renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
48
+ putSignalWaiter(waiter: SignalWaiter): Promise<void>;
49
+ takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
50
+ listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
51
+ bufferSignal(token: string, payload: unknown): Promise<void>;
52
+ takeBufferedSignal(token: string): Promise<{
53
+ payload: unknown;
54
+ } | null>;
55
+ listRuns(query: RunQuery): Promise<WorkflowRun[]>;
56
+ listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
57
+ getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
58
+ listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
59
+ }
60
+ //# sourceMappingURL=codec-state-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec-state-store.d.ts","sourceRoot":"","sources":["../../src/codec-state-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * A `StateStore` decorator that runs run/step **payloads** (input + output) through a
3
+ * {@link PayloadCodec} — encoded on write, decoded on read — so they're never stored in the clear.
4
+ * Adapter-agnostic: wrap any store. Searchable metadata (id, status, workflow, tags, timestamps) and
5
+ * the structured `error` are left untouched so the dashboard, queries, and recovery still work.
6
+ *
7
+ * ```ts
8
+ * const store = new CodecStateStore(new TypeOrmStateStore(ds), aesCodec);
9
+ * ```
10
+ */
11
+ export class CodecStateStore {
12
+ inner;
13
+ codec;
14
+ constructor(inner, codec) {
15
+ this.inner = inner;
16
+ this.codec = codec;
17
+ }
18
+ enc(v) {
19
+ return v === undefined ? undefined : this.codec.encode(v);
20
+ }
21
+ dec(v) {
22
+ return v === undefined ? undefined : this.codec.decode(v);
23
+ }
24
+ encRun(run) {
25
+ return { ...run, input: this.enc(run.input), output: this.enc(run.output) };
26
+ }
27
+ decRun(run) {
28
+ return (run && { ...run, input: this.dec(run.input), output: this.dec(run.output) });
29
+ }
30
+ encCp(cp) {
31
+ return { ...cp, input: this.enc(cp.input), output: this.enc(cp.output) };
32
+ }
33
+ decCp(cp) {
34
+ return (cp && { ...cp, input: this.dec(cp.input), output: this.dec(cp.output) });
35
+ }
36
+ ensureSchema() {
37
+ return this.inner.ensureSchema?.() ?? Promise.resolve();
38
+ }
39
+ createRun(run) {
40
+ return this.inner.createRun(this.encRun(run));
41
+ }
42
+ updateRun(runId, patch) {
43
+ const next = { ...patch };
44
+ if ('input' in patch)
45
+ next.input = this.enc(patch.input);
46
+ if ('output' in patch)
47
+ next.output = this.enc(patch.output);
48
+ return this.inner.updateRun(runId, next);
49
+ }
50
+ async getRun(runId) {
51
+ return this.decRun(await this.inner.getRun(runId));
52
+ }
53
+ deleteRun(runId) {
54
+ return this.inner.deleteRun(runId);
55
+ }
56
+ async getCheckpoint(runId, seq) {
57
+ return this.decCp(await this.inner.getCheckpoint(runId, seq));
58
+ }
59
+ saveCheckpoint(checkpoint) {
60
+ return this.inner.saveCheckpoint(this.encCp(checkpoint));
61
+ }
62
+ transaction(work) {
63
+ if (!this.inner.transaction) {
64
+ throw new Error('the wrapped store does not support transactions');
65
+ }
66
+ // Encode the checkpoint inside the tx, so payloads stay encoded at rest like the normal path.
67
+ return this.inner.transaction((tx) => work({ raw: tx.raw, saveCheckpoint: (cp) => tx.saveCheckpoint(this.encCp(cp)) }));
68
+ }
69
+ async listIncompleteRuns() {
70
+ return (await this.inner.listIncompleteRuns()).map((r) => this.decRun(r));
71
+ }
72
+ async listPendingRuns(limit) {
73
+ return (await this.inner.listPendingRuns(limit)).map((r) => this.decRun(r));
74
+ }
75
+ async listDueTimers(nowMs) {
76
+ return (await this.inner.listDueTimers(nowMs)).map((r) => this.decRun(r));
77
+ }
78
+ tryLockRun(runId, owner, leaseUntilMs, nowMs) {
79
+ return this.inner.tryLockRun(runId, owner, leaseUntilMs, nowMs);
80
+ }
81
+ releaseRunLock(runId) {
82
+ return this.inner.releaseRunLock(runId);
83
+ }
84
+ renewRunLock(runId, owner, leaseUntilMs) {
85
+ return this.inner.renewRunLock(runId, owner, leaseUntilMs);
86
+ }
87
+ putSignalWaiter(waiter) {
88
+ return this.inner.putSignalWaiter(waiter);
89
+ }
90
+ takeSignalWaiter(token) {
91
+ return this.inner.takeSignalWaiter(token);
92
+ }
93
+ listSignalWaiters(prefix) {
94
+ return this.inner.listSignalWaiters(prefix);
95
+ }
96
+ bufferSignal(token, payload) {
97
+ return this.inner.bufferSignal(token, this.enc(payload));
98
+ }
99
+ async takeBufferedSignal(token) {
100
+ const buffered = await this.inner.takeBufferedSignal(token);
101
+ return buffered && { payload: this.dec(buffered.payload) };
102
+ }
103
+ async listRuns(query) {
104
+ return (await this.inner.listRuns(query)).map((r) => this.decRun(r));
105
+ }
106
+ async listCheckpoints(runId) {
107
+ return (await this.inner.listCheckpoints(runId)).map((c) => this.decCp(c));
108
+ }
109
+ async getLatestCheckpointByName(runId, name) {
110
+ // Forward the targeted read when the inner store implements it; otherwise fall back to the
111
+ // (always-present) listCheckpoints scan so wrapping a legacy custom store keeps working.
112
+ if (this.inner.getLatestCheckpointByName) {
113
+ const cp = await this.inner.getLatestCheckpointByName(runId, name);
114
+ return cp ? this.decCp(cp) : undefined;
115
+ }
116
+ let latest;
117
+ for (const cp of await this.inner.listCheckpoints(runId))
118
+ if (cp.name === name)
119
+ latest = cp;
120
+ return latest ? this.decCp(latest) : undefined;
121
+ }
122
+ async listCheckpointsByNamePrefix(runId, prefixes) {
123
+ const matches = this.inner.listCheckpointsByNamePrefix
124
+ ? await this.inner.listCheckpointsByNamePrefix(runId, prefixes)
125
+ : (await this.inner.listCheckpoints(runId)).filter((cp) => prefixes.some((p) => cp.name.startsWith(p)));
126
+ return matches.map((c) => this.decCp(c));
127
+ }
128
+ }
129
+ //# sourceMappingURL=codec-state-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec-state-store.js","sourceRoot":"","sources":["../../src/codec-state-store.ts"],"names":[],"mappings":"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"}