@rikalabs/akter 0.1.0-alpha.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 (603) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +8 -0
  4. package/README.md +116 -0
  5. package/dist/actor/codecs.d.ts +77 -0
  6. package/dist/actor/codecs.js +87 -0
  7. package/dist/actor/connections.d.ts +34 -0
  8. package/dist/actor/connections.js +145 -0
  9. package/dist/actor/definition.d.ts +893 -0
  10. package/dist/actor/definition.js +147 -0
  11. package/dist/actor/descriptor.d.ts +172 -0
  12. package/dist/actor/descriptor.js +367 -0
  13. package/dist/actor/handles.d.ts +77 -0
  14. package/dist/actor/handles.js +265 -0
  15. package/dist/actor/jobs.d.ts +10 -0
  16. package/dist/actor/jobs.js +137 -0
  17. package/dist/actor/layers.d.ts +41 -0
  18. package/dist/actor/layers.js +177 -0
  19. package/dist/actor/reads.d.ts +47 -0
  20. package/dist/actor/reads.js +224 -0
  21. package/dist/actor/served.d.ts +120 -0
  22. package/dist/actor/served.js +72 -0
  23. package/dist/actor/turns.d.ts +13 -0
  24. package/dist/actor/turns.js +332 -0
  25. package/dist/actor/workflows.d.ts +16 -0
  26. package/dist/actor/workflows.js +37 -0
  27. package/dist/client/calls.d.ts +47 -0
  28. package/dist/client/calls.js +425 -0
  29. package/dist/client/clock.d.ts +56 -0
  30. package/dist/client/clock.js +119 -0
  31. package/dist/client/fleet-page.d.ts +15 -0
  32. package/dist/client/fleet-page.js +13 -0
  33. package/dist/client/fleet.d.ts +29 -0
  34. package/dist/client/fleet.js +51 -0
  35. package/dist/client/index.d.ts +17 -0
  36. package/dist/client/index.js +6 -0
  37. package/dist/client/make.d.ts +157 -0
  38. package/dist/client/make.js +221 -0
  39. package/dist/client/offline/queue.d.ts +118 -0
  40. package/dist/client/offline/queue.js +292 -0
  41. package/dist/client/offline/store.d.ts +74 -0
  42. package/dist/client/offline/store.js +78 -0
  43. package/dist/client/optimistic.d.ts +57 -0
  44. package/dist/client/optimistic.js +136 -0
  45. package/dist/client/report.d.ts +2 -0
  46. package/dist/client/report.js +8 -0
  47. package/dist/client/sessions/connection.d.ts +118 -0
  48. package/dist/client/sessions/connection.js +162 -0
  49. package/dist/client/sessions/feed.d.ts +42 -0
  50. package/dist/client/sessions/feed.js +43 -0
  51. package/dist/client/sessions/sse.d.ts +47 -0
  52. package/dist/client/sessions/sse.js +112 -0
  53. package/dist/client/sessions/stream.d.ts +28 -0
  54. package/dist/client/sessions/stream.js +35 -0
  55. package/dist/client/sessions/watch.d.ts +34 -0
  56. package/dist/client/sessions/watch.js +40 -0
  57. package/dist/client/transport.d.ts +37 -0
  58. package/dist/client/transport.js +66 -0
  59. package/dist/contexts/command.d.ts +132 -0
  60. package/dist/contexts/command.js +20 -0
  61. package/dist/contexts/connection.d.ts +68 -0
  62. package/dist/contexts/connection.js +0 -0
  63. package/dist/contexts/job.d.ts +49 -0
  64. package/dist/contexts/job.js +0 -0
  65. package/dist/contexts/workflow.d.ts +147 -0
  66. package/dist/contexts/workflow.js +22 -0
  67. package/dist/errors/actor.d.ts +200 -0
  68. package/dist/errors/actor.js +271 -0
  69. package/dist/errors/content.d.ts +19 -0
  70. package/dist/errors/content.js +14 -0
  71. package/dist/errors/database.d.ts +22 -0
  72. package/dist/errors/database.js +23 -0
  73. package/dist/errors/events.d.ts +17 -0
  74. package/dist/errors/events.js +14 -0
  75. package/dist/errors/subscription.d.ts +12 -0
  76. package/dist/errors/subscription.js +8 -0
  77. package/dist/errors/workflow.d.ts +28 -0
  78. package/dist/errors/workflow.js +17 -0
  79. package/dist/handles/actors.d.ts +17 -0
  80. package/dist/handles/actors.js +7 -0
  81. package/dist/handles/content.d.ts +59 -0
  82. package/dist/handles/content.js +36 -0
  83. package/dist/handles/intents.d.ts +164 -0
  84. package/dist/handles/intents.js +214 -0
  85. package/dist/handles/run.d.ts +31 -0
  86. package/dist/handles/run.js +21 -0
  87. package/dist/handles/workflow.d.ts +27 -0
  88. package/dist/handles/workflow.js +23 -0
  89. package/dist/identity/caller.d.ts +105 -0
  90. package/dist/identity/caller.js +68 -0
  91. package/dist/identity/child.d.ts +18 -0
  92. package/dist/identity/child.js +39 -0
  93. package/dist/identity/command.d.ts +19 -0
  94. package/dist/identity/command.js +21 -0
  95. package/dist/identity/connection.d.ts +24 -0
  96. package/dist/identity/connection.js +29 -0
  97. package/dist/identity/content.d.ts +18 -0
  98. package/dist/identity/content.js +12 -0
  99. package/dist/identity/digest.d.ts +4 -0
  100. package/dist/identity/digest.js +5 -0
  101. package/dist/identity/execution.d.ts +32 -0
  102. package/dist/identity/execution.js +63 -0
  103. package/dist/identity/mint.d.ts +51 -0
  104. package/dist/identity/mint.js +76 -0
  105. package/dist/identity/version.d.ts +2 -0
  106. package/dist/identity/version.js +3 -0
  107. package/dist/index.d.ts +159 -0
  108. package/dist/index.js +97 -0
  109. package/dist/members/blob.d.ts +43 -0
  110. package/dist/members/blob.js +29 -0
  111. package/dist/members/command.d.ts +109 -0
  112. package/dist/members/command.js +61 -0
  113. package/dist/members/connection.d.ts +55 -0
  114. package/dist/members/connection.js +49 -0
  115. package/dist/members/event.d.ts +36 -0
  116. package/dist/members/event.js +24 -0
  117. package/dist/members/job.d.ts +157 -0
  118. package/dist/members/job.js +67 -0
  119. package/dist/members/payload.d.ts +93 -0
  120. package/dist/members/payload.js +145 -0
  121. package/dist/members/reducer.d.ts +64 -0
  122. package/dist/members/reducer.js +27 -0
  123. package/dist/members/stream.d.ts +36 -0
  124. package/dist/members/stream.js +14 -0
  125. package/dist/members/subscription.d.ts +182 -0
  126. package/dist/members/subscription.js +91 -0
  127. package/dist/members/workflow.d.ts +99 -0
  128. package/dist/members/workflow.js +2 -0
  129. package/dist/policies/access.d.ts +35 -0
  130. package/dist/policies/access.js +6 -0
  131. package/dist/policies/command.d.ts +127 -0
  132. package/dist/policies/command.js +45 -0
  133. package/dist/policies/job.d.ts +28 -0
  134. package/dist/policies/job.js +60 -0
  135. package/dist/policies/schedules.d.ts +33 -0
  136. package/dist/policies/schedules.js +131 -0
  137. package/dist/protocol/frames.d.ts +66 -0
  138. package/dist/protocol/frames.js +55 -0
  139. package/dist/protocol/inspection.d.ts +807 -0
  140. package/dist/protocol/inspection.js +243 -0
  141. package/dist/protocol/wire.d.ts +184 -0
  142. package/dist/protocol/wire.js +150 -0
  143. package/dist/runtime/actors.d.ts +155 -0
  144. package/dist/runtime/actors.js +4 -0
  145. package/dist/runtime/admission.d.ts +34 -0
  146. package/dist/runtime/admission.js +72 -0
  147. package/dist/runtime/adoption/backfill.d.ts +27 -0
  148. package/dist/runtime/adoption/backfill.js +144 -0
  149. package/dist/runtime/adoption/enforce.d.ts +55 -0
  150. package/dist/runtime/adoption/enforce.js +241 -0
  151. package/dist/runtime/adoption/observe.d.ts +43 -0
  152. package/dist/runtime/adoption/observe.js +139 -0
  153. package/dist/runtime/adoption/plan.d.ts +45 -0
  154. package/dist/runtime/adoption/plan.js +192 -0
  155. package/dist/runtime/adoption/startup.d.ts +32 -0
  156. package/dist/runtime/adoption/startup.js +76 -0
  157. package/dist/runtime/adoption/status.d.ts +23 -0
  158. package/dist/runtime/adoption/status.js +47 -0
  159. package/dist/runtime/adoption/target.d.ts +33 -0
  160. package/dist/runtime/adoption/target.js +49 -0
  161. package/dist/runtime/connections/holder.d.ts +221 -0
  162. package/dist/runtime/connections/holder.js +816 -0
  163. package/dist/runtime/connections/owner.d.ts +263 -0
  164. package/dist/runtime/connections/owner.js +896 -0
  165. package/dist/runtime/connections/protocol.d.ts +441 -0
  166. package/dist/runtime/connections/protocol.js +222 -0
  167. package/dist/runtime/connections/reads.d.ts +24 -0
  168. package/dist/runtime/connections/reads.js +19 -0
  169. package/dist/runtime/connections/streams.d.ts +62 -0
  170. package/dist/runtime/connections/streams.js +128 -0
  171. package/dist/runtime/connections/transport.d.ts +40 -0
  172. package/dist/runtime/connections/transport.js +68 -0
  173. package/dist/runtime/connections/watch.d.ts +52 -0
  174. package/dist/runtime/connections/watch.js +159 -0
  175. package/dist/runtime/content/grant.d.ts +34 -0
  176. package/dist/runtime/content/grant.js +58 -0
  177. package/dist/runtime/content/store.d.ts +63 -0
  178. package/dist/runtime/content/store.js +294 -0
  179. package/dist/runtime/cron/key.d.ts +2 -0
  180. package/dist/runtime/cron/key.js +2 -0
  181. package/dist/runtime/cron/schedule.d.ts +69 -0
  182. package/dist/runtime/cron/schedule.js +144 -0
  183. package/dist/runtime/database/bounded.d.ts +43 -0
  184. package/dist/runtime/database/bounded.js +108 -0
  185. package/dist/runtime/database/connection.d.ts +8 -0
  186. package/dist/runtime/database/connection.js +18 -0
  187. package/dist/runtime/database/coordination.d.ts +19 -0
  188. package/dist/runtime/database/coordination.js +72 -0
  189. package/dist/runtime/database/flock.d.ts +10 -0
  190. package/dist/runtime/database/flock.js +60 -0
  191. package/dist/runtime/database/gate.d.ts +26 -0
  192. package/dist/runtime/database/gate.js +54 -0
  193. package/dist/runtime/database/keepalive.d.ts +11 -0
  194. package/dist/runtime/database/keepalive.js +22 -0
  195. package/dist/runtime/database/migrations.d.ts +177 -0
  196. package/dist/runtime/database/migrations.js +1018 -0
  197. package/dist/runtime/database/neki/migrations.d.ts +31 -0
  198. package/dist/runtime/database/neki/migrations.js +276 -0
  199. package/dist/runtime/database/neki/session.d.ts +23 -0
  200. package/dist/runtime/database/neki/session.js +31 -0
  201. package/dist/runtime/database/pglite.d.ts +21 -0
  202. package/dist/runtime/database/pglite.js +74 -0
  203. package/dist/runtime/database/replica.d.ts +41 -0
  204. package/dist/runtime/database/replica.js +51 -0
  205. package/dist/runtime/database/shards.d.ts +21 -0
  206. package/dist/runtime/database/shards.js +37 -0
  207. package/dist/runtime/database/snapshot.d.ts +7 -0
  208. package/dist/runtime/database/snapshot.js +7 -0
  209. package/dist/runtime/database/tenancy.d.ts +69 -0
  210. package/dist/runtime/database/tenancy.js +145 -0
  211. package/dist/runtime/database/transient.d.ts +10 -0
  212. package/dist/runtime/database/transient.js +25 -0
  213. package/dist/runtime/drain.d.ts +132 -0
  214. package/dist/runtime/drain.js +109 -0
  215. package/dist/runtime/entity/mailbox.d.ts +86 -0
  216. package/dist/runtime/entity/mailbox.js +121 -0
  217. package/dist/runtime/entity/register.d.ts +424 -0
  218. package/dist/runtime/entity/register.js +669 -0
  219. package/dist/runtime/events/append.d.ts +192 -0
  220. package/dist/runtime/events/append.js +128 -0
  221. package/dist/runtime/events/cursor.d.ts +2 -0
  222. package/dist/runtime/events/cursor.js +5 -0
  223. package/dist/runtime/events/replay.d.ts +20 -0
  224. package/dist/runtime/events/replay.js +45 -0
  225. package/dist/runtime/feeds.d.ts +26 -0
  226. package/dist/runtime/feeds.js +100 -0
  227. package/dist/runtime/fleet/checks.d.ts +17 -0
  228. package/dist/runtime/fleet/checks.js +106 -0
  229. package/dist/runtime/fleet/maintainer.d.ts +38 -0
  230. package/dist/runtime/fleet/maintainer.js +383 -0
  231. package/dist/runtime/fleet/pgoutput.d.ts +41 -0
  232. package/dist/runtime/fleet/pgoutput.js +102 -0
  233. package/dist/runtime/fleet/setup.d.ts +36 -0
  234. package/dist/runtime/fleet/setup.js +58 -0
  235. package/dist/runtime/fleet/subscribe.d.ts +42 -0
  236. package/dist/runtime/fleet/subscribe.js +147 -0
  237. package/dist/runtime/index.d.ts +66 -0
  238. package/dist/runtime/index.js +37 -0
  239. package/dist/runtime/inspector/http.d.ts +19 -0
  240. package/dist/runtime/inspector/http.js +36 -0
  241. package/dist/runtime/inspector/layer.d.ts +46 -0
  242. package/dist/runtime/inspector/layer.js +180 -0
  243. package/dist/runtime/inspector/queries.d.ts +711 -0
  244. package/dist/runtime/inspector/queries.js +494 -0
  245. package/dist/runtime/jobs/attempt.d.ts +254 -0
  246. package/dist/runtime/jobs/attempt.js +413 -0
  247. package/dist/runtime/jobs/isolation.d.ts +20 -0
  248. package/dist/runtime/jobs/isolation.js +10 -0
  249. package/dist/runtime/jobs/progress.d.ts +95 -0
  250. package/dist/runtime/jobs/progress.js +144 -0
  251. package/dist/runtime/layer.d.ts +260 -0
  252. package/dist/runtime/layer.js +876 -0
  253. package/dist/runtime/members.d.ts +381 -0
  254. package/dist/runtime/members.js +8 -0
  255. package/dist/runtime/operators/audit.d.ts +64 -0
  256. package/dist/runtime/operators/audit.js +56 -0
  257. package/dist/runtime/operators/auth.d.ts +29 -0
  258. package/dist/runtime/operators/auth.js +42 -0
  259. package/dist/runtime/operators/export.d.ts +68 -0
  260. package/dist/runtime/operators/export.js +137 -0
  261. package/dist/runtime/operators/grants.d.ts +45 -0
  262. package/dist/runtime/operators/grants.js +40 -0
  263. package/dist/runtime/operators/repair.d.ts +206 -0
  264. package/dist/runtime/operators/repair.js +226 -0
  265. package/dist/runtime/operators/routes.d.ts +35 -0
  266. package/dist/runtime/operators/routes.js +190 -0
  267. package/dist/runtime/operators/seed.d.ts +123 -0
  268. package/dist/runtime/operators/seed.js +123 -0
  269. package/dist/runtime/payloads/versions.d.ts +65 -0
  270. package/dist/runtime/payloads/versions.js +240 -0
  271. package/dist/runtime/peering/authority.d.ts +42 -0
  272. package/dist/runtime/peering/authority.js +132 -0
  273. package/dist/runtime/peering/credentials.d.ts +21 -0
  274. package/dist/runtime/peering/credentials.js +13 -0
  275. package/dist/runtime/peering/transport.d.ts +51 -0
  276. package/dist/runtime/peering/transport.js +175 -0
  277. package/dist/runtime/queries.d.ts +18 -0
  278. package/dist/runtime/queries.js +169 -0
  279. package/dist/runtime/readiness.d.ts +36 -0
  280. package/dist/runtime/readiness.js +44 -0
  281. package/dist/runtime/registration.d.ts +58 -0
  282. package/dist/runtime/registration.js +186 -0
  283. package/dist/runtime/request.d.ts +158 -0
  284. package/dist/runtime/request.js +102 -0
  285. package/dist/runtime/retry.d.ts +8 -0
  286. package/dist/runtime/retry.js +11 -0
  287. package/dist/runtime/runner.d.ts +93 -0
  288. package/dist/runtime/runner.js +122 -0
  289. package/dist/runtime/storage/codec.d.ts +39 -0
  290. package/dist/runtime/storage/codec.js +41 -0
  291. package/dist/runtime/storage/generation.d.ts +69 -0
  292. package/dist/runtime/storage/generation.js +49 -0
  293. package/dist/runtime/storage/placements.d.ts +15 -0
  294. package/dist/runtime/storage/placements.js +38 -0
  295. package/dist/runtime/storage/platform.d.ts +6 -0
  296. package/dist/runtime/storage/platform.js +9 -0
  297. package/dist/runtime/storage/retention.d.ts +73 -0
  298. package/dist/runtime/storage/retention.js +158 -0
  299. package/dist/runtime/subscriptions/identity.d.ts +31 -0
  300. package/dist/runtime/subscriptions/identity.js +45 -0
  301. package/dist/runtime/subscriptions/relay.d.ts +175 -0
  302. package/dist/runtime/subscriptions/relay.js +763 -0
  303. package/dist/runtime/subscriptions/storage.d.ts +40 -0
  304. package/dist/runtime/subscriptions/storage.js +98 -0
  305. package/dist/runtime/sweeps.d.ts +39 -0
  306. package/dist/runtime/sweeps.js +54 -0
  307. package/dist/runtime/telemetry/defects.d.ts +57 -0
  308. package/dist/runtime/telemetry/defects.js +43 -0
  309. package/dist/runtime/telemetry/metrics.d.ts +52 -0
  310. package/dist/runtime/telemetry/metrics.js +138 -0
  311. package/dist/runtime/telemetry/routes.d.ts +19 -0
  312. package/dist/runtime/telemetry/routes.js +18 -0
  313. package/dist/runtime/telemetry/sampler.d.ts +26 -0
  314. package/dist/runtime/telemetry/sampler.js +101 -0
  315. package/dist/runtime/telemetry/spans.d.ts +38 -0
  316. package/dist/runtime/telemetry/spans.js +44 -0
  317. package/dist/runtime/telemetry/usage.d.ts +52 -0
  318. package/dist/runtime/telemetry/usage.js +30 -0
  319. package/dist/runtime/topology/admission.d.ts +13 -0
  320. package/dist/runtime/topology/admission.js +36 -0
  321. package/dist/runtime/topology/locks.d.ts +56 -0
  322. package/dist/runtime/topology/locks.js +81 -0
  323. package/dist/runtime/topology/messages.d.ts +15 -0
  324. package/dist/runtime/topology/messages.js +32 -0
  325. package/dist/runtime/turn/admission.d.ts +22 -0
  326. package/dist/runtime/turn/admission.js +41 -0
  327. package/dist/runtime/turn/blobs.d.ts +38 -0
  328. package/dist/runtime/turn/blobs.js +215 -0
  329. package/dist/runtime/turn/execute.d.ts +208 -0
  330. package/dist/runtime/turn/execute.js +972 -0
  331. package/dist/runtime/turn/group.d.ts +165 -0
  332. package/dist/runtime/turn/group.js +326 -0
  333. package/dist/runtime/turn/hooks.d.ts +44 -0
  334. package/dist/runtime/turn/hooks.js +28 -0
  335. package/dist/runtime/turn/outbox.d.ts +159 -0
  336. package/dist/runtime/turn/outbox.js +269 -0
  337. package/dist/runtime/turn/pipeline.d.ts +68 -0
  338. package/dist/runtime/turn/pipeline.js +83 -0
  339. package/dist/runtime/turn/receipt.d.ts +81 -0
  340. package/dist/runtime/turn/receipt.js +32 -0
  341. package/dist/runtime/turn/relay.d.ts +179 -0
  342. package/dist/runtime/turn/relay.js +685 -0
  343. package/dist/runtime/turn/rows.d.ts +43 -0
  344. package/dist/runtime/turn/rows.js +592 -0
  345. package/dist/runtime/workflows/compatibility.d.ts +125 -0
  346. package/dist/runtime/workflows/compatibility.js +285 -0
  347. package/dist/runtime/workflows/engine.d.ts +107 -0
  348. package/dist/runtime/workflows/engine.js +826 -0
  349. package/dist/runtime/workflows/manifest.d.ts +34 -0
  350. package/dist/runtime/workflows/manifest.js +39 -0
  351. package/dist/runtime/workflows/steps.d.ts +33 -0
  352. package/dist/runtime/workflows/steps.js +152 -0
  353. package/dist/serve/api.d.ts +99 -0
  354. package/dist/serve/api.js +403 -0
  355. package/dist/serve/assertion/binding.d.ts +41 -0
  356. package/dist/serve/assertion/binding.js +92 -0
  357. package/dist/serve/assertion/verify.d.ts +93 -0
  358. package/dist/serve/assertion/verify.js +189 -0
  359. package/dist/serve/auth.d.ts +200 -0
  360. package/dist/serve/auth.js +92 -0
  361. package/dist/serve/clock.d.ts +10 -0
  362. package/dist/serve/clock.js +25 -0
  363. package/dist/serve/jwt.d.ts +65 -0
  364. package/dist/serve/jwt.js +202 -0
  365. package/dist/serve/layer.d.ts +98 -0
  366. package/dist/serve/layer.js +809 -0
  367. package/dist/serve/mcp/endpoint.d.ts +39 -0
  368. package/dist/serve/mcp/endpoint.js +187 -0
  369. package/dist/serve/mcp/tools.d.ts +37 -0
  370. package/dist/serve/mcp/tools.js +125 -0
  371. package/dist/serve/origin.d.ts +6 -0
  372. package/dist/serve/origin.js +21 -0
  373. package/dist/serve/sessions/feed.d.ts +79 -0
  374. package/dist/serve/sessions/feed.js +115 -0
  375. package/dist/serve/sessions/socket.d.ts +56 -0
  376. package/dist/serve/sessions/socket.js +212 -0
  377. package/dist/serve/sessions/sse.d.ts +27 -0
  378. package/dist/serve/sessions/sse.js +26 -0
  379. package/dist/serve/sessions/stream.d.ts +9 -0
  380. package/dist/serve/sessions/stream.js +9 -0
  381. package/dist/serve/sessions/watch.d.ts +12 -0
  382. package/dist/serve/sessions/watch.js +11 -0
  383. package/dist/serve/wire.d.ts +16 -0
  384. package/dist/serve/wire.js +50 -0
  385. package/dist/state/blob.d.ts +71 -0
  386. package/dist/state/blob.js +0 -0
  387. package/dist/state/migration.d.ts +53 -0
  388. package/dist/state/migration.js +29 -0
  389. package/dist/tables/fleet.d.ts +170 -0
  390. package/dist/tables/fleet.js +214 -0
  391. package/dist/tables/owned.d.ts +250 -0
  392. package/dist/tables/owned.js +233 -0
  393. package/dist/testing/actor-test.d.ts +360 -0
  394. package/dist/testing/actor-test.js +337 -0
  395. package/dist/testing/cluster.d.ts +93 -0
  396. package/dist/testing/cluster.js +296 -0
  397. package/dist/testing/conformance/access.d.ts +5 -0
  398. package/dist/testing/conformance/access.js +531 -0
  399. package/dist/testing/conformance/admission.d.ts +11 -0
  400. package/dist/testing/conformance/admission.js +182 -0
  401. package/dist/testing/conformance/adoption.d.ts +2 -0
  402. package/dist/testing/conformance/adoption.js +1019 -0
  403. package/dist/testing/conformance/assertions.d.ts +202 -0
  404. package/dist/testing/conformance/assertions.js +849 -0
  405. package/dist/testing/conformance/batches.d.ts +41 -0
  406. package/dist/testing/conformance/batches.js +388 -0
  407. package/dist/testing/conformance/blobs.d.ts +14 -0
  408. package/dist/testing/conformance/blobs.js +405 -0
  409. package/dist/testing/conformance/capacity.d.ts +8 -0
  410. package/dist/testing/conformance/capacity.js +433 -0
  411. package/dist/testing/conformance/client.d.ts +5 -0
  412. package/dist/testing/conformance/client.js +719 -0
  413. package/dist/testing/conformance/cold-serve.d.ts +16 -0
  414. package/dist/testing/conformance/cold-serve.js +254 -0
  415. package/dist/testing/conformance/connections/actors.d.ts +1143 -0
  416. package/dist/testing/conformance/connections/actors.js +180 -0
  417. package/dist/testing/conformance/connections/harness.d.ts +1754 -0
  418. package/dist/testing/conformance/connections/harness.js +142 -0
  419. package/dist/testing/conformance/connections/holders.d.ts +4 -0
  420. package/dist/testing/conformance/connections/holders.js +165 -0
  421. package/dist/testing/conformance/connections/lifecycle.d.ts +4 -0
  422. package/dist/testing/conformance/connections/lifecycle.js +113 -0
  423. package/dist/testing/conformance/connections/limits.d.ts +4 -0
  424. package/dist/testing/conformance/connections/limits.js +127 -0
  425. package/dist/testing/conformance/connections/reauthorization.d.ts +4 -0
  426. package/dist/testing/conformance/connections/reauthorization.js +170 -0
  427. package/dist/testing/conformance/connections/resync.d.ts +4 -0
  428. package/dist/testing/conformance/connections/resync.js +360 -0
  429. package/dist/testing/conformance/connections/takeover.d.ts +4 -0
  430. package/dist/testing/conformance/connections/takeover.js +287 -0
  431. package/dist/testing/conformance/connections.d.ts +4 -0
  432. package/dist/testing/conformance/connections.js +15 -0
  433. package/dist/testing/conformance/content-blobs.d.ts +16 -0
  434. package/dist/testing/conformance/content-blobs.js +534 -0
  435. package/dist/testing/conformance/counter.d.ts +16 -0
  436. package/dist/testing/conformance/counter.js +813 -0
  437. package/dist/testing/conformance/cron.d.ts +5 -0
  438. package/dist/testing/conformance/cron.js +665 -0
  439. package/dist/testing/conformance/drain.d.ts +26 -0
  440. package/dist/testing/conformance/drain.js +540 -0
  441. package/dist/testing/conformance/effect-control.d.ts +38 -0
  442. package/dist/testing/conformance/effect-control.js +1059 -0
  443. package/dist/testing/conformance/effects.d.ts +23 -0
  444. package/dist/testing/conformance/effects.js +432 -0
  445. package/dist/testing/conformance/events.d.ts +28 -0
  446. package/dist/testing/conformance/events.js +335 -0
  447. package/dist/testing/conformance/export.d.ts +2 -0
  448. package/dist/testing/conformance/export.js +368 -0
  449. package/dist/testing/conformance/fleet.d.ts +444 -0
  450. package/dist/testing/conformance/fleet.js +1009 -0
  451. package/dist/testing/conformance/groups.d.ts +7 -0
  452. package/dist/testing/conformance/groups.js +353 -0
  453. package/dist/testing/conformance/heap.d.ts +3 -0
  454. package/dist/testing/conformance/heap.js +149 -0
  455. package/dist/testing/conformance/http.d.ts +2809 -0
  456. package/dist/testing/conformance/http.js +1234 -0
  457. package/dist/testing/conformance/inspection-views.d.ts +8 -0
  458. package/dist/testing/conformance/inspection-views.js +272 -0
  459. package/dist/testing/conformance/inspector.d.ts +7 -0
  460. package/dist/testing/conformance/inspector.js +650 -0
  461. package/dist/testing/conformance/mint.d.ts +58 -0
  462. package/dist/testing/conformance/mint.js +692 -0
  463. package/dist/testing/conformance/multi-runner.d.ts +8 -0
  464. package/dist/testing/conformance/multi-runner.js +403 -0
  465. package/dist/testing/conformance/neki/plan.d.ts +19 -0
  466. package/dist/testing/conformance/neki/plan.js +30 -0
  467. package/dist/testing/conformance/observability.d.ts +3 -0
  468. package/dist/testing/conformance/observability.js +343 -0
  469. package/dist/testing/conformance/offline.d.ts +4 -0
  470. package/dist/testing/conformance/offline.js +474 -0
  471. package/dist/testing/conformance/operator-harness.d.ts +57 -0
  472. package/dist/testing/conformance/operator-harness.js +78 -0
  473. package/dist/testing/conformance/operator.d.ts +3 -0
  474. package/dist/testing/conformance/operator.js +379 -0
  475. package/dist/testing/conformance/outbox-cross-shard.d.ts +14 -0
  476. package/dist/testing/conformance/outbox-cross-shard.js +160 -0
  477. package/dist/testing/conformance/outbox.d.ts +36 -0
  478. package/dist/testing/conformance/outbox.js +437 -0
  479. package/dist/testing/conformance/payload-migrations.d.ts +3 -0
  480. package/dist/testing/conformance/payload-migrations.js +612 -0
  481. package/dist/testing/conformance/pipeline.d.ts +3 -0
  482. package/dist/testing/conformance/pipeline.js +1040 -0
  483. package/dist/testing/conformance/placement.d.ts +22 -0
  484. package/dist/testing/conformance/placement.js +539 -0
  485. package/dist/testing/conformance/platform.d.ts +40 -0
  486. package/dist/testing/conformance/platform.js +101 -0
  487. package/dist/testing/conformance/progress.d.ts +39 -0
  488. package/dist/testing/conformance/progress.js +666 -0
  489. package/dist/testing/conformance/properties.d.ts +6 -0
  490. package/dist/testing/conformance/properties.js +295 -0
  491. package/dist/testing/conformance/protocols.d.ts +4 -0
  492. package/dist/testing/conformance/protocols.js +618 -0
  493. package/dist/testing/conformance/provider/advertise.d.ts +21 -0
  494. package/dist/testing/conformance/provider/advertise.js +44 -0
  495. package/dist/testing/conformance/read-your-writes.d.ts +20 -0
  496. package/dist/testing/conformance/read-your-writes.js +192 -0
  497. package/dist/testing/conformance/reducers.d.ts +8 -0
  498. package/dist/testing/conformance/reducers.js +258 -0
  499. package/dist/testing/conformance/relay.d.ts +42 -0
  500. package/dist/testing/conformance/relay.js +1115 -0
  501. package/dist/testing/conformance/restore.d.ts +43 -0
  502. package/dist/testing/conformance/restore.js +341 -0
  503. package/dist/testing/conformance/retention.d.ts +14 -0
  504. package/dist/testing/conformance/retention.js +463 -0
  505. package/dist/testing/conformance/rls.d.ts +24 -0
  506. package/dist/testing/conformance/rls.js +576 -0
  507. package/dist/testing/conformance/simulation.d.ts +31 -0
  508. package/dist/testing/conformance/simulation.js +167 -0
  509. package/dist/testing/conformance/single-shard.d.ts +2 -0
  510. package/dist/testing/conformance/single-shard.js +125 -0
  511. package/dist/testing/conformance/singleton.d.ts +3 -0
  512. package/dist/testing/conformance/singleton.js +293 -0
  513. package/dist/testing/conformance/statements.d.ts +60 -0
  514. package/dist/testing/conformance/statements.js +108 -0
  515. package/dist/testing/conformance/streams.d.ts +7 -0
  516. package/dist/testing/conformance/streams.js +280 -0
  517. package/dist/testing/conformance/subscriptions/access.d.ts +4 -0
  518. package/dist/testing/conformance/subscriptions/access.js +98 -0
  519. package/dist/testing/conformance/subscriptions/actors.d.ts +4910 -0
  520. package/dist/testing/conformance/subscriptions/actors.js +372 -0
  521. package/dist/testing/conformance/subscriptions/cluster.d.ts +4 -0
  522. package/dist/testing/conformance/subscriptions/cluster.js +89 -0
  523. package/dist/testing/conformance/subscriptions/delivery.d.ts +4 -0
  524. package/dist/testing/conformance/subscriptions/delivery.js +253 -0
  525. package/dist/testing/conformance/subscriptions/deploys.d.ts +4 -0
  526. package/dist/testing/conformance/subscriptions/deploys.js +162 -0
  527. package/dist/testing/conformance/subscriptions/epochs.d.ts +4 -0
  528. package/dist/testing/conformance/subscriptions/epochs.js +169 -0
  529. package/dist/testing/conformance/subscriptions/failures.d.ts +4 -0
  530. package/dist/testing/conformance/subscriptions/failures.js +108 -0
  531. package/dist/testing/conformance/subscriptions/feeds.d.ts +4 -0
  532. package/dist/testing/conformance/subscriptions/feeds.js +218 -0
  533. package/dist/testing/conformance/subscriptions/harness.d.ts +124 -0
  534. package/dist/testing/conformance/subscriptions/harness.js +143 -0
  535. package/dist/testing/conformance/subscriptions/operator.d.ts +4 -0
  536. package/dist/testing/conformance/subscriptions/operator.js +128 -0
  537. package/dist/testing/conformance/subscriptions/retention.d.ts +4 -0
  538. package/dist/testing/conformance/subscriptions/retention.js +288 -0
  539. package/dist/testing/conformance/subscriptions/routing.d.ts +4 -0
  540. package/dist/testing/conformance/subscriptions/routing.js +179 -0
  541. package/dist/testing/conformance/subscriptions.d.ts +4 -0
  542. package/dist/testing/conformance/subscriptions.js +19 -0
  543. package/dist/testing/conformance/tables.d.ts +125 -0
  544. package/dist/testing/conformance/tables.js +664 -0
  545. package/dist/testing/conformance/transports/actors.d.ts +1907 -0
  546. package/dist/testing/conformance/transports/actors.js +163 -0
  547. package/dist/testing/conformance/transports/client.d.ts +3 -0
  548. package/dist/testing/conformance/transports/client.js +237 -0
  549. package/dist/testing/conformance/transports/feeds.d.ts +3 -0
  550. package/dist/testing/conformance/transports/feeds.js +232 -0
  551. package/dist/testing/conformance/transports/harness.d.ts +93 -0
  552. package/dist/testing/conformance/transports/harness.js +84 -0
  553. package/dist/testing/conformance/transports/streams.d.ts +3 -0
  554. package/dist/testing/conformance/transports/streams.js +96 -0
  555. package/dist/testing/conformance/transports/websocket.d.ts +3 -0
  556. package/dist/testing/conformance/transports/websocket.js +384 -0
  557. package/dist/testing/conformance/transports/wire.d.ts +141 -0
  558. package/dist/testing/conformance/transports/wire.js +204 -0
  559. package/dist/testing/conformance/transports.d.ts +3 -0
  560. package/dist/testing/conformance/transports.js +11 -0
  561. package/dist/testing/conformance/watch.d.ts +59 -0
  562. package/dist/testing/conformance/watch.js +879 -0
  563. package/dist/testing/conformance/workflow-engine.d.ts +97 -0
  564. package/dist/testing/conformance/workflow-engine.js +253 -0
  565. package/dist/testing/conformance/workflow-versions.d.ts +3 -0
  566. package/dist/testing/conformance/workflow-versions.js +545 -0
  567. package/dist/testing/conformance/workflows/access.d.ts +4 -0
  568. package/dist/testing/conformance/workflows/access.js +147 -0
  569. package/dist/testing/conformance/workflows/actors.d.ts +2317 -0
  570. package/dist/testing/conformance/workflows/actors.js +305 -0
  571. package/dist/testing/conformance/workflows/basics.d.ts +4 -0
  572. package/dist/testing/conformance/workflows/basics.js +144 -0
  573. package/dist/testing/conformance/workflows/engine.d.ts +4 -0
  574. package/dist/testing/conformance/workflows/engine.js +80 -0
  575. package/dist/testing/conformance/workflows/guards.d.ts +4 -0
  576. package/dist/testing/conformance/workflows/guards.js +172 -0
  577. package/dist/testing/conformance/workflows/harness.d.ts +14 -0
  578. package/dist/testing/conformance/workflows/harness.js +44 -0
  579. package/dist/testing/conformance/workflows/recovery.d.ts +4 -0
  580. package/dist/testing/conformance/workflows/recovery.js +181 -0
  581. package/dist/testing/conformance/workflows/starts.d.ts +4 -0
  582. package/dist/testing/conformance/workflows/starts.js +57 -0
  583. package/dist/testing/conformance/workflows/suspension.d.ts +4 -0
  584. package/dist/testing/conformance/workflows/suspension.js +106 -0
  585. package/dist/testing/conformance/workflows/waits.d.ts +4 -0
  586. package/dist/testing/conformance/workflows/waits.js +284 -0
  587. package/dist/testing/conformance/workflows.d.ts +4 -0
  588. package/dist/testing/conformance/workflows.js +19 -0
  589. package/dist/testing/conformance.d.ts +330 -0
  590. package/dist/testing/conformance.js +364 -0
  591. package/dist/testing/database.d.ts +31 -0
  592. package/dist/testing/database.js +70 -0
  593. package/dist/testing/foundation.d.ts +41 -0
  594. package/dist/testing/foundation.js +517 -0
  595. package/dist/testing/index.d.ts +20 -0
  596. package/dist/testing/index.js +12 -0
  597. package/dist/testing/property.d.ts +40 -0
  598. package/dist/testing/property.js +95 -0
  599. package/dist/testing/simulate-cluster.d.ts +112 -0
  600. package/dist/testing/simulate-cluster.js +257 -0
  601. package/dist/testing/simulate.d.ts +90 -0
  602. package/dist/testing/simulate.js +127 -0
  603. package/package.json +1 -0
@@ -0,0 +1,685 @@
1
+ import { Cause, Clock, Crypto, Deferred, Effect, Exit, FiberSet, Queue, Random, Result, Schema, Semaphore, } from "effect";
2
+ import { SqlClient, Statement } from "effect/sql";
3
+ import { Outcome, Request } from "../request.js";
4
+ import {} from "../members.js";
5
+ import { progressPool } from "../jobs/progress.js";
6
+ import { groupLock, groupRow, jobAttempts, } from "../jobs/attempt.js";
7
+ import { CRON_PREFIX } from "../cron/key.js";
8
+ import { cronTicks } from "../cron/schedule.js";
9
+ import { TurnHooks } from "./hooks.js";
10
+ import { count as tally, Metrics } from "../telemetry/metrics.js";
11
+ import { SpanNames } from "../telemetry/spans.js";
12
+ import { databaseTime, FrameworkClock } from "./admission.js";
13
+ import { BUCKETS, CallerJson } from "./outbox.js";
14
+ import { shardClients } from "../database/shards.js";
15
+ /**
16
+ * A drain whose deliveries keep staging due work after this many rounds, each
17
+ * of which claimed every row then due, is a delivery loop.
18
+ */
19
+ const DRAIN_ROUNDS = 100;
20
+ /**
21
+ * The claim statement's result columns before its last three, in order: each
22
+ * outbox column's source in the claimed row aliased `o`, and the value a
23
+ * result row that claimed no outbox row carries in its place.
24
+ */
25
+ const CLAIMED_COLUMNS = [
26
+ ["kind", "o.kind", "NULL::text"],
27
+ ["routing_key", "o.routing_key::text", "NULL::text"],
28
+ ["intent_id", "o.intent_id", "NULL::text"],
29
+ ["attempts", "o.attempts", "0"],
30
+ ["last_error", "o.last_error", "NULL::text"],
31
+ ["ambiguous", "o.ambiguous", "false"],
32
+ ["tenant_id", "o.tenant_id", "NULL::text"],
33
+ ["actor_type", "o.actor_type", "NULL::text"],
34
+ ["actor_id", "o.actor_id", "NULL::text"],
35
+ ["target_type", "o.target_type", "NULL::text"],
36
+ ["target_id", "o.target_id", "NULL::text"],
37
+ ["command", "o.command", "NULL::text"],
38
+ ["payload", "o.payload", "NULL::text"],
39
+ ["payload_version", "o.payload_version", "0"],
40
+ ["caller", "o.caller", "NULL::text"],
41
+ ["claimed_until", "o.due_at_ms::text", "NULL::text"],
42
+ ["timer_key", "o.timer_key", "NULL::text"],
43
+ ["scheduled_at", "o.scheduled_at_ms::text", "NULL::text"],
44
+ ["cancelled", "o.cancelled_at_ms IS NOT NULL", "false"],
45
+ ["maybe_applied", "o.maybe_applied", "false"],
46
+ ];
47
+ /** The claimed columns of the outbox row aliased `o`. */
48
+ const claimedColumns = (sql) => sql.literal(CLAIMED_COLUMNS.map(([name, source]) => `${source} AS ${name}`).join(", "));
49
+ /** The claimed columns of a result row that claimed no outbox row, with `kind` set. */
50
+ const unclaimedColumns = (sql, kind) => sql `${kind}::text, ${sql.literal(CLAIMED_COLUMNS.slice(1)
51
+ .map(([, , empty]) => empty)
52
+ .join(", "))}`;
53
+ /**
54
+ * The due-work probe: up to `limit` due rows of `kind` from one
55
+ * `(bucket, kind, due_at_ms)` index range per bucket, so its cost follows due
56
+ * rows of one kind, not stored actors or future timers. It takes no locks; a
57
+ * claim locks only the rows it takes from it. `only` filters inside each
58
+ * bucket's probe, so due rows the caller cannot claim never fill the
59
+ * per-bucket limit ahead of rows it can.
60
+ */
61
+ export const candidates = ({ sql, kind, now, limit, only = sql.literal(""), range = BUCKETS, }) => sql `SELECT o.routing_key, o.intent_id, o.actor_type, o.command
62
+ FROM generate_series(${range.first}::int, ${range.last}::int) AS b(bucket)
63
+ CROSS JOIN LATERAL (
64
+ SELECT routing_key, intent_id, due_at_ms, actor_type, command FROM actor_outbox
65
+ WHERE actor_outbox.bucket = b.bucket AND actor_outbox.kind = ${kind}
66
+ AND actor_outbox.due_at_ms <= ${now} ${only}
67
+ ORDER BY actor_outbox.due_at_ms LIMIT ${limit}
68
+ ) o`;
69
+ /**
70
+ * Claims due intents and due jobs in one autocommit statement. `now` is the
71
+ * outbox clock: the database's statement start time plus the test offset,
72
+ * which is never earlier than a row committed before the claim was sent. A
73
+ * pass therefore costs one round trip whatever it claims.
74
+ *
75
+ * `SKIP LOCKED` passes over rows another runner is claiming, and each claim
76
+ * moves the row's `due_at_ms` past its lease, so no runner scans it again
77
+ * until the lease ends. An intent whose settle dies therefore waits
78
+ * `max(lease, backoff(attempts))` instead of sorting ahead of newer work. A
79
+ * job with no executor on this runner is never claimed here; it stays due for
80
+ * a runner that has one. The two kinds never share a row, so the two updates
81
+ * are disjoint. A kind that claims nothing although its probe found
82
+ * candidates returns one `skipped-*` row with the candidate count, so the
83
+ * relay can widen the next probe past rows other transactions hold locked.
84
+ * Subscription work rides in the same statement, so a pass stays one round
85
+ * trip.
86
+ */
87
+ const claimDue = ({ sql, now, intents, jobs, subscriptions, range, }) => {
88
+ const parts = [];
89
+ const results = [];
90
+ if (intents !== undefined) {
91
+ const { limit, leaseMs, maxBackoffMs, probe = 2 * limit, cronActors = [] } = intents;
92
+ const local = cronActors.length === 0 ? sql `` : sql ` OR actor_type IN ${sql.in(cronActors)}`;
93
+ parts.push(sql `intent_candidates AS (
94
+ ${candidates({
95
+ sql,
96
+ range,
97
+ kind: "intent",
98
+ now,
99
+ limit: probe,
100
+ only: sql `AND (timer_key IS NULL OR left(timer_key, 6) <> ${CRON_PREFIX}${local})`,
101
+ })}
102
+ ORDER BY o.due_at_ms LIMIT ${probe}
103
+ ),
104
+ intent_locked AS (
105
+ SELECT o.routing_key, o.intent_id FROM actor_outbox o
106
+ JOIN intent_candidates USING (routing_key, intent_id)
107
+ WHERE o.kind = 'intent' AND o.due_at_ms <= ${now}
108
+ ORDER BY o.due_at_ms LIMIT ${limit}
109
+ FOR UPDATE OF o SKIP LOCKED
110
+ ),
111
+ intent_claimed AS (
112
+ UPDATE actor_outbox o SET attempts = o.attempts + 1,
113
+ due_at_ms = ${now} + greatest(${leaseMs}::bigint,
114
+ least(1000 * power(2, least(o.attempts, 31)), ${maxBackoffMs}::bigint))::bigint
115
+ FROM intent_locked c
116
+ WHERE o.routing_key = c.routing_key AND o.intent_id = c.intent_id
117
+ RETURNING ${claimedColumns(sql)},
118
+ (SELECT count(*) FROM intent_candidates)::int AS candidates, false AS exhausted,
119
+ NULL::text AS work
120
+ )`);
121
+ results.push(sql `SELECT * FROM intent_claimed`, skipped(sql, "intent"));
122
+ }
123
+ if (jobs !== undefined && jobs.executors.length > 0) {
124
+ const { permits, leaseMs, executors, probe = 2 * permits } = jobs;
125
+ parts.push(sql `mine (actor_type, command, max_attempts) AS (
126
+ VALUES ${sql.csv(executors.map(({ actor, job, registered }) => sql `(${actor}::text, ${job}::text, ${registered.attempts}::int)`))}
127
+ ),
128
+ job_candidates AS (
129
+ ${candidates({
130
+ sql,
131
+ range,
132
+ kind: "job",
133
+ now,
134
+ limit: probe,
135
+ only: sql.literal("AND (actor_type, command) IN (SELECT actor_type, command FROM mine)"),
136
+ })}
137
+ ORDER BY o.due_at_ms LIMIT ${probe}
138
+ ),
139
+ job_locked AS (
140
+ SELECT o.routing_key, o.intent_id, o.attempts AS previous, m.max_attempts
141
+ FROM actor_outbox o
142
+ JOIN job_candidates USING (routing_key, intent_id)
143
+ JOIN mine m ON m.actor_type = o.actor_type AND m.command = o.command
144
+ WHERE o.kind = 'job' AND o.due_at_ms <= ${now}
145
+ ORDER BY o.due_at_ms LIMIT ${permits}
146
+ FOR UPDATE OF o SKIP LOCKED
147
+ ),
148
+ job_claimed AS (
149
+ ${claimJobs(sql, now, leaseMs, sql `job_locked`, sql `(SELECT count(*) FROM job_candidates)::int`)}
150
+ )`);
151
+ results.push(sql `SELECT *, NULL::text AS work FROM job_claimed`, skipped(sql, "job"));
152
+ }
153
+ if (subscriptions !== undefined) {
154
+ parts.push(...subscriptions.parts);
155
+ for (const result of subscriptions.results)
156
+ results.push(sql `SELECT ${unclaimedColumns(sql, "work")}, 0, false, claimed.work
157
+ FROM (${result}) AS claimed`);
158
+ }
159
+ if (results.length === 0)
160
+ return Effect.succeed([]);
161
+ return sql `WITH ${sql.csv(parts)}
162
+ ${sql.join(" UNION ALL ", false)(results)}`;
163
+ };
164
+ /**
165
+ * Claims the job rows `locked` names. A cancelled row is claimed only to be
166
+ * settled, so it keeps its attempts; any other row starts its next attempt
167
+ * and runs, unless its last attempt already ended without an outcome or an
168
+ * attempt's failure was final, either of which exhausts it. `maybe_applied`
169
+ * then covers every attempt before this one. RETURNING sees the updated row,
170
+ * so exhaustion is judged on the attempts before this claim.
171
+ */
172
+ const claimJobs = (sql, now, leaseMs, locked, candidateCount) => {
173
+ const attempting = sql `o.cancelled_at_ms IS NULL AND o.attempts < c.max_attempts
174
+ AND NOT o.final_failure`;
175
+ return sql `UPDATE actor_outbox o SET
176
+ due_at_ms = ${now} + ${leaseMs}::bigint,
177
+ attempts = CASE WHEN ${attempting} THEN o.attempts + 1 ELSE o.attempts END,
178
+ ambiguous = CASE WHEN ${attempting} THEN true ELSE o.ambiguous END,
179
+ maybe_applied = CASE WHEN ${attempting}
180
+ THEN o.maybe_applied OR (o.attempts > 0 AND o.ambiguous) ELSE o.maybe_applied END,
181
+ last_error = CASE WHEN ${attempting}
182
+ THEN 'Attempt ' || (o.attempts + 1) || ' ended without reporting an outcome'
183
+ ELSE o.last_error END,
184
+ running = ${attempting},
185
+ waiting = false
186
+ FROM ${locked} c
187
+ WHERE o.routing_key = c.routing_key AND o.intent_id = c.intent_id
188
+ RETURNING ${claimedColumns(sql)}, ${candidateCount} AS candidates,
189
+ o.cancelled_at_ms IS NULL
190
+ AND (c.previous >= c.max_attempts OR o.final_failure) AS exhausted`;
191
+ };
192
+ /**
193
+ * The actors with due rows of capped jobs this runner executes, oldest first;
194
+ * at most `limit`. Like the uncapped probe, it reads one index range per
195
+ * bucket and takes no locks.
196
+ */
197
+ const cappedGroups = ({ sql, now, executors, limit, range, }) => sql `WITH mine (actor_type, command) AS (
198
+ VALUES ${sql.csv(executors.map(({ actor, job }) => sql `(${actor}::text, ${job}::text)`))}
199
+ ),
200
+ due AS (
201
+ ${candidates({
202
+ sql,
203
+ range,
204
+ kind: "job",
205
+ now,
206
+ limit,
207
+ only: sql.literal("AND (actor_type, command) IN (SELECT actor_type, command FROM mine)"),
208
+ })}
209
+ )
210
+ SELECT o.routing_key::text AS routing_key, o.tenant_id, o.actor_type, o.actor_id, o.command,
211
+ (SELECT count(*) FROM due)::int AS due_rows
212
+ FROM actor_outbox o JOIN due USING (routing_key, intent_id)
213
+ GROUP BY o.routing_key, o.tenant_id, o.actor_type, o.actor_id, o.command
214
+ ORDER BY min(o.due_at_ms) LIMIT ${limit}`;
215
+ /**
216
+ * Claims one capped group's jobs in its own transaction, under its actor's
217
+ * generation row lock, so claims on every runner see each other's running rows. It
218
+ * settles cancelled rows whose attempt ended, starts the oldest rows by
219
+ * `(ready_at_ms, intent_id)` while fewer than `cap` attempts hold a live
220
+ * lease, and moves the group's other due rows out of the due range as
221
+ * waiting, so they never fill a probe ahead of other actors' work. A waiting
222
+ * row becomes due again when an attempt of its group settles, or after one
223
+ * lease.
224
+ *
225
+ * An attempt renews, backs off, and settles its own row without the group's
226
+ * lock, so before it counts live leases the claim waits for those writes on
227
+ * the group's running rows. Otherwise `SKIP LOCKED` would pass over an older
228
+ * row whose lost lease is being renewed and start a younger one, and the
229
+ * renewal would then leave two attempts holding leases under a cap of one.
230
+ */
231
+ export const claimCapped = ({ sql, now, group, cap, maxAttempts, permits, leaseMs, }) => sql.withTransaction(Effect.gen(function* () {
232
+ yield* groupLock({ sql, group });
233
+ const inGroup = groupRow({ sql, group });
234
+ yield* sql `SELECT 1 FROM actor_outbox o WHERE ${inGroup} AND o.running FOR UPDATE OF o`;
235
+ return yield* sql `WITH live AS (
236
+ SELECT count(*)::int AS n FROM actor_outbox o
237
+ WHERE ${inGroup} AND o.running AND o.due_at_ms > ${now}
238
+ ),
239
+ settle AS (
240
+ SELECT o.routing_key, o.intent_id, o.attempts AS previous, ${maxAttempts}::int AS max_attempts
241
+ FROM actor_outbox o
242
+ WHERE ${inGroup} AND o.cancelled_at_ms IS NOT NULL AND o.due_at_ms <= ${now}
243
+ ORDER BY o.due_at_ms, o.intent_id
244
+ LIMIT ${permits}::int
245
+ FOR UPDATE OF o SKIP LOCKED
246
+ ),
247
+ next AS (
248
+ SELECT o.routing_key, o.intent_id, o.attempts AS previous, ${maxAttempts}::int AS max_attempts
249
+ FROM actor_outbox o
250
+ WHERE ${inGroup} AND o.cancelled_at_ms IS NULL
251
+ AND (o.due_at_ms <= ${now} OR (o.waiting AND NOT o.running))
252
+ ORDER BY o.ready_at_ms, o.intent_id
253
+ LIMIT greatest(0, least(${cap}::int - (SELECT n FROM live),
254
+ ${permits}::int - (SELECT count(*)::int FROM settle)))
255
+ FOR UPDATE OF o SKIP LOCKED
256
+ ),
257
+ locked AS (SELECT * FROM settle UNION ALL SELECT * FROM next),
258
+ claimed AS (${claimJobs(sql, now, leaseMs, sql `locked`, sql `0`)}),
259
+ deferred AS (
260
+ UPDATE actor_outbox o SET waiting = true, running = false,
261
+ due_at_ms = ${now} + ${leaseMs}::bigint
262
+ WHERE ${inGroup} AND o.cancelled_at_ms IS NULL AND o.due_at_ms <= ${now}
263
+ AND o.intent_id NOT IN (SELECT intent_id FROM next)
264
+ AND o.intent_id IN (
265
+ SELECT o.intent_id FROM actor_outbox o
266
+ WHERE ${inGroup} AND o.cancelled_at_ms IS NULL AND o.due_at_ms <= ${now}
267
+ FOR UPDATE OF o SKIP LOCKED
268
+ )
269
+ RETURNING 1
270
+ )
271
+ SELECT * FROM claimed`;
272
+ }));
273
+ /** One row reporting `kind`'s candidates when its claim took none of them. */
274
+ const skipped = (sql, kind) => {
275
+ const claimed = sql.literal(`${kind}_claimed`);
276
+ const found = sql.literal(`${kind}_candidates`);
277
+ return sql `SELECT ${unclaimedColumns(sql, `skipped-${kind}`)},
278
+ (SELECT count(*) FROM ${found})::int, false, NULL::text
279
+ WHERE NOT EXISTS (SELECT 1 FROM ${claimed}) AND EXISTS (SELECT 1 FROM ${found})`;
280
+ };
281
+ /** The outbox clock inside a statement: its start time on the database plus the test offset. */
282
+ export const outboxNow = ({ sql, offsetMillis, }) => sql `(floor(extract(epoch FROM statement_timestamp()) * 1000)::bigint + ${offsetMillis}::bigint)`;
283
+ /** The intent half of `claimDue` at a fixed `now`, as a statement to inspect. */
284
+ export const claimIntents = ({ sql, now, range, ...intents }) => claimDue({
285
+ sql,
286
+ range,
287
+ now: sql `${now}::bigint`,
288
+ intents,
289
+ });
290
+ /** The widest probe, as a multiple of twice the free capacity. */
291
+ const MAX_WIDEN = 64;
292
+ /** Logs a non-interrupt failure of the work under `message` and completes with `void`; an interruption stays one. */
293
+ const logFailure = (message) => (self) => Effect.catchCause(self, (cause) => Cause.hasInterruptsOnly(cause) ? Effect.interrupt : Effect.logError(message, cause));
294
+ /**
295
+ * One family of claimed work on this runner: its running fibers, how many may
296
+ * run at once, and what its last claim saw. A claim takes at most `free` rows
297
+ * of the family, so no claimed row waits locally while its lease runs down.
298
+ * When the last claim saw more due candidates than it took, each finishing
299
+ * fiber wakes the relay to claim again instead of waiting for the poll; when
300
+ * it also left room, which happens when other transactions hold the earliest
301
+ * rows locked, the next probe doubles, up to `MAX_WIDEN`.
302
+ */
303
+ const lane = Effect.fnUntraced(function* (capacity, wake) {
304
+ const fibers = yield* FiberSet.make();
305
+ let more = false;
306
+ let widen = 1;
307
+ return {
308
+ fibers,
309
+ /** Rows a claim may take now. */
310
+ free: Effect.map(FiberSet.size(fibers), (running) => capacity - running),
311
+ /** The probe size for `free` rows. */
312
+ probe: (free) => 2 * free * widen,
313
+ get more() {
314
+ return more;
315
+ },
316
+ /** Records what a claim of up to `free` rows took and how many due candidates it found. */
317
+ claimed: (free, taken, found) => {
318
+ more = taken > 0 && found > taken;
319
+ widen = taken < free && found > taken ? Math.min(widen * 2, MAX_WIDEN) : 1;
320
+ },
321
+ /** Records whether a claim may have left due rows of the family behind. */
322
+ backlog: (left) => {
323
+ more = left;
324
+ },
325
+ /** Runs `work` on the family; its end wakes the relay when rows were left due. */
326
+ start: (work) => FiberSet.run(fibers, work.pipe(Effect.ensuring(Effect.suspend(() => (more ? wake : Effect.void))))).pipe(Effect.asVoid),
327
+ };
328
+ });
329
+ /**
330
+ * The outbox relay of one runner among any number sharing the database. Each
331
+ * pass claims, for every family of work (intents, job attempts, and feed,
332
+ * control, and subscription work), only as many rows as the family has free
333
+ * fibers, then starts each one at once. A pass runs uninterruptibly under a
334
+ * lock, so every row a claim returns reaches a fiber that can release it.
335
+ * When both capped and uncapped jobs are due, passes alternate which claims
336
+ * first, so a steady stream of either can't take every permit.
337
+ *
338
+ * An intent is delivered as a direct command whose command id is the intent
339
+ * id, and its row is deleted only after the receiver's receipt has committed.
340
+ * A crash leaves the claim in place until its lease ends; any runner then
341
+ * redelivers, and the receipt deduplicates. A row that cannot form a request,
342
+ * a delivery that fails, and a defect outcome back off and retry with no retry
343
+ * limit; the logged warning and `attempts` are the operator signal. A declared
344
+ * failure is a committed receipt, so only a missing receipt retries. An
345
+ * interrupted delivery makes its row due at once.
346
+ *
347
+ * A job attempt runs on its own family and settles its row itself; see
348
+ * `jobAttempts`. A running attempt is registered before the pass releases its
349
+ * lock and until its outcome is written, so every clock jump after the claim
350
+ * moves its lease; a local cancellation signals running attempts directly.
351
+ *
352
+ * Subscription work runs in its own families, so a subscription backlog never
353
+ * delays intents, timers, or jobs; expansion work starts the rows it leased
354
+ * without a claim pass; `schedules` names the cron actors whose ticks this
355
+ * runner claims. The returned handle: `run` loops passes on a jittered poll
356
+ * and `wake`; `drain` waits for in-flight work, which may stage more, then
357
+ * claims again until a claim finds nothing while nothing was running (work
358
+ * runs only on fibers a pass starts, so none running means nothing can stage
359
+ * rows after that claim read), failing if deliveries keep staging due work,
360
+ * and counting toward that limit only rounds that drained every due row;
361
+ * `stop` ends claims and interrupts deliveries at once, since a delivery only
362
+ * waits on a turn its receiver's owner finishes or rolls back on its own, and
363
+ * the receiver's receipt answers a redelivery of work that did commit;
364
+ * `interruptAttempts` interrupts running attempts and returns how many,
365
+ * leaving their claims and `ambiguous` marks because the provider may have
366
+ * applied the call, so another runner takes the job over once the lease ends;
367
+ * `attemptsIdle` waits for attempts to end; `extendLeases` moves running
368
+ * attempts' leases with a jump of the outbox clock, as the renewals during
369
+ * that time would have, holding the pass lock so no claim reads the clock
370
+ * between the moved leases and the jump; `cancelled` makes running attempts
371
+ * check for cancellation now. Closing the scope stops further claims, after
372
+ * taking the lock so a pass in progress hands its rows to fibers first and
373
+ * they are interrupted and released.
374
+ */
375
+ export const outboxRelay = Effect.fnUntraced(function* (deliver, executors, settings, subscriptions, schedules = () => new Map()) {
376
+ const sql = yield* SqlClient.SqlClient;
377
+ const shards = yield* shardClients;
378
+ let firstShard = 0;
379
+ const services = yield* Effect.context();
380
+ const lock = Semaphore.makeUnsafe(1);
381
+ const signals = yield* Queue.sliding(1);
382
+ const wakeRelay = Queue.offer(signals, undefined).pipe(Effect.asVoid);
383
+ const workConcurrency = subscriptions?.concurrency ?? 0;
384
+ const lanes = {
385
+ intents: yield* lane(settings.deliveryConcurrency, wakeRelay),
386
+ jobs: yield* lane(settings.executorConcurrency, wakeRelay),
387
+ feed: yield* lane(workConcurrency, wakeRelay),
388
+ control: yield* lane(workConcurrency, wakeRelay),
389
+ subscription: yield* lane(workConcurrency, wakeRelay),
390
+ };
391
+ const hooks = yield* TurnHooks;
392
+ let cancelChecks = Deferred.makeUnsafe();
393
+ const renewEveryMs = Math.min(settings.cancelCheckMs ?? settings.executorLeaseMs / 3, settings.executorLeaseMs / 3);
394
+ const attempt = yield* jobAttempts({
395
+ progress: yield* progressPool(),
396
+ leaseMs: settings.executorLeaseMs,
397
+ renewEveryMs,
398
+ wake: wakeRelay,
399
+ });
400
+ const ticks = cronTicks({
401
+ sql,
402
+ crypto: yield* Crypto.Crypto,
403
+ schedules,
404
+ retryWindowMs: settings.retryWindowMs,
405
+ });
406
+ const startWork = (work) => subscriptions === undefined
407
+ ? Effect.void
408
+ : lanes[work.kind].start(subscriptions.run(work, handoff).pipe(logFailure("Subscription relay work failed")));
409
+ const handoff = { free: lanes.subscription.free, start: startWork };
410
+ let cappedTurn = false;
411
+ let stopping = false;
412
+ const running = new Map();
413
+ const backoffMs = (attempts) => Math.min(1000 * 2 ** Math.max(attempts - 1, 0), settings.maxBackoffMs);
414
+ const requestOf = (row) => Schema.decodeEffect(CallerJson)(row.caller).pipe(Effect.flatMap((caller) => Schema.decodeEffect(Request)({
415
+ ref: { tenant: row.tenant_id, actor: row.target_type, id: row.target_id },
416
+ caller,
417
+ command: row.command,
418
+ commandId: row.intent_id,
419
+ payload: row.payload,
420
+ intent: { tenant: row.tenant_id, actor: row.actor_type, id: row.actor_id },
421
+ })));
422
+ const deliverIntent = Effect.fnUntraced(function* (row) {
423
+ const routingKey = BigInt(row.routing_key);
424
+ const claim = sql `routing_key = ${routingKey} AND intent_id = ${row.intent_id}
425
+ AND kind = 'intent' AND due_at_ms = ${BigInt(row.claimed_until)}`;
426
+ const retryLater = (reason, cause) => Effect.gen(function* () {
427
+ yield* Effect.logWarning("Outbox delivery failed; retrying with backoff", cause).pipe(Effect.annotateLogs({
428
+ actor: row.target_type,
429
+ id: row.target_id,
430
+ tenant: row.tenant_id,
431
+ command: row.command,
432
+ commandId: row.intent_id,
433
+ reason,
434
+ }));
435
+ yield* sql `UPDATE actor_outbox SET due_at_ms = ${(yield* databaseTime) + backoffMs(row.attempts)}
436
+ WHERE ${claim}`;
437
+ yield* tally(Metrics.relayRetried, { kind: "intent" }, 1);
438
+ });
439
+ return yield* Effect.gen(function* () {
440
+ const tick = ticks.isTick(row);
441
+ const route = tick ? yield* ticks.settleUnfired(row, claim, backoffMs(row.attempts)) : row;
442
+ if (route === undefined)
443
+ return;
444
+ const decoded = yield* requestOf({ ...row, ...route }).pipe(Effect.result);
445
+ if (Result.isFailure(decoded))
446
+ return yield* retryLater("UnreadableRow", decoded.failure);
447
+ const request = decoded.success;
448
+ yield* hooks.at("afterClaim", request);
449
+ const delivered = yield* deliver(request).pipe(Effect.result);
450
+ if (Result.isFailure(delivered))
451
+ return yield* retryLater(delivered.failure.reason._tag, delivered.failure);
452
+ if (Outcome.guards.Defect(delivered.success))
453
+ return yield* retryLater("Defect", delivered.success.cause);
454
+ yield* hooks.at("beforeOutboxDelete", request);
455
+ if (tick)
456
+ yield* ticks.settleFired(row, claim);
457
+ else
458
+ yield* sql `DELETE FROM actor_outbox WHERE ${claim}`;
459
+ yield* tally(Metrics.relayDelivered, { kind: "intent" }, 1);
460
+ }).pipe(Effect.withSpan(SpanNames.relayIntent, {
461
+ kind: "producer",
462
+ attributes: {
463
+ "actor.type": row.target_type,
464
+ "actor.tenant": row.tenant_id,
465
+ "actor.id": row.target_id,
466
+ "command.name": row.command,
467
+ "command.id": row.intent_id,
468
+ "relay.attempt": row.attempts,
469
+ "relay.timer": row.timer_key !== null,
470
+ },
471
+ }, { captureStackTrace: false }), Effect.onInterrupt(() => databaseTime.pipe(Effect.flatMap((at) => sql `UPDATE actor_outbox SET due_at_ms = ${at} WHERE ${claim}`), Effect.ignore)));
472
+ });
473
+ const cancelled = Effect.sync(() => {
474
+ const previous = cancelChecks;
475
+ cancelChecks = Deferred.makeUnsafe();
476
+ Deferred.doneUnsafe(previous, Exit.void);
477
+ });
478
+ const all = Object.values(lanes);
479
+ const interruptible = all.filter((family) => family !== lanes.jobs);
480
+ const inFlight = Effect.map(Effect.forEach(all, (family) => FiberSet.size(family.fibers)), (sizes) => sizes.reduce((sum, size) => sum + size, 0));
481
+ const pass = lock
482
+ .withPermit(Effect.uninterruptible(Effect.gen(function* () {
483
+ if (stopping)
484
+ return { claimed: 0, backlog: false, quiet: true };
485
+ const quiet = (yield* inFlight) === 0;
486
+ const slots = Math.min(yield* lanes.intents.free, settings.passLimit);
487
+ const local = executors();
488
+ const uncapped = local.filter(({ registered }) => registered.perActor === undefined);
489
+ const capped = local.filter(({ registered }) => registered.perActor !== undefined);
490
+ const permits = yield* lanes.jobs.free;
491
+ const claimedAt = yield* Clock.currentTimeNanos;
492
+ const claimSignal = cancelChecks;
493
+ const clock = yield* FrameworkClock;
494
+ const workSlots = subscriptions === undefined
495
+ ? undefined
496
+ : {
497
+ feed: yield* lanes.feed.free,
498
+ control: yield* lanes.control.free,
499
+ subscription: yield* lanes.subscription.free,
500
+ };
501
+ const now = outboxNow({ sql, offsetMillis: clock.offsetMillis() });
502
+ const ordered = [...shards.slice(firstShard), ...shards.slice(0, firstShard)];
503
+ firstShard = shards.length === 0 ? 0 : (firstShard + 1) % shards.length;
504
+ const claimGroups = (limit) => Effect.gen(function* () {
505
+ const claimed = [];
506
+ let backlog = false;
507
+ for (const { sql, range } of ordered) {
508
+ const remaining = limit - claimed.length;
509
+ if (remaining <= 0) {
510
+ backlog = true;
511
+ break;
512
+ }
513
+ const groups = yield* cappedGroups({
514
+ sql,
515
+ range,
516
+ now,
517
+ executors: capped,
518
+ limit: remaining,
519
+ });
520
+ backlog ||= groups.length === remaining || (groups[0]?.due_rows ?? 0) >= remaining;
521
+ for (const group of groups) {
522
+ const left = limit - claimed.length;
523
+ if (left <= 0)
524
+ break;
525
+ const { registered } = capped.find(({ actor, job }) => actor === group.actor_type && job === group.command);
526
+ claimed.push(...(yield* claimCapped({
527
+ sql,
528
+ now,
529
+ group,
530
+ cap: registered.perActor,
531
+ maxAttempts: registered.attempts,
532
+ permits: left,
533
+ leaseMs: settings.executorLeaseMs,
534
+ })));
535
+ }
536
+ }
537
+ return { claimed, backlog };
538
+ });
539
+ const cappedFirst = capped.length > 0 && uncapped.length > 0 && cappedTurn;
540
+ if (capped.length > 0 && uncapped.length > 0)
541
+ cappedTurn = !cappedTurn;
542
+ const early = cappedFirst && permits > 0
543
+ ? yield* claimGroups(permits)
544
+ : { claimed: [], backlog: false };
545
+ const uncappedPermits = permits - early.claimed.length;
546
+ const rows = [];
547
+ const work = [];
548
+ let intentSlots = slots;
549
+ let jobSlots = uncappedPermits;
550
+ const remainingWork = workSlots === undefined ? undefined : { ...workSlots };
551
+ const found = { intent: 0, job: 0 };
552
+ for (const { sql, range } of ordered) {
553
+ const claimed = yield* claimDue({
554
+ sql,
555
+ range,
556
+ now,
557
+ intents: intentSlots > 0
558
+ ? {
559
+ limit: intentSlots,
560
+ leaseMs: settings.claimLeaseMs(),
561
+ maxBackoffMs: settings.maxBackoffMs,
562
+ probe: lanes.intents.probe(intentSlots),
563
+ cronActors: [...schedules().keys()],
564
+ }
565
+ : undefined,
566
+ jobs: jobSlots > 0 && uncapped.length > 0
567
+ ? {
568
+ permits: jobSlots,
569
+ leaseMs: settings.executorLeaseMs,
570
+ executors: uncapped,
571
+ probe: lanes.jobs.probe(jobSlots),
572
+ }
573
+ : undefined,
574
+ subscriptions: remainingWork === undefined
575
+ ? undefined
576
+ : subscriptions.claim(remainingWork, range),
577
+ });
578
+ for (const kind of ["intent", "job"]) {
579
+ found[kind] +=
580
+ claimed.find((row) => row.kind === kind || row.kind === `skipped-${kind}`)
581
+ ?.candidates ?? 0;
582
+ }
583
+ intentSlots -= claimed.filter((row) => row.kind === "intent").length;
584
+ jobSlots -= claimed.filter((row) => row.kind === "job").length;
585
+ rows.push(...claimed);
586
+ if (subscriptions !== undefined && remainingWork !== undefined) {
587
+ for (const row of claimed.filter((row) => row.kind === "work")) {
588
+ const item = yield* subscriptions.decode(row.work);
589
+ remainingWork[item.kind]--;
590
+ work.push(item);
591
+ }
592
+ }
593
+ }
594
+ const intents = rows.filter((row) => row.kind === "intent");
595
+ const uncappedRows = rows.filter((row) => row.kind === "job");
596
+ const late = !cappedFirst && capped.length > 0 && uncappedPermits - uncappedRows.length > 0
597
+ ? yield* claimGroups(uncappedPermits - uncappedRows.length)
598
+ : { claimed: [], backlog: false };
599
+ const jobs = [...early.claimed, ...uncappedRows, ...late.claimed];
600
+ if (slots > 0)
601
+ lanes.intents.claimed(slots, intents.length, found.intent);
602
+ if (permits > 0 && local.length > 0) {
603
+ lanes.jobs.claimed(uncappedPermits, uncappedRows.length, found.job);
604
+ if (early.backlog || late.backlog)
605
+ lanes.jobs.backlog(true);
606
+ }
607
+ let claimedWork = 0;
608
+ if (subscriptions !== undefined && workSlots !== undefined) {
609
+ claimedWork = work.length;
610
+ for (const kind of ["feed", "control", "subscription"]) {
611
+ const taken = work.filter((item) => item.kind === kind).length;
612
+ lanes[kind].backlog(workSlots[kind] > 0 && taken >= workSlots[kind]);
613
+ }
614
+ for (const item of work)
615
+ yield* startWork(item);
616
+ }
617
+ for (const row of intents)
618
+ yield* lanes.intents.start(deliverIntent(row).pipe(logFailure("Outbox relay crashed settling a row")));
619
+ for (const row of jobs) {
620
+ const registered = local.find(({ actor, job }) => actor === row.actor_type && job === row.command).registered;
621
+ const lease = { until: Number(row.claimed_until) };
622
+ running.set(row.intent_id, {
623
+ routingKey: BigInt(row.routing_key),
624
+ attempt: row.attempts,
625
+ lease,
626
+ });
627
+ yield* lanes.jobs.start(attempt(row, registered, {
628
+ at: claimedAt,
629
+ signal: claimSignal,
630
+ next: () => cancelChecks,
631
+ lease,
632
+ }).pipe(Effect.ensuring(Effect.sync(() => running.delete(row.intent_id))), logFailure("Job attempt crashed before it settled")));
633
+ }
634
+ return {
635
+ claimed: intents.length + jobs.length + claimedWork,
636
+ backlog: all.some((family) => family.more),
637
+ quiet,
638
+ };
639
+ })))
640
+ .pipe(Effect.provideContext(services));
641
+ const run = Effect.gen(function* () {
642
+ while (true) {
643
+ const jitter = 0.9 + 0.2 * (yield* Random.next);
644
+ yield* Queue.take(signals).pipe(Effect.timeoutOption(settings.pollMs * jitter));
645
+ yield* pass.pipe(logFailure("Outbox relay pass failed"));
646
+ }
647
+ });
648
+ const idle = Effect.forEach(all, (family) => FiberSet.awaitEmpty(family.fibers), {
649
+ discard: true,
650
+ });
651
+ const drain = Effect.gen(function* () {
652
+ for (let rounds = 0; rounds < DRAIN_ROUNDS;) {
653
+ yield* idle;
654
+ const { claimed, backlog, quiet } = yield* pass;
655
+ if (claimed === 0 && quiet)
656
+ return;
657
+ if (claimed > 0 && !backlog)
658
+ rounds++;
659
+ }
660
+ return yield* Effect.die(new Error("Outbox did not settle; intents keep producing due work"));
661
+ }).pipe(Effect.orDie);
662
+ const halt = lock.withPermit(Effect.sync(() => {
663
+ stopping = true;
664
+ }));
665
+ yield* Effect.addFinalizer(() => halt);
666
+ const extendLeases = (millis, jump) => lock
667
+ .withPermit(Effect.forEach([...running], ([intentId, { routingKey, attempt: current, lease }]) => sql `UPDATE actor_outbox SET due_at_ms = due_at_ms + ${millis}
668
+ WHERE routing_key = ${routingKey} AND intent_id = ${intentId}
669
+ AND kind = 'job' AND attempts = ${current}`.pipe(Effect.tap(Effect.sync(() => {
670
+ lease.until += millis;
671
+ }))), { discard: true }).pipe(Effect.andThen(jump)))
672
+ .pipe(Effect.orDie);
673
+ const stop = Effect.andThen(halt, Effect.forEach(interruptible, (family) => FiberSet.clear(family.fibers), { discard: true }));
674
+ const interruptAttempts = Effect.flatMap(FiberSet.size(lanes.jobs.fibers), (count) => FiberSet.clear(lanes.jobs.fibers).pipe(Effect.as(count)));
675
+ return {
676
+ run,
677
+ drain,
678
+ stop,
679
+ attemptsIdle: FiberSet.awaitEmpty(lanes.jobs.fibers),
680
+ interruptAttempts,
681
+ extendLeases,
682
+ wake: wakeRelay,
683
+ cancelled,
684
+ };
685
+ });