@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,813 @@
1
+ import { Cause, Clock, Deferred, Effect, Exit, Fiber, Layer, Schedule, Schema } from "effect";
2
+ import { SqlClient, Statement } from "effect/sql";
3
+ import { Actor, Actors, CurrentCaller, User } from "../../index.js";
4
+ import { CommandConflict, CommandExpired, InvalidCommandId, Unauthorized, } from "../../errors/actor.js";
5
+ import { checkIdentity, databaseTime } from "../../runtime/turn/admission.js";
6
+ import { routingKey } from "../../runtime/storage/codec.js";
7
+ import { ActorTest } from "../actor-test.js";
8
+ import { payloadHash } from "./admission.js";
9
+ class Rejected extends Schema.TaggedError()("Rejected", { amount: Schema.Finite }) {
10
+ }
11
+ const Increment = Actor.command("Increment", { payload: Schema.Finite, success: Schema.Finite });
12
+ const Reject = Actor.command("Reject", { payload: Schema.Finite, error: Rejected });
13
+ const Nested = Actor.command("Nested");
14
+ const Escape = Actor.command("Escape");
15
+ const Hold = Actor.command("Hold");
16
+ const Steal = Actor.command("Steal");
17
+ class Forbidden extends Schema.TaggedError()("Forbidden", { below: Schema.Finite }) {
18
+ }
19
+ const Count = Actor.query("Count", { success: Schema.Finite });
20
+ const AtLeast = Actor.query("AtLeast", {
21
+ payload: Schema.Finite,
22
+ success: Schema.Finite,
23
+ error: Forbidden,
24
+ });
25
+ const Counter = Actor.make("Counter", {
26
+ key: Schema.String,
27
+ state: Actor.state({ count: Schema.Finite.pipe(Schema.withDecodingDefault(Effect.succeed(0))) }),
28
+ api: { Increment, Reject, Nested, Escape, Hold, Steal, Count, AtLeast },
29
+ });
30
+ const CounterReads = (fixture) => Counter.toQueryLayer(Effect.succeed({
31
+ Count: Effect.fnUntraced(function* () {
32
+ fixture.queries += 1;
33
+ yield* fixture.duringQuery.pipe(Effect.orDie);
34
+ return (yield* Counter.Read).state.count;
35
+ }),
36
+ AtLeast: Effect.fnUntraced(function* (minimum) {
37
+ const count = (yield* Counter.Read).state.count;
38
+ if (count < minimum)
39
+ return yield* Forbidden.make({ below: minimum });
40
+ return count;
41
+ }),
42
+ }));
43
+ const CounterLive = (fixture) => Counter.toLayer(Effect.succeed({
44
+ Increment: Effect.fnUntraced(function* (amount) {
45
+ const turn = yield* Counter.Turn;
46
+ fixture.executions += 1;
47
+ yield* turn.state.set({ count: turn.state.count + amount });
48
+ return turn.state.count;
49
+ }),
50
+ Reject: Effect.fnUntraced(function* (amount) {
51
+ const turn = yield* Counter.Turn;
52
+ fixture.executions += 1;
53
+ yield* turn.state.set({ count: 999 });
54
+ return yield* Rejected.make({ amount });
55
+ }),
56
+ Nested: Effect.fnUntraced(function* () {
57
+ const turn = yield* Counter.Turn;
58
+ yield* turn.state.set({ count: 99 });
59
+ yield* fixture.captured.pipe(Effect.orDie);
60
+ }),
61
+ Escape: Effect.fnUntraced(function* () {
62
+ const turn = yield* Counter.Turn;
63
+ fixture.escaped = turn.state.set({ count: 1000 });
64
+ yield* turn.state.set({ count: 3 });
65
+ }),
66
+ Hold: Effect.fnUntraced(function* () {
67
+ const turn = yield* Counter.Turn;
68
+ fixture.escaped = turn.state.set({ count: 1000 });
69
+ yield* fixture.holdHandler;
70
+ yield* turn.state.set({ count: 3 });
71
+ }),
72
+ Steal: () => Effect.suspend(() => fixture.escaped),
73
+ }));
74
+ /** The Counter actor's handlers and queries, which count their runs and run what a case plants. */
75
+ export const counterSuite = {
76
+ fixture: () => ({
77
+ executions: 0,
78
+ queries: 0,
79
+ captured: Effect.succeed(0),
80
+ escaped: Effect.void,
81
+ holdHandler: Effect.void,
82
+ duringQuery: Effect.void,
83
+ }),
84
+ layer: (fixture) => Layer.merge(CounterLive(fixture), CounterReads(fixture)),
85
+ };
86
+ /** Durable-turn cases written against the Counter actor. */
87
+ export const counterConformance = [
88
+ {
89
+ name: "commits state and receipt, replays an identical command effect, and keeps its generation",
90
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
91
+ const test = yield* ActorTest;
92
+ const counter = yield* Counter.get("replay");
93
+ expect(yield* test.inspect(counter.ref)).toEqual({
94
+ generation: undefined,
95
+ state: {},
96
+ receipts: 0,
97
+ events: 0,
98
+ outbox: 0,
99
+ jobs: 0,
100
+ });
101
+ const increment = counter.Increment(7);
102
+ expect(yield* increment).toBe(7);
103
+ expect(yield* increment).toBe(7);
104
+ expect(yield* counter.Increment(3)).toBe(10);
105
+ expect(yield* test.inspect(counter.ref)).toEqual({
106
+ generation: "1",
107
+ state: { count: 10 },
108
+ receipts: 2,
109
+ events: 0,
110
+ outbox: 0,
111
+ jobs: 0,
112
+ });
113
+ })),
114
+ },
115
+ {
116
+ name: "rolls back declared failures and replays their class and payload without executing again",
117
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
118
+ const test = yield* ActorTest;
119
+ const counter = yield* Counter.get("failure");
120
+ expect(yield* counter.Increment(5)).toBe(5);
121
+ const rejected = counter.Reject(13);
122
+ const before = fixture.executions;
123
+ const first = yield* rejected.pipe(Effect.flip);
124
+ expect(first).toBeInstanceOf(Rejected);
125
+ expect(first).toEqual(Rejected.make({ amount: 13 }));
126
+ expect(yield* rejected.pipe(Effect.flip)).toEqual(first);
127
+ expect(fixture.executions - before).toBe(1);
128
+ expect(yield* test.inspect(counter.ref)).toEqual({
129
+ generation: "1",
130
+ state: { count: 5 },
131
+ receipts: 2,
132
+ events: 0,
133
+ outbox: 0,
134
+ jobs: 0,
135
+ });
136
+ })),
137
+ },
138
+ {
139
+ name: "deduplicates concurrent deliveries and rejects changed input or command",
140
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
141
+ const test = yield* ActorTest;
142
+ const counter = yield* Counter.get("concurrent");
143
+ const id = yield* (yield* Actors).mintCommandId;
144
+ const before = fixture.executions;
145
+ expect(yield* Effect.forEach([counter.Increment(11), counter.Increment(11)], (call) => call.pipe(Actor.commandId(id)), { concurrency: 2 })).toEqual([11, 11]);
146
+ expect(fixture.executions - before).toBe(1);
147
+ expect((yield* counter.Increment(12).pipe(Actor.commandId(id), Effect.flip)).reason._tag).toBe("CommandConflict");
148
+ expect(yield* counter.Reject(11).pipe(Actor.commandId(id), Effect.flip)).toMatchObject({
149
+ reason: CommandConflict.make({ commandId: id }),
150
+ });
151
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
152
+ state: { count: 11 },
153
+ receipts: 1,
154
+ });
155
+ })),
156
+ },
157
+ {
158
+ name: "captures callers, preserves same-subject access, and never partitions deduplication by caller",
159
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
160
+ const test = yield* ActorTest;
161
+ const alice = yield* Counter.get("privacy");
162
+ const bob = yield* Counter.get("privacy").pipe(Actor.as(User.make({ subject: "bob" })));
163
+ const id = yield* (yield* Actors).mintCommandId;
164
+ expect(yield* alice
165
+ .Increment(17)
166
+ .pipe(Actor.commandId(id), Effect.provideService(CurrentCaller, User.make({ subject: "bob" })))).toBe(17);
167
+ const rotated = yield* Counter.get("privacy").pipe(Actor.as(User.make({ subject: "alice" })));
168
+ expect(yield* rotated.Increment(17).pipe(Actor.commandId(id))).toBe(17);
169
+ expect(yield* bob.Increment(17).pipe(Actor.commandId(id), Effect.flip)).toMatchObject({
170
+ reason: Unauthorized.make({ code: "receipt_access_denied" }),
171
+ });
172
+ const otherTenant = yield* Counter.get("privacy").pipe(Actor.tenant("other"), Actor.as(User.make({ subject: "bob" })));
173
+ expect(yield* otherTenant.Increment(29).pipe(Actor.commandId(id))).toBe(29);
174
+ expect(yield* test.inspect(alice.ref)).toMatchObject({
175
+ state: { count: 17 },
176
+ receipts: 1,
177
+ });
178
+ })),
179
+ },
180
+ {
181
+ name: "recovers beforeHandler crashes with the same command and one committed transition",
182
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
183
+ const test = yield* ActorTest;
184
+ const counter = yield* Counter.get("crash-beforeHandler");
185
+ const before = fixture.executions;
186
+ yield* test.crashNext("beforeHandler");
187
+ expect(yield* counter.Increment(23)).toBe(23);
188
+ expect(fixture.executions - before).toBe(1);
189
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
190
+ state: { count: 23 },
191
+ receipts: 1,
192
+ });
193
+ })),
194
+ },
195
+ {
196
+ name: "recovers beforeCommit crashes with the same command and one committed transition",
197
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
198
+ const test = yield* ActorTest;
199
+ const counter = yield* Counter.get("crash-beforeCommit");
200
+ const before = fixture.executions;
201
+ yield* test.crashNext("beforeCommit");
202
+ expect(yield* counter.Increment(23)).toBe(23);
203
+ expect(fixture.executions - before).toBe(2);
204
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
205
+ state: { count: 23 },
206
+ receipts: 1,
207
+ });
208
+ })),
209
+ },
210
+ {
211
+ name: "recovers afterCommit crashes with the same command and one committed transition",
212
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
213
+ const test = yield* ActorTest;
214
+ const counter = yield* Counter.get("crash-afterCommit");
215
+ const before = fixture.executions;
216
+ yield* test.crashNext("afterCommit");
217
+ expect(yield* counter.Increment(23)).toBe(23);
218
+ expect(fixture.executions - before).toBe(1);
219
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
220
+ state: { count: 23 },
221
+ receipts: 1,
222
+ });
223
+ })),
224
+ },
225
+ {
226
+ name: "keeps retries prompt after many crashed turns of one actor type",
227
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
228
+ const test = yield* ActorTest;
229
+ for (let index = 0; index < 12; index++) {
230
+ yield* test.crashNext("beforeCommit");
231
+ yield* (yield* Counter.get(`crash-many-${index}`)).Increment(1);
232
+ }
233
+ yield* test.crashNext("beforeCommit");
234
+ const started = yield* Clock.currentTimeMillis;
235
+ expect(yield* (yield* Counter.get("crash-many-last")).Increment(2)).toBe(2);
236
+ expect((yield* Clock.currentTimeMillis) - started < 2_000).toBe(true);
237
+ })),
238
+ },
239
+ {
240
+ name: "backs off an actor whose turn dies on every attempt, and only that actor",
241
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
242
+ const test = yield* ActorTest;
243
+ const crashing = yield* Counter.get("crash-always");
244
+ const elapsed = (effect) => Effect.gen(function* () {
245
+ const started = yield* Clock.currentTimeMillis;
246
+ const value = yield* effect;
247
+ return { value, ms: (yield* Clock.currentTimeMillis) - started };
248
+ });
249
+ for (let crash = 0; crash < 5; crash++)
250
+ yield* test.crashNext("beforeCommit");
251
+ const before = fixture.executions;
252
+ const backedOff = yield* elapsed(crashing.Increment(1));
253
+ expect(backedOff.value).toBe(1);
254
+ expect(fixture.executions - before).toBe(6);
255
+ expect(backedOff.ms >= 1_500).toBe(true);
256
+ yield* test.crashNext("beforeCommit");
257
+ const other = yield* elapsed((yield* Counter.get("crash-always-other")).Increment(2));
258
+ expect(other.value).toBe(2);
259
+ expect(other.ms < 1_000).toBe(true);
260
+ yield* test.crashNext("beforeCommit");
261
+ const reset = yield* elapsed(crashing.Increment(1));
262
+ expect(reset.value).toBe(2);
263
+ expect(reset.ms < 1_000).toBe(true);
264
+ })),
265
+ },
266
+ {
267
+ name: "does not cancel an accepted turn with its waiter",
268
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
269
+ const test = yield* ActorTest;
270
+ const counter = yield* Counter.get("disconnect");
271
+ const pause = yield* test.pauseNext("beforeCommit");
272
+ const call = counter.Increment(31);
273
+ const waiter = yield* call.pipe(Effect.forkChild);
274
+ yield* pause.reached;
275
+ yield* Fiber.interrupt(waiter);
276
+ yield* pause.release;
277
+ expect(yield* call).toBe(31);
278
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
279
+ state: { count: 31 },
280
+ receipts: 1,
281
+ });
282
+ })),
283
+ },
284
+ {
285
+ name: "keeps uncommitted state invisible to a second connection",
286
+ requiresIndependentConnections: true,
287
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
288
+ const test = yield* ActorTest;
289
+ const counter = yield* Counter.get("disconnect-visibility");
290
+ const pause = yield* test.pauseNext("beforeCommit");
291
+ const call = counter.Increment(31);
292
+ const waiter = yield* call.pipe(Effect.forkChild);
293
+ yield* pause.reached;
294
+ expect(yield* test.inspect(counter.ref)).toEqual({
295
+ generation: undefined,
296
+ state: {},
297
+ receipts: 0,
298
+ events: 0,
299
+ outbox: 0,
300
+ jobs: 0,
301
+ });
302
+ yield* Fiber.interrupt(waiter);
303
+ yield* pause.release;
304
+ expect(yield* call).toBe(31);
305
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
306
+ state: { count: 31 },
307
+ receipts: 1,
308
+ });
309
+ })),
310
+ },
311
+ {
312
+ name: "compresses state, keys rows by routing_key, and reads state once per activation",
313
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
314
+ const test = yield* ActorTest;
315
+ const counter = yield* Counter.get("warm");
316
+ const sql = yield* SqlClient.SqlClient;
317
+ expect(yield* counter.Increment(1)).toBe(1);
318
+ const stored = yield* sql `
319
+ SELECT routing_key::text AS routing_key, encode(substring(value FROM 1 FOR 4), 'hex') AS magic
320
+ FROM actor_state WHERE tenant_id = ${counter.ref.tenant} AND actor_id = 'warm'`;
321
+ expect(stored).toEqual([
322
+ {
323
+ routing_key: String(routingKey({ ref: counter.ref, placement: "tenant" })),
324
+ magic: "28b52ffd",
325
+ },
326
+ ]);
327
+ const statements = [];
328
+ const recorded = (effect) => effect.pipe(Effect.provideService(Statement.CurrentTransformer, (statement) => Effect.sync(() => {
329
+ statements.push(statement.compile()[0]);
330
+ return statement;
331
+ })));
332
+ for (const amount of [2, 3, 4])
333
+ yield* recorded(counter.Increment(amount));
334
+ expect(statements.some((text) => /INSERT INTO actor_receipts/.test(text))).toBe(true);
335
+ const stateReads = statements.filter((text) => /SELECT key, value FROM actor_state/.test(text));
336
+ expect(stateReads).toEqual([]);
337
+ yield* test.invalidate(counter.ref);
338
+ statements.length = 0;
339
+ expect(yield* recorded(counter.Increment(5))).toBe(15);
340
+ expect(statements.filter((text) => /SELECT key, value FROM actor_state/.test(text)).length).toBe(1);
341
+ })),
342
+ },
343
+ {
344
+ name: "queries read committed state without activating, fencing, or receipting the actor",
345
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
346
+ const test = yield* ActorTest;
347
+ const counter = yield* Counter.get("query-cold");
348
+ expect(yield* counter.Count()).toBe(0);
349
+ expect(yield* test.inspect(counter.ref)).toEqual({
350
+ generation: undefined,
351
+ state: {},
352
+ receipts: 0,
353
+ events: 0,
354
+ outbox: 0,
355
+ jobs: 0,
356
+ });
357
+ expect(yield* counter.Increment(4)).toBe(4);
358
+ yield* test.invalidate(counter.ref);
359
+ const generation = (yield* test.inspect(counter.ref)).generation;
360
+ expect(yield* counter.Count()).toBe(4);
361
+ expect(yield* counter.AtLeast(4)).toBe(4);
362
+ expect(yield* counter.AtLeast(5).pipe(Effect.flip)).toEqual(Forbidden.make({ below: 5 }));
363
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
364
+ generation,
365
+ state: { count: 4 },
366
+ receipts: 1,
367
+ });
368
+ })),
369
+ },
370
+ {
371
+ name: "queries never observe a running turn's uncommitted state",
372
+ requiresIndependentConnections: true,
373
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
374
+ const test = yield* ActorTest;
375
+ const counter = yield* Counter.get("query-isolation");
376
+ expect(yield* counter.Increment(2)).toBe(2);
377
+ const pause = yield* test.pauseNext("beforeCommit");
378
+ const writer = yield* counter.Increment(40).pipe(Effect.forkChild);
379
+ yield* pause.reached;
380
+ expect(yield* counter.Count()).toBe(2);
381
+ yield* pause.release;
382
+ expect(yield* Fiber.join(writer)).toBe(42);
383
+ expect(yield* counter.Count()).toBe(42);
384
+ })),
385
+ },
386
+ {
387
+ name: "applies the caller authorization to queries",
388
+ run: ({ expect, environment, fixture, access }) => environment.run(Effect.gen(function* () {
389
+ const counter = yield* Counter.get("query-denied");
390
+ const before = fixture.queries;
391
+ access.allowed = false;
392
+ expect(yield* counter.Count().pipe(Effect.flip)).toMatchObject({
393
+ reason: Unauthorized.make({ code: "access_denied" }),
394
+ });
395
+ expect(fixture.queries).toBe(before);
396
+ }).pipe(Effect.ensuring(Effect.sync(() => {
397
+ access.allowed = true;
398
+ })))),
399
+ },
400
+ {
401
+ name: "withholds a query result from a caller revoked while the handler ran",
402
+ run: ({ expect, environment, fixture, access }) => environment.run(Effect.gen(function* () {
403
+ const counter = yield* Counter.get("query-revoked");
404
+ expect(yield* counter.Increment(6)).toBe(6);
405
+ fixture.duringQuery = Effect.sync(() => {
406
+ access.allowed = false;
407
+ });
408
+ const before = fixture.queries;
409
+ expect(yield* counter.Count().pipe(Effect.flip)).toMatchObject({
410
+ reason: Unauthorized.make({ code: "access_denied" }),
411
+ });
412
+ expect(fixture.queries).toBe(before + 1);
413
+ }).pipe(Effect.ensuring(Effect.sync(() => {
414
+ access.allowed = true;
415
+ fixture.duringQuery = Effect.void;
416
+ })))),
417
+ },
418
+ {
419
+ name: "rejects request/reply calls from a query handler without writing",
420
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
421
+ const test = yield* ActorTest;
422
+ const counter = yield* Counter.get("query-guard");
423
+ fixture.duringQuery = counter.Increment(100);
424
+ const exit = yield* counter.Count().pipe(Effect.exit);
425
+ expect(Exit.isFailure(exit) && Cause.pretty(exit.cause)).toContain("Request/reply inside a turn");
426
+ expect(yield* test.inspect(counter.ref)).toEqual({
427
+ generation: undefined,
428
+ state: {},
429
+ receipts: 0,
430
+ events: 0,
431
+ outbox: 0,
432
+ jobs: 0,
433
+ });
434
+ }).pipe(Effect.ensuring(Effect.sync(() => {
435
+ fixture.duringQuery = Effect.void;
436
+ })))),
437
+ },
438
+ {
439
+ name: "rejects a stale generation before rerunning the handler under new authority",
440
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
441
+ const test = yield* ActorTest;
442
+ const counter = yield* Counter.get("stale");
443
+ expect(yield* counter.Increment(5)).toBe(5);
444
+ yield* test.invalidate(counter.ref);
445
+ const before = fixture.executions;
446
+ expect(yield* counter.Increment(8)).toBe(13);
447
+ expect(fixture.executions - before).toBe(1);
448
+ expect(yield* test.inspect(counter.ref)).toEqual({
449
+ generation: "3",
450
+ state: { count: 13 },
451
+ receipts: 2,
452
+ events: 0,
453
+ outbox: 0,
454
+ jobs: 0,
455
+ });
456
+ })),
457
+ },
458
+ {
459
+ name: "rolls back captured request/reply misuse and rejects escaped state capabilities",
460
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
461
+ const test = yield* ActorTest;
462
+ const counter = yield* Counter.get("guard");
463
+ fixture.captured = counter.Increment(100);
464
+ const exit = yield* counter.Nested().pipe(Effect.exit);
465
+ expect(Exit.isFailure(exit) && Cause.pretty(exit.cause)).toContain("Request/reply inside a turn");
466
+ expect(yield* test.inspect(counter.ref)).toEqual({
467
+ generation: undefined,
468
+ state: {},
469
+ receipts: 0,
470
+ events: 0,
471
+ outbox: 0,
472
+ jobs: 0,
473
+ });
474
+ yield* counter.Escape();
475
+ const escapedExit = yield* fixture.escaped.pipe(Effect.exit);
476
+ expect(Exit.isFailure(escapedExit) && Cause.pretty(escapedExit.cause)).toContain("State capability escaped its turn");
477
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
478
+ state: { count: 3 },
479
+ receipts: 1,
480
+ });
481
+ })),
482
+ },
483
+ {
484
+ name: "rejects a state setter from another still-active actor turn",
485
+ requiresIndependentConnections: true,
486
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
487
+ const owner = yield* Counter.get("capability-owner");
488
+ const thief = yield* Counter.get("capability-thief");
489
+ const test = yield* ActorTest;
490
+ const ready = yield* Deferred.make();
491
+ const release = yield* Deferred.make();
492
+ fixture.holdHandler = Deferred.succeed(ready, undefined).pipe(Effect.andThen(Deferred.await(release)));
493
+ const holder = yield* owner.Hold().pipe(Effect.forkChild);
494
+ yield* Deferred.await(ready);
495
+ const stolen = yield* thief
496
+ .Steal()
497
+ .pipe(Effect.exit, Effect.ensuring(Deferred.succeed(release, undefined)));
498
+ expect(Exit.isFailure(stolen) && Cause.pretty(stolen.cause)).toContain("State capability escaped its turn");
499
+ yield* Fiber.join(holder);
500
+ expect(yield* test.inspect(owner.ref)).toMatchObject({
501
+ state: { count: 3 },
502
+ receipts: 1,
503
+ });
504
+ expect(yield* test.inspect(thief.ref)).toEqual({
505
+ generation: undefined,
506
+ state: {},
507
+ receipts: 0,
508
+ events: 0,
509
+ outbox: 0,
510
+ jobs: 0,
511
+ });
512
+ })),
513
+ },
514
+ {
515
+ name: "denies a competing caller admitted while the original failure is still uncommitted",
516
+ requiresIndependentConnections: true,
517
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
518
+ const test = yield* ActorTest;
519
+ const alice = yield* Counter.get("caller-race");
520
+ const bob = yield* Counter.get("caller-race").pipe(Actor.as(User.make({ subject: "bob" })));
521
+ const id = yield* (yield* Actors).mintCommandId;
522
+ const committing = yield* test.pauseNext("beforeCommit");
523
+ const before = fixture.executions;
524
+ const original = yield* alice
525
+ .Reject(71)
526
+ .pipe(Actor.commandId(id), Effect.flip, Effect.forkChild);
527
+ yield* committing.reached;
528
+ const delivering = yield* test.pauseNext("beforeDelivery");
529
+ const competitor = yield* bob
530
+ .Reject(71)
531
+ .pipe(Actor.commandId(id), Effect.flip, Effect.forkChild);
532
+ yield* delivering.reached;
533
+ yield* committing.release;
534
+ expect(yield* Fiber.join(original)).toEqual(Rejected.make({ amount: 71 }));
535
+ yield* delivering.release;
536
+ expect(yield* Fiber.join(competitor)).toMatchObject({
537
+ reason: Unauthorized.make({ code: "receipt_access_denied" }),
538
+ });
539
+ expect(fixture.executions - before).toBe(1);
540
+ expect(yield* test.inspect(alice.ref)).toMatchObject({ state: {}, receipts: 1 });
541
+ })),
542
+ },
543
+ {
544
+ name: "refuses to reinterpret retained identities under a changed retry window",
545
+ run: ({ expect, environment }) => Effect.runPromise(Effect.gen(function* () {
546
+ yield* environment.stop;
547
+ const incompatible = environment.build({ retryWindowMs: 60_001 });
548
+ const exit = yield* Effect.promise(() => incompatible.runPromiseExit(Effect.void));
549
+ yield* Effect.promise(() => incompatible.dispose());
550
+ yield* environment.restart;
551
+ expect(Exit.isFailure(exit) && Cause.pretty(exit.cause)).toContain("differs from the deployment");
552
+ })),
553
+ },
554
+ {
555
+ name: "revokes external access without cancelling an in-flight command or its retry",
556
+ run: ({ expect, environment, access }) => environment.run(Effect.gen(function* () {
557
+ const test = yield* ActorTest;
558
+ const counter = yield* Counter.get("revoked");
559
+ const id = yield* (yield* Actors).mintCommandId;
560
+ const pause = yield* test.pauseNext("beforeHandler");
561
+ yield* test.crashNext("beforeCommit");
562
+ const call = counter.Increment(37).pipe(Actor.commandId(id));
563
+ const waiter = yield* call.pipe(Effect.result, Effect.forkChild);
564
+ yield* pause.reached;
565
+ access.allowed = false;
566
+ yield* pause.release;
567
+ const result = yield* Fiber.join(waiter);
568
+ expect(result).toMatchObject({
569
+ failure: { reason: Unauthorized.make({ code: "access_denied" }) },
570
+ });
571
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
572
+ state: { count: 37 },
573
+ receipts: 1,
574
+ });
575
+ expect(yield* counter.Increment(1).pipe(Effect.flip)).toMatchObject({
576
+ reason: Unauthorized.make({ code: "access_denied" }),
577
+ });
578
+ expect(yield* call.pipe(Effect.flip)).toMatchObject({
579
+ reason: Unauthorized.make({ code: "access_denied" }),
580
+ });
581
+ access.allowed = true;
582
+ expect(yield* call).toBe(37);
583
+ }).pipe(Effect.ensuring(Effect.sync(() => {
584
+ access.allowed = true;
585
+ })))),
586
+ },
587
+ {
588
+ name: "defines exact expiry boundaries and rejects invalid/future identities",
589
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
590
+ const id = "v1.1000.6000.17b3670b-3f17-4a9b-aade-037e1dd1bba8";
591
+ yield* checkIdentity(id, 5000, 5999);
592
+ for (const now of [6000, 6001])
593
+ expect(yield* checkIdentity(id, 5000, now).pipe(Effect.flip)).toMatchObject({
594
+ reason: CommandExpired.make({ commandId: id }),
595
+ });
596
+ expect(yield* checkIdentity(id, 5000, 999).pipe(Effect.flip)).toMatchObject({
597
+ reason: InvalidCommandId.make({ commandId: id, code: "future" }),
598
+ });
599
+ expect(yield* checkIdentity(id.replace("6000", "7000"), 5000, 1000).pipe(Effect.flip)).toMatchObject({
600
+ reason: InvalidCommandId.make({
601
+ commandId: id.replace("6000", "7000"),
602
+ code: "window",
603
+ }),
604
+ });
605
+ const counter = yield* Counter.get("expiry-first");
606
+ const before = fixture.executions;
607
+ const expired = id.replace("6000", "61000");
608
+ expect(yield* counter.Increment(41).pipe(Actor.commandId(expired), Effect.flip)).toMatchObject({
609
+ reason: CommandExpired.make({ commandId: expired }),
610
+ });
611
+ expect(fixture.executions).toBe(before);
612
+ })),
613
+ },
614
+ {
615
+ name: "canonicalizes object keys but preserves array order in payload hashes",
616
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
617
+ const first = yield* payloadHash('{"value":{"b":2,"a":[3,7]}}');
618
+ expect(yield* payloadHash('{"value":{"a":[3,7],"b":2}}')).toBe(first);
619
+ expect(yield* payloadHash('{"value":{"a":[7,3],"b":2}}')).not.toBe(first);
620
+ })),
621
+ },
622
+ {
623
+ name: "decodes regclass so the migrator can reopen the database",
624
+ requiresIndependentConnections: true,
625
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
626
+ const sql = yield* SqlClient.SqlClient;
627
+ expect(yield* sql `SELECT 128::regclass AS value`).toEqual([{ value: 128 }]);
628
+ })),
629
+ },
630
+ {
631
+ name: "recovers a declared failure beforeCommit without persisting dirty state",
632
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
633
+ const test = yield* ActorTest;
634
+ const counter = yield* Counter.get("failure-beforeCommit");
635
+ yield* counter.Increment(19);
636
+ const before = fixture.executions;
637
+ const call = counter.Reject(53);
638
+ yield* test.crashNext("beforeCommit");
639
+ expect(yield* call.pipe(Effect.flip)).toEqual(Rejected.make({ amount: 53 }));
640
+ expect(fixture.executions - before).toBe(2);
641
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
642
+ state: { count: 19 },
643
+ receipts: 2,
644
+ });
645
+ yield* counter.Increment(2);
646
+ expect(yield* call.pipe(Effect.flip)).toEqual(Rejected.make({ amount: 53 }));
647
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
648
+ state: { count: 21 },
649
+ receipts: 3,
650
+ });
651
+ })),
652
+ },
653
+ {
654
+ name: "recovers a declared failure afterCommit without persisting dirty state",
655
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
656
+ const test = yield* ActorTest;
657
+ const counter = yield* Counter.get("failure-afterCommit");
658
+ yield* counter.Increment(19);
659
+ const before = fixture.executions;
660
+ const call = counter.Reject(53);
661
+ yield* test.crashNext("afterCommit");
662
+ expect(yield* call.pipe(Effect.flip)).toEqual(Rejected.make({ amount: 53 }));
663
+ expect(fixture.executions - before).toBe(1);
664
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
665
+ state: { count: 19 },
666
+ receipts: 2,
667
+ });
668
+ yield* counter.Increment(2);
669
+ expect(yield* call.pipe(Effect.flip)).toEqual(Rejected.make({ amount: 53 }));
670
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
671
+ state: { count: 21 },
672
+ receipts: 3,
673
+ });
674
+ })),
675
+ },
676
+ {
677
+ name: "retries a real generation lock timeout without entering the handler",
678
+ requiresIndependentConnections: true,
679
+ timeoutMs: 15_000,
680
+ run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
681
+ const counter = yield* Counter.get("locked");
682
+ const test = yield* ActorTest;
683
+ yield* counter.Increment(2);
684
+ const connect = environment.connect;
685
+ if (connect === undefined)
686
+ return yield* Effect.die(new Error("backend lacks independent connections"));
687
+ const lock = yield* connect;
688
+ yield* lock.query("BEGIN");
689
+ yield* lock.query("SELECT generation FROM actor_generations WHERE tenant_id = $1 AND actor_type = $2 AND actor_id = $3 FOR UPDATE", [counter.ref.tenant, counter.ref.actor, counter.ref.id]);
690
+ yield* Effect.gen(function* () {
691
+ const before = fixture.executions;
692
+ const waiter = yield* counter.Increment(59).pipe(Effect.forkChild);
693
+ const waitingAttempt = lock.query("SELECT pg_stat_clear_snapshot()").pipe(Effect.andThen(lock.query(`SELECT pid, query_start::text AS query_start
694
+ FROM pg_stat_activity
695
+ WHERE datname = current_database()
696
+ AND pid <> pg_backend_pid()
697
+ AND wait_event_type = 'Lock'
698
+ AND query LIKE '%actor_generations%FOR UPDATE%'`)), Effect.map((rows) => rows[0]));
699
+ const first = yield* waitingAttempt.pipe(Effect.repeat({
700
+ while: (attempt) => attempt === undefined,
701
+ schedule: Schedule.spaced("10 millis"),
702
+ }), Effect.timeout("5 seconds"));
703
+ const retry = yield* waitingAttempt.pipe(Effect.repeat({
704
+ while: (attempt) => attempt === undefined ||
705
+ (attempt.pid === first.pid && attempt.query_start === first.query_start),
706
+ schedule: Schedule.spaced("10 millis"),
707
+ }), Effect.timeout("5 seconds"));
708
+ expect(retry).not.toEqual(first);
709
+ expect(fixture.executions).toBe(before);
710
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
711
+ state: { count: 2 },
712
+ receipts: 1,
713
+ });
714
+ yield* lock.query("COMMIT");
715
+ expect(yield* Fiber.join(waiter)).toBe(61);
716
+ expect(fixture.executions - before).toBe(1);
717
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
718
+ state: { count: 61 },
719
+ receipts: 2,
720
+ });
721
+ }).pipe(Effect.ensuring(lock.query("ROLLBACK")));
722
+ })),
723
+ },
724
+ {
725
+ name: "completes an in-flight command past expiry but refuses the external outcome",
726
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
727
+ const counter = yield* Counter.get("expired-pending");
728
+ const test = yield* ActorTest;
729
+ const pause = yield* test.pauseNext("beforeHandler");
730
+ yield* test.crashNext("beforeCommit");
731
+ const now = yield* databaseTime;
732
+ const id = `v1.${now - 59_500}.${now + 500}.17b3670b-3f17-4a9b-aade-037e1dd1bba8`;
733
+ const waiter = yield* counter
734
+ .Increment(67)
735
+ .pipe(Actor.commandId(id), Effect.flip, Effect.forkChild);
736
+ yield* pause.reached;
737
+ yield* Effect.sleep("550 millis");
738
+ yield* pause.release;
739
+ expect(yield* Fiber.join(waiter)).toMatchObject({
740
+ reason: CommandExpired.make({ commandId: id }),
741
+ });
742
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
743
+ state: { count: 67 },
744
+ receipts: 1,
745
+ });
746
+ })),
747
+ },
748
+ {
749
+ name: "rejects an expired identity after receipt pruning and runtime restart",
750
+ run: ({ expect, environment }) => Effect.runPromise(Effect.gen(function* () {
751
+ const saved = yield* Effect.promise(() => environment.run(Effect.gen(function* () {
752
+ const counter = yield* Counter.get("restart");
753
+ const now = yield* databaseTime;
754
+ const id = `v1.${now - 59_500}.${now + 500}.17b3670b-3f17-4a9b-aade-037e1dd1bba8`;
755
+ expect(yield* counter.Increment(43).pipe(Actor.commandId(id))).toBe(43);
756
+ const sql = yield* SqlClient.SqlClient;
757
+ yield* Effect.sleep("550 millis");
758
+ yield* sql `DELETE FROM actor_receipts WHERE tenant_id = ${counter.ref.tenant} AND actor_type = ${counter.ref.actor} AND actor_id = ${counter.ref.id}`;
759
+ return { ref: counter.ref, id };
760
+ })));
761
+ yield* environment.restart;
762
+ yield* Effect.promise(() => environment.run(Effect.gen(function* () {
763
+ const counter = yield* Counter.get(saved.ref.id).pipe(Actor.tenant(saved.ref.tenant));
764
+ expect(yield* counter.Increment(43).pipe(Actor.commandId(saved.id), Effect.flip)).toMatchObject({ reason: CommandExpired.make({ commandId: saved.id }) });
765
+ const test = yield* ActorTest;
766
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
767
+ state: { count: 43 },
768
+ receipts: 0,
769
+ });
770
+ expect(yield* counter.Increment(2)).toBe(45);
771
+ })));
772
+ })),
773
+ },
774
+ {
775
+ name: "isolates durable state between fresh layer builds",
776
+ requiresFreshDatabase: true,
777
+ run: ({ expect, environment }) => Effect.runPromise(Effect.gen(function* () {
778
+ const tenant = yield* Effect.promise(() => environment.run(Effect.gen(function* () {
779
+ const counter = yield* Counter.get("isolated");
780
+ expect(yield* counter.Increment(4)).toBe(4);
781
+ const test = yield* ActorTest;
782
+ return test.tenant;
783
+ })));
784
+ const database = yield* environment.freshDatabase;
785
+ const isolated = yield* Effect.acquireRelease(Effect.sync(() => environment.build({ database })), (runtime) => Effect.promise(() => runtime.dispose()));
786
+ yield* Effect.promise(() => isolated.runPromise(Effect.gen(function* () {
787
+ const counter = yield* Counter.get("isolated").pipe(Actor.tenant(tenant));
788
+ const test = yield* ActorTest;
789
+ expect(yield* test.inspect(counter.ref)).toEqual({
790
+ generation: undefined,
791
+ state: {},
792
+ receipts: 0,
793
+ events: 0,
794
+ outbox: 0,
795
+ jobs: 0,
796
+ });
797
+ expect(yield* counter.Increment(6)).toBe(6);
798
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
799
+ state: { count: 6 },
800
+ receipts: 1,
801
+ });
802
+ })));
803
+ yield* Effect.promise(() => environment.run(Effect.gen(function* () {
804
+ const counter = yield* Counter.get("isolated").pipe(Actor.tenant(tenant));
805
+ const test = yield* ActorTest;
806
+ expect(yield* test.inspect(counter.ref)).toMatchObject({
807
+ state: { count: 4 },
808
+ receipts: 1,
809
+ });
810
+ })));
811
+ }).pipe(Effect.scoped)),
812
+ },
813
+ ];