@zlink-systems/framework 0.30.1 → 0.30.2

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 (340) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +6 -0
  3. package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
  4. package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
  5. package/dist/contracts/Common/encoded-payload-storage.js +5 -2
  6. package/dist/contracts/Configuration/Builders.d.ts +1 -1
  7. package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
  8. package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
  9. package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
  10. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
  11. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
  12. package/dist/contracts/Locations/Rows.d.ts +2 -0
  13. package/dist/contracts/Spots/Contracts.d.ts +4 -0
  14. package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
  15. package/dist/runtime/abort.d.ts +5 -0
  16. package/dist/runtime/abort.js +10 -0
  17. package/dist/runtime/actors/actor-authority-publication.js +3 -2
  18. package/dist/runtime/actors/actor-client.d.ts +0 -1
  19. package/dist/runtime/actors/actor-client.js +38 -45
  20. package/dist/runtime/actors/actor-context.d.ts +2 -0
  21. package/dist/runtime/actors/actor-context.js +46 -9
  22. package/dist/runtime/actors/actor-handoff.d.ts +7 -3
  23. package/dist/runtime/actors/actor-handoff.js +212 -88
  24. package/dist/runtime/actors/actor-join-relocation.d.ts +2 -0
  25. package/dist/runtime/actors/actor-local-native-join.js +4 -0
  26. package/dist/runtime/actors/actor-mailbox.js +27 -1
  27. package/dist/runtime/actors/actor-remote-joiner.js +3 -1
  28. package/dist/runtime/actors/actor-reply.d.ts +4 -0
  29. package/dist/runtime/actors/actor-reply.js +29 -0
  30. package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
  31. package/dist/runtime/actors/actor-runtime-state.js +2 -20
  32. package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
  33. package/dist/runtime/actors/index.d.ts +6 -2
  34. package/dist/runtime/actors/index.js +37 -21
  35. package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
  36. package/dist/runtime/actors/session-disconnected-wire.js +48 -0
  37. package/dist/runtime/admission.d.ts +9 -1
  38. package/dist/runtime/admission.js +55 -32
  39. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +13 -0
  40. package/dist/runtime/application-jobs/application-job-queue-scope.js +27 -1
  41. package/dist/runtime/application-jobs/contracts.d.ts +2 -0
  42. package/dist/runtime/backend/contracts/index.d.ts +19 -3
  43. package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
  44. package/dist/runtime/backend/mesh-completion-table.js +5 -0
  45. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +26 -2
  46. package/dist/runtime/backend/mesh-dispatch-pump.js +193 -107
  47. package/dist/runtime/backend/node/node-raw-binding-port.js +90 -58
  48. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +33 -8
  49. package/dist/runtime/backend/node/node-raw-mesh-backend.js +142 -143
  50. package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
  51. package/dist/runtime/backend/node/node-socket-backend-adapter.js +5 -5
  52. package/dist/runtime/channels/channel-clients.d.ts +1 -0
  53. package/dist/runtime/channels/channel-clients.js +20 -5
  54. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +22 -3
  55. package/dist/runtime/channels/channel-dispatch-pipeline.js +103 -57
  56. package/dist/runtime/channels/channel-dispatchers.d.ts +3 -2
  57. package/dist/runtime/channels/channel-dispatchers.js +44 -37
  58. package/dist/runtime/channels/channel-envelope.d.ts +1 -0
  59. package/dist/runtime/channels/channel-envelope.js +74 -31
  60. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
  61. package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
  62. package/dist/runtime/channels/channel-runtime-manager.d.ts +3 -2
  63. package/dist/runtime/channels/channel-runtime-manager.js +4 -4
  64. package/dist/runtime/channels/channel-transports.d.ts +0 -1
  65. package/dist/runtime/channels/channel-transports.js +35 -33
  66. package/dist/runtime/channels/client-server-location-runtime.js +5 -4
  67. package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
  68. package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
  69. package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
  70. package/dist/runtime/channels/fanout-location-runtime.js +3 -3
  71. package/dist/runtime/diagnostics/flow-context.js +19 -7
  72. package/dist/runtime/diagnostics/index.js +4 -9
  73. package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
  74. package/dist/runtime/diagnostics/message-flow.js +35 -5
  75. package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
  76. package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
  77. package/dist/runtime/execution/index.d.ts +4 -2
  78. package/dist/runtime/execution/index.js +33 -10
  79. package/dist/runtime/execution/serial-execution-queue.d.ts +4 -0
  80. package/dist/runtime/execution/serial-execution-queue.js +21 -2
  81. package/dist/runtime/execution/state-lane.d.ts +2 -0
  82. package/dist/runtime/execution/state-lane.js +5 -0
  83. package/dist/runtime/foundation/operation-registry.d.ts +25 -1
  84. package/dist/runtime/foundation/operation-registry.js +86 -18
  85. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +39 -3
  86. package/dist/runtime/foundation/raw-service-mesh-runtime.js +151 -55
  87. package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
  88. package/dist/runtime/foundation/service-liveness-registry.js +38 -0
  89. package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
  90. package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
  91. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +2 -3
  92. package/dist/runtime/foundation/service-relocation-object-owner.js +48 -35
  93. package/dist/runtime/foundation/service-relocation-runtime.js +7 -3
  94. package/dist/runtime/foundation/service-runtime-contracts.d.ts +8 -1
  95. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
  96. package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
  97. package/dist/runtime/foundation/service-stateful-registry.js +24 -26
  98. package/dist/runtime/foundation/service-stateful-runtime.d.ts +72 -6
  99. package/dist/runtime/foundation/service-stateful-runtime.js +444 -117
  100. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -1
  101. package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
  102. package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
  103. package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
  104. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  105. package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
  106. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +10 -0
  107. package/dist/runtime/foundation/service-wire-m6a-codec.js +166 -53
  108. package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
  109. package/dist/runtime/host/actor-packet-relay.js +59 -72
  110. package/dist/runtime/host/actor-placement-coordinator.js +25 -8
  111. package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
  112. package/dist/runtime/host/actor-transfer-runtime.js +85 -331
  113. package/dist/runtime/host/application-job-queue.d.ts +24 -3
  114. package/dist/runtime/host/application-job-queue.js +28 -41
  115. package/dist/runtime/host/index.d.ts +5 -1
  116. package/dist/runtime/host/index.js +171 -97
  117. package/dist/runtime/host/location-runtime-owner.js +2 -6
  118. package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
  119. package/dist/runtime/host/mesh-router-resolver.js +14 -6
  120. package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
  121. package/dist/runtime/host/remote-bound-session-relay.js +71 -190
  122. package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
  123. package/dist/runtime/host/route-mesh-runtime.js +5 -3
  124. package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
  125. package/dist/runtime/host/runtime-shutdown.js +3 -2
  126. package/dist/runtime/host/service-relocation-host-runtime.d.ts +17 -7
  127. package/dist/runtime/host/service-relocation-host-runtime.js +550 -367
  128. package/dist/runtime/host/spot-runtime-options-factory.js +15 -7
  129. package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
  130. package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
  131. package/dist/runtime/locations/auto-connect-loop.js +22 -14
  132. package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
  133. package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
  134. package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
  135. package/dist/runtime/locations/descriptor-publication.js +11 -0
  136. package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
  137. package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
  138. package/dist/runtime/locations/in-memory-authority-store.js +56 -19
  139. package/dist/runtime/locations/in-memory-location-store.js +1 -10
  140. package/dist/runtime/locations/index.d.ts +1 -0
  141. package/dist/runtime/locations/index.js +1 -0
  142. package/dist/runtime/locations/lease-tracker.js +1 -6
  143. package/dist/runtime/locations/location-store-repository.d.ts +51 -1
  144. package/dist/runtime/locations/location-store-repository.js +363 -139
  145. package/dist/runtime/locations/relocation-blob.d.ts +18 -1
  146. package/dist/runtime/locations/relocation-blob.js +52 -1
  147. package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
  148. package/dist/runtime/locations/request-source-resolver.js +80 -0
  149. package/dist/runtime/locations/resolvers.js +1 -1
  150. package/dist/runtime/locations/runtime.d.ts +6 -2
  151. package/dist/runtime/locations/runtime.js +37 -30
  152. package/dist/runtime/messaging/payload-codec.d.ts +7 -1
  153. package/dist/runtime/messaging/payload-codec.js +7 -6
  154. package/dist/runtime/messaging/request-result.js +29 -26
  155. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +19 -1
  156. package/dist/runtime/protocol/service_wire_codec.generated.js +85 -0
  157. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +7 -1
  158. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +23 -7
  159. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +3 -2
  160. package/dist/runtime/spots/index.d.ts +18 -11
  161. package/dist/runtime/spots/index.js +234 -176
  162. package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
  163. package/dist/runtime/spots/spot-activation-registry.js +21 -11
  164. package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
  165. package/dist/runtime/spots/spot-activation-state.js +7 -7
  166. package/dist/runtime/spots/spot-activation.d.ts +6 -3
  167. package/dist/runtime/spots/spot-activation.js +123 -44
  168. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
  169. package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
  170. package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
  171. package/dist/runtime/spots/spot-actor-membership.js +0 -8
  172. package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
  173. package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
  174. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
  175. package/dist/runtime/spots/spot-entry-activation.js +14 -9
  176. package/dist/runtime/spots/spot-handler-registry.d.ts +4 -3
  177. package/dist/runtime/spots/spot-handler-registry.js +6 -2
  178. package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
  179. package/dist/runtime/spots/spot-manager-public.js +2 -2
  180. package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
  181. package/dist/runtime/spots/spot-node-autoconnect.js +18 -39
  182. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
  183. package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
  184. package/dist/runtime/spots/spot-outbound.js +3 -3
  185. package/dist/runtime/spots/spot-remote-codec.js +5 -1
  186. package/dist/runtime/spots/spot-runtime-ports.d.ts +0 -1
  187. package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
  188. package/dist/runtime/spots/spot-serial-executor.js +61 -8
  189. package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
  190. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
  191. package/dist/runtime/spots/spot-timer.d.ts +57 -5
  192. package/dist/runtime/spots/spot-timer.js +287 -103
  193. package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
  194. package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
  195. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
  196. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
  197. package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
  198. package/dist/runtime/streams/bound-session-service.d.ts +1 -1
  199. package/dist/runtime/streams/bound-session-service.js +6 -11
  200. package/dist/runtime/streams/index.d.ts +3 -2
  201. package/dist/runtime/streams/index.js +6 -4
  202. package/dist/runtime/streams/managed-stream.js +3 -2
  203. package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
  204. package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
  205. package/dist/runtime/streams/session-actor-coordinator.js +15 -32
  206. package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
  207. package/dist/runtime/streams/stream-session-runtime.js +1 -4
  208. package/dist/runtime/workers/index.js +1 -1
  209. package/package.json +3 -3
  210. package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
  211. package/src/contracts/Common/encoded-payload-storage.ts +6 -3
  212. package/src/contracts/Configuration/Builders.ts +4 -1
  213. package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
  214. package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
  215. package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
  216. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
  217. package/src/contracts/Locations/Rows.ts +2 -0
  218. package/src/contracts/Spots/Contracts.ts +8 -0
  219. package/src/contracts/Spots/ZLinkSpot.ts +3 -3
  220. package/src/runtime/abort.ts +12 -0
  221. package/src/runtime/actors/actor-authority-publication.ts +3 -2
  222. package/src/runtime/actors/actor-client.ts +61 -70
  223. package/src/runtime/actors/actor-context.ts +49 -9
  224. package/src/runtime/actors/actor-handoff.ts +282 -106
  225. package/src/runtime/actors/actor-join-relocation.ts +2 -0
  226. package/src/runtime/actors/actor-local-native-join.ts +4 -0
  227. package/src/runtime/actors/actor-mailbox.ts +40 -2
  228. package/src/runtime/actors/actor-remote-joiner.ts +6 -1
  229. package/src/runtime/actors/actor-reply.ts +42 -0
  230. package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
  231. package/src/runtime/actors/actor-runtime-state.ts +2 -20
  232. package/src/runtime/actors/actor-source-transfer.ts +1 -34
  233. package/src/runtime/actors/index.ts +41 -27
  234. package/src/runtime/actors/session-disconnected-wire.ts +66 -0
  235. package/src/runtime/admission.ts +69 -34
  236. package/src/runtime/application-jobs/application-job-queue-scope.ts +33 -1
  237. package/src/runtime/application-jobs/contracts.ts +2 -0
  238. package/src/runtime/backend/contracts/index.ts +39 -4
  239. package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
  240. package/src/runtime/backend/mesh-completion-table.ts +4 -0
  241. package/src/runtime/backend/mesh-dispatch-pump.ts +223 -124
  242. package/src/runtime/backend/node/node-raw-binding-port.ts +106 -70
  243. package/src/runtime/backend/node/node-raw-mesh-backend.ts +221 -170
  244. package/src/runtime/backend/node/node-socket-backend-adapter.ts +5 -5
  245. package/src/runtime/channels/channel-clients.ts +49 -24
  246. package/src/runtime/channels/channel-dispatch-pipeline.ts +173 -124
  247. package/src/runtime/channels/channel-dispatchers.ts +116 -72
  248. package/src/runtime/channels/channel-envelope.ts +105 -36
  249. package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
  250. package/src/runtime/channels/channel-runtime-manager.ts +12 -5
  251. package/src/runtime/channels/channel-transports.ts +49 -58
  252. package/src/runtime/channels/client-server-location-runtime.ts +11 -10
  253. package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
  254. package/src/runtime/channels/fanout-location-runtime.ts +17 -9
  255. package/src/runtime/diagnostics/flow-context.ts +20 -7
  256. package/src/runtime/diagnostics/index.ts +1 -7
  257. package/src/runtime/diagnostics/message-flow.ts +47 -9
  258. package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
  259. package/src/runtime/execution/index.ts +40 -14
  260. package/src/runtime/execution/serial-execution-queue.ts +31 -4
  261. package/src/runtime/execution/state-lane.ts +7 -0
  262. package/src/runtime/foundation/operation-registry.ts +98 -20
  263. package/src/runtime/foundation/raw-service-mesh-runtime.ts +244 -67
  264. package/src/runtime/foundation/service-liveness-registry.ts +39 -0
  265. package/src/runtime/foundation/service-mailbox.ts +8 -0
  266. package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
  267. package/src/runtime/foundation/service-relocation-object-owner.ts +54 -37
  268. package/src/runtime/foundation/service-relocation-runtime.ts +7 -3
  269. package/src/runtime/foundation/service-runtime-contracts.ts +12 -1
  270. package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
  271. package/src/runtime/foundation/service-stateful-registry.ts +38 -35
  272. package/src/runtime/foundation/service-stateful-runtime.ts +667 -152
  273. package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -11
  274. package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
  275. package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
  276. package/src/runtime/foundation/service-wire-m6a-codec.ts +187 -51
  277. package/src/runtime/host/actor-packet-relay.ts +113 -94
  278. package/src/runtime/host/actor-placement-coordinator.ts +31 -8
  279. package/src/runtime/host/actor-transfer-runtime.ts +104 -430
  280. package/src/runtime/host/application-job-queue.ts +29 -44
  281. package/src/runtime/host/index.ts +286 -129
  282. package/src/runtime/host/location-runtime-owner.ts +2 -6
  283. package/src/runtime/host/mesh-router-resolver.ts +13 -5
  284. package/src/runtime/host/remote-bound-session-relay.ts +86 -248
  285. package/src/runtime/host/route-mesh-runtime.ts +11 -5
  286. package/src/runtime/host/runtime-shutdown.ts +15 -4
  287. package/src/runtime/host/service-relocation-host-runtime.ts +784 -475
  288. package/src/runtime/host/spot-runtime-options-factory.ts +18 -7
  289. package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
  290. package/src/runtime/locations/auto-connect-loop.ts +22 -14
  291. package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
  292. package/src/runtime/locations/auto-connect-types.ts +0 -1
  293. package/src/runtime/locations/descriptor-publication.ts +7 -0
  294. package/src/runtime/locations/domain-store-contract.ts +5 -0
  295. package/src/runtime/locations/in-memory-authority-store.ts +85 -23
  296. package/src/runtime/locations/in-memory-location-store.ts +1 -12
  297. package/src/runtime/locations/index.ts +1 -0
  298. package/src/runtime/locations/lease-tracker.ts +2 -9
  299. package/src/runtime/locations/location-store-repository.ts +628 -152
  300. package/src/runtime/locations/relocation-blob.ts +75 -1
  301. package/src/runtime/locations/request-source-resolver.ts +138 -0
  302. package/src/runtime/locations/resolvers.ts +2 -2
  303. package/src/runtime/locations/runtime.ts +70 -40
  304. package/src/runtime/messaging/payload-codec.ts +8 -11
  305. package/src/runtime/messaging/request-result.ts +33 -29
  306. package/src/runtime/protocol/service_wire_codec.generated.ts +87 -4
  307. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +13 -10
  308. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +3 -2
  309. package/src/runtime/spots/index.ts +375 -281
  310. package/src/runtime/spots/spot-activation-registry.ts +25 -13
  311. package/src/runtime/spots/spot-activation-state.ts +12 -19
  312. package/src/runtime/spots/spot-activation.ts +170 -86
  313. package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
  314. package/src/runtime/spots/spot-actor-membership.ts +0 -15
  315. package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
  316. package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
  317. package/src/runtime/spots/spot-entry-activation.ts +24 -19
  318. package/src/runtime/spots/spot-handler-registry.ts +15 -4
  319. package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
  320. package/src/runtime/spots/spot-manager-public.ts +2 -2
  321. package/src/runtime/spots/spot-node-autoconnect.ts +22 -45
  322. package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
  323. package/src/runtime/spots/spot-outbound.ts +3 -3
  324. package/src/runtime/spots/spot-remote-codec.ts +9 -2
  325. package/src/runtime/spots/spot-runtime-ports.ts +0 -1
  326. package/src/runtime/spots/spot-serial-executor.ts +97 -25
  327. package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
  328. package/src/runtime/spots/spot-subscription-dispatch.ts +1 -1
  329. package/src/runtime/spots/spot-timer.ts +413 -157
  330. package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
  331. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
  332. package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
  333. package/src/runtime/streams/bound-session-service.ts +7 -10
  334. package/src/runtime/streams/index.ts +12 -6
  335. package/src/runtime/streams/managed-stream.ts +4 -2
  336. package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
  337. package/src/runtime/streams/session-actor-coordinator.ts +39 -88
  338. package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
  339. package/src/runtime/streams/stream-session-runtime.ts +1 -5
  340. package/src/runtime/workers/index.ts +1 -1
@@ -18,6 +18,10 @@ import type {
18
18
  import { ZLinkTimerOverrunPolicy } from '../../contracts';
19
19
  import { validateTimerRegistration } from '../../contracts/Configuration/TimerRegistrationValidator';
20
20
  import { throwIfAborted } from '../abort';
21
+ import {
22
+ ZLinkFrameworkInternalErrorKind,
23
+ createInternalFrameworkException
24
+ } from '../framework-errors-internal';
21
25
  import { ZLinkSpotSerialTurnExecutor } from './spot-serial-turn-executor';
22
26
  import {
23
27
  prepareHandlerInstance,
@@ -25,10 +29,7 @@ import {
25
29
  } from '../handlers/handler-instance-scope';
26
30
  import { createInboundFlow, runWithFlow } from '../diagnostics/flow-context';
27
31
  import type { ZLinkExecutionBarrier } from '../execution';
28
- import { ZLinkStateLane } from '../execution/state-lane';
29
- import { AsyncResource } from 'node:async_hooks';
30
-
31
- const detachedTimerStateLaneResource = new AsyncResource('zlink:spot-timer');
32
+ import { startOutsideStateLane, ZLinkStateLane } from '../execution/state-lane';
32
33
 
33
34
  export interface ZLinkTimerClock {
34
35
  now(): number;
@@ -54,7 +55,7 @@ interface ZLinkTimerFinalization {
54
55
 
55
56
  interface ZLinkTimerFinalizationStart {
56
57
  readonly timeout: unknown;
57
- readonly running: Promise<void>;
58
+ readonly running: Promise<void> | undefined;
58
59
  }
59
60
 
60
61
  type ZLinkTimerFailureReporter = (
@@ -78,6 +79,13 @@ export interface ZLinkTimerRelocationState {
78
79
  readonly pendingTicks: readonly ZLinkTimerRelocationPendingTick[];
79
80
  }
80
81
 
82
+ export interface ZLinkManagedTimerStart {
83
+ /** Created paused; the owner starts it with `resume()`. */
84
+ readonly paused?: boolean;
85
+ /** Relocated logical schedule the timer continues from. */
86
+ readonly cursor?: ZLinkTimerRelocationState;
87
+ }
88
+
81
89
  export interface ZLinkTimerRelocationPendingTick {
82
90
  readonly deliveryIndex: bigint;
83
91
  readonly scheduledIndex: bigint;
@@ -85,18 +93,56 @@ export interface ZLinkTimerRelocationPendingTick {
85
93
  readonly skippedTicks: bigint;
86
94
  }
87
95
 
96
+ interface ZLinkTimerBinding {
97
+ readonly handler: ZLinkSpotTimerHandler<ZLinkTimerOwnerSpot>;
98
+ readonly spot: ZLinkTimerOwnerSpot;
99
+ readonly executionSerial?: ZLinkSpotSerialTurnExecutor;
100
+ readonly reportFailure?: ZLinkTimerFailureReporter;
101
+ }
102
+
103
+ interface ZLinkTimerBindingSlot {
104
+ binding: ZLinkTimerBinding | undefined;
105
+ }
106
+
107
+ interface ZLinkTimerEntry {
108
+ readonly generation: bigint;
109
+ readonly handlerType: string;
110
+ readonly timer: ZLinkManagedTimer;
111
+ readonly handle: ZLinkRegisteredTimer;
112
+ /** Restored from a relocation payload; Configure and OnInitialize reattach it. */
113
+ readonly restored: boolean;
114
+ readonly slot: ZLinkTimerBindingSlot;
115
+ }
116
+
117
+ type ZLinkTimerAddPreparation =
118
+ | { readonly kind: 'reattached'; readonly handle: ZLinkTimer }
119
+ | {
120
+ readonly kind: 'registered';
121
+ readonly previous: ZLinkTimerEntry | undefined;
122
+ readonly generation: bigint;
123
+ };
124
+
88
125
  export class ZLinkSpotTimerRegistry {
89
126
  private readonly lane = new ZLinkStateLane();
90
- private readonly timers = new Map<
91
- string,
92
- {
93
- readonly generation: bigint;
94
- readonly handlerType: string;
95
- readonly timer: ZLinkManagedTimer;
96
- }
97
- >();
127
+ private readonly timers = new Map<string, ZLinkTimerEntry>();
98
128
  private readonly generations = new Map<string, bigint>();
99
129
  private executionBarrier: ZLinkExecutionBarrier | undefined;
130
+ /** Frozen from the source capture or the target restore until abort or Ready. */
131
+ private frozen = false;
132
+ /**
133
+ * The lifecycle owner opens this window around the target's Configure and
134
+ * pre-Ready OnInitialize only (Spot timer §1). A registration captures it at
135
+ * the call, so a registration started inside the window reattaches a
136
+ * restored key even when its asynchronous part finishes after the window.
137
+ */
138
+ private initializing = false;
139
+ /**
140
+ * Handler constructors the target Spot announced with `addTimer(...)` during
141
+ * Configure. Together with `handlers.addTimerHandler(...)` they form the
142
+ * table that resolves a restored timer's handler by name.
143
+ */
144
+ private readonly configureTimerHandlers = new Set<Type>();
145
+ private relocationHandlersResolved = false;
100
146
 
101
147
  constructor(
102
148
  _metrics?: import('../diagnostics').ZLinkRuntimeMetrics,
@@ -137,61 +183,148 @@ export class ZLinkSpotTimerRegistry {
137
183
  reportFailure?: ZLinkTimerFailureReporter
138
184
  ): Promise<ZLinkTimer> {
139
185
  validateTimerRegistration(name, periodMs, options);
186
+ const initializing = this.initializing;
187
+ if (initializing && !this.relocationHandlersResolved) {
188
+ this.configureTimerHandlers.add(handlerType);
189
+ }
140
190
  throwIfAborted(signal);
141
191
  prepareHandlerInstance(providerResolver, handlerType);
142
192
  const handler = await resolveLifecycleHandler(spot, handlerType, providerResolver);
143
- const prepared = await this.lane.run(() => this.prepareAddCore(name));
144
- if (prepared.previous !== undefined) await prepared.previous.timer.cancel(signal);
145
193
  const executionSerial =
146
194
  this.executeTimer === undefined
147
195
  ? (this.executionSerialForTimer?.(name, serial) ?? serial)
148
196
  : undefined;
149
197
  if (this.executionBarrier !== undefined)
150
198
  executionSerial?.setExecutionBarrier(this.executionBarrier);
151
- let callbackStarted = false;
152
- const timer = startOutsideStateLane(
153
- () =>
154
- new ZLinkManagedTimer(
155
- name,
156
- periodMs,
157
- normalizeTimerOptions(options),
158
- async (tick) => {
159
- callbackStarted = false;
160
- const timerFlow = createInboundFlow(
161
- undefined,
162
- 'Timer',
163
- this.flowCreationEnabled(),
164
- undefined
165
- );
166
- const operation = () => {
167
- const current = this.timers.get(name);
168
- if (
169
- current === undefined ||
170
- current.generation !== prepared.generation ||
171
- current.timer !== timer
172
- ) {
173
- return undefined;
174
- }
175
- if (!this.executionAllowed()) return undefined;
176
- callbackStarted = true;
177
- return runWithFlow(timerFlow, () => handler.handle(spot, tick));
178
- };
179
- if (this.executeTimer !== undefined) {
180
- await this.executeTimer(name, operation);
181
- } else {
182
- await executionSerial!.execute(operation);
183
- }
184
- },
185
- reportFailure,
186
- () => callbackStarted,
187
- this.clock
188
- )
199
+ const binding: ZLinkTimerBinding = {
200
+ handler: handler as ZLinkSpotTimerHandler<ZLinkTimerOwnerSpot>,
201
+ spot,
202
+ executionSerial,
203
+ reportFailure
204
+ };
205
+ const prepared = await this.lane.run(() =>
206
+ this.prepareAddCore(name, handlerType.name, initializing, binding)
189
207
  );
208
+ if (prepared.kind === 'reattached') return prepared.handle;
209
+ if (prepared.previous !== undefined) await prepared.previous.timer.cancel(signal);
210
+ const slot: ZLinkTimerBindingSlot = { binding };
211
+ const timer = this.createTimer(name, prepared.generation, periodMs, options, slot);
212
+ const handle = new ZLinkRegisteredTimer(this, name, prepared.generation, timer);
190
213
  const registered = await this.lane.run(() =>
191
- this.completeAddCore(name, prepared.generation, handlerType.name, timer)
214
+ this.completeAddCore(name, {
215
+ generation: prepared.generation,
216
+ handlerType: handlerType.name,
217
+ timer,
218
+ handle,
219
+ restored: false,
220
+ slot
221
+ })
192
222
  );
193
223
  if (!registered) await timer.cancel(signal);
194
- return new ZLinkRegisteredTimer(this, name, prepared.generation, timer);
224
+ return handle;
225
+ }
226
+
227
+ /**
228
+ * Stages the source payload on a fresh relocation target before Configure.
229
+ * The restored registrations keep their schedule and stay frozen until Ready.
230
+ */
231
+ stageRelocation(states: readonly ZLinkTimerRelocationState[]): void {
232
+ if (this.frozen || this.timers.size !== 0) {
233
+ throw new Error('Timer relocation can only be restored into a fresh registry.');
234
+ }
235
+ const restored = states.map((state): [string, ZLinkTimerEntry] => {
236
+ if (states.some((other) => other !== state && other.name === state.name)) {
237
+ throw new Error(`Timer '${state.name}' appears twice in relocation state.`);
238
+ }
239
+ const options = {
240
+ overrunPolicy: state.overrunPolicy,
241
+ maxCatchUpTicks: state.maxCatchUpTicks,
242
+ stopOnUnhandledException: state.stopOnUnhandledException
243
+ };
244
+ validateTimerRegistration(state.name, state.periodMs, options);
245
+ const slot: ZLinkTimerBindingSlot = { binding: undefined };
246
+ const timer = this.createTimer(state.name, 1n, state.periodMs, options, slot, state);
247
+ return [
248
+ state.name,
249
+ {
250
+ generation: 1n,
251
+ handlerType: state.handlerType,
252
+ timer,
253
+ handle: new ZLinkRegisteredTimer(this, state.name, 1n, timer),
254
+ restored: true,
255
+ slot
256
+ }
257
+ ];
258
+ });
259
+ this.frozen = true;
260
+ for (const [name, entry] of restored) {
261
+ this.generations.set(name, entry.generation);
262
+ this.timers.set(name, entry);
263
+ }
264
+ }
265
+
266
+ /** Runs one relocation-target initialization callback inside the reattach window. */
267
+ async runRelocationInitialization<T>(operation: () => Promise<T> | T): Promise<T> {
268
+ if (!this.frozen || this.initializing) {
269
+ throw new Error('Timer relocation initialization is not active.');
270
+ }
271
+ this.initializing = true;
272
+ try {
273
+ return await operation();
274
+ } finally {
275
+ this.initializing = false;
276
+ }
277
+ }
278
+
279
+ /**
280
+ * After Configure, resolves every restored timer's handler constructor by
281
+ * name among the constructors the Spot announced in Configure: `addTimer(...)`
282
+ * there or `handlers.addTimerHandler(...)`. A restored key that Configure did
283
+ * not reattach is registered with the resolved constructor so it survives
284
+ * Ready without re-registration. No match or more than one match fails the
285
+ * target restore.
286
+ */
287
+ async resolveRelocationHandlers(
288
+ declared: readonly Type[],
289
+ register: (name: string, periodMs: number, handlerType: Type) => Promise<unknown>
290
+ ): Promise<void> {
291
+ if (!this.frozen || !this.initializing || this.relocationHandlersResolved) {
292
+ throw new Error('Timer relocation handler resolution is not active.');
293
+ }
294
+ this.relocationHandlersResolved = true;
295
+ const constructors = [...new Set([...declared, ...this.configureTimerHandlers])];
296
+ const restored = await this.lane.run(() =>
297
+ [...this.timers.entries()].filter(([, entry]) => entry.restored)
298
+ );
299
+ for (const [name, entry] of restored) {
300
+ const matches = constructors.filter((type) => type.name === entry.handlerType);
301
+ if (matches.length !== 1) {
302
+ throw createInternalFrameworkException(
303
+ ZLinkFrameworkInternalErrorKind.RelocationFailed,
304
+ matches.length === 0
305
+ ? `Timer '${name}' handler '${entry.handlerType}' is not declared. Register its timer with context.addTimer(...) in configure(), or declare it with context.handlers.addTimerHandler(Handler) in configure().`
306
+ : `Timer '${name}' handler '${entry.handlerType}' is ambiguous: ${matches.length} declared constructors share the name.`
307
+ );
308
+ }
309
+ if (entry.slot.binding === undefined) {
310
+ await register(name, entry.timer.periodMs, matches[0]!);
311
+ }
312
+ }
313
+ }
314
+
315
+ /** Ready: starts the restored and newly registered timers. */
316
+ async publishRelocation(): Promise<void> {
317
+ await this.lane.run(() => {
318
+ for (const [name, entry] of this.timers) {
319
+ if (entry.slot.binding === undefined) {
320
+ throw new Error(`Restored timer '${name}' has no resolved handler at Ready.`);
321
+ }
322
+ }
323
+ this.frozen = false;
324
+ for (const entry of this.timers.values()) {
325
+ startOutsideStateLane(() => entry.timer.resume());
326
+ }
327
+ });
195
328
  }
196
329
 
197
330
  async dispose(): Promise<void> {
@@ -209,9 +342,10 @@ export class ZLinkSpotTimerRegistry {
209
342
  }
210
343
 
211
344
  async captureRelocation(): Promise<readonly ZLinkTimerRelocationState[]> {
212
- const timers = await this.lane.run(() =>
213
- [...this.timers.entries()].sort(([left], [right]) => left.localeCompare(right))
214
- );
345
+ const timers = await this.lane.run(() => {
346
+ this.frozen = true;
347
+ return [...this.timers.entries()].sort(([left], [right]) => left.localeCompare(right));
348
+ });
215
349
  const states: ZLinkTimerRelocationState[] = [];
216
350
  for (const [name, entry] of timers) {
217
351
  const state = await entry.timer.captureRelocation(entry.handlerType);
@@ -221,24 +355,14 @@ export class ZLinkSpotTimerRegistry {
221
355
  return states;
222
356
  }
223
357
 
224
- restoreRelocation(states: readonly ZLinkTimerRelocationState[]): void {
225
- const byName = new Map(states.map((state) => [state.name, state]));
226
- if (byName.size !== states.length || byName.size !== this.timers.size) {
227
- throw new Error('Timer relocation inventory does not match registered handlers.');
228
- }
229
- for (const [name, entry] of this.timers) {
230
- const state = byName.get(name);
231
- if (state === undefined) {
232
- throw new Error(`Timer '${name}' is missing from relocation state.`);
233
- }
234
- entry.timer.restoreRelocation(state, entry.handlerType);
358
+ /** Source abort: the captured timers continue from their own unchanged cursor. */
359
+ abortRelocation(): void {
360
+ this.frozen = false;
361
+ for (const entry of this.timers.values()) {
362
+ startOutsideStateLane(() => entry.timer.resume());
235
363
  }
236
364
  }
237
365
 
238
- abortRelocation(states: readonly ZLinkTimerRelocationState[]): void {
239
- this.restoreRelocation(states);
240
- }
241
-
242
366
  async commitRelocation(): Promise<void> {
243
367
  await this.dispose();
244
368
  }
@@ -254,31 +378,92 @@ export class ZLinkSpotTimerRegistry {
254
378
  await timer.cancel(signal);
255
379
  }
256
380
 
257
- private prepareAddCore(name: string): {
258
- readonly previous:
259
- | {
260
- readonly generation: bigint;
261
- readonly handlerType: string;
262
- readonly timer: ZLinkManagedTimer;
263
- }
264
- | undefined;
265
- readonly generation: bigint;
266
- } {
381
+ private createTimer(
382
+ name: string,
383
+ generation: bigint,
384
+ periodMs: number,
385
+ options: ZLinkTimerOptions | undefined,
386
+ slot: ZLinkTimerBindingSlot,
387
+ cursor?: ZLinkTimerRelocationState
388
+ ): ZLinkManagedTimer {
389
+ let callbackStarted = false;
390
+ const timer: ZLinkManagedTimer = startOutsideStateLane(
391
+ () =>
392
+ new ZLinkManagedTimer(
393
+ name,
394
+ periodMs,
395
+ normalizeTimerOptions(options),
396
+ async (tick) => {
397
+ callbackStarted = false;
398
+ const binding = slot.binding;
399
+ if (binding === undefined) return;
400
+ const timerFlow = createInboundFlow(
401
+ undefined,
402
+ 'Timer',
403
+ this.flowCreationEnabled(),
404
+ undefined
405
+ );
406
+ const operation = () => {
407
+ const current = this.timers.get(name);
408
+ if (
409
+ current === undefined ||
410
+ current.generation !== generation ||
411
+ current.timer !== timer
412
+ ) {
413
+ return undefined;
414
+ }
415
+ if (!this.executionAllowed()) return undefined;
416
+ callbackStarted = true;
417
+ return runWithFlow(timerFlow, () => binding.handler.handle(binding.spot, tick));
418
+ };
419
+ if (this.executeTimer !== undefined) {
420
+ await this.executeTimer(name, operation);
421
+ } else {
422
+ await binding.executionSerial!.execute(operation);
423
+ }
424
+ },
425
+ (tick, cause, event) => slot.binding?.reportFailure?.(tick, cause, event),
426
+ () => callbackStarted,
427
+ this.clock,
428
+ { paused: true, cursor }
429
+ )
430
+ );
431
+ return timer;
432
+ }
433
+
434
+ private prepareAddCore(
435
+ name: string,
436
+ handlerType: string,
437
+ initializing: boolean,
438
+ binding: ZLinkTimerBinding
439
+ ): ZLinkTimerAddPreparation {
440
+ if (this.frozen && !initializing) {
441
+ throw createInternalFrameworkException(
442
+ ZLinkFrameworkInternalErrorKind.InvalidOperation,
443
+ `Timer '${name}' registration is sealed for relocation.`
444
+ );
445
+ }
267
446
  const previous = this.timers.get(name);
447
+ if (initializing && previous?.restored === true) {
448
+ if (previous.handlerType !== handlerType) {
449
+ throw createInternalFrameworkException(
450
+ ZLinkFrameworkInternalErrorKind.InvalidOperation,
451
+ `Timer '${name}' was restored with handler '${previous.handlerType}', not '${handlerType}'.`
452
+ );
453
+ }
454
+ previous.slot.binding = binding;
455
+ return { kind: 'reattached', handle: previous.handle };
456
+ }
268
457
  this.timers.delete(name);
269
458
  const generation = (this.generations.get(name) ?? 0n) + 1n;
270
459
  this.generations.set(name, generation);
271
- return { previous, generation };
460
+ return { kind: 'registered', previous, generation };
272
461
  }
273
462
 
274
- private completeAddCore(
275
- name: string,
276
- generation: bigint,
277
- handlerType: string,
278
- timer: ZLinkManagedTimer
279
- ): boolean {
280
- if (this.generations.get(name) !== generation) return false;
281
- this.timers.set(name, { generation, handlerType, timer });
463
+ private completeAddCore(name: string, entry: ZLinkTimerEntry): boolean {
464
+ if (this.generations.get(name) !== entry.generation) return false;
465
+ this.timers.set(name, entry);
466
+ if (!this.frozen) startOutsideStateLane(() => entry.timer.resume());
282
467
  return true;
283
468
  }
284
469
 
@@ -326,24 +511,44 @@ export class ZLinkManagedTimer implements ZLinkTimer {
326
511
  private pausedForRelocation = false;
327
512
  private startedAtMs: number;
328
513
  private startedAtUnixMs: number;
514
+ /** DeliveryIndex and ScheduledIndex of the last completed tick. */
329
515
  private deliveryIndex = 0n;
330
516
  private lastScheduledIndex = 0n;
517
+ /**
518
+ * The selected tick whose callback has not completed (Spot timer §1). It is
519
+ * the only pending tick a relocation captures, and a restored timer delivers
520
+ * it before it selects another tick.
521
+ */
522
+ private pendingScheduledIndex: bigint | undefined;
331
523
  private timeout: unknown;
332
- private running: Promise<void> = Promise.resolve();
524
+ /** The dispatch in flight, from its timer callback until `completeFireCore`. */
525
+ private running: Promise<void> | undefined;
333
526
  private dispatchFailure: { readonly cause: unknown } | undefined;
334
527
  private finalization: ZLinkTimerFinalization | undefined;
335
528
 
336
529
  constructor(
337
530
  private readonly name: string,
338
- private readonly periodMs: number,
531
+ readonly periodMs: number,
339
532
  private readonly options: Required<ZLinkTimerOptions>,
340
533
  private readonly onTick: (tick: ZLinkTimerTick) => Promise<void>,
341
534
  private readonly onFailure?: ZLinkTimerFailureReporter,
342
535
  private readonly shouldWaitForRunningOnCancel: () => boolean = () => true,
343
- private readonly clock: ZLinkTimerClock = systemTimerClock
536
+ private readonly clock: ZLinkTimerClock = systemTimerClock,
537
+ start: ZLinkManagedTimerStart = {}
344
538
  ) {
345
- this.startedAtMs = clock.now();
346
- this.startedAtUnixMs = clock.utcNow();
539
+ const cursor = start.cursor;
540
+ if (cursor === undefined) {
541
+ this.startedAtMs = clock.now();
542
+ this.startedAtUnixMs = clock.utcNow();
543
+ } else {
544
+ validateRelocationCursor(name, periodMs, cursor);
545
+ this.startedAtUnixMs = cursor.startedAtUnixMs;
546
+ this.startedAtMs = clock.now() - Math.max(0, clock.utcNow() - cursor.startedAtUnixMs);
547
+ this.deliveryIndex = cursor.deliveryIndex;
548
+ this.lastScheduledIndex = cursor.lastScheduledIndex;
549
+ this.pendingScheduledIndex = cursor.pendingTicks[0]?.scheduledIndex;
550
+ }
551
+ this.pausedForRelocation = start.paused === true;
347
552
  this.scheduleNext();
348
553
  }
349
554
 
@@ -392,59 +597,44 @@ export class ZLinkManagedTimer implements ZLinkTimer {
392
597
  deliveryIndex: this.deliveryIndex,
393
598
  lastScheduledIndex: this.lastScheduledIndex,
394
599
  nextDueAtUnixMs: this.startedAtUnixMs + Number(this.lastScheduledIndex + 1n) * this.periodMs,
395
- pendingTicks: []
600
+ pendingTicks:
601
+ this.pendingScheduledIndex === undefined
602
+ ? []
603
+ : [this.pendingTick(this.pendingScheduledIndex)]
396
604
  }));
397
605
  }
398
606
 
399
- restoreRelocation(state: ZLinkTimerRelocationState, handlerType = ''): void {
400
- if (this.disposed) throw new Error(`Timer '${this.name}' is disposed.`);
401
- if (
402
- state.name !== this.name ||
403
- state.handlerType !== handlerType ||
404
- state.periodMs !== this.periodMs ||
405
- state.overrunPolicy !== this.options.overrunPolicy ||
406
- state.maxCatchUpTicks !== this.options.maxCatchUpTicks ||
407
- state.stopOnUnhandledException !== this.options.stopOnUnhandledException ||
408
- !Number.isSafeInteger(state.startedAtUnixMs) ||
409
- state.deliveryIndex < 0n ||
410
- state.lastScheduledIndex < 0n ||
411
- !Number.isSafeInteger(state.nextDueAtUnixMs) ||
412
- state.nextDueAtUnixMs !==
413
- state.startedAtUnixMs + Number(state.lastScheduledIndex + 1n) * state.periodMs ||
414
- state.pendingTicks.length !== 0
415
- ) {
416
- throw new Error(`Timer '${this.name}' relocation contract does not match its registration.`);
417
- }
418
- if (this.timeout !== undefined) this.clock.clearTimeout(this.timeout);
419
- this.timeout = undefined;
420
- this.startedAtUnixMs = state.startedAtUnixMs;
421
- this.startedAtMs = this.clock.now() - Math.max(0, this.clock.utcNow() - state.startedAtUnixMs);
422
- this.deliveryIndex = state.deliveryIndex;
423
- this.lastScheduledIndex = state.lastScheduledIndex;
607
+ /** Starts a timer created paused, or continues one paused by a relocation capture. */
608
+ resume(): void {
609
+ if (this.disposed || !this.pausedForRelocation) return;
424
610
  this.pausedForRelocation = false;
425
611
  this.scheduleNext();
426
612
  }
427
613
 
428
614
  private scheduleNext(): void {
429
- if (this.disposed || this.pausedForRelocation) {
615
+ // The dispatch in flight schedules the next tick when it completes.
616
+ if (this.disposed || this.pausedForRelocation || this.running !== undefined) {
430
617
  return;
431
618
  }
432
619
 
433
620
  const delayMs =
434
- this.options.overrunPolicy === ZLinkTimerOverrunPolicy.DelayNextTick
435
- ? this.periodMs
436
- : Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
437
- this.timeout = this.clock.setTimeout(() => {
438
- this.timeout = undefined;
439
- const execution = startOutsideStateLane(() => this.fire());
440
- this.running = execution.then(
441
- () => undefined,
442
- (cause) =>
443
- this.lane.run(() => {
444
- this.dispatchFailure ??= { cause };
445
- })
446
- );
447
- }, delayMs);
621
+ this.pendingScheduledIndex !== undefined
622
+ ? 0
623
+ : this.options.overrunPolicy === ZLinkTimerOverrunPolicy.DelayNextTick
624
+ ? this.periodMs
625
+ : Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
626
+ this.timeout = startOutsideStateLane(() =>
627
+ this.clock.setTimeout(() => {
628
+ this.timeout = undefined;
629
+ this.running = this.fire().then(
630
+ () => undefined,
631
+ (cause) =>
632
+ this.lane.run(() => {
633
+ this.dispatchFailure ??= { cause };
634
+ })
635
+ );
636
+ }, delayMs)
637
+ );
448
638
  }
449
639
 
450
640
  private async fire(): Promise<void> {
@@ -455,6 +645,11 @@ export class ZLinkManagedTimer implements ZLinkTimer {
455
645
  try {
456
646
  await this.onTick(prepared.tick);
457
647
  } catch (cause) {
648
+ // A tick whose callback had not started when a relocation captured it
649
+ // belongs to the capture: the target delivers it, so the source neither
650
+ // reports nor completes it.
651
+ const started = this.shouldWaitForRunningOnCancel();
652
+ if (!started && (await this.lane.run(() => this.pausedForRelocation))) return;
458
653
  shouldContinue = !this.options.stopOnUnhandledException;
459
654
  await this.onFailure?.(
460
655
  prepared.tick,
@@ -465,7 +660,7 @@ export class ZLinkManagedTimer implements ZLinkTimer {
465
660
  );
466
661
  }
467
662
 
468
- await this.lane.run(() => this.completeFireCore(prepared.scheduledIndex, shouldContinue));
663
+ await this.lane.run(() => this.completeFireCore(shouldContinue));
469
664
  }
470
665
 
471
666
  private cancelCore(): ZLinkTimerFinalizationStart {
@@ -474,7 +669,7 @@ export class ZLinkManagedTimer implements ZLinkTimer {
474
669
  this.timeout = undefined;
475
670
  return {
476
671
  timeout,
477
- running: this.shouldWaitForRunningOnCancel() ? this.running : Promise.resolve()
672
+ running: this.shouldWaitForRunningOnCancel() ? this.running : undefined
478
673
  };
479
674
  }
480
675
 
@@ -507,43 +702,54 @@ export class ZLinkManagedTimer implements ZLinkTimer {
507
702
  }
508
703
  }
509
704
 
510
- private prepareCaptureCore(): { readonly running: Promise<void> } {
705
+ private prepareCaptureCore(): { readonly running: Promise<void> | undefined } {
511
706
  if (this.disposed) throw new Error(`Timer '${this.name}' is disposed.`);
512
707
  this.pausedForRelocation = true;
513
708
  if (this.timeout !== undefined) {
514
709
  this.clock.clearTimeout(this.timeout);
515
710
  this.timeout = undefined;
516
711
  }
517
- return { running: this.shouldWaitForRunningOnCancel() ? this.running : Promise.resolve() };
712
+ return { running: this.shouldWaitForRunningOnCancel() ? this.running : undefined };
518
713
  }
519
714
 
520
- private prepareFireCore():
521
- { readonly tick: ZLinkTimerTick; readonly scheduledIndex: bigint } | undefined {
715
+ private prepareFireCore(): { readonly tick: ZLinkTimerTick } | undefined {
522
716
  if (this.disposed) return undefined;
523
- const scheduledIndex = this.selectScheduledIndex();
524
- const skippedTicks = scheduledIndex - this.lastScheduledIndex - 1n;
717
+ this.pendingScheduledIndex ??= this.selectScheduledIndex();
718
+ const pending = this.pendingTick(this.pendingScheduledIndex);
525
719
  const startedElapsedMs = this.elapsedMs();
526
- const scheduledElapsedMs = Number(scheduledIndex) * this.periodMs;
527
- this.deliveryIndex += 1n;
720
+ const scheduledElapsedMs = Number(pending.scheduledIndex) * this.periodMs;
528
721
  return {
529
- scheduledIndex,
530
722
  tick: {
531
723
  name: this.name,
532
- deliveryIndex: this.deliveryIndex,
533
- scheduledIndex,
724
+ deliveryIndex: pending.deliveryIndex,
725
+ scheduledIndex: pending.scheduledIndex,
534
726
  periodMs: this.periodMs,
535
- scheduledAt: new Date(this.startedAtUnixMs + scheduledElapsedMs),
727
+ scheduledAt: new Date(pending.scheduledAtUnixMs),
536
728
  startedAt: new Date(this.clock.utcNow()),
537
729
  scheduledElapsedMs,
538
730
  startedElapsedMs,
539
731
  delayMs: startedElapsedMs - scheduledElapsedMs,
540
- skippedTicks
732
+ skippedTicks: pending.skippedTicks
541
733
  }
542
734
  };
543
735
  }
544
736
 
545
- private completeFireCore(scheduledIndex: bigint, shouldContinue: boolean): void {
546
- this.lastScheduledIndex = scheduledIndex;
737
+ /** The tick info of the pending tick at `scheduledIndex` after the completed cursor. */
738
+ private pendingTick(scheduledIndex: bigint): ZLinkTimerRelocationPendingTick {
739
+ return relocationPendingTick(
740
+ this.startedAtUnixMs,
741
+ this.periodMs,
742
+ this.deliveryIndex,
743
+ this.lastScheduledIndex,
744
+ scheduledIndex
745
+ );
746
+ }
747
+
748
+ private completeFireCore(shouldContinue: boolean): void {
749
+ this.deliveryIndex += 1n;
750
+ this.lastScheduledIndex = this.pendingScheduledIndex!;
751
+ this.pendingScheduledIndex = undefined;
752
+ this.running = undefined;
547
753
  if (!shouldContinue) {
548
754
  this.disposed = true;
549
755
  return;
@@ -580,8 +786,58 @@ export class ZLinkManagedTimer implements ZLinkTimer {
580
786
  }
581
787
  }
582
788
 
583
- function startOutsideStateLane<T>(work: () => T): T {
584
- return detachedTimerStateLaneResource.runInAsyncScope(work);
789
+ function validateRelocationCursor(
790
+ name: string,
791
+ periodMs: number,
792
+ state: ZLinkTimerRelocationState
793
+ ): void {
794
+ if (
795
+ state.name !== name ||
796
+ state.periodMs !== periodMs ||
797
+ !Number.isSafeInteger(state.startedAtUnixMs) ||
798
+ state.deliveryIndex < 0n ||
799
+ state.lastScheduledIndex < 0n ||
800
+ !Number.isSafeInteger(state.nextDueAtUnixMs) ||
801
+ state.nextDueAtUnixMs !==
802
+ state.startedAtUnixMs + Number(state.lastScheduledIndex + 1n) * state.periodMs ||
803
+ state.pendingTicks.length > 1 ||
804
+ state.pendingTicks.some((pending) => {
805
+ const expected = relocationPendingTick(
806
+ state.startedAtUnixMs,
807
+ state.periodMs,
808
+ state.deliveryIndex,
809
+ state.lastScheduledIndex,
810
+ pending.scheduledIndex
811
+ );
812
+ return (
813
+ pending.scheduledIndex <= state.lastScheduledIndex ||
814
+ pending.deliveryIndex !== expected.deliveryIndex ||
815
+ pending.scheduledAtUnixMs !== expected.scheduledAtUnixMs ||
816
+ pending.skippedTicks !== expected.skippedTicks
817
+ );
818
+ })
819
+ ) {
820
+ throw new Error(`Timer '${name}' relocation state is invalid.`);
821
+ }
822
+ }
823
+
824
+ /**
825
+ * Spot timer §1: the tick info that follows the completed cursor. A timer holds
826
+ * at most one pending tick, so its DeliveryIndex is the next one.
827
+ */
828
+ function relocationPendingTick(
829
+ startedAtUnixMs: number,
830
+ periodMs: number,
831
+ deliveryIndex: bigint,
832
+ lastScheduledIndex: bigint,
833
+ scheduledIndex: bigint
834
+ ): ZLinkTimerRelocationPendingTick {
835
+ return {
836
+ deliveryIndex: deliveryIndex + 1n,
837
+ scheduledIndex,
838
+ scheduledAtUnixMs: startedAtUnixMs + Number(scheduledIndex) * periodMs,
839
+ skippedTicks: scheduledIndex - lastScheduledIndex - 1n
840
+ };
585
841
  }
586
842
 
587
843
  export function createTimerDiagnostics(