@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,650 @@
1
+ import { Context, Crypto, Data, Effect, Exit, Layer, Option, Schedule, Schema } from "effect";
2
+ import { FetchHttpClient, Headers, HttpClient, HttpClientRequest, HttpRouter } from "effect/http";
3
+ import { SqlClient } from "effect/sql";
4
+ import { Actor, Intent, Unauthorized, User } from "../../index.js";
5
+ import { Inspector } from "../../runtime/index.js";
6
+ import * as Queries from "../../runtime/inspector/queries.js";
7
+ import { ActorTest } from "../actor-test.js";
8
+ import { Auth } from "../../runtime/index.js";
9
+ import { serveFetch, zstdDecompress } from "./platform.js";
10
+ const Noted = Actor.event("Noted", { body: Schema.String });
11
+ class Rejected extends Schema.TaggedError()("Rejected", {}) {
12
+ }
13
+ class Undeliverable extends Schema.TaggedError()("Undeliverable", {}) {
14
+ }
15
+ const Notify = Actor.job("Notify", { payload: { body: Schema.String } });
16
+ const Write = Actor.command("Write", { payload: Schema.String });
17
+ const WriteThenReject = Actor.command("WriteThenReject", {
18
+ payload: Schema.String,
19
+ error: Rejected,
20
+ });
21
+ const WriteThenDie = Actor.command("WriteThenDie", { payload: Schema.String });
22
+ const Nudge = Actor.command("Nudge");
23
+ const Settle = Actor.workflow("Settle", {
24
+ payload: { order: Schema.String },
25
+ success: Schema.String,
26
+ key: ({ order }) => order,
27
+ });
28
+ const Reserve = Settle.step("reserve", { payload: Schema.String, success: Schema.String });
29
+ const Pause = Settle.sleep("pause");
30
+ const Inspected = Actor.make("Inspected", {
31
+ key: Schema.String,
32
+ state: Actor.state({
33
+ notes: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
34
+ }),
35
+ events: [Noted],
36
+ jobs: { Notify: { job: Notify, retry: { times: 0 } } },
37
+ api: { Write, WriteThenReject, WriteThenDie, Settle },
38
+ internal: { Nudge },
39
+ });
40
+ const write = Effect.fnUntraced(function* (body) {
41
+ const turn = yield* Inspected.Turn;
42
+ yield* turn.state.set({ notes: [...turn.state.notes, body] });
43
+ yield* turn.emit(Noted.make({ body }));
44
+ const self = yield* Inspected.intents(turn.id);
45
+ yield* self.Nudge().pipe(Intent.after("1 hour"), Intent.key("nudge"));
46
+ yield* turn.enqueue(Notify.make({ body }));
47
+ });
48
+ export const inspectorLayer = Layer.mergeAll(Inspected.toLayer(Effect.succeed({
49
+ Write: write,
50
+ WriteThenReject: Effect.fnUntraced(function* (body) {
51
+ yield* write(body);
52
+ return yield* Rejected.make({});
53
+ }),
54
+ WriteThenDie: Effect.fnUntraced(function* (body) {
55
+ yield* write(body);
56
+ return yield* Effect.die(new Error("Inspected defect after writing"));
57
+ }),
58
+ Nudge: () => Effect.void,
59
+ Settle: Effect.fnUntraced(function* ({ order }) {
60
+ const reserved = yield* Reserve.run(order, (id) => Effect.succeed(`held-${id}`));
61
+ yield* Pause("10 seconds");
62
+ return `${reserved}:settled`;
63
+ }),
64
+ })), Inspected.toJobLayer(Effect.succeed({
65
+ Notify: Effect.fnUntraced(function* () {
66
+ return yield* Undeliverable.make({});
67
+ }),
68
+ })));
69
+ /** How many requests reached the operators' provider. */
70
+ const reached = { count: 0 };
71
+ const operators = Auth.make((request) => Option.match(Headers.get(request.headers, "authorization").pipe(Option.map((header) => {
72
+ reached.count += 1;
73
+ return header;
74
+ })), {
75
+ onNone: () => Effect.fail(Unauthorized.make({ code: "missing_credentials" })),
76
+ onSome: (header) => {
77
+ const match = /^Bearer ([A-Za-z0-9._:-]+)$/.exec(header);
78
+ return match === null
79
+ ? Effect.fail(Unauthorized.make({ code: "invalid_credentials" }))
80
+ : Effect.succeed({ tenant: match[1], caller: User.make({ subject: "operator" }) });
81
+ },
82
+ }));
83
+ const decodeJson = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Json));
84
+ /** Serves the inspector from a real listening server for the rest of the scope. */
85
+ const serveInspector = Effect.gen(function* () {
86
+ const context = yield* Effect.context();
87
+ const web = HttpRouter.toWebHandler(Inspector.serve({ auth: operators }).pipe(Layer.provide(Layer.succeedContext(context))), { disableLogger: true });
88
+ yield* Effect.addFinalizer(() => Effect.promise(() => web.dispose()));
89
+ const port = yield* serveFetch((request) => web.handler(request));
90
+ const client = Context.get(yield* Layer.build(FetchHttpClient.layer), HttpClient.HttpClient);
91
+ const url = `http://127.0.0.1:${port}`;
92
+ return (path, tenant, origin) => Effect.gen(function* () {
93
+ const request = HttpClientRequest.get(`${url}/inspector${path}`, {
94
+ headers: origin === undefined ? {} : { origin: origin === "self" ? url : origin },
95
+ });
96
+ const response = yield* client.execute(tenant === undefined ? request : HttpClientRequest.bearerToken(request, tenant));
97
+ return { status: response.status, body: yield* decodeJson(yield* response.text) };
98
+ }).pipe(Effect.orDie);
99
+ });
100
+ const isRecord = Schema.is(Schema.Record(Schema.String, Schema.Json));
101
+ const isArray = Schema.is(Schema.Array(Schema.Json));
102
+ const isNumber = Schema.is(Schema.Finite);
103
+ /** A field of a decoded JSON body, without trusting its shape. */
104
+ const field = (value, ...path) => {
105
+ let current = value;
106
+ for (const key of path)
107
+ current = isArray(current)
108
+ ? (current[Number(key)] ?? null)
109
+ : isRecord(current)
110
+ ? (current[key] ?? null)
111
+ : null;
112
+ return current;
113
+ };
114
+ const list = (value, ...path) => {
115
+ const found = field(value, ...path);
116
+ return isArray(found) ? found : [];
117
+ };
118
+ const text = (value) => (Schema.is(Schema.String)(value) ? value : "");
119
+ const byText = (left, right) => left.localeCompare(right);
120
+ const numeral = (value) => (isNumber(value) ? String(value) : "");
121
+ const parseJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
122
+ const decodeJsonSync = (json) => Option.getOrThrow(parseJson(json));
123
+ /** Independent decode of a compressed view value: the bytes as Postgres stores them. */
124
+ const rawJson = (bytes) => decodeJsonSync(new TextDecoder().decode(zstdDecompress(bytes)));
125
+ const eventually = (check, what) => check.pipe(Effect.repeat({ schedule: Schedule.spaced("25 millis"), until: (held) => held }), Effect.timeoutOrElse({
126
+ duration: "30 seconds",
127
+ orElse: () => Effect.die(new Error(`Timed out waiting for ${what}`)),
128
+ }), Effect.asVoid);
129
+ const tenantCounts = Effect.fnUntraced(function* (tenant) {
130
+ const sql = yield* SqlClient.SqlClient;
131
+ const [row] = yield* sql `
132
+ SELECT
133
+ (SELECT count(*)::int FROM durable.actors WHERE tenant_id = ${tenant}) AS actors,
134
+ (SELECT count(*)::int FROM durable.receipts WHERE tenant_id = ${tenant}) AS receipts,
135
+ (SELECT count(*)::int FROM durable.events WHERE tenant_id = ${tenant}) AS events,
136
+ (SELECT count(*)::int FROM durable.outbox WHERE tenant_id = ${tenant}) AS outbox,
137
+ (SELECT count(*)::int FROM durable.timers WHERE tenant_id = ${tenant}) AS timers,
138
+ (SELECT count(*)::int FROM durable.jobs WHERE tenant_id = ${tenant}) AS jobs,
139
+ (SELECT count(*)::int FROM durable.dead_letters WHERE tenant_id = ${tenant}) AS "deadLetters",
140
+ (SELECT count(*)::int FROM durable.workflows WHERE tenant_id = ${tenant}) AS workflows,
141
+ (SELECT count(*)::int FROM durable.workflows
142
+ WHERE tenant_id = ${tenant} AND status <> 'finished') AS "openWorkflows"`;
143
+ return row;
144
+ });
145
+ const RUNTIME_TABLES = [
146
+ "actor_generations",
147
+ "actor_state",
148
+ "actor_receipts",
149
+ "actor_events",
150
+ "actor_outbox",
151
+ "actor_dead_letters",
152
+ "actor_workflow_executions",
153
+ "actor_workflow_step",
154
+ ];
155
+ const fingerprint = Effect.gen(function* () {
156
+ const sql = yield* SqlClient.SqlClient;
157
+ const [row] = yield* sql.unsafe(`SELECT ${RUNTIME_TABLES.map((table) => `(SELECT count(*)::text || ':' || coalesce(md5(string_agg(t::text, ',' ORDER BY t::text)), '')
158
+ FROM ${table} t WHERE t.actor_type = 'Inspected') AS ${table}`).join(", ")}`);
159
+ return row;
160
+ });
161
+ const rejection = (exit) => Exit.isFailure(exit) ? String(exit.cause) : "succeeded";
162
+ class Probed extends Data.TaggedError("Probed") {
163
+ }
164
+ /** Inspector cases: decoded committed rows and events, workflow step history, and tenant scoping by the authenticated principal. */
165
+ export const inspectorConformance = [
166
+ {
167
+ name: "inspector: shows an actor's committed rows decoded, with the events each receipt committed",
168
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
169
+ const test = yield* ActorTest;
170
+ const sql = yield* SqlClient.SqlClient;
171
+ const get = yield* serveInspector;
172
+ const inspected = yield* Inspected.get("flow");
173
+ yield* inspected.Write("first");
174
+ yield* inspected.WriteThenReject("second").pipe(Effect.exit);
175
+ yield* inspected.WriteThenDie("third").pipe(Effect.exit);
176
+ yield* test.advance(0);
177
+ const detail = yield* get("/actor?type=Inspected&id=flow", test.tenant);
178
+ expect(detail.status).toBe(200);
179
+ const [stored] = yield* sql `SELECT generation::int AS generation, created, last_event_sequence::int AS last_event_sequence
180
+ FROM durable.actors
181
+ WHERE tenant_id = ${test.tenant} AND actor_type = 'Inspected' AND actor_id = 'flow'`;
182
+ expect(field(detail.body, "actor")).toEqual({
183
+ actorType: "Inspected",
184
+ actorId: "flow",
185
+ placement: "tenant",
186
+ generation: stored.generation,
187
+ created: stored.created,
188
+ lastEventSequence: stored.last_event_sequence,
189
+ });
190
+ expect(field(detail.body, "state")).toMatchObject([
191
+ { key: "notes", value: { json: ["first"] } },
192
+ ]);
193
+ const [event] = yield* sql `
194
+ SELECT value, command_id FROM durable.events
195
+ WHERE tenant_id = ${test.tenant} AND actor_type = 'Inspected' AND actor_id = 'flow'`;
196
+ expect(field(detail.body, "events")).toMatchObject([
197
+ {
198
+ sequence: 1,
199
+ event: "Noted",
200
+ commandId: event.command_id,
201
+ value: { json: rawJson(event.value) },
202
+ },
203
+ ]);
204
+ const receipts = list(detail.body, "receipts").toSorted((left, right) => text(field(left, "command")).localeCompare(text(field(right, "command"))));
205
+ expect(receipts).toMatchObject([
206
+ { command: "Write", commandId: event.command_id, outcomeTag: "Success", events: [1] },
207
+ { command: "WriteThenReject", outcomeTag: "Failure", events: [] },
208
+ ]);
209
+ expect(field(detail.body, "outbox")).toMatchObject([
210
+ {
211
+ timerKey: "nudge",
212
+ targetType: "Inspected",
213
+ targetId: "flow",
214
+ command: "Nudge",
215
+ attempts: 0,
216
+ },
217
+ ]);
218
+ const [dead] = yield* sql `
219
+ SELECT payload FROM durable.dead_letters
220
+ WHERE tenant_id = ${test.tenant} AND actor_type = 'Inspected' AND actor_id = 'flow'`;
221
+ expect(field(detail.body, "jobs")).toEqual([]);
222
+ expect(field(detail.body, "deadLetters")).toMatchObject([
223
+ {
224
+ job: "Notify",
225
+ attempts: 1,
226
+ ambiguous: false,
227
+ payload: { json: decodeJsonSync(dead.payload) },
228
+ },
229
+ ]);
230
+ expect(field(detail.body, "totals")).toEqual({
231
+ receipts: 2,
232
+ events: 1,
233
+ outbox: 1,
234
+ jobs: 0,
235
+ deadLetters: 1,
236
+ workflows: 0,
237
+ });
238
+ expect(list((yield* get("/actors?type=Inspected", test.tenant)).body, "actors").map((row) => field(row, "actorId"))).toContain("flow");
239
+ const overview = yield* get("/overview", test.tenant);
240
+ expect(field(overview.body, "tenant")).toBe(test.tenant);
241
+ expect(field(overview.body, "counts")).toEqual(yield* tenantCounts(test.tenant));
242
+ expect(field(overview.body, "views")).toEqual((yield* sql `
243
+ SELECT view_name, version FROM durable.views ORDER BY view_name COLLATE "C"`).map(({ view_name, version }) => ({ view: view_name, version })));
244
+ const missing = yield* get("/actor?type=Inspected&id=never-written", test.tenant);
245
+ expect(missing.status).toBe(404);
246
+ expect(field(missing.body, "_tag")).toBe("NotFound");
247
+ expect((yield* get("/actors?limit=0", test.tenant)).status).toBe(400);
248
+ })),
249
+ },
250
+ {
251
+ name: "inspector: pages an actor's receipts, timeline and latest events, counts actor and job types, and finds actors by address prefix",
252
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
253
+ const test = yield* ActorTest;
254
+ const sql = yield* SqlClient.SqlClient;
255
+ const get = yield* serveInspector;
256
+ const tenant = `${test.tenant}-paged`;
257
+ const paged = yield* Inspected.get("paged").pipe(Actor.tenant(tenant));
258
+ yield* paged.Write("one");
259
+ yield* paged.Write("two");
260
+ yield* paged.WriteThenReject("three").pipe(Effect.exit);
261
+ yield* (yield* Inspected.get("pages").pipe(Actor.tenant(tenant))).Write("other");
262
+ yield* (yield* Inspected.get("unpaged").pipe(Actor.tenant(tenant))).Write("apart");
263
+ yield* test.advance(0);
264
+ const byCodeUnit = (left, right) => left < right ? -1 : left > right ? 1 : 0;
265
+ const stored = yield* sql `SELECT actor_id, command_id, command, caller_key, outcome_tag, expires_at_ms::text
266
+ FROM durable.receipts WHERE tenant_id = ${tenant}`;
267
+ const receiptOrder = (left, right) => Number(right.expires_at_ms) - Number(left.expires_at_ms) ||
268
+ byCodeUnit(left.actor_id, right.actor_id) ||
269
+ byCodeUnit(left.command_id, right.command_id);
270
+ const expectedReceipts = (rows) => rows.toSorted(receiptOrder).map((row) => ({
271
+ actorId: row.actor_id,
272
+ commandId: row.command_id,
273
+ command: row.command,
274
+ outcomeTag: row.outcome_tag,
275
+ callerKey: { json: decodeJsonSync(row.caller_key) },
276
+ expiresAtMs: Number(row.expires_at_ms),
277
+ }));
278
+ const pages = Effect.fnUntraced(function* (path, key, cursor) {
279
+ const rows = [];
280
+ let after = "";
281
+ for (let page = 0; page < 50; page++) {
282
+ const reply = yield* get(`${path}${after}`, tenant);
283
+ expect([path, reply.status]).toEqual([path, 200]);
284
+ rows.push(...list(reply.body, key));
285
+ const next = field(reply.body, "next");
286
+ if (next === null)
287
+ return rows;
288
+ after = cursor(next);
289
+ }
290
+ return yield* Effect.die(new Error(`${path} never reached its last page`));
291
+ });
292
+ const receiptCursor = (next) => `&afterExpiresAtMs=${numeral(field(next, "expiresAtMs"))}&afterType=${encodeURIComponent(text(field(next, "actorType")))}&afterId=${encodeURIComponent(text(field(next, "actorId")))}&afterCommandId=${encodeURIComponent(text(field(next, "commandId")))}`;
293
+ const ownReceipts = yield* pages("/receipts?type=Inspected&id=paged&limit=1", "receipts", receiptCursor);
294
+ expect(ownReceipts.length).toBe(3);
295
+ expect(ownReceipts).toMatchObject(expectedReceipts(stored.filter((row) => row.actor_id === "paged")));
296
+ expect(yield* pages("/receipts?limit=2", "receipts", receiptCursor)).toMatchObject(expectedReceipts(stored));
297
+ expect(list((yield* get("/receipts?outcome=Failure", tenant)).body, "receipts")).toMatchObject(expectedReceipts(stored.filter((row) => row.outcome_tag === "Failure")));
298
+ expect((yield* get("/receipts?type=Inspected&id=never", tenant)).status).toBe(404);
299
+ expect((yield* get("/receipts?id=paged", tenant)).status).toBe(400);
300
+ const cursorTail = "&afterType=Inspected&afterId=paged&afterCommandId=c";
301
+ for (const path of [
302
+ `/receipts?afterExpiresAtMs=1.5${cursorTail}`,
303
+ `/receipts?afterExpiresAtMs=1e21${cursorTail}`,
304
+ "/receipts?afterExpiresAtMs=1&afterType=Inspected",
305
+ "/actors?afterType=Inspected",
306
+ "/dead-letters?afterDeadAtMs=1",
307
+ "/workflows?afterExecutionId=x",
308
+ "/timeline?type=Inspected&id=paged&beforeSequence=2",
309
+ ])
310
+ expect([path, (yield* get(path, tenant)).status]).toEqual([path, 400]);
311
+ const events = yield* sql `SELECT sequence::int AS sequence, event, command_id, emitted_at_ms::text
312
+ FROM durable.events
313
+ WHERE tenant_id = ${tenant} AND actor_type = 'Inspected' AND actor_id = 'paged'`;
314
+ expect(events.length).toBe(2);
315
+ const callerOf = (commandId) => {
316
+ const receipt = stored.find((row) => row.actor_id === "paged" && row.command_id === commandId);
317
+ return receipt === undefined ? null : { json: decodeJsonSync(receipt.caller_key) };
318
+ };
319
+ const commands = [...new Set(events.map((event) => event.command_id))].map((commandId) => {
320
+ const own = events.filter((event) => event.command_id === commandId);
321
+ return {
322
+ kind: "command",
323
+ sequence: Math.min(...own.map((event) => event.sequence)),
324
+ name: stored.find((row) => row.command_id === commandId).command,
325
+ commandId,
326
+ callerKey: callerOf(commandId),
327
+ atMs: Math.min(...own.map((event) => Number(event.emitted_at_ms))),
328
+ };
329
+ });
330
+ const expectedTimeline = [
331
+ ...events.map((event) => ({
332
+ kind: "event",
333
+ sequence: event.sequence,
334
+ name: event.event,
335
+ commandId: event.command_id,
336
+ callerKey: callerOf(event.command_id),
337
+ atMs: Number(event.emitted_at_ms),
338
+ })),
339
+ ...commands,
340
+ ].toSorted((left, right) => right.sequence - left.sequence ||
341
+ Number(right.kind === "event") - Number(left.kind === "event"));
342
+ expect(expectedTimeline.map((entry) => entry.kind)).toEqual([
343
+ "event",
344
+ "command",
345
+ "event",
346
+ "command",
347
+ ]);
348
+ expect(yield* pages("/timeline?type=Inspected&id=paged&limit=3", "entries", (next) => `&beforeSequence=${numeral(field(next, "sequence"))}&beforeKind=${text(field(next, "kind"))}`)).toEqual(expectedTimeline);
349
+ const newest = events.toSorted((left, right) => right.sequence - left.sequence)[0];
350
+ expect(yield* pages("/latest-events?type=Inspected&id=paged&limit=1", "events", (next) => `&after=${encodeURIComponent(text(next))}`)).toEqual([
351
+ {
352
+ event: "Noted",
353
+ sequence: newest.sequence,
354
+ emittedAtMs: Number(newest.emitted_at_ms),
355
+ },
356
+ ]);
357
+ const prefixed = (prefix) => get(`/actors?prefix=${encodeURIComponent(prefix)}`, tenant).pipe(Effect.map((reply) => list(reply.body, "actors").map((row) => field(row, "actorId"))));
358
+ expect(yield* prefixed("Inspected/page")).toEqual(["paged", "pages"]);
359
+ expect(yield* prefixed("Inspected/paged")).toEqual(["paged"]);
360
+ expect(yield* prefixed("Insp")).toEqual(["paged", "pages", "unpaged"]);
361
+ expect(yield* prefixed("Inspected/zz")).toEqual([]);
362
+ expect(yield* prefixed("Other")).toEqual([]);
363
+ expect(yield* pages("/actor-types?limit=1", "actorTypes", (next) => `&after=${text(next)}`)).toEqual([{ actorType: "Inspected", actors: 3 }]);
364
+ expect(field((yield* get("/actor-types?prefix=Insp", tenant)).body, "actorTypes")).toEqual([{ actorType: "Inspected", actors: 3 }]);
365
+ expect(field((yield* get("/actor-types?prefix=Zed", tenant)).body, "actorTypes")).toEqual([]);
366
+ expect(field((yield* get("/actor-types?type=Missing", tenant)).body)).toMatchObject({
367
+ actorTypes: [],
368
+ next: null,
369
+ });
370
+ const pending = yield* sql `
371
+ SELECT job, attempts::int AS attempts FROM durable.jobs WHERE tenant_id = ${tenant}`;
372
+ const dead = yield* sql `
373
+ SELECT job, job_id, dead_at_ms::text FROM durable.dead_letters
374
+ WHERE tenant_id = ${tenant}`;
375
+ expect(dead.length).toBe(4);
376
+ expect(yield* pages("/job-types?limit=1", "jobTypes", (next) => `&after=${text(next)}`)).toEqual([...new Set([...pending, ...dead].map((row) => row.job))]
377
+ .toSorted(byCodeUnit)
378
+ .map((job) => ({
379
+ job,
380
+ queued: pending.filter((row) => row.job === job && row.attempts === 0).length,
381
+ retrying: pending.filter((row) => row.job === job && row.attempts > 0).length,
382
+ deadLetters: dead.filter((row) => row.job === job).length,
383
+ })));
384
+ expect((yield* pages("/dead-letters?limit=1", "deadLetters", (next) => `&afterDeadAtMs=${numeral(field(next, "deadAtMs"))}&afterJobId=${encodeURIComponent(text(field(next, "jobId")))}`)).map((row) => field(row, "jobId"))).toEqual(dead
385
+ .toSorted((left, right) => Number(right.dead_at_ms) - Number(left.dead_at_ms) ||
386
+ byCodeUnit(left.job_id, right.job_id))
387
+ .map((row) => row.job_id));
388
+ const [timer] = yield* sql `
389
+ SELECT min(due_at_ms)::text AS due FROM durable.timers WHERE tenant_id = ${tenant}`;
390
+ expect(timer.due).not.toBe(null);
391
+ expect(field((yield* get("/overview", tenant)).body, "nextTimerDueAtMs")).toBe(Number(timer.due));
392
+ })),
393
+ },
394
+ {
395
+ name: "inspector: shows a workflow's step history while open and its result once finished",
396
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
397
+ const test = yield* ActorTest;
398
+ const sql = yield* SqlClient.SqlClient;
399
+ const get = yield* serveInspector;
400
+ const inspected = yield* Inspected.get("settling");
401
+ const run = yield* inspected.Settle({ order: "o-1" });
402
+ yield* eventually(Effect.gen(function* () {
403
+ const rows = yield* sql `
404
+ SELECT status FROM durable.workflows WHERE execution_id = ${run.executionId}`;
405
+ return rows[0]?.status === "suspended";
406
+ }).pipe(Effect.orDie), "the execution to suspend");
407
+ const [reserve] = yield* sql `
408
+ SELECT exit FROM durable.workflow_steps
409
+ WHERE execution_id = ${run.executionId} AND step = 'reserve'`;
410
+ const [started] = yield* sql `
411
+ SELECT payload FROM durable.workflows WHERE execution_id = ${run.executionId}`;
412
+ const open = yield* get("/actor?type=Inspected&id=settling", test.tenant);
413
+ expect(field(open.body, "workflows")).toMatchObject([
414
+ {
415
+ executionId: run.executionId,
416
+ workflow: "Settle",
417
+ workflowKey: "o-1",
418
+ status: "suspended",
419
+ interrupt: false,
420
+ result: null,
421
+ finishedAtMs: null,
422
+ steps: [
423
+ { step: "reserve", kind: "activity", exit: { json: rawJson(reserve.exit) } },
424
+ { step: "pause", kind: "clock", exit: null },
425
+ ],
426
+ },
427
+ ]);
428
+ expect(field(open.body, "workflows", 0, "payload")).toEqual({
429
+ json: rawJson(started.payload),
430
+ });
431
+ expect(isNumber(field(open.body, "workflows", 0, "steps", 1, "dueAtMs"))).toBe(true);
432
+ const listed = (status) => get(`/workflows?status=${status}`, test.tenant).pipe(Effect.map((reply) => list(reply.body, "workflows").map((workflow) => field(workflow, "executionId"))));
433
+ expect(yield* listed("open")).toContain(run.executionId);
434
+ yield* test.advance("11 seconds");
435
+ expect(yield* run.result).toBe("held-o-1:settled");
436
+ const [finished] = yield* sql `
437
+ SELECT result FROM durable.workflows WHERE execution_id = ${run.executionId}`;
438
+ const done = yield* get("/actor?type=Inspected&id=settling", test.tenant);
439
+ expect(field(done.body, "workflows")).toMatchObject([
440
+ {
441
+ executionId: run.executionId,
442
+ status: "finished",
443
+ result: { json: rawJson(finished.result) },
444
+ steps: [],
445
+ },
446
+ ]);
447
+ expect(yield* listed("open")).not.toContain(run.executionId);
448
+ expect(yield* listed("all")).toContain(run.executionId);
449
+ expect(yield* listed("finished")).toContain(run.executionId);
450
+ expect(yield* listed("completed")).toContain(run.executionId);
451
+ expect(yield* listed("failed")).not.toContain(run.executionId);
452
+ expect(yield* listed("suspended")).not.toContain(run.executionId);
453
+ const flows = `${test.tenant}-flows`;
454
+ const listedIn = (status) => get(`/workflows?status=${status}`, flows).pipe(Effect.map((reply) => list(reply.body, "workflows").map((workflow) => field(workflow, "executionId"))));
455
+ const starts = yield* Effect.forEach(["a", "b"], (order) => Inspected.get(`flow-${order}`).pipe(Effect.flatMap((actor) => actor.Settle({ order: `o-${order}` })), Actor.tenant(flows)));
456
+ const executions = starts.map((start) => start.executionId);
457
+ yield* eventually(Effect.map(listedIn("suspended"), (ids) => executions.every((id) => ids.includes(id))), "both executions to suspend");
458
+ expect(yield* listedIn("running")).toEqual([]);
459
+ const everyExecution = [];
460
+ let after = "";
461
+ for (let page = 0; page < 10; page++) {
462
+ const reply = (yield* get(`/workflows?status=all&limit=1${after}`, flows)).body;
463
+ everyExecution.push(...list(reply, "workflows").map((row) => field(row, "executionId")));
464
+ const next = field(reply, "next");
465
+ if (next === null)
466
+ break;
467
+ after = `&afterStartedAtMs=${numeral(field(next, "startedAtMs"))}&afterExecutionId=${text(field(next, "executionId"))}`;
468
+ }
469
+ expect(everyExecution).toEqual(yield* listedIn("all"));
470
+ expect(everyExecution.map(text).toSorted(byText)).toEqual(executions.toSorted(byText));
471
+ })),
472
+ },
473
+ {
474
+ name: "inspector: reads only the authenticated principal's tenant, refuses missing credentials, and refuses an oversized one before its provider runs",
475
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
476
+ const test = yield* ActorTest;
477
+ const get = yield* serveInspector;
478
+ const home = `${test.tenant}-home`;
479
+ const abroad = `${test.tenant}-inspected`;
480
+ yield* (yield* Inspected.get("shared").pipe(Actor.tenant(home))).Write("home");
481
+ yield* (yield* Inspected.get("shared").pipe(Actor.tenant(abroad))).Write("abroad");
482
+ yield* (yield* Inspected.get("only-abroad").pipe(Actor.tenant(abroad))).Write("abroad");
483
+ yield* test.advance(0);
484
+ const shared = yield* get("/actor?type=Inspected&id=shared", home);
485
+ expect(field(shared.body, "state")).toMatchObject([{ value: { json: ["home"] } }]);
486
+ expect(field(shared.body, "totals", "receipts")).toBe(1);
487
+ expect(yield* get(`/actor?type=Inspected&id=shared&tenant=${abroad}`, home)).toEqual(shared);
488
+ expect((yield* get("/actor?type=Inspected&id=only-abroad", home)).status).toBe(404);
489
+ expect(field((yield* get("/actor?type=Inspected&id=only-abroad", abroad)).body, "state")).toMatchObject([{ value: { json: ["abroad"] } }]);
490
+ const homeActors = list((yield* get("/actors?type=Inspected", home)).body, "actors");
491
+ expect(homeActors.map((row) => field(row, "actorId"))).not.toContain("only-abroad");
492
+ expect(list((yield* get("/actors?type=Inspected", abroad)).body, "actors").map((row) => field(row, "actorId"))).toEqual(["only-abroad", "shared"]);
493
+ for (const tenant of [home, abroad])
494
+ expect(field((yield* get("/overview", tenant)).body, "counts")).toEqual(yield* tenantCounts(tenant));
495
+ const abroadLetters = list((yield* get("/dead-letters", abroad)).body, "deadLetters");
496
+ expect(abroadLetters
497
+ .map((row) => text(field(row, "actorId")))
498
+ .toSorted((left, right) => left.localeCompare(right))).toEqual(["only-abroad", "shared"]);
499
+ for (const path of [
500
+ "/receipts?type=Inspected&id=only-abroad",
501
+ "/latest-events?type=Inspected&id=only-abroad",
502
+ "/timeline?type=Inspected&id=only-abroad",
503
+ ])
504
+ expect((yield* get(path, home)).status).toBe(404);
505
+ const sql = yield* SqlClient.SqlClient;
506
+ const commandsOf = (tenant) => sql `
507
+ SELECT command_id FROM durable.receipts
508
+ WHERE tenant_id = ${tenant} AND actor_type = 'Inspected' AND actor_id = 'shared'`.pipe(Effect.map((rows) => rows.map((row) => row.command_id)));
509
+ const homeCommands = yield* commandsOf(home);
510
+ const abroadCommands = yield* commandsOf(abroad);
511
+ expect(homeCommands.length).toBe(1);
512
+ expect(abroadCommands.length).toBe(1);
513
+ expect(homeCommands).not.toEqual(abroadCommands);
514
+ const [homeEvent] = yield* sql `
515
+ SELECT sequence::int AS sequence, command_id FROM durable.events
516
+ WHERE tenant_id = ${home} AND actor_type = 'Inspected' AND actor_id = 'shared'`;
517
+ expect(homeEvent.command_id).toBe(homeCommands[0]);
518
+ const sharedAt = (path, key, name) => get(`/${path}?type=Inspected&id=shared`, home).pipe(Effect.map((reply) => list(reply.body, key).map((row) => field(row, name))));
519
+ expect(yield* sharedAt("receipts", "receipts", "commandId")).toEqual(homeCommands);
520
+ expect(yield* sharedAt("timeline", "entries", "commandId")).toEqual([
521
+ homeCommands[0],
522
+ homeCommands[0],
523
+ ]);
524
+ expect(yield* sharedAt("latest-events", "events", "sequence")).toEqual([
525
+ homeEvent.sequence,
526
+ ]);
527
+ const receiptActors = (tenant) => get("/receipts", tenant).pipe(Effect.map((reply) => list(reply.body, "receipts")
528
+ .map((row) => text(field(row, "actorId")))
529
+ .toSorted((left, right) => left.localeCompare(right))));
530
+ expect(yield* receiptActors(abroad)).toEqual(["only-abroad", "shared"]);
531
+ expect(yield* receiptActors(home)).toEqual(["shared"]);
532
+ expect(field((yield* get("/actor-types", abroad)).body, "actorTypes")).toEqual([
533
+ { actorType: "Inspected", actors: 2 },
534
+ ]);
535
+ expect(field((yield* get("/actor-types", home)).body, "actorTypes")).toEqual([
536
+ { actorType: "Inspected", actors: 1 },
537
+ ]);
538
+ expect(field((yield* get("/job-types", abroad)).body, "jobTypes")).toEqual([
539
+ { job: "Notify", queued: 0, retrying: 0, deadLetters: 2 },
540
+ ]);
541
+ expect(field((yield* get("/job-types", home)).body, "jobTypes")).toEqual([
542
+ { job: "Notify", queued: 0, retrying: 0, deadLetters: 1 },
543
+ ]);
544
+ expect(list((yield* get("/actors?prefix=Inspected/only", home)).body, "actors")).toEqual([]);
545
+ for (const path of ["/overview", "/actors", "/actor?type=Inspected&id=shared"]) {
546
+ const anonymous = yield* get(path);
547
+ expect(anonymous.status).toBe(401);
548
+ expect(field(anonymous.body, "reason", "_tag")).toBe("Unauthorized");
549
+ expect(field(anonymous.body, "reason", "code")).toBe("missing_credentials");
550
+ }
551
+ const foreign = yield* get("/actor?type=Inspected&id=shared", home, "https://elsewhere.example");
552
+ expect(foreign.status).toBe(403);
553
+ expect(field(foreign.body, "reason", "code")).toBe("origin_not_allowed");
554
+ expect(field(foreign.body, "state")).toBe(null);
555
+ expect(yield* get("/actor?type=Inspected&id=shared", home, "self")).toEqual(shared);
556
+ const before = reached.count;
557
+ const oversized = yield* get("/overview", "a".repeat(9 * 1024));
558
+ expect(oversized.status).toBe(413);
559
+ expect(field(oversized.body, "reason", "code")).toBe("too_large");
560
+ expect(reached.count).toBe(before);
561
+ const everyone = list((yield* get("/actors?type=Inspected", abroad)).body, "actors");
562
+ const paged = [];
563
+ let cursor = "";
564
+ for (let page = 0; page <= everyone.length; page++) {
565
+ const reply = (yield* get(`/actors?type=Inspected&limit=1${cursor}`, abroad)).body;
566
+ paged.push(...list(reply, "actors"));
567
+ const next = field(reply, "next");
568
+ if (next === null)
569
+ break;
570
+ cursor = `&afterType=${encodeURIComponent(text(field(next, "actorType")))}&afterId=${encodeURIComponent(text(field(next, "actorId")))}`;
571
+ }
572
+ expect(paged).toEqual(everyone);
573
+ })),
574
+ },
575
+ {
576
+ name: "inspector: reads through the durable views only and never writes",
577
+ run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
578
+ const test = yield* ActorTest;
579
+ const sql = yield* SqlClient.SqlClient;
580
+ const get = yield* serveInspector;
581
+ const inspected = yield* Inspected.get("untouched");
582
+ yield* inspected.Write("kept");
583
+ yield* test.advance(0);
584
+ const before = yield* fingerprint;
585
+ for (const path of [
586
+ "/overview",
587
+ "/actors",
588
+ "/actor?type=Inspected&id=untouched",
589
+ "/outbox",
590
+ "/jobs",
591
+ "/dead-letters",
592
+ "/workflows?status=all",
593
+ "/actor-types",
594
+ "/job-types",
595
+ "/receipts",
596
+ "/receipts?type=Inspected&id=untouched",
597
+ "/latest-events?type=Inspected&id=untouched",
598
+ "/timeline?type=Inspected&id=untouched",
599
+ ])
600
+ expect((yield* get(path, test.tenant)).status).toBe(200);
601
+ expect(yield* fingerprint).toEqual(before);
602
+ const uuid = yield* (yield* Crypto.Crypto).randomUUIDv4.pipe(Effect.orDie);
603
+ const role = `inspector_${uuid.replaceAll("-", "")}`;
604
+ const probe = yield* sql
605
+ .withTransaction(Effect.gen(function* () {
606
+ yield* sql.unsafe(`CREATE ROLE ${role} NOLOGIN`);
607
+ yield* sql.unsafe(`GRANT USAGE ON SCHEMA durable TO ${role}`);
608
+ yield* sql.unsafe(`GRANT SELECT ON ALL TABLES IN SCHEMA durable TO ${role}`);
609
+ yield* sql.unsafe(`SET LOCAL ROLE ${role}`);
610
+ yield* sql `SET TRANSACTION READ ONLY`;
611
+ const page = { tenant: test.tenant, limit: 50 };
612
+ yield* Queries.overview(page);
613
+ const listed = yield* Queries.actors(page);
614
+ yield* Queries.outbox(page);
615
+ yield* Queries.jobs(page);
616
+ yield* Queries.deadLetters(page);
617
+ yield* Queries.workflows({ ...page, status: "all" });
618
+ yield* Queries.actorTypes(page);
619
+ yield* Queries.jobTypes(page);
620
+ yield* Queries.receipts(page);
621
+ const untouched = { ...page, actorType: "Inspected", actorId: "untouched" };
622
+ yield* Queries.receipts(untouched);
623
+ yield* Queries.latestEvents(untouched);
624
+ yield* Queries.timeline(untouched);
625
+ const found = yield* Queries.actor({
626
+ ...page,
627
+ actorType: "Inspected",
628
+ actorId: "untouched",
629
+ });
630
+ const denied = yield* sql `SELECT 1 FROM actor_receipts LIMIT 1`.pipe(sql.withTransaction, Effect.exit, Effect.map(rejection));
631
+ return yield* new Probed({
632
+ found: Option.isSome(found),
633
+ listed: listed.actors.length,
634
+ denied,
635
+ });
636
+ }))
637
+ .pipe(Effect.catchTag("Probed", Effect.succeed));
638
+ expect(probe.found).toBe(true);
639
+ expect(probe.listed > 0).toBe(true);
640
+ expect(/permission denied/.test(probe.denied)).toBe(true);
641
+ const write = rejection(yield* Queries.readOnly(test.tenant)(sql `DELETE FROM actor_receipts`).pipe(Effect.exit));
642
+ expect(/read-only transaction/.test(write)).toBe(true);
643
+ expect(yield* fingerprint).toEqual(before);
644
+ })),
645
+ },
646
+ ];
647
+ /** Inspector actors. */
648
+ export const inspectorSuite = {
649
+ layer: () => inspectorLayer,
650
+ };