@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,1018 @@
1
+ import { Effect, Option } from "effect";
2
+ import { PgClient } from "@effect/sql-pg";
3
+ import { Migrator, SqlClient } from "effect/sql";
4
+ import { NekiTurnSessions } from "./neki/session.js";
5
+ import { MigrationResuming, nekiMigrator, withMigrationCoordination } from "./neki/migrations.js";
6
+ /** Every framework migration by id, applied in order above the latest applied id. */
7
+ export const migrations = {
8
+ "0001_foundation": Effect.gen(function* () {
9
+ const sql = yield* SqlClient.SqlClient;
10
+ yield* sql `CREATE TABLE actor_deployment (
11
+ singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
12
+ protocol integer NOT NULL,
13
+ retry_window_ms bigint NOT NULL CHECK (retry_window_ms > 0)
14
+ )`;
15
+ yield* sql `CREATE TABLE actor_generations (
16
+ tenant_id text NOT NULL,
17
+ actor_type text NOT NULL,
18
+ actor_id text NOT NULL,
19
+ generation bigint NOT NULL DEFAULT 0,
20
+ PRIMARY KEY (tenant_id, actor_type, actor_id)
21
+ )`;
22
+ yield* sql `CREATE TABLE actor_state (
23
+ tenant_id text NOT NULL,
24
+ actor_type text NOT NULL,
25
+ actor_id text NOT NULL,
26
+ key text NOT NULL,
27
+ value jsonb NOT NULL,
28
+ PRIMARY KEY (tenant_id, actor_type, actor_id, key),
29
+ FOREIGN KEY (tenant_id, actor_type, actor_id) REFERENCES actor_generations
30
+ )`;
31
+ yield* sql `CREATE TABLE actor_receipts (
32
+ tenant_id text NOT NULL,
33
+ actor_type text NOT NULL,
34
+ actor_id text NOT NULL,
35
+ command_id text NOT NULL,
36
+ command text NOT NULL,
37
+ payload_hash text NOT NULL,
38
+ caller_key text NOT NULL,
39
+ outcome text NOT NULL,
40
+ expires_at_ms bigint NOT NULL,
41
+ PRIMARY KEY (tenant_id, actor_type, actor_id, command_id),
42
+ FOREIGN KEY (tenant_id, actor_type, actor_id) REFERENCES actor_generations
43
+ )`;
44
+ }),
45
+ "0002_creation": Effect.gen(function* () {
46
+ const sql = yield* SqlClient.SqlClient;
47
+ yield* sql `ALTER TABLE actor_generations ADD COLUMN created boolean NOT NULL DEFAULT false`;
48
+ }),
49
+ /**
50
+ * Every framework row carries its routing key, leading the primary key so a shard index can
51
+ * place it; state is opaque bytea.
52
+ *
53
+ * Placement and its encoding decide every row's routing key, so a change would fork each
54
+ * existing actor into a second identity.
55
+ */
56
+ "0003_routing_state": Effect.gen(function* () {
57
+ const sql = yield* SqlClient.SqlClient;
58
+ if (!(yield* MigrationResuming) &&
59
+ (yield* sql `SELECT count(*)::int AS rows FROM actor_generations`)[0].rows > 0)
60
+ return yield* Effect.die(new Error("0003 requires an empty foundation database; M0 stored no production data"));
61
+ yield* sql `DROP TABLE actor_receipts`;
62
+ yield* sql `DROP TABLE actor_state`;
63
+ yield* sql `DROP TABLE actor_generations`;
64
+ yield* sql `CREATE TABLE actor_generations (
65
+ routing_key bigint NOT NULL,
66
+ tenant_id text NOT NULL,
67
+ actor_type text NOT NULL,
68
+ actor_id text NOT NULL,
69
+ generation bigint NOT NULL DEFAULT 0,
70
+ created boolean NOT NULL DEFAULT false,
71
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id)
72
+ ) WITH (fillfactor = 80)`;
73
+ yield* sql `CREATE TABLE actor_state (
74
+ routing_key bigint NOT NULL,
75
+ tenant_id text NOT NULL,
76
+ actor_type text NOT NULL,
77
+ actor_id text NOT NULL,
78
+ key text NOT NULL,
79
+ value bytea NOT NULL,
80
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, key),
81
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
82
+ ) WITH (fillfactor = 80)`;
83
+ yield* sql `CREATE TABLE actor_receipts (
84
+ routing_key bigint NOT NULL,
85
+ tenant_id text NOT NULL,
86
+ actor_type text NOT NULL,
87
+ actor_id text NOT NULL,
88
+ command_id text NOT NULL,
89
+ command text NOT NULL,
90
+ payload_hash text NOT NULL,
91
+ caller_key text NOT NULL,
92
+ outcome text NOT NULL,
93
+ expires_at_ms bigint NOT NULL,
94
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, command_id),
95
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
96
+ )`;
97
+ yield* sql `CREATE TABLE actor_placements (
98
+ actor_type text PRIMARY KEY,
99
+ placement text NOT NULL CHECK (placement IN ('tenant', 'actor')),
100
+ encoding integer NOT NULL
101
+ )`;
102
+ }),
103
+ /**
104
+ * Every intent and timer lives on its sender's shard. `bucket` is the top eight bits of
105
+ * `routing_key`, so the relay probes `(bucket, due_at_ms)` and never reads actors with
106
+ * nothing due.
107
+ */
108
+ "0004_outbox": Effect.gen(function* () {
109
+ const sql = yield* SqlClient.SqlClient;
110
+ yield* sql `CREATE TABLE actor_outbox (
111
+ routing_key bigint NOT NULL,
112
+ intent_id text NOT NULL,
113
+ bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
114
+ due_at_ms bigint NOT NULL,
115
+ tenant_id text NOT NULL,
116
+ actor_type text NOT NULL,
117
+ actor_id text NOT NULL,
118
+ timer_key text,
119
+ target_type text NOT NULL,
120
+ target_id text NOT NULL,
121
+ command text NOT NULL,
122
+ payload text NOT NULL,
123
+ caller text NOT NULL,
124
+ attempts integer NOT NULL DEFAULT 0,
125
+ PRIMARY KEY (routing_key, intent_id),
126
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
127
+ )`;
128
+ yield* sql `CREATE INDEX actor_outbox_due ON actor_outbox (bucket, due_at_ms)`;
129
+ yield* sql `CREATE UNIQUE INDEX actor_outbox_timer
130
+ ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, timer_key)
131
+ WHERE timer_key IS NOT NULL`;
132
+ }),
133
+ /**
134
+ * Application tables come from drizzle-kit; the framework only records which actor type owns
135
+ * each one, so a second owner cannot read its rows.
136
+ */
137
+ "0005_tables": Effect.gen(function* () {
138
+ const sql = yield* SqlClient.SqlClient;
139
+ yield* sql `CREATE TABLE actor_tables (
140
+ table_schema text NOT NULL,
141
+ table_name text NOT NULL,
142
+ actor_type text NOT NULL,
143
+ PRIMARY KEY (table_schema, table_name)
144
+ )`;
145
+ }),
146
+ /**
147
+ * Events share the actor's routing key and commit with its turn. The sequence counter lives
148
+ * on the fenced generation row, so a pruned stream never reissues a cursor.
149
+ */
150
+ "0006_events": Effect.gen(function* () {
151
+ const sql = yield* SqlClient.SqlClient;
152
+ yield* sql `ALTER TABLE actor_generations ADD COLUMN event_sequence bigint NOT NULL DEFAULT 0`;
153
+ yield* sql `CREATE TABLE actor_events (
154
+ routing_key bigint NOT NULL,
155
+ tenant_id text NOT NULL,
156
+ actor_type text NOT NULL,
157
+ actor_id text NOT NULL,
158
+ sequence bigint NOT NULL CHECK (sequence > 0),
159
+ event text NOT NULL,
160
+ command_id text NOT NULL,
161
+ value bytea NOT NULL,
162
+ emitted_at_ms bigint NOT NULL,
163
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, sequence),
164
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
165
+ )`;
166
+ }),
167
+ /**
168
+ * An effect is an outbox row whose executor runs when it is due. On success or exhaustion it
169
+ * becomes an intent to its route, so the route is delivered like any intent, with the effect
170
+ * id as its command id. `ambiguous` records whether the last attempt's outcome is unknown.
171
+ *
172
+ * Exhausted effects stay visible to operators after their row settles.
173
+ */
174
+ "0008_effects": Effect.gen(function* () {
175
+ const sql = yield* SqlClient.SqlClient;
176
+ yield* sql `ALTER TABLE actor_outbox
177
+ ADD COLUMN kind text NOT NULL DEFAULT 'intent' CHECK (kind IN ('intent', 'effect')),
178
+ ADD COLUMN last_error text,
179
+ ADD COLUMN ambiguous boolean NOT NULL DEFAULT false`;
180
+ yield* sql `CREATE TABLE actor_dead_letters (
181
+ routing_key bigint NOT NULL,
182
+ effect_id text NOT NULL,
183
+ tenant_id text NOT NULL,
184
+ actor_type text NOT NULL,
185
+ actor_id text NOT NULL,
186
+ effect text NOT NULL,
187
+ payload text NOT NULL,
188
+ attempts integer NOT NULL,
189
+ cause text NOT NULL,
190
+ ambiguous boolean NOT NULL,
191
+ dead_at_ms bigint NOT NULL,
192
+ PRIMARY KEY (routing_key, effect_id),
193
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
194
+ )`;
195
+ }),
196
+ /**
197
+ * Blob entries are chunked bytea beside the actor's other rows: `append` adds a chunk
198
+ * without rewriting earlier ones, and `compact` folds them into chunk 0.
199
+ */
200
+ "0009_blobs": Effect.gen(function* () {
201
+ const sql = yield* SqlClient.SqlClient;
202
+ yield* sql `CREATE TABLE actor_blobs (
203
+ routing_key bigint NOT NULL,
204
+ tenant_id text NOT NULL,
205
+ actor_type text NOT NULL,
206
+ actor_id text NOT NULL,
207
+ blob text NOT NULL,
208
+ name text NOT NULL,
209
+ chunk integer NOT NULL CHECK (chunk >= 0),
210
+ bytes bytea NOT NULL,
211
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, blob, name, chunk),
212
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
213
+ )`;
214
+ }),
215
+ /**
216
+ * Cleanup reads each actor type's oldest receipts and events by age, and keeps a receipt
217
+ * while an outbox row with its id can still be redelivered; these indexes keep every cleanup
218
+ * batch a range read.
219
+ */
220
+ "0010_retention": Effect.gen(function* () {
221
+ const sql = yield* SqlClient.SqlClient;
222
+ yield* sql `CREATE INDEX actor_receipts_expiry ON actor_receipts (actor_type, expires_at_ms)`;
223
+ yield* sql `CREATE INDEX actor_events_emitted ON actor_events (actor_type, emitted_at_ms)`;
224
+ yield* sql `CREATE INDEX actor_outbox_intent ON actor_outbox (intent_id)`;
225
+ }),
226
+ /**
227
+ * Claims and backoff move `due_at_ms`, so `scheduled_at_ms` keeps the time a row first
228
+ * became due. `kind` follows `bucket` in the due index so intent scans never read effect
229
+ * rows waiting for an executor, and the reverse.
230
+ */
231
+ "0011_relay": Effect.gen(function* () {
232
+ const sql = yield* SqlClient.SqlClient;
233
+ yield* sql `ALTER TABLE actor_outbox ADD COLUMN scheduled_at_ms bigint NOT NULL`;
234
+ yield* sql `CREATE INDEX actor_outbox_due_kind ON actor_outbox (bucket, kind, due_at_ms)`;
235
+ yield* sql `DROP INDEX actor_outbox_due`;
236
+ }),
237
+ /**
238
+ * Workflow executions, their recorded steps, and the manifests deployments accepted. Every
239
+ * execution and step row lives on its owner's shard; a step row is written pending before
240
+ * its work starts and settled once.
241
+ */
242
+ "0012_workflows": Effect.gen(function* () {
243
+ const sql = yield* SqlClient.SqlClient;
244
+ yield* sql `CREATE TABLE actor_workflow_executions (
245
+ routing_key bigint NOT NULL,
246
+ execution_id text NOT NULL,
247
+ bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
248
+ tenant_id text NOT NULL,
249
+ actor_type text NOT NULL,
250
+ actor_id text NOT NULL,
251
+ workflow text NOT NULL,
252
+ workflow_key text NOT NULL,
253
+ manifest_hash text NOT NULL,
254
+ payload bytea NOT NULL,
255
+ caller text NOT NULL,
256
+ event_cursor bigint NOT NULL,
257
+ status text NOT NULL CHECK (status IN ('running', 'suspended', 'finished')),
258
+ interrupt boolean NOT NULL DEFAULT false,
259
+ result bytea,
260
+ started_at_ms bigint NOT NULL,
261
+ finished_at_ms bigint,
262
+ PRIMARY KEY (routing_key, execution_id),
263
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations,
264
+ CHECK ((status = 'finished') = (result IS NOT NULL AND finished_at_ms IS NOT NULL))
265
+ )`;
266
+ yield* sql `CREATE INDEX actor_workflow_executions_open
267
+ ON actor_workflow_executions (routing_key, tenant_id, actor_type, actor_id)
268
+ WHERE status <> 'finished'`;
269
+ yield* sql `CREATE INDEX actor_workflow_executions_finished
270
+ ON actor_workflow_executions (bucket, finished_at_ms)
271
+ WHERE status = 'finished'`;
272
+ yield* sql `CREATE INDEX actor_workflow_executions_check
273
+ ON actor_workflow_executions (actor_type, workflow, manifest_hash)
274
+ WHERE status <> 'finished'`;
275
+ yield* sql `CREATE TABLE actor_workflow_step (
276
+ routing_key bigint NOT NULL,
277
+ execution_id text NOT NULL,
278
+ tenant_id text NOT NULL,
279
+ actor_type text NOT NULL,
280
+ actor_id text NOT NULL,
281
+ step text NOT NULL,
282
+ attempt integer NOT NULL,
283
+ kind text NOT NULL CHECK (kind IN ('activity', 'clock', 'deferred', 'wait', 'version')),
284
+ exit bytea,
285
+ due_at_ms bigint,
286
+ wait_event text,
287
+ wait_after bigint,
288
+ scanned bigint,
289
+ matched bigint,
290
+ version integer,
291
+ started_at_ms bigint NOT NULL,
292
+ settled_at_ms bigint,
293
+ PRIMARY KEY (routing_key, execution_id, step, attempt),
294
+ FOREIGN KEY (routing_key, execution_id) REFERENCES actor_workflow_executions ON DELETE CASCADE,
295
+ CHECK ((kind = 'wait') = (wait_event IS NOT NULL AND wait_after IS NOT NULL AND scanned IS NOT NULL)),
296
+ CHECK ((kind = 'clock') <= (due_at_ms IS NOT NULL)),
297
+ CHECK ((kind = 'version') = (version IS NOT NULL AND exit IS NULL))
298
+ )`;
299
+ yield* sql `CREATE INDEX actor_workflow_step_waits
300
+ ON actor_workflow_step (routing_key, tenant_id, actor_type, actor_id, wait_event)
301
+ WHERE kind = 'wait' AND exit IS NULL`;
302
+ yield* sql `CREATE TABLE actor_workflow_manifests (
303
+ actor_type text NOT NULL,
304
+ workflow text NOT NULL,
305
+ manifest_hash text NOT NULL,
306
+ manifest jsonb NOT NULL,
307
+ accepted_at_ms bigint NOT NULL,
308
+ PRIMARY KEY (actor_type, workflow, manifest_hash)
309
+ )`;
310
+ }),
311
+ /**
312
+ * The placement join keeps every view from being automatically updatable, so writes fail
313
+ * without triggers or rules to maintain.
314
+ *
315
+ * The catalog is how a tool checks which view versions a database has.
316
+ */
317
+ "0013_inspection_views": Effect.gen(function* () {
318
+ const sql = yield* SqlClient.SqlClient;
319
+ yield* sql `CREATE SCHEMA durable`;
320
+ yield* sql `CREATE VIEW durable.actors AS
321
+ SELECT g.tenant_id, g.actor_type, g.actor_id, g.routing_key, p.placement,
322
+ g.generation, g.created, g.event_sequence AS last_event_sequence
323
+ FROM actor_generations g
324
+ LEFT JOIN actor_placements p ON p.actor_type = g.actor_type`;
325
+ yield* sql `CREATE VIEW durable.state AS
326
+ SELECT s.tenant_id, s.actor_type, s.actor_id, s.routing_key, p.placement,
327
+ s.key, s.value, octet_length(s.value) AS value_bytes
328
+ FROM actor_state s
329
+ LEFT JOIN actor_placements p ON p.actor_type = s.actor_type`;
330
+ yield* sql `CREATE VIEW durable.receipts AS
331
+ SELECT r.tenant_id, r.actor_type, r.actor_id, r.routing_key, p.placement,
332
+ r.command_id, r.command, r.caller_key,
333
+ r.outcome::jsonb ->> '_tag' AS outcome_tag, r.outcome,
334
+ r.expires_at_ms, to_timestamp(r.expires_at_ms::float8 / 1000) AS expires_at
335
+ FROM actor_receipts r
336
+ LEFT JOIN actor_placements p ON p.actor_type = r.actor_type`;
337
+ yield* sql `CREATE VIEW durable.events AS
338
+ SELECT e.tenant_id, e.actor_type, e.actor_id, e.routing_key, p.placement,
339
+ e.sequence, e.event, e.command_id, e.value, octet_length(e.value) AS value_bytes,
340
+ e.emitted_at_ms, to_timestamp(e.emitted_at_ms::float8 / 1000) AS emitted_at
341
+ FROM actor_events e
342
+ LEFT JOIN actor_placements p ON p.actor_type = e.actor_type`;
343
+ yield* sql `CREATE VIEW durable.outbox AS
344
+ SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
345
+ o.intent_id, o.timer_key, o.target_type, o.target_id, o.command, o.payload, o.caller,
346
+ o.attempts, o.last_error, o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
347
+ FROM actor_outbox o
348
+ LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
349
+ WHERE o.kind = 'intent'`;
350
+ yield* sql `CREATE VIEW durable.timers AS
351
+ SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
352
+ o.timer_key, o.intent_id, o.target_type, o.target_id, o.command, o.payload, o.caller,
353
+ o.attempts, o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
354
+ FROM actor_outbox o
355
+ LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
356
+ WHERE o.kind = 'intent' AND o.timer_key IS NOT NULL`;
357
+ yield* sql `CREATE VIEW durable.effects AS
358
+ SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
359
+ o.intent_id AS effect_id, o.command AS effect, o.payload, o.caller,
360
+ o.attempts, o.last_error, o.ambiguous,
361
+ o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
362
+ FROM actor_outbox o
363
+ LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
364
+ WHERE o.kind = 'effect'`;
365
+ yield* sql `CREATE VIEW durable.dead_letters AS
366
+ SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
367
+ d.effect_id, d.effect, d.payload, d.attempts, d.cause, d.ambiguous,
368
+ d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at
369
+ FROM actor_dead_letters d
370
+ LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
371
+ yield* sql `CREATE VIEW durable.workflows AS
372
+ SELECT w.tenant_id, w.actor_type, w.actor_id, w.routing_key, p.placement,
373
+ w.execution_id, w.workflow, w.workflow_key, w.manifest_hash, w.status, w.interrupt,
374
+ w.caller, w.payload, octet_length(w.payload) AS payload_bytes,
375
+ w.result, octet_length(w.result) AS result_bytes,
376
+ w.started_at_ms, to_timestamp(w.started_at_ms::float8 / 1000) AS started_at,
377
+ w.finished_at_ms, to_timestamp(w.finished_at_ms::float8 / 1000) AS finished_at
378
+ FROM actor_workflow_executions w
379
+ LEFT JOIN actor_placements p ON p.actor_type = w.actor_type`;
380
+ yield* sql `CREATE VIEW durable.workflow_steps AS
381
+ SELECT s.tenant_id, s.actor_type, s.actor_id, s.routing_key, p.placement,
382
+ s.execution_id, s.step, s.attempt, s.kind, s.exit, s.wait_event, s.version,
383
+ s.due_at_ms, to_timestamp(s.due_at_ms::float8 / 1000) AS due_at,
384
+ s.started_at_ms, to_timestamp(s.started_at_ms::float8 / 1000) AS started_at,
385
+ s.settled_at_ms, to_timestamp(s.settled_at_ms::float8 / 1000) AS settled_at
386
+ FROM actor_workflow_step s
387
+ LEFT JOIN actor_placements p ON p.actor_type = s.actor_type`;
388
+ yield* sql `CREATE VIEW durable.views AS
389
+ SELECT view_name, version FROM (VALUES
390
+ ('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
391
+ ('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
392
+ ('workflow_steps', 1), ('views', 1)
393
+ ) AS v(view_name, version)`;
394
+ }),
395
+ /**
396
+ * A connection's session lives beside the actor's rows; its socket and buffers live at the
397
+ * holder runner named by `holder` and `holder_epoch`.
398
+ */
399
+ "0014_connections": Effect.gen(function* () {
400
+ const sql = yield* SqlClient.SqlClient;
401
+ yield* sql `CREATE TABLE actor_connections (
402
+ routing_key bigint NOT NULL,
403
+ connection_id text NOT NULL,
404
+ bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
405
+ tenant_id text NOT NULL,
406
+ actor_type text NOT NULL,
407
+ actor_id text NOT NULL,
408
+ member text NOT NULL,
409
+ holder text NOT NULL,
410
+ holder_epoch text NOT NULL,
411
+ caller text NOT NULL,
412
+ session bytea,
413
+ frame_seq bigint NOT NULL DEFAULT 0,
414
+ opened_at_ms bigint NOT NULL,
415
+ opened_through bigint NOT NULL,
416
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, connection_id),
417
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
418
+ )`;
419
+ yield* sql `CREATE INDEX actor_connections_holder ON actor_connections (bucket, holder, holder_epoch)`;
420
+ }),
421
+ "0015_effect_control": Effect.gen(function* () {
422
+ const sql = yield* SqlClient.SqlClient;
423
+ yield* sql `ALTER TABLE actor_outbox
424
+ ADD COLUMN running boolean NOT NULL DEFAULT false,
425
+ ADD COLUMN cancelled_at_ms bigint,
426
+ ADD COLUMN maybe_applied boolean NOT NULL DEFAULT false,
427
+ ADD COLUMN ready_at_ms bigint,
428
+ ADD COLUMN waiting boolean NOT NULL DEFAULT false,
429
+ ADD CONSTRAINT actor_outbox_effect_ready CHECK (kind <> 'effect' OR ready_at_ms IS NOT NULL)`;
430
+ yield* sql `CREATE INDEX actor_outbox_running
431
+ ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command)
432
+ WHERE kind = 'effect' AND running`;
433
+ yield* sql `CREATE INDEX actor_outbox_effect_queue
434
+ ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command, ready_at_ms, intent_id)
435
+ WHERE kind = 'effect' AND NOT running`;
436
+ }),
437
+ /**
438
+ * An attempt can end an effect before its retries run out, as when its route rejects the
439
+ * result. `final_failure` records that, with the outcome, so a dead letter that fails to
440
+ * commit is retried without another provider call.
441
+ */
442
+ "0016_final_effect_failures": Effect.gen(function* () {
443
+ const sql = yield* SqlClient.SqlClient;
444
+ yield* sql `ALTER TABLE actor_outbox ADD COLUMN final_failure boolean NOT NULL DEFAULT false`;
445
+ }),
446
+ /**
447
+ * Subscriptions fan out on the source's shard after commit: one row per (source,
448
+ * subscription, subscriber), routed rows with subscriber_id = ''. The tag summary makes the
449
+ * publisher's probe a key lookup per emitted tag, and the subscriber's cursor, on its own
450
+ * shard, deduplicates every delivery after its receipt is pruned. The due index leads with
451
+ * `subscriber_type` so a runner never scans types it doesn't register.
452
+ *
453
+ * The routed declarations of the deployment, by source type: a runner that serves a source
454
+ * must register every subscriber type routing from it, because the publishing turn creates
455
+ * the routed rows.
456
+ */
457
+ "0017_subscriptions": Effect.gen(function* () {
458
+ const sql = yield* SqlClient.SqlClient;
459
+ yield* sql `CREATE TABLE actor_subscriptions (
460
+ routing_key bigint NOT NULL,
461
+ tenant_id text NOT NULL,
462
+ source_type text NOT NULL,
463
+ source_id text NOT NULL,
464
+ subscriber_type text NOT NULL,
465
+ subscription text NOT NULL,
466
+ subscriber_id text NOT NULL,
467
+ events text[] NOT NULL,
468
+ epoch bigint NOT NULL DEFAULT 0,
469
+ active boolean NOT NULL DEFAULT true,
470
+ delivered bigint NOT NULL,
471
+ marked bigint NOT NULL DEFAULT 0,
472
+ bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
473
+ due_at_ms bigint,
474
+ attempts integer NOT NULL DEFAULT 0,
475
+ last_error text,
476
+ gaps bigint NOT NULL DEFAULT 0,
477
+ gap_at_ms bigint,
478
+ gap_through bigint,
479
+ PRIMARY KEY (routing_key, tenant_id, source_type, source_id, subscriber_type, subscription, subscriber_id),
480
+ FOREIGN KEY (routing_key, tenant_id, source_type, source_id) REFERENCES actor_generations
481
+ ) WITH (fillfactor = 80)`;
482
+ yield* sql `CREATE INDEX actor_subscriptions_due ON actor_subscriptions (bucket, subscriber_type, due_at_ms)
483
+ WHERE due_at_ms IS NOT NULL`;
484
+ yield* sql `CREATE TABLE actor_subscription_tags (
485
+ routing_key bigint NOT NULL,
486
+ tenant_id text NOT NULL,
487
+ source_type text NOT NULL,
488
+ source_id text NOT NULL,
489
+ event text NOT NULL,
490
+ rows integer NOT NULL CHECK (rows > 0),
491
+ PRIMARY KEY (routing_key, tenant_id, source_type, source_id, event),
492
+ FOREIGN KEY (routing_key, tenant_id, source_type, source_id) REFERENCES actor_generations
493
+ )`;
494
+ yield* sql `CREATE TABLE actor_subscription_cursors (
495
+ routing_key bigint NOT NULL,
496
+ tenant_id text NOT NULL,
497
+ actor_type text NOT NULL,
498
+ actor_id text NOT NULL,
499
+ subscription text NOT NULL,
500
+ source_type text NOT NULL,
501
+ source_id text NOT NULL,
502
+ epoch bigint NOT NULL DEFAULT 0,
503
+ active boolean NOT NULL DEFAULT true,
504
+ applied bigint NOT NULL,
505
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, subscription, source_type, source_id),
506
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
507
+ )`;
508
+ yield* sql `CREATE TABLE actor_routed_subscriptions (
509
+ source_type text NOT NULL,
510
+ subscriber_type text NOT NULL,
511
+ subscription text NOT NULL,
512
+ PRIMARY KEY (source_type, subscriber_type, subscription)
513
+ )`;
514
+ yield* sql `ALTER TABLE actor_outbox DROP CONSTRAINT actor_outbox_kind_check,
515
+ ADD CONSTRAINT actor_outbox_kind_check CHECK (kind IN ('intent', 'effect', 'feed', 'control'))`;
516
+ }),
517
+ /**
518
+ * Every tenant row admits only the tenant its transaction names. The table owner and
519
+ * superusers are exempt, so nothing changes until a deployment runs its turns and views as a
520
+ * role that is neither.
521
+ */
522
+ "0018_rls": Effect.gen(function* () {
523
+ const sql = yield* SqlClient.SqlClient;
524
+ for (const table of [
525
+ "actor_generations",
526
+ "actor_state",
527
+ "actor_receipts",
528
+ "actor_outbox",
529
+ "actor_events",
530
+ "actor_dead_letters",
531
+ "actor_blobs",
532
+ "actor_workflow_executions",
533
+ "actor_workflow_step",
534
+ "actor_connections",
535
+ "actor_subscriptions",
536
+ "actor_subscription_tags",
537
+ "actor_subscription_cursors",
538
+ ]) {
539
+ yield* sql `ALTER TABLE ${sql(table)} ENABLE ROW LEVEL SECURITY`;
540
+ yield* sql `CREATE POLICY durable_tenant ON ${sql(table)}
541
+ USING (tenant_id = current_setting('durable.tenant', true))
542
+ WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
543
+ }
544
+ }),
545
+ /**
546
+ * Content is stored once per tenant on the tenant's routing key; actors hold references on
547
+ * their own shard. Nothing counts references and no key points from a reference or a chunk
548
+ * to a content row: the sweep finds unreferenced content by scanning references, and grants
549
+ * gate it.
550
+ *
551
+ * Grants name the database they were issued by, so a key shared with another deployment
552
+ * never makes its grants valid here.
553
+ *
554
+ * Every tenant that ever uploaded, and when its content was last swept.
555
+ *
556
+ * A query reads references and chunks as the tenant role when row-level security is on;
557
+ * uploads, grants, and the sweep keep the exempt connecting role.
558
+ *
559
+ * The longest turn of each actor type that declares content, which the sweep waits out; it
560
+ * only grows, so a runner still on an earlier deploy with longer turns stays covered.
561
+ *
562
+ * The sweep join shows how far each tenant's collection lags, and keeps the view from being
563
+ * automatically updatable.
564
+ */
565
+ "0020_content_blobs": Effect.gen(function* () {
566
+ const sql = yield* SqlClient.SqlClient;
567
+ yield* sql `ALTER TABLE actor_deployment
568
+ ADD COLUMN deployment_id text NOT NULL DEFAULT gen_random_uuid()::text`;
569
+ yield* sql `CREATE TABLE tenant_contents (
570
+ routing_key bigint NOT NULL,
571
+ tenant_id text NOT NULL,
572
+ hash text NOT NULL,
573
+ size bigint NOT NULL CHECK (size >= 0),
574
+ granted_until_ms bigint NOT NULL,
575
+ PRIMARY KEY (routing_key, tenant_id, hash)
576
+ )`;
577
+ yield* sql `CREATE INDEX tenant_contents_granted
578
+ ON tenant_contents (routing_key, tenant_id, granted_until_ms, hash)`;
579
+ yield* sql `CREATE TABLE tenant_content_chunks (
580
+ routing_key bigint NOT NULL,
581
+ tenant_id text NOT NULL,
582
+ hash text NOT NULL,
583
+ chunk integer NOT NULL CHECK (chunk >= 0),
584
+ bytes bytea NOT NULL,
585
+ PRIMARY KEY (routing_key, tenant_id, hash, chunk)
586
+ )`;
587
+ yield* sql `CREATE TABLE actor_content_refs (
588
+ routing_key bigint NOT NULL,
589
+ tenant_id text NOT NULL,
590
+ actor_type text NOT NULL,
591
+ actor_id text NOT NULL,
592
+ blob text NOT NULL,
593
+ name text NOT NULL,
594
+ hash text NOT NULL,
595
+ size bigint NOT NULL CHECK (size >= 0),
596
+ PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, blob, name),
597
+ FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
598
+ )`;
599
+ yield* sql `CREATE INDEX actor_content_refs_hash ON actor_content_refs (tenant_id, hash)`;
600
+ yield* sql `CREATE TABLE tenant_content_sweeps (
601
+ routing_key bigint NOT NULL,
602
+ tenant_id text NOT NULL,
603
+ swept_at_ms bigint NOT NULL,
604
+ PRIMARY KEY (routing_key, tenant_id)
605
+ )`;
606
+ for (const table of [
607
+ "actor_content_refs",
608
+ "tenant_contents",
609
+ "tenant_content_chunks",
610
+ "tenant_content_sweeps",
611
+ ]) {
612
+ yield* sql `ALTER TABLE ${sql(table)} ENABLE ROW LEVEL SECURITY`;
613
+ yield* sql `CREATE POLICY durable_tenant ON ${sql(table)}
614
+ USING (tenant_id = current_setting('durable.tenant', true))
615
+ WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
616
+ }
617
+ yield* sql `CREATE TABLE actor_content_types (
618
+ actor_type text PRIMARY KEY,
619
+ turn_ms bigint NOT NULL CHECK (turn_ms > 0)
620
+ )`;
621
+ yield* sql `CREATE VIEW durable.contents AS
622
+ SELECT c.tenant_id, c.routing_key, c.hash, c.size,
623
+ c.granted_until_ms, to_timestamp(c.granted_until_ms::float8 / 1000) AS granted_until,
624
+ s.swept_at_ms, to_timestamp(s.swept_at_ms::float8 / 1000) AS swept_at
625
+ FROM tenant_contents c
626
+ LEFT JOIN tenant_content_sweeps s ON s.routing_key = c.routing_key AND s.tenant_id = c.tenant_id`;
627
+ yield* sql `CREATE VIEW durable.content_refs AS
628
+ SELECT r.tenant_id, r.actor_type, r.actor_id, r.routing_key, p.placement,
629
+ r.blob, r.name, r.hash, r.size
630
+ FROM actor_content_refs r
631
+ LEFT JOIN actor_placements p ON p.actor_type = r.actor_type`;
632
+ yield* sql `CREATE OR REPLACE VIEW durable.views AS
633
+ SELECT view_name, version FROM (VALUES
634
+ ('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
635
+ ('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
636
+ ('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1)
637
+ ) AS v(view_name, version)`;
638
+ }),
639
+ /**
640
+ * Stored events and effect payloads carry the version of their class's migration chain they
641
+ * were written at; readers upcast from it. A settled effect row becomes its route intent and
642
+ * goes back to 0. The two tables let startup refuse a deploy that would strand a stored
643
+ * version: `actor_payload_versions` records each version some runtime has written, and
644
+ * `actor_payload_writers` is each runtime's heartbeat per version it writes, which a clear
645
+ * waits out before it scans. Nothing written before it has a recorded version, so a database
646
+ * that already holds events or effect rows is refused rather than guessed at.
647
+ *
648
+ * Columns are added at the end, which a view version allows.
649
+ */
650
+ "0021_payload_versions": Effect.gen(function* () {
651
+ const sql = yield* SqlClient.SqlClient;
652
+ const [held] = yield* sql `SELECT
653
+ EXISTS (SELECT 1 FROM actor_events) OR EXISTS (SELECT 1 FROM actor_outbox)
654
+ OR EXISTS (SELECT 1 FROM actor_dead_letters) AS held`;
655
+ if (held?.held === true)
656
+ return yield* new Migrator.MigrationError({
657
+ kind: "BadState",
658
+ message: "Migration 0021_payload_versions needs a database without events, outbox rows, or dead letters; recreate this development database",
659
+ });
660
+ yield* sql `ALTER TABLE actor_events ADD COLUMN payload_version integer NOT NULL DEFAULT 0
661
+ CHECK (payload_version >= 0)`;
662
+ yield* sql `ALTER TABLE actor_outbox ADD COLUMN payload_version integer NOT NULL DEFAULT 0
663
+ CHECK (payload_version >= 0)`;
664
+ yield* sql `ALTER TABLE actor_dead_letters ADD COLUMN payload_version integer NOT NULL DEFAULT 0
665
+ CHECK (payload_version >= 0)`;
666
+ yield* sql `CREATE TABLE actor_payload_versions (
667
+ actor_type text NOT NULL,
668
+ kind text NOT NULL CHECK (kind IN ('event', 'effect')),
669
+ tag text NOT NULL,
670
+ version integer NOT NULL CHECK (version >= 0),
671
+ first_written_at_ms bigint NOT NULL,
672
+ superseded_at_ms bigint,
673
+ cleared_at_ms bigint,
674
+ PRIMARY KEY (actor_type, kind, tag, version)
675
+ )`;
676
+ yield* sql `CREATE TABLE actor_payload_writers (
677
+ runtime_id text NOT NULL,
678
+ actor_type text NOT NULL,
679
+ kind text NOT NULL CHECK (kind IN ('event', 'effect')),
680
+ tag text NOT NULL,
681
+ version integer NOT NULL CHECK (version >= 0),
682
+ window_ms bigint NOT NULL CHECK (window_ms > 0),
683
+ refreshed_at_ms bigint NOT NULL,
684
+ PRIMARY KEY (runtime_id, actor_type, kind, tag, version)
685
+ )`;
686
+ yield* sql `CREATE INDEX actor_payload_writers_version
687
+ ON actor_payload_writers (actor_type, kind, tag, version, refreshed_at_ms)`;
688
+ yield* sql `CREATE OR REPLACE VIEW durable.events AS
689
+ SELECT e.tenant_id, e.actor_type, e.actor_id, e.routing_key, p.placement,
690
+ e.sequence, e.event, e.command_id, e.value, octet_length(e.value) AS value_bytes,
691
+ e.emitted_at_ms, to_timestamp(e.emitted_at_ms::float8 / 1000) AS emitted_at,
692
+ e.payload_version
693
+ FROM actor_events e
694
+ LEFT JOIN actor_placements p ON p.actor_type = e.actor_type`;
695
+ yield* sql `CREATE OR REPLACE VIEW durable.effects AS
696
+ SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
697
+ o.intent_id AS effect_id, o.command AS effect, o.payload, o.caller,
698
+ o.attempts, o.last_error, o.ambiguous,
699
+ o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at,
700
+ o.payload_version
701
+ FROM actor_outbox o
702
+ LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
703
+ WHERE o.kind = 'effect'`;
704
+ yield* sql `CREATE OR REPLACE VIEW durable.dead_letters AS
705
+ SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
706
+ d.effect_id, d.effect, d.payload, d.attempts, d.cause, d.ambiguous,
707
+ d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at,
708
+ d.payload_version
709
+ FROM actor_dead_letters d
710
+ LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
711
+ }),
712
+ /**
713
+ * A parent-placed type routes through its parent type's placement, so the parent is part of
714
+ * the record a later build must match.
715
+ */
716
+ "0022_parent_placement": Effect.gen(function* () {
717
+ const sql = yield* SqlClient.SqlClient;
718
+ yield* sql `ALTER TABLE actor_placements ADD COLUMN parent_type text,
719
+ DROP CONSTRAINT actor_placements_placement_check,
720
+ ADD CONSTRAINT actor_placements_placement_check
721
+ CHECK (placement IN ('tenant', 'actor', 'parent')),
722
+ ADD CONSTRAINT actor_placements_parent_type_check
723
+ CHECK ((placement = 'parent') = (parent_type IS NOT NULL))`;
724
+ }),
725
+ /**
726
+ * Every authorized operator action, and every authenticated one refused by scope. A repair
727
+ * writes its row in its own transaction, on the target actor's routing key, so the two
728
+ * commit together on one shard; a tenant-wide action uses the tenant's routing key. Rows are
729
+ * never pruned.
730
+ *
731
+ * The join keeps the view read-only, as every other `durable` view is.
732
+ */
733
+ "0023_operator_audit": Effect.gen(function* () {
734
+ const sql = yield* SqlClient.SqlClient;
735
+ yield* sql `CREATE TABLE actor_operator_audit (
736
+ routing_key bigint NOT NULL,
737
+ audit_id text NOT NULL,
738
+ at_ms bigint NOT NULL,
739
+ operator text NOT NULL,
740
+ action text NOT NULL,
741
+ tenant_id text NOT NULL,
742
+ actor_type text,
743
+ actor_id text,
744
+ target text,
745
+ capability text,
746
+ reason text,
747
+ outcome text NOT NULL,
748
+ PRIMARY KEY (routing_key, audit_id)
749
+ )`;
750
+ yield* sql `CREATE INDEX actor_operator_audit_tenant ON actor_operator_audit (tenant_id, at_ms)`;
751
+ yield* sql `ALTER TABLE actor_operator_audit ENABLE ROW LEVEL SECURITY`;
752
+ yield* sql `CREATE POLICY durable_tenant ON actor_operator_audit
753
+ USING (tenant_id = current_setting('durable.tenant', true))
754
+ WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
755
+ yield* sql `CREATE VIEW durable.operator_audit AS
756
+ SELECT a.tenant_id, a.actor_type, a.actor_id, a.routing_key, p.placement, a.audit_id,
757
+ a.operator, a.action, a.target, a.capability, a.reason, a.outcome,
758
+ a.at_ms, to_timestamp(a.at_ms::float8 / 1000) AS at
759
+ FROM actor_operator_audit a
760
+ LEFT JOIN actor_placements p ON p.actor_type = a.actor_type`;
761
+ yield* sql `CREATE OR REPLACE VIEW durable.views AS
762
+ SELECT view_name, version FROM (VALUES
763
+ ('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
764
+ ('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
765
+ ('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1),
766
+ ('operator_audit', 1)
767
+ ) AS v(view_name, version)`;
768
+ }),
769
+ "0024_adoption": Effect.gen(function* () {
770
+ const sql = yield* SqlClient.SqlClient;
771
+ const schema = (yield* sql `SELECT quote_ident(current_schema()) AS schema`)[0].schema;
772
+ yield* sql `CREATE TABLE actor_adoptions (
773
+ table_schema text NOT NULL,
774
+ table_name text NOT NULL,
775
+ actor_type text NOT NULL,
776
+ tenant_column text NOT NULL,
777
+ actor_column text NOT NULL,
778
+ mode text NOT NULL CHECK (mode IN ('observe', 'enforce')),
779
+ writer_role text,
780
+ allowed_roles text[] NOT NULL DEFAULT '{}',
781
+ revoked jsonb NOT NULL DEFAULT '[]',
782
+ changed_at_ms bigint NOT NULL,
783
+ changed_by text NOT NULL,
784
+ PRIMARY KEY (table_schema, table_name),
785
+ CHECK ((mode = 'enforce') = (writer_role IS NOT NULL)),
786
+ CHECK (mode = 'enforce' OR cardinality(allowed_roles) = 0)
787
+ )`;
788
+ yield* sql `CREATE TABLE actor_adoption_writes (
789
+ observed_at_ms bigint NOT NULL,
790
+ table_schema text NOT NULL,
791
+ table_name text NOT NULL,
792
+ operation text NOT NULL,
793
+ session_user_name text NOT NULL,
794
+ application_name text NOT NULL,
795
+ in_turn boolean NOT NULL,
796
+ allowed boolean NOT NULL,
797
+ rows bigint NOT NULL
798
+ )`;
799
+ yield* sql `CREATE INDEX actor_adoption_writes_table
800
+ ON actor_adoption_writes (table_schema, table_name, observed_at_ms)`;
801
+ yield* sql.unsafe(`CREATE FUNCTION actor_adoption_observe() RETURNS trigger
802
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $$
803
+ DECLARE
804
+ caller text := CASE WHEN current_setting('role') = 'none'
805
+ THEN session_user::text ELSE current_setting('role') END;
806
+ changed bigint := 0;
807
+ BEGIN
808
+ IF coalesce(current_setting('durable.backfill', true), '') = 'on' AND (
809
+ pg_has_role(caller, (SELECT relowner FROM pg_class WHERE oid = TG_RELID), 'MEMBER')
810
+ OR EXISTS (SELECT 1 FROM ${schema}.actor_adoptions a
811
+ WHERE a.table_schema = TG_TABLE_SCHEMA AND a.table_name = TG_TABLE_NAME
812
+ AND a.changed_by = session_user::text)) THEN
813
+ RETURN NULL;
814
+ END IF;
815
+ IF TG_NARGS > 0 AND caller = TG_ARGV[0] THEN
816
+ RETURN NULL;
817
+ END IF;
818
+ IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
819
+ SELECT count(*) INTO changed FROM new_rows;
820
+ ELSIF TG_OP = 'DELETE' THEN
821
+ SELECT count(*) INTO changed FROM old_rows;
822
+ END IF;
823
+ INSERT INTO ${schema}.actor_adoption_writes (observed_at_ms, table_schema, table_name,
824
+ operation, session_user_name, application_name, in_turn, allowed, rows)
825
+ VALUES (floor(extract(epoch FROM clock_timestamp()) * 1000), TG_TABLE_SCHEMA, TG_TABLE_NAME,
826
+ TG_OP, session_user, current_setting('application_name'),
827
+ coalesce(current_setting('durable.turn', true), '') = 'on',
828
+ TG_NARGS > 1 AND caller = ANY(TG_ARGV[1:TG_NARGS - 1]), changed);
829
+ RETURN NULL;
830
+ END
831
+ $$`);
832
+ yield* sql.unsafe(`CREATE FUNCTION actor_adoption_guard() RETURNS trigger
833
+ LANGUAGE plpgsql AS $$
834
+ DECLARE
835
+ writer text := TG_ARGV[0];
836
+ allowed text[] := string_to_array(TG_ARGV[1], ',');
837
+ tenant_column text := TG_ARGV[2];
838
+ actor_column text := TG_ARGV[3];
839
+ owner text := TG_ARGV[4];
840
+ incoming jsonb;
841
+ BEGIN
842
+ IF TG_OP = 'TRUNCATE' THEN
843
+ IF current_user = ANY(allowed) THEN
844
+ RETURN NULL;
845
+ END IF;
846
+ ELSE
847
+ IF TG_OP = 'UPDATE' THEN
848
+ IF to_jsonb(OLD) -> tenant_column IS DISTINCT FROM to_jsonb(NEW) -> tenant_column
849
+ OR to_jsonb(OLD) -> actor_column IS DISTINCT FROM to_jsonb(NEW) -> actor_column THEN
850
+ RAISE EXCEPTION 'a row of %.% cannot move to another tenant or actor', TG_TABLE_SCHEMA, TG_TABLE_NAME
851
+ USING ERRCODE = '42501';
852
+ END IF;
853
+ END IF;
854
+ IF current_user = writer OR current_user = ANY(allowed) THEN
855
+ IF TG_OP = 'DELETE' THEN
856
+ RETURN OLD;
857
+ END IF;
858
+ incoming := to_jsonb(NEW);
859
+ IF incoming -> 'routing_key' = 'null'::jsonb
860
+ OR incoming -> tenant_column = 'null'::jsonb
861
+ OR incoming -> actor_column = 'null'::jsonb THEN
862
+ RAISE EXCEPTION 'a row of %.% needs routing_key, %, and %', TG_TABLE_SCHEMA, TG_TABLE_NAME,
863
+ tenant_column, actor_column USING ERRCODE = '23514';
864
+ END IF;
865
+ RETURN NEW;
866
+ END IF;
867
+ END IF;
868
+ RAISE EXCEPTION '%.% belongs to actor %; % by % is rejected', TG_TABLE_SCHEMA, TG_TABLE_NAME, owner, TG_OP,
869
+ current_user USING ERRCODE = '42501';
870
+ END
871
+ $$`);
872
+ }),
873
+ /**
874
+ * Per-view state of the fleet maintainer: its source, the hash of the
875
+ * definition that built its rows, its status, and how far it has applied
876
+ * the change feed. Framework state without a tenant, like `actor_tables`,
877
+ * so it carries no policy and no `durable` view.
878
+ */
879
+ "0025_fleet": Effect.gen(function* () {
880
+ const sql = yield* SqlClient.SqlClient;
881
+ yield* sql `CREATE TABLE actor_fleet_views (
882
+ view_name text PRIMARY KEY,
883
+ source_schema text NOT NULL,
884
+ source_table text NOT NULL,
885
+ definition_hash text NOT NULL,
886
+ status text NOT NULL CHECK (status IN ('building', 'ready', 'stale')),
887
+ applied_lsn numeric,
888
+ updated_at_ms bigint NOT NULL,
889
+ last_error text
890
+ )`;
891
+ }),
892
+ /**
893
+ * Staged external work is named a job everywhere it is stored: the outbox kind, keyed-job
894
+ * timer keys, dead-letter columns, payload-version kinds, and the inspection views. Rows keep
895
+ * their ids, attempts, ambiguity, and cancellation marks, so a job in flight settles under the
896
+ * same id. Dropping the renamed views drops any grants on them.
897
+ *
898
+ * A subscription change lowers a tag count in place and deletes a count it
899
+ * lowered to 0 before its transaction commits, so a summary row may hold 0
900
+ * only inside that transaction.
901
+ */
902
+ "0026_jobs": Effect.gen(function* () {
903
+ const sql = yield* SqlClient.SqlClient;
904
+ yield* sql `ALTER TABLE actor_outbox
905
+ DROP CONSTRAINT actor_outbox_kind_check,
906
+ DROP CONSTRAINT actor_outbox_effect_ready`;
907
+ yield* sql `DROP INDEX actor_outbox_running`;
908
+ yield* sql `DROP INDEX actor_outbox_effect_queue`;
909
+ yield* sql `UPDATE actor_outbox SET kind = 'job',
910
+ timer_key = CASE WHEN left(timer_key, 8) = '$effect:' THEN '$job:' || substr(timer_key, 9)
911
+ ELSE timer_key END
912
+ WHERE kind = 'effect'`;
913
+ yield* sql `ALTER TABLE actor_outbox
914
+ ADD CONSTRAINT actor_outbox_kind_check CHECK (kind IN ('intent', 'job', 'feed', 'control')),
915
+ ADD CONSTRAINT actor_outbox_job_ready CHECK (kind <> 'job' OR ready_at_ms IS NOT NULL)`;
916
+ yield* sql `CREATE INDEX actor_outbox_running
917
+ ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command)
918
+ WHERE kind = 'job' AND running`;
919
+ yield* sql `CREATE INDEX actor_outbox_job_queue
920
+ ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command, ready_at_ms, intent_id)
921
+ WHERE kind = 'job' AND NOT running`;
922
+ yield* sql `ALTER TABLE actor_dead_letters RENAME COLUMN effect_id TO job_id`;
923
+ yield* sql `ALTER TABLE actor_dead_letters RENAME COLUMN effect TO job`;
924
+ for (const table of ["actor_payload_versions", "actor_payload_writers"]) {
925
+ yield* sql `ALTER TABLE ${sql(table)} DROP CONSTRAINT ${sql(`${table}_kind_check`)}`;
926
+ yield* sql `UPDATE ${sql(table)} SET kind = 'job' WHERE kind = 'effect'`;
927
+ yield* sql `ALTER TABLE ${sql(table)} ADD CONSTRAINT ${sql(`${table}_kind_check`)}
928
+ CHECK (kind IN ('event', 'job'))`;
929
+ }
930
+ yield* sql `ALTER TABLE actor_subscription_tags DROP CONSTRAINT actor_subscription_tags_rows_check,
931
+ ADD CONSTRAINT actor_subscription_tags_rows_check CHECK (rows >= 0)`;
932
+ yield* sql `DROP VIEW durable.effects`;
933
+ yield* sql `DROP VIEW durable.dead_letters`;
934
+ yield* sql `CREATE VIEW durable.jobs AS
935
+ SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
936
+ o.intent_id AS job_id, o.command AS job, o.payload, o.caller,
937
+ o.attempts, o.last_error, o.ambiguous,
938
+ o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at,
939
+ o.payload_version
940
+ FROM actor_outbox o
941
+ LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
942
+ WHERE o.kind = 'job'`;
943
+ yield* sql `CREATE VIEW durable.dead_letters AS
944
+ SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
945
+ d.job_id, d.job, d.payload, d.attempts, d.cause, d.ambiguous,
946
+ d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at,
947
+ d.payload_version
948
+ FROM actor_dead_letters d
949
+ LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
950
+ yield* sql `CREATE OR REPLACE VIEW durable.views AS
951
+ SELECT view_name, version FROM (VALUES
952
+ ('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
953
+ ('timers', 1), ('jobs', 1), ('dead_letters', 2), ('workflows', 1),
954
+ ('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1),
955
+ ('operator_audit', 1)
956
+ ) AS v(view_name, version)`;
957
+ }),
958
+ "0027_coordination": Effect.gen(function* () {
959
+ const sql = yield* SqlClient.SqlClient;
960
+ yield* sql `CREATE TABLE IF NOT EXISTS actor_coordination (resource text PRIMARY KEY)`;
961
+ }),
962
+ "0029_runner_configuration": Effect.gen(function* () {
963
+ const sql = yield* SqlClient.SqlClient;
964
+ yield* sql `ALTER TABLE actor_deployment ADD COLUMN runner_shards integer,
965
+ ADD COLUMN runner_lock_expiration_ms bigint,
966
+ ADD CONSTRAINT actor_deployment_runner_configuration CHECK (
967
+ (runner_shards IS NULL AND runner_lock_expiration_ms IS NULL) OR
968
+ (runner_shards IS NOT NULL AND
969
+ runner_lock_expiration_ms IS NOT NULL AND runner_lock_expiration_ms >= 3000 AND
970
+ runner_shards BETWEEN 1 AND 65536)
971
+ )`;
972
+ }),
973
+ };
974
+ /**
975
+ * Runs `record` like `Migrator`, but first refuses a database where a
976
+ * registered id below the latest applied one was never applied: `Migrator`
977
+ * would skip it forever, leaving its tables missing. The check runs again
978
+ * after the migrations because a concurrent runner with fewer migrations can
979
+ * commit a higher id between the first check and the migration lock.
980
+ */
981
+ export const migrator = (record) => {
982
+ const run = Migrator.make({})({
983
+ table: "actor_migrations",
984
+ loader: Migrator.fromRecord(record),
985
+ });
986
+ const registered = Object.keys(record).map((key) => Number(key.split("_")[0]));
987
+ const refuseSkipped = Effect.gen(function* () {
988
+ const sql = yield* SqlClient.SqlClient;
989
+ const [table] = yield* sql `SELECT to_regclass('actor_migrations')::text AS name`;
990
+ const rows = table?.name == null
991
+ ? []
992
+ : yield* sql `SELECT migration_id::int AS id FROM actor_migrations`;
993
+ const applied = new Set(rows.map(({ id }) => id));
994
+ const latest = Math.max(0, ...applied);
995
+ const skipped = registered.filter((id) => id < latest && !applied.has(id)).sort((a, b) => a - b);
996
+ if (skipped.length > 0) {
997
+ return yield* new Migrator.MigrationError({
998
+ kind: "BadState",
999
+ message: `Migrations ${skipped.join(", ")} were never applied but migration ${latest} was; they would be skipped. Restore this database from before migration ${latest} or recreate it.`,
1000
+ });
1001
+ }
1002
+ });
1003
+ const transactional = refuseSkipped.pipe(Effect.andThen(run), Effect.tap(() => refuseSkipped));
1004
+ return Effect.gen(function* () {
1005
+ const neki = yield* NekiTurnSessions;
1006
+ const selected = neki
1007
+ ? nekiMigrator({
1008
+ record: Object.fromEntries(Object.entries(record).map(([key, effect]) => [key, Effect.orDie(effect)])),
1009
+ refuseSkipped,
1010
+ })
1011
+ : transactional;
1012
+ return yield* neki || Option.isSome(yield* Effect.serviceOption(PgClient.PgClient))
1013
+ ? withMigrationCoordination(selected)
1014
+ : selected;
1015
+ });
1016
+ };
1017
+ /** Applies every framework migration not yet recorded in `actor_migrations`, refusing a database that skipped one. */
1018
+ export const migrate = migrator(migrations);