@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,809 @@
1
+ import { Cause, DateTime, Effect, Option, Schema, SchemaAST, Stream } from "effect";
2
+ import { Headers, HttpRouter, HttpServerResponse } from "effect/http";
3
+ import {} from "../actor/served.js";
4
+ import { descriptorOf } from "../actor/descriptor.js";
5
+ import { ActorError, InvalidInput, NotCreated, RunnerAtCapacity, Unauthorized, } from "../errors/actor.js";
6
+ import { InternalActors } from "../runtime/actors.js";
7
+ import { overloaded } from "../runtime/admission.js";
8
+ import { Outcome, Request } from "../runtime/request.js";
9
+ import { ContentStore } from "../handles/content.js";
10
+ import { MAX_CONTENT_BYTES } from "../runtime/content/store.js";
11
+ import { ActorRef, Anonymous, CurrentCaller, User } from "../identity/caller.js";
12
+ import { isVersion } from "../identity/version.js";
13
+ import { buildServedApi, CONTENT_ROUTE, openApiDocument, memberPath, PROTOCOL_OPERATIONS, PROTOCOL_PATHS, schemeName, } from "./api.js";
14
+ import { RuntimeControl } from "../runtime/drain.js";
15
+ import { CREDENTIAL_BYTES, Credential, oversizedCredential, readsCookies, withinLimits, } from "./auth.js";
16
+ import { ASSERTION_HEADER, KEY_REFRESH_PATH, reauthenticationDigest, requestDigest, } from "./assertion/binding.js";
17
+ import { databaseClock } from "./clock.js";
18
+ import { SUBPROTOCOL } from "../protocol/frames.js";
19
+ import { isSameOrigin } from "./origin.js";
20
+ import { handleMcp } from "./mcp/endpoint.js";
21
+ import { mcpTools } from "./mcp/tools.js";
22
+ import { cursorErrorBody, feedStream, MAX_FEED_FILTERS, openFeed } from "./sessions/feed.js";
23
+ import { MAX_AWAITING_HELLO, socketSession } from "./sessions/socket.js";
24
+ import { streamResponse } from "./sessions/stream.js";
25
+ import { watchResponse } from "./sessions/watch.js";
26
+ import { FLEET_PAGE_DEFAULT, FLEET_PAGE_LIMIT } from "../client/fleet-page.js";
27
+ import { actorErrorBody, Defect, PROTOCOL } from "../protocol/wire.js";
28
+ import { actorErrorResponse, invalidInput, undecodable } from "./wire.js";
29
+ const NAME = /^[A-Za-z][A-Za-z0-9_]*$/;
30
+ /** The path segment an actor's event feed is served at, so no member may take it. */
31
+ const FEED_ROUTE = "events";
32
+ const RESERVED_MEMBERS = new Set([FEED_ROUTE, CONTENT_ROUTE]);
33
+ const ALLOWED_HEADERS = [
34
+ "authorization",
35
+ "content-type",
36
+ "idempotency-key",
37
+ "durable-protocol",
38
+ "durable-min-version",
39
+ "last-event-id",
40
+ "traceparent",
41
+ "tracestate",
42
+ ].join(", ");
43
+ /**
44
+ * Clients mint ids up to a second or a round trip behind the database clock,
45
+ * then retry within the window; shorter windows expire ids before delivery.
46
+ */
47
+ const MIN_RETRY_WINDOW_MS = 60_000;
48
+ const EXPOSED_HEADERS = [
49
+ "x-request-id",
50
+ "durable-now",
51
+ "durable-version",
52
+ "durable-replayed",
53
+ "retry-after",
54
+ ].join(", ");
55
+ const JSON_TYPE = /^application\/json[ ]*(;.*)?$/i;
56
+ /** A quoted value is the idempotency-key draft's structured-field string. */
57
+ const QUOTED = /^"(.*)"$/;
58
+ /** A `content-length` value: decimal digits only, so a sign, a fraction or `NaN` is malformed framing. */
59
+ const DECIMAL = /^[0-9]+$/;
60
+ const strictUtf8 = new TextDecoder("utf-8", { fatal: true });
61
+ const decodeJson = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Json));
62
+ const decodeSuccess = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({ value: Schema.optionalKey(Schema.Json) })));
63
+ const traceId = Effect.currentSpan.pipe(Effect.map((span) => span.traceId), Effect.orElseSucceed(() => "0".repeat(32)));
64
+ /** The opaque body a defect is answered with on every transport, after its cause is logged. */
65
+ const defectBody = Effect.fnUntraced(function* (cause) {
66
+ const trace = yield* traceId;
67
+ yield* Effect.logError("Actor.serve request failed", cause);
68
+ return Defect.make({ traceId: trace });
69
+ });
70
+ const defectResponse = (cause) => Effect.map(defectBody(cause), (body) => HttpServerResponse.jsonUnsafe(body, { status: 500 }));
71
+ /** An actor's id as a request names it: the key schema decodes it exactly once, and no route or tool may name `.` or `..`. */
72
+ const decodeId = Effect.fnUntraced(function* (definition, raw) {
73
+ if (definition.key === "singleton")
74
+ return yield* definition.decodeId("").pipe(Effect.orDie);
75
+ if (raw === "." || raw === "..")
76
+ return yield* invalidInput("unservable_id");
77
+ return yield* definition.decodeId(raw).pipe(Effect.mapError((error) => undecodable(error)));
78
+ });
79
+ const pathId = Effect.fnUntraced(function* (definition) {
80
+ return yield* decodeId(definition, (yield* HttpRouter.params).id ?? "");
81
+ });
82
+ export { isSameOrigin } from "./origin.js";
83
+ const isPrincipal = Schema.is(Schema.Union([User, Anonymous]));
84
+ /** Sockets awaiting `hello`, per runtime, across every `Actor.serve` layer it runs. */
85
+ const awaitingHello = new WeakMap();
86
+ const offeredProtocols = (request) => Option.match(Headers.get(request.headers, "sec-websocket-protocol"), {
87
+ onNone: () => [],
88
+ onSome: (value) => value.split(",").map((protocol) => protocol.trim()),
89
+ });
90
+ /** A connection route answers only a WebSocket upgrade that offers our subprotocol first, which the server then selects. */
91
+ /** A server-sent event response that proxies must neither cache nor buffer. */
92
+ const eventStream = (body) => HttpServerResponse.stream(body, {
93
+ contentType: "text/event-stream",
94
+ headers: { "cache-control": "no-cache", "x-accel-buffering": "no" },
95
+ });
96
+ const isConnectionUpgrade = (request) => Option.exists(Headers.get(request.headers, "upgrade"), (value) => value.toLowerCase() === "websocket") && offeredProtocols(request)[0] === SUBPROTOCOL;
97
+ const resolve = (actor) => {
98
+ const definition = descriptorOf(actor)?.served;
99
+ if (definition === undefined)
100
+ throw new Error(`Actor.serve: ${actor.name} is not an Actor.make definition`);
101
+ if (!NAME.test(definition.name))
102
+ throw new Error(`Actor.serve: actor name ${definition.name} is not [A-Za-z][A-Za-z0-9_]*`);
103
+ for (const member of [...definition.members, ...definition.connections, ...definition.streams]) {
104
+ if (!NAME.test(member.tag) || RESERVED_MEMBERS.has(member.tag))
105
+ throw new Error(`Actor.serve: ${definition.name}.${member.tag} can't be served`);
106
+ }
107
+ return definition;
108
+ };
109
+ /**
110
+ * Serves `actors`' public commands, reducers, queries, streams, connections,
111
+ * event feeds, and content over HTTP by adding routes to the `HttpRouter`,
112
+ * plus `/protocol`, `/command-ids`, `/ready`, and, when configured, the
113
+ * OpenAPI document. Building the layer dies when an actor is listed twice, a
114
+ * member collides with a protocol route, the runtime's retry window is under
115
+ * 60 seconds, an actor's layers are not provided, `limits.contentBytes` is out
116
+ * of range, or the auth provider declares two credentials of one OpenAPI
117
+ * scheme (the document names one scheme per kind, so the second would vanish).
118
+ *
119
+ * Guarantees and ordering:
120
+ * - Every route checks the origin before authenticating, then the protocol
121
+ * version.
122
+ * - A credential bound to a request admits only that request, checked before
123
+ * any turn; a renewal binds the session's upgrade path and its own session id.
124
+ * - A malformed `durable-min-version` is refused, because ignoring it would
125
+ * silently drop the caller's read-your-writes guarantee.
126
+ * - A command accepted for execution continues if the client disconnects: the
127
+ * runtime owns its execution and only the caller's wait is interrupted.
128
+ * - A request with neither `content-length` nor `transfer-encoding` may carry
129
+ * no body stream at all, and is read as empty.
130
+ * - A connection is a WebSocket upgrade; nothing is authorized or woken before
131
+ * its `hello`. A non-browser client, or a cookie provider, may authenticate
132
+ * the upgrade request itself.
133
+ * - An event feed authorizes every tag the caller reads (there is no
134
+ * wildcard), answers cursor errors before any stream starts, and never
135
+ * creates the actor it follows. A browser's reconnect resumes from
136
+ * `Last-Event-ID`.
137
+ * - A content upload streams into the store hashed as it arrives; past the
138
+ * limit its transaction rolls back and nothing is stored. Under a credential
139
+ * bound to its request it is read whole first, up to the same limit, so the
140
+ * binding is checked before anything is stored. A sweep between
141
+ * resolving a download's name and reading its bytes ends the body before any
142
+ * byte, short of its declared length.
143
+ * - `/ready` carries no credentials and is never cached, because a stale
144
+ * answer would route traffic to a draining runner.
145
+ * - With a provider that has `refreshKeys`, the edge's push after it revokes a
146
+ * signing key rereads the key set at once.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const Api = Actor.serve({ actors: [Counter], auth: Actor.auth.none, basePath: "/api" })
151
+ * ```
152
+ */
153
+ export const serve = (options) => HttpRouter.use(Effect.fnUntraced(function* (router) {
154
+ const basePath = (options.basePath ?? "").replace(/\/+$/, "");
155
+ const definitions = options.actors.map(resolve);
156
+ const names = new Set();
157
+ for (const definition of definitions) {
158
+ if (names.has(definition.name))
159
+ return yield* Effect.die(new Error(`Actor.serve: ${definition.name} is listed twice`));
160
+ names.add(definition.name);
161
+ const collision = [
162
+ ...definition.members,
163
+ ...definition.connections,
164
+ ...definition.streams,
165
+ ].find((member) => PROTOCOL_OPERATIONS.has(`${definition.name}.${member.tag}`));
166
+ if (collision !== undefined)
167
+ return yield* Effect.die(new Error(`Actor.serve: ${definition.name}.${collision.tag} collides with a protocol operation id`));
168
+ }
169
+ const documentPaths = [
170
+ { option: "openapi", path: options.openapi?.path },
171
+ { option: "mcp", path: options.mcp?.path },
172
+ ];
173
+ for (const { option, path } of documentPaths)
174
+ if (path !== undefined &&
175
+ (PROTOCOL_PATHS.has(path) ||
176
+ path === KEY_REFRESH_PATH ||
177
+ path === "/actors" ||
178
+ path.startsWith("/actors/")))
179
+ return yield* Effect.die(new Error(`Actor.serve: ${option}.path ${path} collides with a protocol route`));
180
+ if (options.mcp !== undefined && options.mcp.path === options.openapi?.path)
181
+ return yield* Effect.die(new Error(`Actor.serve: mcp.path and openapi.path are both ${options.mcp.path}`));
182
+ const schemes = options.auth.credentials.map(schemeName);
183
+ if (new Set(schemes).size !== schemes.length)
184
+ return yield* Effect.die(new Error(`Actor.serve: the auth provider declares more than one credential documented as the same OpenAPI scheme (${schemes.join(", ")})`));
185
+ const actors = yield* InternalActors;
186
+ const control = yield* RuntimeControl;
187
+ const fleet = options.fleet ?? [];
188
+ for (const view of fleet)
189
+ if (!actors.fleetViews.has(view.name))
190
+ return yield* Effect.die(new Error(`Actor.serve: fleet view ${view.name} is not registered; list it in the runtime's fleet option`));
191
+ if (new Set(fleet.map(({ name }) => name)).size !== fleet.length)
192
+ return yield* Effect.die(new Error("Actor.serve: a fleet view is listed twice"));
193
+ if (actors.retryWindowMs < MIN_RETRY_WINDOW_MS)
194
+ return yield* Effect.die(new Error(`Actor.serve: retryWindowMs is ${actors.retryWindowMs}; served clients need at least 60 seconds`));
195
+ for (const definition of definitions) {
196
+ const registered = actors.registered(definition.name);
197
+ const missing = definition.members.some((member) => member.kind === "query" ? !registered.queries : !registered.commands) ||
198
+ (definition.connections.length + definition.streams.length > 0 && !registered.commands);
199
+ if (missing)
200
+ return yield* Effect.die(new Error(`Actor.serve: provide ${definition.name}'s command and query layers`));
201
+ }
202
+ const clock = yield* databaseClock;
203
+ const context = yield* Effect.context();
204
+ const origins = new Set(options.origins ?? []);
205
+ const requestBytes = options.limits?.requestBytes ?? 1024 * 1024;
206
+ const credentialBytes = options.limits?.credentialBytes ?? CREDENTIAL_BYTES;
207
+ const contentBytes = options.limits?.contentBytes ?? MAX_CONTENT_BYTES;
208
+ if (!Number.isSafeInteger(contentBytes) ||
209
+ contentBytes < 0 ||
210
+ contentBytes > MAX_CONTENT_BYTES)
211
+ return yield* Effect.die(new Error(`Actor.serve: limits.contentBytes must be a whole number of bytes up to ${MAX_CONTENT_BYTES}`));
212
+ const contentStore = yield* Effect.serviceOption(ContentStore);
213
+ const withCookies = readsCookies(options.auth);
214
+ const withAssertion = options.auth.credentials.some(Credential.$is("Assertion"));
215
+ /**
216
+ * A runner that accepts edge assertions is hosted, and its edge reserves
217
+ * capacity for any keyed request as a command. A query, watch or stream
218
+ * never commits a receipt that could settle that reservation, so a hosted
219
+ * runner refuses a command identity on them whether or not the request
220
+ * carries an assertion, and the edge releases the reservation on that
221
+ * refusal.
222
+ */
223
+ const queryIdentity = Effect.fnUntraced(function* (request) {
224
+ if (!withAssertion || !Headers.has(request.headers, "idempotency-key"))
225
+ return;
226
+ return yield* ActorError.make({
227
+ reason: InvalidInput.make({
228
+ code: "decode",
229
+ issues: [
230
+ {
231
+ path: "idempotency-key",
232
+ message: "Hosted queries, watches and streams do not accept command identities",
233
+ },
234
+ ],
235
+ }),
236
+ });
237
+ });
238
+ const usageToken = (request, authenticated) => {
239
+ if (!withAssertion ||
240
+ authenticated.binding === undefined ||
241
+ !Headers.has(request.headers, ASSERTION_HEADER))
242
+ return undefined;
243
+ const token = new URL(request.url, "http://runner").searchParams.get("__akter_usage");
244
+ return token !== null &&
245
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(token)
246
+ ? token
247
+ : undefined;
248
+ };
249
+ const api = buildServedApi({
250
+ definitions,
251
+ fleet: fleet.map(({ name, groupBy }) => ({ name, groupBy })),
252
+ basePath,
253
+ content: Option.isSome(contentStore),
254
+ });
255
+ const withProtocol = (request, response) => {
256
+ const origin = Headers.get(request.headers, "origin");
257
+ const stamped = HttpServerResponse.setHeaders(response, {
258
+ "durable-protocol": String(PROTOCOL),
259
+ "durable-now": String(clock.now()),
260
+ });
261
+ return Option.isSome(origin) && origins.has(origin.value)
262
+ ? HttpServerResponse.setHeaders(stamped, {
263
+ "access-control-allow-origin": origin.value,
264
+ "access-control-expose-headers": EXPOSED_HEADERS,
265
+ vary: "Origin",
266
+ })
267
+ : stamped;
268
+ };
269
+ const guard = Effect.fnUntraced(function* (request) {
270
+ const origin = Headers.get(request.headers, "origin");
271
+ if (Option.isSome(origin) &&
272
+ !origins.has(origin.value) &&
273
+ !isSameOrigin({ request, origin: origin.value }))
274
+ return yield* invalidInput("origin_not_allowed");
275
+ const protocol = Headers.get(request.headers, "durable-protocol");
276
+ if (Option.isSome(protocol) && protocol.value.trim() !== String(PROTOCOL))
277
+ return yield* invalidInput("unsupported_protocol");
278
+ });
279
+ /**
280
+ * Answers one route. A handler passes the authenticated caller in each
281
+ * request it makes, and runs with `Anonymous` as the ambient caller, so
282
+ * a path that forgot to pass it is refused like a visitor without
283
+ * credentials instead of running as the process's trusted `System`.
284
+ */
285
+ const respond = (handler, headers) => (request) => guard(request).pipe(Effect.andThen(handler(request)), Effect.provideService(CurrentCaller, Anonymous.make({})), Effect.catch((error) => actorErrorResponse(error)), Effect.catchCause(defectResponse), Effect.map((response) => withProtocol(request, headers === undefined
286
+ ? response
287
+ : HttpServerResponse.setHeaders(response, headers(request)))));
288
+ const authenticate = Effect.fnUntraced(function* (request, credential) {
289
+ if (oversizedCredential({
290
+ provider: options.auth,
291
+ headers: request.headers,
292
+ limit: credentialBytes,
293
+ credential,
294
+ }))
295
+ return yield* invalidInput("too_large");
296
+ const cookies = withCookies ? request.cookies : {};
297
+ const authenticated = yield* options.auth
298
+ .authenticate(credential === undefined
299
+ ? { headers: request.headers, cookies }
300
+ : { headers: request.headers, cookies, credential })
301
+ .pipe(Effect.provideContext(context), Effect.mapError((reason) => ActorError.make({ reason })));
302
+ if (!isPrincipal(authenticated.caller))
303
+ return yield* Effect.die(new Error("Actor.serve: an auth provider returned a System caller"));
304
+ if (!withinLimits(authenticated)) {
305
+ yield* Effect.logWarning("Actor.serve: auth provider result exceeds principal limits");
306
+ return yield* ActorError.make({
307
+ reason: Unauthorized.make({ code: "invalid_credentials" }),
308
+ });
309
+ }
310
+ return authenticated;
311
+ });
312
+ const readBytes = Effect.fnUntraced(function* (request, limit = requestBytes) {
313
+ const length = Headers.get(request.headers, "content-length");
314
+ if (Option.isSome(length)) {
315
+ if (!DECIMAL.test(length.value))
316
+ return yield* invalidInput("decode");
317
+ const declared = Number(length.value);
318
+ if (declared > limit)
319
+ return yield* invalidInput("too_large");
320
+ if (declared === 0)
321
+ return new Uint8Array(0);
322
+ const framed = new Uint8Array(declared);
323
+ let filled = 0;
324
+ yield* request.stream.pipe(Stream.mapError(() => invalidInput("decode")), Stream.runForEach((chunk) => {
325
+ if (filled + chunk.byteLength > declared)
326
+ return Effect.fail(invalidInput("too_large"));
327
+ framed.set(chunk, filled);
328
+ filled += chunk.byteLength;
329
+ return Effect.void;
330
+ }));
331
+ if (filled !== declared)
332
+ return yield* invalidInput("decode");
333
+ return framed;
334
+ }
335
+ const unframed = Option.isNone(length) && !Headers.has(request.headers, "transfer-encoding");
336
+ let received = 0;
337
+ const chunks = yield* request.stream.pipe(Stream.catch(() => unframed && received === 0 ? Stream.empty : Stream.fail(invalidInput("decode"))), Stream.runFoldEffect(() => ({ size: 0, chunks: [] }), (acc, chunk) => {
338
+ const size = acc.size + chunk.byteLength;
339
+ if (size > limit)
340
+ return Effect.fail(invalidInput("too_large"));
341
+ received = size;
342
+ acc.chunks.push(chunk);
343
+ return Effect.succeed({ size, chunks: acc.chunks });
344
+ }));
345
+ const body = new Uint8Array(chunks.size);
346
+ let offset = 0;
347
+ for (const chunk of chunks.chunks) {
348
+ body.set(chunk, offset);
349
+ offset += chunk.byteLength;
350
+ }
351
+ return body;
352
+ });
353
+ const decodeJsonBody = Effect.fnUntraced(function* (request, body) {
354
+ if (body.byteLength === 0)
355
+ return undefined;
356
+ if (!Headers.has(request.headers, "content-type"))
357
+ return yield* invalidInput("unsupported_media_type");
358
+ const text = yield* Effect.try({
359
+ try: () => strictUtf8.decode(body),
360
+ catch: () => invalidInput("decode"),
361
+ });
362
+ return yield* decodeJson(text).pipe(Effect.mapError((error) => undecodable(error)));
363
+ });
364
+ const refuseBinding = ActorError.make({
365
+ reason: Unauthorized.make({ code: "invalid_credentials" }),
366
+ });
367
+ const checkBinding = (authenticated, request, body) => authenticated.binding === undefined
368
+ ? Effect.void
369
+ : requestDigest({
370
+ method: request.method,
371
+ target: request.url,
372
+ idempotencyKey: Option.getOrUndefined(Headers.get(request.headers, "idempotency-key")),
373
+ body,
374
+ }).pipe(Effect.flatMap((digest) => digest === authenticated.binding?.request
375
+ ? Effect.void
376
+ : Effect.fail(refuseBinding)));
377
+ /** Checks a route that ignores its body against the credential's binding, reading the body only when bound. */
378
+ const checkUnreadBody = (authenticated, request) => authenticated.binding === undefined
379
+ ? Effect.void
380
+ : readBytes(request).pipe(Effect.flatMap((body) => checkBinding(authenticated, request, body)));
381
+ const checkRenewal = (authenticated, request) => {
382
+ const binding = authenticated.binding;
383
+ if (binding === undefined)
384
+ return Effect.void;
385
+ if (binding.session === undefined)
386
+ return Effect.fail(refuseBinding);
387
+ return reauthenticationDigest({ path: request.url, session: binding.session }).pipe(Effect.flatMap((digest) => digest === binding.request ? Effect.void : Effect.fail(refuseBinding)));
388
+ };
389
+ const empty = new Uint8Array(0);
390
+ /** Reads a request body a JSON content type or none allows, checked against the credential's binding. */
391
+ const readBound = Effect.fnUntraced(function* (request, authenticated) {
392
+ const type = Headers.get(request.headers, "content-type");
393
+ if (Option.isSome(type) && !JSON_TYPE.test(type.value))
394
+ return yield* invalidInput("unsupported_media_type");
395
+ const body = yield* readBytes(request);
396
+ yield* checkBinding(authenticated, request, body);
397
+ return body;
398
+ });
399
+ const refOf = (definition, id, authenticated) => ActorRef.make({ tenant: authenticated.tenant, actor: definition.name, id });
400
+ const commandId = (request) => {
401
+ const header = Headers.get(request.headers, "idempotency-key");
402
+ if (Option.isNone(header))
403
+ return undefined;
404
+ const value = header.value.trim();
405
+ return QUOTED.exec(value)?.[1] ?? value;
406
+ };
407
+ const minVersion = (request) => {
408
+ const token = Option.getOrUndefined(Headers.get(request.headers, "durable-min-version"));
409
+ if (token === undefined || isVersion(token))
410
+ return Effect.succeed(token);
411
+ return Effect.fail(ActorError.make({
412
+ reason: InvalidInput.make({
413
+ code: "decode",
414
+ issues: [
415
+ {
416
+ path: "durable-min-version",
417
+ message: "Expected a non-negative decimal integer without leading zeros",
418
+ },
419
+ ],
420
+ }),
421
+ }));
422
+ };
423
+ const successBody = (member, value) => SchemaAST.isVoid(member.output.ast)
424
+ ? Effect.succeed({ ok: true, status: 204, body: undefined })
425
+ : Effect.map(Effect.orDie(decodeSuccess(value)), (decoded) => ({
426
+ ok: true,
427
+ status: 200,
428
+ body: decoded.value ?? null,
429
+ }));
430
+ const failureBody = Effect.fnUntraced(function* (member, value) {
431
+ const status = yield* member.failureStatus(value);
432
+ return { ok: false, status, body: yield* decodeJson(value) };
433
+ }, Effect.orDie);
434
+ const outcomeBody = (member, outcome) => Outcome.match(outcome, {
435
+ Success: (success) => successBody(member, success.value),
436
+ Failure: (failure) => failureBody(member, failure.value),
437
+ Defect: (defect) => Effect.failCause(Cause.die(defect.cause)),
438
+ Acknowledged: (acknowledged) => Effect.die(new Error(`Unexpected ${acknowledged.reason} acknowledgement`)),
439
+ });
440
+ const outcomeResponse = (member, outcome) => Effect.map(outcomeBody(member, outcome), ({ status, body }) => body === undefined
441
+ ? HttpServerResponse.empty({ status })
442
+ : HttpServerResponse.jsonUnsafe(body, { status }));
443
+ const runMember = Effect.fnUntraced(function* (input) {
444
+ const { definition, member, authenticated } = input;
445
+ const payload = yield* member
446
+ .payload(input.body)
447
+ .pipe(Effect.mapError((error) => undecodable(error)));
448
+ let call = Request.make({
449
+ ref: refOf(definition, input.id, authenticated),
450
+ caller: authenticated.caller,
451
+ command: member.tag,
452
+ commandId: input.commandId,
453
+ payload,
454
+ });
455
+ if (input.usageToken !== undefined)
456
+ call = Request.make({ ...call, usageToken: input.usageToken });
457
+ if (member.kind === "query")
458
+ return {
459
+ outcome: yield* actors.query(call, input.minVersion === undefined ? undefined : yield* input.minVersion),
460
+ version: undefined,
461
+ replayed: undefined,
462
+ };
463
+ return yield* actors.execute(call);
464
+ });
465
+ const memberHandler = (definition, member) => Effect.fnUntraced(function* (request) {
466
+ if (member.kind !== "query" && actors.overloaded())
467
+ return yield* overloaded("runner");
468
+ const id = yield* pathId(definition);
469
+ const authenticated = yield* authenticate(request);
470
+ if (member.kind === "query")
471
+ yield* queryIdentity(request);
472
+ const key = member.kind === "query" ? "" : commandId(request);
473
+ if (key === undefined)
474
+ return yield* invalidInput("missing_command_id");
475
+ const bytes = yield* readBound(request, authenticated);
476
+ const { outcome, version, replayed } = yield* runMember({
477
+ definition,
478
+ member,
479
+ id,
480
+ authenticated,
481
+ commandId: key,
482
+ usageToken: usageToken(request, authenticated),
483
+ body: yield* decodeJsonBody(request, bytes),
484
+ minVersion: minVersion(request),
485
+ });
486
+ const original = yield* outcomeResponse(member, outcome);
487
+ const response = version === undefined
488
+ ? original
489
+ : HttpServerResponse.setHeader(original, "durable-replayed", String(replayed ?? false));
490
+ return version === undefined
491
+ ? response
492
+ : HttpServerResponse.setHeader(response, "durable-version", version);
493
+ });
494
+ const callTool = (authenticated) => (call) => {
495
+ const route = call.tool.route;
496
+ return Effect.gen(function* () {
497
+ if (route === undefined)
498
+ return { ok: true, value: { commandId: yield* actors.mintCommandId } };
499
+ const { definition, member } = route;
500
+ const { outcome } = yield* runMember({
501
+ definition,
502
+ member,
503
+ id: yield* decodeId(definition, call.id ?? ""),
504
+ authenticated,
505
+ commandId: call.commandId ?? "",
506
+ body: call.input,
507
+ });
508
+ const settled = yield* outcomeBody(member, outcome);
509
+ return settled.ok
510
+ ? { ok: true, value: settled.body }
511
+ : { ok: false, body: settled.body };
512
+ }).pipe(Effect.catch((error) => Effect.map(actorErrorBody(error), (body) => ({ ok: false, body }))), Effect.catchCause((cause) => Effect.map(defectBody(cause), (body) => ({ ok: false, body }))));
513
+ };
514
+ const streamHandler = (definition, member) => Effect.fnUntraced(function* (request) {
515
+ const id = yield* pathId(definition);
516
+ const authenticated = yield* authenticate(request);
517
+ yield* queryIdentity(request);
518
+ const bytes = yield* readBound(request, authenticated);
519
+ const body = yield* decodeJsonBody(request, bytes);
520
+ const payload = yield* member
521
+ .payload(body)
522
+ .pipe(Effect.mapError((error) => undecodable(error)));
523
+ const elements = actors.subscribe(Request.make({
524
+ ref: refOf(definition, id, authenticated),
525
+ caller: authenticated.caller,
526
+ command: member.tag,
527
+ commandId: "",
528
+ payload,
529
+ }));
530
+ return elements.pipe(streamResponse, eventStream);
531
+ });
532
+ const watchHandler = (definition, member) => Effect.fnUntraced(function* (request) {
533
+ const id = yield* pathId(definition);
534
+ const authenticated = yield* authenticate(request);
535
+ yield* queryIdentity(request);
536
+ if (!member.watch)
537
+ return yield* invalidInput("not_watchable");
538
+ const bytes = yield* readBound(request, authenticated);
539
+ const body = yield* decodeJsonBody(request, bytes);
540
+ const payload = yield* member
541
+ .payload(body)
542
+ .pipe(Effect.mapError((error) => undecodable(error)));
543
+ const token = usageToken(request, authenticated);
544
+ let call = Request.make({
545
+ ref: refOf(definition, id, authenticated),
546
+ caller: authenticated.caller,
547
+ command: member.tag,
548
+ commandId: "",
549
+ payload,
550
+ });
551
+ if (token !== undefined)
552
+ call = Request.make({ ...call, usageToken: token });
553
+ const results = yield* actors.watch(call, {
554
+ minVersion: yield* minVersion(request),
555
+ expiresAt: authenticated.expiresAt === undefined
556
+ ? undefined
557
+ : DateTime.toEpochMillis(authenticated.expiresAt),
558
+ });
559
+ return results.pipe(watchResponse, eventStream);
560
+ });
561
+ /**
562
+ * A fleet view's page for the caller's tenant: equality filters on group
563
+ * columns and `limit` from the query string, and no parameter names a
564
+ * tenant. A repeated or unknown parameter, or a limit outside 1 to
565
+ * 1,000, is refused before any read.
566
+ */
567
+ const fleetHandler = (view) => Effect.fnUntraced(function* (request) {
568
+ const query = new URL(request.url, "http://fleet").searchParams;
569
+ const keys = view.groupBy;
570
+ const filter = {};
571
+ let limit = FLEET_PAGE_DEFAULT;
572
+ for (const name of new Set(query.keys())) {
573
+ const values = query.getAll(name);
574
+ if (values.length !== 1)
575
+ return yield* invalidInput("decode");
576
+ if (name === "limit") {
577
+ limit = Number(values[0]);
578
+ if (!Number.isInteger(limit) || limit < 1 || limit > FLEET_PAGE_LIMIT)
579
+ return yield* invalidInput("decode");
580
+ }
581
+ else if (keys.includes(name))
582
+ filter[name] = values[0];
583
+ else
584
+ return yield* invalidInput("decode");
585
+ }
586
+ const authenticated = yield* authenticate(request);
587
+ yield* checkBinding(authenticated, request, empty);
588
+ const results = yield* actors.fleet({
589
+ view: view.name,
590
+ caller: authenticated.caller,
591
+ tenant: authenticated.tenant,
592
+ filter,
593
+ limit,
594
+ expiresAt: authenticated.expiresAt === undefined
595
+ ? undefined
596
+ : DateTime.toEpochMillis(authenticated.expiresAt),
597
+ });
598
+ return results.pipe(watchResponse, eventStream);
599
+ });
600
+ const awaiting = awaitingHello.get(actors) ?? { count: 0 };
601
+ awaitingHello.set(actors, awaiting);
602
+ const connectionHandler = (definition, connection) => Effect.fnUntraced(function* (request) {
603
+ const id = yield* pathId(definition);
604
+ if (!isConnectionUpgrade(request))
605
+ return yield* invalidInput("unsupported_protocol");
606
+ const upgrade = Headers.has(request.headers, "authorization") ||
607
+ (withCookies && Headers.has(request.headers, "cookie")) ||
608
+ (withAssertion && Headers.has(request.headers, ASSERTION_HEADER))
609
+ ? yield* authenticate(request).pipe(Effect.tap((authenticated) => checkBinding(authenticated, request, empty)))
610
+ : undefined;
611
+ if (awaiting.count >= MAX_AWAITING_HELLO)
612
+ return yield* ActorError.make({ reason: RunnerAtCapacity.make({}) });
613
+ awaiting.count += 1;
614
+ let waiting = true;
615
+ const greeted = Effect.sync(() => {
616
+ if (!waiting)
617
+ return;
618
+ waiting = false;
619
+ awaiting.count -= 1;
620
+ });
621
+ yield* request.upgrade.pipe(Effect.flatMap((socket) => socketSession({
622
+ socket,
623
+ connection,
624
+ holder: actors.holder,
625
+ ref: (tenant) => ActorRef.make({ tenant, actor: definition.name, id }),
626
+ upgrade,
627
+ authenticate: (credential) => authenticate(request, credential).pipe(Effect.tap((authenticated) => checkBinding(authenticated, request, empty))),
628
+ reauthenticate: (credential) => authenticate(request, credential).pipe(Effect.tap((authenticated) => checkRenewal(authenticated, request))),
629
+ greeted,
630
+ })), Effect.ensuring(greeted), Effect.scoped, Effect.orDie);
631
+ return HttpServerResponse.empty();
632
+ });
633
+ const feedHandler = (definition) => Effect.fnUntraced(function* (request) {
634
+ const id = yield* pathId(definition);
635
+ const query = new URL(request.url, "http://feed").searchParams;
636
+ const tags = [...new Set(query.getAll("event"))];
637
+ if (tags.length > MAX_FEED_FILTERS)
638
+ return yield* invalidInput("too_many_filters");
639
+ if (tags.length === 0 || tags.some((tag) => !definition.feeds.includes(tag)))
640
+ return yield* invalidInput("unknown_event");
641
+ const after = Option.getOrUndefined(Option.orElse(Headers.get(request.headers, "last-event-id"), () => Option.fromNullishOr(query.get("after"))));
642
+ const authenticated = yield* authenticate(request);
643
+ yield* checkBinding(authenticated, request, empty);
644
+ const ref = refOf(definition, id, authenticated);
645
+ if (!(yield* actors.exists(ref)))
646
+ return yield* ActorError.make({ reason: NotCreated.make({}) });
647
+ const options = {
648
+ actors,
649
+ ref,
650
+ tags,
651
+ caller: authenticated.caller,
652
+ expiresAt: authenticated.expiresAt === undefined
653
+ ? undefined
654
+ : DateTime.toEpochMillis(authenticated.expiresAt),
655
+ };
656
+ const held = yield* openFeed(options);
657
+ const checked = yield* actors.readFeed(ref, tags, after, 1).pipe(Effect.as(undefined), Effect.catchTags({
658
+ UnknownCursor: (error) => Effect.succeed({ error, status: 404 }),
659
+ RetentionGap: (error) => Effect.succeed({ error, status: 410 }),
660
+ }), Effect.tapError(() => held.close));
661
+ if (checked !== undefined) {
662
+ yield* held.close;
663
+ const body = yield* cursorErrorBody(checked.error);
664
+ return HttpServerResponse.jsonUnsafe(body, { status: checked.status });
665
+ }
666
+ return eventStream(feedStream({ options, first: held, after }));
667
+ });
668
+ const uploadHandler = (store) => Effect.fnUntraced(function* (request) {
669
+ const authenticated = yield* authenticate(request);
670
+ if (authenticated.binding !== undefined) {
671
+ const bytes = yield* readBytes(request, contentBytes);
672
+ yield* checkBinding(authenticated, request, bytes);
673
+ return yield* store.upload(authenticated.tenant, Stream.make(bytes), contentBytes).pipe(Effect.catchTag("ContentTooLarge", () => invalidInput("too_large")), Effect.map((ref) => HttpServerResponse.jsonUnsafe(ref, { status: 200 })));
674
+ }
675
+ const length = Headers.get(request.headers, "content-length");
676
+ if (Option.isSome(length) && Number(length.value) > contentBytes)
677
+ return yield* invalidInput("too_large");
678
+ const empty = (Option.isSome(length) && Number(length.value) === 0) ||
679
+ (Option.isNone(length) && !Headers.has(request.headers, "transfer-encoding"));
680
+ const body = empty
681
+ ? Stream.empty
682
+ : request.stream.pipe(Stream.mapError(() => invalidInput("decode")));
683
+ const ref = yield* store
684
+ .upload(authenticated.tenant, body, contentBytes)
685
+ .pipe(Effect.catchTag("ContentTooLarge", () => invalidInput("too_large")));
686
+ return HttpServerResponse.jsonUnsafe(ref, { status: 200 });
687
+ });
688
+ const contentParams = Effect.fnUntraced(function* (definition) {
689
+ const { blob = "", name = "" } = yield* HttpRouter.params;
690
+ if (!definition.contents.includes(blob) || name === "")
691
+ return yield* invalidInput("unknown_content");
692
+ return { blob, name };
693
+ });
694
+ const downloadHandler = (store, definition) => Effect.fnUntraced(function* (request) {
695
+ const id = yield* pathId(definition);
696
+ const { blob, name } = yield* contentParams(definition);
697
+ const authenticated = yield* authenticate(request);
698
+ yield* checkUnreadBody(authenticated, request);
699
+ const ref = refOf(definition, id, authenticated);
700
+ const found = yield* store.download(ref, authenticated.caller, blob, name);
701
+ if (Option.isNone(found))
702
+ return yield* invalidInput("unknown_content");
703
+ return HttpServerResponse.stream(found.value.bytes, {
704
+ contentType: "application/octet-stream",
705
+ contentLength: found.value.size,
706
+ });
707
+ });
708
+ const grantHandler = (store, definition) => Effect.fnUntraced(function* (request) {
709
+ const id = yield* pathId(definition);
710
+ const { blob, name } = yield* contentParams(definition);
711
+ const authenticated = yield* authenticate(request);
712
+ yield* checkUnreadBody(authenticated, request);
713
+ const ref = refOf(definition, id, authenticated);
714
+ const granted = yield* store.grant(ref, authenticated.caller, blob, name);
715
+ if (Option.isNone(granted))
716
+ return yield* invalidInput("unknown_content");
717
+ return HttpServerResponse.jsonUnsafe(granted.value, { status: 200 });
718
+ });
719
+ const requestId = (member) => (request) => {
720
+ if (member.kind === "query")
721
+ return {};
722
+ const key = commandId(request);
723
+ return key === undefined ? {} : { "x-request-id": key };
724
+ };
725
+ const route = (method, path, handler, headers) => router.add(method, `${basePath}${path}`, respond(handler, headers));
726
+ for (const definition of definitions) {
727
+ for (const member of definition.members)
728
+ yield* route("POST", memberPath({ definition, member }), (request) => {
729
+ const handle = memberHandler(definition, member)(request);
730
+ return member.kind === "query" ? handle : actors.admitRequest(handle);
731
+ }, requestId(member));
732
+ for (const member of definition.streams)
733
+ yield* route("POST", memberPath({ definition, member }), streamHandler(definition, member));
734
+ for (const member of definition.members)
735
+ if (member.kind === "query")
736
+ yield* route("POST", `${memberPath({ definition, member })}/watch`, watchHandler(definition, member));
737
+ if (definition.feeds.length > 0)
738
+ yield* route("GET", memberPath({ definition, member: { tag: FEED_ROUTE } }), feedHandler(definition));
739
+ for (const connection of definition.connections)
740
+ yield* route("GET", memberPath({ definition, member: connection }), connectionHandler(definition, connection));
741
+ }
742
+ for (const view of fleet)
743
+ yield* route("GET", `/fleet/${view.name}`, fleetHandler(view));
744
+ if (Option.isSome(contentStore)) {
745
+ const store = contentStore.value;
746
+ yield* route("POST", "/content", uploadHandler(store));
747
+ for (const definition of definitions)
748
+ if (definition.contents.length > 0) {
749
+ const entry = `${memberPath({ definition, member: { tag: CONTENT_ROUTE } })}/:blob/:name`;
750
+ yield* route("GET", entry, downloadHandler(store, definition));
751
+ yield* route("POST", `${entry}/grant`, grantHandler(store, definition));
752
+ }
753
+ }
754
+ yield* route("GET", "/protocol", () => Effect.succeed(HttpServerResponse.jsonUnsafe({
755
+ protocol: PROTOCOL,
756
+ retryWindowMs: actors.retryWindowMs,
757
+ now: clock.now(),
758
+ })));
759
+ yield* route("GET", "/ready", () => Effect.map(control.readiness, (readiness) => HttpServerResponse.jsonUnsafe(readiness, {
760
+ status: readiness.ready ? 200 : 503,
761
+ headers: { "cache-control": "no-store" },
762
+ })));
763
+ yield* route("POST", "/command-ids", (request) => Effect.gen(function* () {
764
+ const authenticated = yield* authenticate(request);
765
+ yield* checkUnreadBody(authenticated, request);
766
+ return HttpServerResponse.jsonUnsafe({ commandId: yield* actors.mintCommandId });
767
+ }));
768
+ const refreshKeys = options.auth.refreshKeys;
769
+ if (refreshKeys !== undefined)
770
+ yield* route("POST", KEY_REFRESH_PATH, (request) => refreshKeys({ headers: request.headers, cookies: {} }).pipe(Effect.provideContext(context), Effect.mapError((reason) => ActorError.make({ reason })), Effect.as(HttpServerResponse.empty({ status: 204 }))));
771
+ const preflight = HttpServerResponse.empty({
772
+ status: 204,
773
+ headers: {
774
+ "access-control-allow-methods": "GET, POST",
775
+ "access-control-allow-headers": ALLOWED_HEADERS,
776
+ "access-control-max-age": "600",
777
+ },
778
+ });
779
+ const fallback = (request) => request.method === "OPTIONS" ? Effect.succeed(preflight) : invalidInput("unknown_route");
780
+ yield* route("*", "/actors/*", fallback);
781
+ yield* route("OPTIONS", "/*", fallback);
782
+ if (options.openapi !== undefined || options.mcp !== undefined) {
783
+ const spec = openApiDocument({
784
+ api,
785
+ auth: options.auth,
786
+ title: options.openapi?.title ?? "akter",
787
+ version: options.openapi?.version ?? "1",
788
+ });
789
+ if (options.openapi !== undefined)
790
+ yield* route("GET", options.openapi.path, () => Effect.succeed(HttpServerResponse.jsonUnsafe(spec)));
791
+ if (options.mcp !== undefined) {
792
+ const endpoint = {
793
+ tools: yield* mcpTools({ document: spec, basePath, definitions }),
794
+ info: {
795
+ name: options.mcp.name ?? "akter",
796
+ version: options.mcp.version ?? "1",
797
+ },
798
+ };
799
+ yield* route("POST", options.mcp.path, (request) => Effect.gen(function* () {
800
+ const authenticated = yield* authenticate(request);
801
+ const body = yield* readBound(request, authenticated);
802
+ return yield* handleMcp({ ...endpoint, call: callTool(authenticated) }, { headers: request.headers, body });
803
+ }));
804
+ const postOnly = () => Effect.succeed(HttpServerResponse.empty({ status: 405, headers: { allow: "POST" } }));
805
+ yield* route("GET", options.mcp.path, postOnly);
806
+ yield* route("DELETE", options.mcp.path, postOnly);
807
+ }
808
+ }
809
+ }));