@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,972 @@
1
+ import { Cause, Clock, Crypto, Effect, Exit, Option, Predicate, Result, Schema, Scope, } from "effect";
2
+ import { SqlClient, SqlError } from "effect/sql";
3
+ import { ActorError, CommandExpired, NotCreated, Unauthorized } from "../../errors/actor.js";
4
+ import {} from "../members.js";
5
+ import { Outcome } from "../request.js";
6
+ import { callerKey, System } from "../../identity/caller.js";
7
+ import { commandTimes } from "../../identity/command.js";
8
+ import { isMintedId, provesMint } from "../../identity/mint.js";
9
+ import { parseChildId } from "../../identity/child.js";
10
+ import { writeTicks } from "../cron/schedule.js";
11
+ import { eventsStatement, notifyEvents } from "../events/append.js";
12
+ import { COMMIT_VERSION } from "../database/replica.js";
13
+ import { isPoolRefusal } from "../database/bounded.js";
14
+ import { NekiTurnSessions } from "../database/neki/session.js";
15
+ import { compress, decompress } from "../storage/codec.js";
16
+ import { Metrics, record } from "../telemetry/metrics.js";
17
+ import { SpanNames } from "../telemetry/spans.js";
18
+ import { receiptMarginMs } from "../storage/retention.js";
19
+ import { hashedPayload } from "../subscriptions/identity.js";
20
+ import { tenantSettings, TenantScope } from "../database/tenancy.js";
21
+ import { actorRow as rowOf, forget } from "../storage/generation.js";
22
+ import { checkIdentity, databaseTime, FrameworkClock } from "./admission.js";
23
+ import { abortedBefore, errorOf, Shared, TurnGroups, Unseated } from "./group.js";
24
+ import { RetryTurn, TurnHooks } from "./hooks.js";
25
+ import { OutboxRuntime, outboxStatements } from "./outbox.js";
26
+ import { asSqlConnection, awaitReplies, queueStatements, sendPipelined, sendSequentially, TurnConnections, } from "./pipeline.js";
27
+ import { MERGE_CAP, merges } from "../entity/mailbox.js";
28
+ import { checkReceipt, encodeOutcome, hashCanonical } from "./receipt.js";
29
+ const isSystem = Schema.is(System);
30
+ /**
31
+ * The cursor row once `delivery` applied, as its commit writes it: a routed
32
+ * delivery creates the epoch-0 row or raises its position, a dynamic one moves
33
+ * the row of its own epoch, and a rejection deactivates that row.
34
+ */
35
+ const applied = (delivery, cursor) => {
36
+ const epoch = cursor.sub_epoch ?? null;
37
+ if (delivery.kind === "rejected")
38
+ return epoch === delivery.epoch ? { ...cursor, sub_active: false } : cursor;
39
+ if (delivery.epoch === "0") {
40
+ if (epoch === null)
41
+ return { sub_epoch: "0", sub_active: true, sub_applied: delivery.position };
42
+ if (epoch !== "0")
43
+ return cursor;
44
+ const position = BigInt(cursor.sub_applied) >= BigInt(delivery.position)
45
+ ? cursor.sub_applied
46
+ : delivery.position;
47
+ return { ...cursor, sub_applied: position };
48
+ }
49
+ return epoch === delivery.epoch ? { ...cursor, sub_applied: delivery.position } : cursor;
50
+ };
51
+ /**
52
+ * How the subscriber's cursor row settles a subscription delivery before its
53
+ * handler runs: undefined runs the handler. A row of a newer epoch makes the
54
+ * delivery stale, an inactive row at its epoch means it was unsubscribed, and
55
+ * a position at or below `applied` was already applied. A dynamic
56
+ * subscription always has a row, so only a routed one (epoch 0) creates it.
57
+ */
58
+ const acknowledgement = (delivery, admission) => {
59
+ const epoch = BigInt(delivery.epoch);
60
+ if (admission.sub_epoch == null)
61
+ return epoch > 0n ? "Stale" : undefined;
62
+ const stored = BigInt(admission.sub_epoch);
63
+ if (stored !== epoch)
64
+ return "Stale";
65
+ if (admission.sub_active !== true)
66
+ return "Unsubscribed";
67
+ if (delivery.kind === "rejected")
68
+ return undefined;
69
+ return BigInt(admission.sub_applied) >= BigInt(delivery.position) ? "AlreadyApplied" : undefined;
70
+ };
71
+ /** The first cron ticks a newly created actor stages, read against the database clock. */
72
+ const cronTicks = Effect.fnUntraced(function* (routingKey, ref, cron) {
73
+ const now = yield* databaseTime;
74
+ const services = yield* Effect.context();
75
+ return [writeTicks(routingKey, ref, cron, now).pipe(Effect.provideContext(services))];
76
+ });
77
+ /**
78
+ * True when `request` is a minted actor's creating intent: its caller carries
79
+ * the parent's mint proof for the actor's id, and its delivery carries the
80
+ * sender of the committed outbox row the relay claimed. The trusted relay
81
+ * copies the request from that row, and external admission refuses this
82
+ * provenance. The child's transaction needs no read on the parent's shard.
83
+ */
84
+ const committedMintIntent = Effect.fnUntraced(function* (request, parent) {
85
+ const { caller, ref, intent } = request;
86
+ if (request.external === true ||
87
+ intent === undefined ||
88
+ !isSystem(caller) ||
89
+ caller.ref === undefined ||
90
+ intent.tenant !== ref.tenant ||
91
+ intent.tenant !== caller.ref.tenant ||
92
+ intent.actor !== caller.ref.actor ||
93
+ intent.id !== caller.ref.id)
94
+ return false;
95
+ return yield* provesMint(caller, ref, parent);
96
+ });
97
+ const nothingWritten = { receipts: 0, events: 0, intents: 0, jobs: 0 };
98
+ const nothingWrote = { state: false, events: [], tables: [], blobs: [] };
99
+ class RolledBack {
100
+ plan;
101
+ constructor(plan) {
102
+ this.plan = plan;
103
+ }
104
+ }
105
+ const HANDLER_SAVEPOINT = "durable_handler";
106
+ /**
107
+ * Commands already waiting for one actor, run in delivery order in one
108
+ * framework transaction sent as two groups. The admission group opens the
109
+ * transaction, takes the generation fence, resolves every command's receipt
110
+ * under it, and on a cold activation acquires the next generation and reads
111
+ * state. The handlers run in memory once those replies arrive, each on the
112
+ * state the previous one left. The commit group writes the dirty state, each
113
+ * command's events and outbox rows, the creation marker, and every receipt,
114
+ * then commits. A lone command is a batch of one.
115
+ *
116
+ * Each command keeps its own outcome: a declared failure discards only its
117
+ * own staged work, and an admission error (expiry, `NotCreated`, a receipt
118
+ * conflict) answers only that command. A stale fence or any defect fails the
119
+ * whole batch and writes nothing. A batch with nothing to commit rolls back.
120
+ * Every command id in a batch must be distinct.
121
+ *
122
+ * `statements` marks an actor whose handler can issue SQL; only its handlers
123
+ * run under a savepoint, so a declared failure discards the handler's rows.
124
+ * The first handler's savepoint goes out with admission and each later one's
125
+ * with that handler's first statement. A handler that issued no statement has
126
+ * nothing to roll back, so its unsent savepoint is dropped. Rolling back to a
127
+ * savepoint keeps it, so each failed handler leaves one open until commit; a
128
+ * batch never holds more savepoints than its command cap. `parent` is a
129
+ * parent-placed actor's parent type, whose turns alone mint it, and `waited`
130
+ * holds the event tags workflows wait on.
131
+ *
132
+ * Admission statements take no parameter from another's reply, so they share
133
+ * one flight. The generation insert precedes the fenced read, so a brand-new
134
+ * actor's receipts resolve under the generation row lock. The timeout
135
+ * `set_config` runs before that insert, so `lock_timeout` bounds the insert's
136
+ * row waits but not the table lock taken as the statement starts;
137
+ * `statement_timeout` applies from the next statement. With row-level
138
+ * security the same statement takes the tenant role, which binds every later
139
+ * statement of the turn, the handler's included, to the actor's tenant at no
140
+ * extra round trip. A subscription delivery's cursor row is read in the
141
+ * fenced statement, and the delivery's canonical payload, not the event's
142
+ * bytes, binds its identity. When another runner advanced the generation
143
+ * since this activation acquired it, its cached state may be stale, so
144
+ * nothing runs, the activation drops its cache, and the retry reloads under a
145
+ * new generation.
146
+ *
147
+ * Admission checks. The generation row is locked in a materialized input
148
+ * before the read evaluates its clock, so time spent waiting for the fence
149
+ * cannot admit an identity that expired meanwhile. External ids are validated
150
+ * against the fenced read's clock before their receipt is released or a handler
151
+ * runs. An owner reports
152
+ * that admission on a retryable failure, so only a previously admitted
153
+ * redelivery can run past expiry, and not once retention could have pruned
154
+ * its receipt. No pre-delivery receipt read is needed. Only the relay's subscription
155
+ * deliveries reach a handler, and only a handler takes one, so no caller
156
+ * reaches it around the cursor or route. A routed event for a subscriber its
157
+ * creating command has not created is skipped, and its cursor keeps a stale
158
+ * redelivery from running after another command creates the subscriber. A
159
+ * minted actor is created only by the relay delivering the creating intent
160
+ * its parent's turn staged and committed: the mint proof binds the id to the
161
+ * parent's command, and the relay's claimed-row provenance proves that
162
+ * command committed the intent without reading another actor's shard.
163
+ *
164
+ * Handlers and commit. Calls of a commutative reducer already waiting right
165
+ * behind a command merge into its turn: their inputs are combined and reduced
166
+ * once. A complete result lists every key it keeps, and any other key it was
167
+ * given is deleted. A delivery's cursor position is applied with its receipt,
168
+ * declared failures included. Re-arming waiting workflows reads their steps,
169
+ * so it runs before the commit group and only an actor with a workflow
170
+ * waiting on an emitted class pays for it. A cold activation that replays or
171
+ * acknowledges still commits and keeps the generation it acquired, so work
172
+ * the replay wakes runs under it. The first batch a generation commits
173
+ * schedules every cron entry not yet ticking from the database clock after
174
+ * its handlers ran, so a first tick is never due before the batch that
175
+ * writes it. A batch's work is bounded like a lone turn's: interruption and
176
+ * the command timeout roll it back, and SQL failures are defects. A failed or
177
+ * unknown commit leaves nothing the cached state can be trusted for, so it
178
+ * clears the cache.
179
+ *
180
+ * On Postgres a run uses one leased session. Each admission group opens with
181
+ * `BEGIN`, and each commit group ends with `COMMIT`, whose command tag must
182
+ * be `COMMIT`, since Postgres answers `COMMIT` in an aborted transaction with
183
+ * `ROLLBACK`. While batch N commits, the next batch already waiting sends its
184
+ * `BEGIN` and admission group in the same flight, right behind N's `COMMIT`:
185
+ * the server runs them strictly after it, as a new transaction on the same
186
+ * session. That admission is built as if N commits, which its fence proves. A
187
+ * batch that leaves the cache cold is committed alone, so the next one
188
+ * prepares, which may acquire the generation itself, before its admission
189
+ * locks the generation row. So is a batch whose publication may wait for that
190
+ * lock in another transaction: an admission already behind its `COMMIT` would
191
+ * hold the lock until the publication it waits on ends, and neither would
192
+ * ever finish. N's callers are answered once its `COMMIT` reply
193
+ * arrives, and the next batch's handlers run only once its own fence and
194
+ * receipt replies arrive. If N's commit fails, the next batch's transaction
195
+ * is rolled back unseen with it. The commit version is read on the same
196
+ * session after the transaction ends, in the same flight, so it covers the
197
+ * batch's commit record and any receipt it replayed. The same statement reads
198
+ * a fresh clock after the transaction ended for the external expiry recheck,
199
+ * never reusing admission time.
200
+ *
201
+ * The session is unsafe from the moment a `BEGIN` may be queued until a
202
+ * transaction-ending reply with nothing queued behind it. Any other exit
203
+ * rolls back, and a session whose transaction state is unknown never goes
204
+ * back to the pool: an interrupted batch cancels its backend's statement and
205
+ * discards the session, so an unsent `COMMIT` rolls back with it and one
206
+ * already sent resolves through the receipt on retry. Deferred statements go
207
+ * out in the same flight as the next statement a handler sends, or at the
208
+ * head of the commit group.
209
+ */
210
+ export const executeBatches = Effect.fnUntraced(function* (run, cache, routingKey, policy, mintable, parent, statements, waited = new Set(), connections, cron = [], accounting) {
211
+ const sql = yield* SqlClient.SqlClient;
212
+ const hooks = yield* TurnHooks;
213
+ const clock = yield* FrameworkClock;
214
+ const scope = yield* TenantScope;
215
+ const { ref } = run.first[0].request;
216
+ const { tenant, actor, id } = ref;
217
+ const role = scope.role ?? (scope.adoption?.enforced.has(actor) === true ? scope.adoption.role : undefined);
218
+ const actorRow = rowOf({ sql, actor: { key: routingKey, ref } });
219
+ const canonicalsOf = (batch) => Effect.forEach(batch, ({ request }) => hashedPayload(request));
220
+ const cursorOf = (delivery) => sql `${actorRow} AND subscription = ${delivery.subscription}
221
+ AND source_type = ${delivery.sourceType} AND source_id = ${delivery.sourceId}`;
222
+ const applyCursor = (delivery) => Effect.asVoid(sql `INSERT INTO actor_subscription_cursors (routing_key, tenant_id, actor_type, actor_id,
223
+ subscription, source_type, source_id, epoch, active, applied)
224
+ VALUES (${routingKey}, ${tenant}, ${actor}, ${id}, ${delivery.subscription},
225
+ ${delivery.sourceType}, ${delivery.sourceId}, 0, true, ${delivery.position})
226
+ ON CONFLICT (routing_key, tenant_id, actor_type, actor_id, subscription, source_type, source_id)
227
+ DO UPDATE SET applied = greatest(actor_subscription_cursors.applied, EXCLUDED.applied)
228
+ WHERE actor_subscription_cursors.epoch = 0`);
229
+ const timeouts = sql `set_config('lock_timeout', ${`${policy.lockWaitMs}ms`}, true),
230
+ set_config('statement_timeout', ${`${policy.executionMs}ms`}, true),
231
+ set_config('durable.turn', 'on', true)
232
+ ${role === undefined ? sql.literal("") : sql `, ${tenantSettings({ sql, role, tenant })}`}`;
233
+ /**
234
+ * Turns that set the same transaction settings, which a group sets once for
235
+ * all of them. Each part is encoded so no two settings share a key.
236
+ */
237
+ const groupKey = [
238
+ policy.lockWaitMs,
239
+ policy.executionMs,
240
+ role === undefined ? "-" : `+${encodeURIComponent(role)}`,
241
+ role === undefined ? "-" : `+${encodeURIComponent(tenant)}`,
242
+ ].join(":");
243
+ const admit = (batch, canonicals, view, session, begin, grouped = false) => {
244
+ const cold = view.generation === undefined;
245
+ const readsState = cold || view.state === undefined;
246
+ let admissions = [];
247
+ let bumped;
248
+ let stored = [];
249
+ const subscribed = batch.some(({ request }) => request.delivery !== undefined);
250
+ const commands = sql.csv(batch.map(({ request: { commandId, delivery } }, index) => subscribed
251
+ ? sql `(${index}::integer, ${commandId}::text, ${canonicals[index]}::text,
252
+ ${delivery?.subscription ?? null}::text, ${delivery?.sourceType ?? null}::text,
253
+ ${delivery?.sourceId ?? null}::text)`
254
+ : sql `(${index}::integer, ${commandId}::text, ${canonicals[index]}::text)`));
255
+ const cursorColumns = subscribed
256
+ ? sql `, s.epoch::text AS sub_epoch, s.active AS sub_active, s.applied::text AS sub_applied`
257
+ : sql.literal("");
258
+ const values = subscribed
259
+ ? sql `(VALUES ${commands}) AS c (ordinal, command_id, payload, subscription, source_type, source_id)`
260
+ : sql `(VALUES ${commands}) AS c (ordinal, command_id, payload)`;
261
+ const cursorJoin = subscribed
262
+ ? sql `LEFT JOIN actor_subscription_cursors s ON s.routing_key = g.routing_key
263
+ AND s.tenant_id = g.tenant_id AND s.actor_type = g.actor_type AND s.actor_id = g.actor_id
264
+ AND s.subscription = c.subscription AND s.source_type = c.source_type
265
+ AND s.source_id = c.source_id`
266
+ : sql.literal("");
267
+ const group = [
268
+ ...begin,
269
+ ...(grouped
270
+ ? []
271
+ : [
272
+ cold
273
+ ? Effect.asVoid(sql `INSERT INTO actor_generations (routing_key, tenant_id, actor_type, actor_id)
274
+ SELECT ${routingKey}, ${tenant}, ${actor}, ${id}
275
+ FROM (SELECT ${timeouts}) AS timeouts
276
+ ON CONFLICT DO NOTHING`)
277
+ : Effect.asVoid(sql `SELECT ${timeouts}`),
278
+ ]),
279
+ Effect.map(sql `
280
+ WITH locked AS MATERIALIZED (
281
+ SELECT g.routing_key, g.tenant_id, g.actor_type, g.actor_id,
282
+ g.generation, g.created, g.event_sequence
283
+ FROM actor_generations g
284
+ WHERE ${rowOf({ sql, actor: { key: routingKey, ref }, alias: "g" })}
285
+ FOR UPDATE OF g ${grouped ? sql.literal("SKIP LOCKED") : sql.literal("")}
286
+ )
287
+ SELECT floor(extract(epoch FROM clock_timestamp()) * 1000)::text AS now,
288
+ g.generation::text AS generation, g.created,
289
+ c.payload::jsonb::text AS canonical,
290
+ r.caller_key, r.command, r.payload_hash, r.outcome, g.event_sequence::text AS head
291
+ ${cursorColumns}
292
+ FROM locked g
293
+ CROSS JOIN ${values}
294
+ LEFT JOIN actor_receipts r ON r.routing_key = g.routing_key AND r.tenant_id = g.tenant_id
295
+ AND r.actor_type = g.actor_type AND r.actor_id = g.actor_id AND r.command_id = c.command_id
296
+ ${cursorJoin}
297
+ ORDER BY c.ordinal`, (rows) => {
298
+ admissions = rows;
299
+ }),
300
+ ...(cold
301
+ ? [
302
+ Effect.map(sql `
303
+ UPDATE actor_generations SET generation = generation + 1 WHERE ${actorRow}
304
+ RETURNING generation::text AS generation`, (rows) => {
305
+ bumped = rows[0]?.generation;
306
+ }),
307
+ ]
308
+ : []),
309
+ ...(readsState
310
+ ? [
311
+ Effect.map(sql `
312
+ SELECT key, value FROM actor_state WHERE ${actorRow}`, (rows) => {
313
+ stored = rows;
314
+ }),
315
+ ]
316
+ : []),
317
+ ...(statements ? [session.control(`SAVEPOINT ${HANDLER_SAVEPOINT}`)] : []),
318
+ ];
319
+ const resume = Effect.fnUntraced(function* () {
320
+ const first = admissions[0];
321
+ if (first === undefined && grouped)
322
+ return yield* Effect.die(new Unseated());
323
+ if (first === undefined || (!cold && view.generation !== first.generation)) {
324
+ forget(cache);
325
+ return yield* Effect.die(RetryTurn.make({ message: "Stale actor generation" }));
326
+ }
327
+ const current = cold ? bumped : first.generation;
328
+ const now = Number(first.now) + clock.offsetMillis();
329
+ const { retryWindowMs } = yield* OutboxRuntime;
330
+ const expiryMarginMs = receiptMarginMs({
331
+ keepReceiptsMs: policy.keepReceiptsMs,
332
+ deliveryMs: policy.deliveryMs,
333
+ retryWindowMs,
334
+ });
335
+ const ticks = !cold || cron.length === 0 ? Effect.succeed([]) : cronTicks(routingKey, ref, cron);
336
+ const settled = Array.from({ length: batch.length });
337
+ let next;
338
+ const dirty = new Map();
339
+ const removed = new Set();
340
+ const staged = [];
341
+ const receipts = [];
342
+ let created = first.created;
343
+ let creates = false;
344
+ let replayed = false;
345
+ let wake = false;
346
+ const broadcasts = [];
347
+ let events = 0;
348
+ let intents = 0;
349
+ let jobs = 0;
350
+ const eventTags = new Set();
351
+ const tables = new Set();
352
+ const blobs = new Set();
353
+ const replays = new Set();
354
+ const committed = [];
355
+ const emitted = [];
356
+ const cursors = new Map();
357
+ const cursorKey = (delivery) => JSON.stringify([delivery.subscription, delivery.sourceType, delivery.sourceId]);
358
+ const cursorAt = (index, delivery) => cursors.get(cursorKey(delivery)) ?? admissions[index];
359
+ const apply = (index, delivery) => cursors.set(cursorKey(delivery), applied(delivery, cursorAt(index, delivery)));
360
+ const outboxes = [];
361
+ /**
362
+ * Admits one command of the batch once its payload hash is known: settles
363
+ * a replay, an expiry, or a refusal and yields nothing, or yields the
364
+ * hash its receipt records. A plain function rather than a generator,
365
+ * because a generator built for every turn is compiled again and again as
366
+ * the engine sees new copies of it.
367
+ */
368
+ const admitHashed = (index, hash) => {
369
+ const { request, command } = batch[index];
370
+ const admitted = admissions[index];
371
+ if (admitted.outcome !== null)
372
+ return Effect.map(checkReceipt(request, hash, admitted).pipe(Effect.result), (replay) => {
373
+ if (Result.isSuccess(replay)) {
374
+ replayed = true;
375
+ replays.add(index);
376
+ }
377
+ settled[index] = replay;
378
+ return undefined;
379
+ });
380
+ if (request.external === true &&
381
+ Number(admitted.now) + (request.clockOffset ?? clock.offsetMillis()) >=
382
+ commandTimes(request.commandId).expiresAt + expiryMarginMs) {
383
+ settled[index] = Result.fail(ActorError.make({ reason: CommandExpired.make({ commandId: request.commandId }) }));
384
+ return Effect.undefined;
385
+ }
386
+ if (command.internal && !isSystem(request.caller))
387
+ return Effect.die(new Error("Internal commands require a System caller"));
388
+ const { delivery } = request;
389
+ const acknowledge = (reason) => {
390
+ replayed = true;
391
+ settled[index] = Result.succeed(Outcome.cases.Acknowledged.make({ reason }));
392
+ };
393
+ if (command.handler || delivery !== undefined) {
394
+ const caller = request.caller;
395
+ if (!command.handler ||
396
+ delivery === undefined ||
397
+ !isSystem(caller) ||
398
+ caller.source !== "subscription" ||
399
+ caller.ref?.actor !== delivery.sourceType ||
400
+ caller.ref.id !== delivery.sourceId)
401
+ return Effect.die(new Error("Subscription handlers accept only subscription deliveries"));
402
+ if (caller.ref.tenant !== tenant)
403
+ return Effect.die(new Error("A subscription delivery crosses tenants"));
404
+ const reason = acknowledgement(delivery, cursorAt(index, delivery));
405
+ if (reason !== undefined) {
406
+ acknowledge(reason);
407
+ return Effect.undefined;
408
+ }
409
+ }
410
+ if (policy.createdBy !== undefined && !created && policy.createdBy !== request.command) {
411
+ if (delivery !== undefined && delivery.epoch === "0" && delivery.kind === "event") {
412
+ staged.push(applyCursor(delivery));
413
+ apply(index, delivery);
414
+ acknowledge("NotCreated");
415
+ return Effect.undefined;
416
+ }
417
+ settled[index] = Result.fail(ActorError.make({ reason: NotCreated.make({}) }));
418
+ return Effect.undefined;
419
+ }
420
+ if (!mintable ||
421
+ policy.createdBy !== request.command ||
422
+ created ||
423
+ !isMintedId(parent === undefined ? id : (parseChildId(id)?.local ?? "")))
424
+ return Effect.succeed(hash);
425
+ const refuse = () => {
426
+ settled[index] = Result.fail(ActorError.make({ reason: Unauthorized.make({ code: "access_denied" }) }));
427
+ return undefined;
428
+ };
429
+ return request.external === true
430
+ ? Effect.sync(refuse)
431
+ : Effect.map(committedMintIntent(request, parent), (committed) => committed ? hash : refuse());
432
+ };
433
+ const admitOne = (index) => {
434
+ const request = batch[index].request;
435
+ const identityNow = Number(admissions[index].now) + (request.clockOffset ?? clock.offsetMillis());
436
+ const hashed = Effect.flatMap(hashCanonical(admissions[index].canonical), (hash) => admitHashed(index, hash));
437
+ if (request.external !== true)
438
+ return hashed;
439
+ return Effect.flatMap(checkIdentity(request.commandId, retryWindowMs, identityNow).pipe(Effect.result), (identity) => {
440
+ if (Result.isFailure(identity) &&
441
+ (request.redelivered !== true || !Schema.is(CommandExpired)(identity.failure.reason))) {
442
+ settled[index] = Result.fail(identity.failure);
443
+ return Effect.undefined;
444
+ }
445
+ batch[index].admitted = true;
446
+ return hashed;
447
+ });
448
+ };
449
+ let index = 0;
450
+ while (index < batch.length) {
451
+ const { request, command } = batch[index];
452
+ const hash = yield* admitOne(index);
453
+ if (hash === undefined) {
454
+ index += 1;
455
+ continue;
456
+ }
457
+ const start = index;
458
+ const members = [{ index, request, hash }];
459
+ index += 1;
460
+ while (index < batch.length &&
461
+ members.length < MERGE_CAP &&
462
+ merges({ previous: batch[start], next: batch[index] })) {
463
+ const joined = yield* admitOne(index);
464
+ if (joined !== undefined)
465
+ members.push({ index, request: batch[index].request, hash: joined });
466
+ index += 1;
467
+ }
468
+ next ??= readsState
469
+ ? new Map(stored.map(({ key, value }) => [key, decompress(value)]))
470
+ : new Map(view.state);
471
+ const given = next;
472
+ const head = String(BigInt(first.head) + BigInt(events));
473
+ const savepoint = `SAVEPOINT ${HANDLER_SAVEPOINT}`;
474
+ if (statements && start > 0)
475
+ yield* session.defer(savepoint);
476
+ const business = yield* hooks.at("beforeHandler", request).pipe(Effect.andThen(() => members.length === 1
477
+ ? command.run(request, [...given], { head, connections })
478
+ : command.merge(members.map((member) => member.request), [...given])), Effect.catchIf(SqlError.isSqlError, Effect.die), Effect.result);
479
+ const result = Result.isSuccess(business)
480
+ ? business.success
481
+ : business.failure;
482
+ if (statements && (start === 0 || !session.withdraw(savepoint)))
483
+ yield* session.defer(Result.isSuccess(business)
484
+ ? `RELEASE SAVEPOINT ${HANDLER_SAVEPOINT}`
485
+ : `ROLLBACK TO SAVEPOINT ${HANDLER_SAVEPOINT}`);
486
+ const written = new Map(result.state);
487
+ if (result.complete)
488
+ for (const key of given.keys())
489
+ if (!written.has(key)) {
490
+ given.delete(key);
491
+ dirty.delete(key);
492
+ removed.add(key);
493
+ }
494
+ for (const [key, value] of written) {
495
+ given.set(key, value);
496
+ dirty.set(key, value);
497
+ removed.delete(key);
498
+ }
499
+ if (result.events.length > 0) {
500
+ const appended = yield* eventsStatement(request, routingKey, result.events);
501
+ staged.push(appended.statement);
502
+ committed.push({
503
+ after: head,
504
+ events: result.events,
505
+ commandId: request.commandId,
506
+ });
507
+ emitted.push(appended.stamp);
508
+ }
509
+ events += result.events.length;
510
+ for (const event of result.events)
511
+ eventTags.add(event.tag);
512
+ for (const table of result.writes?.tables ?? [])
513
+ tables.add(table);
514
+ for (const blob of result.writes?.blobs ?? [])
515
+ blobs.add(blob);
516
+ intents += result.outbox.intents.length;
517
+ jobs += result.outbox.jobs.length;
518
+ if (Outcome.guards.Success(result.outcome))
519
+ broadcasts.push(...(result.broadcasts ?? []));
520
+ if (yield* notifyEvents(request, routingKey, result.events, waited))
521
+ wake = true;
522
+ if (Outcome.guards.Success(result.outcome) &&
523
+ policy.createdBy === request.command &&
524
+ !created) {
525
+ created = true;
526
+ creates = true;
527
+ }
528
+ const outbox = yield* outboxStatements(routingKey, request.ref, result.outbox, Effect.succeed(now), { slackMs: policy.executionMs });
529
+ staged.push(...outbox.statements);
530
+ outboxes.push(outbox.replies);
531
+ const { delivery } = request;
532
+ if (delivery !== undefined) {
533
+ apply(start, delivery);
534
+ staged.push(delivery.kind === "rejected"
535
+ ? Effect.asVoid(sql `UPDATE actor_subscription_cursors SET active = false
536
+ WHERE ${cursorOf(delivery)} AND epoch = ${delivery.epoch}`)
537
+ : delivery.epoch === "0"
538
+ ? applyCursor(delivery)
539
+ : Effect.asVoid(sql `UPDATE actor_subscription_cursors SET applied = ${delivery.position}
540
+ WHERE ${cursorOf(delivery)} AND epoch = ${delivery.epoch}`));
541
+ }
542
+ const outcome = yield* encodeOutcome(result.outcome).pipe(Effect.orDie);
543
+ for (const member of members) {
544
+ receipts.push({
545
+ routing_key: routingKey,
546
+ tenant_id: tenant,
547
+ actor_type: actor,
548
+ actor_id: id,
549
+ command_id: member.request.commandId,
550
+ command: member.request.command,
551
+ payload_hash: member.hash,
552
+ caller_key: callerKey(member.request.caller),
553
+ outcome,
554
+ expires_at_ms: commandTimes(member.request.commandId).expiresAt,
555
+ });
556
+ yield* hooks.at("beforeCommit", member.request);
557
+ settled[member.index] = Result.succeed(result.outcome);
558
+ }
559
+ }
560
+ if (receipts.length === 0 && staged.length === 0 && !(cold && replayed))
561
+ return {
562
+ writes: undefined,
563
+ settled,
564
+ generation: current,
565
+ state: view.state,
566
+ wake: false,
567
+ broadcasts: [],
568
+ head: first.head,
569
+ committed: [],
570
+ emitted: [],
571
+ outbox: [],
572
+ replays,
573
+ written: nothingWritten,
574
+ wrote: nothingWrote,
575
+ };
576
+ const writes = [];
577
+ if (dirty.size > 0)
578
+ writes.push(Effect.asVoid(sql `INSERT INTO actor_state ${sql.insert([...dirty].map(([key, value]) => ({
579
+ routing_key: routingKey,
580
+ tenant_id: tenant,
581
+ actor_type: actor,
582
+ actor_id: id,
583
+ key,
584
+ value: compress(value),
585
+ })))}
586
+ ON CONFLICT (routing_key, tenant_id, actor_type, actor_id, key) DO UPDATE SET value = EXCLUDED.value`));
587
+ if (removed.size > 0)
588
+ writes.push(Effect.asVoid(sql `DELETE FROM actor_state WHERE ${actorRow} AND key IN ${sql.in([...removed])}`));
589
+ writes.push(...staged);
590
+ if (creates)
591
+ writes.push(Effect.asVoid(sql `UPDATE actor_generations SET created = true WHERE ${actorRow}`));
592
+ if (receipts.length > 0) {
593
+ writes.push(Effect.asVoid(sql `INSERT INTO actor_receipts ${sql.insert(receipts)}`));
594
+ if (accounting !== undefined)
595
+ writes.push(accounting.commands({
596
+ ref,
597
+ commandIds: receipts.map((receipt) => receipt.command_id),
598
+ sql,
599
+ }));
600
+ }
601
+ writes.push(...(yield* ticks));
602
+ return {
603
+ writes,
604
+ settled,
605
+ generation: current,
606
+ state: next,
607
+ wake,
608
+ broadcasts,
609
+ head: String(BigInt(first.head) + BigInt(events)),
610
+ committed,
611
+ emitted,
612
+ outbox: outboxes,
613
+ replays,
614
+ written: { receipts: receipts.length, events, intents, jobs },
615
+ wrote: {
616
+ state: dirty.size > 0 || removed.size > 0,
617
+ events: [...eventTags],
618
+ tables: [...tables],
619
+ blobs: [...blobs],
620
+ },
621
+ };
622
+ });
623
+ return { group, resume };
624
+ };
625
+ const bounded = (effect) => effect.pipe(Effect.interruptible, Effect.timeoutOrElse({
626
+ duration: policy.executionMs,
627
+ orElse: () => Effect.die(RetryTurn.make({ message: "Command execution timeout" })),
628
+ }), Effect.catchIf(SqlError.isSqlError, Effect.die));
629
+ const view = () => ({ generation: cache.generation, state: cache.state });
630
+ let current = run.first;
631
+ let orphan;
632
+ let answering = false;
633
+ let poolRefused = false;
634
+ const locate = (batch, following) => {
635
+ current = batch;
636
+ orphan = following;
637
+ };
638
+ /**
639
+ * Remembers what a commit proved and hands the ended batch to the
640
+ * activation, which publishes it. Only a commit replaces the cache.
641
+ */
642
+ const finish = (batch, plan, version, endedAtMs) => Effect.suspend(() => {
643
+ if (plan.writes !== undefined) {
644
+ cache.generation = plan.generation;
645
+ cache.state = plan.state;
646
+ }
647
+ answering = true;
648
+ return run.committed(batch, {
649
+ settled: plan.settled,
650
+ broadcasts: plan.broadcasts,
651
+ head: plan.head,
652
+ committed: plan.committed.map((entry, index) => ({
653
+ ...entry,
654
+ emittedAtMs: plan.emitted[index].emittedAtMs,
655
+ })),
656
+ cancelledJobs: plan.outbox.flatMap((replies) => replies.cancelledIds),
657
+ generation: plan.generation,
658
+ replays: plan.replays,
659
+ written: plan.writes === undefined ? nothingWritten : plan.written,
660
+ version,
661
+ endedAtMs,
662
+ wrote: plan.writes === undefined ? nothingWrote : plan.wrote,
663
+ wake: plan.wake ||
664
+ plan.outbox.some((replies) => replies.wake) ||
665
+ plan.emitted.some((stamp) => stamp.fed),
666
+ cancelled: plan.outbox.some((replies) => replies.cancelled),
667
+ });
668
+ }).pipe(Effect.andThen(Effect.sync(() => {
669
+ answering = false;
670
+ })));
671
+ /**
672
+ * The one batch lifecycle both backends share. Each batch is located, so a
673
+ * failure names it and any batch admitted behind it, and prepared before its
674
+ * handlers can run; one whose admission is not yet under way is opened;
675
+ * `transact` ends its transaction; and the ended batch is finished within its
676
+ * own span before the next is taken. The next batch is the one `transact`
677
+ * took while committing, whose admission may already ride behind this
678
+ * `COMMIT`, else whatever is waiting once this batch was answered.
679
+ * Opening stays lazy because preparation may acquire a generation and fill
680
+ * the cache whose view admission uses.
681
+ */
682
+ const drive = (open, transact) => {
683
+ const step = (admitting, pending) => {
684
+ locate(admitting, undefined);
685
+ return run.prepare.pipe(Effect.andThen(() => (pending === undefined ? open(admitting) : Effect.succeed(pending))), Effect.flatMap((admitted) => transact(admitting, admitted).pipe(Effect.tap((ended) => finish(admitting, ended.plan, ended.version, ended.endedAtMs)), run.observe(admitting))), Effect.flatMap((ended) => ended.following === undefined
686
+ ? Effect.flatMap(run.next, (next) => next === undefined ? Effect.void : step(next, ended.chained))
687
+ : step(ended.following, ended.chained)));
688
+ };
689
+ return Effect.suspend(() => step(run.first, undefined));
690
+ };
691
+ /**
692
+ * Postgres: a leased session per chain of batches. A batch is opened by
693
+ * queuing `BEGIN` and its admission group; `transact` waits for those
694
+ * replies, runs the handlers, takes the batch already waiting and, when this
695
+ * one leaves the cache warm, queues that batch's `BEGIN` and admission right
696
+ * behind this `COMMIT` in the same flight. The commit tag and version are read
697
+ * in that flight too.
698
+ *
699
+ * A batch that chains nothing ends with every reply in and no transaction
700
+ * open, so the session goes back to the pool before its callers are
701
+ * answered; publishing never holds a session another turn waits for. A batch
702
+ * that arrives later leases a session again.
703
+ */
704
+ const pipelined = (turns) => Effect.scoped(Effect.gen(function* () {
705
+ const scope = yield* Effect.scope;
706
+ let lease;
707
+ const leased = Effect.suspend(() => {
708
+ if (lease !== undefined)
709
+ return Effect.void;
710
+ return Effect.gen(function* () {
711
+ const held = yield* Scope.fork(scope);
712
+ const leasing = yield* Clock.currentTimeMillis;
713
+ const connection = yield* Scope.provide(turns.lease, held).pipe(Effect.tapError((error) => Effect.sync(() => {
714
+ poolRefused = isPoolRefusal(error);
715
+ })), Effect.onError((cause) => Scope.close(held, Exit.failCause(cause))));
716
+ yield* record(Metrics.poolWait, {}, (yield* Clock.currentTimeMillis) - leasing);
717
+ lease = { connection, scope: held };
718
+ });
719
+ });
720
+ const release = Effect.suspend(() => {
721
+ const held = lease;
722
+ lease = undefined;
723
+ return held === undefined ? Effect.void : Scope.close(held.scope, Exit.void);
724
+ });
725
+ const session = () => lease.connection;
726
+ let open = false;
727
+ const deferred = [];
728
+ const control = (text) => session().query(text, [], true);
729
+ const flush = () => deferred.splice(0).map((text) => Effect.asVoid(control(text)));
730
+ const send = (statement) => Effect.suspend(() => {
731
+ if (deferred.length === 0)
732
+ return statement;
733
+ let reply;
734
+ return sendPipelined([
735
+ ...flush(),
736
+ Effect.map(statement, (value) => {
737
+ reply = { value };
738
+ }),
739
+ ]).pipe(Effect.map(() => reply.value));
740
+ });
741
+ const pipeline = {
742
+ send: sendPipelined,
743
+ control: (text) => Effect.asVoid(control(text)),
744
+ defer: (text) => Effect.sync(() => {
745
+ deferred.push(text);
746
+ }),
747
+ withdraw: (text) => deferred.at(-1) === text && deferred.pop() !== undefined,
748
+ };
749
+ const begin = Effect.suspend(() => {
750
+ open = true;
751
+ return Effect.asVoid(control("BEGIN"));
752
+ });
753
+ const inTurn = (effect) => Effect.suspend(() => Effect.provideService(effect, sql.transactionService, [
754
+ asSqlConnection({ connection: session(), send }),
755
+ 0,
756
+ ]));
757
+ const queue = (batch, view, ahead) => Effect.flatMap(canonicalsOf(batch), (canonicals) => {
758
+ const admission = admit(batch, canonicals, view, pipeline, [begin]);
759
+ return Effect.map(queueStatements({ scope, group: [...ahead, ...admission.group] }), (flight) => ({ admission, flight }));
760
+ });
761
+ /**
762
+ * Queues a batch's commit group, and the next batch's admission behind
763
+ * it when the cache stays warm, then waits for the commit replies.
764
+ */
765
+ const commitPlan = (batch, plan, following) => {
766
+ const ending = plan.writes === undefined ? "ROLLBACK" : "COMMIT";
767
+ const after = plan.writes === undefined ? view() : { generation: plan.generation, state: plan.state };
768
+ const chained = following !== undefined &&
769
+ after.generation !== undefined &&
770
+ after.state !== undefined &&
771
+ !run.publishesUnderLock(batch);
772
+ let tag;
773
+ let version = "";
774
+ let endedAtMs = 0;
775
+ const commit = [
776
+ ...(plan.writes === undefined ? [] : [...flush(), ...plan.writes]),
777
+ Effect.map(control(ending), (result) => {
778
+ tag = result.command;
779
+ if (!chained)
780
+ open = false;
781
+ }),
782
+ Effect.map(session().query(COMMIT_VERSION, [], true), (result) => {
783
+ const ended = result.rows[0];
784
+ version = ended.version;
785
+ endedAtMs = Number(ended.now);
786
+ }),
787
+ ];
788
+ locate(batch, following);
789
+ const upcoming = chained
790
+ ? queue(following, after, commit)
791
+ : Effect.map(queueStatements({ scope, group: commit }), (flight) => ({
792
+ admission: undefined,
793
+ flight,
794
+ }));
795
+ return Effect.flatMap(upcoming, ({ admission: next, flight }) => {
796
+ const answered = awaitReplies(flight.slice(0, commit.length));
797
+ return Effect.map(ending === "COMMIT" ? answered.pipe(Effect.withSpan(SpanNames.commit)) : answered, () => ({
798
+ plan,
799
+ version,
800
+ endedAtMs,
801
+ ending,
802
+ tag,
803
+ following,
804
+ chained: next === undefined
805
+ ? undefined
806
+ : { admission: next, admitted: flight.slice(commit.length) },
807
+ }));
808
+ });
809
+ };
810
+ const alone = (batch, { admission, admitted }, taken) => awaitReplies(admitted).pipe(Effect.andThen(() => admission.resume()), Effect.flatMap((plan) => Effect.flatMap(taken === undefined ? run.next : Effect.succeed(taken), (following) => commitPlan(batch, plan, following))), inTurn, bounded, Effect.filterOrElse((stepped) => stepped.ending !== "COMMIT" || stepped.tag === "COMMIT", () => Effect.die(RetryTurn.make({ message: "Turn commit rolled back" }))), Effect.tap((stepped) => (stepped.chained === undefined ? release : Effect.void)));
811
+ const openAlone = (batch) => leased.pipe(Effect.andThen(queue(batch, view(), []).pipe(inTurn)), Effect.map(({ admission, flight }) => ({ admission, admitted: flight })));
812
+ const onMember = (member) => (effect) => Effect.suspend(() => Effect.provideService(effect, sql.transactionService, [
813
+ asSqlConnection({ connection: member.connection(), send: member.send }),
814
+ 0,
815
+ ]));
816
+ /**
817
+ * Joins the forming group for this batch's settings with its fenced
818
+ * admission read alone; the group opens the transaction.
819
+ */
820
+ const openGrouped = (batch, joining) => Effect.flatMap(canonicalsOf(batch), (canonicals) => {
821
+ const admission = admit(batch, canonicals, view(), pipeline, [], true);
822
+ return joining
823
+ .join({
824
+ key: groupKey,
825
+ settings: (connection) => Effect.provideService(Effect.asVoid(sql `SELECT ${timeouts}`), sql.transactionService, [asSqlConnection({ connection, send: (statement) => statement }), 0]),
826
+ cancel: (connection) => Effect.ignore(sql `SELECT pg_cancel_backend(${connection.processId})`),
827
+ admission: (member) => admission.group.map(onMember(member)),
828
+ })
829
+ .pipe(Effect.tapError((error) => Effect.sync(() => {
830
+ poolRefused = isPoolRefusal(error);
831
+ })), Effect.map((member) => ({ admission, admitted: [], member })));
832
+ });
833
+ /**
834
+ * A grouped batch: its own admission replies, its handlers, and its
835
+ * writes handed to the group, answered once the group commits. When
836
+ * the actor's next batch is already waiting and this one leaves the
837
+ * cache warm, that batch's admission rides behind the group's
838
+ * `COMMIT` and the batch takes the group's session, as a batch on its
839
+ * own session would. `ended` is undefined when the batch must run again
840
+ * alone: the group ended without it, aborted on a neighbour's
841
+ * statement, skipped its locked generation row, or the batch tried to
842
+ * send another statement.
843
+ */
844
+ const grouped = (batch, admission, member) => {
845
+ let following;
846
+ return Effect.gen(function* () {
847
+ const replies = yield* Effect.exit(awaitReplies(member.replies()));
848
+ if (Exit.isFailure(replies)) {
849
+ yield* member.leave(true);
850
+ const error = errorOf(replies);
851
+ if (error !== undefined && abortedBefore(error))
852
+ return undefined;
853
+ return yield* Effect.failCause(replies.cause);
854
+ }
855
+ if (!(yield* member.handling))
856
+ return undefined;
857
+ const plan = yield* onMember(member)(admission.resume());
858
+ following = yield* run.next;
859
+ locate(batch, following);
860
+ const after = plan.writes === undefined
861
+ ? view()
862
+ : { generation: plan.generation, state: plan.state };
863
+ let next;
864
+ const handoff = following !== undefined &&
865
+ after.generation !== undefined &&
866
+ after.state !== undefined &&
867
+ !run.publishesUnderLock(batch)
868
+ ? yield* Effect.map(canonicalsOf(following), (canonicals) => ({
869
+ chain: (connection) => {
870
+ const on = (effect) => Effect.provideService(effect, sql.transactionService, [
871
+ asSqlConnection({ connection, send }),
872
+ 0,
873
+ ]);
874
+ next = admit(following, canonicals, after, pipeline, [
875
+ Effect.asVoid(connection.query("BEGIN", [], true)),
876
+ ]);
877
+ return next.group.map(on);
878
+ },
879
+ adopt: (connection, held) => Effect.suspend(() => {
880
+ if (Predicate.isTagged(scope.state, "Closed"))
881
+ return Effect.succeed(false);
882
+ lease = { connection, scope: held };
883
+ open = true;
884
+ return Effect.as(Scope.addFinalizer(scope, Scope.close(held, Exit.void)), true);
885
+ }),
886
+ }))
887
+ : undefined;
888
+ const writes = (plan.writes ?? []).map(onMember(member));
889
+ const waiting = member.commit(writes, handoff);
890
+ const shared = yield* writes.length > 0
891
+ ? waiting.pipe(Effect.withSpan(SpanNames.commit))
892
+ : waiting;
893
+ if (Shared.$is("Alone")(shared))
894
+ return undefined;
895
+ return {
896
+ plan,
897
+ version: shared.version,
898
+ endedAtMs: shared.endedAtMs,
899
+ following,
900
+ chained: shared.chained === undefined || next === undefined
901
+ ? undefined
902
+ : { admission: next, admitted: [...shared.chained] },
903
+ };
904
+ }).pipe(Effect.catchDefect((defect) => defect instanceof Unseated ? Effect.undefined : Effect.die(defect)), Effect.onExit(() => member.leave(false)), bounded, Effect.map((ended) => ({ ended, following })));
905
+ };
906
+ const transact = (batch, admitting) => admitting.member === undefined
907
+ ? alone(batch, admitting)
908
+ : Effect.flatMap(grouped(batch, admitting.admission, admitting.member), (outcome) => outcome.ended !== undefined
909
+ ? Effect.succeed(outcome.ended)
910
+ : Effect.flatMap(openAlone(batch), (opened) => alone(batch, opened, outcome.following)));
911
+ let first = true;
912
+ return yield* drive((batch) => {
913
+ const joining = first && view().generation !== undefined && view().state !== undefined
914
+ ? groups
915
+ : undefined;
916
+ first = false;
917
+ return joining === undefined ? openAlone(batch) : openGrouped(batch, joining);
918
+ }, transact).pipe(Effect.onExit((exit) => {
919
+ if (Exit.isSuccess(exit) || !open)
920
+ return Effect.void;
921
+ const connection = session();
922
+ if (Cause.hasInterrupts(exit.cause))
923
+ return sql `SELECT pg_cancel_backend(${connection.processId})`.pipe(Effect.ignore, Effect.andThen(turns.invalidate(connection)));
924
+ return control("ROLLBACK").pipe(Effect.flatMap((result) => result.command === "ROLLBACK" ? Effect.void : turns.invalidate(connection)), Effect.catch(() => turns.invalidate(connection)));
925
+ }));
926
+ }));
927
+ /**
928
+ * PGlite: one in-process session and nothing to pipeline, so each batch runs
929
+ * its groups one statement at a time inside `withTransaction`, which holds
930
+ * the session only for that transaction; a batch with nothing to commit rolls
931
+ * back. The commit version is read after the transaction ends.
932
+ */
933
+ const sequential = drive(() => Effect.void, (batch) => Effect.gen(function* () {
934
+ const control = (text) => Effect.asVoid(sql.unsafe(text));
935
+ const session = { send: sendSequentially, control, defer: control, withdraw: () => false };
936
+ const plan = yield* bounded(sql
937
+ .withTransaction(Effect.gen(function* () {
938
+ const admission = admit(batch, yield* canonicalsOf(batch), view(), session, []);
939
+ yield* sendSequentially(admission.group);
940
+ const decided = yield* admission.resume();
941
+ if (decided.writes === undefined)
942
+ return yield* Effect.fail(new RolledBack(decided));
943
+ yield* sendSequentially(decided.writes).pipe(Effect.withSpan(SpanNames.commit));
944
+ return decided;
945
+ }))
946
+ .pipe(Effect.catchIf((error) => error instanceof RolledBack, (rolled) => Effect.succeed(rolled.plan))));
947
+ const [ended] = yield* sql.unsafe(COMMIT_VERSION);
948
+ return {
949
+ plan,
950
+ version: ended.version,
951
+ endedAtMs: Number(ended.now),
952
+ };
953
+ }));
954
+ const turns = yield* Effect.serviceOption(TurnConnections);
955
+ const groups = statements || waited.size > 0 || (yield* NekiTurnSessions)
956
+ ? undefined
957
+ : Option.getOrUndefined(yield* Effect.serviceOption(TurnGroups));
958
+ const exit = yield* (Option.isSome(turns) ? pipelined(turns.value) : sequential).pipe(Effect.onError(() => Effect.sync(() => {
959
+ cache.state = undefined;
960
+ })), Effect.exit);
961
+ if (Exit.isSuccess(exit))
962
+ return undefined;
963
+ if (Cause.hasInterruptsOnly(exit.cause))
964
+ return yield* Effect.failCause(exit.cause);
965
+ return {
966
+ batch: current,
967
+ orphan,
968
+ cause: exit.cause,
969
+ committed: answering,
970
+ poolRefused,
971
+ };
972
+ });