@zlink-systems/framework 0.30.0 → 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 (456) hide show
  1. package/LICENSE +150 -68
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/contracts/Actors/ZLinkActorClient.d.ts +2 -1
  4. package/dist/contracts/Channels/Calls.d.ts +2 -1
  5. package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +6 -0
  6. package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
  7. package/dist/contracts/Common/ZLinkMessageMetadata.js +11 -5
  8. package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
  9. package/dist/contracts/Common/encoded-payload-storage.js +5 -2
  10. package/dist/contracts/Configuration/Builders.d.ts +1 -1
  11. package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
  12. package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
  13. package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
  14. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
  15. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
  16. package/dist/contracts/Handlers/Attributes.d.ts +5 -13
  17. package/dist/contracts/Handlers/Attributes.js +7 -11
  18. package/dist/contracts/Handlers/JsonContract.d.ts +2 -46
  19. package/dist/contracts/Handlers/JsonContract.js +4 -153
  20. package/dist/contracts/Locations/Rows.d.ts +2 -0
  21. package/dist/contracts/Spots/Contracts.d.ts +6 -1
  22. package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
  23. package/dist/nest-integration.d.ts +1 -1
  24. package/dist/nest-integration.js +2 -1
  25. package/dist/runtime/abort.d.ts +5 -0
  26. package/dist/runtime/abort.js +10 -0
  27. package/dist/runtime/actors/actor-authority-publication.js +3 -2
  28. package/dist/runtime/actors/actor-client.d.ts +1 -2
  29. package/dist/runtime/actors/actor-client.js +52 -57
  30. package/dist/runtime/actors/actor-context.d.ts +2 -0
  31. package/dist/runtime/actors/actor-context.js +46 -9
  32. package/dist/runtime/actors/actor-handoff.d.ts +8 -4
  33. package/dist/runtime/actors/actor-handoff.js +219 -92
  34. package/dist/runtime/actors/actor-join-relocation.d.ts +3 -0
  35. package/dist/runtime/actors/actor-local-native-join.js +13 -12
  36. package/dist/runtime/actors/actor-mailbox.js +27 -1
  37. package/dist/runtime/actors/actor-remote-joiner.js +3 -1
  38. package/dist/runtime/actors/actor-remote-wire.js +15 -0
  39. package/dist/runtime/actors/actor-reply.d.ts +4 -0
  40. package/dist/runtime/actors/actor-reply.js +29 -0
  41. package/dist/runtime/actors/actor-routed-join-transport.d.ts +6 -2
  42. package/dist/runtime/actors/actor-routed-json-request.d.ts +2 -0
  43. package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
  44. package/dist/runtime/actors/actor-runtime-state.js +2 -20
  45. package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
  46. package/dist/runtime/actors/index.d.ts +8 -3
  47. package/dist/runtime/actors/index.js +55 -27
  48. package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
  49. package/dist/runtime/actors/session-disconnected-wire.js +48 -0
  50. package/dist/runtime/actors/spot-actor-dispatch.js +2 -2
  51. package/dist/runtime/admission.d.ts +9 -1
  52. package/dist/runtime/admission.js +56 -23
  53. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +6 -8
  54. package/dist/runtime/application-jobs/application-ingress-record-owner.js +39 -22
  55. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +15 -1
  56. package/dist/runtime/application-jobs/application-job-queue-scope.js +61 -20
  57. package/dist/runtime/application-jobs/contracts.d.ts +12 -1
  58. package/dist/runtime/backend/contracts/index.d.ts +37 -12
  59. package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
  60. package/dist/runtime/backend/mesh-completion-table.d.ts +9 -1
  61. package/dist/runtime/backend/mesh-completion-table.js +154 -49
  62. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +28 -1
  63. package/dist/runtime/backend/mesh-dispatch-pump.js +208 -101
  64. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
  65. package/dist/runtime/backend/node/node-backend-adapter-support.js +16 -9
  66. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  67. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +5 -2
  68. package/dist/runtime/backend/node/node-raw-binding-port.js +124 -72
  69. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +46 -13
  70. package/dist/runtime/backend/node/node-raw-mesh-backend.js +181 -164
  71. package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
  72. package/dist/runtime/backend/node/node-socket-backend-adapter.js +11 -11
  73. package/dist/runtime/backend/raw-binding-port.d.ts +2 -2
  74. package/dist/runtime/backend/runtime-message.d.ts +1 -0
  75. package/dist/runtime/backend/runtime-message.js +5 -0
  76. package/dist/runtime/backend/runtime-values.d.ts +1 -0
  77. package/dist/runtime/backend/runtime-values.js +2 -1
  78. package/dist/runtime/channels/channel-clients.d.ts +2 -0
  79. package/dist/runtime/channels/channel-clients.js +75 -23
  80. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +25 -5
  81. package/dist/runtime/channels/channel-dispatch-pipeline.js +111 -56
  82. package/dist/runtime/channels/channel-dispatch-services.d.ts +1 -0
  83. package/dist/runtime/channels/channel-dispatch-services.js +45 -0
  84. package/dist/runtime/channels/channel-dispatchers.d.ts +4 -3
  85. package/dist/runtime/channels/channel-dispatchers.js +62 -54
  86. package/dist/runtime/channels/channel-envelope-inspection.d.ts +1 -2
  87. package/dist/runtime/channels/channel-envelope-inspection.js +1 -16
  88. package/dist/runtime/channels/channel-envelope.d.ts +2 -0
  89. package/dist/runtime/channels/channel-envelope.js +146 -28
  90. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  91. package/dist/runtime/channels/channel-outbound-operations.js +8 -8
  92. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
  93. package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
  94. package/dist/runtime/channels/channel-runtime-manager.d.ts +9 -8
  95. package/dist/runtime/channels/channel-runtime-manager.js +16 -16
  96. package/dist/runtime/channels/channel-transports.d.ts +18 -16
  97. package/dist/runtime/channels/channel-transports.js +77 -58
  98. package/dist/runtime/channels/client-server-location-runtime.js +5 -4
  99. package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
  100. package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
  101. package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
  102. package/dist/runtime/channels/fanout-location-runtime.js +3 -3
  103. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  104. package/dist/runtime/channels/local-spot-route-transport.js +2 -1
  105. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +2 -2
  106. package/dist/runtime/channels/router-socket-spot-route-transport.js +4 -4
  107. package/dist/runtime/channels/source-spot-router.d.ts +2 -2
  108. package/dist/runtime/channels/source-spot-router.js +4 -4
  109. package/dist/runtime/channels/spot-node-route-transport.d.ts +2 -2
  110. package/dist/runtime/channels/spot-node-route-transport.js +4 -4
  111. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +2 -2
  112. package/dist/runtime/channels/spot-route-bridge-transport.js +4 -4
  113. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +5 -4
  114. package/dist/runtime/channels/spot-route-dispatch-strategy.js +18 -18
  115. package/dist/runtime/diagnostics/flow-context.js +19 -7
  116. package/dist/runtime/diagnostics/index.js +4 -9
  117. package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
  118. package/dist/runtime/diagnostics/message-flow.js +35 -5
  119. package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
  120. package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
  121. package/dist/runtime/execution/index.d.ts +4 -2
  122. package/dist/runtime/execution/index.js +33 -10
  123. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -0
  124. package/dist/runtime/execution/serial-execution-queue.js +22 -2
  125. package/dist/runtime/execution/state-lane.d.ts +2 -0
  126. package/dist/runtime/execution/state-lane.js +5 -0
  127. package/dist/runtime/foundation/actor-join-recovery-codec.js +2 -2
  128. package/dist/runtime/foundation/durable-service-request.d.ts +14 -0
  129. package/dist/runtime/foundation/durable-service-request.js +98 -0
  130. package/dist/runtime/foundation/operation-identity.d.ts +1 -0
  131. package/dist/runtime/foundation/operation-identity.js +5 -1
  132. package/dist/runtime/foundation/operation-registry.d.ts +25 -1
  133. package/dist/runtime/foundation/operation-registry.js +86 -18
  134. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +61 -8
  135. package/dist/runtime/foundation/raw-service-mesh-runtime.js +486 -266
  136. package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
  137. package/dist/runtime/foundation/service-liveness-registry.js +38 -0
  138. package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
  139. package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
  140. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +3 -3
  141. package/dist/runtime/foundation/service-relocation-object-owner.js +48 -34
  142. package/dist/runtime/foundation/service-relocation-runtime.d.ts +2 -0
  143. package/dist/runtime/foundation/service-relocation-runtime.js +20 -4
  144. package/dist/runtime/foundation/service-runtime-contracts.d.ts +10 -1
  145. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
  146. package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
  147. package/dist/runtime/foundation/service-stateful-registry.js +24 -26
  148. package/dist/runtime/foundation/service-stateful-runtime.d.ts +75 -9
  149. package/dist/runtime/foundation/service-stateful-runtime.js +486 -218
  150. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -6
  151. package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
  152. package/dist/runtime/foundation/service-topology-registry.d.ts +12 -2
  153. package/dist/runtime/foundation/service-topology-registry.js +61 -27
  154. package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
  155. package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
  156. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  157. package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
  158. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +13 -3
  159. package/dist/runtime/foundation/service-wire-m6a-codec.js +189 -78
  160. package/dist/runtime/handlers/handler-instance-scope.d.ts +8 -0
  161. package/dist/runtime/handlers/handler-instance-scope.js +54 -11
  162. package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
  163. package/dist/runtime/host/actor-packet-relay.js +59 -72
  164. package/dist/runtime/host/actor-placement-coordinator.js +25 -8
  165. package/dist/runtime/host/actor-runtime-options-factory.js +2 -2
  166. package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
  167. package/dist/runtime/host/actor-transfer-runtime.js +85 -331
  168. package/dist/runtime/host/application-job-queue.d.ts +40 -7
  169. package/dist/runtime/host/application-job-queue.js +137 -42
  170. package/dist/runtime/host/entry-actor-runtime.d.ts +1 -0
  171. package/dist/runtime/host/entry-actor-runtime.js +3 -0
  172. package/dist/runtime/host/index.d.ts +12 -2
  173. package/dist/runtime/host/index.js +300 -202
  174. package/dist/runtime/host/location-runtime-owner.js +2 -6
  175. package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
  176. package/dist/runtime/host/mesh-router-resolver.js +14 -6
  177. package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
  178. package/dist/runtime/host/remote-bound-session-relay.js +73 -208
  179. package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
  180. package/dist/runtime/host/route-mesh-runtime.js +6 -4
  181. package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
  182. package/dist/runtime/host/runtime-shutdown.js +3 -2
  183. package/dist/runtime/host/service-relocation-control.d.ts +1 -0
  184. package/dist/runtime/host/service-relocation-control.js +10 -4
  185. package/dist/runtime/host/service-relocation-host-runtime.d.ts +22 -14
  186. package/dist/runtime/host/service-relocation-host-runtime.js +754 -599
  187. package/dist/runtime/host/spot-address-transport.js +3 -2
  188. package/dist/runtime/host/spot-runtime-options-factory.js +16 -7
  189. package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
  190. package/dist/runtime/locations/actor-location-claims.d.ts +2 -2
  191. package/dist/runtime/locations/actor-location-claims.js +34 -19
  192. package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
  193. package/dist/runtime/locations/auto-connect-loop.js +22 -14
  194. package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
  195. package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
  196. package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
  197. package/dist/runtime/locations/descriptor-publication.js +11 -0
  198. package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
  199. package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
  200. package/dist/runtime/locations/in-memory-authority-store.js +56 -19
  201. package/dist/runtime/locations/in-memory-location-store.js +1 -10
  202. package/dist/runtime/locations/index.d.ts +1 -0
  203. package/dist/runtime/locations/index.js +1 -0
  204. package/dist/runtime/locations/lease-tracker.js +1 -6
  205. package/dist/runtime/locations/lifecycle.d.ts +1 -2
  206. package/dist/runtime/locations/lifecycle.js +2 -5
  207. package/dist/runtime/locations/location-store-repository.d.ts +51 -1
  208. package/dist/runtime/locations/location-store-repository.js +363 -139
  209. package/dist/runtime/locations/relocation-blob.d.ts +18 -1
  210. package/dist/runtime/locations/relocation-blob.js +52 -1
  211. package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
  212. package/dist/runtime/locations/request-source-resolver.js +80 -0
  213. package/dist/runtime/locations/resolvers.js +1 -1
  214. package/dist/runtime/locations/runtime.d.ts +7 -3
  215. package/dist/runtime/locations/runtime.js +61 -53
  216. package/dist/runtime/messaging/framework-json-v1.d.ts +4 -6
  217. package/dist/runtime/messaging/framework-json-v1.js +38 -294
  218. package/dist/runtime/messaging/packet-name.d.ts +1 -1
  219. package/dist/runtime/messaging/packet-name.js +8 -24
  220. package/dist/runtime/messaging/payload-codec.d.ts +7 -1
  221. package/dist/runtime/messaging/payload-codec.js +7 -6
  222. package/dist/runtime/messaging/request-result.d.ts +2 -0
  223. package/dist/runtime/messaging/request-result.js +57 -0
  224. package/dist/runtime/messaging/submission-result.d.ts +1 -0
  225. package/dist/runtime/messaging/submission-result.js +5 -2
  226. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +34 -1
  227. package/dist/runtime/protocol/service_wire_codec.generated.js +229 -0
  228. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +16 -1
  229. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +37 -8
  230. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +7 -2
  231. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +36 -21
  232. package/dist/runtime/spots/index.d.ts +21 -11
  233. package/dist/runtime/spots/index.js +243 -178
  234. package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
  235. package/dist/runtime/spots/spot-activation-registry.js +21 -11
  236. package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
  237. package/dist/runtime/spots/spot-activation-state.js +7 -7
  238. package/dist/runtime/spots/spot-activation.d.ts +7 -3
  239. package/dist/runtime/spots/spot-activation.js +140 -52
  240. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
  241. package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
  242. package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
  243. package/dist/runtime/spots/spot-actor-membership.js +0 -8
  244. package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
  245. package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
  246. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
  247. package/dist/runtime/spots/spot-entry-activation.js +20 -11
  248. package/dist/runtime/spots/spot-handler-registry.d.ts +7 -4
  249. package/dist/runtime/spots/spot-handler-registry.js +24 -10
  250. package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
  251. package/dist/runtime/spots/spot-manager-public.js +2 -2
  252. package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
  253. package/dist/runtime/spots/spot-node-autoconnect.js +22 -42
  254. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
  255. package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
  256. package/dist/runtime/spots/spot-outbound.d.ts +3 -0
  257. package/dist/runtime/spots/spot-outbound.js +27 -18
  258. package/dist/runtime/spots/spot-remote-codec.js +5 -1
  259. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +1 -0
  260. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +1 -1
  261. package/dist/runtime/spots/spot-runtime-ports.d.ts +1 -1
  262. package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
  263. package/dist/runtime/spots/spot-serial-executor.js +61 -8
  264. package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
  265. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -4
  266. package/dist/runtime/spots/spot-timer.d.ts +57 -5
  267. package/dist/runtime/spots/spot-timer.js +288 -103
  268. package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
  269. package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
  270. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
  271. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
  272. package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
  273. package/dist/runtime/streams/bound-session-service.d.ts +1 -1
  274. package/dist/runtime/streams/bound-session-service.js +6 -11
  275. package/dist/runtime/streams/index.d.ts +3 -2
  276. package/dist/runtime/streams/index.js +6 -4
  277. package/dist/runtime/streams/managed-stream.js +3 -2
  278. package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
  279. package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
  280. package/dist/runtime/streams/session-actor-coordinator.js +15 -32
  281. package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
  282. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -1
  283. package/dist/runtime/streams/stream-session-runtime.js +14 -31
  284. package/dist/runtime/workers/index.js +1 -1
  285. package/package.json +3 -3
  286. package/src/contracts/Actors/ZLinkActorClient.ts +2 -1
  287. package/src/contracts/Channels/Calls.ts +2 -1
  288. package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
  289. package/src/contracts/Common/ZLinkMessageMetadata.ts +13 -8
  290. package/src/contracts/Common/encoded-payload-storage.ts +6 -3
  291. package/src/contracts/Configuration/Builders.ts +4 -1
  292. package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
  293. package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
  294. package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
  295. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
  296. package/src/contracts/Handlers/Attributes.ts +14 -21
  297. package/src/contracts/Handlers/JsonContract.ts +8 -216
  298. package/src/contracts/Locations/Rows.ts +2 -0
  299. package/src/contracts/Spots/Contracts.ts +10 -1
  300. package/src/contracts/Spots/ZLinkSpot.ts +3 -3
  301. package/src/nest-integration.ts +1 -0
  302. package/src/runtime/abort.ts +12 -0
  303. package/src/runtime/actors/actor-authority-publication.ts +3 -2
  304. package/src/runtime/actors/actor-client.ts +100 -85
  305. package/src/runtime/actors/actor-context.ts +49 -9
  306. package/src/runtime/actors/actor-handoff.ts +291 -110
  307. package/src/runtime/actors/actor-join-relocation.ts +3 -0
  308. package/src/runtime/actors/actor-local-native-join.ts +21 -26
  309. package/src/runtime/actors/actor-mailbox.ts +40 -2
  310. package/src/runtime/actors/actor-remote-joiner.ts +6 -1
  311. package/src/runtime/actors/actor-remote-wire.ts +17 -0
  312. package/src/runtime/actors/actor-reply.ts +42 -0
  313. package/src/runtime/actors/actor-routed-join-transport.ts +14 -4
  314. package/src/runtime/actors/actor-routed-json-request.ts +10 -2
  315. package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
  316. package/src/runtime/actors/actor-runtime-state.ts +2 -20
  317. package/src/runtime/actors/actor-source-transfer.ts +1 -34
  318. package/src/runtime/actors/index.ts +60 -32
  319. package/src/runtime/actors/session-disconnected-wire.ts +66 -0
  320. package/src/runtime/actors/spot-actor-dispatch.ts +4 -2
  321. package/src/runtime/admission.ts +69 -21
  322. package/src/runtime/application-jobs/application-ingress-record-owner.ts +41 -25
  323. package/src/runtime/application-jobs/application-job-queue-scope.ts +83 -22
  324. package/src/runtime/application-jobs/contracts.ts +16 -1
  325. package/src/runtime/backend/contracts/index.ts +92 -18
  326. package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
  327. package/src/runtime/backend/mesh-completion-table.ts +160 -57
  328. package/src/runtime/backend/mesh-dispatch-pump.ts +237 -108
  329. package/src/runtime/backend/node/node-backend-adapter-support.ts +17 -8
  330. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +12 -2
  331. package/src/runtime/backend/node/node-raw-binding-port.ts +148 -85
  332. package/src/runtime/backend/node/node-raw-mesh-backend.ts +315 -201
  333. package/src/runtime/backend/node/node-socket-backend-adapter.ts +26 -11
  334. package/src/runtime/backend/raw-binding-port.ts +7 -2
  335. package/src/runtime/backend/runtime-message.ts +6 -0
  336. package/src/runtime/backend/runtime-values.ts +1 -0
  337. package/src/runtime/channels/channel-clients.ts +132 -58
  338. package/src/runtime/channels/channel-dispatch-pipeline.ts +198 -114
  339. package/src/runtime/channels/channel-dispatch-services.ts +69 -1
  340. package/src/runtime/channels/channel-dispatchers.ts +136 -90
  341. package/src/runtime/channels/channel-envelope-inspection.ts +2 -16
  342. package/src/runtime/channels/channel-envelope.ts +225 -31
  343. package/src/runtime/channels/channel-outbound-operations.ts +22 -8
  344. package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
  345. package/src/runtime/channels/channel-runtime-manager.ts +41 -17
  346. package/src/runtime/channels/channel-transports.ts +171 -112
  347. package/src/runtime/channels/client-server-location-runtime.ts +11 -10
  348. package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
  349. package/src/runtime/channels/fanout-location-runtime.ts +17 -9
  350. package/src/runtime/channels/local-spot-route-transport.ts +3 -1
  351. package/src/runtime/channels/router-socket-spot-route-transport.ts +13 -4
  352. package/src/runtime/channels/source-spot-router.ts +8 -4
  353. package/src/runtime/channels/spot-node-route-transport.ts +8 -4
  354. package/src/runtime/channels/spot-route-bridge-transport.ts +6 -4
  355. package/src/runtime/channels/spot-route-dispatch-strategy.ts +47 -18
  356. package/src/runtime/diagnostics/flow-context.ts +20 -7
  357. package/src/runtime/diagnostics/index.ts +1 -7
  358. package/src/runtime/diagnostics/message-flow.ts +47 -9
  359. package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
  360. package/src/runtime/execution/index.ts +40 -14
  361. package/src/runtime/execution/serial-execution-queue.ts +33 -4
  362. package/src/runtime/execution/state-lane.ts +7 -0
  363. package/src/runtime/foundation/actor-join-recovery-codec.ts +2 -2
  364. package/src/runtime/foundation/durable-service-request.ts +137 -0
  365. package/src/runtime/foundation/operation-identity.ts +5 -1
  366. package/src/runtime/foundation/operation-registry.ts +98 -20
  367. package/src/runtime/foundation/raw-service-mesh-runtime.ts +704 -336
  368. package/src/runtime/foundation/service-liveness-registry.ts +39 -0
  369. package/src/runtime/foundation/service-mailbox.ts +8 -0
  370. package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
  371. package/src/runtime/foundation/service-relocation-object-owner.ts +55 -36
  372. package/src/runtime/foundation/service-relocation-runtime.ts +25 -4
  373. package/src/runtime/foundation/service-runtime-contracts.ts +19 -2
  374. package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
  375. package/src/runtime/foundation/service-stateful-registry.ts +38 -35
  376. package/src/runtime/foundation/service-stateful-runtime.ts +732 -275
  377. package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -16
  378. package/src/runtime/foundation/service-topology-registry.ts +91 -36
  379. package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
  380. package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
  381. package/src/runtime/foundation/service-wire-m6a-codec.ts +231 -76
  382. package/src/runtime/handlers/handler-instance-scope.ts +67 -16
  383. package/src/runtime/host/actor-packet-relay.ts +113 -94
  384. package/src/runtime/host/actor-placement-coordinator.ts +31 -8
  385. package/src/runtime/host/actor-runtime-options-factory.ts +13 -2
  386. package/src/runtime/host/actor-transfer-runtime.ts +104 -430
  387. package/src/runtime/host/application-job-queue.ts +160 -41
  388. package/src/runtime/host/entry-actor-runtime.ts +4 -0
  389. package/src/runtime/host/index.ts +473 -271
  390. package/src/runtime/host/location-runtime-owner.ts +2 -6
  391. package/src/runtime/host/mesh-router-resolver.ts +13 -5
  392. package/src/runtime/host/remote-bound-session-relay.ts +88 -271
  393. package/src/runtime/host/route-mesh-runtime.ts +12 -6
  394. package/src/runtime/host/runtime-shutdown.ts +15 -4
  395. package/src/runtime/host/service-relocation-control.ts +11 -5
  396. package/src/runtime/host/service-relocation-host-runtime.ts +1092 -759
  397. package/src/runtime/host/spot-address-transport.ts +4 -2
  398. package/src/runtime/host/spot-runtime-options-factory.ts +20 -7
  399. package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
  400. package/src/runtime/locations/actor-location-claims.ts +52 -30
  401. package/src/runtime/locations/auto-connect-loop.ts +22 -14
  402. package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
  403. package/src/runtime/locations/auto-connect-types.ts +0 -1
  404. package/src/runtime/locations/descriptor-publication.ts +7 -0
  405. package/src/runtime/locations/domain-store-contract.ts +5 -0
  406. package/src/runtime/locations/in-memory-authority-store.ts +85 -23
  407. package/src/runtime/locations/in-memory-location-store.ts +1 -12
  408. package/src/runtime/locations/index.ts +1 -0
  409. package/src/runtime/locations/lease-tracker.ts +2 -9
  410. package/src/runtime/locations/lifecycle.ts +8 -26
  411. package/src/runtime/locations/location-store-repository.ts +628 -152
  412. package/src/runtime/locations/relocation-blob.ts +75 -1
  413. package/src/runtime/locations/request-source-resolver.ts +138 -0
  414. package/src/runtime/locations/resolvers.ts +2 -2
  415. package/src/runtime/locations/runtime.ts +98 -64
  416. package/src/runtime/messaging/framework-json-v1.ts +5 -325
  417. package/src/runtime/messaging/packet-name.ts +5 -25
  418. package/src/runtime/messaging/payload-codec.ts +8 -11
  419. package/src/runtime/messaging/request-result.ts +62 -0
  420. package/src/runtime/messaging/submission-result.ts +5 -1
  421. package/src/runtime/protocol/service_wire_codec.generated.ts +218 -5
  422. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +16 -13
  423. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +46 -25
  424. package/src/runtime/spots/index.ts +389 -283
  425. package/src/runtime/spots/spot-activation-registry.ts +25 -13
  426. package/src/runtime/spots/spot-activation-state.ts +12 -19
  427. package/src/runtime/spots/spot-activation.ts +198 -96
  428. package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
  429. package/src/runtime/spots/spot-actor-membership.ts +0 -15
  430. package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
  431. package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
  432. package/src/runtime/spots/spot-entry-activation.ts +33 -21
  433. package/src/runtime/spots/spot-handler-registry.ts +56 -12
  434. package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
  435. package/src/runtime/spots/spot-manager-public.ts +2 -2
  436. package/src/runtime/spots/spot-node-autoconnect.ts +28 -47
  437. package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
  438. package/src/runtime/spots/spot-outbound.ts +39 -19
  439. package/src/runtime/spots/spot-remote-codec.ts +9 -2
  440. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +2 -1
  441. package/src/runtime/spots/spot-runtime-ports.ts +1 -1
  442. package/src/runtime/spots/spot-serial-executor.ts +97 -25
  443. package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
  444. package/src/runtime/spots/spot-subscription-dispatch.ts +1 -4
  445. package/src/runtime/spots/spot-timer.ts +418 -158
  446. package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
  447. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
  448. package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
  449. package/src/runtime/streams/bound-session-service.ts +7 -10
  450. package/src/runtime/streams/index.ts +12 -6
  451. package/src/runtime/streams/managed-stream.ts +4 -2
  452. package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
  453. package/src/runtime/streams/session-actor-coordinator.ts +39 -88
  454. package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
  455. package/src/runtime/streams/stream-session-runtime.ts +13 -34
  456. package/src/runtime/workers/index.ts +1 -1
@@ -1,3 +1,5 @@
1
+ import type { ZLinkRuntimeMetrics } from '../diagnostics/runtime-metrics';
2
+ import { routingIdsEqual, toBackendRoutingId } from '../routing-id';
1
3
  import {
2
4
  ServiceRelocationAuthorityPayloadCodec,
3
5
  serviceRelocationAuthorityApplicationPayload
@@ -22,6 +24,7 @@ import {
22
24
  type ZLinkStoreKey,
23
25
  type ZLinkStoreReadResult,
24
26
  type ZLinkStoreScanCursor,
27
+ type ZLinkStoreValue,
25
28
  type ZLinkStoreVersion,
26
29
  type ZLinkStoreWriteRequest,
27
30
  ZLinkFrameworkException,
@@ -43,6 +46,7 @@ import {
43
46
  import { ZLinkLocationWriteStatus as WriteStatus } from '../../contracts/Locations';
44
47
  import {
45
48
  ZLINK_PROVIDER_MAX_PAGE_SIZE,
49
+ ZLINK_PROVIDER_MAX_WRITE_KEYS,
46
50
  ZLINK_PROVIDER_MAX_VALUE_BYTES
47
51
  } from '../../contracts/Locations/Stores';
48
52
  import { SHA256_DIGEST_BYTES } from '../foundation/actor-join-recovery-codec';
@@ -130,6 +134,15 @@ interface AuthorityRecord {
130
134
  readonly visibleStoreVersion?: string;
131
135
  }
132
136
 
137
+ /** An Actor membership of an ended User Spot incarnation (location runtime §6.1). */
138
+ interface EndedSpotMember {
139
+ readonly key: ZLinkStoreKey;
140
+ readonly version: ZLinkStoreVersion;
141
+ readonly ownerId: string;
142
+ readonly leaseGeneration: bigint;
143
+ readonly allocation: StoredAuthoritySnapshot['allocation'];
144
+ }
145
+
133
146
  interface CapacityRecord {
134
147
  readonly active: CapacityUsage;
135
148
  readonly pending: CapacityUsage;
@@ -207,28 +220,48 @@ const MAX_OWNER_CLEANUP_BATCH_ROWS = 2_047;
207
220
  */
208
221
  export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
209
222
  private readonly provider: ZLinkLocationStore;
223
+ private readonly confirmedCapacities = new Map<string, CapacityRecord | undefined>();
210
224
  private readonly aggregateInventory: ZLinkAggregateInventoryStore;
211
225
 
212
226
  constructor(
213
227
  provider: ZLinkLocationStore,
214
- private readonly nowProvider: () => Date = () => new Date()
228
+ private readonly nowProvider: () => Date = () => new Date(),
229
+ private readonly metrics?: ZLinkRuntimeMetrics
215
230
  ) {
216
231
  super(nowProvider);
217
232
  // Classify only failures of the opaque SPI, before interpreting its records.
233
+ const confirm = (key: ZLinkStoreKey, bytes?: Uint8Array) => {
234
+ if (this.confirmedCapacities.has(key.value)) {
235
+ this.confirmedCapacities.set(
236
+ key.value,
237
+ bytes === undefined ? emptyCapacityRecord() : decodeJson<CapacityRecord>(bytes)
238
+ );
239
+ }
240
+ };
218
241
  this.provider = {
219
242
  async read(key, signal) {
243
+ let result: ZLinkStoreReadResult;
220
244
  try {
221
- return await provider.read(key, signal);
245
+ result = await provider.read(key, signal);
222
246
  } catch (error) {
223
247
  throw locationStoreProviderFailure(error, signal);
224
248
  }
249
+ confirm(key, result.kind === 'found' ? result.value.bytes : undefined);
250
+ return result;
225
251
  },
226
252
  async write(request, signal) {
253
+ let result;
227
254
  try {
228
- return await provider.write(request, signal);
255
+ result = await provider.write(request, signal);
229
256
  } catch (error) {
230
257
  throw locationStoreProviderFailure(error, signal);
231
258
  }
259
+ if (result.kind === 'applied') {
260
+ for (const mutation of request.mutations) {
261
+ if (mutation.kind === 'put') confirm(mutation.key, mutation.bytes);
262
+ }
263
+ }
264
+ return result;
232
265
  },
233
266
  async scan(request, signal) {
234
267
  try {
@@ -241,9 +274,38 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
241
274
  this.aggregateInventory = new ZLinkAggregateInventoryStore(this.provider);
242
275
  }
243
276
 
277
+ private async observeStore<T>(operation: string, request: () => Promise<T>): Promise<T> {
278
+ try {
279
+ return await request();
280
+ } catch (error) {
281
+ if (
282
+ error instanceof ZLinkFrameworkException &&
283
+ error.kind === ZLinkFrameworkErrorKind.Unavailable
284
+ ) {
285
+ this.metrics?.recordLocationStoreError(operation);
286
+ }
287
+ throw error;
288
+ }
289
+ }
290
+
291
+ confirmedCapacity(
292
+ key: ZLinkMeshNodeDescriptorKey,
293
+ limits: ZLinkMeshNodeDescriptor['populationCapacity']
294
+ ): ZLinkMeshNodeDescriptor['populationCapacity'] | undefined {
295
+ const record = this.confirmedCapacities.get(capacityKey(key.meshName, String(key.rid)).value);
296
+ return record === undefined ? undefined : projectCapacity(limits, record);
297
+ }
298
+
244
299
  override async readAuthority(
245
300
  key: ZLinkAuthorityKey,
246
301
  signal?: AbortSignal
302
+ ): Promise<ZLinkAuthorityReadResult> {
303
+ return this.observeStore('read', () => this.readAuthorityRequest(key, signal));
304
+ }
305
+
306
+ private async readAuthorityRequest(
307
+ key: ZLinkAuthorityKey,
308
+ signal?: AbortSignal
247
309
  ): Promise<ZLinkAuthorityReadResult> {
248
310
  const result = await this.provider.read(authorityKey(key.value), signal);
249
311
  if (result.kind === 'missing') {
@@ -262,6 +324,17 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
262
324
  cursor: ZLinkAuthorityScanCursor | undefined,
263
325
  limit: number,
264
326
  signal?: AbortSignal
327
+ ): Promise<ZLinkAuthorityScanResult> {
328
+ return this.observeStore('read', () =>
329
+ this.listAuthoritiesRequest(prefix, cursor, limit, signal)
330
+ );
331
+ }
332
+
333
+ private async listAuthoritiesRequest(
334
+ prefix: string,
335
+ cursor: ZLinkAuthorityScanCursor | undefined,
336
+ limit: number,
337
+ signal?: AbortSignal
265
338
  ): Promise<ZLinkAuthorityScanResult> {
266
339
  if (!Number.isInteger(limit) || limit < 1 || limit > ZLINK_PROVIDER_MAX_PAGE_SIZE) {
267
340
  throw new RangeError(`Authority scan limit must be in 1..${ZLINK_PROVIDER_MAX_PAGE_SIZE}.`);
@@ -303,6 +376,17 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
303
376
  expectedStoreVersion: ZLinkAuthorityStoreVersion,
304
377
  mutation: ZLinkAuthorityMutation,
305
378
  signal?: AbortSignal
379
+ ): Promise<ZLinkAuthorityCompareExchangeResult> {
380
+ return this.observeStore('compare_exchange', () =>
381
+ this.compareExchangeAuthorityRequest(key, expectedStoreVersion, mutation, signal)
382
+ );
383
+ }
384
+
385
+ private async compareExchangeAuthorityRequest(
386
+ key: ZLinkAuthorityKey,
387
+ expectedStoreVersion: ZLinkAuthorityStoreVersion,
388
+ mutation: ZLinkAuthorityMutation,
389
+ signal?: AbortSignal
306
390
  ): Promise<ZLinkAuthorityCompareExchangeResult> {
307
391
  if (mutation.kind !== 'delete') {
308
392
  validatePayloadSize(mutation.payload, 'Authority payload');
@@ -551,6 +635,15 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
551
635
  override async prepareAggregate(
552
636
  request: ZLinkAggregatePrepareRequest,
553
637
  signal?: AbortSignal
638
+ ): Promise<ZLinkAggregatePrepareResult> {
639
+ return this.observeStore('compare_exchange', () =>
640
+ this.prepareAggregateRequest(request, signal)
641
+ );
642
+ }
643
+
644
+ private async prepareAggregateRequest(
645
+ request: ZLinkAggregatePrepareRequest,
646
+ signal?: AbortSignal
554
647
  ): Promise<ZLinkAggregatePrepareResult> {
555
648
  validateProviderAggregateRequest(request);
556
649
  const fence = aggregateFence(request.aggregateId, request.aggregateGeneration);
@@ -838,7 +931,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
838
931
  aggregateRecord.requestFingerprint === fingerprint &&
839
932
  (aggregateRecord.state === 'staging' || aggregateRecord.state === 'prepared')
840
933
  ) {
841
- return await this.prepareAggregate(request, signal);
934
+ return await this.prepareAggregateRequest(request, signal);
842
935
  }
843
936
  }
844
937
  await this.clearAggregateMarkers(fence, installed, signal);
@@ -915,6 +1008,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
915
1008
  override async commitAggregate(
916
1009
  fence: ZLinkAggregateFence,
917
1010
  signal?: AbortSignal
1011
+ ): Promise<ZLinkAggregateCommitResult> {
1012
+ return this.observeStore('compare_exchange', () => this.commitAggregateRequest(fence, signal));
1013
+ }
1014
+
1015
+ private async commitAggregateRequest(
1016
+ fence: ZLinkAggregateFence,
1017
+ signal?: AbortSignal
918
1018
  ): Promise<ZLinkAggregateCommitResult> {
919
1019
  validateAggregateFence(fence);
920
1020
  return await this.commitAggregateCore(fence, signal);
@@ -1146,6 +1246,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1146
1246
  override async abortAggregate(
1147
1247
  fence: ZLinkAggregateFence,
1148
1248
  signal?: AbortSignal
1249
+ ): Promise<ZLinkAggregateAbortResult> {
1250
+ return this.observeStore('compare_exchange', () => this.abortAggregateRequest(fence, signal));
1251
+ }
1252
+
1253
+ private async abortAggregateRequest(
1254
+ fence: ZLinkAggregateFence,
1255
+ signal?: AbortSignal
1149
1256
  ): Promise<ZLinkAggregateAbortResult> {
1150
1257
  validateAggregateFence(fence);
1151
1258
  const rowKey = aggregateKey(fence);
@@ -1206,6 +1313,17 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1206
1313
  expectedStoreVersion: string,
1207
1314
  signal?: AbortSignal,
1208
1315
  request?: ZLinkEndedOwnerReleaseIntent
1316
+ ): Promise<boolean> {
1317
+ return this.observeStore('release', () =>
1318
+ this.releaseEndedReservationRequest(key, expectedStoreVersion, signal, request)
1319
+ );
1320
+ }
1321
+
1322
+ private async releaseEndedReservationRequest(
1323
+ key: ZLinkAuthorityKey,
1324
+ expectedStoreVersion: string,
1325
+ signal?: AbortSignal,
1326
+ request?: ZLinkEndedOwnerReleaseIntent
1209
1327
  ): Promise<boolean> {
1210
1328
  return (
1211
1329
  (await this.tryReleaseEndedReservation(key, expectedStoreVersion, signal, request)) ===
@@ -1245,9 +1363,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1245
1363
  this.provider.read(descriptorKey, signal),
1246
1364
  this.provider.read(capacityRowKey, signal)
1247
1365
  ]);
1248
- if (lease.kind === 'found' && lease.value.expiresAt === undefined)
1249
- throw new Error('Location Store owner lease record is invalid.');
1250
1366
  const ownerLive = sameLiveOwner(lease, snapshot);
1367
+ let members: EndedSpotMember[] = [];
1251
1368
  if (snapshot.allocation.state === 'active') {
1252
1369
  if (ownerLive) return 'notReclaimable';
1253
1370
  let unavailable = record.aggregate !== undefined || hasRelocationAuthority(snapshot.payload);
@@ -1259,46 +1376,12 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1259
1376
  spot.stableType !== request.intent.stableType ||
1260
1377
  spot.spotId !== request.key.globalId ||
1261
1378
  spot.activationRecovery !== undefined;
1262
- if (spot?.kind === 'user_spot') {
1263
- // The snapshot starts only after the old owner's lease was found invalid.
1264
- for (const prefix of [`${PREFIX}actor:`, `${AUTHORITY_PREIMAGE_PREFIX}actor\0`]) {
1265
- membershipSnapshot: for (;;) {
1266
- let snapshotHasMember = false;
1267
- let cursor: ZLinkStoreScanCursor | undefined;
1268
- do {
1269
- const page = await this.provider.scan(
1270
- { prefix, cursor, limit: ZLINK_PROVIDER_MAX_PAGE_SIZE },
1271
- signal
1272
- );
1273
- if (page.kind === 'expired') continue membershipSnapshot;
1274
- for (const item of page.value.items) {
1275
- if (prefix === `${PREFIX}actor:`) {
1276
- const actor = normalizeActor(
1277
- decodeJson<DescriptorRecord<ZLinkActorLocation>>(item.value.bytes).descriptor
1278
- );
1279
- snapshotHasMember ||=
1280
- actor.spotKind === ZLinkSpotKind.User &&
1281
- String(actor.spotId) === spot.spotId &&
1282
- actor.spotGeneration === snapshot.objectGeneration;
1283
- } else {
1284
- const authority = decodeAuthorityRecord(item.value.bytes).snapshot;
1285
- if (authority.allocation.objectKind !== 'actor') continue;
1286
- const actor = decodeActorAuthorityPayload(
1287
- serviceRelocationAuthorityApplicationPayload(authority.payload)
1288
- );
1289
- snapshotHasMember ||=
1290
- actor?.currentSpotKind === ZLinkSpotKind.User &&
1291
- actor.currentSpotId === spot.spotId &&
1292
- actor.currentSpotGeneration === snapshot.objectGeneration;
1293
- }
1294
- }
1295
- cursor = page.value.nextCursor;
1296
- } while (cursor !== undefined);
1297
- unavailable ||= snapshotHasMember;
1298
- break;
1299
- }
1300
- }
1301
- }
1379
+ if (
1380
+ spot?.kind === 'user_spot' &&
1381
+ !unavailable &&
1382
+ request?.actorRelocationPolicy === 'disabled'
1383
+ )
1384
+ members = await this.readEndedSpotMembers(spot.spotId, snapshot.objectGeneration, signal);
1302
1385
  }
1303
1386
  if (unavailable || request?.actorRelocationPolicy !== 'disabled')
1304
1387
  throw new ZLinkFrameworkException(
@@ -1318,60 +1401,171 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1318
1401
  (record.aggregate !== undefined || hasRelocationAuthority(snapshot.payload))
1319
1402
  )
1320
1403
  return 'notReclaimable';
1404
+ // Location runtime §6.1: the authority and every ended-owner member are released in
1405
+ // one conditional batch. Each member is fenced by its row version and the observed
1406
+ // (invalid) state of its own owner lease; member capacity is returned like the Spot's.
1407
+ const conditions: ZLinkStoreCondition[] = [
1408
+ { kind: 'version', key: rowKey, expected: current.value.version },
1409
+ conditionFor(descriptorKey, descriptor)
1410
+ ];
1321
1411
  const mutations: ZLinkStoreWriteRequest['mutations'][number][] = [
1322
1412
  { kind: 'delete', key: rowKey }
1323
1413
  ];
1324
- if (capacity.kind === 'found') {
1325
- const stored = decodeJson<CapacityRecord>(capacity.value.bytes);
1326
- try {
1414
+ const owners = new Map<string, ZLinkStoreReadResult>([[snapshot.ownerId, lease]]);
1415
+ for (const member of members) {
1416
+ let memberLease = owners.get(member.ownerId);
1417
+ if (memberLease === undefined) {
1418
+ memberLease = await this.provider.read(ownerKey(member.ownerId), signal);
1419
+ owners.set(member.ownerId, memberLease);
1420
+ }
1421
+ if (
1422
+ memberLease.kind === 'found' &&
1423
+ matchesLiveOwnerLease(memberLease, member.ownerId, member.leaseGeneration)
1424
+ )
1425
+ throw new ZLinkFrameworkException(
1426
+ ZLinkFrameworkErrorKind.Unavailable,
1427
+ 'User Spot still has a member with a live owner lease.'
1428
+ );
1429
+ conditions.push({ kind: 'version', key: member.key, expected: member.version });
1430
+ mutations.push({ kind: 'delete', key: member.key });
1431
+ }
1432
+ for (const [ownerId, read] of owners) conditions.push(conditionFor(ownerKey(ownerId), read));
1433
+ const capacities = new Map<string, { read: ZLinkStoreReadResult; next?: CapacityRecord }>([
1434
+ [capacityRowKey.value, { read: capacity }]
1435
+ ]);
1436
+ const releases: { allocation: StoredAuthoritySnapshot['allocation'] }[] = [
1437
+ { allocation: snapshot.allocation },
1438
+ ...members.map((member) => ({ allocation: member.allocation }))
1439
+ ];
1440
+ try {
1441
+ for (const { allocation } of releases) {
1442
+ const key = capacityKey(allocation.descriptor.meshName, String(allocation.descriptor.rid));
1443
+ let entry = capacities.get(key.value);
1444
+ if (entry === undefined) {
1445
+ entry = { read: await this.provider.read(key, signal) };
1446
+ capacities.set(key.value, entry);
1447
+ }
1448
+ if (entry.read.kind !== 'found') continue;
1449
+ const stored = entry.next ?? decodeJson<CapacityRecord>(entry.read.value.bytes);
1450
+ entry.next = {
1451
+ active:
1452
+ allocation.state === 'active'
1453
+ ? subtractCapacity(stored.active, allocation.capacity)
1454
+ : stored.active,
1455
+ pending:
1456
+ allocation.state === 'reserved'
1457
+ ? subtractCapacity(stored.pending, allocation.capacity)
1458
+ : stored.pending
1459
+ };
1460
+ }
1461
+ } catch (error) {
1462
+ if (
1463
+ !(error instanceof ZLinkFrameworkException) ||
1464
+ error.kind !== ZLinkFrameworkErrorKind.DataLost
1465
+ )
1466
+ throw error;
1467
+ for (const authority of [{ key: rowKey, version: current.value.version }, ...members]) {
1468
+ const observed = await this.provider.read(authority.key, signal);
1469
+ if (observed.kind === 'missing' || observed.value.version.value !== authority.version.value)
1470
+ return 'conflict';
1471
+ }
1472
+ throw error;
1473
+ }
1474
+ for (const [key, entry] of capacities) {
1475
+ conditions.push(conditionFor(storeKey(key), entry.read));
1476
+ if (entry.next !== undefined)
1327
1477
  mutations.push({
1328
1478
  kind: 'put',
1329
- key: capacityRowKey,
1330
- bytes: encodeJson({
1331
- active:
1332
- snapshot.allocation.state === 'active'
1333
- ? subtractCapacity(stored.active, snapshot.allocation.capacity)
1334
- : stored.active,
1335
- pending:
1336
- snapshot.allocation.state === 'reserved'
1337
- ? subtractCapacity(stored.pending, snapshot.allocation.capacity)
1338
- : stored.pending
1339
- } satisfies CapacityRecord)
1479
+ key: storeKey(key),
1480
+ bytes: encodeJson(entry.next satisfies CapacityRecord)
1340
1481
  });
1341
- } catch (error) {
1342
- if (
1343
- !(error instanceof ZLinkFrameworkException) ||
1344
- error.kind !== ZLinkFrameworkErrorKind.DataLost
1345
- )
1346
- throw error;
1347
- const observed = await this.provider.read(rowKey, signal);
1348
- if (
1349
- observed.kind === 'missing' ||
1350
- observed.value.version.value !== current.value.version.value
1351
- )
1352
- return 'conflict';
1353
- throw error;
1354
- }
1355
1482
  }
1356
- const result = await this.provider.write(
1357
- {
1358
- conditions: [
1359
- { kind: 'version', key: rowKey, expected: current.value.version },
1360
- conditionFor(leaseKey, lease),
1361
- conditionFor(descriptorKey, descriptor),
1362
- conditionFor(capacityRowKey, capacity)
1363
- ],
1364
- mutations
1365
- },
1366
- signal
1367
- );
1483
+ // Every mutated key is also a condition key, so the condition count is the batch's
1484
+ // unique key count. A release beyond the provider bound changes nothing.
1485
+ if (conditions.length > ZLINK_PROVIDER_MAX_WRITE_KEYS)
1486
+ throw new ZLinkFrameworkException(
1487
+ ZLinkFrameworkErrorKind.Unavailable,
1488
+ 'User Spot member release exceeds one Location Store batch.'
1489
+ );
1490
+ const result = await this.provider.write({ conditions, mutations }, signal);
1368
1491
  return result.kind === 'applied' ? 'reclaimed' : 'conflict';
1369
1492
  }
1370
1493
 
1494
+ /**
1495
+ * Reads every Actor membership of one User Spot incarnation from complete snapshot scans
1496
+ * (location runtime §6.1), from Actor authorities alone. An expired snapshot
1497
+ * discards the earlier pages and restarts; a Store failure propagates. A member authority
1498
+ * that is relocating, part of an aggregate or not yet activated keeps the Spot.
1499
+ */
1500
+ private async readEndedSpotMembers(
1501
+ spotId: string,
1502
+ spotGeneration: bigint,
1503
+ signal?: AbortSignal
1504
+ ): Promise<EndedSpotMember[]> {
1505
+ membershipSnapshot: for (;;) {
1506
+ const members: EndedSpotMember[] = [];
1507
+ let cursor: ZLinkStoreScanCursor | undefined;
1508
+ do {
1509
+ const page = await this.provider.scan(
1510
+ {
1511
+ prefix: `${AUTHORITY_PREIMAGE_PREFIX}actor\0`,
1512
+ cursor,
1513
+ limit: ZLINK_PROVIDER_MAX_PAGE_SIZE
1514
+ },
1515
+ signal
1516
+ );
1517
+ if (page.kind === 'expired') continue membershipSnapshot;
1518
+ for (const item of page.value.items) {
1519
+ const record = decodeAuthorityRecord(item.value.bytes);
1520
+ const authority = record.snapshot;
1521
+ if (authority.allocation.objectKind !== 'actor') continue;
1522
+ const actor = decodeActorAuthorityPayload(
1523
+ serviceRelocationAuthorityApplicationPayload(authority.payload)
1524
+ );
1525
+ if (
1526
+ actor?.currentSpotKind !== ZLinkSpotKind.User ||
1527
+ actor.currentSpotId !== spotId ||
1528
+ actor.currentSpotGeneration !== spotGeneration
1529
+ )
1530
+ continue;
1531
+ if (
1532
+ authority.allocation.state !== 'active' ||
1533
+ record.aggregate !== undefined ||
1534
+ hasRelocationAuthority(authority.payload) ||
1535
+ decodeActorAuthorityPayload(authority.payload)?.state !== 'ready'
1536
+ )
1537
+ throw new ZLinkFrameworkException(
1538
+ ZLinkFrameworkErrorKind.Unavailable,
1539
+ 'User Spot still has a relocating or unactivated member.'
1540
+ );
1541
+ members.push({
1542
+ key: item.key,
1543
+ version: item.value.version,
1544
+ ownerId: authority.ownerId,
1545
+ leaseGeneration: authority.ownerLeaseGeneration,
1546
+ allocation: authority.allocation
1547
+ });
1548
+ }
1549
+ cursor = page.value.nextCursor;
1550
+ } while (cursor !== undefined);
1551
+ return members;
1552
+ }
1553
+ }
1554
+
1371
1555
  override async reserve(
1372
1556
  request: ZLinkObjectReserveRequest,
1373
1557
  signal?: AbortSignal,
1374
1558
  deadlineUnixMs?: bigint
1559
+ ): Promise<ZLinkObjectReserveResult> {
1560
+ return this.observeStore('compare_exchange', () =>
1561
+ this.reserveRequest(request, signal, deadlineUnixMs)
1562
+ );
1563
+ }
1564
+
1565
+ private async reserveRequest(
1566
+ request: ZLinkObjectReserveRequest,
1567
+ signal?: AbortSignal,
1568
+ deadlineUnixMs?: bigint
1375
1569
  ): Promise<ZLinkObjectReserveResult> {
1376
1570
  const encodedAuthorityKey = encodeAuthorityKey(request.key.kind, request.key.globalId);
1377
1571
  const reservationId = randomUUID();
@@ -1517,6 +1711,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1517
1711
  request: ZLinkObjectCommitRequest,
1518
1712
  signal?: AbortSignal,
1519
1713
  deadlineUnixMs?: bigint
1714
+ ): Promise<ZLinkObjectCommitResult> {
1715
+ return this.observeStore('compare_exchange', () =>
1716
+ this.commitRequest(request, signal, deadlineUnixMs)
1717
+ );
1718
+ }
1719
+
1720
+ private async commitRequest(
1721
+ request: ZLinkObjectCommitRequest,
1722
+ signal?: AbortSignal,
1723
+ deadlineUnixMs?: bigint
1520
1724
  ): Promise<ZLinkObjectCommitResult> {
1521
1725
  const key = encodeAuthorityKey(request.key.kind, request.key.globalId);
1522
1726
  const rowKey = authorityKey(key.value);
@@ -1603,6 +1807,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1603
1807
  request: ZLinkObjectAbortRequest,
1604
1808
  signal?: AbortSignal,
1605
1809
  deadlineUnixMs?: bigint
1810
+ ): Promise<ZLinkObjectAbortResult> {
1811
+ return this.observeStore('compare_exchange', () =>
1812
+ this.abortRequest(request, signal, deadlineUnixMs)
1813
+ );
1814
+ }
1815
+
1816
+ private async abortRequest(
1817
+ request: ZLinkObjectAbortRequest,
1818
+ signal?: AbortSignal,
1819
+ deadlineUnixMs?: bigint
1606
1820
  ): Promise<ZLinkObjectAbortResult> {
1607
1821
  const key = encodeAuthorityKey(request.key.kind, request.key.globalId);
1608
1822
  const rowKey = authorityKey(key.value);
@@ -1627,12 +1841,6 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1627
1841
  this.provider.read(leaseKey, signal),
1628
1842
  this.provider.read(capacityRowKey, signal)
1629
1843
  ]);
1630
- if (leaseRead.kind === 'found' && leaseRead.value.expiresAt === undefined) {
1631
- // A lease is always written with a positive TTL, so a missing expiry
1632
- // is a corrupt record. Abort deletes authority state, so refuse
1633
- // rather than read the absence as expiry.
1634
- throw new Error('Location Store owner lease record is invalid.');
1635
- }
1636
1844
  if (liveTargetDescriptor(descriptorRead, leaseRead, request.target) === undefined)
1637
1845
  return { kind: 'stale' };
1638
1846
  const capacity =
@@ -1672,6 +1880,15 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1672
1880
  override async completeCreation(
1673
1881
  request: ZLinkObjectCreationCompleteRequest,
1674
1882
  signal?: AbortSignal
1883
+ ): Promise<ZLinkObjectCreationCompleteResult> {
1884
+ return this.observeStore('compare_exchange', () =>
1885
+ this.completeCreationRequest(request, signal)
1886
+ );
1887
+ }
1888
+
1889
+ private async completeCreationRequest(
1890
+ request: ZLinkObjectCreationCompleteRequest,
1891
+ signal?: AbortSignal
1675
1892
  ): Promise<ZLinkObjectCreationCompleteResult> {
1676
1893
  if (request.key.kind !== 'actor') {
1677
1894
  throw new TypeError('completeCreation is reserved for Actor creation.');
@@ -1840,6 +2057,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1840
2057
  override async readCreationTerminal(
1841
2058
  operation: ZLinkCreationOperationIdentity,
1842
2059
  signal?: AbortSignal
2060
+ ): Promise<ZLinkCreationTerminalReadResult> {
2061
+ return this.observeStore('read', () => this.readCreationTerminalRequest(operation, signal));
2062
+ }
2063
+
2064
+ private async readCreationTerminalRequest(
2065
+ operation: ZLinkCreationOperationIdentity,
2066
+ signal?: AbortSignal
1843
2067
  ): Promise<ZLinkCreationTerminalReadResult> {
1844
2068
  validateCreationOperation(operation);
1845
2069
  const result = await this.provider.read(creationTerminalKey(operation), signal);
@@ -1855,6 +2079,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1855
2079
  ownerId: string,
1856
2080
  leaseTtlMs: number,
1857
2081
  signal?: AbortSignal
2082
+ ): Promise<ZLinkOwnerLeaseClaimResult> {
2083
+ return this.observeStore('compare_exchange', () =>
2084
+ this.claimOwnerLeaseRequest(ownerId, leaseTtlMs, signal)
2085
+ );
2086
+ }
2087
+
2088
+ private async claimOwnerLeaseRequest(
2089
+ ownerId: string,
2090
+ leaseTtlMs: number,
2091
+ signal?: AbortSignal
1858
2092
  ): Promise<ZLinkOwnerLeaseClaimResult> {
1859
2093
  requireOwnerInput(ownerId, leaseTtlMs);
1860
2094
  for (;;) {
@@ -1906,17 +2140,25 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1906
2140
  override async readOwnerLease(
1907
2141
  ownerId: string,
1908
2142
  signal?: AbortSignal
2143
+ ): Promise<ZLinkOwnerLeaseReadResult> {
2144
+ return this.observeStore('read', () => this.readOwnerLeaseRequest(ownerId, signal));
2145
+ }
2146
+
2147
+ private async readOwnerLeaseRequest(
2148
+ ownerId: string,
2149
+ signal?: AbortSignal
1909
2150
  ): Promise<ZLinkOwnerLeaseReadResult> {
1910
2151
  const result = await this.provider.read(ownerKey(ownerId), signal);
1911
2152
  if (result.kind === 'missing') return { kind: 'missing' };
2153
+ const leaseExpiresAt = requireOwnerLeaseExpiry(result.value);
1912
2154
  const record = decodeOwnerRecord(result.value.bytes);
1913
- if (record.ownerId !== ownerId || result.value.expiresAt === undefined) {
2155
+ if (record.ownerId !== ownerId) {
1914
2156
  throw new Error('Location Store owner lease record is invalid.');
1915
2157
  }
1916
2158
  return {
1917
2159
  kind: 'found',
1918
2160
  token: { ownerId, leaseGeneration: BigInt(record.leaseGeneration) },
1919
- leaseExpiresAt: result.value.expiresAt,
2161
+ leaseExpiresAt,
1920
2162
  storeNow: result.value.storeNow
1921
2163
  };
1922
2164
  }
@@ -1925,6 +2167,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1925
2167
  token: ZLinkLocationOwnerToken,
1926
2168
  leaseTtlMs: number,
1927
2169
  signal?: AbortSignal
2170
+ ): Promise<ZLinkOwnerLeaseRenewResult> {
2171
+ return this.observeStore('lease_renew', () =>
2172
+ this.renewOwnerLeaseRequest(token, leaseTtlMs, signal)
2173
+ );
2174
+ }
2175
+
2176
+ private async renewOwnerLeaseRequest(
2177
+ token: ZLinkLocationOwnerToken,
2178
+ leaseTtlMs: number,
2179
+ signal?: AbortSignal
1928
2180
  ): Promise<ZLinkOwnerLeaseRenewResult> {
1929
2181
  requireOwnerInput(token.ownerId, leaseTtlMs);
1930
2182
  const key = ownerKey(token.ownerId);
@@ -1957,6 +2209,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1957
2209
  override async releaseOwnerLease(
1958
2210
  token: ZLinkLocationOwnerToken,
1959
2211
  signal?: AbortSignal
2212
+ ): Promise<ZLinkOwnerLeaseReleaseResult> {
2213
+ return this.observeStore('release', () => this.releaseOwnerLeaseRequest(token, signal));
2214
+ }
2215
+
2216
+ private async releaseOwnerLeaseRequest(
2217
+ token: ZLinkLocationOwnerToken,
2218
+ signal?: AbortSignal
1960
2219
  ): Promise<ZLinkOwnerLeaseReleaseResult> {
1961
2220
  const key = ownerKey(token.ownerId);
1962
2221
  const current = await this.provider.read(key, signal);
@@ -1978,6 +2237,19 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1978
2237
  intent: ZLinkLocationWriteIntent,
1979
2238
  signal?: AbortSignal
1980
2239
  ): Promise<ZLinkLocationWriteResult> {
2240
+ return this.observeStore('compare_exchange', () =>
2241
+ this.updateMeshNodeRequest(descriptor, intent, signal)
2242
+ );
2243
+ }
2244
+
2245
+ private async updateMeshNodeRequest(
2246
+ descriptor: ZLinkMeshNodeDescriptor,
2247
+ intent: ZLinkLocationWriteIntent,
2248
+ signal?: AbortSignal
2249
+ ): Promise<ZLinkLocationWriteResult> {
2250
+ const capacityRowKey = capacityKey(descriptor.meshName, String(descriptor.rid));
2251
+ if (!this.confirmedCapacities.has(capacityRowKey.value))
2252
+ this.confirmedCapacities.set(capacityRowKey.value, undefined);
1981
2253
  return this.updateDescriptor(
1982
2254
  meshKey(descriptor.meshName, descriptor.rid),
1983
2255
  descriptor,
@@ -1993,6 +2265,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1993
2265
  key: ZLinkMeshNodeDescriptorKey,
1994
2266
  owner: ZLinkLocationOwnerToken,
1995
2267
  signal?: AbortSignal
2268
+ ): Promise<ZLinkLocationWriteStatus> {
2269
+ return this.observeStore('release', () => this.removeMeshNodeRequest(key, owner, signal));
2270
+ }
2271
+
2272
+ private async removeMeshNodeRequest(
2273
+ key: ZLinkMeshNodeDescriptorKey,
2274
+ owner: ZLinkLocationOwnerToken,
2275
+ signal?: AbortSignal
1996
2276
  ): Promise<ZLinkLocationWriteStatus> {
1997
2277
  const rowKey = meshKey(key.meshName, key.rid);
1998
2278
  const current = await this.provider.read(rowKey, signal);
@@ -2013,6 +2293,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2013
2293
  },
2014
2294
  signal
2015
2295
  );
2296
+ if (result.kind === 'applied')
2297
+ this.confirmedCapacities.delete(capacityKey(key.meshName, String(key.rid)).value);
2016
2298
  return result.kind === 'applied' ? WriteStatus.Stored : WriteStatus.IgnoredStale;
2017
2299
  }
2018
2300
 
@@ -2020,6 +2302,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2020
2302
  meshName: string,
2021
2303
  page: ZLinkPageRequest = {},
2022
2304
  signal?: AbortSignal
2305
+ ): Promise<ZLinkLocationPage<ZLinkMeshNodeDescriptor>> {
2306
+ return this.observeStore('read', () => this.listMeshNodesRequest(meshName, page, signal));
2307
+ }
2308
+
2309
+ private async listMeshNodesRequest(
2310
+ meshName: string,
2311
+ page: ZLinkPageRequest = {},
2312
+ signal?: AbortSignal
2023
2313
  ): Promise<ZLinkLocationPage<ZLinkMeshNodeDescriptor>> {
2024
2314
  const result = await this.provider.scan(
2025
2315
  {
@@ -2050,30 +2340,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2050
2340
  : decodeJson<CapacityRecord>(capacityRead.value.bytes);
2051
2341
  return {
2052
2342
  ...descriptor,
2053
- populationCapacity: {
2054
- actors: {
2055
- ...descriptor.populationCapacity.actors,
2056
- active: capacity.active.actors,
2057
- reserved: capacity.pending.actors
2058
- },
2059
- spots: {
2060
- ...descriptor.populationCapacity.spots,
2061
- active: capacity.active.spots,
2062
- reserved: capacity.pending.spots
2063
- },
2064
- spotTypes: descriptor.populationCapacity.spotTypes.map((value) => {
2065
- const key = capacityTypeKey({
2066
- objectKind: value.objectKind,
2067
- stableType: value.stableType,
2068
- count: 0
2069
- });
2070
- return {
2071
- ...value,
2072
- active: capacity.active.spotTypes[key] ?? 0,
2073
- reserved: capacity.pending.spotTypes[key] ?? 0
2074
- };
2075
- })
2076
- }
2343
+ populationCapacity: projectCapacity(descriptor.populationCapacity, capacity)
2077
2344
  };
2078
2345
  })
2079
2346
  );
@@ -2087,6 +2354,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2087
2354
  descriptor: ZLinkClientServerServerDescriptor,
2088
2355
  intent: ZLinkLocationWriteIntent,
2089
2356
  signal?: AbortSignal
2357
+ ): Promise<ZLinkLocationWriteResult> {
2358
+ return this.observeStore('compare_exchange', () =>
2359
+ this.updateClientServerRequest(descriptor, intent, signal)
2360
+ );
2361
+ }
2362
+
2363
+ private async updateClientServerRequest(
2364
+ descriptor: ZLinkClientServerServerDescriptor,
2365
+ intent: ZLinkLocationWriteIntent,
2366
+ signal?: AbortSignal
2090
2367
  ): Promise<ZLinkLocationWriteResult> {
2091
2368
  validateClientServerDescriptor(descriptor);
2092
2369
  const normalized = reviveClientServerDescriptor(descriptor);
@@ -2105,6 +2382,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2105
2382
  key: ZLinkClientServerServerDescriptorKey,
2106
2383
  owner: ZLinkLocationOwnerToken,
2107
2384
  signal?: AbortSignal
2385
+ ): Promise<ZLinkLocationWriteStatus> {
2386
+ return this.observeStore('release', () => this.removeClientServerRequest(key, owner, signal));
2387
+ }
2388
+
2389
+ private async removeClientServerRequest(
2390
+ key: ZLinkClientServerServerDescriptorKey,
2391
+ owner: ZLinkLocationOwnerToken,
2392
+ signal?: AbortSignal
2108
2393
  ): Promise<ZLinkLocationWriteStatus> {
2109
2394
  return this.removeDescriptor(
2110
2395
  clientServerKey(key.channelName, key.serverRid),
@@ -2118,6 +2403,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2118
2403
  channelName: string,
2119
2404
  page: ZLinkPageRequest = {},
2120
2405
  signal?: AbortSignal
2406
+ ): Promise<ZLinkLocationPage<ZLinkClientServerServerDescriptor>> {
2407
+ return this.observeStore('read', () =>
2408
+ this.listClientServersRequest(channelName, page, signal)
2409
+ );
2410
+ }
2411
+
2412
+ private async listClientServersRequest(
2413
+ channelName: string,
2414
+ page: ZLinkPageRequest = {},
2415
+ signal?: AbortSignal
2121
2416
  ): Promise<ZLinkLocationPage<ZLinkClientServerServerDescriptor>> {
2122
2417
  return this.listDescriptors(
2123
2418
  clientServerPrefix(channelName),
@@ -2131,6 +2426,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2131
2426
  descriptor: ZLinkFanoutPublisherDescriptor,
2132
2427
  intent: ZLinkLocationWriteIntent,
2133
2428
  signal?: AbortSignal
2429
+ ): Promise<ZLinkLocationWriteResult> {
2430
+ return this.observeStore('compare_exchange', () =>
2431
+ this.updateFanoutPublisherRequest(descriptor, intent, signal)
2432
+ );
2433
+ }
2434
+
2435
+ private async updateFanoutPublisherRequest(
2436
+ descriptor: ZLinkFanoutPublisherDescriptor,
2437
+ intent: ZLinkLocationWriteIntent,
2438
+ signal?: AbortSignal
2134
2439
  ): Promise<ZLinkLocationWriteResult> {
2135
2440
  validateFanoutPublisherDescriptor(descriptor);
2136
2441
  const normalized = reviveFanoutDescriptor(descriptor);
@@ -2149,6 +2454,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2149
2454
  key: ZLinkFanoutPublisherDescriptorKey,
2150
2455
  owner: ZLinkLocationOwnerToken,
2151
2456
  signal?: AbortSignal
2457
+ ): Promise<ZLinkLocationWriteStatus> {
2458
+ return this.observeStore('release', () =>
2459
+ this.removeFanoutPublisherRequest(key, owner, signal)
2460
+ );
2461
+ }
2462
+
2463
+ private async removeFanoutPublisherRequest(
2464
+ key: ZLinkFanoutPublisherDescriptorKey,
2465
+ owner: ZLinkLocationOwnerToken,
2466
+ signal?: AbortSignal
2152
2467
  ): Promise<ZLinkLocationWriteStatus> {
2153
2468
  return this.removeDescriptor(
2154
2469
  fanoutKey(key.channelName, key.publisherRid),
@@ -2162,6 +2477,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2162
2477
  channelName: string,
2163
2478
  page: ZLinkPageRequest = {},
2164
2479
  signal?: AbortSignal
2480
+ ): Promise<ZLinkLocationPage<ZLinkFanoutPublisherDescriptor>> {
2481
+ return this.observeStore('read', () =>
2482
+ this.listFanoutPublishersRequest(channelName, page, signal)
2483
+ );
2484
+ }
2485
+
2486
+ private async listFanoutPublishersRequest(
2487
+ channelName: string,
2488
+ page: ZLinkPageRequest = {},
2489
+ signal?: AbortSignal
2165
2490
  ): Promise<ZLinkLocationPage<ZLinkFanoutPublisherDescriptor>> {
2166
2491
  return this.listDescriptors(fanoutPrefix(channelName), page, reviveFanoutDescriptor, signal);
2167
2492
  }
@@ -2170,6 +2495,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2170
2495
  location: ZLinkSpotLocation,
2171
2496
  intent: ZLinkLocationWriteIntent,
2172
2497
  signal?: AbortSignal
2498
+ ): Promise<ZLinkLocationWriteResult> {
2499
+ return this.observeStore('compare_exchange', () =>
2500
+ this.updateSpotRequest(location, intent, signal)
2501
+ );
2502
+ }
2503
+
2504
+ private async updateSpotRequest(
2505
+ location: ZLinkSpotLocation,
2506
+ intent: ZLinkLocationWriteIntent,
2507
+ signal?: AbortSignal
2173
2508
  ): Promise<ZLinkLocationWriteResult> {
2174
2509
  return this.updateOwnedLocation(
2175
2510
  spotKey(location.meshName, String(location.spotId)),
@@ -2183,6 +2518,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2183
2518
  key: ZLinkSpotLocationKey,
2184
2519
  owner: ZLinkLocationOwnerToken,
2185
2520
  signal?: AbortSignal
2521
+ ): Promise<ZLinkLocationWriteStatus> {
2522
+ return this.observeStore('release', () => this.removeSpotRequest(key, owner, signal));
2523
+ }
2524
+
2525
+ private async removeSpotRequest(
2526
+ key: ZLinkSpotLocationKey,
2527
+ owner: ZLinkLocationOwnerToken,
2528
+ signal?: AbortSignal
2186
2529
  ): Promise<ZLinkLocationWriteStatus> {
2187
2530
  return this.removeOwnedLocation(spotKey(key.meshName, String(key.spotId)), owner, signal);
2188
2531
  }
@@ -2190,6 +2533,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2190
2533
  override async resolveSpot(
2191
2534
  key: ZLinkSpotLocationKey,
2192
2535
  signal?: AbortSignal
2536
+ ): Promise<ZLinkSpotLocation | undefined> {
2537
+ return this.observeStore('read', () => this.resolveSpotRequest(key, signal));
2538
+ }
2539
+
2540
+ private async resolveSpotRequest(
2541
+ key: ZLinkSpotLocationKey,
2542
+ signal?: AbortSignal
2193
2543
  ): Promise<ZLinkSpotLocation | undefined> {
2194
2544
  const value = await this.readOwnedLocation<ZLinkSpotLocation>(
2195
2545
  spotKey(key.meshName, String(key.spotId)),
@@ -2202,6 +2552,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2202
2552
  filter: ZLinkSpotLocationFilter,
2203
2553
  page: ZLinkPageRequest = {},
2204
2554
  signal?: AbortSignal
2555
+ ): Promise<ZLinkLocationPage<ZLinkSpotLocation>> {
2556
+ return this.observeStore('read', () => this.listSpotsRequest(filter, page, signal));
2557
+ }
2558
+
2559
+ private async listSpotsRequest(
2560
+ filter: ZLinkSpotLocationFilter,
2561
+ page: ZLinkPageRequest = {},
2562
+ signal?: AbortSignal
2205
2563
  ): Promise<ZLinkLocationPage<ZLinkSpotLocation>> {
2206
2564
  return this.listOwnedLocations(
2207
2565
  `${PREFIX}spot:`,
@@ -2216,6 +2574,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2216
2574
  location: ZLinkActorLocation,
2217
2575
  intent: ZLinkLocationWriteIntent,
2218
2576
  signal?: AbortSignal
2577
+ ): Promise<ZLinkLocationWriteResult> {
2578
+ return this.observeStore('compare_exchange', () =>
2579
+ this.updateActorRequest(location, intent, signal)
2580
+ );
2581
+ }
2582
+
2583
+ private async updateActorRequest(
2584
+ location: ZLinkActorLocation,
2585
+ intent: ZLinkLocationWriteIntent,
2586
+ signal?: AbortSignal
2219
2587
  ): Promise<ZLinkLocationWriteResult> {
2220
2588
  return this.updateOwnedLocation(
2221
2589
  actorKey(location.meshName, location.actorId),
@@ -2230,6 +2598,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2230
2598
  key: ZLinkActorLocationKey,
2231
2599
  owner: ZLinkLocationOwnerToken,
2232
2600
  signal?: AbortSignal
2601
+ ): Promise<ZLinkLocationWriteStatus> {
2602
+ return this.observeStore('release', () => this.removeActorRequest(key, owner, signal));
2603
+ }
2604
+
2605
+ private async removeActorRequest(
2606
+ key: ZLinkActorLocationKey,
2607
+ owner: ZLinkLocationOwnerToken,
2608
+ signal?: AbortSignal
2233
2609
  ): Promise<ZLinkLocationWriteStatus> {
2234
2610
  return this.removeOwnedLocation(actorKey(key.meshName, key.actorId), owner, signal);
2235
2611
  }
@@ -2237,6 +2613,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2237
2613
  override async resolveActor(
2238
2614
  key: ZLinkActorLocationKey,
2239
2615
  signal?: AbortSignal
2616
+ ): Promise<ZLinkActorLocation | undefined> {
2617
+ return this.observeStore('read', () => this.resolveActorRequest(key, signal));
2618
+ }
2619
+
2620
+ private async resolveActorRequest(
2621
+ key: ZLinkActorLocationKey,
2622
+ signal?: AbortSignal
2240
2623
  ): Promise<ZLinkActorLocation | undefined> {
2241
2624
  const value = await this.readOwnedLocation<ZLinkActorLocation>(
2242
2625
  actorKey(key.meshName, key.actorId),
@@ -2249,6 +2632,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2249
2632
  filter: ZLinkActorLocationFilter,
2250
2633
  page: ZLinkPageRequest = {},
2251
2634
  signal?: AbortSignal
2635
+ ): Promise<ZLinkLocationPage<ZLinkActorLocation>> {
2636
+ return this.observeStore('read', () => this.listActorsRequest(filter, page, signal));
2637
+ }
2638
+
2639
+ private async listActorsRequest(
2640
+ filter: ZLinkActorLocationFilter,
2641
+ page: ZLinkPageRequest = {},
2642
+ signal?: AbortSignal
2252
2643
  ): Promise<ZLinkLocationPage<ZLinkActorLocation>> {
2253
2644
  return this.listOwnedLocations(
2254
2645
  `${PREFIX}actor:`,
@@ -2263,6 +2654,16 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2263
2654
  location: ZLinkRouteLocation,
2264
2655
  intent: ZLinkLocationWriteIntent,
2265
2656
  signal?: AbortSignal
2657
+ ): Promise<ZLinkLocationWriteResult> {
2658
+ return this.observeStore('compare_exchange', () =>
2659
+ this.updateRouteRequest(location, intent, signal)
2660
+ );
2661
+ }
2662
+
2663
+ private async updateRouteRequest(
2664
+ location: ZLinkRouteLocation,
2665
+ intent: ZLinkLocationWriteIntent,
2666
+ signal?: AbortSignal
2266
2667
  ): Promise<ZLinkLocationWriteResult> {
2267
2668
  const rowKey = routeKey(String(location.routeKind), location.routeKey);
2268
2669
  const normalized = normalizeRoute(location);
@@ -2324,6 +2725,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2324
2725
  key: ZLinkRouteLocationKey,
2325
2726
  owner: ZLinkLocationOwnerToken,
2326
2727
  signal?: AbortSignal
2728
+ ): Promise<ZLinkLocationWriteResult> {
2729
+ return this.observeStore('release', () => this.removeRouteRequest(key, owner, signal));
2730
+ }
2731
+
2732
+ private async removeRouteRequest(
2733
+ key: ZLinkRouteLocationKey,
2734
+ owner: ZLinkLocationOwnerToken,
2735
+ signal?: AbortSignal
2327
2736
  ): Promise<ZLinkLocationWriteResult> {
2328
2737
  const rowKey = routeKey(String(key.routeKind), key.routeKey);
2329
2738
  const current = await this.provider.read(rowKey, signal);
@@ -2358,6 +2767,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2358
2767
  override async resolveRoute(
2359
2768
  key: ZLinkRouteLocationKey,
2360
2769
  signal?: AbortSignal
2770
+ ): Promise<ZLinkRouteLocation | undefined> {
2771
+ return this.observeStore('read', () => this.resolveRouteRequest(key, signal));
2772
+ }
2773
+
2774
+ private async resolveRouteRequest(
2775
+ key: ZLinkRouteLocationKey,
2776
+ signal?: AbortSignal
2361
2777
  ): Promise<ZLinkRouteLocation | undefined> {
2362
2778
  const value = await this.readOwnedLocation<ZLinkRouteLocation>(
2363
2779
  routeKey(String(key.routeKind), key.routeKey),
@@ -2370,6 +2786,14 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2370
2786
  filter: ZLinkRouteLocationFilter,
2371
2787
  page: ZLinkPageRequest = {},
2372
2788
  signal?: AbortSignal
2789
+ ): Promise<ZLinkLocationPage<ZLinkRouteLocation>> {
2790
+ return this.observeStore('read', () => this.listRoutesRequest(filter, page, signal));
2791
+ }
2792
+
2793
+ private async listRoutesRequest(
2794
+ filter: ZLinkRouteLocationFilter,
2795
+ page: ZLinkPageRequest = {},
2796
+ signal?: AbortSignal
2373
2797
  ): Promise<ZLinkLocationPage<ZLinkRouteLocation>> {
2374
2798
  return this.listOwnedLocations(
2375
2799
  `${PREFIX}route:`,
@@ -2383,6 +2807,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2383
2807
  override async removeAllByOwner(
2384
2808
  owner: ZLinkLocationOwnerToken,
2385
2809
  signal?: AbortSignal
2810
+ ): Promise<bigint> {
2811
+ return this.observeStore('release', () => this.removeAllByOwnerRequest(owner, signal));
2812
+ }
2813
+
2814
+ private async removeAllByOwnerRequest(
2815
+ owner: ZLinkLocationOwnerToken,
2816
+ signal?: AbortSignal
2386
2817
  ): Promise<bigint> {
2387
2818
  const leaseKey = ownerKey(owner.ownerId);
2388
2819
  const lease = await this.provider.read(leaseKey, signal);
@@ -2484,7 +2915,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2484
2915
  const encodedAuthority = authorityContractValueFromPreimage(candidate.key.value);
2485
2916
  const identity = decodeAuthorityKey({ value: encodedAuthority } as ZLinkAuthorityKey);
2486
2917
  if (record.snapshot.allocation.state === 'active') {
2487
- const result = await this.compareExchangeAuthority(
2918
+ const result = await this.compareExchangeAuthorityRequest(
2488
2919
  { value: encodedAuthority } as ZLinkAuthorityKey,
2489
2920
  current.value.version as unknown as ZLinkAuthorityStoreVersion,
2490
2921
  { kind: 'delete' },
@@ -2495,7 +2926,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2495
2926
  }
2496
2927
  const reservationId = record.snapshot.pendingCreation?.reservationId;
2497
2928
  if (reservationId === undefined) continue;
2498
- const aborted = await this.abort(
2929
+ const aborted = await this.abortRequest(
2499
2930
  {
2500
2931
  key: identity,
2501
2932
  reservationId,
@@ -2682,13 +3113,27 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2682
3113
  currentRow.kind === 'found'
2683
3114
  ? { kind: 'version', key: rowKey, expected: currentRow.value.version }
2684
3115
  : { kind: 'missing', key: rowKey };
2685
- const result = await this.provider.write(
2686
- {
2687
- conditions: [leaseValueCondition(leaseKey, { ownerId, leaseGeneration }), rowCondition],
2688
- mutations: [{ kind: 'put', key: rowKey, bytes: encodedDescriptor }]
2689
- },
2690
- signal
2691
- );
3116
+ let result: Awaited<ReturnType<ZLinkLocationStore['write']>>;
3117
+ try {
3118
+ result = await this.provider.write(
3119
+ {
3120
+ conditions: [leaseValueCondition(leaseKey, { ownerId, leaseGeneration }), rowCondition],
3121
+ mutations: [{ kind: 'put', key: rowKey, bytes: encodedDescriptor }]
3122
+ },
3123
+ signal
3124
+ );
3125
+ } catch (failure) {
3126
+ // Location runtime §10: the Store may have applied a write whose result
3127
+ // was lost. Re-read the same key; the same bytes confirm the write.
3128
+ const confirmed = await this.provider.read(rowKey, signal).catch(() => undefined);
3129
+ if (
3130
+ confirmed?.kind === 'found' &&
3131
+ Buffer.from(confirmed.value.bytes).equals(Buffer.from(encodedDescriptor))
3132
+ ) {
3133
+ return { status: WriteStatus.Stored, generation, updatedAt: confirmed.value.storeNow };
3134
+ }
3135
+ throw failure;
3136
+ }
2692
3137
  if (result.kind === 'applied') {
2693
3138
  return { status: WriteStatus.Stored, generation, updatedAt: result.storeNow };
2694
3139
  }
@@ -3737,6 +4182,12 @@ function reviveCanonical(value: unknown, key = ''): unknown {
3737
4182
  return value;
3738
4183
  }
3739
4184
 
4185
+ function requireOwnerLeaseExpiry(value: ZLinkStoreValue): Date {
4186
+ if (value.expiresAt === undefined)
4187
+ throw new Error('Location Store owner lease record is invalid.');
4188
+ return value.expiresAt;
4189
+ }
4190
+
3740
4191
  function decodeOwnerRecord(bytes: Uint8Array): OwnerRecord {
3741
4192
  const record = decodeJson<OwnerRecord>(bytes);
3742
4193
  requireRecordVersion(record, 'owner lease');
@@ -4196,21 +4647,22 @@ function liveTargetDescriptor(
4196
4647
  readonly owner: ZLinkLocationOwnerToken;
4197
4648
  }
4198
4649
  ): ZLinkMeshNodeDescriptor | undefined {
4199
- if (descriptorRead.kind === 'missing' || leaseRead.kind === 'missing') return undefined;
4650
+ if (leaseRead.kind === 'missing') return undefined;
4651
+ const leaseExpiresAt = requireOwnerLeaseExpiry(leaseRead.value);
4652
+ if (descriptorRead.kind === 'missing') return undefined;
4200
4653
  const descriptor = reviveMeshDescriptor(
4201
4654
  decodeCanonicalDescriptorRecord<ZLinkMeshNodeDescriptor>(descriptorRead.value.bytes).descriptor
4202
4655
  );
4203
4656
  const lease = decodeOwnerRecord(leaseRead.value.bytes);
4204
4657
  return descriptor.meshName === target.meshName &&
4205
- String(descriptor.rid) === String(target.nodeRid) &&
4658
+ routingIdsEqual(descriptor.rid, toBackendRoutingId(target.nodeRid)) &&
4206
4659
  descriptor.lifecycleGeneration === target.nodeLifecycleGeneration &&
4207
4660
  descriptor.ownerId === target.owner.ownerId &&
4208
4661
  descriptor.leaseGeneration === target.owner.leaseGeneration &&
4209
4662
  descriptor.objectRole === ZLinkObjectRole.Server &&
4210
4663
  lease.ownerId === target.owner.ownerId &&
4211
4664
  BigInt(lease.leaseGeneration) === target.owner.leaseGeneration &&
4212
- leaseRead.value.expiresAt !== undefined &&
4213
- leaseRead.value.expiresAt.getTime() > leaseRead.value.storeNow.getTime()
4665
+ leaseExpiresAt.getTime() > leaseRead.value.storeNow.getTime()
4214
4666
  ? descriptor
4215
4667
  : undefined;
4216
4668
  }
@@ -4337,7 +4789,7 @@ function sameCreationTarget(
4337
4789
  ): boolean {
4338
4790
  return (
4339
4791
  snapshot.allocation.descriptor.meshName === target.meshName &&
4340
- String(snapshot.allocation.descriptor.rid) === String(target.nodeRid) &&
4792
+ routingIdsEqual(snapshot.allocation.descriptor.rid, target.nodeRid) &&
4341
4793
  snapshot.allocation.descriptorLifecycleGeneration === target.nodeLifecycleGeneration &&
4342
4794
  snapshot.ownerId === target.owner.ownerId &&
4343
4795
  snapshot.ownerLeaseGeneration === target.owner.leaseGeneration
@@ -4360,10 +4812,7 @@ function matchesLiveOwnerLease(
4360
4812
  ownerId: string,
4361
4813
  leaseGeneration: bigint
4362
4814
  ): boolean {
4363
- if (
4364
- lease.value.expiresAt === undefined ||
4365
- lease.value.expiresAt.getTime() <= lease.value.storeNow.getTime()
4366
- ) {
4815
+ if (requireOwnerLeaseExpiry(lease.value).getTime() <= lease.value.storeNow.getTime()) {
4367
4816
  return false;
4368
4817
  }
4369
4818
  const owner = decodeOwnerRecord(lease.value.bytes);
@@ -4381,11 +4830,8 @@ function sameDescriptorPredecessor(
4381
4830
  }
4382
4831
 
4383
4832
  function liveOwnerLeaseGeneration(lease: ZLinkStoreReadResult): bigint | undefined {
4384
- if (
4385
- lease.kind === 'missing' ||
4386
- lease.value.expiresAt === undefined ||
4387
- lease.value.expiresAt.getTime() <= lease.value.storeNow.getTime()
4388
- ) {
4833
+ if (lease.kind === 'missing') return undefined;
4834
+ if (requireOwnerLeaseExpiry(lease.value).getTime() <= lease.value.storeNow.getTime()) {
4389
4835
  return undefined;
4390
4836
  }
4391
4837
  return BigInt(decodeOwnerRecord(lease.value.bytes).leaseGeneration);
@@ -4415,10 +4861,6 @@ function canTakeOverStoredLocation(
4415
4861
  );
4416
4862
  }
4417
4863
 
4418
- function persistMeshDescriptor(descriptor: ZLinkMeshNodeDescriptor): ZLinkMeshNodeDescriptor {
4419
- return descriptor;
4420
- }
4421
-
4422
4864
  function reviveMeshDescriptor(descriptor: ZLinkMeshNodeDescriptor): ZLinkMeshNodeDescriptor {
4423
4865
  const value = descriptor as unknown as Record<string, unknown>;
4424
4866
  if ('routingIdHex' in value) {
@@ -4550,7 +4992,7 @@ function matchesSpot(value: ZLinkSpotLocation, filter: ZLinkSpotLocationFilter):
4550
4992
  return (
4551
4993
  (filter.meshName === undefined || value.meshName === filter.meshName) &&
4552
4994
  (filter.spotType === undefined || value.spotType === filter.spotType) &&
4553
- (filter.nodeRid === undefined || String(value.ownerNodeRid) === String(filter.nodeRid)) &&
4995
+ (filter.nodeRid === undefined || routingIdsEqual(value.ownerNodeRid, filter.nodeRid)) &&
4554
4996
  (filter.spotKind === undefined || value.spotKind === filter.spotKind)
4555
4997
  );
4556
4998
  }
@@ -4558,7 +5000,7 @@ function matchesSpot(value: ZLinkSpotLocation, filter: ZLinkSpotLocationFilter):
4558
5000
  function matchesActor(value: ZLinkActorLocation, filter: ZLinkActorLocationFilter): boolean {
4559
5001
  return (
4560
5002
  (filter.actorType === undefined || value.actorType === filter.actorType) &&
4561
- (filter.nodeRid === undefined || String(value.ownerNodeRid) === String(filter.nodeRid)) &&
5003
+ (filter.nodeRid === undefined || routingIdsEqual(value.ownerNodeRid, filter.nodeRid)) &&
4562
5004
  (filter.spotId === undefined || String(value.spotId) === String(filter.spotId)) &&
4563
5005
  (filter.locationKind === undefined || value.spotKind === filter.locationKind)
4564
5006
  );
@@ -4568,7 +5010,7 @@ function matchesRoute(value: ZLinkRouteLocation, filter: ZLinkRouteLocationFilte
4568
5010
  return (
4569
5011
  (filter.routeKind === undefined || value.routeKind === filter.routeKind) &&
4570
5012
  (filter.ownerNodeRid === undefined ||
4571
- String(value.ownerNodeRid) === String(filter.ownerNodeRid)) &&
5013
+ routingIdsEqual(value.ownerNodeRid, filter.ownerNodeRid)) &&
4572
5014
  (filter.ownerId === undefined || value.ownerId === filter.ownerId)
4573
5015
  );
4574
5016
  }
@@ -4589,13 +5031,14 @@ function descriptorOwnerLeaseGeneration(value: OwnedStoreRecord): bigint | undef
4589
5031
  return undefined;
4590
5032
  }
4591
5033
 
5034
+ // Location runtime §10: a write whose result was lost is confirmed by reading
5035
+ // the same key. The stored row already is this write when it differs only in
5036
+ // the time the Store stamped, as for every other descriptor kind.
4592
5037
  function sameMeshDescriptor(
4593
5038
  left: ZLinkMeshNodeDescriptor,
4594
5039
  right: ZLinkMeshNodeDescriptor
4595
5040
  ): boolean {
4596
- return Buffer.from(encodeJson(persistMeshDescriptor(left))).equals(
4597
- Buffer.from(encodeJson(persistMeshDescriptor(right)))
4598
- );
5041
+ return descriptorFingerprint(left, ['updatedAt']) === descriptorFingerprint(right, ['updatedAt']);
4599
5042
  }
4600
5043
 
4601
5044
  function sameClientServerDescriptor(
@@ -4740,7 +5183,10 @@ function requireOwnerInput(ownerId: string, leaseTtlMs: number): void {
4740
5183
  function throwIfOperationExpired(signal?: AbortSignal, deadlineUnixMs?: bigint): void {
4741
5184
  signal?.throwIfAborted();
4742
5185
  if (deadlineUnixMs !== undefined && Date.now() >= Number(deadlineUnixMs)) {
4743
- throw new DOMException('Operation deadline exceeded.', 'TimeoutError');
5186
+ throw new ZLinkFrameworkException(
5187
+ ZLinkFrameworkErrorKind.DeadlineExceeded,
5188
+ 'Operation deadline exceeded.'
5189
+ );
4744
5190
  }
4745
5191
  }
4746
5192
 
@@ -4759,3 +5205,33 @@ function locationStoreProviderFailure(error: unknown, signal?: AbortSignal): unk
4759
5205
  error
4760
5206
  );
4761
5207
  }
5208
+
5209
+ function projectCapacity(
5210
+ limits: ZLinkMeshNodeDescriptor['populationCapacity'],
5211
+ capacity: CapacityRecord
5212
+ ): ZLinkMeshNodeDescriptor['populationCapacity'] {
5213
+ return {
5214
+ actors: {
5215
+ ...limits.actors,
5216
+ active: capacity.active.actors,
5217
+ reserved: capacity.pending.actors
5218
+ },
5219
+ spots: {
5220
+ ...limits.spots,
5221
+ active: capacity.active.spots,
5222
+ reserved: capacity.pending.spots
5223
+ },
5224
+ spotTypes: limits.spotTypes.map((value) => {
5225
+ const key = capacityTypeKey({
5226
+ objectKind: value.objectKind,
5227
+ stableType: value.stableType,
5228
+ count: 0
5229
+ });
5230
+ return {
5231
+ ...value,
5232
+ active: capacity.active.spotTypes[key] ?? 0,
5233
+ reserved: capacity.pending.spotTypes[key] ?? 0
5234
+ };
5235
+ })
5236
+ };
5237
+ }