@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,896 @@
1
+ import { Cause, Clock, Deferred, Effect, Exit, Option, Queue, Schema, Semaphore, Stream, } from "effect";
2
+ import { inTenant } from "../database/tenancy.js";
3
+ import { Entity } from "effect/cluster";
4
+ import { SqlClient, SqlError } from "effect/sql";
5
+ import { ActorError, ActorUnavailable, NotCreated, RunnerAtCapacity, SessionEnded, } from "../../errors/actor.js";
6
+ import { ConnectionPhase, } from "../members.js";
7
+ import { Caller, principal } from "../../identity/caller.js";
8
+ import { replayEvents } from "../events/replay.js";
9
+ import { compress, decompress } from "../storage/codec.js";
10
+ import { FrameworkClock } from "../turn/admission.js";
11
+ import { actorRow, emptyActivationCache, forget as forgetGeneration, heldGeneration, } from "../storage/generation.js";
12
+ import { Committed, FEED_MEMBER, FeedFrame, HolderItem, isWatchMember, watchedQuery, watchMember, } from "./protocol.js";
13
+ import { streamSubscriptions } from "./streams.js";
14
+ import { HolderUnreachable } from "./transport.js";
15
+ import { WatchTap } from "./watch.js";
16
+ /** Encoded bytes one connection's session may hold. */
17
+ export const MAX_SESSION_BYTES = 16_384;
18
+ /** Open connections one actor may have per connection member. */
19
+ const MAX_MEMBER_CONNECTIONS = 10_000;
20
+ /** Events one `read.follow` page reads at most. */
21
+ const FOLLOW_PAGE = 1_000;
22
+ /** Progress frames one actor accepts per second, with a burst of as many. */
23
+ const PROGRESS_PER_SECOND = 20;
24
+ /** How long a job check's answer stands before a later frame reads the row again. */
25
+ const JOB_CHECK_MS = 5_000;
26
+ /** How long after a failed job check the next one waits. */
27
+ const JOB_CHECK_RETRY_MS = 1_000;
28
+ /** Clock skew allowed between the executor's lease deadline and the owner's clock. */
29
+ const PROGRESS_SKEW_MS = 1_000;
30
+ /** Progress entries one `read.progress` subscription buffers; the oldest goes first. */
31
+ const STREAM_PROGRESS_BUFFER = 16;
32
+ /** How long a progress record stays after its last frame, check, or close. */
33
+ const PROGRESS_RECORD_MS = 60_000;
34
+ const closedJob = (at) => ({
35
+ open: false,
36
+ checkedAt: at,
37
+ touchedAt: at,
38
+ attempt: 0,
39
+ seq: 0,
40
+ principal: Option.none(),
41
+ payload: "",
42
+ holders: new Set(),
43
+ });
44
+ const samePrincipal = (a, b) => Option.isSome(a) && Option.isSome(b) && a.value.subject === b.value.subject;
45
+ const utf8 = new TextEncoder();
46
+ /** A session is stored inside its codec's `{"value":…}` envelope, which the limit does not count. */
47
+ const SESSION_ENVELOPE_BYTES = utf8.encode(JSON.stringify({ value: null })).byteLength - utf8.encode("null").byteLength;
48
+ const encodeCaller = Schema.encodeEffect(Schema.fromJsonString(Caller));
49
+ const decodeCaller = Schema.decodeEffect(Schema.fromJsonString(Caller));
50
+ const emptyResult = {
51
+ session: undefined,
52
+ changed: false,
53
+ sends: [],
54
+ broadcasts: [],
55
+ close: false,
56
+ };
57
+ const ended = (cause, resync) => SessionEnded.make({ cause, resync });
58
+ /** Answers a database failure as `ActorUnavailable`, so the caller retries. */
59
+ const sqlUnavailable = (cause) => Effect.fail(ActorError.make({ reason: ActorUnavailable.make({ cause }) }));
60
+ const unavailable = (message) => ActorError.make({ reason: ActorUnavailable.make({ cause: new Error(message) }) });
61
+ /**
62
+ * The owner side of connections for one actor type: the activation registry
63
+ * shared with commands, the fenced `actor_connections` writes, and ordered,
64
+ * post-commit delivery of frames to every holder.
65
+ *
66
+ * Ordering and safety: every holder learns this owner before any of its
67
+ * broadcasts, so it can resync if the owner dies, and a holder that cannot be
68
+ * reached is excluded, its rows dropped, so it ends those connections itself.
69
+ * Dropping a generation's state means whatever runs next acquires a new one,
70
+ * reloads the rows, and numbers each holder's messages from 1 again, which a
71
+ * holder requires of every new generation; a clean end tells holders not to
72
+ * resync. A flush that read an older head than a concurrent one never moves the
73
+ * cursor back. Ends run on their own fibers so one slow handler never holds up the
74
+ * rest. A handler defect closes only its connection.
75
+ *
76
+ * Connections: feeds are framework connections, so an actor type with feeds loads
77
+ * its rows like one with members, and a feed has no handler, so its open only
78
+ * inserts the row and fixes its baseline and its close only deletes the row. A
79
+ * feed's holder rereads events itself once the new owner answers, and a feed
80
+ * serves the current shape even under a `writeVersion`. A connection's lock lives
81
+ * only as long as its row. A retried open cannot know whether its opening frames
82
+ * reached the holder. The owner runs frames only from the connection's stored
83
+ * holder, and a frame or subscription that reaches it past its authorization bound
84
+ * never runs.
85
+ *
86
+ * Subscriptions: followers wait on `advanced`, and a new head wakes every one. A
87
+ * replay reads pages from where the previous stopped up to the head, which only
88
+ * moves after a commit, so nothing is skipped or read twice. The subscription
89
+ * limit is checked and taken in one step so concurrent subscriptions cannot all
90
+ * pass it. A hook that hangs is retried at the next tick while the bound still
91
+ * ends the session, and an answer past the bound never extends it. A stream
92
+ * subscriber's live progress is a sliding buffer, so a slow reader loses the
93
+ * oldest.
94
+ *
95
+ * Progress frames: a route or settle that committed while the job check read
96
+ * keeps the job closed. A row that has not counted an attempt yet does not
97
+ * prove a frame open. Every frame of a job shares one activation record so its
98
+ * order and holders stay whole, and a record decides after a permit wait, never a
99
+ * copy taken before it. A record nothing touched for a while goes, and a late
100
+ * frame runs the job check again.
101
+ */
102
+ export const activationOwner = ({ registration, transport, authorize, clock, role, }) => {
103
+ const activations = new Map();
104
+ const hasConnections = registration.connections.size > 0 ||
105
+ registration.feeds.size > 0 ||
106
+ registration.watches.size > 0;
107
+ const hasStreams = registration.streams.size > 0;
108
+ const wanted = new Set([
109
+ ...[...registration.connections.values()].flatMap((member) => [
110
+ ...(member.progress?.jobs ?? []),
111
+ ]),
112
+ ...[...registration.streams.values()].flatMap((member) => [...member.progress]),
113
+ ]);
114
+ const hasProgress = wanted.size > 0;
115
+ const now = Effect.map(Clock.currentTimeMillis, (millis) => millis + clock.offsetMillis());
116
+ const advance = (activation, head) => {
117
+ if (BigInt(head) <= BigInt(activation.head))
118
+ return;
119
+ activation.head = head;
120
+ const previous = activation.advanced;
121
+ activation.advanced = Deferred.makeUnsafe();
122
+ Deferred.doneUnsafe(previous, Exit.void);
123
+ };
124
+ const encodeFeedFrame = Schema.encodeEffect(Schema.fromJsonString(FeedFrame));
125
+ const encodeCommitted = Schema.encodeEffect(Schema.fromJsonString(Committed));
126
+ /**
127
+ * The frame every watch of the actor receives after a commit, one broadcast
128
+ * per watchable query so each reaches its own connections. A commit that
129
+ * wrote nothing a query could have read sends none.
130
+ */
131
+ const watchBroadcasts = (activation, writes, version) => Effect.gen(function* () {
132
+ const nothing = !writes.state &&
133
+ writes.events.length === 0 &&
134
+ writes.tables.length === 0 &&
135
+ writes.blobs.length === 0;
136
+ if (nothing || registration.watches.size === 0 || (yield* WatchTap).dropsCommitted())
137
+ return [];
138
+ if (![...(activation.rows?.values() ?? [])].some((row) => isWatchMember(row.member)))
139
+ return [];
140
+ const frame = yield* encodeCommitted({ version, writes }).pipe(Effect.orDie);
141
+ return [...registration.watches].map((query) => ({
142
+ member: watchMember(query),
143
+ frame,
144
+ }));
145
+ });
146
+ /** A committed turn's feed events, broadcast to every open feed of the actor. */
147
+ const feedBroadcasts = (committed) => Effect.forEach(committed.events.flatMap((event, index) => registration.feeds.has(event.tag)
148
+ ? [{ event, cursor: String(BigInt(committed.after) + BigInt(index) + 1n) }]
149
+ : []), ({ event, cursor }) => registration.upcastEvent(event.tag, event.version, event.value).pipe(Effect.flatMap((value) => encodeFeedFrame({
150
+ tag: event.tag,
151
+ value,
152
+ commandId: committed.commandId,
153
+ timestampMs: committed.emittedAtMs,
154
+ }).pipe(Effect.orDie)), Effect.map((frame) => ({ member: FEED_MEMBER, frame, event: cursor }))));
155
+ const enter = (entityId, ref, key) => Effect.acquireRelease(Effect.sync(() => {
156
+ const found = activations.get(entityId);
157
+ if (found !== undefined) {
158
+ found.presence += 1;
159
+ return found;
160
+ }
161
+ const created = {
162
+ ref,
163
+ key,
164
+ cache: emptyActivationCache(),
165
+ presence: 1,
166
+ rows: undefined,
167
+ head: "0",
168
+ advanced: Deferred.makeUnsafe(),
169
+ streams: new Set(),
170
+ progress: new Map(),
171
+ checking: new Set(),
172
+ checkFailed: new Map(),
173
+ listeners: new Set(),
174
+ progressTokens: PROGRESS_PER_SECOND,
175
+ progressRefilledAt: 0,
176
+ progressSweptAt: 0,
177
+ through: "0",
178
+ channels: new Map(),
179
+ flush: Semaphore.makeUnsafe(1),
180
+ loading: Semaphore.makeUnsafe(1),
181
+ locks: new Map(),
182
+ opened: new Set(),
183
+ keptAwake: undefined,
184
+ acquiring: Semaphore.makeUnsafe(1),
185
+ };
186
+ activations.set(entityId, created);
187
+ return created;
188
+ }), (activation) => Effect.gen(function* () {
189
+ activation.presence -= 1;
190
+ if (activation.presence > 0)
191
+ return;
192
+ activations.delete(entityId);
193
+ yield* seal(activation);
194
+ }));
195
+ const channelOf = (activation, holder, epoch) => {
196
+ const name = `${holder}|${epoch}`;
197
+ const found = activation.channels.get(name);
198
+ if (found !== undefined)
199
+ return found;
200
+ const created = { holder, epoch, seq: 0 };
201
+ activation.channels.set(name, created);
202
+ return created;
203
+ };
204
+ const dropRows = (activation, ids) => Effect.gen(function* () {
205
+ if (ids.length === 0)
206
+ return;
207
+ const sql = yield* SqlClient.SqlClient;
208
+ const actor = actorRow({ sql, actor: activation });
209
+ yield* sql `DELETE FROM actor_connections WHERE ${actor} AND connection_id IN ${sql.in(ids)}`;
210
+ for (const id of ids)
211
+ activation.rows?.delete(id);
212
+ const holder = activation.keptAwake;
213
+ if (holder !== undefined && (activation.rows?.size ?? 0) === 0) {
214
+ activation.keptAwake = undefined;
215
+ yield* Entity.keepAlive(false).pipe(Effect.provideContext(holder));
216
+ }
217
+ });
218
+ const send = (activation, channel, items) => Effect.gen(function* () {
219
+ channel.seq += 1;
220
+ const message = {
221
+ epoch: channel.epoch,
222
+ owner: transport.holder,
223
+ ownerEpoch: transport.epoch,
224
+ ref: activation.ref,
225
+ generation: activation.cache.generation ?? "0",
226
+ seq: channel.seq,
227
+ through: activation.through,
228
+ items,
229
+ };
230
+ const held = [...(activation.rows?.values() ?? [])].flatMap((row) => row.holder === channel.holder && row.holderEpoch === channel.epoch
231
+ ? [row.connectionId]
232
+ : []);
233
+ const answer = yield* transport
234
+ .deliver(channel.holder, channel.epoch, message)
235
+ .pipe(Effect.exit);
236
+ if (Exit.isFailure(answer)) {
237
+ if (Option.isSome(Cause.findErrorOption(answer.cause)))
238
+ return yield* dropRows(activation, held);
239
+ return yield* Effect.die(HolderUnreachable.make({ message: "Holder delivery failed" }));
240
+ }
241
+ if (answer.value.wrongEpoch)
242
+ return yield* dropRows(activation, held);
243
+ const mine = new Set(held);
244
+ yield* dropRows(activation, answer.value.unknown.filter((id) => mine.has(id)));
245
+ }).pipe(Effect.catchIf(SqlError.isSqlError, () => Effect.void));
246
+ const seal = (activation) => activation.flush
247
+ .withPermit(Effect.forEach(new Set([
248
+ ...activation.channels.values(),
249
+ ...[...(activation.rows?.values() ?? [])].map((row) => channelOf(activation, row.holder, row.holderEpoch)),
250
+ ]), (channel) => send(activation, channel, [HolderItem.cases.Seal.make({})]), { discard: true }))
251
+ .pipe(Effect.timeout("2 seconds"), Effect.ignore);
252
+ const forget = (activation) => {
253
+ forgetGeneration(activation.cache);
254
+ activation.rows = undefined;
255
+ activation.channels.clear();
256
+ };
257
+ /** Fences this activation's generation and loads committed state, as a command turn would. */
258
+ const acquire = (activation) => activation.cache.generation !== undefined && activation.cache.state !== undefined
259
+ ? Effect.void
260
+ : activation.acquiring.withPermit(acquireOnce(activation));
261
+ const acquireOnce = (activation) => Effect.gen(function* () {
262
+ if (activation.cache.generation !== undefined && activation.cache.state !== undefined)
263
+ return;
264
+ const sql = yield* SqlClient.SqlClient;
265
+ const actor = actorRow({ sql, actor: activation });
266
+ const { ref, key } = activation;
267
+ const acquired = yield* sql.withTransaction(Effect.gen(function* () {
268
+ yield* sql `INSERT INTO actor_generations (routing_key, tenant_id, actor_type, actor_id)
269
+ VALUES (${key}, ${ref.tenant}, ${ref.actor}, ${ref.id}) ON CONFLICT DO NOTHING`;
270
+ const [row] = activation.cache.generation === undefined
271
+ ? yield* sql `
272
+ UPDATE actor_generations SET generation = generation + 1 WHERE ${actor}
273
+ RETURNING generation::text AS generation, event_sequence::text AS head`
274
+ : yield* sql `
275
+ SELECT generation::text AS generation, event_sequence::text AS head
276
+ FROM actor_generations WHERE ${actor}`;
277
+ if (activation.cache.generation !== undefined &&
278
+ activation.cache.generation !== row.generation) {
279
+ forget(activation);
280
+ return yield* unavailable("Stale actor generation");
281
+ }
282
+ const state = yield* sql `
283
+ SELECT key, value FROM actor_state WHERE ${actor}`;
284
+ return { row: row, state };
285
+ }));
286
+ activation.cache.generation = acquired.row.generation;
287
+ activation.cache.state = new Map(acquired.state.map(({ key, value }) => [key, decompress(value)]));
288
+ advance(activation, acquired.row.head);
289
+ activation.through = acquired.row.head;
290
+ });
291
+ /** Loads the actor's connection rows once per activation, excluding holders that are gone. */
292
+ const load = (activation) => activation.rows !== undefined || !hasConnections
293
+ ? Effect.void
294
+ : activation.loading.withPermit(loadOnce(activation));
295
+ const loadOnce = (activation) => Effect.gen(function* () {
296
+ if (activation.rows !== undefined)
297
+ return;
298
+ const sql = yield* SqlClient.SqlClient;
299
+ const actor = actorRow({ sql, actor: activation });
300
+ const stored = yield* sql `SELECT connection_id, member, holder, holder_epoch, caller, session, frame_seq::text AS frame_seq,
301
+ opened_through::text AS opened_through
302
+ FROM actor_connections WHERE ${actor}`;
303
+ const rows = new Map();
304
+ for (const row of stored)
305
+ rows.set(row.connection_id, {
306
+ connectionId: row.connection_id,
307
+ member: row.member,
308
+ holder: row.holder,
309
+ holderEpoch: row.holder_epoch,
310
+ caller: yield* decodeCaller(row.caller).pipe(Effect.orDie),
311
+ baseline: row.opened_through,
312
+ session: row.session === null ? undefined : decompress(row.session),
313
+ frameSeq: Number(row.frame_seq),
314
+ });
315
+ activation.rows = rows;
316
+ yield* setKeepAwake(activation);
317
+ yield* activation.flush.withPermit(Effect.forEach(new Set([...rows.values()].map((row) => `${row.holder}|${row.holderEpoch}`)), (name) => {
318
+ const [holder, epoch] = name.split("|");
319
+ return send(activation, channelOf(activation, holder, epoch), []);
320
+ }, { discard: true }));
321
+ }).pipe(Effect.catchIf(SqlError.isSqlError, Effect.die));
322
+ /** Readies an activation that is about to run a turn: fenced, with its connection rows. */
323
+ const prepare = (activation) => hasConnections
324
+ ? Effect.andThen(acquire(activation), load(activation)).pipe(Effect.orDie)
325
+ : Effect.void;
326
+ const list = (activation) => (member) => Effect.sync(() => [...(activation.rows?.values() ?? [])]
327
+ .filter((row) => row.member === member && row.buffered === undefined)
328
+ .slice(0, 1_000)
329
+ .map((row) => ({
330
+ connectionId: row.connectionId,
331
+ caller: row.caller,
332
+ session: row.session,
333
+ })));
334
+ /** Sends committed frames to their holders in order and advances the flushed-through cursor. */
335
+ const flush = (activation, broadcasts, head, own) => activation.flush.withPermit(Effect.gen(function* () {
336
+ if (activation.rows === undefined || (broadcasts.length === 0 && own === undefined)) {
337
+ advance(activation, head);
338
+ if (BigInt(head) > BigInt(activation.through))
339
+ activation.through = head;
340
+ return;
341
+ }
342
+ const perChannel = new Map();
343
+ const add = (row, item) => {
344
+ const channel = channelOf(activation, row.holder, row.holderEpoch);
345
+ const items = perChannel.get(channel) ?? [];
346
+ const last = items.at(-1);
347
+ if (last?._tag === "Frame" &&
348
+ last.frame === item.frame &&
349
+ last.member === item.member &&
350
+ last.event === item.event &&
351
+ !last.to.includes(item.to[0]))
352
+ perChannel.set(channel, [
353
+ ...items.slice(0, -1),
354
+ { ...last, to: [...last.to, ...item.to] },
355
+ ]);
356
+ else
357
+ perChannel.set(channel, [...items, item]);
358
+ };
359
+ const self = own === undefined ? undefined : activation.rows.get(own.connectionId);
360
+ if (own !== undefined && self !== undefined) {
361
+ const stamp = registration.connections.get(own.member)?.stampCursor ?? true;
362
+ for (const frame of own.frames)
363
+ add(self, HolderItem.cases.Frame.make({
364
+ member: own.member,
365
+ to: [own.connectionId],
366
+ frame: frame.frame,
367
+ event: frame.event,
368
+ stamp,
369
+ replay: own.replay === true,
370
+ }));
371
+ }
372
+ for (const broadcast of broadcasts) {
373
+ const stamp = registration.connections.get(broadcast.member)?.stampCursor ?? true;
374
+ const to = broadcast.to === undefined ? undefined : new Set(broadcast.to);
375
+ const except = new Set(broadcast.except ?? []);
376
+ for (const row of activation.rows.values())
377
+ if (row.member === broadcast.member &&
378
+ (to === undefined || to.has(row.connectionId)) &&
379
+ !except.has(row.connectionId))
380
+ if (row.buffered !== undefined)
381
+ row.buffered.push({ frame: broadcast.frame, event: broadcast.event });
382
+ else
383
+ add(row, HolderItem.cases.Frame.make({
384
+ member: row.member,
385
+ to: [row.connectionId],
386
+ frame: broadcast.frame,
387
+ event: broadcast.event,
388
+ stamp,
389
+ }));
390
+ }
391
+ const advanced = BigInt(head) > BigInt(activation.through);
392
+ for (const [channel, items] of perChannel)
393
+ yield* send(activation, channel, advanced ? [...items, HolderItem.cases.Flushed.make({ through: head })] : items);
394
+ advance(activation, head);
395
+ if (advanced)
396
+ activation.through = head;
397
+ }));
398
+ const lockOf = (activation, connectionId) => {
399
+ const found = activation.locks.get(connectionId);
400
+ if (found !== undefined)
401
+ return found;
402
+ const created = Semaphore.makeUnsafe(1);
403
+ activation.locks.set(connectionId, created);
404
+ return created;
405
+ };
406
+ const withLock = (activation, connectionId, effect) => lockOf(activation, connectionId)
407
+ .withPermit(effect)
408
+ .pipe(Effect.ensuring(Effect.sync(() => {
409
+ if (activation.rows?.has(connectionId) !== true)
410
+ activation.locks.delete(connectionId);
411
+ })));
412
+ const events = (activation, sql) => (tag, after, limit) => replayEvents(activation.ref, activation.key, [tag], after, BigInt(activation.head), limit).pipe(inTenant({ sql, role, tenant: activation.ref.tenant }), Effect.catchIf(SqlError.isSqlError, Effect.die), Effect.provideService(SqlClient.SqlClient, sql));
413
+ const run = (activation, row, phase, commands) => Effect.gen(function* () {
414
+ const connection = registration.connections.get(row.member);
415
+ const sql = yield* SqlClient.SqlClient;
416
+ return yield* connection.run({
417
+ ref: activation.ref,
418
+ connectionId: row.connectionId,
419
+ member: row.member,
420
+ caller: row.caller,
421
+ resumed: !ConnectionPhase.guards.Open(phase) && !activation.opened.has(row.connectionId),
422
+ cursor: activation.through,
423
+ state: [...(activation.cache.state ?? new Map())],
424
+ session: row.session,
425
+ connections: list(activation),
426
+ events: events(activation, sql),
427
+ commands,
428
+ }, phase);
429
+ });
430
+ const checkSession = (result) => result.session !== undefined &&
431
+ utf8.encode(result.session).byteLength - SESSION_ENVELOPE_BYTES > MAX_SESSION_BYTES
432
+ ? Effect.die(new Error("Connection session exceeds 16 KiB"))
433
+ : Effect.void;
434
+ const setKeepAwake = (activation) => Effect.gen(function* () {
435
+ if (registration.policy.connections !== "keepAwake")
436
+ return;
437
+ const open = (activation.rows?.size ?? 0) > 0;
438
+ if (open === (activation.keptAwake !== undefined))
439
+ return;
440
+ if (open) {
441
+ activation.keptAwake = yield* Effect.context();
442
+ yield* Entity.keepAlive(true);
443
+ return;
444
+ }
445
+ const holder = activation.keptAwake;
446
+ activation.keptAwake = undefined;
447
+ yield* Entity.keepAlive(false).pipe(Effect.provideContext(holder));
448
+ });
449
+ const closeOnDefect = (activation, connectionId) => (cause) => Effect.gen(function* () {
450
+ yield* Effect.logError("Connection handler defect", Cause.die(cause));
451
+ yield* dropRows(activation, [connectionId]);
452
+ return { _tag: "Closed", ended: ended("Defect", false) };
453
+ });
454
+ const identity = (activation) => ({
455
+ generation: activation.cache.generation,
456
+ owner: transport.holder,
457
+ ownerEpoch: transport.epoch,
458
+ });
459
+ const open = (activation, request) => withLock(activation, request.connectionId, Effect.gen(function* () {
460
+ const feed = request.member === FEED_MEMBER;
461
+ const watch = isWatchMember(request.member);
462
+ if (feed
463
+ ? registration.feeds.size === 0
464
+ : watch
465
+ ? !registration.watches.has(watchedQuery(request.member))
466
+ : !registration.connections.has(request.member))
467
+ return yield* Effect.die(new Error(`Unregistered connection ${request.member}`));
468
+ yield* acquire(activation);
469
+ yield* load(activation);
470
+ const existing = activation.rows.get(request.connectionId);
471
+ if (existing !== undefined && existing.holderEpoch === request.holderEpoch)
472
+ return {
473
+ _tag: "Opened",
474
+ ...identity(activation),
475
+ baseline: existing.baseline,
476
+ recovered: true,
477
+ };
478
+ const sql = yield* SqlClient.SqlClient;
479
+ const actor = actorRow({ sql, actor: activation });
480
+ if (registration.policy.createdBy !== undefined) {
481
+ const [created] = yield* sql `
482
+ SELECT created FROM actor_generations WHERE ${actor}`;
483
+ if (created?.created !== true)
484
+ return yield* ActorError.make({ reason: NotCreated.make({}) });
485
+ }
486
+ if (watch &&
487
+ [...activation.rows.values()].filter((row) => isWatchMember(row.member)).length >=
488
+ registration.policy.watch.maxPerActor)
489
+ return yield* ActorError.make({ reason: RunnerAtCapacity.make({}) });
490
+ if ([...activation.rows.values()].filter((row) => row.member === request.member).length >=
491
+ MAX_MEMBER_CONNECTIONS)
492
+ return yield* feed
493
+ ? ActorError.make({ reason: RunnerAtCapacity.make({}) })
494
+ : unavailable("Actor is at its connection limit for this member");
495
+ const baseline = activation.through;
496
+ const row = {
497
+ connectionId: request.connectionId,
498
+ member: request.member,
499
+ holder: request.holder,
500
+ holderEpoch: request.holderEpoch,
501
+ caller: request.caller,
502
+ baseline,
503
+ session: undefined,
504
+ };
505
+ activation.rows.set(request.connectionId, { ...row, frameSeq: 0, buffered: [] });
506
+ const result = yield* (feed || watch
507
+ ? Effect.succeed(emptyResult)
508
+ : run(activation, row, ConnectionPhase.cases.Open.make({ params: request.params }), request.commands)).pipe(Effect.catchDefect((cause) => Effect.gen(function* () {
509
+ yield* Effect.logError("Connection open defect", Cause.die(cause));
510
+ return yield* ActorError.make({ reason: ended("Defect", false) });
511
+ })), Effect.catchIf((error) => "failure" in error, (error) => Effect.succeed({ failure: error.failure })));
512
+ if ("failure" in result)
513
+ return { _tag: "Failed", value: result.failure };
514
+ yield* checkSession(result).pipe(Effect.catchDefect(() => ActorError.make({ reason: ended("Defect", false) })));
515
+ const caller = yield* encodeCaller(request.caller).pipe(Effect.orDie);
516
+ const inserted = yield* sql `
517
+ INSERT INTO actor_connections (routing_key, connection_id, bucket, tenant_id, actor_type, actor_id,
518
+ member, holder, holder_epoch, caller, session, opened_at_ms, opened_through)
519
+ SELECT routing_key, ${request.connectionId}, (routing_key >> 56)::integer, tenant_id, actor_type, actor_id,
520
+ ${request.member}, ${request.holder}, ${request.holderEpoch}, ${caller},
521
+ ${result.session === undefined ? null : compress(result.session)},
522
+ floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint, ${baseline}::bigint
523
+ FROM (${heldGeneration({ sql, actor: activation, generation: activation.cache.generation, lock: "SHARE" })}) g
524
+ RETURNING connection_id`;
525
+ if (inserted.length === 0) {
526
+ forget(activation);
527
+ return yield* unavailable("Stale actor generation");
528
+ }
529
+ const buffered = activation.rows.get(request.connectionId)?.buffered ?? [];
530
+ activation.rows.set(request.connectionId, { ...row, session: result.session, frameSeq: 0 });
531
+ activation.opened.add(request.connectionId);
532
+ yield* setKeepAwake(activation);
533
+ yield* flush(activation, result.broadcasts, activation.head, {
534
+ connectionId: request.connectionId,
535
+ member: request.member,
536
+ frames: [...result.sends, ...buffered],
537
+ });
538
+ if (result.close)
539
+ yield* closeRow(activation, request.connectionId, ended("ServerClosed", false));
540
+ return { _tag: "Opened", ...identity(activation), baseline };
541
+ }).pipe(Effect.ensuring(Effect.sync(() => {
542
+ if (activation.rows?.get(request.connectionId)?.buffered !== undefined)
543
+ activation.rows.delete(request.connectionId);
544
+ })), Effect.catchIf(SqlError.isSqlError, sqlUnavailable)));
545
+ const closeRow = (activation, connectionId, cause) => Effect.gen(function* () {
546
+ const row = activation.rows?.get(connectionId);
547
+ if (row === undefined)
548
+ return;
549
+ yield* activation.flush.withPermit(Effect.gen(function* () {
550
+ yield* dropRows(activation, [connectionId]);
551
+ yield* send(activation, channelOf(activation, row.holder, row.holderEpoch), [
552
+ HolderItem.cases.End.make({ connectionId, ended: cause }),
553
+ ]);
554
+ }));
555
+ yield* setKeepAwake(activation);
556
+ });
557
+ /**
558
+ * Runs `body` under the connection's lock with the activation fenced and
559
+ * its rows loaded, on the connection's row when the requesting holder still
560
+ * owns it. A database failure answers `ActorUnavailable`, so the holder retries.
561
+ */
562
+ const onOwnedRow = (activation, request, body) => withLock(activation, request.connectionId, Effect.gen(function* () {
563
+ yield* acquire(activation);
564
+ yield* load(activation);
565
+ const row = activation.rows?.get(request.connectionId);
566
+ return yield* body(row !== undefined &&
567
+ row.holder === request.holder &&
568
+ row.holderEpoch === request.holderEpoch
569
+ ? row
570
+ : undefined);
571
+ }).pipe(Effect.catchIf(SqlError.isSqlError, sqlUnavailable)));
572
+ /** Whether a request's authorization has lapsed on the framework clock. */
573
+ const lapsed = (authorizedUntil) => Effect.gen(function* () {
574
+ const clock = yield* FrameworkClock;
575
+ return (yield* Clock.currentTimeMillis) + clock.offsetMillis() >= authorizedUntil;
576
+ });
577
+ /** Drops a connection the server ended and answers it `ServerClosed`. */
578
+ const serverClosed = (activation, connectionId) => Effect.gen(function* () {
579
+ yield* dropRows(activation, [connectionId]);
580
+ yield* setKeepAwake(activation);
581
+ return { _tag: "Closed", ended: ended("ServerClosed", false) };
582
+ });
583
+ const frame = (activation, request) => onOwnedRow(activation, request, (row) => Effect.gen(function* () {
584
+ if (row === undefined)
585
+ return { _tag: "Closed", ended: ended("ServerClosed", true) };
586
+ if (request.seq <= row.frameSeq)
587
+ return { _tag: "Acked", ...identity(activation) };
588
+ if (yield* lapsed(request.authorizedUntil))
589
+ return yield* serverClosed(activation, request.connectionId);
590
+ return yield* Effect.gen(function* () {
591
+ const result = yield* run(activation, row, ConnectionPhase.cases.Frame.make({ frame: request.frame }), request.commands).pipe(Effect.catch(() => Effect.die(new Error("A frame handler cannot fail"))));
592
+ yield* checkSession(result);
593
+ if (result.changed) {
594
+ const sql = yield* SqlClient.SqlClient;
595
+ const written = yield* sql `
596
+ UPDATE actor_connections c SET session = ${result.session === undefined ? null : compress(result.session)},
597
+ frame_seq = ${request.seq}
598
+ FROM (${heldGeneration({ sql, actor: activation, generation: activation.cache.generation, lock: "SHARE" })}) g
599
+ WHERE c.routing_key = g.routing_key AND c.tenant_id = g.tenant_id
600
+ AND c.actor_type = g.actor_type AND c.actor_id = g.actor_id
601
+ AND c.connection_id = ${request.connectionId} AND c.frame_seq < ${request.seq}
602
+ RETURNING c.connection_id`;
603
+ if (written.length === 0) {
604
+ forget(activation);
605
+ return yield* unavailable("Stale actor generation");
606
+ }
607
+ row.session = result.session;
608
+ }
609
+ row.frameSeq = request.seq;
610
+ yield* flush(activation, result.broadcasts, activation.head, {
611
+ connectionId: request.connectionId,
612
+ member: row.member,
613
+ frames: result.sends,
614
+ });
615
+ if (result.close)
616
+ return yield* serverClosed(activation, request.connectionId);
617
+ return { _tag: "Acked", ...identity(activation) };
618
+ }).pipe(Effect.catchDefect(closeOnDefect(activation, request.connectionId)));
619
+ }));
620
+ const close = (activation, request) => onOwnedRow(activation, request, (row) => Effect.gen(function* () {
621
+ if (row === undefined)
622
+ return;
623
+ const result = row.member === FEED_MEMBER || isWatchMember(row.member)
624
+ ? undefined
625
+ : yield* run(activation, row, ConnectionPhase.cases.Close.make({ reason: request.cause.cause })).pipe(Effect.catchDefect((cause) => Effect.as(Effect.logError("Connection close defect", Cause.die(cause)), undefined)), Effect.orElseSucceed(() => undefined));
626
+ yield* dropRows(activation, [request.connectionId]);
627
+ activation.opened.delete(request.connectionId);
628
+ activation.locks.delete(request.connectionId);
629
+ yield* setKeepAwake(activation);
630
+ if (result !== undefined)
631
+ yield* flush(activation, result.broadcasts, activation.head);
632
+ }));
633
+ const resync = (activation, request) => onOwnedRow(activation, request, (row) => Effect.gen(function* () {
634
+ if (row === undefined)
635
+ return { _tag: "Closed", ended: ended("OwnerLost", true) };
636
+ if (yield* lapsed(request.authorizedUntil))
637
+ return yield* serverClosed(activation, request.connectionId);
638
+ if (row.member === FEED_MEMBER ||
639
+ isWatchMember(row.member) ||
640
+ !registration.connections.get(row.member).hasResync)
641
+ return { _tag: "Replayed", ...identity(activation) };
642
+ return yield* Effect.gen(function* () {
643
+ const result = yield* run(activation, row, ConnectionPhase.cases.Resync.make({ after: request.after })).pipe(Effect.catch(() => Effect.die(new Error("A resync handler cannot fail"))));
644
+ yield* flush(activation, result.broadcasts, activation.head, {
645
+ connectionId: request.connectionId,
646
+ member: row.member,
647
+ frames: result.sends,
648
+ replay: true,
649
+ });
650
+ if (result.close)
651
+ return yield* serverClosed(activation, request.connectionId);
652
+ return { _tag: "Replayed", ...identity(activation) };
653
+ }).pipe(Effect.catchDefect(closeOnDefect(activation, request.connectionId)));
654
+ }));
655
+ const follow = (activation, sql) => (tag, after) => Stream.paginate({ cursor: after, first: true }, ({ cursor, first }) => Effect.gen(function* () {
656
+ const signal = activation.advanced;
657
+ const head = activation.head;
658
+ if (!first && BigInt(head) <= BigInt(cursor ?? "0")) {
659
+ yield* Deferred.await(signal);
660
+ return [[], Option.some({ cursor, first: false })];
661
+ }
662
+ const page = yield* replayEvents(activation.ref, activation.key, [tag], cursor, BigInt(head), FOLLOW_PAGE).pipe(inTenant({ sql, role, tenant: activation.ref.tenant }), Effect.catchIf(SqlError.isSqlError, Effect.die), Effect.provideService(SqlClient.SqlClient, sql));
663
+ const next = page.length === FOLLOW_PAGE ? page.at(-1).cursor : head;
664
+ return [page, Option.some({ cursor: next, first: false })];
665
+ }));
666
+ const streams = streamSubscriptions({
667
+ registration,
668
+ activations,
669
+ transport,
670
+ authorize,
671
+ now,
672
+ prepare: Effect.fnUntraced(function* (activation) {
673
+ yield* acquire(activation).pipe(Effect.catchIf(SqlError.isSqlError, sqlUnavailable));
674
+ const sql = yield* SqlClient.SqlClient;
675
+ return {
676
+ ref: activation.ref,
677
+ cursor: activation.head,
678
+ state: [...(activation.cache.state ?? new Map())],
679
+ events: events(activation, sql),
680
+ follow: follow(activation, sql),
681
+ progress: (tag, jobId) => progressFeed(activation, tag, jobId),
682
+ };
683
+ }),
684
+ });
685
+ const progressFeed = (activation, tag, jobId) => Stream.unwrap(Effect.gen(function* () {
686
+ const queue = yield* Queue.sliding(STREAM_PROGRESS_BUFFER);
687
+ const listener = { tag, jobId, queue };
688
+ yield* Effect.acquireRelease(Effect.sync(() => activation.listeners.add(listener)), () => Effect.sync(() => activation.listeners.delete(listener)));
689
+ return Stream.fromQueue(queue);
690
+ }));
691
+ const takeProgressToken = (activation, at) => {
692
+ activation.progressTokens = Math.min(PROGRESS_PER_SECOND, activation.progressTokens +
693
+ ((at - activation.progressRefilledAt) * PROGRESS_PER_SECOND) / 1000);
694
+ activation.progressRefilledAt = at;
695
+ if (activation.progressTokens < 1)
696
+ return false;
697
+ activation.progressTokens -= 1;
698
+ return true;
699
+ };
700
+ /**
701
+ * Whether the job is still open: one indexed read of its outbox row per
702
+ * job id per `JOB_CHECK_MS`. A row that is gone or no longer a job
703
+ * closes it for this activation.
704
+ */
705
+ const checkJob = (activation, message, at) => Effect.gen(function* () {
706
+ const current = activation.progress.get(message.jobId);
707
+ if (current !== undefined && (!current.open || at - current.checkedAt < JOB_CHECK_MS))
708
+ return current;
709
+ const failed = activation.checkFailed.get(message.jobId);
710
+ if (failed !== undefined && at - failed < JOB_CHECK_RETRY_MS)
711
+ return undefined;
712
+ const sql = yield* SqlClient.SqlClient;
713
+ const actor = actorRow({ sql, actor: activation });
714
+ activation.checking.add(message.jobId);
715
+ const read = yield* sql `SELECT kind, attempts, cancelled_at_ms IS NOT NULL AS cancelled, caller, payload
716
+ FROM actor_outbox
717
+ WHERE ${actor} AND intent_id = ${message.jobId}`.pipe(Effect.ensuring(Effect.sync(() => activation.checking.delete(message.jobId))), Effect.exit);
718
+ if (Exit.isFailure(read)) {
719
+ activation.checkFailed.set(message.jobId, at);
720
+ return undefined;
721
+ }
722
+ activation.checkFailed.delete(message.jobId);
723
+ const closedMeanwhile = activation.progress.get(message.jobId);
724
+ if (closedMeanwhile !== undefined && !closedMeanwhile.open)
725
+ return closedMeanwhile;
726
+ const [row] = read.value;
727
+ if (row === undefined || row.kind !== "job" || row.cancelled) {
728
+ const closed = closedJob(at);
729
+ activation.progress.set(message.jobId, closed);
730
+ return closed;
731
+ }
732
+ if (row.attempts < message.attempt)
733
+ return undefined;
734
+ const stored = activation.progress.get(message.jobId);
735
+ if (stored !== undefined && stored.open) {
736
+ stored.checkedAt = at;
737
+ stored.touchedAt = at;
738
+ return stored;
739
+ }
740
+ const checked = {
741
+ open: true,
742
+ checkedAt: at,
743
+ touchedAt: at,
744
+ attempt: 0,
745
+ seq: 0,
746
+ principal: principal(yield* decodeCaller(row.caller).pipe(Effect.orDie)),
747
+ payload: row.payload,
748
+ holders: new Set(),
749
+ };
750
+ activation.progress.set(message.jobId, checked);
751
+ return checked;
752
+ });
753
+ const after = (message, job) => message.attempt > job.attempt || (message.attempt === job.attempt && message.seq > job.seq);
754
+ /**
755
+ * Admits one executor progress frame and forwards it through this
756
+ * activation's ordered channels to the member connections that want it,
757
+ * and to stream subscribers. Every refusal drops the frame; nothing here
758
+ * fails, writes, or closes a session.
759
+ */
760
+ const progress = (activation, message) => Effect.gen(function* () {
761
+ const { ref } = activation;
762
+ if (message.ref.tenant !== ref.tenant ||
763
+ message.ref.actor !== ref.actor ||
764
+ message.ref.id !== ref.id ||
765
+ !wanted.has(message.job))
766
+ return;
767
+ const at = yield* now;
768
+ if (at - activation.progressSweptAt >= PROGRESS_RECORD_MS) {
769
+ activation.progressSweptAt = at;
770
+ for (const [jobId, record] of activation.progress)
771
+ if (at - record.touchedAt >= PROGRESS_RECORD_MS)
772
+ activation.progress.delete(jobId);
773
+ }
774
+ if (at > message.leaseUntil + PROGRESS_SKEW_MS)
775
+ return;
776
+ const known = activation.progress.get(message.jobId);
777
+ if (known !== undefined && (!known.open || !after(message, known)))
778
+ return;
779
+ if (!takeProgressToken(activation, at))
780
+ return;
781
+ yield* acquire(activation);
782
+ yield* load(activation);
783
+ const job = yield* checkJob(activation, message, at);
784
+ if (job === undefined)
785
+ return;
786
+ yield* activation.flush.withPermit(Effect.gen(function* () {
787
+ if (activation.progress.get(message.jobId) !== job)
788
+ return;
789
+ if (!job.open || !after(message, job))
790
+ return;
791
+ job.attempt = message.attempt;
792
+ job.seq = message.seq;
793
+ job.touchedAt = at;
794
+ const perChannel = new Map();
795
+ for (const row of activation.rows?.values() ?? []) {
796
+ const wants = registration.connections.get(row.member)?.progress;
797
+ if (wants === undefined ||
798
+ !wants.jobs.has(message.job) ||
799
+ row.buffered !== undefined ||
800
+ (wants.to === "principal" && !samePrincipal(principal(row.caller), job.principal)))
801
+ continue;
802
+ const channel = channelOf(activation, row.holder, row.holderEpoch);
803
+ const members = perChannel.get(channel) ?? new Map();
804
+ members.set(row.member, [...(members.get(row.member) ?? []), row.connectionId]);
805
+ perChannel.set(channel, members);
806
+ }
807
+ for (const [channel, members] of perChannel) {
808
+ job.holders.add(`${channel.holder}|${channel.epoch}`);
809
+ yield* send(activation, channel, [...members].map(([member, to]) => HolderItem.cases.Progress.make({
810
+ member,
811
+ to,
812
+ job: message.job,
813
+ jobId: message.jobId,
814
+ attempt: message.attempt,
815
+ seq: message.seq,
816
+ frame: message.frame,
817
+ })));
818
+ }
819
+ for (const listener of activation.listeners)
820
+ if (listener.tag === message.job &&
821
+ (listener.jobId === undefined || listener.jobId === message.jobId))
822
+ Queue.offerUnsafe(listener.queue, {
823
+ jobId: message.jobId,
824
+ job: job.payload,
825
+ attempt: message.attempt,
826
+ seq: message.seq,
827
+ frame: message.frame,
828
+ });
829
+ }));
830
+ }).pipe(Effect.catchCause((cause) => Effect.logDebug("Progress frame dropped", cause)));
831
+ /**
832
+ * Closes a job's progress on this activation, once its route turn (whose
833
+ * command id is the job id) commits or its settle is reported: holders
834
+ * that received its progress discard what they still buffer, before the
835
+ * route's own broadcasts.
836
+ */
837
+ const closeProgress = (activation, jobId) => Effect.suspend(() => {
838
+ const job = activation.progress.get(jobId);
839
+ if (job === undefined && !activation.checking.has(jobId))
840
+ return Effect.void;
841
+ return activation.flush.withPermit(Effect.gen(function* () {
842
+ const current = activation.progress.get(jobId);
843
+ activation.progress.set(jobId, closedJob(yield* now));
844
+ if (current === undefined || !current.open)
845
+ return;
846
+ current.open = false;
847
+ for (const name of current.holders) {
848
+ const channel = activation.channels.get(name);
849
+ if (channel !== undefined)
850
+ yield* send(activation, channel, [HolderItem.cases.ProgressEnd.make({ jobId })]);
851
+ }
852
+ }));
853
+ });
854
+ const progressClosed = (activation, message) => {
855
+ const job = activation.progress.get(message.jobId);
856
+ return job !== undefined && message.attempt < job.attempt
857
+ ? Effect.void
858
+ : closeProgress(activation, message.jobId);
859
+ };
860
+ const hibernate = (entityId) => Effect.gen(function* () {
861
+ const activation = activations.get(entityId);
862
+ if (activation === undefined)
863
+ return;
864
+ yield* streams.end(activation);
865
+ yield* seal(activation);
866
+ forget(activation);
867
+ activation.opened.clear();
868
+ activation.progress.clear();
869
+ activation.checkFailed.clear();
870
+ activation.head = "0";
871
+ activation.through = "0";
872
+ });
873
+ return {
874
+ activations,
875
+ hasConnections,
876
+ hasStreams,
877
+ hasProgress,
878
+ progress,
879
+ closeProgress,
880
+ progressClosed,
881
+ feedBroadcasts,
882
+ watchBroadcasts,
883
+ hibernate,
884
+ subscribe: streams.subscribe,
885
+ endStreams: streams.end,
886
+ watchStreams: streams.watch,
887
+ enter,
888
+ prepare,
889
+ list,
890
+ flush,
891
+ open,
892
+ frame,
893
+ close,
894
+ resync,
895
+ };
896
+ };