@rikalabs/akter 0.1.0-alpha.0 → 0.1.0-alpha.3

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 (308) hide show
  1. package/CHANGELOG.md +46 -3
  2. package/README.md +2 -2
  3. package/dist/actor/definition.d.ts +13 -9
  4. package/dist/actor/descriptor.d.ts +2 -2
  5. package/dist/actor/descriptor.js +4 -4
  6. package/dist/client/calls.js +1 -1
  7. package/dist/client/index.d.ts +1 -1
  8. package/dist/client/index.js +1 -1
  9. package/dist/errors/actor.d.ts +12 -42
  10. package/dist/errors/actor.js +13 -42
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/members/payload.d.ts +1 -1
  14. package/dist/protocol/inspection.d.ts +221 -1
  15. package/dist/protocol/inspection.js +151 -2
  16. package/dist/protocol/wire.d.ts +6 -49
  17. package/dist/protocol/wire.js +1 -10
  18. package/dist/runtime/actors.d.ts +3 -0
  19. package/dist/runtime/adoption/backfill.js +1 -1
  20. package/dist/runtime/adoption/enforce.js +2 -2
  21. package/dist/runtime/adoption/observe.js +1 -1
  22. package/dist/runtime/adoption/plan.d.ts +1 -1
  23. package/dist/runtime/adoption/startup.js +1 -1
  24. package/dist/runtime/adoption/status.d.ts +1 -1
  25. package/dist/runtime/app.d.ts +65 -0
  26. package/dist/runtime/app.js +38 -0
  27. package/dist/runtime/connections/holder.d.ts +11 -1
  28. package/dist/runtime/connections/holder.js +8 -4
  29. package/dist/runtime/connections/streams.d.ts +4 -4
  30. package/dist/runtime/content/store.js +15 -11
  31. package/dist/runtime/database/bounded.d.ts +4 -18
  32. package/dist/runtime/database/bounded.js +36 -22
  33. package/dist/runtime/database/coordination.d.ts +12 -3
  34. package/dist/runtime/database/coordination.js +29 -36
  35. package/dist/runtime/database/migrations.d.ts +61 -29
  36. package/dist/runtime/database/migrations.js +375 -139
  37. package/dist/runtime/database/replica.d.ts +4 -1
  38. package/dist/runtime/database/replica.js +4 -1
  39. package/dist/runtime/database/schema.d.ts +13 -0
  40. package/dist/runtime/database/schema.js +13 -0
  41. package/dist/runtime/database/shards.d.ts +31 -13
  42. package/dist/runtime/database/shards.js +38 -26
  43. package/dist/runtime/entity/register.d.ts +8 -1
  44. package/dist/runtime/entity/register.js +58 -7
  45. package/dist/runtime/fleet/checks.js +4 -4
  46. package/dist/runtime/fleet/maintainer.d.ts +1 -1
  47. package/dist/runtime/fleet/maintainer.js +1 -1
  48. package/dist/runtime/index.d.ts +2 -0
  49. package/dist/runtime/index.js +1 -0
  50. package/dist/runtime/inspector/layer.d.ts +20 -0
  51. package/dist/runtime/inspector/layer.js +224 -5
  52. package/dist/runtime/inspector/queries.d.ts +54 -8
  53. package/dist/runtime/inspector/queries.js +84 -15
  54. package/dist/runtime/layer.d.ts +48 -39
  55. package/dist/runtime/layer.js +124 -56
  56. package/dist/runtime/operators/audit.d.ts +3 -0
  57. package/dist/runtime/operators/audit.js +6 -2
  58. package/dist/runtime/operators/repair.js +30 -10
  59. package/dist/runtime/operators/seed.js +4 -2
  60. package/dist/runtime/payloads/versions.d.ts +4 -4
  61. package/dist/runtime/payloads/versions.js +24 -15
  62. package/dist/runtime/queries.js +4 -2
  63. package/dist/runtime/registration.d.ts +5 -2
  64. package/dist/runtime/registration.js +3 -3
  65. package/dist/runtime/runner.d.ts +12 -0
  66. package/dist/runtime/runner.js +12 -0
  67. package/dist/runtime/storage/codec.d.ts +19 -7
  68. package/dist/runtime/storage/codec.js +23 -7
  69. package/dist/runtime/storage/placements.d.ts +6 -1
  70. package/dist/runtime/storage/placements.js +98 -6
  71. package/dist/runtime/storage/retention.d.ts +6 -0
  72. package/dist/runtime/storage/retention.js +25 -13
  73. package/dist/runtime/subscriptions/relay.js +47 -44
  74. package/dist/runtime/subscriptions/storage.d.ts +3 -2
  75. package/dist/runtime/subscriptions/storage.js +9 -6
  76. package/dist/runtime/telemetry/live.d.ts +255 -0
  77. package/dist/runtime/telemetry/live.js +540 -0
  78. package/dist/runtime/telemetry/metrics.d.ts +2 -0
  79. package/dist/runtime/telemetry/metrics.js +4 -1
  80. package/dist/runtime/telemetry/sampler.js +33 -13
  81. package/dist/runtime/telemetry/spans.d.ts +1 -1
  82. package/dist/runtime/telemetry/spans.js +1 -1
  83. package/dist/runtime/topology/locks.d.ts +6 -3
  84. package/dist/runtime/topology/locks.js +27 -4
  85. package/dist/runtime/turn/execute.d.ts +2 -0
  86. package/dist/runtime/turn/execute.js +5 -2
  87. package/dist/runtime/turn/pipeline.d.ts +5 -12
  88. package/dist/runtime/turn/pipeline.js +11 -11
  89. package/dist/runtime/turn/relay.d.ts +13 -3
  90. package/dist/runtime/turn/relay.js +27 -14
  91. package/dist/runtime/turn/rows.js +4 -4
  92. package/dist/runtime/workflows/compatibility.d.ts +1 -1
  93. package/dist/runtime/workflows/compatibility.js +85 -31
  94. package/dist/runtime/workflows/engine.js +20 -17
  95. package/dist/serve/layer.js +3 -2
  96. package/dist/tables/owned.d.ts +1 -1
  97. package/dist/tables/owned.js +1 -1
  98. package/dist/testing/actor-test.d.ts +2 -24
  99. package/dist/testing/actor-test.js +1 -27
  100. package/dist/testing/index.d.ts +0 -12
  101. package/dist/testing/index.js +0 -6
  102. package/package.json +1 -1
  103. package/dist/runtime/database/neki/migrations.d.ts +0 -31
  104. package/dist/runtime/database/neki/migrations.js +0 -276
  105. package/dist/runtime/database/neki/session.d.ts +0 -23
  106. package/dist/runtime/database/neki/session.js +0 -31
  107. package/dist/testing/cluster.d.ts +0 -93
  108. package/dist/testing/cluster.js +0 -296
  109. package/dist/testing/conformance/access.d.ts +0 -5
  110. package/dist/testing/conformance/access.js +0 -531
  111. package/dist/testing/conformance/admission.d.ts +0 -11
  112. package/dist/testing/conformance/admission.js +0 -182
  113. package/dist/testing/conformance/adoption.d.ts +0 -2
  114. package/dist/testing/conformance/adoption.js +0 -1019
  115. package/dist/testing/conformance/assertions.d.ts +0 -202
  116. package/dist/testing/conformance/assertions.js +0 -849
  117. package/dist/testing/conformance/batches.d.ts +0 -41
  118. package/dist/testing/conformance/batches.js +0 -388
  119. package/dist/testing/conformance/blobs.d.ts +0 -14
  120. package/dist/testing/conformance/blobs.js +0 -405
  121. package/dist/testing/conformance/capacity.d.ts +0 -8
  122. package/dist/testing/conformance/capacity.js +0 -433
  123. package/dist/testing/conformance/client.d.ts +0 -5
  124. package/dist/testing/conformance/client.js +0 -719
  125. package/dist/testing/conformance/cold-serve.d.ts +0 -16
  126. package/dist/testing/conformance/cold-serve.js +0 -254
  127. package/dist/testing/conformance/connections/actors.d.ts +0 -1143
  128. package/dist/testing/conformance/connections/actors.js +0 -180
  129. package/dist/testing/conformance/connections/harness.d.ts +0 -1754
  130. package/dist/testing/conformance/connections/harness.js +0 -142
  131. package/dist/testing/conformance/connections/holders.d.ts +0 -4
  132. package/dist/testing/conformance/connections/holders.js +0 -165
  133. package/dist/testing/conformance/connections/lifecycle.d.ts +0 -4
  134. package/dist/testing/conformance/connections/lifecycle.js +0 -113
  135. package/dist/testing/conformance/connections/limits.d.ts +0 -4
  136. package/dist/testing/conformance/connections/limits.js +0 -127
  137. package/dist/testing/conformance/connections/reauthorization.d.ts +0 -4
  138. package/dist/testing/conformance/connections/reauthorization.js +0 -170
  139. package/dist/testing/conformance/connections/resync.d.ts +0 -4
  140. package/dist/testing/conformance/connections/resync.js +0 -360
  141. package/dist/testing/conformance/connections/takeover.d.ts +0 -4
  142. package/dist/testing/conformance/connections/takeover.js +0 -287
  143. package/dist/testing/conformance/connections.d.ts +0 -4
  144. package/dist/testing/conformance/connections.js +0 -15
  145. package/dist/testing/conformance/content-blobs.d.ts +0 -16
  146. package/dist/testing/conformance/content-blobs.js +0 -534
  147. package/dist/testing/conformance/counter.d.ts +0 -16
  148. package/dist/testing/conformance/counter.js +0 -813
  149. package/dist/testing/conformance/cron.d.ts +0 -5
  150. package/dist/testing/conformance/cron.js +0 -665
  151. package/dist/testing/conformance/drain.d.ts +0 -26
  152. package/dist/testing/conformance/drain.js +0 -540
  153. package/dist/testing/conformance/effect-control.d.ts +0 -38
  154. package/dist/testing/conformance/effect-control.js +0 -1059
  155. package/dist/testing/conformance/effects.d.ts +0 -23
  156. package/dist/testing/conformance/effects.js +0 -432
  157. package/dist/testing/conformance/events.d.ts +0 -28
  158. package/dist/testing/conformance/events.js +0 -335
  159. package/dist/testing/conformance/export.d.ts +0 -2
  160. package/dist/testing/conformance/export.js +0 -368
  161. package/dist/testing/conformance/fleet.d.ts +0 -444
  162. package/dist/testing/conformance/fleet.js +0 -1009
  163. package/dist/testing/conformance/groups.d.ts +0 -7
  164. package/dist/testing/conformance/groups.js +0 -353
  165. package/dist/testing/conformance/heap.d.ts +0 -3
  166. package/dist/testing/conformance/heap.js +0 -149
  167. package/dist/testing/conformance/http.d.ts +0 -2809
  168. package/dist/testing/conformance/http.js +0 -1234
  169. package/dist/testing/conformance/inspection-views.d.ts +0 -8
  170. package/dist/testing/conformance/inspection-views.js +0 -272
  171. package/dist/testing/conformance/inspector.d.ts +0 -7
  172. package/dist/testing/conformance/inspector.js +0 -650
  173. package/dist/testing/conformance/mint.d.ts +0 -58
  174. package/dist/testing/conformance/mint.js +0 -692
  175. package/dist/testing/conformance/multi-runner.d.ts +0 -8
  176. package/dist/testing/conformance/multi-runner.js +0 -403
  177. package/dist/testing/conformance/neki/plan.d.ts +0 -19
  178. package/dist/testing/conformance/neki/plan.js +0 -30
  179. package/dist/testing/conformance/observability.d.ts +0 -3
  180. package/dist/testing/conformance/observability.js +0 -343
  181. package/dist/testing/conformance/offline.d.ts +0 -4
  182. package/dist/testing/conformance/offline.js +0 -474
  183. package/dist/testing/conformance/operator-harness.d.ts +0 -57
  184. package/dist/testing/conformance/operator-harness.js +0 -78
  185. package/dist/testing/conformance/operator.d.ts +0 -3
  186. package/dist/testing/conformance/operator.js +0 -379
  187. package/dist/testing/conformance/outbox-cross-shard.d.ts +0 -14
  188. package/dist/testing/conformance/outbox-cross-shard.js +0 -160
  189. package/dist/testing/conformance/outbox.d.ts +0 -36
  190. package/dist/testing/conformance/outbox.js +0 -437
  191. package/dist/testing/conformance/payload-migrations.d.ts +0 -3
  192. package/dist/testing/conformance/payload-migrations.js +0 -612
  193. package/dist/testing/conformance/pipeline.d.ts +0 -3
  194. package/dist/testing/conformance/pipeline.js +0 -1040
  195. package/dist/testing/conformance/placement.d.ts +0 -22
  196. package/dist/testing/conformance/placement.js +0 -539
  197. package/dist/testing/conformance/platform.d.ts +0 -40
  198. package/dist/testing/conformance/platform.js +0 -101
  199. package/dist/testing/conformance/progress.d.ts +0 -39
  200. package/dist/testing/conformance/progress.js +0 -666
  201. package/dist/testing/conformance/properties.d.ts +0 -6
  202. package/dist/testing/conformance/properties.js +0 -295
  203. package/dist/testing/conformance/protocols.d.ts +0 -4
  204. package/dist/testing/conformance/protocols.js +0 -618
  205. package/dist/testing/conformance/provider/advertise.d.ts +0 -21
  206. package/dist/testing/conformance/provider/advertise.js +0 -44
  207. package/dist/testing/conformance/read-your-writes.d.ts +0 -20
  208. package/dist/testing/conformance/read-your-writes.js +0 -192
  209. package/dist/testing/conformance/reducers.d.ts +0 -8
  210. package/dist/testing/conformance/reducers.js +0 -258
  211. package/dist/testing/conformance/relay.d.ts +0 -42
  212. package/dist/testing/conformance/relay.js +0 -1115
  213. package/dist/testing/conformance/restore.d.ts +0 -43
  214. package/dist/testing/conformance/restore.js +0 -341
  215. package/dist/testing/conformance/retention.d.ts +0 -14
  216. package/dist/testing/conformance/retention.js +0 -463
  217. package/dist/testing/conformance/rls.d.ts +0 -24
  218. package/dist/testing/conformance/rls.js +0 -576
  219. package/dist/testing/conformance/simulation.d.ts +0 -31
  220. package/dist/testing/conformance/simulation.js +0 -167
  221. package/dist/testing/conformance/single-shard.d.ts +0 -2
  222. package/dist/testing/conformance/single-shard.js +0 -125
  223. package/dist/testing/conformance/singleton.d.ts +0 -3
  224. package/dist/testing/conformance/singleton.js +0 -293
  225. package/dist/testing/conformance/statements.d.ts +0 -60
  226. package/dist/testing/conformance/statements.js +0 -108
  227. package/dist/testing/conformance/streams.d.ts +0 -7
  228. package/dist/testing/conformance/streams.js +0 -280
  229. package/dist/testing/conformance/subscriptions/access.d.ts +0 -4
  230. package/dist/testing/conformance/subscriptions/access.js +0 -98
  231. package/dist/testing/conformance/subscriptions/actors.d.ts +0 -4910
  232. package/dist/testing/conformance/subscriptions/actors.js +0 -372
  233. package/dist/testing/conformance/subscriptions/cluster.d.ts +0 -4
  234. package/dist/testing/conformance/subscriptions/cluster.js +0 -89
  235. package/dist/testing/conformance/subscriptions/delivery.d.ts +0 -4
  236. package/dist/testing/conformance/subscriptions/delivery.js +0 -253
  237. package/dist/testing/conformance/subscriptions/deploys.d.ts +0 -4
  238. package/dist/testing/conformance/subscriptions/deploys.js +0 -162
  239. package/dist/testing/conformance/subscriptions/epochs.d.ts +0 -4
  240. package/dist/testing/conformance/subscriptions/epochs.js +0 -169
  241. package/dist/testing/conformance/subscriptions/failures.d.ts +0 -4
  242. package/dist/testing/conformance/subscriptions/failures.js +0 -108
  243. package/dist/testing/conformance/subscriptions/feeds.d.ts +0 -4
  244. package/dist/testing/conformance/subscriptions/feeds.js +0 -218
  245. package/dist/testing/conformance/subscriptions/harness.d.ts +0 -124
  246. package/dist/testing/conformance/subscriptions/harness.js +0 -143
  247. package/dist/testing/conformance/subscriptions/operator.d.ts +0 -4
  248. package/dist/testing/conformance/subscriptions/operator.js +0 -128
  249. package/dist/testing/conformance/subscriptions/retention.d.ts +0 -4
  250. package/dist/testing/conformance/subscriptions/retention.js +0 -288
  251. package/dist/testing/conformance/subscriptions/routing.d.ts +0 -4
  252. package/dist/testing/conformance/subscriptions/routing.js +0 -179
  253. package/dist/testing/conformance/subscriptions.d.ts +0 -4
  254. package/dist/testing/conformance/subscriptions.js +0 -19
  255. package/dist/testing/conformance/tables.d.ts +0 -125
  256. package/dist/testing/conformance/tables.js +0 -664
  257. package/dist/testing/conformance/transports/actors.d.ts +0 -1907
  258. package/dist/testing/conformance/transports/actors.js +0 -163
  259. package/dist/testing/conformance/transports/client.d.ts +0 -3
  260. package/dist/testing/conformance/transports/client.js +0 -237
  261. package/dist/testing/conformance/transports/feeds.d.ts +0 -3
  262. package/dist/testing/conformance/transports/feeds.js +0 -232
  263. package/dist/testing/conformance/transports/harness.d.ts +0 -93
  264. package/dist/testing/conformance/transports/harness.js +0 -84
  265. package/dist/testing/conformance/transports/streams.d.ts +0 -3
  266. package/dist/testing/conformance/transports/streams.js +0 -96
  267. package/dist/testing/conformance/transports/websocket.d.ts +0 -3
  268. package/dist/testing/conformance/transports/websocket.js +0 -384
  269. package/dist/testing/conformance/transports/wire.d.ts +0 -141
  270. package/dist/testing/conformance/transports/wire.js +0 -204
  271. package/dist/testing/conformance/transports.d.ts +0 -3
  272. package/dist/testing/conformance/transports.js +0 -11
  273. package/dist/testing/conformance/watch.d.ts +0 -59
  274. package/dist/testing/conformance/watch.js +0 -879
  275. package/dist/testing/conformance/workflow-engine.d.ts +0 -97
  276. package/dist/testing/conformance/workflow-engine.js +0 -253
  277. package/dist/testing/conformance/workflow-versions.d.ts +0 -3
  278. package/dist/testing/conformance/workflow-versions.js +0 -545
  279. package/dist/testing/conformance/workflows/access.d.ts +0 -4
  280. package/dist/testing/conformance/workflows/access.js +0 -147
  281. package/dist/testing/conformance/workflows/actors.d.ts +0 -2317
  282. package/dist/testing/conformance/workflows/actors.js +0 -305
  283. package/dist/testing/conformance/workflows/basics.d.ts +0 -4
  284. package/dist/testing/conformance/workflows/basics.js +0 -144
  285. package/dist/testing/conformance/workflows/engine.d.ts +0 -4
  286. package/dist/testing/conformance/workflows/engine.js +0 -80
  287. package/dist/testing/conformance/workflows/guards.d.ts +0 -4
  288. package/dist/testing/conformance/workflows/guards.js +0 -172
  289. package/dist/testing/conformance/workflows/harness.d.ts +0 -14
  290. package/dist/testing/conformance/workflows/harness.js +0 -44
  291. package/dist/testing/conformance/workflows/recovery.d.ts +0 -4
  292. package/dist/testing/conformance/workflows/recovery.js +0 -181
  293. package/dist/testing/conformance/workflows/starts.d.ts +0 -4
  294. package/dist/testing/conformance/workflows/starts.js +0 -57
  295. package/dist/testing/conformance/workflows/suspension.d.ts +0 -4
  296. package/dist/testing/conformance/workflows/suspension.js +0 -106
  297. package/dist/testing/conformance/workflows/waits.d.ts +0 -4
  298. package/dist/testing/conformance/workflows/waits.js +0 -284
  299. package/dist/testing/conformance/workflows.d.ts +0 -4
  300. package/dist/testing/conformance/workflows.js +0 -19
  301. package/dist/testing/conformance.d.ts +0 -330
  302. package/dist/testing/conformance.js +0 -364
  303. package/dist/testing/foundation.d.ts +0 -41
  304. package/dist/testing/foundation.js +0 -517
  305. package/dist/testing/simulate-cluster.d.ts +0 -112
  306. package/dist/testing/simulate-cluster.js +0 -257
  307. package/dist/testing/simulate.d.ts +0 -90
  308. package/dist/testing/simulate.js +0 -127
@@ -1,8 +1,11 @@
1
- import { Effect, Schema } from "effect";
2
- import { HttpRouter } from "effect/http";
1
+ import { Clock, DateTime, Effect, Option, Schema, Stream } from "effect";
2
+ import { HttpRouter, HttpServerResponse } from "effect/http";
3
3
  import { SqlClient } from "effect/sql";
4
- import { ActorError, Unauthorized } from "../../errors/actor.js";
4
+ import { ActorError, RunnerAtCapacity, Unauthorized } from "../../errors/actor.js";
5
5
  import { Anonymous, User } from "../../identity/caller.js";
6
+ import { InternalActors } from "../actors.js";
7
+ import { StreamMessage } from "../telemetry/live.js";
8
+ import { message } from "../../serve/sessions/sse.js";
6
9
  import { CREDENTIAL_BYTES, oversizedCredential, readsCookies, withinLimits, } from "../../serve/auth.js";
7
10
  import { invalidInput, undecodable } from "../../serve/wire.js";
8
11
  import { foundOrNotFound, operatorResponse, refuseCrossOrigin } from "./http.js";
@@ -61,7 +64,26 @@ const WorkflowsParams = Schema.Struct({
61
64
  afterStartedAtMs: Schema.optional(Millis),
62
65
  afterExecutionId: Schema.optional(Schema.String),
63
66
  }).check(whole("afterStartedAtMs", "afterExecutionId"));
67
+ const Window = Schema.optional(Schema.Literals(["1h", "24h", "7d"]));
68
+ const LiveTypeParams = Schema.Struct({ type: Schema.optional(Schema.String), window: Window });
69
+ /** The most actors one live actors read names. */
70
+ const MAX_LIVE_ACTORS = 100;
71
+ const LiveActorsParams = Schema.Struct({
72
+ type: Schema.String,
73
+ ids: Schema.fromJsonString(Schema.Array(Schema.String).check(Schema.isMaxLength(MAX_LIVE_ACTORS))),
74
+ });
75
+ const StreamParams = Schema.Struct({
76
+ type: Schema.optional(Schema.String),
77
+ outcome: Schema.optional(Schema.Literals(["Success", "Failure"])),
78
+ after: Schema.optional(Schema.String),
79
+ });
64
80
  const isPrincipal = Schema.is(Schema.Union([User, Anonymous]));
81
+ /**
82
+ * How often a command stream with no commands sends a comment. A proxy in
83
+ * front of the inspector, such as the edge or the control plane's client,
84
+ * closes a connection idle for 10 seconds, so this stays well under it.
85
+ */
86
+ const STREAM_KEEPALIVE_MS = 4_000;
65
87
  /**
66
88
  * Serves read-only JSON over the `durable` inspection views as routes on the
67
89
  * application's `HttpRouter`. A request from another browser origin is
@@ -87,6 +109,22 @@ const isPrincipal = Schema.is(Schema.Union([User, Anonymous]));
87
109
  * - `GET /outbox`, `/jobs`, `/dead-letters?afterDeadAtMs&afterJobId`,
88
110
  * `/workflows?status=open|all|running|suspended|finished|completed|failed&afterStartedAtMs&afterExecutionId`:
89
111
  * tenant-wide lists; the paged ones answer the `next` cursor to pass back.
112
+ *
113
+ * Inside a runtime it also serves the schedules and this runner's own memory,
114
+ * each live answer naming the runner, its region, when it began recording and
115
+ * how many other runners the cluster lists:
116
+ *
117
+ * - `GET /schedules`: each declared cron entry with its pending ticks and last committed tick.
118
+ * - `GET /live/overview`: recent rates, resident activations and the deepest mailbox, by type.
119
+ * - `GET /live/activity?type&window`, `/live/latency?type&window`: one type's, or every type's,
120
+ * committed turns over `1h`, `24h` (default) or `7d`.
121
+ * - `GET /live/actors?type&ids`: whether each named actor is resident, its
122
+ * mailbox, sockets and feeds; `ids` is a JSON array of at most 100.
123
+ * - `GET /live/connections`: open sockets and SSE responses, by type.
124
+ * - `GET /commands/stream?type&outcome&after`: SSE of each command this runner
125
+ * commits for the tenant, `id` its position, resuming after `after` while the
126
+ * runner still holds it and sending `gap` when it does not, until the
127
+ * credential expires.
90
128
  */
91
129
  const serve = (options) => HttpRouter.use(Effect.fnUntraced(function* (router) {
92
130
  const basePath = (options.basePath ?? "/inspector").replace(/\/+$/, "");
@@ -109,11 +147,11 @@ const serve = (options) => HttpRouter.use(Effect.fnUntraced(function* (router) {
109
147
  return yield* ActorError.make({
110
148
  reason: Unauthorized.make({ code: "invalid_credentials" }),
111
149
  });
112
- return authenticated.tenant;
150
+ return { tenant: authenticated.tenant, expiresAt: authenticated.expiresAt };
113
151
  });
114
152
  const route = (path, params, read) => router.add("GET", `${basePath}${path}`, (request) => Effect.gen(function* () {
115
153
  yield* refuseCrossOrigin(request);
116
- const tenant = yield* authenticate(request);
154
+ const { tenant } = yield* authenticate(request);
117
155
  const decoded = yield* HttpRouter.schemaParams(params).pipe(Effect.mapError(undecodable));
118
156
  return foundOrNotFound(yield* Queries.readOnly(tenant)(read(tenant, decoded)).pipe(Effect.provideService(SqlClient.SqlClient, sql), Effect.orDie));
119
157
  }).pipe(operatorResponse("Inspector request failed")));
@@ -165,6 +203,184 @@ const serve = (options) => HttpRouter.use(Effect.fnUntraced(function* (router) {
165
203
  commandId: afterCommandId,
166
204
  },
167
205
  }));
206
+ /**
207
+ * The live reads, the schedules and the command stream, which need the
208
+ * runtime itself; mounted only where the inspector runs inside one.
209
+ */
210
+ const mountLive = Effect.fnUntraced(function* (actors) {
211
+ const recorder = yield* actors.live.enable;
212
+ const scope = Effect.gen(function* () {
213
+ const peers = yield* actors.live.peers;
214
+ return {
215
+ runner: options.runner ?? null,
216
+ region: options.region ?? null,
217
+ startedAtMs: recorder.startedAtMs,
218
+ peers: peers ?? null,
219
+ };
220
+ });
221
+ /** A read of this runner's memory alone, answered as JSON with the scope it covers. */
222
+ const live = (path, params, read) => router.add("GET", `${basePath}${path}`, (request) => Effect.gen(function* () {
223
+ yield* refuseCrossOrigin(request);
224
+ const { tenant } = yield* authenticate(request);
225
+ const decoded = yield* HttpRouter.schemaParams(params).pipe(Effect.mapError(undecodable));
226
+ return HttpServerResponse.jsonUnsafe(yield* read(tenant, decoded, yield* Clock.currentTimeMillis, yield* scope));
227
+ }).pipe(operatorResponse("Inspector request failed")));
228
+ yield* live("/live/overview", Schema.Struct({}), (tenant, _params, nowMs, covered) => Effect.sync(() => {
229
+ const resident = actors.live.resident(tenant);
230
+ const types = new Set([
231
+ ...recorder.actorTypes(tenant),
232
+ ...resident.map((r) => r.actorType),
233
+ ]);
234
+ const deepest = (activations) => activations.reduce((found, activation) => activation.mailbox > 0 &&
235
+ (found === undefined || activation.mailbox > found.mailbox)
236
+ ? activation
237
+ : found, undefined);
238
+ const rates = (actorType) => {
239
+ const found = recorder.rates(tenant, actorType, nowMs);
240
+ return {
241
+ perSecond: found?.perSecond ?? null,
242
+ p50Ms: found?.p50Ms ?? null,
243
+ p99Ms: found?.p99Ms ?? null,
244
+ };
245
+ };
246
+ const max = deepest(resident);
247
+ return {
248
+ scope: covered,
249
+ total: {
250
+ ...rates(undefined),
251
+ awake: resident.length,
252
+ maxMailbox: max === undefined
253
+ ? null
254
+ : { depth: max.mailbox, actorType: max.actorType, actorId: max.actorId },
255
+ },
256
+ actorTypes: [...types].sort().map((actorType) => {
257
+ const ofType = resident.filter((activation) => activation.actorType === actorType);
258
+ const typeMax = deepest(ofType);
259
+ return {
260
+ actorType,
261
+ ...rates(actorType),
262
+ awake: ofType.length,
263
+ maxMailbox: typeMax === undefined
264
+ ? null
265
+ : { depth: typeMax.mailbox, actorId: typeMax.actorId },
266
+ };
267
+ }),
268
+ };
269
+ }));
270
+ yield* live("/live/activity", LiveTypeParams, (tenant, { type, window }, nowMs, covered) => Effect.sync(() => {
271
+ const found = recorder.activity(tenant, type, window ?? "24h", nowMs);
272
+ return {
273
+ scope: covered,
274
+ activity: found === undefined
275
+ ? null
276
+ : { sinceMs: found.since, points: found.points, commands: found.commands },
277
+ };
278
+ }));
279
+ yield* live("/live/latency", LiveTypeParams, (tenant, { type, window }, nowMs, covered) => Effect.sync(() => {
280
+ const found = recorder.latency(tenant, type, window ?? "24h", nowMs);
281
+ return {
282
+ scope: covered,
283
+ latency: found === undefined
284
+ ? null
285
+ : {
286
+ sinceMs: found.since,
287
+ count: found.count,
288
+ buckets: found.buckets,
289
+ p50Ms: found.p50Ms,
290
+ p95Ms: found.p95Ms,
291
+ p99Ms: found.p99Ms,
292
+ },
293
+ };
294
+ }));
295
+ yield* live("/live/actors", LiveActorsParams, (tenant, { type, ids }, _nowMs, covered) => Effect.sync(() => {
296
+ const resident = actors.live
297
+ .resident(tenant)
298
+ .filter((activation) => activation.actorType === type);
299
+ const open = actors.live
300
+ .connections(tenant)
301
+ .filter((connection) => connection.actorType === type);
302
+ return {
303
+ scope: covered,
304
+ actors: ids.map((actorId) => {
305
+ const activation = resident.find((found) => found.actorId === actorId);
306
+ const mine = open.filter((connection) => connection.actorId === actorId);
307
+ const feeds = new Map();
308
+ for (const connection of mine)
309
+ if (connection.kind === "feed")
310
+ for (const event of connection.events)
311
+ feeds.set(event, (feeds.get(event) ?? 0) + 1);
312
+ return {
313
+ actorId,
314
+ awake: activation !== undefined,
315
+ mailbox: activation?.mailbox ?? null,
316
+ sockets: mine.filter((connection) => connection.kind === "socket").length,
317
+ feeds: [...feeds]
318
+ .sort(([left], [right]) => (left < right ? -1 : 1))
319
+ .map(([event, subscribers]) => ({ event, subscribers })),
320
+ };
321
+ }),
322
+ };
323
+ }));
324
+ yield* live("/live/connections", Schema.Struct({}), (tenant, _params, _nowMs, covered) => Effect.sync(() => {
325
+ const open = actors.live.connections(tenant);
326
+ const counts = (connections) => ({
327
+ sockets: connections.filter((connection) => connection.kind === "socket").length,
328
+ feeds: connections.filter((connection) => connection.kind === "feed").length,
329
+ streams: connections.filter((connection) => connection.kind === "stream").length,
330
+ watches: connections.filter((connection) => connection.kind === "watch").length,
331
+ });
332
+ return {
333
+ scope: covered,
334
+ ...counts(open),
335
+ byActorType: [...new Set(open.map((connection) => connection.actorType))]
336
+ .sort()
337
+ .map((actorType) => ({
338
+ actorType,
339
+ ...counts(open.filter((connection) => connection.actorType === actorType)),
340
+ })),
341
+ };
342
+ }));
343
+ yield* route("/schedules", Schema.Struct({}), (tenant) => Effect.asSome(Queries.schedules({ tenant, declared: actors.live.schedules() })));
344
+ yield* router.add("GET", `${basePath}/commands/stream`, (request) => Effect.gen(function* () {
345
+ yield* refuseCrossOrigin(request);
346
+ const { tenant, expiresAt } = yield* authenticate(request);
347
+ const { type, outcome, after } = yield* HttpRouter.schemaParams(StreamParams).pipe(Effect.mapError(undecodable));
348
+ const nowMs = yield* Clock.currentTimeMillis;
349
+ if (recorder.full(tenant))
350
+ return yield* ActorError.make({ reason: RunnerAtCapacity.make({}) });
351
+ const opened = recorder.subscribe(tenant, {
352
+ actorType: type,
353
+ failed: outcome === undefined ? undefined : outcome === "Failure",
354
+ }, after);
355
+ const until = expiresAt === undefined
356
+ ? Effect.never
357
+ : Effect.sleep(Math.max(0, DateTime.toEpochMillis(expiresAt) - nowMs));
358
+ const messages = opened.pipe(Stream.mapEffect(StreamMessage.$match({
359
+ gap: () => message({ event: "gap", data: null }),
360
+ command: ({ entry }) => message({
361
+ event: "command",
362
+ id: entry.id,
363
+ data: {
364
+ id: entry.id,
365
+ commandId: entry.commandId,
366
+ atMs: entry.atMs,
367
+ durationMs: entry.durationMs,
368
+ actorType: entry.actorType,
369
+ actorId: entry.actorId,
370
+ command: entry.command,
371
+ callerKey: Queries.decodeText(entry.callerKey),
372
+ outcomeTag: entry.failed ? "Failure" : "Success",
373
+ errorTag: entry.errorTag,
374
+ payloadPreview: entry.payloadPreview,
375
+ },
376
+ }),
377
+ })), Stream.interruptWhen(until), Stream.concat(Stream.fromEffect(message({ event: "end", data: null }))));
378
+ return HttpServerResponse.stream(Stream.concat(Stream.succeed(": open\n\n"), messages).pipe(Stream.merge(Stream.tick(STREAM_KEEPALIVE_MS).pipe(Stream.drop(1), Stream.map(() => ": keepalive\n\n")), { haltStrategy: "left" }), Stream.encodeText), {
379
+ contentType: "text/event-stream",
380
+ headers: { "x-accel-buffering": "no" },
381
+ });
382
+ }).pipe(operatorResponse("Inspector request failed")));
383
+ });
168
384
  yield* route("/latest-events", LatestEventsParams, (tenant, { limit, type, id, after }) => Queries.latestEvents({ tenant, limit, actorType: type, actorId: id, after }));
169
385
  yield* route("/timeline", TimelineParams, (tenant, { limit, type, id, beforeSequence, beforeKind }) => Queries.timeline({
170
386
  tenant,
@@ -175,6 +391,9 @@ const serve = (options) => HttpRouter.use(Effect.fnUntraced(function* (router) {
175
391
  ? undefined
176
392
  : { sequence: beforeSequence, kind: beforeKind },
177
393
  }));
394
+ const runtime = yield* Effect.serviceOption(InternalActors);
395
+ if (Option.isSome(runtime))
396
+ yield* mountLive(runtime.value);
178
397
  }));
179
398
  /** The read-only inspector: `serve` mounts its routes. */
180
399
  export const Inspector = { serve };
@@ -52,19 +52,32 @@ interface ActorsPage extends Page {
52
52
  readonly after?: ActorIdentity | undefined;
53
53
  }
54
54
  /**
55
- * The tenant's actors in `(actor_type, actor_id)` order, one page after `after`. The cursor
55
+ * The receipts an actor's last command is looked for among: its greatest
56
+ * command ids, which the receipts' primary key reads in order and which for a
57
+ * minted id are the newest issued, so the read is bounded however many
58
+ * receipts the actor retains.
59
+ */
60
+ export declare const LAST_COMMAND_RECEIPTS = 256;
61
+ /**
62
+ * The tenant's actors in `(actor_type, actor_id)` order, one page after `after`,
63
+ * each with its last command: the receipt with the newest recorded commit
64
+ * time among its `LAST_COMMAND_RECEIPTS` greatest command ids. The cursor
56
65
  * comparison uses the same `C` collation as the ORDER BY, or a cursor could skip or repeat actors.
57
66
  * A prefix that names a whole type, `type/` and more, matches that type's ids by their own prefix,
58
67
  * so it reads one type rather than every actor of the tenant.
59
68
  */
60
69
  export declare const actors: (page: ActorsPage) => Effect.Effect<{
61
- actors: readonly {
62
- readonly actorType: string;
63
- readonly actorId: string;
64
- readonly placement: string | null;
65
- readonly generation: number;
66
- readonly created: boolean;
67
- readonly lastEventSequence: number;
70
+ actors: {
71
+ actorType: string;
72
+ actorId: string;
73
+ placement: string | null;
74
+ generation: number;
75
+ created: boolean;
76
+ lastEventSequence: number;
77
+ lastCommand: {
78
+ command: string;
79
+ committedAtMs: number;
80
+ } | null;
68
81
  }[];
69
82
  next: {
70
83
  actorType: string;
@@ -709,3 +722,36 @@ export declare const timeline: (page: TimelinePage) => Effect.Effect<Option.None
709
722
  kind: "command" | "event";
710
723
  } | null;
711
724
  }>, import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
725
+ /** The newest receipts of a type a schedule's last run is looked for among. */
726
+ export declare const SCHEDULE_RECEIPTS = 10000;
727
+ /**
728
+ * The tenant's state of each declared cron entry: its pending ticks and the
729
+ * soonest one's due time, and its newest committed tick (a receipt of the
730
+ * entry's command whose caller is the cron source) among the type's newest
731
+ * `SCHEDULE_RECEIPTS` receipts by expiry, which the expiry index reads in
732
+ * order, so a type with more receipts than that answers no last run rather
733
+ * than scan them all.
734
+ */
735
+ export declare const schedules: ({ tenant, declared, }: {
736
+ readonly tenant: string;
737
+ readonly declared: ReadonlyArray<{
738
+ readonly actorType: string;
739
+ readonly key: string;
740
+ readonly command: string;
741
+ }>;
742
+ }) => Effect.Effect<{
743
+ schedules: {
744
+ actorType: string;
745
+ key: string;
746
+ expression: string;
747
+ command: string;
748
+ pending: number;
749
+ nextDueAtMs: number | null;
750
+ lastRun: {
751
+ commandId: string;
752
+ committedAtMs: number;
753
+ durationMs: number | null;
754
+ outcomeTag: string | null;
755
+ } | null;
756
+ }[];
757
+ }, import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
@@ -2,6 +2,7 @@ import { Effect, Option, Predicate, Schema } from "effect";
2
2
  import { SqlClient } from "effect/sql";
3
3
  import { inReadOnlySnapshot } from "../database/snapshot.js";
4
4
  import { decompress } from "../storage/codec.js";
5
+ import { CRON_PREFIX } from "../cron/key.js";
5
6
  const parseJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
6
7
  /** Decodes a JSON `text` column such as a payload, caller, or outcome. */
7
8
  export const decodeText = (text) => {
@@ -61,37 +62,62 @@ export const overview = ({ tenant }) => Effect.gen(function* () {
61
62
  };
62
63
  });
63
64
  /**
64
- * The tenant's actors in `(actor_type, actor_id)` order, one page after `after`. The cursor
65
+ * The receipts an actor's last command is looked for among: its greatest
66
+ * command ids, which the receipts' primary key reads in order and which for a
67
+ * minted id are the newest issued, so the read is bounded however many
68
+ * receipts the actor retains.
69
+ */
70
+ export const LAST_COMMAND_RECEIPTS = 256;
71
+ /**
72
+ * The tenant's actors in `(actor_type, actor_id)` order, one page after `after`,
73
+ * each with its last command: the receipt with the newest recorded commit
74
+ * time among its `LAST_COMMAND_RECEIPTS` greatest command ids. The cursor
65
75
  * comparison uses the same `C` collation as the ORDER BY, or a cursor could skip or repeat actors.
66
76
  * A prefix that names a whole type, `type/` and more, matches that type's ids by their own prefix,
67
77
  * so it reads one type rather than every actor of the tenant.
68
78
  */
69
79
  export const actors = (page) => Effect.gen(function* () {
70
80
  const sql = yield* SqlClient.SqlClient;
71
- const byType = page.actorType === undefined ? sql `TRUE` : sql `actor_type = ${page.actorType}`;
81
+ const byType = page.actorType === undefined ? sql `TRUE` : sql `a.actor_type = ${page.actorType}`;
72
82
  const slash = page.prefix?.indexOf("/") ?? -1;
73
83
  const byPrefix = page.prefix === undefined
74
84
  ? sql `TRUE`
75
85
  : slash === -1
76
- ? sql `starts_with(actor_type, ${page.prefix})`
77
- : sql `actor_type = ${page.prefix.slice(0, slash)}
78
- AND starts_with(actor_id, ${page.prefix.slice(slash + 1)})`;
86
+ ? sql `starts_with(a.actor_type, ${page.prefix})`
87
+ : sql `a.actor_type = ${page.prefix.slice(0, slash)}
88
+ AND starts_with(a.actor_id, ${page.prefix.slice(slash + 1)})`;
79
89
  const after = page.after === undefined
80
90
  ? sql `TRUE`
81
- : sql `(actor_type COLLATE "C", actor_id COLLATE "C") > (${page.after.actorType}, ${page.after.actorId})`;
91
+ : sql `(a.actor_type COLLATE "C", a.actor_id COLLATE "C") > (${page.after.actorType}, ${page.after.actorId})`;
82
92
  const rows = yield* sql `
83
- SELECT actor_type AS "actorType", actor_id AS "actorId", placement,
84
- generation::float8 AS generation, created,
85
- last_event_sequence::float8 AS "lastEventSequence"
86
- FROM durable.actors
87
- WHERE tenant_id = ${page.tenant} AND ${byType} AND ${byPrefix} AND ${after}
88
- ORDER BY actor_type COLLATE "C", actor_id COLLATE "C"
93
+ SELECT a.actor_type AS "actorType", a.actor_id AS "actorId",
94
+ a.generation::float8 AS generation, a.created, a.placement,
95
+ a.last_event_sequence::float8 AS "lastEventSequence",
96
+ l.command AS "lastCommand", l.committed_at_ms::float8 AS "lastCommittedAtMs"
97
+ FROM durable.actors a
98
+ LEFT JOIN LATERAL (
99
+ SELECT n.command, n.committed_at_ms FROM (
100
+ SELECT r.command, r.command_id, r.committed_at_ms FROM durable.receipts r
101
+ WHERE r.routing_key = a.routing_key AND r.tenant_id = a.tenant_id
102
+ AND r.actor_type = a.actor_type AND r.actor_id = a.actor_id
103
+ ORDER BY r.command_id DESC
104
+ LIMIT ${LAST_COMMAND_RECEIPTS}) n
105
+ WHERE n.committed_at_ms IS NOT NULL
106
+ ORDER BY n.committed_at_ms DESC, n.command_id COLLATE "C" DESC
107
+ LIMIT 1) l ON TRUE
108
+ WHERE a.tenant_id = ${page.tenant} AND ${byType} AND ${byPrefix} AND ${after}
109
+ ORDER BY a.actor_type COLLATE "C", a.actor_id COLLATE "C"
89
110
  LIMIT ${page.limit + 1}`;
90
111
  const more = rows.length > page.limit;
91
112
  const items = more ? rows.slice(0, page.limit) : rows;
92
113
  const last = items.at(-1);
93
114
  return {
94
- actors: items,
115
+ actors: items.map(({ lastCommand, lastCommittedAtMs, ...actor }) => ({
116
+ ...actor,
117
+ lastCommand: lastCommand === null || lastCommittedAtMs === null
118
+ ? null
119
+ : { command: lastCommand, committedAtMs: lastCommittedAtMs },
120
+ })),
95
121
  next: more && last !== undefined ? { actorType: last.actorType, actorId: last.actorId } : null,
96
122
  };
97
123
  });
@@ -99,11 +125,11 @@ const findActor = ({ tenant, actorType, actorId }) => Effect.gen(function* () {
99
125
  const sql = yield* SqlClient.SqlClient;
100
126
  const [row] = yield* sql `
101
127
  SELECT actor_type AS "actorType", actor_id AS "actorId", routing_key::text AS "routingKey",
102
- placement, generation::float8 AS generation, created,
128
+ generation::float8 AS generation, created, placement,
103
129
  last_event_sequence::float8 AS "lastEventSequence"
104
130
  FROM durable.actors
105
131
  WHERE tenant_id = ${tenant} AND actor_type = ${actorType} AND actor_id = ${actorId}`;
106
- return Option.fromNullishOr(row);
132
+ return Option.fromUndefinedOr(row);
107
133
  });
108
134
  const outboxOf = (row) => ({
109
135
  ...row,
@@ -179,6 +205,7 @@ export const actor = (page) => Effect.gen(function* () {
179
205
  const receipts = yield* sql `
180
206
  SELECT r.command_id AS "commandId", r.command, r.caller_key AS "callerKey",
181
207
  r.outcome_tag AS "outcomeTag", r.outcome, r.expires_at_ms::float8 AS "expiresAtMs",
208
+ r.started_at_ms::float8 AS "startedAtMs", r.committed_at_ms::float8 AS "committedAtMs",
182
209
  (SELECT string_agg(e.sequence::text, ',' ORDER BY e.sequence) FROM durable.events e
183
210
  WHERE e.routing_key = r.routing_key AND e.tenant_id = r.tenant_id
184
211
  AND e.actor_type = r.actor_type AND e.actor_id = r.actor_id
@@ -387,6 +414,7 @@ export const receipts = (page) => Effect.gen(function* () {
387
414
  SELECT r.actor_type AS "actorType", r.actor_id AS "actorId",
388
415
  r.command_id AS "commandId", r.command, r.caller_key AS "callerKey",
389
416
  r.outcome_tag AS "outcomeTag", r.outcome, r.expires_at_ms::float8 AS "expiresAtMs",
417
+ r.started_at_ms::float8 AS "startedAtMs", r.committed_at_ms::float8 AS "committedAtMs",
390
418
  (SELECT string_agg(e.sequence::text, ',' ORDER BY e.sequence) FROM durable.events e
391
419
  WHERE e.routing_key = r.routing_key AND e.tenant_id = r.tenant_id
392
420
  AND e.actor_type = r.actor_type AND e.actor_id = r.actor_id
@@ -492,3 +520,44 @@ export const timeline = (page) => Effect.gen(function* () {
492
520
  : null,
493
521
  });
494
522
  });
523
+ /** The newest receipts of a type a schedule's last run is looked for among. */
524
+ export const SCHEDULE_RECEIPTS = 10_000;
525
+ /**
526
+ * The tenant's state of each declared cron entry: its pending ticks and the
527
+ * soonest one's due time, and its newest committed tick (a receipt of the
528
+ * entry's command whose caller is the cron source) among the type's newest
529
+ * `SCHEDULE_RECEIPTS` receipts by expiry, which the expiry index reads in
530
+ * order, so a type with more receipts than that answers no last run rather
531
+ * than scan them all.
532
+ */
533
+ export const schedules = ({ tenant, declared, }) => Effect.forEach(declared, (entry) => Effect.gen(function* () {
534
+ const sql = yield* SqlClient.SqlClient;
535
+ const [timers] = yield* sql `
536
+ SELECT count(*)::int AS pending, min(due_at_ms)::float8 AS "dueAtMs" FROM durable.timers
537
+ WHERE tenant_id = ${tenant} AND actor_type = ${entry.actorType}
538
+ AND timer_key = ${entry.key}`;
539
+ const [last] = yield* sql `
540
+ SELECT command_id AS "commandId", committed_at_ms::float8 AS "committedAtMs",
541
+ duration_ms::float8 AS "durationMs", outcome_tag AS "outcomeTag"
542
+ FROM (
543
+ SELECT command_id, command, caller_key, committed_at_ms, duration_ms, outcome_tag
544
+ FROM durable.receipts
545
+ WHERE tenant_id = ${tenant} AND actor_type = ${entry.actorType}
546
+ ORDER BY expires_at_ms DESC
547
+ LIMIT ${SCHEDULE_RECEIPTS}) r
548
+ WHERE command = ${entry.command} AND starts_with(caller_key, '["System","cron",')
549
+ AND committed_at_ms IS NOT NULL
550
+ ORDER BY committed_at_ms DESC, command_id COLLATE "C" DESC
551
+ LIMIT 1`;
552
+ return {
553
+ actorType: entry.actorType,
554
+ key: entry.key,
555
+ expression: entry.key.startsWith(CRON_PREFIX)
556
+ ? entry.key.slice(CRON_PREFIX.length)
557
+ : entry.key,
558
+ command: entry.command,
559
+ pending: timers?.pending ?? 0,
560
+ nextDueAtMs: timers?.dueAtMs ?? null,
561
+ lastRun: last ?? null,
562
+ };
563
+ })).pipe(Effect.map((found) => ({ schedules: found })));
@@ -81,7 +81,7 @@ export interface Options {
81
81
  };
82
82
  /** Telemetry this runner keeps beside the spans and metrics it reports. */
83
83
  readonly observability?: {
84
- /** Defect spans the runner keeps for `durable defects list`, newest last. Default 1,000. */
84
+ /** Defect spans the runner keeps for `akter defects list`, newest last. Default 1,000. */
85
85
  readonly defects?: number;
86
86
  /**
87
87
  * How often one runner of the deployment samples the database gauges
@@ -94,7 +94,7 @@ export interface Options {
94
94
  * How long this runtime keeps writing event and effect payload versions
95
95
  * without refreshing its writer rows; it refreshes every half window and
96
96
  * refuses new turns once a window passes without a refresh, so
97
- * `durable payloads clear` can tell when no turn still writes an old
97
+ * `akter payloads clear` can tell when no turn still writes an old
98
98
  * version. Default 2 minutes, at least 1 second.
99
99
  */
100
100
  readonly payloadWriterWindow?: Duration.Input;
@@ -159,7 +159,7 @@ export interface Options {
159
159
  };
160
160
  /**
161
161
  * The fleet views this runtime maintains and serves. They need Postgres with
162
- * `wal_level=logical`, a login with `REPLICATION`, and `durable fleet setup`;
162
+ * `wal_level=logical`, a login with `REPLICATION`, and `akter fleet setup`;
163
163
  * startup refuses otherwise, and PGlite refuses any view. One runner at a
164
164
  * time maintains them, off the turn path.
165
165
  */
@@ -174,7 +174,10 @@ export interface Options {
174
174
  * Constraints the wiring keeps:
175
175
  * - Turns run through the drain gate, so a drain refuses new turns and
176
176
  * interrupts the rest. The runtime scope, not the caller, owns an in-flight
177
- * turn: interrupting a waiter never cancels it.
177
+ * turn: interrupting a waiter never cancels it. Closing that scope does, and
178
+ * a command it cuts off, or one sent after it starts closing, fails with a
179
+ * retryable `ActorUnavailable` instead of an interrupt or a wait that never
180
+ * ends; delivery stops retrying once the scope is closed.
178
181
  * - Commands are direct, so Cluster keeps no messages and the receipt is the
179
182
  * only admission record; a restarted activation or lost runner drops the
180
183
  * uncommitted attempt, the handle retries with the same command id, and the
@@ -219,42 +222,48 @@ export interface Options {
219
222
  * retry like any delivery failure; it is not a defect.
220
223
  */
221
224
  export declare const layer: (options?: Options) => Layer.Layer<Actors | ContentStore | DefectLog | InternalActors | OperatorRuntime | RuntimeControl | TelemetrySampler, import("effect/sql/Migrator").MigrationError | SqlError.SqlError, Crypto.Crypto | SqlClient.SqlClient>;
225
+ type PostgresOptions = Omit<PgClient.PgPoolConfig, "types"> & {
226
+ readonly offTurnConnections?: number;
227
+ readonly queryConnections?: number;
228
+ readonly replica?: Omit<PgClient.PgPoolConfig, "types"> | undefined;
229
+ /** An unsharded primary shared by every runner; owns coordination rows and Cluster and fleet locks. */
230
+ readonly coordination?: Omit<PgClient.PgPoolConfig, "types"> | undefined;
231
+ };
232
+ /**
233
+ * A runner holds three primary pools, plus optional replica and coordination
234
+ * pools. Turns lease sessions from the turn pool, `maxConnections` (default
235
+ * 50): a command holds one session until its transaction ends, so a pool
236
+ * smaller than the commands in flight queues callers behind
237
+ * it. Queries read from the query pool, `queryConnections` (default 10),
238
+ * except those of types with owned tables or blobs. Command admission,
239
+ * receipt replays, the relay, migrations, and cluster storage use the
240
+ * off-turn pool, `offTurnConnections` (default 10), unless `coordination`
241
+ * moves Cluster storage and deployment locks to an independent unsharded
242
+ * primary. All runners must designate the same authority. Every pool opens
243
+ * connections only as load needs them, refuses checkouts past its
244
+ * connections plus a bounded waiter allowance, and hands its connections out
245
+ * first come, first served. Keep the sum across runners below the server's
246
+ * `max_connections`.
247
+ *
248
+ * `replica` is this runner's nearest streaming replica of the same primary.
249
+ * Queries read there once it has replayed the commit version their caller
250
+ * last saw, and read the primary when it is behind or fails. Its pool
251
+ * (`maxConnections` default 10) opens connections only as queries need them.
252
+ * Every pool requests server TCP keepalives at 5 seconds idle, 2 seconds
253
+ * between probes, and 3 probes. `startupParameters` overrides these defaults
254
+ * independently on the primary and replica configurations.
255
+ *
256
+ * Registers a `regclass` codec because the pinned driver lacks one and the
257
+ * migrator needs it on restart; remove once Effect #8309 lands.
258
+ */
259
+ declare function postgres(options: PostgresOptions): Layer.Layer<PgClient.PgClient | SqlClient.SqlClient | TurnConnections, SqlError.SqlError>;
222
260
  /** Database layers for `Actors.layer`: `postgres` for real deployments, `pglite` for embedded and test use. */
223
261
  export declare const Database: {
224
- /**
225
- * A runner holds three primary pools, plus optional replica and coordination
226
- * pools. Turns lease sessions from the turn pool, `maxConnections` (default
227
- * 50): a command holds one session until its transaction ends, so a pool
228
- * smaller than the commands in flight queues callers behind
229
- * it. Queries read from the query pool, `queryConnections` (default 10),
230
- * except those of types with owned tables or blobs. Command admission,
231
- * receipt replays, the relay, migrations, and cluster storage use the
232
- * off-turn pool, `offTurnConnections` (default 10), unless `coordination`
233
- * moves Cluster storage and deployment locks to an independent unsharded
234
- * primary. All runners must designate the same authority. Every pool opens
235
- * connections only as load needs them, refuses checkouts past its
236
- * connections plus a bounded waiter allowance, and hands its connections out
237
- * first come, first served. Keep the sum across runners below the server's
238
- * `max_connections`.
239
- *
240
- * `replica` is this runner's nearest streaming replica of the same primary.
241
- * Queries read there once it has replayed the commit version their caller
242
- * last saw, and read the primary when it is behind or fails. Its pool
243
- * (`maxConnections` default 10) opens connections only as queries need them.
244
- * Every pool requests server TCP keepalives at 5 seconds idle, 2 seconds
245
- * between probes, and 3 probes. `startupParameters` overrides these defaults
246
- * independently on the primary and replica configurations.
247
- *
248
- * Registers a `regclass` codec because the pinned driver lacks one and the
249
- * migrator needs it on restart; remove once Effect #8309 lands.
250
- */
251
- postgres: (options: Omit<PgClient.PgPoolConfig, "types"> & {
252
- readonly offTurnConnections?: number;
253
- readonly queryConnections?: number;
254
- readonly neki?: boolean;
255
- readonly replica?: Omit<PgClient.PgPoolConfig, "types"> | undefined;
256
- /** An unsharded primary shared by every runner; owns coordination rows and Cluster and fleet locks. */
257
- readonly coordination?: Omit<PgClient.PgPoolConfig, "types"> | undefined;
258
- }) => Layer.Layer<PgClient.PgClient | SqlClient.SqlClient | TurnConnections, SqlError.SqlError, never>;
262
+ postgres: typeof postgres;
259
263
  pglite: typeof pglite;
264
+ schemaChange: {
265
+ (lock: number): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | SqlError.SqlError, R | SqlClient.SqlClient>;
266
+ <A, E, R>(effect: Effect.Effect<A, E, R>, lock: number): Effect.Effect<A, E | SqlError.SqlError, R | SqlClient.SqlClient>;
267
+ };
260
268
  };
269
+ export {};