@zlink-systems/framework 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +105 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/contracts/Actors/ZLinkActor.d.ts +7 -0
- package/dist/contracts/Actors/ZLinkActor.js +2 -0
- package/dist/contracts/Actors/ZLinkActorClient.d.ts +15 -0
- package/dist/contracts/Actors/ZLinkActorClient.js +2 -0
- package/dist/contracts/Actors/ZLinkActorContext.d.ts +14 -0
- package/dist/contracts/Actors/ZLinkActorContext.js +2 -0
- package/dist/contracts/Actors/ZLinkActorFactory.d.ts +34 -0
- package/dist/contracts/Actors/ZLinkActorFactory.js +2 -0
- package/dist/contracts/Actors/ZLinkActorManager.d.ts +34 -0
- package/dist/contracts/Actors/ZLinkActorManager.js +2 -0
- package/dist/contracts/Actors/index.d.ts +5 -0
- package/dist/contracts/Actors/index.js +21 -0
- package/dist/contracts/Channels/Calls.d.ts +28 -0
- package/dist/contracts/Channels/Calls.js +2 -0
- package/dist/contracts/Channels/IZLinkChannelClient.d.ts +5 -0
- package/dist/contracts/Channels/IZLinkChannelClient.js +2 -0
- package/dist/contracts/Channels/IZLinkFanoutClient.d.ts +11 -0
- package/dist/contracts/Channels/IZLinkFanoutClient.js +2 -0
- package/dist/contracts/Channels/RouteCalls.d.ts +11 -0
- package/dist/contracts/Channels/RouteCalls.js +2 -0
- package/dist/contracts/Channels/index.d.ts +4 -0
- package/dist/contracts/Channels/index.js +20 -0
- package/dist/contracts/Codecs/IZLinkCodecRegistryBuilder.d.ts +14 -0
- package/dist/contracts/Codecs/IZLinkCodecRegistryBuilder.js +2 -0
- package/dist/contracts/Codecs/ZLinkMessageSerializer.d.ts +5 -0
- package/dist/contracts/Codecs/ZLinkMessageSerializer.js +2 -0
- package/dist/contracts/Codecs/index.d.ts +2 -0
- package/dist/contracts/Codecs/index.js +18 -0
- package/dist/contracts/Common/ActorRef.d.ts +7 -0
- package/dist/contracts/Common/ActorRef.js +2 -0
- package/dist/contracts/Common/CoreTypes.d.ts +6 -0
- package/dist/contracts/Common/CoreTypes.js +2 -0
- package/dist/contracts/Common/Message.d.ts +9 -0
- package/dist/contracts/Common/Message.js +2 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +12 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.js +41 -0
- package/dist/contracts/Common/ZLinkMessage.d.ts +25 -0
- package/dist/contracts/Common/ZLinkMessage.js +127 -0
- package/dist/contracts/Common/ZLinkMessageMetadata.d.ts +10 -0
- package/dist/contracts/Common/ZLinkMessageMetadata.js +49 -0
- package/dist/contracts/Common/ZLinkProviderResolver.d.ts +5 -0
- package/dist/contracts/Common/ZLinkProviderResolver.js +2 -0
- package/dist/contracts/Common/encoded-payload-storage.d.ts +3 -0
- package/dist/contracts/Common/encoded-payload-storage.js +18 -0
- package/dist/contracts/Common/index.d.ts +5 -0
- package/dist/contracts/Common/index.js +24 -0
- package/dist/contracts/Configuration/Builders.d.ts +153 -0
- package/dist/contracts/Configuration/Builders.js +2 -0
- package/dist/contracts/Configuration/CodecContentType.d.ts +2 -0
- package/dist/contracts/Configuration/CodecContentType.js +68 -0
- package/dist/contracts/Configuration/CodecSerializerSelection.d.ts +9 -0
- package/dist/contracts/Configuration/CodecSerializerSelection.js +15 -0
- package/dist/contracts/Configuration/Configs.d.ts +23 -0
- package/dist/contracts/Configuration/Configs.js +2 -0
- package/dist/contracts/Configuration/ConfigurationException.d.ts +3 -0
- package/dist/contracts/Configuration/ConfigurationException.js +10 -0
- package/dist/contracts/Configuration/Connections.d.ts +5 -0
- package/dist/contracts/Configuration/Connections.js +2 -0
- package/dist/contracts/Configuration/DiagnosticsValidation.d.ts +3 -0
- package/dist/contracts/Configuration/DiagnosticsValidation.js +23 -0
- package/dist/contracts/Configuration/EndpointNotation.d.ts +77 -0
- package/dist/contracts/Configuration/EndpointNotation.js +218 -0
- package/dist/contracts/Configuration/InternalDefaults.d.ts +7 -0
- package/dist/contracts/Configuration/InternalDefaults.js +47 -0
- package/dist/contracts/Configuration/ObjectRoles.d.ts +38 -0
- package/dist/contracts/Configuration/ObjectRoles.js +13 -0
- package/dist/contracts/Configuration/Registration.d.ts +11 -0
- package/dist/contracts/Configuration/Registration.js +162 -0
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +14 -0
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +61 -0
- package/dist/contracts/Configuration/RegistrationBuilders.d.ts +61 -0
- package/dist/contracts/Configuration/RegistrationBuilders.js +1181 -0
- package/dist/contracts/Configuration/RegistrationCodecRegistry.d.ts +19 -0
- package/dist/contracts/Configuration/RegistrationCodecRegistry.js +79 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +23 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.js +319 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +414 -0
- package/dist/contracts/Configuration/RegistrationTypes.js +2 -0
- package/dist/contracts/Configuration/RegistrationValidators.d.ts +2 -0
- package/dist/contracts/Configuration/RegistrationValidators.js +461 -0
- package/dist/contracts/Configuration/RouteChannelInternalState.d.ts +10 -0
- package/dist/contracts/Configuration/RouteChannelInternalState.js +39 -0
- package/dist/contracts/Configuration/RuntimeEndpointConnections.d.ts +9 -0
- package/dist/contracts/Configuration/RuntimeEndpointConnections.js +80 -0
- package/dist/contracts/Configuration/SendTimeoutValidation.d.ts +2 -0
- package/dist/contracts/Configuration/SendTimeoutValidation.js +16 -0
- package/dist/contracts/Configuration/TimerRegistrationValidator.d.ts +2 -0
- package/dist/contracts/Configuration/TimerRegistrationValidator.js +24 -0
- package/dist/contracts/Configuration/index.d.ts +5 -0
- package/dist/contracts/Configuration/index.js +18 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +181 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.js +89 -0
- package/dist/contracts/Dispatch/ZLinkFlowOrigin.d.ts +1 -0
- package/dist/contracts/Dispatch/ZLinkFlowOrigin.js +2 -0
- package/dist/contracts/Dispatch/index.d.ts +2 -0
- package/dist/contracts/Dispatch/index.js +18 -0
- package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +20 -0
- package/dist/contracts/Errors/ZLinkFrameworkException.js +29 -0
- package/dist/contracts/Errors/index.d.ts +1 -0
- package/dist/contracts/Errors/index.js +6 -0
- package/dist/contracts/Eventing/Metrics.d.ts +29 -0
- package/dist/contracts/Eventing/Metrics.js +4 -0
- package/dist/contracts/Eventing/index.d.ts +1 -0
- package/dist/contracts/Eventing/index.js +17 -0
- package/dist/contracts/Handlers/Attributes.d.ts +24 -0
- package/dist/contracts/Handlers/Attributes.js +84 -0
- package/dist/contracts/Handlers/Contexts.d.ts +18 -0
- package/dist/contracts/Handlers/Contexts.js +2 -0
- package/dist/contracts/Handlers/IZLinkChannelHandlers.d.ts +43 -0
- package/dist/contracts/Handlers/IZLinkChannelHandlers.js +2 -0
- package/dist/contracts/Handlers/IZLinkHandlerFilter.d.ts +15 -0
- package/dist/contracts/Handlers/IZLinkHandlerFilter.js +11 -0
- package/dist/contracts/Handlers/JsonContract.d.ts +46 -0
- package/dist/contracts/Handlers/JsonContract.js +137 -0
- package/dist/contracts/Handlers/ZLinkHandlerFilterNext.d.ts +1 -0
- package/dist/contracts/Handlers/ZLinkHandlerFilterNext.js +2 -0
- package/dist/contracts/Handlers/index.d.ts +5 -0
- package/dist/contracts/Handlers/index.js +33 -0
- package/dist/contracts/Locations/ActorTransfer.d.ts +41 -0
- package/dist/contracts/Locations/ActorTransfer.js +2 -0
- package/dist/contracts/Locations/Authority.d.ts +296 -0
- package/dist/contracts/Locations/Authority.js +17 -0
- package/dist/contracts/Locations/Diagnostics.d.ts +42 -0
- package/dist/contracts/Locations/Diagnostics.js +12 -0
- package/dist/contracts/Locations/Keys.d.ts +86 -0
- package/dist/contracts/Locations/Keys.js +2 -0
- package/dist/contracts/Locations/Models.d.ts +7 -0
- package/dist/contracts/Locations/Models.js +23 -0
- package/dist/contracts/Locations/Options.d.ts +23 -0
- package/dist/contracts/Locations/Options.js +20 -0
- package/dist/contracts/Locations/Readiness.d.ts +5 -0
- package/dist/contracts/Locations/Readiness.js +2 -0
- package/dist/contracts/Locations/RelocationStore.d.ts +38 -0
- package/dist/contracts/Locations/RelocationStore.js +2 -0
- package/dist/contracts/Locations/Resolvers.d.ts +4 -0
- package/dist/contracts/Locations/Resolvers.js +2 -0
- package/dist/contracts/Locations/Rows.d.ts +152 -0
- package/dist/contracts/Locations/Rows.js +19 -0
- package/dist/contracts/Locations/RuntimeQuery.d.ts +25 -0
- package/dist/contracts/Locations/RuntimeQuery.js +2 -0
- package/dist/contracts/Locations/Stores.d.ts +88 -0
- package/dist/contracts/Locations/Stores.js +2 -0
- package/dist/contracts/Locations/Values.d.ts +27 -0
- package/dist/contracts/Locations/Values.js +36 -0
- package/dist/contracts/Locations/Watch.d.ts +14 -0
- package/dist/contracts/Locations/Watch.js +12 -0
- package/dist/contracts/Locations/Writes.d.ts +48 -0
- package/dist/contracts/Locations/Writes.js +15 -0
- package/dist/contracts/Locations/index.d.ts +12 -0
- package/dist/contracts/Locations/index.js +30 -0
- package/dist/contracts/RouteMesh/Contracts.d.ts +102 -0
- package/dist/contracts/RouteMesh/Contracts.js +39 -0
- package/dist/contracts/RouteMesh/RuntimeTopology.d.ts +165 -0
- package/dist/contracts/RouteMesh/RuntimeTopology.js +38 -0
- package/dist/contracts/RouteMesh/index.d.ts +2 -0
- package/dist/contracts/RouteMesh/index.js +18 -0
- package/dist/contracts/Spots/Builders.d.ts +10 -0
- package/dist/contracts/Spots/Builders.js +2 -0
- package/dist/contracts/Spots/Contracts.d.ts +110 -0
- package/dist/contracts/Spots/Contracts.js +9 -0
- package/dist/contracts/Spots/SpotKind.d.ts +8 -0
- package/dist/contracts/Spots/SpotKind.js +28 -0
- package/dist/contracts/Spots/ZLinkSpot.d.ts +62 -0
- package/dist/contracts/Spots/ZLinkSpot.js +15 -0
- package/dist/contracts/Spots/index.d.ts +3 -0
- package/dist/contracts/Spots/index.js +19 -0
- package/dist/contracts/Streams/BoundSessionContracts.d.ts +8 -0
- package/dist/contracts/Streams/BoundSessionContracts.js +2 -0
- package/dist/contracts/Streams/IZLinkSession.d.ts +44 -0
- package/dist/contracts/Streams/IZLinkSession.js +2 -0
- package/dist/contracts/Streams/IZLinkSessionActor.d.ts +15 -0
- package/dist/contracts/Streams/IZLinkSessionActor.js +2 -0
- package/dist/contracts/Streams/IZLinkSessionPacketHandler.d.ts +9 -0
- package/dist/contracts/Streams/IZLinkSessionPacketHandler.js +2 -0
- package/dist/contracts/Streams/IZLinkStream.d.ts +9 -0
- package/dist/contracts/Streams/IZLinkStream.js +2 -0
- package/dist/contracts/Streams/ZLinkMessageMetadata.d.ts +1 -0
- package/dist/contracts/Streams/ZLinkMessageMetadata.js +2 -0
- package/dist/contracts/Streams/ZLinkStreamCompression.d.ts +8 -0
- package/dist/contracts/Streams/ZLinkStreamCompression.js +2 -0
- package/dist/contracts/Streams/ZLinkStreamError.d.ts +8 -0
- package/dist/contracts/Streams/ZLinkStreamError.js +8 -0
- package/dist/contracts/Streams/index.d.ts +8 -0
- package/dist/contracts/Streams/index.js +24 -0
- package/dist/contracts/Timers/IZLinkTimer.d.ts +5 -0
- package/dist/contracts/Timers/IZLinkTimer.js +2 -0
- package/dist/contracts/Timers/ZLinkTimerOptions.d.ts +10 -0
- package/dist/contracts/Timers/ZLinkTimerOptions.js +9 -0
- package/dist/contracts/Timers/ZLinkTimerTick.d.ts +12 -0
- package/dist/contracts/Timers/ZLinkTimerTick.js +2 -0
- package/dist/contracts/Timers/index.d.ts +3 -0
- package/dist/contracts/Timers/index.js +19 -0
- package/dist/contracts/index.d.ts +25 -0
- package/dist/contracts/index.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/internal.d.ts +26 -0
- package/dist/internal.js +48 -0
- package/dist/location-store-integration.d.ts +5 -0
- package/dist/location-store-integration.js +31 -0
- package/dist/nest-integration.d.ts +49 -0
- package/dist/nest-integration.js +143 -0
- package/dist/runtime/abort.d.ts +13 -0
- package/dist/runtime/abort.js +71 -0
- package/dist/runtime/actors/actor-authority-payload-codec.d.ts +33 -0
- package/dist/runtime/actors/actor-authority-payload-codec.js +366 -0
- package/dist/runtime/actors/actor-authority-publication.d.ts +28 -0
- package/dist/runtime/actors/actor-authority-publication.js +207 -0
- package/dist/runtime/actors/actor-client.d.ts +36 -0
- package/dist/runtime/actors/actor-client.js +612 -0
- package/dist/runtime/actors/actor-context.d.ts +26 -0
- package/dist/runtime/actors/actor-context.js +433 -0
- package/dist/runtime/actors/actor-creation.d.ts +19 -0
- package/dist/runtime/actors/actor-creation.js +158 -0
- package/dist/runtime/actors/actor-execution-context.d.ts +6 -0
- package/dist/runtime/actors/actor-execution-context.js +27 -0
- package/dist/runtime/actors/actor-handoff.d.ts +191 -0
- package/dist/runtime/actors/actor-handoff.js +1252 -0
- package/dist/runtime/actors/actor-join-deferred-scope.d.ts +11 -0
- package/dist/runtime/actors/actor-join-deferred-scope.js +144 -0
- package/dist/runtime/actors/actor-join-relocation.d.ts +38 -0
- package/dist/runtime/actors/actor-join-relocation.js +2 -0
- package/dist/runtime/actors/actor-lifecycle-snapshot.d.ts +21 -0
- package/dist/runtime/actors/actor-lifecycle-snapshot.js +58 -0
- package/dist/runtime/actors/actor-local-native-join.d.ts +27 -0
- package/dist/runtime/actors/actor-local-native-join.js +409 -0
- package/dist/runtime/actors/actor-mailbox.d.ts +10 -0
- package/dist/runtime/actors/actor-mailbox.js +53 -0
- package/dist/runtime/actors/actor-message-follow-context.d.ts +46 -0
- package/dist/runtime/actors/actor-message-follow-context.js +257 -0
- package/dist/runtime/actors/actor-native-lookup.d.ts +4 -0
- package/dist/runtime/actors/actor-native-lookup.js +30 -0
- package/dist/runtime/actors/actor-packet-relay-wire.d.ts +73 -0
- package/dist/runtime/actors/actor-packet-relay-wire.js +144 -0
- package/dist/runtime/actors/actor-remote-joiner.d.ts +37 -0
- package/dist/runtime/actors/actor-remote-joiner.js +122 -0
- package/dist/runtime/actors/actor-remote-wire.d.ts +73 -0
- package/dist/runtime/actors/actor-remote-wire.js +84 -0
- package/dist/runtime/actors/actor-retry-delay.d.ts +5 -0
- package/dist/runtime/actors/actor-retry-delay.js +40 -0
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +33 -0
- package/dist/runtime/actors/actor-routed-join-transport.js +2 -0
- package/dist/runtime/actors/actor-routed-json-request.d.ts +11 -0
- package/dist/runtime/actors/actor-routed-json-request.js +39 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +63 -0
- package/dist/runtime/actors/actor-runtime-contracts.js +2 -0
- package/dist/runtime/actors/actor-runtime-state.d.ts +152 -0
- package/dist/runtime/actors/actor-runtime-state.js +545 -0
- package/dist/runtime/actors/actor-source-transfer.d.ts +27 -0
- package/dist/runtime/actors/actor-source-transfer.js +2 -0
- package/dist/runtime/actors/actor-transfer-registry.d.ts +18 -0
- package/dist/runtime/actors/actor-transfer-registry.js +71 -0
- package/dist/runtime/actors/actor-transport-delivery-gate.d.ts +6 -0
- package/dist/runtime/actors/actor-transport-delivery-gate.js +4 -0
- package/dist/runtime/actors/bound-session-wire.d.ts +81 -0
- package/dist/runtime/actors/bound-session-wire.js +184 -0
- package/dist/runtime/actors/deferred-join-accepted-journal.d.ts +47 -0
- package/dist/runtime/actors/deferred-join-accepted-journal.js +962 -0
- package/dist/runtime/actors/index.d.ts +79 -0
- package/dist/runtime/actors/index.js +639 -0
- package/dist/runtime/actors/post-commit-actor-binder.d.ts +18 -0
- package/dist/runtime/actors/post-commit-actor-binder.js +68 -0
- package/dist/runtime/actors/post-commit-actor-location.d.ts +19 -0
- package/dist/runtime/actors/post-commit-actor-location.js +72 -0
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +59 -0
- package/dist/runtime/actors/spot-actor-dispatch.js +179 -0
- package/dist/runtime/actors/transferred-actor-rollback.d.ts +16 -0
- package/dist/runtime/actors/transferred-actor-rollback.js +91 -0
- package/dist/runtime/admission.d.ts +20 -0
- package/dist/runtime/admission.js +166 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +57 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +185 -0
- package/dist/runtime/application-jobs/application-job-queue-handler-start-gate.d.ts +8 -0
- package/dist/runtime/application-jobs/application-job-queue-handler-start-gate.js +8 -0
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +17 -0
- package/dist/runtime/application-jobs/application-job-queue-scope.js +57 -0
- package/dist/runtime/application-jobs/contracts.d.ts +19 -0
- package/dist/runtime/application-jobs/contracts.js +2 -0
- package/dist/runtime/application-jobs/receive-flow-controller.d.ts +32 -0
- package/dist/runtime/application-jobs/receive-flow-controller.js +165 -0
- package/dist/runtime/backend/contracts/index.d.ts +487 -0
- package/dist/runtime/backend/contracts/index.js +29 -0
- package/dist/runtime/backend/index.d.ts +9 -0
- package/dist/runtime/backend/index.js +25 -0
- package/dist/runtime/backend/mesh-actor-session-node-adapter.d.ts +3 -0
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +51 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +34 -0
- package/dist/runtime/backend/mesh-completion-table.js +124 -0
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +48 -0
- package/dist/runtime/backend/mesh-dispatch-pump.js +322 -0
- package/dist/runtime/backend/mesh-record-dispatcher.d.ts +19 -0
- package/dist/runtime/backend/mesh-record-dispatcher.js +53 -0
- package/dist/runtime/backend/node/index.d.ts +4 -0
- package/dist/runtime/backend/node/index.js +22 -0
- package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +12 -0
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +161 -0
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +52 -0
- package/dist/runtime/backend/node/node-backend-adapter-support.js +203 -0
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +13 -0
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +52 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +11 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +23 -0
- package/dist/runtime/backend/node/node-monitor-backend-adapter.d.ts +6 -0
- package/dist/runtime/backend/node/node-monitor-backend-adapter.js +38 -0
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +8 -0
- package/dist/runtime/backend/node/node-raw-binding-port.js +411 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +266 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +1707 -0
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +9 -0
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +286 -0
- package/dist/runtime/backend/node-backend-adapter.d.ts +9 -0
- package/dist/runtime/backend/node-backend-adapter.js +12 -0
- package/dist/runtime/backend/raw-binding-port.d.ts +58 -0
- package/dist/runtime/backend/raw-binding-port.js +2 -0
- package/dist/runtime/backend/runtime-message.d.ts +14 -0
- package/dist/runtime/backend/runtime-message.js +44 -0
- package/dist/runtime/backend/runtime-values.d.ts +78 -0
- package/dist/runtime/backend/runtime-values.js +66 -0
- package/dist/runtime/channels/channel-autoconnect.d.ts +14 -0
- package/dist/runtime/channels/channel-autoconnect.js +23 -0
- package/dist/runtime/channels/channel-clients.d.ts +50 -0
- package/dist/runtime/channels/channel-clients.js +329 -0
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +76 -0
- package/dist/runtime/channels/channel-dispatch-pipeline.js +237 -0
- package/dist/runtime/channels/channel-dispatch-services.d.ts +52 -0
- package/dist/runtime/channels/channel-dispatch-services.js +132 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +125 -0
- package/dist/runtime/channels/channel-dispatchers.js +575 -0
- package/dist/runtime/channels/channel-envelope-inspection.d.ts +25 -0
- package/dist/runtime/channels/channel-envelope-inspection.js +96 -0
- package/dist/runtime/channels/channel-envelope.d.ts +73 -0
- package/dist/runtime/channels/channel-envelope.js +546 -0
- package/dist/runtime/channels/channel-framework-packets.d.ts +2 -0
- package/dist/runtime/channels/channel-framework-packets.js +6 -0
- package/dist/runtime/channels/channel-multipart.d.ts +16 -0
- package/dist/runtime/channels/channel-multipart.js +31 -0
- package/dist/runtime/channels/channel-outbound-operations.d.ts +33 -0
- package/dist/runtime/channels/channel-outbound-operations.js +343 -0
- package/dist/runtime/channels/channel-receive-loops.d.ts +151 -0
- package/dist/runtime/channels/channel-receive-loops.js +728 -0
- package/dist/runtime/channels/channel-receive-task-tracker.d.ts +14 -0
- package/dist/runtime/channels/channel-receive-task-tracker.js +37 -0
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +77 -0
- package/dist/runtime/channels/channel-runtime-lifecycle.js +630 -0
- package/dist/runtime/channels/channel-runtime-manager.d.ts +84 -0
- package/dist/runtime/channels/channel-runtime-manager.js +193 -0
- package/dist/runtime/channels/channel-socket-options.d.ts +18 -0
- package/dist/runtime/channels/channel-socket-options.js +119 -0
- package/dist/runtime/channels/channel-socket-registry.d.ts +157 -0
- package/dist/runtime/channels/channel-socket-registry.js +1428 -0
- package/dist/runtime/channels/channel-transports.d.ts +126 -0
- package/dist/runtime/channels/channel-transports.js +448 -0
- package/dist/runtime/channels/client-server-location-runtime.d.ts +41 -0
- package/dist/runtime/channels/client-server-location-runtime.js +465 -0
- package/dist/runtime/channels/client-server-service-wire.d.ts +48 -0
- package/dist/runtime/channels/client-server-service-wire.js +306 -0
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +22 -0
- package/dist/runtime/channels/dispatch-error-reporter.js +133 -0
- package/dist/runtime/channels/fanout-location-runtime.d.ts +38 -0
- package/dist/runtime/channels/fanout-location-runtime.js +356 -0
- package/dist/runtime/channels/fanout-service-wire.d.ts +12 -0
- package/dist/runtime/channels/fanout-service-wire.js +29 -0
- package/dist/runtime/channels/handler-filter-scope.d.ts +8 -0
- package/dist/runtime/channels/handler-filter-scope.js +11 -0
- package/dist/runtime/channels/index.d.ts +9 -0
- package/dist/runtime/channels/index.js +30 -0
- package/dist/runtime/channels/local-spot-route-transport.d.ts +9 -0
- package/dist/runtime/channels/local-spot-route-transport.js +40 -0
- package/dist/runtime/channels/route-disconnected-error.d.ts +3 -0
- package/dist/runtime/channels/route-disconnected-error.js +10 -0
- package/dist/runtime/channels/route-member-snapshot.d.ts +10 -0
- package/dist/runtime/channels/route-member-snapshot.js +55 -0
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +12 -0
- package/dist/runtime/channels/router-socket-spot-route-transport.js +43 -0
- package/dist/runtime/channels/source-spot-router.d.ts +10 -0
- package/dist/runtime/channels/source-spot-router.js +46 -0
- package/dist/runtime/channels/spot-direct-envelope.d.ts +5 -0
- package/dist/runtime/channels/spot-direct-envelope.js +29 -0
- package/dist/runtime/channels/spot-node-route-transport.d.ts +13 -0
- package/dist/runtime/channels/spot-node-route-transport.js +60 -0
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +14 -0
- package/dist/runtime/channels/spot-route-bridge-transport.js +58 -0
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +50 -0
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +128 -0
- package/dist/runtime/channels/spot-route-target-resolver.d.ts +16 -0
- package/dist/runtime/channels/spot-route-target-resolver.js +92 -0
- package/dist/runtime/codecs/index.d.ts +14 -0
- package/dist/runtime/codecs/index.js +44 -0
- package/dist/runtime/configuration/index.d.ts +1 -0
- package/dist/runtime/configuration/index.js +17 -0
- package/dist/runtime/diagnostics/dispatch-error-port.d.ts +8 -0
- package/dist/runtime/diagnostics/dispatch-error-port.js +2 -0
- package/dist/runtime/diagnostics/flow-context.d.ts +21 -0
- package/dist/runtime/diagnostics/flow-context.js +68 -0
- package/dist/runtime/diagnostics/index.d.ts +82 -0
- package/dist/runtime/diagnostics/index.js +335 -0
- package/dist/runtime/diagnostics/internal-event-contracts.d.ts +139 -0
- package/dist/runtime/diagnostics/internal-event-contracts.js +68 -0
- package/dist/runtime/diagnostics/message-flow.d.ts +55 -0
- package/dist/runtime/diagnostics/message-flow.js +420 -0
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +123 -0
- package/dist/runtime/diagnostics/runtime-metrics.js +454 -0
- package/dist/runtime/diagnostics/runtime-observation-queue.d.ts +58 -0
- package/dist/runtime/diagnostics/runtime-observation-queue.js +324 -0
- package/dist/runtime/diagnostics/topology-runtime-projections.d.ts +33 -0
- package/dist/runtime/diagnostics/topology-runtime-projections.js +255 -0
- package/dist/runtime/execution/index.d.ts +114 -0
- package/dist/runtime/execution/index.js +363 -0
- package/dist/runtime/execution/serial-execution-queue.d.ts +94 -0
- package/dist/runtime/execution/serial-execution-queue.js +411 -0
- package/dist/runtime/execution/serial-work-size.d.ts +4 -0
- package/dist/runtime/execution/serial-work-size.js +28 -0
- package/dist/runtime/execution/state-lane.d.ts +28 -0
- package/dist/runtime/execution/state-lane.js +105 -0
- package/dist/runtime/foundation/actor-join-recovery-codec.d.ts +72 -0
- package/dist/runtime/foundation/actor-join-recovery-codec.js +333 -0
- package/dist/runtime/foundation/event-loop-resources.d.ts +37 -0
- package/dist/runtime/foundation/event-loop-resources.js +163 -0
- package/dist/runtime/foundation/index.d.ts +18 -0
- package/dist/runtime/foundation/index.js +34 -0
- package/dist/runtime/foundation/message-follow-suppression-registry.d.ts +38 -0
- package/dist/runtime/foundation/message-follow-suppression-registry.js +89 -0
- package/dist/runtime/foundation/operation-identity.d.ts +12 -0
- package/dist/runtime/foundation/operation-identity.js +33 -0
- package/dist/runtime/foundation/operation-registry.d.ts +42 -0
- package/dist/runtime/foundation/operation-registry.js +146 -0
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +143 -0
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +1349 -0
- package/dist/runtime/foundation/route-mesh-connection-policy.d.ts +3 -0
- package/dist/runtime/foundation/route-mesh-connection-policy.js +13 -0
- package/dist/runtime/foundation/runtime-state-projections.d.ts +13 -0
- package/dist/runtime/foundation/runtime-state-projections.js +44 -0
- package/dist/runtime/foundation/service-authority-payload-codec.d.ts +56 -0
- package/dist/runtime/foundation/service-authority-payload-codec.js +411 -0
- package/dist/runtime/foundation/service-discovery-registry.d.ts +42 -0
- package/dist/runtime/foundation/service-discovery-registry.js +175 -0
- package/dist/runtime/foundation/service-instance-activation-recovery-codec.d.ts +19 -0
- package/dist/runtime/foundation/service-instance-activation-recovery-codec.js +287 -0
- package/dist/runtime/foundation/service-liveness-registry.d.ts +28 -0
- package/dist/runtime/foundation/service-liveness-registry.js +109 -0
- package/dist/runtime/foundation/service-mailbox.d.ts +60 -0
- package/dist/runtime/foundation/service-mailbox.js +283 -0
- package/dist/runtime/foundation/service-maintenance-runtime.d.ts +45 -0
- package/dist/runtime/foundation/service-maintenance-runtime.js +198 -0
- package/dist/runtime/foundation/service-metadata-codec.d.ts +2 -0
- package/dist/runtime/foundation/service-metadata-codec.js +69 -0
- package/dist/runtime/foundation/service-relocation-aggregate-committer.d.ts +35 -0
- package/dist/runtime/foundation/service-relocation-aggregate-committer.js +139 -0
- package/dist/runtime/foundation/service-relocation-coordinator.d.ts +19 -0
- package/dist/runtime/foundation/service-relocation-coordinator.js +16 -0
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +71 -0
- package/dist/runtime/foundation/service-relocation-object-owner.js +242 -0
- package/dist/runtime/foundation/service-relocation-runtime.d.ts +143 -0
- package/dist/runtime/foundation/service-relocation-runtime.js +1080 -0
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +332 -0
- package/dist/runtime/foundation/service-runtime-contracts.js +66 -0
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +23 -0
- package/dist/runtime/foundation/service-session-binding-ingress-port.js +11 -0
- package/dist/runtime/foundation/service-stateful-registry.d.ts +142 -0
- package/dist/runtime/foundation/service-stateful-registry.js +465 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +393 -0
- package/dist/runtime/foundation/service-stateful-runtime.js +3898 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +597 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.js +2006 -0
- package/dist/runtime/foundation/service-topology-registry.d.ts +68 -0
- package/dist/runtime/foundation/service-topology-registry.js +393 -0
- package/dist/runtime/foundation/service-weighted-selection.d.ts +14 -0
- package/dist/runtime/foundation/service-weighted-selection.js +128 -0
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +10 -0
- package/dist/runtime/foundation/service-wire-binary-primitives.js +53 -0
- package/dist/runtime/foundation/service-wire-codec.d.ts +22 -0
- package/dist/runtime/foundation/service-wire-codec.js +67 -0
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +110 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +132 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +102 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +568 -0
- package/dist/runtime/framework-errors-internal.d.ts +95 -0
- package/dist/runtime/framework-errors-internal.js +301 -0
- package/dist/runtime/handlers/handler-instance-scope.d.ts +11 -0
- package/dist/runtime/handlers/handler-instance-scope.js +284 -0
- package/dist/runtime/handlers/index.d.ts +13 -0
- package/dist/runtime/handlers/index.js +57 -0
- package/dist/runtime/host/actor-packet-relay.d.ts +60 -0
- package/dist/runtime/host/actor-packet-relay.js +664 -0
- package/dist/runtime/host/actor-placement-coordinator.d.ts +32 -0
- package/dist/runtime/host/actor-placement-coordinator.js +386 -0
- package/dist/runtime/host/actor-runtime-options-factory.d.ts +65 -0
- package/dist/runtime/host/actor-runtime-options-factory.js +160 -0
- package/dist/runtime/host/actor-transfer-authority-runtime.d.ts +26 -0
- package/dist/runtime/host/actor-transfer-authority-runtime.js +138 -0
- package/dist/runtime/host/actor-transfer-runtime.d.ts +149 -0
- package/dist/runtime/host/actor-transfer-runtime.js +1245 -0
- package/dist/runtime/host/application-job-queue.d.ts +99 -0
- package/dist/runtime/host/application-job-queue.js +409 -0
- package/dist/runtime/host/bound-session-relay.d.ts +41 -0
- package/dist/runtime/host/bound-session-relay.js +52 -0
- package/dist/runtime/host/bounded-replay-map.d.ts +12 -0
- package/dist/runtime/host/bounded-replay-map.js +42 -0
- package/dist/runtime/host/channel-runtime-options-factory.d.ts +22 -0
- package/dist/runtime/host/channel-runtime-options-factory.js +76 -0
- package/dist/runtime/host/entry-actor-runtime.d.ts +30 -0
- package/dist/runtime/host/entry-actor-runtime.js +80 -0
- package/dist/runtime/host/host-capacity-status.d.ts +11 -0
- package/dist/runtime/host/host-capacity-status.js +85 -0
- package/dist/runtime/host/index.d.ts +188 -0
- package/dist/runtime/host/index.js +2552 -0
- package/dist/runtime/host/instance-activation-authority.d.ts +33 -0
- package/dist/runtime/host/instance-activation-authority.js +496 -0
- package/dist/runtime/host/location-runtime-owner.d.ts +48 -0
- package/dist/runtime/host/location-runtime-owner.js +227 -0
- package/dist/runtime/host/mesh-router-resolver.d.ts +16 -0
- package/dist/runtime/host/mesh-router-resolver.js +108 -0
- package/dist/runtime/host/relocation-direct-transfer.d.ts +81 -0
- package/dist/runtime/host/relocation-direct-transfer.js +240 -0
- package/dist/runtime/host/relocation-integrity-fault-gate.d.ts +6 -0
- package/dist/runtime/host/relocation-integrity-fault-gate.js +5 -0
- package/dist/runtime/host/relocation-state-adapter.d.ts +8 -0
- package/dist/runtime/host/relocation-state-adapter.js +13 -0
- package/dist/runtime/host/remote-actor-join-receiver.d.ts +47 -0
- package/dist/runtime/host/remote-actor-join-receiver.js +161 -0
- package/dist/runtime/host/remote-actor-packet-target-store.d.ts +29 -0
- package/dist/runtime/host/remote-actor-packet-target-store.js +185 -0
- package/dist/runtime/host/remote-bound-session-relay.d.ts +58 -0
- package/dist/runtime/host/remote-bound-session-relay.js +540 -0
- package/dist/runtime/host/route-mesh-runtime-options.d.ts +9 -0
- package/dist/runtime/host/route-mesh-runtime-options.js +41 -0
- package/dist/runtime/host/route-mesh-runtime.d.ts +76 -0
- package/dist/runtime/host/route-mesh-runtime.js +746 -0
- package/dist/runtime/host/runtime-shutdown.d.ts +33 -0
- package/dist/runtime/host/runtime-shutdown.js +68 -0
- package/dist/runtime/host/service-relocation-control.d.ts +7 -0
- package/dist/runtime/host/service-relocation-control.js +24 -0
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +241 -0
- package/dist/runtime/host/service-relocation-host-runtime.js +4113 -0
- package/dist/runtime/host/spot-address-transport.d.ts +47 -0
- package/dist/runtime/host/spot-address-transport.js +601 -0
- package/dist/runtime/host/spot-node-runtime-options-factory.d.ts +38 -0
- package/dist/runtime/host/spot-node-runtime-options-factory.js +36 -0
- package/dist/runtime/host/spot-runtime-options-factory.d.ts +48 -0
- package/dist/runtime/host/spot-runtime-options-factory.js +169 -0
- package/dist/runtime/host/stateful-authority-route-runtime.d.ts +34 -0
- package/dist/runtime/host/stateful-authority-route-runtime.js +440 -0
- package/dist/runtime/host/user-spot-creation-coordinator.d.ts +69 -0
- package/dist/runtime/host/user-spot-creation-coordinator.js +759 -0
- package/dist/runtime/locations/actor-location-claims.d.ts +43 -0
- package/dist/runtime/locations/actor-location-claims.js +319 -0
- package/dist/runtime/locations/actor-session-route-claims.d.ts +10 -0
- package/dist/runtime/locations/actor-session-route-claims.js +51 -0
- package/dist/runtime/locations/aggregate-inventory-store.d.ts +18 -0
- package/dist/runtime/locations/aggregate-inventory-store.js +399 -0
- package/dist/runtime/locations/authority-key-codec.d.ts +6 -0
- package/dist/runtime/locations/authority-key-codec.js +78 -0
- package/dist/runtime/locations/auto-connect-loop.d.ts +37 -0
- package/dist/runtime/locations/auto-connect-loop.js +112 -0
- package/dist/runtime/locations/auto-connect-planner.d.ts +9 -0
- package/dist/runtime/locations/auto-connect-planner.js +137 -0
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +57 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +358 -0
- package/dist/runtime/locations/auto-connect-types.d.ts +42 -0
- package/dist/runtime/locations/auto-connect-types.js +2 -0
- package/dist/runtime/locations/auto-connect.d.ts +4 -0
- package/dist/runtime/locations/auto-connect.js +20 -0
- package/dist/runtime/locations/canonical-codec.d.ts +7 -0
- package/dist/runtime/locations/canonical-codec.js +48 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +34 -0
- package/dist/runtime/locations/domain-store-contract.js +2 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +51 -0
- package/dist/runtime/locations/in-memory-authority-store.js +895 -0
- package/dist/runtime/locations/in-memory-location-store.d.ts +78 -0
- package/dist/runtime/locations/in-memory-location-store.js +1008 -0
- package/dist/runtime/locations/in-memory-provider-location-store.d.ts +19 -0
- package/dist/runtime/locations/in-memory-provider-location-store.js +183 -0
- package/dist/runtime/locations/index.d.ts +11 -0
- package/dist/runtime/locations/index.js +27 -0
- package/dist/runtime/locations/internal-location-contracts.d.ts +7 -0
- package/dist/runtime/locations/internal-location-contracts.js +21 -0
- package/dist/runtime/locations/internal-store-contracts.d.ts +35 -0
- package/dist/runtime/locations/internal-store-contracts.js +2 -0
- package/dist/runtime/locations/key-codec.d.ts +13 -0
- package/dist/runtime/locations/key-codec.js +41 -0
- package/dist/runtime/locations/lease-tracker.d.ts +36 -0
- package/dist/runtime/locations/lease-tracker.js +155 -0
- package/dist/runtime/locations/lifecycle-runtime.d.ts +19 -0
- package/dist/runtime/locations/lifecycle-runtime.js +2 -0
- package/dist/runtime/locations/lifecycle.d.ts +40 -0
- package/dist/runtime/locations/lifecycle.js +120 -0
- package/dist/runtime/locations/location-store-repository.d.ts +72 -0
- package/dist/runtime/locations/location-store-repository.js +3198 -0
- package/dist/runtime/locations/relocation-blob.d.ts +7 -0
- package/dist/runtime/locations/relocation-blob.js +24 -0
- package/dist/runtime/locations/resolvers.d.ts +145 -0
- package/dist/runtime/locations/resolvers.js +693 -0
- package/dist/runtime/locations/runtime.d.ts +144 -0
- package/dist/runtime/locations/runtime.js +792 -0
- package/dist/runtime/locations/spot-location-claims.d.ts +36 -0
- package/dist/runtime/locations/spot-location-claims.js +305 -0
- package/dist/runtime/messaging/creation-payload-codec.d.ts +4 -0
- package/dist/runtime/messaging/creation-payload-codec.js +40 -0
- package/dist/runtime/messaging/framework-json-v1.d.ts +7 -0
- package/dist/runtime/messaging/framework-json-v1.js +296 -0
- package/dist/runtime/messaging/index.d.ts +1 -0
- package/dist/runtime/messaging/index.js +2 -0
- package/dist/runtime/messaging/packet-name.d.ts +4 -0
- package/dist/runtime/messaging/packet-name.js +79 -0
- package/dist/runtime/messaging/payload-codec.d.ts +25 -0
- package/dist/runtime/messaging/payload-codec.js +255 -0
- package/dist/runtime/messaging/submission-result.d.ts +15 -0
- package/dist/runtime/messaging/submission-result.js +37 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +395 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +645 -0
- package/dist/runtime/routing-id.d.ts +8 -0
- package/dist/runtime/routing-id.js +112 -0
- package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +124 -0
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +269 -0
- package/dist/runtime/spots/formal-remote-actor-transfer-registry.d.ts +33 -0
- package/dist/runtime/spots/formal-remote-actor-transfer-registry.js +94 -0
- package/dist/runtime/spots/index.d.ts +247 -0
- package/dist/runtime/spots/index.js +2090 -0
- package/dist/runtime/spots/spot-activation-registry.d.ts +47 -0
- package/dist/runtime/spots/spot-activation-registry.js +272 -0
- package/dist/runtime/spots/spot-activation-state.d.ts +104 -0
- package/dist/runtime/spots/spot-activation-state.js +288 -0
- package/dist/runtime/spots/spot-activation.d.ts +89 -0
- package/dist/runtime/spots/spot-activation.js +601 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +20 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +194 -0
- package/dist/runtime/spots/spot-actor-join-dispatch.d.ts +83 -0
- package/dist/runtime/spots/spot-actor-join-dispatch.js +185 -0
- package/dist/runtime/spots/spot-actor-lifecycle-drain.d.ts +22 -0
- package/dist/runtime/spots/spot-actor-lifecycle-drain.js +51 -0
- package/dist/runtime/spots/spot-actor-membership.d.ts +39 -0
- package/dist/runtime/spots/spot-actor-membership.js +179 -0
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +91 -0
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +315 -0
- package/dist/runtime/spots/spot-actor-packet-drain.d.ts +40 -0
- package/dist/runtime/spots/spot-actor-packet-drain.js +235 -0
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.d.ts +18 -0
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +110 -0
- package/dist/runtime/spots/spot-closing.d.ts +2 -0
- package/dist/runtime/spots/spot-closing.js +30 -0
- package/dist/runtime/spots/spot-context.d.ts +46 -0
- package/dist/runtime/spots/spot-context.js +113 -0
- package/dist/runtime/spots/spot-entry-activation.d.ts +107 -0
- package/dist/runtime/spots/spot-entry-activation.js +342 -0
- package/dist/runtime/spots/spot-handle.d.ts +28 -0
- package/dist/runtime/spots/spot-handle.js +38 -0
- package/dist/runtime/spots/spot-handler-registry.d.ts +52 -0
- package/dist/runtime/spots/spot-handler-registry.js +155 -0
- package/dist/runtime/spots/spot-lifecycle-metrics.d.ts +8 -0
- package/dist/runtime/spots/spot-lifecycle-metrics.js +16 -0
- package/dist/runtime/spots/spot-location-claim.d.ts +25 -0
- package/dist/runtime/spots/spot-location-claim.js +55 -0
- package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +10 -0
- package/dist/runtime/spots/spot-manager-internal-contracts.js +2 -0
- package/dist/runtime/spots/spot-manager-public.d.ts +32 -0
- package/dist/runtime/spots/spot-manager-public.js +219 -0
- package/dist/runtime/spots/spot-native-actor-join-admission.d.ts +26 -0
- package/dist/runtime/spots/spot-native-actor-join-admission.js +105 -0
- package/dist/runtime/spots/spot-native-flags.d.ts +2 -0
- package/dist/runtime/spots/spot-native-flags.js +5 -0
- package/dist/runtime/spots/spot-node-autoconnect.d.ts +23 -0
- package/dist/runtime/spots/spot-node-autoconnect.js +246 -0
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +137 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +1111 -0
- package/dist/runtime/spots/spot-outbound.d.ts +78 -0
- package/dist/runtime/spots/spot-outbound.js +460 -0
- package/dist/runtime/spots/spot-provider.d.ts +4 -0
- package/dist/runtime/spots/spot-provider.js +28 -0
- package/dist/runtime/spots/spot-publisher-transport.d.ts +14 -0
- package/dist/runtime/spots/spot-publisher-transport.js +28 -0
- package/dist/runtime/spots/spot-remote-codec.d.ts +50 -0
- package/dist/runtime/spots/spot-remote-codec.js +190 -0
- package/dist/runtime/spots/spot-remote-route-codec.d.ts +47 -0
- package/dist/runtime/spots/spot-remote-route-codec.js +168 -0
- package/dist/runtime/spots/spot-route-packet-dispatch.d.ts +25 -0
- package/dist/runtime/spots/spot-route-packet-dispatch.js +238 -0
- package/dist/runtime/spots/spot-route-replies.d.ts +17 -0
- package/dist/runtime/spots/spot-route-replies.js +76 -0
- package/dist/runtime/spots/spot-route-reply-wire.d.ts +10 -0
- package/dist/runtime/spots/spot-route-reply-wire.js +6 -0
- package/dist/runtime/spots/spot-routed-actor-admission.d.ts +36 -0
- package/dist/runtime/spots/spot-routed-actor-admission.js +240 -0
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.d.ts +19 -0
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +46 -0
- package/dist/runtime/spots/spot-routed-frame-dispatch.d.ts +54 -0
- package/dist/runtime/spots/spot-routed-frame-dispatch.js +162 -0
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +43 -0
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +152 -0
- package/dist/runtime/spots/spot-routing-internal.d.ts +28 -0
- package/dist/runtime/spots/spot-routing-internal.js +2 -0
- package/dist/runtime/spots/spot-runtime-ports.d.ts +64 -0
- package/dist/runtime/spots/spot-runtime-ports.js +2 -0
- package/dist/runtime/spots/spot-serial-executor.d.ts +38 -0
- package/dist/runtime/spots/spot-serial-executor.js +133 -0
- package/dist/runtime/spots/spot-serial-turn-executor.d.ts +54 -0
- package/dist/runtime/spots/spot-serial-turn-executor.js +219 -0
- package/dist/runtime/spots/spot-subscription-dispatch.d.ts +32 -0
- package/dist/runtime/spots/spot-subscription-dispatch.js +194 -0
- package/dist/runtime/spots/spot-timer.d.ts +110 -0
- package/dist/runtime/spots/spot-timer.js +419 -0
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +193 -0
- package/dist/runtime/streams/actor-session-binding-registry.js +1127 -0
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +20 -0
- package/dist/runtime/streams/actor-session-binding-runtime-owner.js +19 -0
- package/dist/runtime/streams/actor-session-lifecycle-coordinator.d.ts +4 -0
- package/dist/runtime/streams/actor-session-lifecycle-coordinator.js +24 -0
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +29 -0
- package/dist/runtime/streams/bound-actor-relay-sender.js +181 -0
- package/dist/runtime/streams/bound-session-response-target.d.ts +25 -0
- package/dist/runtime/streams/bound-session-response-target.js +54 -0
- package/dist/runtime/streams/bound-session-service.d.ts +48 -0
- package/dist/runtime/streams/bound-session-service.js +201 -0
- package/dist/runtime/streams/index.d.ts +152 -0
- package/dist/runtime/streams/index.js +338 -0
- package/dist/runtime/streams/managed-stream.d.ts +53 -0
- package/dist/runtime/streams/managed-stream.js +329 -0
- package/dist/runtime/streams/native-fallback-bound-session.d.ts +24 -0
- package/dist/runtime/streams/native-fallback-bound-session.js +201 -0
- package/dist/runtime/streams/protocol.d.ts +67 -0
- package/dist/runtime/streams/protocol.js +244 -0
- package/dist/runtime/streams/session-actor-coordinator.d.ts +56 -0
- package/dist/runtime/streams/session-actor-coordinator.js +371 -0
- package/dist/runtime/streams/session-calls.d.ts +59 -0
- package/dist/runtime/streams/session-calls.js +123 -0
- package/dist/runtime/streams/session-context.d.ts +108 -0
- package/dist/runtime/streams/session-context.js +377 -0
- package/dist/runtime/streams/session-local-actors.d.ts +10 -0
- package/dist/runtime/streams/session-local-actors.js +26 -0
- package/dist/runtime/streams/session-provider.d.ts +4 -0
- package/dist/runtime/streams/session-provider.js +30 -0
- package/dist/runtime/streams/session-requests.d.ts +25 -0
- package/dist/runtime/streams/session-requests.js +100 -0
- package/dist/runtime/streams/session-serial-executor.d.ts +12 -0
- package/dist/runtime/streams/session-serial-executor.js +58 -0
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +62 -0
- package/dist/runtime/streams/stream-binding-runtime-ports.js +2 -0
- package/dist/runtime/streams/stream-frame-factory.d.ts +44 -0
- package/dist/runtime/streams/stream-frame-factory.js +149 -0
- package/dist/runtime/streams/stream-message-utils.d.ts +5 -0
- package/dist/runtime/streams/stream-message-utils.js +74 -0
- package/dist/runtime/streams/stream-session-runtime.d.ts +166 -0
- package/dist/runtime/streams/stream-session-runtime.js +1144 -0
- package/dist/runtime/workers/index.d.ts +38 -0
- package/dist/runtime/workers/index.js +474 -0
- package/package.json +19 -0
- package/src/contracts/Actors/ZLinkActor.ts +8 -0
- package/src/contracts/Actors/ZLinkActorClient.ts +18 -0
- package/src/contracts/Actors/ZLinkActorContext.ts +15 -0
- package/src/contracts/Actors/ZLinkActorFactory.ts +41 -0
- package/src/contracts/Actors/ZLinkActorManager.ts +31 -0
- package/src/contracts/Actors/index.ts +5 -0
- package/src/contracts/Channels/Calls.ts +33 -0
- package/src/contracts/Channels/IZLinkChannelClient.ts +6 -0
- package/src/contracts/Channels/IZLinkFanoutClient.ts +13 -0
- package/src/contracts/Channels/RouteCalls.ts +13 -0
- package/src/contracts/Channels/index.ts +4 -0
- package/src/contracts/Codecs/IZLinkCodecRegistryBuilder.ts +22 -0
- package/src/contracts/Codecs/ZLinkMessageSerializer.ts +6 -0
- package/src/contracts/Codecs/index.ts +2 -0
- package/src/contracts/Common/ActorRef.ts +8 -0
- package/src/contracts/Common/CoreTypes.ts +6 -0
- package/src/contracts/Common/Message.ts +9 -0
- package/src/contracts/Common/ZLinkEncodedPayload.ts +47 -0
- package/src/contracts/Common/ZLinkMessage.ts +135 -0
- package/src/contracts/Common/ZLinkMessageMetadata.ts +77 -0
- package/src/contracts/Common/ZLinkProviderResolver.ts +6 -0
- package/src/contracts/Common/encoded-payload-storage.ts +16 -0
- package/src/contracts/Common/index.ts +5 -0
- package/src/contracts/Configuration/Builders.ts +200 -0
- package/src/contracts/Configuration/CodecContentType.ts +68 -0
- package/src/contracts/Configuration/CodecSerializerSelection.ts +28 -0
- package/src/contracts/Configuration/Configs.ts +27 -0
- package/src/contracts/Configuration/ConfigurationException.ts +6 -0
- package/src/contracts/Configuration/Connections.ts +5 -0
- package/src/contracts/Configuration/DiagnosticsValidation.ts +25 -0
- package/src/contracts/Configuration/EndpointNotation.ts +232 -0
- package/src/contracts/Configuration/InternalDefaults.ts +13 -0
- package/src/contracts/Configuration/ObjectRoles.ts +47 -0
- package/src/contracts/Configuration/Registration.ts +191 -0
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +76 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +1705 -0
- package/src/contracts/Configuration/RegistrationCodecRegistry.ts +118 -0
- package/src/contracts/Configuration/RegistrationNormalizers.ts +404 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +499 -0
- package/src/contracts/Configuration/RegistrationValidators.ts +698 -0
- package/src/contracts/Configuration/RouteChannelInternalState.ts +50 -0
- package/src/contracts/Configuration/RuntimeEndpointConnections.ts +91 -0
- package/src/contracts/Configuration/SendTimeoutValidation.ts +15 -0
- package/src/contracts/Configuration/TimerRegistrationValidator.ts +37 -0
- package/src/contracts/Configuration/index.ts +35 -0
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +222 -0
- package/src/contracts/Dispatch/ZLinkFlowOrigin.ts +1 -0
- package/src/contracts/Dispatch/index.ts +2 -0
- package/src/contracts/Errors/ZLinkFrameworkException.ts +27 -0
- package/src/contracts/Errors/index.ts +4 -0
- package/src/contracts/Eventing/Metrics.ts +27 -0
- package/src/contracts/Eventing/index.ts +1 -0
- package/src/contracts/Handlers/Attributes.ts +111 -0
- package/src/contracts/Handlers/Contexts.ts +21 -0
- package/src/contracts/Handlers/IZLinkChannelHandlers.ts +69 -0
- package/src/contracts/Handlers/IZLinkHandlerFilter.ts +22 -0
- package/src/contracts/Handlers/JsonContract.ts +172 -0
- package/src/contracts/Handlers/ZLinkHandlerFilterNext.ts +1 -0
- package/src/contracts/Handlers/index.ts +17 -0
- package/src/contracts/Locations/ActorTransfer.ts +84 -0
- package/src/contracts/Locations/Authority.ts +308 -0
- package/src/contracts/Locations/Diagnostics.ts +48 -0
- package/src/contracts/Locations/Keys.ts +93 -0
- package/src/contracts/Locations/Models.ts +7 -0
- package/src/contracts/Locations/Options.ts +42 -0
- package/src/contracts/Locations/Readiness.ts +11 -0
- package/src/contracts/Locations/RelocationStore.ts +51 -0
- package/src/contracts/Locations/Resolvers.ts +8 -0
- package/src/contracts/Locations/Rows.ts +164 -0
- package/src/contracts/Locations/RuntimeQuery.ts +56 -0
- package/src/contracts/Locations/Stores.ts +92 -0
- package/src/contracts/Locations/Values.ts +32 -0
- package/src/contracts/Locations/Watch.ts +16 -0
- package/src/contracts/Locations/Writes.ts +56 -0
- package/src/contracts/Locations/index.ts +48 -0
- package/src/contracts/RouteMesh/Contracts.ts +145 -0
- package/src/contracts/RouteMesh/RuntimeTopology.ts +201 -0
- package/src/contracts/RouteMesh/index.ts +2 -0
- package/src/contracts/Spots/Builders.ts +11 -0
- package/src/contracts/Spots/Contracts.ts +150 -0
- package/src/contracts/Spots/SpotKind.ts +24 -0
- package/src/contracts/Spots/ZLinkSpot.ts +88 -0
- package/src/contracts/Spots/index.ts +3 -0
- package/src/contracts/Streams/BoundSessionContracts.ts +9 -0
- package/src/contracts/Streams/IZLinkSession.ts +52 -0
- package/src/contracts/Streams/IZLinkSessionActor.ts +17 -0
- package/src/contracts/Streams/IZLinkSessionPacketHandler.ts +11 -0
- package/src/contracts/Streams/IZLinkStream.ts +10 -0
- package/src/contracts/Streams/ZLinkMessageMetadata.ts +1 -0
- package/src/contracts/Streams/ZLinkStreamCompression.ts +9 -0
- package/src/contracts/Streams/ZLinkStreamError.ts +9 -0
- package/src/contracts/Streams/index.ts +8 -0
- package/src/contracts/Timers/IZLinkTimer.ts +5 -0
- package/src/contracts/Timers/ZLinkTimerOptions.ts +11 -0
- package/src/contracts/Timers/ZLinkTimerTick.ts +12 -0
- package/src/contracts/Timers/index.ts +3 -0
- package/src/contracts/index.ts +71 -0
- package/src/index.ts +1 -0
- package/src/internal.ts +29 -0
- package/src/location-store-integration.ts +51 -0
- package/src/nest-integration.ts +251 -0
- package/src/runtime/abort.ts +70 -0
- package/src/runtime/actors/actor-authority-payload-codec.ts +454 -0
- package/src/runtime/actors/actor-authority-publication.ts +277 -0
- package/src/runtime/actors/actor-client.ts +1018 -0
- package/src/runtime/actors/actor-context.ts +583 -0
- package/src/runtime/actors/actor-creation.ts +254 -0
- package/src/runtime/actors/actor-execution-context.ts +38 -0
- package/src/runtime/actors/actor-handoff.ts +1911 -0
- package/src/runtime/actors/actor-join-deferred-scope.ts +183 -0
- package/src/runtime/actors/actor-join-relocation.ts +39 -0
- package/src/runtime/actors/actor-lifecycle-snapshot.ts +90 -0
- package/src/runtime/actors/actor-local-native-join.ts +725 -0
- package/src/runtime/actors/actor-mailbox.ts +92 -0
- package/src/runtime/actors/actor-message-follow-context.ts +347 -0
- package/src/runtime/actors/actor-native-lookup.ts +38 -0
- package/src/runtime/actors/actor-packet-relay-wire.ts +223 -0
- package/src/runtime/actors/actor-remote-joiner.ts +238 -0
- package/src/runtime/actors/actor-remote-wire.ts +169 -0
- package/src/runtime/actors/actor-retry-delay.ts +37 -0
- package/src/runtime/actors/actor-routed-join-transport.ts +58 -0
- package/src/runtime/actors/actor-routed-json-request.ts +44 -0
- package/src/runtime/actors/actor-runtime-contracts.ts +126 -0
- package/src/runtime/actors/actor-runtime-state.ts +805 -0
- package/src/runtime/actors/actor-source-transfer.ts +62 -0
- package/src/runtime/actors/actor-transfer-registry.ts +112 -0
- package/src/runtime/actors/actor-transport-delivery-gate.ts +17 -0
- package/src/runtime/actors/bound-session-wire.ts +257 -0
- package/src/runtime/actors/deferred-join-accepted-journal.ts +1319 -0
- package/src/runtime/actors/index.ts +972 -0
- package/src/runtime/actors/post-commit-actor-binder.ts +70 -0
- package/src/runtime/actors/post-commit-actor-location.ts +114 -0
- package/src/runtime/actors/spot-actor-dispatch.ts +362 -0
- package/src/runtime/actors/transferred-actor-rollback.ts +123 -0
- package/src/runtime/admission.ts +192 -0
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +216 -0
- package/src/runtime/application-jobs/application-job-queue-handler-start-gate.ts +10 -0
- package/src/runtime/application-jobs/application-job-queue-scope.ts +64 -0
- package/src/runtime/application-jobs/contracts.ts +26 -0
- package/src/runtime/application-jobs/receive-flow-controller.ts +188 -0
- package/src/runtime/backend/contracts/index.ts +765 -0
- package/src/runtime/backend/index.ts +9 -0
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +73 -0
- package/src/runtime/backend/mesh-completion-table.ts +165 -0
- package/src/runtime/backend/mesh-dispatch-pump.ts +363 -0
- package/src/runtime/backend/mesh-record-dispatcher.ts +65 -0
- package/src/runtime/backend/node/index.ts +4 -0
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +204 -0
- package/src/runtime/backend/node/node-backend-adapter-support.ts +257 -0
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +133 -0
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +44 -0
- package/src/runtime/backend/node/node-monitor-backend-adapter.ts +39 -0
- package/src/runtime/backend/node/node-raw-binding-port.contract.ts +21 -0
- package/src/runtime/backend/node/node-raw-binding-port.ts +518 -0
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +2669 -0
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +372 -0
- package/src/runtime/backend/node-backend-adapter.ts +19 -0
- package/src/runtime/backend/raw-binding-port.ts +69 -0
- package/src/runtime/backend/runtime-message.ts +51 -0
- package/src/runtime/backend/runtime-values.ts +107 -0
- package/src/runtime/channels/channel-autoconnect.ts +46 -0
- package/src/runtime/channels/channel-clients.ts +527 -0
- package/src/runtime/channels/channel-dispatch-pipeline.ts +408 -0
- package/src/runtime/channels/channel-dispatch-services.ts +234 -0
- package/src/runtime/channels/channel-dispatchers.ts +790 -0
- package/src/runtime/channels/channel-envelope-inspection.ts +125 -0
- package/src/runtime/channels/channel-envelope.ts +743 -0
- package/src/runtime/channels/channel-framework-packets.ts +8 -0
- package/src/runtime/channels/channel-multipart.ts +61 -0
- package/src/runtime/channels/channel-outbound-operations.ts +584 -0
- package/src/runtime/channels/channel-receive-loops.ts +901 -0
- package/src/runtime/channels/channel-receive-task-tracker.ts +39 -0
- package/src/runtime/channels/channel-runtime-lifecycle.ts +969 -0
- package/src/runtime/channels/channel-runtime-manager.ts +444 -0
- package/src/runtime/channels/channel-socket-options.ts +153 -0
- package/src/runtime/channels/channel-socket-registry.ts +1837 -0
- package/src/runtime/channels/channel-transports.ts +1154 -0
- package/src/runtime/channels/client-server-location-runtime.ts +576 -0
- package/src/runtime/channels/client-server-service-wire.ts +390 -0
- package/src/runtime/channels/dispatch-error-reporter.ts +165 -0
- package/src/runtime/channels/fanout-location-runtime.ts +459 -0
- package/src/runtime/channels/fanout-service-wire.ts +43 -0
- package/src/runtime/channels/handler-filter-scope.ts +29 -0
- package/src/runtime/channels/index.ts +45 -0
- package/src/runtime/channels/local-spot-route-transport.ts +59 -0
- package/src/runtime/channels/route-disconnected-error.ts +6 -0
- package/src/runtime/channels/route-member-snapshot.ts +55 -0
- package/src/runtime/channels/router-socket-spot-route-transport.ts +77 -0
- package/src/runtime/channels/source-spot-router.ts +89 -0
- package/src/runtime/channels/spot-direct-envelope.ts +44 -0
- package/src/runtime/channels/spot-node-route-transport.ts +92 -0
- package/src/runtime/channels/spot-route-bridge-transport.ts +75 -0
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +329 -0
- package/src/runtime/channels/spot-route-target-resolver.ts +101 -0
- package/src/runtime/codecs/index.ts +69 -0
- package/src/runtime/configuration/index.ts +1 -0
- package/src/runtime/diagnostics/dispatch-error-port.ts +8 -0
- package/src/runtime/diagnostics/flow-context.ts +84 -0
- package/src/runtime/diagnostics/index.ts +441 -0
- package/src/runtime/diagnostics/internal-event-contracts.ts +135 -0
- package/src/runtime/diagnostics/message-flow.ts +560 -0
- package/src/runtime/diagnostics/runtime-metrics.ts +601 -0
- package/src/runtime/diagnostics/runtime-observation-queue.ts +362 -0
- package/src/runtime/diagnostics/topology-runtime-projections.ts +298 -0
- package/src/runtime/execution/index.ts +466 -0
- package/src/runtime/execution/serial-execution-queue.ts +534 -0
- package/src/runtime/execution/serial-work-size.ts +31 -0
- package/src/runtime/execution/state-lane.ts +116 -0
- package/src/runtime/foundation/actor-join-recovery-codec.ts +465 -0
- package/src/runtime/foundation/event-loop-resources.ts +162 -0
- package/src/runtime/foundation/index.ts +18 -0
- package/src/runtime/foundation/message-follow-suppression-registry.ts +130 -0
- package/src/runtime/foundation/operation-identity.ts +49 -0
- package/src/runtime/foundation/operation-registry.ts +162 -0
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +1771 -0
- package/src/runtime/foundation/route-mesh-connection-policy.ts +28 -0
- package/src/runtime/foundation/runtime-state-projections.ts +68 -0
- package/src/runtime/foundation/service-authority-payload-codec.ts +566 -0
- package/src/runtime/foundation/service-discovery-registry.ts +263 -0
- package/src/runtime/foundation/service-instance-activation-recovery-codec.ts +329 -0
- package/src/runtime/foundation/service-liveness-registry.ts +144 -0
- package/src/runtime/foundation/service-mailbox.ts +369 -0
- package/src/runtime/foundation/service-maintenance-runtime.ts +242 -0
- package/src/runtime/foundation/service-metadata-codec.ts +75 -0
- package/src/runtime/foundation/service-relocation-aggregate-committer.ts +218 -0
- package/src/runtime/foundation/service-relocation-coordinator.ts +37 -0
- package/src/runtime/foundation/service-relocation-object-owner.ts +428 -0
- package/src/runtime/foundation/service-relocation-runtime.ts +1407 -0
- package/src/runtime/foundation/service-runtime-contracts.ts +423 -0
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +45 -0
- package/src/runtime/foundation/service-stateful-registry.ts +664 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +5753 -0
- package/src/runtime/foundation/service-stateful-wire-codec.ts +3008 -0
- package/src/runtime/foundation/service-topology-registry.ts +599 -0
- package/src/runtime/foundation/service-weighted-selection.ts +166 -0
- package/src/runtime/foundation/service-wire-binary-primitives.ts +80 -0
- package/src/runtime/foundation/service-wire-codec.ts +91 -0
- package/src/runtime/foundation/service-wire-constants.generated.ts +131 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +647 -0
- package/src/runtime/framework-errors-internal.ts +338 -0
- package/src/runtime/handlers/handler-instance-scope.ts +372 -0
- package/src/runtime/handlers/index.ts +93 -0
- package/src/runtime/host/actor-packet-relay.ts +1070 -0
- package/src/runtime/host/actor-placement-coordinator.ts +573 -0
- package/src/runtime/host/actor-runtime-options-factory.ts +282 -0
- package/src/runtime/host/actor-transfer-authority-runtime.ts +225 -0
- package/src/runtime/host/actor-transfer-runtime.ts +1958 -0
- package/src/runtime/host/application-job-queue.ts +518 -0
- package/src/runtime/host/bound-session-relay.ts +94 -0
- package/src/runtime/host/bounded-replay-map.ts +42 -0
- package/src/runtime/host/channel-runtime-options-factory.ts +104 -0
- package/src/runtime/host/entry-actor-runtime.ts +115 -0
- package/src/runtime/host/host-capacity-status.ts +103 -0
- package/src/runtime/host/index.ts +3492 -0
- package/src/runtime/host/instance-activation-authority.ts +658 -0
- package/src/runtime/host/location-runtime-owner.ts +313 -0
- package/src/runtime/host/mesh-router-resolver.ts +114 -0
- package/src/runtime/host/relocation-direct-transfer.ts +273 -0
- package/src/runtime/host/relocation-integrity-fault-gate.ts +8 -0
- package/src/runtime/host/relocation-state-adapter.ts +24 -0
- package/src/runtime/host/remote-actor-join-receiver.ts +264 -0
- package/src/runtime/host/remote-actor-packet-target-store.ts +244 -0
- package/src/runtime/host/remote-bound-session-relay.ts +900 -0
- package/src/runtime/host/route-mesh-runtime-options.ts +46 -0
- package/src/runtime/host/route-mesh-runtime.ts +891 -0
- package/src/runtime/host/runtime-shutdown.ts +101 -0
- package/src/runtime/host/service-relocation-control.ts +28 -0
- package/src/runtime/host/service-relocation-host-runtime.ts +5817 -0
- package/src/runtime/host/spot-address-transport.ts +997 -0
- package/src/runtime/host/spot-node-runtime-options-factory.ts +82 -0
- package/src/runtime/host/spot-runtime-options-factory.ts +286 -0
- package/src/runtime/host/stateful-authority-route-runtime.ts +662 -0
- package/src/runtime/host/user-spot-creation-coordinator.ts +1176 -0
- package/src/runtime/locations/actor-location-claims.ts +426 -0
- package/src/runtime/locations/actor-session-route-claims.ts +64 -0
- package/src/runtime/locations/aggregate-inventory-store.ts +581 -0
- package/src/runtime/locations/authority-key-codec.ts +80 -0
- package/src/runtime/locations/auto-connect-loop.ts +131 -0
- package/src/runtime/locations/auto-connect-planner.ts +178 -0
- package/src/runtime/locations/auto-connect-reconciler.ts +425 -0
- package/src/runtime/locations/auto-connect-types.ts +62 -0
- package/src/runtime/locations/auto-connect.ts +4 -0
- package/src/runtime/locations/canonical-codec.ts +54 -0
- package/src/runtime/locations/domain-store-contract.ts +149 -0
- package/src/runtime/locations/in-memory-authority-store.ts +1172 -0
- package/src/runtime/locations/in-memory-location-store.ts +1467 -0
- package/src/runtime/locations/in-memory-provider-location-store.ts +213 -0
- package/src/runtime/locations/index.ts +11 -0
- package/src/runtime/locations/internal-location-contracts.ts +22 -0
- package/src/runtime/locations/internal-store-contracts.ts +90 -0
- package/src/runtime/locations/key-codec.ts +61 -0
- package/src/runtime/locations/lease-tracker.ts +221 -0
- package/src/runtime/locations/lifecycle-runtime.ts +44 -0
- package/src/runtime/locations/lifecycle.ts +272 -0
- package/src/runtime/locations/location-store-repository.ts +4211 -0
- package/src/runtime/locations/relocation-blob.ts +36 -0
- package/src/runtime/locations/resolvers.ts +1054 -0
- package/src/runtime/locations/runtime.ts +1126 -0
- package/src/runtime/locations/spot-location-claims.ts +435 -0
- package/src/runtime/messaging/creation-payload-codec.ts +59 -0
- package/src/runtime/messaging/framework-json-v1.ts +307 -0
- package/src/runtime/messaging/index.ts +1 -0
- package/src/runtime/messaging/packet-name.ts +94 -0
- package/src/runtime/messaging/payload-codec.ts +418 -0
- package/src/runtime/messaging/submission-result.ts +61 -0
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +189 -0
- package/src/runtime/routing-id.ts +125 -0
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +418 -0
- package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +147 -0
- package/src/runtime/spots/index.ts +3303 -0
- package/src/runtime/spots/spot-activation-registry.ts +335 -0
- package/src/runtime/spots/spot-activation-state.ts +414 -0
- package/src/runtime/spots/spot-activation.ts +1027 -0
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +354 -0
- package/src/runtime/spots/spot-actor-join-dispatch.ts +303 -0
- package/src/runtime/spots/spot-actor-lifecycle-drain.ts +79 -0
- package/src/runtime/spots/spot-actor-membership.ts +266 -0
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +593 -0
- package/src/runtime/spots/spot-actor-packet-drain.ts +328 -0
- package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +162 -0
- package/src/runtime/spots/spot-closing.ts +34 -0
- package/src/runtime/spots/spot-context.ts +259 -0
- package/src/runtime/spots/spot-entry-activation.ts +551 -0
- package/src/runtime/spots/spot-handle.ts +101 -0
- package/src/runtime/spots/spot-handler-registry.ts +273 -0
- package/src/runtime/spots/spot-lifecycle-metrics.ts +15 -0
- package/src/runtime/spots/spot-location-claim.ts +86 -0
- package/src/runtime/spots/spot-manager-internal-contracts.ts +11 -0
- package/src/runtime/spots/spot-manager-public.ts +361 -0
- package/src/runtime/spots/spot-native-actor-join-admission.ts +151 -0
- package/src/runtime/spots/spot-native-flags.ts +4 -0
- package/src/runtime/spots/spot-node-autoconnect.ts +325 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +1573 -0
- package/src/runtime/spots/spot-outbound.ts +761 -0
- package/src/runtime/spots/spot-provider.ts +36 -0
- package/src/runtime/spots/spot-publisher-transport.ts +69 -0
- package/src/runtime/spots/spot-remote-codec.ts +308 -0
- package/src/runtime/spots/spot-remote-route-codec.ts +284 -0
- package/src/runtime/spots/spot-route-packet-dispatch.ts +359 -0
- package/src/runtime/spots/spot-route-replies.ts +126 -0
- package/src/runtime/spots/spot-route-reply-wire.ts +15 -0
- package/src/runtime/spots/spot-routed-actor-admission.ts +351 -0
- package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +132 -0
- package/src/runtime/spots/spot-routed-frame-dispatch.ts +250 -0
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +254 -0
- package/src/runtime/spots/spot-routing-internal.ts +33 -0
- package/src/runtime/spots/spot-runtime-ports.ts +196 -0
- package/src/runtime/spots/spot-serial-executor.ts +170 -0
- package/src/runtime/spots/spot-serial-turn-executor.ts +319 -0
- package/src/runtime/spots/spot-subscription-dispatch.ts +252 -0
- package/src/runtime/spots/spot-timer.ts +609 -0
- package/src/runtime/streams/actor-session-binding-registry.ts +1691 -0
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +78 -0
- package/src/runtime/streams/actor-session-lifecycle-coordinator.ts +20 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +275 -0
- package/src/runtime/streams/bound-session-response-target.ts +91 -0
- package/src/runtime/streams/bound-session-service.ts +500 -0
- package/src/runtime/streams/index.ts +706 -0
- package/src/runtime/streams/managed-stream.ts +509 -0
- package/src/runtime/streams/native-fallback-bound-session.ts +321 -0
- package/src/runtime/streams/protocol.ts +291 -0
- package/src/runtime/streams/session-actor-coordinator.ts +605 -0
- package/src/runtime/streams/session-calls.ts +221 -0
- package/src/runtime/streams/session-context.ts +609 -0
- package/src/runtime/streams/session-local-actors.ts +30 -0
- package/src/runtime/streams/session-provider.ts +50 -0
- package/src/runtime/streams/session-requests.ts +109 -0
- package/src/runtime/streams/session-serial-executor.ts +97 -0
- package/src/runtime/streams/stream-binding-runtime-ports.ts +106 -0
- package/src/runtime/streams/stream-frame-factory.ts +255 -0
- package/src/runtime/streams/stream-message-utils.ts +89 -0
- package/src/runtime/streams/stream-session-runtime.ts +1420 -0
- package/src/runtime/workers/index.ts +580 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,3198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZLinkLocationStoreRepository = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const internal_location_contracts_1 = require("./internal-location-contracts");
|
|
6
|
+
const contracts_1 = require("../../contracts");
|
|
7
|
+
const Locations_1 = require("../../contracts/Locations");
|
|
8
|
+
const in_memory_location_store_1 = require("./in-memory-location-store");
|
|
9
|
+
const in_memory_provider_location_store_1 = require("./in-memory-provider-location-store");
|
|
10
|
+
const authority_key_codec_1 = require("./authority-key-codec");
|
|
11
|
+
const aggregate_inventory_store_1 = require("./aggregate-inventory-store");
|
|
12
|
+
const routing_id_1 = require("../routing-id");
|
|
13
|
+
const PREFIX = 'zlink:v11:';
|
|
14
|
+
const OWNER_COUNTER_KEY = (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}owner-counter`);
|
|
15
|
+
// These counters are store-wide fences. They deliberately are not derived
|
|
16
|
+
// from an authority identity: deleting and recreating an authority must never
|
|
17
|
+
// reuse an incarnation or owner-transition generation.
|
|
18
|
+
const OBJECT_COUNTER_KEY = (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}object-counter`);
|
|
19
|
+
const AUTHORITY_OWNER_COUNTER_KEY = (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}authority-owner-counter`);
|
|
20
|
+
const MAX_GENERATION = 0x7fffffffffffffffn;
|
|
21
|
+
const MAX_U64 = 0xffffffffffffffffn;
|
|
22
|
+
const MAX_CREATION_TERMINAL_BYTES = 1024 * 1024;
|
|
23
|
+
const CREATION_TERMINAL_RETENTION_MS = 5 * 60 * 1000;
|
|
24
|
+
const AGGREGATE_COMMIT_RETRY_WINDOW_MS = 5_000;
|
|
25
|
+
const MAX_AGGREGATE_COMMIT_CONFLICT_RETRIES = 64;
|
|
26
|
+
const MAX_OWNER_CLEANUP_BATCH_ROWS = 2_047;
|
|
27
|
+
/**
|
|
28
|
+
* Maps framework domain records to the minimal provider SPI.
|
|
29
|
+
*
|
|
30
|
+
* Owner lease and MeshNode descriptor publication are persisted through the
|
|
31
|
+
* provider. Remaining domain families still use the inherited framework-owned
|
|
32
|
+
* repository while their record codecs are migrated.
|
|
33
|
+
*/
|
|
34
|
+
class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMemoryLocationStore {
|
|
35
|
+
nowProvider;
|
|
36
|
+
provider;
|
|
37
|
+
aggregateInventory;
|
|
38
|
+
constructor(provider, nowProvider = () => new Date()) {
|
|
39
|
+
super(nowProvider);
|
|
40
|
+
this.nowProvider = nowProvider;
|
|
41
|
+
this.provider = new AmbiguousWriteReconcilingLocationStore(provider);
|
|
42
|
+
this.aggregateInventory = new aggregate_inventory_store_1.ZLinkAggregateInventoryStore(this.provider);
|
|
43
|
+
}
|
|
44
|
+
async readAuthority(key, signal) {
|
|
45
|
+
const result = await this.provider.read(authorityKey(key.value), signal);
|
|
46
|
+
if (result.kind === 'missing') {
|
|
47
|
+
return { kind: 'missing', storeNow: result.storeNow };
|
|
48
|
+
}
|
|
49
|
+
return await this.projectAuthority(decodeAuthorityRecord(result.value.bytes), result.value.version, result.value.storeNow, signal);
|
|
50
|
+
}
|
|
51
|
+
async listAuthorities(prefix, cursor, limit, signal) {
|
|
52
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1_000) {
|
|
53
|
+
throw new RangeError('Authority scan limit must be in 1..1000.');
|
|
54
|
+
}
|
|
55
|
+
const scan = await this.provider.scan({
|
|
56
|
+
prefix: AUTHORITY_PREIMAGE_PREFIX,
|
|
57
|
+
cursor: cursor === undefined
|
|
58
|
+
? undefined
|
|
59
|
+
: { value: cursor.encoded },
|
|
60
|
+
limit
|
|
61
|
+
}, signal);
|
|
62
|
+
if (scan.kind === 'expired')
|
|
63
|
+
return { kind: 'scanExpired' };
|
|
64
|
+
const items = [];
|
|
65
|
+
for (const item of scan.value.items) {
|
|
66
|
+
const encodedKey = authorityContractValueFromPreimage(item.key.value);
|
|
67
|
+
if (!encodedKey.startsWith(prefix))
|
|
68
|
+
continue;
|
|
69
|
+
const snapshot = await this.projectAuthority(decodeAuthorityRecord(item.value.bytes), item.value.version, item.value.storeNow, signal);
|
|
70
|
+
items.push({ key: authorityContractKey(encodedKey), snapshot });
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
kind: 'page',
|
|
74
|
+
items,
|
|
75
|
+
nextCursor: scan.value.nextCursor === undefined
|
|
76
|
+
? undefined
|
|
77
|
+
: { encoded: scan.value.nextCursor.value }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async compareExchangeAuthority(key, expectedStoreVersion, mutation, signal) {
|
|
81
|
+
if (mutation.kind !== 'delete') {
|
|
82
|
+
validatePayloadSize(mutation.payload, 'Authority payload');
|
|
83
|
+
}
|
|
84
|
+
const rowKey = authorityKey(requireText(key.value, 'authority key'));
|
|
85
|
+
for (;;) {
|
|
86
|
+
signal?.throwIfAborted();
|
|
87
|
+
const current = await this.provider.read(rowKey, signal);
|
|
88
|
+
if (current.kind === 'missing') {
|
|
89
|
+
return { kind: 'conflict', current: { kind: 'missing', storeNow: current.storeNow } };
|
|
90
|
+
}
|
|
91
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
92
|
+
const snapshot = await this.projectAuthority(record, current.value.version, current.value.storeNow, signal);
|
|
93
|
+
if (record.aggregate !== undefined) {
|
|
94
|
+
return { kind: 'conflict', current: snapshot };
|
|
95
|
+
}
|
|
96
|
+
if (record.snapshot.allocation.state !== 'active'
|
|
97
|
+
|| (record.visibleStoreVersion ?? current.value.version.value)
|
|
98
|
+
!== expectedStoreVersion.value) {
|
|
99
|
+
return { kind: 'conflict', current: snapshot };
|
|
100
|
+
}
|
|
101
|
+
if (mutation.kind === 'rebindOwnerLease') {
|
|
102
|
+
if (mutation.expectedOwner.ownerId !== record.snapshot.ownerId
|
|
103
|
+
|| mutation.expectedOwner.leaseGeneration !== record.snapshot.ownerLeaseGeneration
|
|
104
|
+
|| mutation.targetOwner.ownerId !== record.snapshot.ownerId
|
|
105
|
+
|| mutation.targetOwner.leaseGeneration === record.snapshot.ownerLeaseGeneration) {
|
|
106
|
+
return { kind: 'conflict', current: snapshot };
|
|
107
|
+
}
|
|
108
|
+
const descriptorKey = meshKey(record.snapshot.allocation.descriptor.meshName, record.snapshot.allocation.descriptor.rid);
|
|
109
|
+
const [descriptorRead, targetLeaseRead] = await Promise.all([
|
|
110
|
+
this.provider.read(descriptorKey, signal),
|
|
111
|
+
this.provider.read(ownerKey(mutation.targetOwner.ownerId), signal)
|
|
112
|
+
]);
|
|
113
|
+
if (liveTargetDescriptor(descriptorRead, targetLeaseRead, {
|
|
114
|
+
meshName: record.snapshot.allocation.descriptor.meshName,
|
|
115
|
+
nodeRid: record.snapshot.allocation.descriptor.rid,
|
|
116
|
+
nodeLifecycleGeneration: record.snapshot.allocation.descriptorLifecycleGeneration,
|
|
117
|
+
owner: mutation.targetOwner
|
|
118
|
+
}) === undefined) {
|
|
119
|
+
return { kind: 'conflict', current: snapshot };
|
|
120
|
+
}
|
|
121
|
+
const nextRecord = {
|
|
122
|
+
...record,
|
|
123
|
+
visibleStoreVersion: undefined,
|
|
124
|
+
snapshot: {
|
|
125
|
+
...record.snapshot,
|
|
126
|
+
ownerLeaseGeneration: mutation.targetOwner.leaseGeneration,
|
|
127
|
+
payload: Buffer.from(mutation.payload)
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const result = await this.provider.write({
|
|
131
|
+
conditions: [
|
|
132
|
+
{ kind: 'version', key: rowKey, expected: current.value.version },
|
|
133
|
+
versionCondition(descriptorKey, descriptorRead),
|
|
134
|
+
versionCondition(ownerKey(mutation.targetOwner.ownerId), targetLeaseRead)
|
|
135
|
+
],
|
|
136
|
+
mutations: [{ kind: 'put', key: rowKey, bytes: encodeAuthorityRecord(nextRecord) }]
|
|
137
|
+
}, signal);
|
|
138
|
+
if (result.kind === 'conflict')
|
|
139
|
+
continue;
|
|
140
|
+
const storeVersion = result.putVersions.find(entry => entry.key.value === rowKey.value)?.version;
|
|
141
|
+
if (storeVersion === undefined) {
|
|
142
|
+
throw new Error('Authority lease rebind did not return a row version.');
|
|
143
|
+
}
|
|
144
|
+
const stored = authoritySnapshot(nextRecord.snapshot, storeVersion, result.storeNow);
|
|
145
|
+
const { kind: _kind, ...withoutKind } = stored;
|
|
146
|
+
return { kind: 'stored', ...withoutKind };
|
|
147
|
+
}
|
|
148
|
+
if (mutation.kind === 'restore'
|
|
149
|
+
&& (mutation.expectedOwner.ownerId !== record.snapshot.ownerId
|
|
150
|
+
|| mutation.expectedOwner.leaseGeneration
|
|
151
|
+
!== record.snapshot.ownerLeaseGeneration)) {
|
|
152
|
+
return { kind: 'conflict', current: snapshot };
|
|
153
|
+
}
|
|
154
|
+
const leaseKey = ownerKey(record.snapshot.ownerId);
|
|
155
|
+
const lease = await this.provider.read(leaseKey, signal);
|
|
156
|
+
if (mutation.kind !== 'restore'
|
|
157
|
+
&& !sameLiveOwner(lease, record.snapshot)) {
|
|
158
|
+
return { kind: 'conflict', current: snapshot };
|
|
159
|
+
}
|
|
160
|
+
const capacityRowKey = capacityKey(record.snapshot.allocation.descriptor.meshName, String(record.snapshot.allocation.descriptor.rid));
|
|
161
|
+
const capacityRead = mutation.kind === 'delete'
|
|
162
|
+
? await this.provider.read(capacityRowKey, signal)
|
|
163
|
+
: undefined;
|
|
164
|
+
const nextRecord = mutation.kind === 'delete'
|
|
165
|
+
? undefined
|
|
166
|
+
: {
|
|
167
|
+
...record,
|
|
168
|
+
visibleStoreVersion: undefined,
|
|
169
|
+
snapshot: {
|
|
170
|
+
...record.snapshot,
|
|
171
|
+
payload: Buffer.from(mutation.payload)
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const result = await this.provider.write({
|
|
175
|
+
conditions: [
|
|
176
|
+
{ kind: 'version', key: rowKey, expected: current.value.version },
|
|
177
|
+
...(mutation.kind === 'restore'
|
|
178
|
+
? []
|
|
179
|
+
: [versionCondition(leaseKey, lease)]),
|
|
180
|
+
...(capacityRead === undefined
|
|
181
|
+
? []
|
|
182
|
+
: [conditionFor(capacityRowKey, capacityRead)])
|
|
183
|
+
],
|
|
184
|
+
mutations: mutation.kind === 'delete'
|
|
185
|
+
? [
|
|
186
|
+
{ kind: 'delete', key: rowKey },
|
|
187
|
+
{
|
|
188
|
+
kind: 'put',
|
|
189
|
+
key: capacityRowKey,
|
|
190
|
+
bytes: encodeJson({
|
|
191
|
+
active: subtractCapacity(capacityRead?.kind === 'found'
|
|
192
|
+
? decodeJson(capacityRead.value.bytes).active
|
|
193
|
+
: emptyCapacityRecord().active, record.snapshot.allocation.capacity),
|
|
194
|
+
pending: capacityRead?.kind === 'found'
|
|
195
|
+
? decodeJson(capacityRead.value.bytes).pending
|
|
196
|
+
: emptyCapacityRecord().pending
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
: [{ kind: 'put', key: rowKey, bytes: encodeAuthorityRecord(nextRecord) }]
|
|
201
|
+
}, signal);
|
|
202
|
+
if (result.kind === 'conflict')
|
|
203
|
+
continue;
|
|
204
|
+
const storeVersion = result.putVersions.find(entry => entry.key.value === rowKey.value)?.version;
|
|
205
|
+
if (mutation.kind === 'delete') {
|
|
206
|
+
return {
|
|
207
|
+
kind: 'deleted',
|
|
208
|
+
storeVersion: {
|
|
209
|
+
value: (storeVersion ?? current.value.version).value
|
|
210
|
+
},
|
|
211
|
+
storeNow: result.storeNow
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (storeVersion === undefined) {
|
|
215
|
+
throw new Error('Authority compare-exchange did not return a row version.');
|
|
216
|
+
}
|
|
217
|
+
const stored = authoritySnapshot(nextRecord.snapshot, storeVersion, result.storeNow);
|
|
218
|
+
const { kind: _kind, ...withoutKind } = stored;
|
|
219
|
+
return { kind: 'stored', ...withoutKind };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async prepareAggregate(request, signal) {
|
|
223
|
+
validateProviderAggregateRequest(request);
|
|
224
|
+
const fence = aggregateFence(request.aggregateId, request.aggregateGeneration);
|
|
225
|
+
const rowKey = aggregateKey(fence);
|
|
226
|
+
const fingerprint = sha256Hex(encodeJson(request));
|
|
227
|
+
let aggregateRead = await this.provider.read(rowKey, signal);
|
|
228
|
+
if (aggregateRead.kind === 'found') {
|
|
229
|
+
const existing = decodeJson(aggregateRead.value.bytes);
|
|
230
|
+
const reconciled = reconcileAggregatePrepare(existing, fingerprint, fence);
|
|
231
|
+
if (reconciled !== undefined) {
|
|
232
|
+
if (reconciled.kind === 'alreadyPrepared') {
|
|
233
|
+
await this.aggregateInventory.read(fence, request.inventoryDigest, signal);
|
|
234
|
+
}
|
|
235
|
+
return reconciled;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const staging = aggregateRecord(request, fingerprint, 'staging');
|
|
240
|
+
const claimed = await this.provider.write({
|
|
241
|
+
conditions: [{ kind: 'missing', key: rowKey }],
|
|
242
|
+
mutations: [{ kind: 'put', key: rowKey, bytes: encodeJson(staging) }]
|
|
243
|
+
}, signal);
|
|
244
|
+
if (claimed.kind === 'conflict') {
|
|
245
|
+
aggregateRead = await this.provider.read(rowKey, signal);
|
|
246
|
+
if (aggregateRead.kind === 'missing')
|
|
247
|
+
return { kind: 'conflict' };
|
|
248
|
+
const existing = decodeJson(aggregateRead.value.bytes);
|
|
249
|
+
const reconciled = reconcileAggregatePrepare(existing, fingerprint, fence);
|
|
250
|
+
if (reconciled !== undefined)
|
|
251
|
+
return reconciled;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const version = claimed.putVersions.find(value => value.key.value === rowKey.value)?.version;
|
|
255
|
+
if (version === undefined) {
|
|
256
|
+
throw new Error('Aggregate staging did not return a Store version.');
|
|
257
|
+
}
|
|
258
|
+
aggregateRead = {
|
|
259
|
+
kind: 'found',
|
|
260
|
+
value: {
|
|
261
|
+
bytes: encodeJson(staging),
|
|
262
|
+
version,
|
|
263
|
+
storeNow: claimed.storeNow
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const aggregate = decodeJson(aggregateRead.value.bytes);
|
|
269
|
+
if (aggregate.state !== 'staging'
|
|
270
|
+
|| aggregate.requestFingerprint !== fingerprint) {
|
|
271
|
+
return { kind: 'conflict' };
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
await this.aggregateInventory.store(request, signal);
|
|
275
|
+
const entries = await this.aggregateInventory.read(fence, request.inventoryDigest, signal);
|
|
276
|
+
if (entries.length !== request.participants.length) {
|
|
277
|
+
throw new Error('Aggregate inventory count changed after staging.');
|
|
278
|
+
}
|
|
279
|
+
await parallelForEach(request.participants, 64, async (participant, index) => {
|
|
280
|
+
const entry = entries[index];
|
|
281
|
+
if (entry.authorityKey !== participant.authorityKey.value
|
|
282
|
+
|| entry.expectedStoreVersion !== participant.expectedStoreVersion.value
|
|
283
|
+
|| entry.ownerTransition !== participant.ownerTransition) {
|
|
284
|
+
throw new Error('Aggregate inventory differs from its participant request.');
|
|
285
|
+
}
|
|
286
|
+
await this.putImmutable(aggregateParticipantPayloadKey(fence, index), participant.authorityPayload, signal);
|
|
287
|
+
await this.putImmutable(aggregateParticipantMembershipKey(fence, index), participant.membershipMutation, signal);
|
|
288
|
+
});
|
|
289
|
+
const targetDescriptorKey = meshKey(request.targetDescriptor.meshName, request.targetDescriptor.rid);
|
|
290
|
+
const targetLeaseKey = ownerKey(request.targetOwner.ownerId);
|
|
291
|
+
const targetCapacityKey = capacityKey(request.targetDescriptor.meshName, request.targetDescriptor.rid);
|
|
292
|
+
const [descriptorRead, leaseRead, targetCapacityRead] = await Promise.all([
|
|
293
|
+
this.provider.read(targetDescriptorKey, signal),
|
|
294
|
+
this.provider.read(targetLeaseKey, signal),
|
|
295
|
+
this.provider.read(targetCapacityKey, signal)
|
|
296
|
+
]);
|
|
297
|
+
const descriptor = liveTargetDescriptor(descriptorRead, leaseRead, aggregateTarget(request));
|
|
298
|
+
const targetCapacity = targetCapacityRead.kind === 'missing'
|
|
299
|
+
? emptyCapacityRecord()
|
|
300
|
+
: decodeJson(targetCapacityRead.value.bytes);
|
|
301
|
+
if (descriptor === undefined
|
|
302
|
+
|| !capacityAvailable(descriptor, request.capacity, targetCapacity)) {
|
|
303
|
+
await this.abortAggregateStaging(fence, signal);
|
|
304
|
+
return { kind: 'conflict' };
|
|
305
|
+
}
|
|
306
|
+
let sourceCapacity = { actors: 0, spots: 0 };
|
|
307
|
+
let ownerGenerationStart = aggregate.ownerGenerationStart;
|
|
308
|
+
let ownerGenerationEnd = aggregate.ownerGenerationEnd;
|
|
309
|
+
let ownerGenerationCounterRead;
|
|
310
|
+
if (ownerGenerationStart === undefined || ownerGenerationEnd === undefined) {
|
|
311
|
+
ownerGenerationCounterRead = await this.provider.read(AUTHORITY_OWNER_COUNTER_KEY, signal);
|
|
312
|
+
ownerGenerationStart = counterNextValue(ownerGenerationCounterRead);
|
|
313
|
+
ownerGenerationEnd = ownerGenerationStart + BigInt(request.participants.length) - 1n;
|
|
314
|
+
if (ownerGenerationEnd >= MAX_GENERATION) {
|
|
315
|
+
await this.abortAggregateStaging(fence, signal);
|
|
316
|
+
return { kind: 'generationExhausted' };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else if (ownerGenerationEnd !== ownerGenerationStart + BigInt(request.participants.length) - 1n) {
|
|
320
|
+
await this.abortAggregateStaging(fence, signal);
|
|
321
|
+
return { kind: 'conflict' };
|
|
322
|
+
}
|
|
323
|
+
const installed = [];
|
|
324
|
+
for (let index = 0; index < request.participants.length; index++) {
|
|
325
|
+
const participant = request.participants[index];
|
|
326
|
+
const entry = entries[index];
|
|
327
|
+
const authorityRowKey = authorityKey(participant.authorityKey.value);
|
|
328
|
+
const current = await this.provider.read(authorityRowKey, signal);
|
|
329
|
+
if (current.kind === 'missing') {
|
|
330
|
+
await this.abortAggregateStaging(fence, signal);
|
|
331
|
+
return { kind: 'conflict' };
|
|
332
|
+
}
|
|
333
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
334
|
+
if (sameAggregateMarker(record.aggregate, fence, index, participant)) {
|
|
335
|
+
if (ownerGenerationCounterRead !== undefined) {
|
|
336
|
+
const reservedAggregate = await this.provider.read(rowKey, signal);
|
|
337
|
+
if (reservedAggregate.kind === 'missing')
|
|
338
|
+
return { kind: 'conflict' };
|
|
339
|
+
const reservedRecord = decodeJson(reservedAggregate.value.bytes);
|
|
340
|
+
if (reservedRecord.ownerGenerationStart === undefined
|
|
341
|
+
|| reservedRecord.ownerGenerationEnd === undefined) {
|
|
342
|
+
return { kind: 'conflict' };
|
|
343
|
+
}
|
|
344
|
+
ownerGenerationStart = reservedRecord.ownerGenerationStart;
|
|
345
|
+
ownerGenerationEnd = reservedRecord.ownerGenerationEnd;
|
|
346
|
+
ownerGenerationCounterRead = undefined;
|
|
347
|
+
aggregateRead = reservedAggregate;
|
|
348
|
+
}
|
|
349
|
+
installed.push({
|
|
350
|
+
key: authorityRowKey,
|
|
351
|
+
expectedStoreVersion: participant.expectedStoreVersion.value
|
|
352
|
+
});
|
|
353
|
+
if (participant.ownerTransition === 'newOwner') {
|
|
354
|
+
sourceCapacity = addCapacityVector(sourceCapacity, record.snapshot.allocation.capacity);
|
|
355
|
+
}
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (record.aggregate !== undefined
|
|
359
|
+
|| record.snapshot.allocation.state !== 'active'
|
|
360
|
+
|| (record.visibleStoreVersion ?? current.value.version.value)
|
|
361
|
+
!== participant.expectedStoreVersion.value) {
|
|
362
|
+
await this.clearAggregateMarkers(fence, installed, signal);
|
|
363
|
+
await this.abortAggregateStaging(fence, signal);
|
|
364
|
+
return { kind: 'conflict' };
|
|
365
|
+
}
|
|
366
|
+
if (participant.ownerTransition === 'preserve') {
|
|
367
|
+
const lease = await this.provider.read(ownerKey(record.snapshot.ownerId), signal);
|
|
368
|
+
if (!sameLiveOwner(lease, record.snapshot)) {
|
|
369
|
+
await this.clearAggregateMarkers(fence, installed, signal);
|
|
370
|
+
await this.abortAggregateStaging(fence, signal);
|
|
371
|
+
return { kind: 'conflict' };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
sourceCapacity = addCapacityVector(sourceCapacity, record.snapshot.allocation.capacity);
|
|
376
|
+
}
|
|
377
|
+
const targetAuthorityOwnerGeneration = participant.ownerTransition === 'newOwner'
|
|
378
|
+
? ownerGenerationStart + BigInt(index)
|
|
379
|
+
: record.snapshot.authorityOwnerGeneration;
|
|
380
|
+
const marker = {
|
|
381
|
+
aggregateId: fence.aggregateId.value,
|
|
382
|
+
aggregateGeneration: fence.aggregateGeneration,
|
|
383
|
+
index,
|
|
384
|
+
expectedStoreVersion: participant.expectedStoreVersion.value,
|
|
385
|
+
ownerTransition: participant.ownerTransition,
|
|
386
|
+
targetAuthorityOwnerGeneration,
|
|
387
|
+
authorityPayloadSha256: entry.authorityPayloadSha256,
|
|
388
|
+
membershipMutationSha256: entry.membershipMutationSha256
|
|
389
|
+
};
|
|
390
|
+
const reservingOwnerGenerationBlock = ownerGenerationCounterRead !== undefined;
|
|
391
|
+
const stored = await this.provider.write({
|
|
392
|
+
conditions: [
|
|
393
|
+
{ kind: 'version', key: authorityRowKey, expected: current.value.version },
|
|
394
|
+
...(reservingOwnerGenerationBlock
|
|
395
|
+
? [conditionFor(AUTHORITY_OWNER_COUNTER_KEY, ownerGenerationCounterRead)]
|
|
396
|
+
: []),
|
|
397
|
+
...(reservingOwnerGenerationBlock
|
|
398
|
+
? [{ kind: 'version', key: rowKey, expected: aggregateRead.value.version }]
|
|
399
|
+
: [])
|
|
400
|
+
],
|
|
401
|
+
mutations: [
|
|
402
|
+
{
|
|
403
|
+
kind: 'put', key: authorityRowKey, bytes: encodeAuthorityRecord({
|
|
404
|
+
...record,
|
|
405
|
+
aggregate: marker,
|
|
406
|
+
visibleStoreVersion: record.visibleStoreVersion ?? current.value.version.value
|
|
407
|
+
})
|
|
408
|
+
},
|
|
409
|
+
...(reservingOwnerGenerationBlock
|
|
410
|
+
? [
|
|
411
|
+
{
|
|
412
|
+
kind: 'put',
|
|
413
|
+
key: AUTHORITY_OWNER_COUNTER_KEY,
|
|
414
|
+
bytes: encodeText((ownerGenerationEnd + 1n).toString())
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
kind: 'put',
|
|
418
|
+
key: rowKey,
|
|
419
|
+
bytes: encodeJson({
|
|
420
|
+
...aggregate,
|
|
421
|
+
ownerGenerationStart,
|
|
422
|
+
ownerGenerationEnd
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
: [])
|
|
427
|
+
]
|
|
428
|
+
}, signal);
|
|
429
|
+
if (stored.kind === 'conflict') {
|
|
430
|
+
const raced = await this.provider.read(authorityRowKey, signal);
|
|
431
|
+
if (raced.kind === 'found' && sameAggregateMarker(decodeAuthorityRecord(raced.value.bytes).aggregate, fence, index, participant)) {
|
|
432
|
+
if (reservingOwnerGenerationBlock) {
|
|
433
|
+
const reservedAggregate = await this.provider.read(rowKey, signal);
|
|
434
|
+
if (reservedAggregate.kind === 'missing')
|
|
435
|
+
return { kind: 'conflict' };
|
|
436
|
+
const reservedRecord = decodeJson(reservedAggregate.value.bytes);
|
|
437
|
+
if (reservedRecord.ownerGenerationStart === undefined
|
|
438
|
+
|| reservedRecord.ownerGenerationEnd === undefined) {
|
|
439
|
+
return { kind: 'conflict' };
|
|
440
|
+
}
|
|
441
|
+
ownerGenerationStart = reservedRecord.ownerGenerationStart;
|
|
442
|
+
ownerGenerationEnd = reservedRecord.ownerGenerationEnd;
|
|
443
|
+
ownerGenerationCounterRead = undefined;
|
|
444
|
+
aggregateRead = reservedAggregate;
|
|
445
|
+
}
|
|
446
|
+
installed.push({ key: authorityRowKey, expectedStoreVersion: participant.expectedStoreVersion.value });
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
// A peer can be in the middle of publishing this same aggregate.
|
|
450
|
+
// Do not abort its shared staging row merely because this attempt
|
|
451
|
+
// lost a marker CAS; re-enter and adopt its terminal state.
|
|
452
|
+
const aggregateRaced = await this.provider.read(rowKey, signal);
|
|
453
|
+
if (aggregateRaced.kind === 'found') {
|
|
454
|
+
const aggregateRecord = decodeJson(aggregateRaced.value.bytes);
|
|
455
|
+
if (aggregateRecord.requestFingerprint === fingerprint
|
|
456
|
+
&& (aggregateRecord.state === 'staging' || aggregateRecord.state === 'prepared')) {
|
|
457
|
+
return await this.prepareAggregate(request, signal);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
await this.clearAggregateMarkers(fence, installed, signal);
|
|
461
|
+
await this.abortAggregateStaging(fence, signal);
|
|
462
|
+
return { kind: 'conflict' };
|
|
463
|
+
}
|
|
464
|
+
if (reservingOwnerGenerationBlock) {
|
|
465
|
+
ownerGenerationCounterRead = undefined;
|
|
466
|
+
}
|
|
467
|
+
installed.push({
|
|
468
|
+
key: authorityRowKey,
|
|
469
|
+
expectedStoreVersion: participant.expectedStoreVersion.value
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (!sameCapacityVector(sourceCapacity, request.capacity)) {
|
|
473
|
+
await this.clearAggregateMarkers(fence, installed, signal);
|
|
474
|
+
await this.abortAggregateStaging(fence, signal);
|
|
475
|
+
return { kind: 'conflict' };
|
|
476
|
+
}
|
|
477
|
+
aggregateRead = await this.provider.read(rowKey, signal);
|
|
478
|
+
if (aggregateRead.kind === 'missing')
|
|
479
|
+
return { kind: 'conflict' };
|
|
480
|
+
const latest = decodeJson(aggregateRead.value.bytes);
|
|
481
|
+
if (latest.state === 'prepared' && latest.requestFingerprint === fingerprint) {
|
|
482
|
+
return { kind: 'alreadyPrepared', fence };
|
|
483
|
+
}
|
|
484
|
+
if (latest.state !== 'staging' || latest.requestFingerprint !== fingerprint) {
|
|
485
|
+
return { kind: 'conflict' };
|
|
486
|
+
}
|
|
487
|
+
const reserved = await this.provider.write({
|
|
488
|
+
conditions: [
|
|
489
|
+
{ kind: 'version', key: rowKey, expected: aggregateRead.value.version },
|
|
490
|
+
versionCondition(targetDescriptorKey, descriptorRead),
|
|
491
|
+
versionCondition(targetLeaseKey, leaseRead),
|
|
492
|
+
conditionFor(targetCapacityKey, targetCapacityRead)
|
|
493
|
+
],
|
|
494
|
+
mutations: [
|
|
495
|
+
{
|
|
496
|
+
kind: 'put',
|
|
497
|
+
key: rowKey,
|
|
498
|
+
bytes: encodeJson({ ...latest, state: 'prepared' })
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
kind: 'put',
|
|
502
|
+
key: targetCapacityKey,
|
|
503
|
+
bytes: encodeJson({
|
|
504
|
+
active: targetCapacity.active,
|
|
505
|
+
pending: addCapacity(targetCapacity.pending, request.capacity)
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
]
|
|
509
|
+
}, signal);
|
|
510
|
+
if (reserved.kind === 'conflict') {
|
|
511
|
+
const raced = await this.provider.read(rowKey, signal);
|
|
512
|
+
if (raced.kind === 'found') {
|
|
513
|
+
const record = decodeJson(raced.value.bytes);
|
|
514
|
+
if (record.state === 'prepared' && record.requestFingerprint === fingerprint) {
|
|
515
|
+
return { kind: 'alreadyPrepared', fence };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
await this.clearAggregateMarkers(fence, installed, signal);
|
|
519
|
+
return { kind: 'conflict' };
|
|
520
|
+
}
|
|
521
|
+
return { kind: 'prepared', fence };
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
await this.abortAggregateStaging(fence, signal);
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async commitAggregate(fence, signal) {
|
|
529
|
+
validateAggregateFence(fence);
|
|
530
|
+
return await this.commitAggregateCore(fence, 0, performance.now() + AGGREGATE_COMMIT_RETRY_WINDOW_MS, signal);
|
|
531
|
+
}
|
|
532
|
+
async commitAggregateCore(fence, retryAttempt, retryDeadlineAtMs, signal) {
|
|
533
|
+
const rowKey = aggregateKey(fence);
|
|
534
|
+
let aggregateRead = await this.provider.read(rowKey, signal);
|
|
535
|
+
if (aggregateRead.kind === 'missing')
|
|
536
|
+
return { kind: 'stale' };
|
|
537
|
+
let aggregate = decodeJson(aggregateRead.value.bytes);
|
|
538
|
+
if (aggregate.state === 'aborted' || aggregate.state === 'staging') {
|
|
539
|
+
return { kind: 'stale' };
|
|
540
|
+
}
|
|
541
|
+
if (aggregate.state === 'committed') {
|
|
542
|
+
await this.normalizeCommittedAggregate(fence, aggregate, signal);
|
|
543
|
+
return { kind: 'alreadyCommitted' };
|
|
544
|
+
}
|
|
545
|
+
const entries = await this.aggregateInventory.read(fence, Buffer.from(aggregate.inventoryDigest), signal);
|
|
546
|
+
if (entries.length !== aggregate.participantCount) {
|
|
547
|
+
throw new Error('Aggregate inventory count differs from its authority record.');
|
|
548
|
+
}
|
|
549
|
+
const authorityRows = [];
|
|
550
|
+
for (let index = 0; index < entries.length; index++) {
|
|
551
|
+
const entry = entries[index];
|
|
552
|
+
const rowKeyForParticipant = authorityKey(entry.authorityKey);
|
|
553
|
+
const current = await this.provider.read(rowKeyForParticipant, signal);
|
|
554
|
+
if (current.kind === 'missing')
|
|
555
|
+
return { kind: 'stale' };
|
|
556
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
557
|
+
if (!sameAggregateMarkerEntry(record.aggregate, fence, index, entry)) {
|
|
558
|
+
// A competing committer may already have published the aggregate and
|
|
559
|
+
// normalized this participant before this reader reached it. The
|
|
560
|
+
// committed aggregate is the terminal outcome to adopt, not a stale
|
|
561
|
+
// failure caused by observing the post-normalization row.
|
|
562
|
+
const latest = await this.provider.read(rowKey, signal);
|
|
563
|
+
if (latest.kind === 'found') {
|
|
564
|
+
const latestAggregate = decodeJson(latest.value.bytes);
|
|
565
|
+
if (latestAggregate.state === 'committed') {
|
|
566
|
+
await this.normalizeCommittedAggregate(fence, latestAggregate, signal);
|
|
567
|
+
return { kind: 'alreadyCommitted' };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return { kind: 'stale' };
|
|
571
|
+
}
|
|
572
|
+
const [payload, membership] = await Promise.all([
|
|
573
|
+
requireProviderBytes(this.provider, aggregateParticipantPayloadKey(fence, index), signal),
|
|
574
|
+
requireProviderBytes(this.provider, aggregateParticipantMembershipKey(fence, index), signal)
|
|
575
|
+
]);
|
|
576
|
+
if (sha256Hex(payload) !== entry.authorityPayloadSha256
|
|
577
|
+
|| sha256Hex(membership) !== entry.membershipMutationSha256) {
|
|
578
|
+
throw new Error('Aggregate participant staging checksum does not match inventory.');
|
|
579
|
+
}
|
|
580
|
+
authorityRows.push({ key: rowKeyForParticipant, current, record, entry });
|
|
581
|
+
}
|
|
582
|
+
const targetDescriptorKey = meshKey(aggregate.targetDescriptor.meshName, aggregate.targetDescriptor.rid);
|
|
583
|
+
const targetLeaseKey = ownerKey(aggregate.targetOwner.ownerId);
|
|
584
|
+
const targetCapacityKey = capacityKey(aggregate.targetDescriptor.meshName, String(aggregate.targetDescriptor.rid));
|
|
585
|
+
const [descriptorRead, leaseRead] = await Promise.all([
|
|
586
|
+
this.provider.read(targetDescriptorKey, signal),
|
|
587
|
+
this.provider.read(targetLeaseKey, signal)
|
|
588
|
+
]);
|
|
589
|
+
if (liveTargetDescriptor(descriptorRead, leaseRead, aggregateTargetFromRecord(aggregate)) === undefined) {
|
|
590
|
+
return { kind: 'stale' };
|
|
591
|
+
}
|
|
592
|
+
const capacityDeltas = new Map();
|
|
593
|
+
for (const row of authorityRows) {
|
|
594
|
+
if (row.entry.ownerTransition !== 'newOwner')
|
|
595
|
+
continue;
|
|
596
|
+
const key = capacityKey(row.record.snapshot.allocation.descriptor.meshName, String(row.record.snapshot.allocation.descriptor.rid)).value;
|
|
597
|
+
capacityDeltas.set(key, addCapacityVector(capacityDeltas.get(key) ?? { actors: 0, spots: 0 }, row.record.snapshot.allocation.capacity));
|
|
598
|
+
}
|
|
599
|
+
const capacityKeys = new Map();
|
|
600
|
+
capacityKeys.set(targetCapacityKey.value, targetCapacityKey);
|
|
601
|
+
for (const value of capacityDeltas.keys())
|
|
602
|
+
capacityKeys.set(value, (0, in_memory_provider_location_store_1.storeKey)(value));
|
|
603
|
+
const capacityReads = new Map();
|
|
604
|
+
for (const [value, key] of capacityKeys) {
|
|
605
|
+
const read = await this.provider.read(key, signal);
|
|
606
|
+
if (read.kind === 'missing') {
|
|
607
|
+
// Capacity rows are provider-private. A foreign source legitimately
|
|
608
|
+
// has no row under this provider's key encoding; its source runtime
|
|
609
|
+
// releases that capacity after observing the committed authority.
|
|
610
|
+
// The target row remains mandatory because this provider reserved it
|
|
611
|
+
// during prepare and must consume it atomically with commit.
|
|
612
|
+
if (value === targetCapacityKey.value)
|
|
613
|
+
return { kind: 'stale' };
|
|
614
|
+
capacityKeys.delete(value);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
capacityReads.set(value, read);
|
|
618
|
+
}
|
|
619
|
+
const capacityMutations = [];
|
|
620
|
+
for (const [value, key] of capacityKeys) {
|
|
621
|
+
const read = capacityReads.get(value);
|
|
622
|
+
let capacity = decodeJson(read.value.bytes);
|
|
623
|
+
const sourceDelta = capacityDeltas.get(value);
|
|
624
|
+
if (sourceDelta !== undefined) {
|
|
625
|
+
capacity = {
|
|
626
|
+
active: subtractCapacity(capacity.active, sourceDelta),
|
|
627
|
+
pending: capacity.pending
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (value === targetCapacityKey.value) {
|
|
631
|
+
capacity = {
|
|
632
|
+
active: addCapacity(capacity.active, aggregate.capacity),
|
|
633
|
+
pending: subtractCapacity(capacity.pending, aggregate.capacity)
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
capacityMutations.push({
|
|
637
|
+
kind: 'put',
|
|
638
|
+
key,
|
|
639
|
+
bytes: encodeJson(capacity)
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
const published = await this.provider.write({
|
|
643
|
+
conditions: [
|
|
644
|
+
{ kind: 'version', key: rowKey, expected: aggregateRead.value.version },
|
|
645
|
+
versionCondition(targetDescriptorKey, descriptorRead),
|
|
646
|
+
versionCondition(targetLeaseKey, leaseRead),
|
|
647
|
+
...[...capacityReads.entries()].map(([value, read]) => ({
|
|
648
|
+
kind: 'version',
|
|
649
|
+
key: capacityKeys.get(value),
|
|
650
|
+
expected: read.value.version
|
|
651
|
+
}))
|
|
652
|
+
],
|
|
653
|
+
mutations: [
|
|
654
|
+
{
|
|
655
|
+
kind: 'put',
|
|
656
|
+
key: rowKey,
|
|
657
|
+
bytes: encodeJson({ ...aggregate, state: 'committed' })
|
|
658
|
+
},
|
|
659
|
+
...capacityMutations
|
|
660
|
+
]
|
|
661
|
+
}, signal);
|
|
662
|
+
if (published.kind === 'conflict') {
|
|
663
|
+
aggregateRead = await this.provider.read(rowKey, signal);
|
|
664
|
+
if (aggregateRead.kind === 'missing')
|
|
665
|
+
return { kind: 'stale' };
|
|
666
|
+
aggregate = decodeJson(aggregateRead.value.bytes);
|
|
667
|
+
if (aggregate.state === 'prepared'
|
|
668
|
+
&& retryAttempt < MAX_AGGREGATE_COMMIT_CONFLICT_RETRIES
|
|
669
|
+
&& performance.now() < retryDeadlineAtMs) {
|
|
670
|
+
// The commit CAS also fences the target's live owner lease, descriptor,
|
|
671
|
+
// and capacity rows. Re-read the whole prepared fence after an
|
|
672
|
+
// auxiliary-row race; a changed aggregate or participant fence exits
|
|
673
|
+
// through the normal stale checks on the next attempt.
|
|
674
|
+
await waitForAggregateCommitRetry(retryAttempt, retryDeadlineAtMs, signal);
|
|
675
|
+
return await this.commitAggregateCore(fence, retryAttempt + 1, retryDeadlineAtMs, signal);
|
|
676
|
+
}
|
|
677
|
+
if (aggregate.state !== 'committed')
|
|
678
|
+
return { kind: 'stale' };
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
aggregate = { ...aggregate, state: 'committed' };
|
|
682
|
+
}
|
|
683
|
+
await this.normalizeCommittedAggregate(fence, aggregate, signal);
|
|
684
|
+
return { kind: 'committed' };
|
|
685
|
+
}
|
|
686
|
+
async abortAggregate(fence, signal) {
|
|
687
|
+
validateAggregateFence(fence);
|
|
688
|
+
const rowKey = aggregateKey(fence);
|
|
689
|
+
for (;;) {
|
|
690
|
+
const current = await this.provider.read(rowKey, signal);
|
|
691
|
+
if (current.kind === 'missing')
|
|
692
|
+
return { kind: 'stale' };
|
|
693
|
+
const aggregate = decodeJson(current.value.bytes);
|
|
694
|
+
if (aggregate.state === 'aborted') {
|
|
695
|
+
await this.clearAggregateMarkersByScan(fence, signal);
|
|
696
|
+
return { kind: 'alreadyAborted' };
|
|
697
|
+
}
|
|
698
|
+
if (aggregate.state === 'committed')
|
|
699
|
+
return { kind: 'stale' };
|
|
700
|
+
const conditions = [{
|
|
701
|
+
kind: 'version',
|
|
702
|
+
key: rowKey,
|
|
703
|
+
expected: current.value.version
|
|
704
|
+
}];
|
|
705
|
+
const mutations = [{
|
|
706
|
+
kind: 'put',
|
|
707
|
+
key: rowKey,
|
|
708
|
+
bytes: encodeJson({ ...aggregate, state: 'aborted' })
|
|
709
|
+
}];
|
|
710
|
+
if (aggregate.state === 'prepared') {
|
|
711
|
+
const targetCapacityKey = capacityKey(aggregate.targetDescriptor.meshName, String(aggregate.targetDescriptor.rid));
|
|
712
|
+
const targetCapacityRead = await this.provider.read(targetCapacityKey, signal);
|
|
713
|
+
if (targetCapacityRead.kind === 'missing')
|
|
714
|
+
return { kind: 'stale' };
|
|
715
|
+
const capacity = decodeJson(targetCapacityRead.value.bytes);
|
|
716
|
+
conditions.push({
|
|
717
|
+
kind: 'version',
|
|
718
|
+
key: targetCapacityKey,
|
|
719
|
+
expected: targetCapacityRead.value.version
|
|
720
|
+
});
|
|
721
|
+
mutations.push({
|
|
722
|
+
kind: 'put',
|
|
723
|
+
key: targetCapacityKey,
|
|
724
|
+
bytes: encodeJson({
|
|
725
|
+
active: capacity.active,
|
|
726
|
+
pending: subtractCapacity(capacity.pending, aggregate.capacity)
|
|
727
|
+
})
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
const result = await this.provider.write({ conditions, mutations }, signal);
|
|
731
|
+
if (result.kind === 'conflict')
|
|
732
|
+
continue;
|
|
733
|
+
await this.clearAggregateMarkersByScan(fence, signal);
|
|
734
|
+
return { kind: 'aborted' };
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
async reserve(request, signal) {
|
|
738
|
+
const encodedAuthorityKey = (0, authority_key_codec_1.encodeAuthorityKey)(request.key.kind, request.key.globalId);
|
|
739
|
+
for (;;) {
|
|
740
|
+
signal?.throwIfAborted();
|
|
741
|
+
const rowKey = authorityKey(encodedAuthorityKey.value);
|
|
742
|
+
const descriptorKey = meshKey(request.target.meshName, request.target.nodeRid);
|
|
743
|
+
const leaseKey = ownerKey(request.target.owner.ownerId);
|
|
744
|
+
const capacityRowKey = capacityKey(request.target.meshName, String(request.target.nodeRid));
|
|
745
|
+
const [current, descriptorRead, leaseRead, capacityRead, objectGenerationRead, authorityOwnerGenerationRead] = await Promise.all([
|
|
746
|
+
this.provider.read(rowKey, signal),
|
|
747
|
+
this.provider.read(descriptorKey, signal),
|
|
748
|
+
this.provider.read(leaseKey, signal),
|
|
749
|
+
this.provider.read(capacityRowKey, signal),
|
|
750
|
+
this.provider.read(OBJECT_COUNTER_KEY, signal),
|
|
751
|
+
this.provider.read(AUTHORITY_OWNER_COUNTER_KEY, signal)
|
|
752
|
+
]);
|
|
753
|
+
if (current.kind === 'found') {
|
|
754
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
755
|
+
const snapshot = authoritySnapshot(record.snapshot, current.value.version, current.value.storeNow);
|
|
756
|
+
if (snapshot.allocation.objectKind !== request.key.kind
|
|
757
|
+
|| snapshot.allocation.stableType !== request.intent.stableType) {
|
|
758
|
+
return { kind: 'typeMismatch', current: snapshot };
|
|
759
|
+
}
|
|
760
|
+
return snapshot.allocation.state === 'active'
|
|
761
|
+
? { kind: 'alreadyExists', current: snapshot }
|
|
762
|
+
: { kind: 'conflict', current: snapshot };
|
|
763
|
+
}
|
|
764
|
+
const descriptor = liveTargetDescriptor(descriptorRead, leaseRead, request.target);
|
|
765
|
+
if (descriptor === undefined) {
|
|
766
|
+
return { kind: 'conflict', current: { kind: 'missing', storeNow: current.storeNow } };
|
|
767
|
+
}
|
|
768
|
+
const capacity = capacityRead.kind === 'missing'
|
|
769
|
+
? emptyCapacityRecord()
|
|
770
|
+
: decodeJson(capacityRead.value.bytes);
|
|
771
|
+
if (!capacityAvailable(descriptor, request.capacity, capacity)) {
|
|
772
|
+
return { kind: 'placementCapacityExhausted' };
|
|
773
|
+
}
|
|
774
|
+
const generation = counterNextValue(objectGenerationRead);
|
|
775
|
+
const authorityOwnerGeneration = counterNextValue(authorityOwnerGenerationRead);
|
|
776
|
+
// Counters retain the next value to issue. Issuing MAX_GENERATION
|
|
777
|
+
// would require persisting MAX_GENERATION + 1, which is outside the
|
|
778
|
+
// contract's valid stored range, so it fails before this batch mutates.
|
|
779
|
+
if (generation >= MAX_GENERATION || authorityOwnerGeneration >= MAX_GENERATION) {
|
|
780
|
+
return { kind: 'generationExhausted' };
|
|
781
|
+
}
|
|
782
|
+
const reservationId = (0, node_crypto_1.randomUUID)();
|
|
783
|
+
const allocation = {
|
|
784
|
+
state: 'reserved',
|
|
785
|
+
objectKind: request.key.kind,
|
|
786
|
+
stableType: request.intent.stableType,
|
|
787
|
+
descriptor: {
|
|
788
|
+
meshName: request.target.meshName,
|
|
789
|
+
rid: request.target.nodeRid
|
|
790
|
+
},
|
|
791
|
+
descriptorLifecycleGeneration: request.target.nodeLifecycleGeneration,
|
|
792
|
+
capacity: cloneCapacity(request.capacity)
|
|
793
|
+
};
|
|
794
|
+
const record = {
|
|
795
|
+
reservationId,
|
|
796
|
+
snapshot: {
|
|
797
|
+
payload: Buffer.from(request.creatingPayload),
|
|
798
|
+
objectGeneration: generation,
|
|
799
|
+
authorityOwnerGeneration,
|
|
800
|
+
ownerId: request.target.owner.ownerId,
|
|
801
|
+
ownerLeaseGeneration: request.target.owner.leaseGeneration,
|
|
802
|
+
allocation,
|
|
803
|
+
pendingCreation: {
|
|
804
|
+
reservationId,
|
|
805
|
+
requestContentReference: request.intent.requestContentReference,
|
|
806
|
+
requestSha256: Buffer.from(request.intent.requestSha256),
|
|
807
|
+
requestEncodedSize: request.intent.requestEncodedSize
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
const result = await this.provider.write({
|
|
812
|
+
conditions: [
|
|
813
|
+
{ kind: 'missing', key: rowKey },
|
|
814
|
+
versionCondition(descriptorKey, descriptorRead),
|
|
815
|
+
versionCondition(leaseKey, leaseRead),
|
|
816
|
+
conditionFor(capacityRowKey, capacityRead),
|
|
817
|
+
conditionFor(OBJECT_COUNTER_KEY, objectGenerationRead),
|
|
818
|
+
conditionFor(AUTHORITY_OWNER_COUNTER_KEY, authorityOwnerGenerationRead)
|
|
819
|
+
],
|
|
820
|
+
mutations: [
|
|
821
|
+
{ kind: 'put', key: rowKey, bytes: encodeAuthorityRecord(record) },
|
|
822
|
+
{
|
|
823
|
+
kind: 'put',
|
|
824
|
+
key: capacityRowKey,
|
|
825
|
+
bytes: encodeJson({
|
|
826
|
+
active: capacity.active,
|
|
827
|
+
pending: addCapacity(capacity.pending, request.capacity)
|
|
828
|
+
})
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
kind: 'put',
|
|
832
|
+
key: OBJECT_COUNTER_KEY,
|
|
833
|
+
bytes: encodeText((generation + 1n).toString())
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
kind: 'put',
|
|
837
|
+
key: AUTHORITY_OWNER_COUNTER_KEY,
|
|
838
|
+
bytes: encodeText((authorityOwnerGeneration + 1n).toString())
|
|
839
|
+
}
|
|
840
|
+
]
|
|
841
|
+
}, signal);
|
|
842
|
+
if (result.kind === 'conflict')
|
|
843
|
+
continue;
|
|
844
|
+
const version = result.putVersions.find(entry => entry.key.value === rowKey.value)?.version;
|
|
845
|
+
if (version === undefined)
|
|
846
|
+
throw new Error('Authority reserve did not return a row version.');
|
|
847
|
+
return {
|
|
848
|
+
kind: 'reserved',
|
|
849
|
+
reservationId,
|
|
850
|
+
creating: authoritySnapshot(record.snapshot, version, result.storeNow)
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
async commit(request, signal) {
|
|
855
|
+
const key = (0, authority_key_codec_1.encodeAuthorityKey)(request.key.kind, request.key.globalId);
|
|
856
|
+
const rowKey = authorityKey(key.value);
|
|
857
|
+
for (;;) {
|
|
858
|
+
signal?.throwIfAborted();
|
|
859
|
+
const current = await this.provider.read(rowKey, signal);
|
|
860
|
+
if (current.kind === 'missing')
|
|
861
|
+
return { kind: 'stale' };
|
|
862
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
863
|
+
if (record.terminal === 'committed'
|
|
864
|
+
&& record.reservationId === request.reservationId
|
|
865
|
+
&& record.snapshot.allocation.state === 'active') {
|
|
866
|
+
return {
|
|
867
|
+
kind: 'alreadyCommitted',
|
|
868
|
+
ready: authoritySnapshot(record.snapshot, current.value.version, current.value.storeNow)
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
if (record.reservationId !== request.reservationId
|
|
872
|
+
|| current.value.version.value !== request.expectedStoreVersion
|
|
873
|
+
|| record.snapshot.allocation.state !== 'reserved'
|
|
874
|
+
|| !sameCreationTarget(record.snapshot, request.target)) {
|
|
875
|
+
return { kind: 'stale' };
|
|
876
|
+
}
|
|
877
|
+
const descriptorKey = meshKey(request.target.meshName, request.target.nodeRid);
|
|
878
|
+
const leaseKey = ownerKey(request.target.owner.ownerId);
|
|
879
|
+
const capacityRowKey = capacityKey(request.target.meshName, request.target.nodeRid);
|
|
880
|
+
const [descriptorRead, leaseRead, capacityRead] = await Promise.all([
|
|
881
|
+
this.provider.read(descriptorKey, signal),
|
|
882
|
+
this.provider.read(leaseKey, signal),
|
|
883
|
+
this.provider.read(capacityRowKey, signal)
|
|
884
|
+
]);
|
|
885
|
+
if (liveTargetDescriptor(descriptorRead, leaseRead, request.target) === undefined) {
|
|
886
|
+
return { kind: 'stale' };
|
|
887
|
+
}
|
|
888
|
+
const capacity = capacityRead.kind === 'missing'
|
|
889
|
+
? emptyCapacityRecord()
|
|
890
|
+
: decodeJson(capacityRead.value.bytes);
|
|
891
|
+
const ready = {
|
|
892
|
+
reservationId: request.reservationId,
|
|
893
|
+
terminal: 'committed',
|
|
894
|
+
snapshot: {
|
|
895
|
+
...record.snapshot,
|
|
896
|
+
payload: Buffer.from(request.readyPayload),
|
|
897
|
+
allocation: { ...record.snapshot.allocation, state: 'active' },
|
|
898
|
+
pendingCreation: undefined
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
const result = await this.provider.write({
|
|
902
|
+
conditions: [
|
|
903
|
+
{ kind: 'version', key: rowKey, expected: current.value.version },
|
|
904
|
+
versionCondition(descriptorKey, descriptorRead),
|
|
905
|
+
versionCondition(leaseKey, leaseRead),
|
|
906
|
+
conditionFor(capacityRowKey, capacityRead)
|
|
907
|
+
],
|
|
908
|
+
mutations: [
|
|
909
|
+
{ kind: 'put', key: rowKey, bytes: encodeAuthorityRecord(ready) },
|
|
910
|
+
{
|
|
911
|
+
kind: 'put',
|
|
912
|
+
key: capacityRowKey,
|
|
913
|
+
bytes: encodeJson({
|
|
914
|
+
active: addCapacity(capacity.active, record.snapshot.allocation.capacity),
|
|
915
|
+
pending: subtractCapacity(capacity.pending, record.snapshot.allocation.capacity)
|
|
916
|
+
})
|
|
917
|
+
}
|
|
918
|
+
]
|
|
919
|
+
}, signal);
|
|
920
|
+
if (result.kind === 'conflict')
|
|
921
|
+
continue;
|
|
922
|
+
const version = result.putVersions.find(entry => entry.key.value === rowKey.value)?.version;
|
|
923
|
+
if (version === undefined)
|
|
924
|
+
throw new Error('Authority commit did not return a row version.');
|
|
925
|
+
return {
|
|
926
|
+
kind: 'committed',
|
|
927
|
+
ready: authoritySnapshot(ready.snapshot, version, result.storeNow)
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
async abort(request, signal) {
|
|
932
|
+
const key = (0, authority_key_codec_1.encodeAuthorityKey)(request.key.kind, request.key.globalId);
|
|
933
|
+
const rowKey = authorityKey(key.value);
|
|
934
|
+
for (;;) {
|
|
935
|
+
signal?.throwIfAborted();
|
|
936
|
+
const current = await this.provider.read(rowKey, signal);
|
|
937
|
+
if (current.kind === 'missing')
|
|
938
|
+
return { kind: 'stale' };
|
|
939
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
940
|
+
if (record.reservationId !== request.reservationId
|
|
941
|
+
|| current.value.version.value !== request.expectedStoreVersion
|
|
942
|
+
|| record.snapshot.allocation.state !== 'reserved'
|
|
943
|
+
|| !sameCreationTarget(record.snapshot, request.target)) {
|
|
944
|
+
return { kind: 'stale' };
|
|
945
|
+
}
|
|
946
|
+
const leaseKey = ownerKey(request.target.owner.ownerId);
|
|
947
|
+
const capacityRowKey = capacityKey(request.target.meshName, String(request.target.nodeRid));
|
|
948
|
+
const [leaseRead, capacityRead] = await Promise.all([
|
|
949
|
+
this.provider.read(leaseKey, signal),
|
|
950
|
+
this.provider.read(capacityRowKey, signal)
|
|
951
|
+
]);
|
|
952
|
+
if (!sameLiveOwner(leaseRead, record.snapshot))
|
|
953
|
+
return { kind: 'stale' };
|
|
954
|
+
const capacity = capacityRead.kind === 'missing'
|
|
955
|
+
? emptyCapacityRecord()
|
|
956
|
+
: decodeJson(capacityRead.value.bytes);
|
|
957
|
+
const result = await this.provider.write({
|
|
958
|
+
conditions: [
|
|
959
|
+
{ kind: 'version', key: rowKey, expected: current.value.version },
|
|
960
|
+
versionCondition(leaseKey, leaseRead),
|
|
961
|
+
conditionFor(capacityRowKey, capacityRead)
|
|
962
|
+
],
|
|
963
|
+
mutations: [
|
|
964
|
+
{ kind: 'delete', key: rowKey },
|
|
965
|
+
{
|
|
966
|
+
kind: 'put',
|
|
967
|
+
key: capacityRowKey,
|
|
968
|
+
bytes: encodeJson({
|
|
969
|
+
active: capacity.active,
|
|
970
|
+
pending: subtractCapacity(capacity.pending, record.snapshot.allocation.capacity)
|
|
971
|
+
})
|
|
972
|
+
}
|
|
973
|
+
]
|
|
974
|
+
}, signal);
|
|
975
|
+
if (result.kind === 'conflict')
|
|
976
|
+
continue;
|
|
977
|
+
return { kind: 'aborted' };
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async completeCreation(request, signal) {
|
|
981
|
+
if (request.key.kind !== 'actor') {
|
|
982
|
+
throw new TypeError('completeCreation is reserved for Actor creation.');
|
|
983
|
+
}
|
|
984
|
+
const terminal = createTerminalRecord(request, this.nowProvider());
|
|
985
|
+
const rowKey = authorityKey((0, authority_key_codec_1.encodeAuthorityKey)(request.key.kind, request.key.globalId).value);
|
|
986
|
+
const terminalRowKey = creationTerminalKey(terminal.operation);
|
|
987
|
+
for (;;) {
|
|
988
|
+
signal?.throwIfAborted();
|
|
989
|
+
const [current, existingTerminal] = await Promise.all([
|
|
990
|
+
this.provider.read(rowKey, signal),
|
|
991
|
+
this.provider.read(terminalRowKey, signal)
|
|
992
|
+
]);
|
|
993
|
+
if (existingTerminal.kind === 'found') {
|
|
994
|
+
return {
|
|
995
|
+
kind: 'alreadyCompleted',
|
|
996
|
+
terminal: reviveCreationTerminal(decodeJson(existingTerminal.value.bytes))
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
if (current.kind === 'missing')
|
|
1000
|
+
return { kind: 'stale' };
|
|
1001
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
1002
|
+
if (record.reservationId !== request.reservationId
|
|
1003
|
+
|| current.value.version.value !== request.expectedStoreVersion
|
|
1004
|
+
|| record.snapshot.allocation.state !== 'reserved'
|
|
1005
|
+
|| !sameCreationTarget(record.snapshot, request.target)) {
|
|
1006
|
+
return { kind: 'stale' };
|
|
1007
|
+
}
|
|
1008
|
+
const descriptorKey = meshKey(request.target.meshName, request.target.nodeRid);
|
|
1009
|
+
const leaseKey = ownerKey(request.target.owner.ownerId);
|
|
1010
|
+
const capacityRowKey = capacityKey(request.target.meshName, request.target.nodeRid);
|
|
1011
|
+
const [descriptorRead, leaseRead, capacityRead] = await Promise.all([
|
|
1012
|
+
this.provider.read(descriptorKey, signal),
|
|
1013
|
+
this.provider.read(leaseKey, signal),
|
|
1014
|
+
this.provider.read(capacityRowKey, signal)
|
|
1015
|
+
]);
|
|
1016
|
+
if (liveTargetDescriptor(descriptorRead, leaseRead, request.target) === undefined) {
|
|
1017
|
+
return { kind: 'stale' };
|
|
1018
|
+
}
|
|
1019
|
+
const capacity = capacityRead.kind === 'missing'
|
|
1020
|
+
? emptyCapacityRecord()
|
|
1021
|
+
: decodeJson(capacityRead.value.bytes);
|
|
1022
|
+
const terminalAtStore = {
|
|
1023
|
+
...terminal,
|
|
1024
|
+
storeNow: current.value.storeNow
|
|
1025
|
+
};
|
|
1026
|
+
const retentionMs = terminalAtStore.expiresAt.getTime()
|
|
1027
|
+
- current.value.storeNow.getTime();
|
|
1028
|
+
if (retentionMs <= 0) {
|
|
1029
|
+
throw new RangeError('Creation terminal expiry must be the live operation deadline plus five minutes.');
|
|
1030
|
+
}
|
|
1031
|
+
const mutations = request.completion.kind === 'created'
|
|
1032
|
+
? [
|
|
1033
|
+
{
|
|
1034
|
+
kind: 'put',
|
|
1035
|
+
key: rowKey,
|
|
1036
|
+
bytes: encodeAuthorityRecord({
|
|
1037
|
+
reservationId: request.reservationId,
|
|
1038
|
+
terminal: 'committed',
|
|
1039
|
+
snapshot: {
|
|
1040
|
+
...record.snapshot,
|
|
1041
|
+
payload: Buffer.from(request.completion.readyPayload),
|
|
1042
|
+
allocation: { ...record.snapshot.allocation, state: 'active' },
|
|
1043
|
+
pendingCreation: undefined
|
|
1044
|
+
}
|
|
1045
|
+
})
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
kind: 'put',
|
|
1049
|
+
key: capacityRowKey,
|
|
1050
|
+
bytes: encodeJson({
|
|
1051
|
+
active: addCapacity(capacity.active, record.snapshot.allocation.capacity),
|
|
1052
|
+
pending: subtractCapacity(capacity.pending, record.snapshot.allocation.capacity)
|
|
1053
|
+
})
|
|
1054
|
+
}
|
|
1055
|
+
]
|
|
1056
|
+
: [
|
|
1057
|
+
{ kind: 'delete', key: rowKey },
|
|
1058
|
+
{
|
|
1059
|
+
kind: 'put',
|
|
1060
|
+
key: capacityRowKey,
|
|
1061
|
+
bytes: encodeJson({
|
|
1062
|
+
active: capacity.active,
|
|
1063
|
+
pending: subtractCapacity(capacity.pending, record.snapshot.allocation.capacity)
|
|
1064
|
+
})
|
|
1065
|
+
}
|
|
1066
|
+
];
|
|
1067
|
+
const result = await this.provider.write({
|
|
1068
|
+
conditions: [
|
|
1069
|
+
{ kind: 'version', key: rowKey, expected: current.value.version },
|
|
1070
|
+
{ kind: 'missing', key: terminalRowKey },
|
|
1071
|
+
versionCondition(descriptorKey, descriptorRead),
|
|
1072
|
+
versionCondition(leaseKey, leaseRead),
|
|
1073
|
+
conditionFor(capacityRowKey, capacityRead)
|
|
1074
|
+
],
|
|
1075
|
+
mutations: [
|
|
1076
|
+
...mutations,
|
|
1077
|
+
{
|
|
1078
|
+
kind: 'put',
|
|
1079
|
+
key: terminalRowKey,
|
|
1080
|
+
bytes: encodeJson(terminalAtStore),
|
|
1081
|
+
retentionMs
|
|
1082
|
+
}
|
|
1083
|
+
]
|
|
1084
|
+
}, signal);
|
|
1085
|
+
if (result.kind === 'conflict')
|
|
1086
|
+
continue;
|
|
1087
|
+
const publishedTerminal = {
|
|
1088
|
+
...terminalAtStore,
|
|
1089
|
+
storeNow: result.storeNow
|
|
1090
|
+
};
|
|
1091
|
+
if (request.completion.kind !== 'created') {
|
|
1092
|
+
return { kind: request.completion.kind, terminal: publishedTerminal };
|
|
1093
|
+
}
|
|
1094
|
+
const version = result.putVersions.find(entry => entry.key.value === rowKey.value)?.version;
|
|
1095
|
+
if (version === undefined) {
|
|
1096
|
+
throw new Error('Actor creation completion did not return an authority row version.');
|
|
1097
|
+
}
|
|
1098
|
+
const readyRecord = decodeAuthorityRecord(encodeAuthorityRecord({
|
|
1099
|
+
reservationId: request.reservationId,
|
|
1100
|
+
terminal: 'committed',
|
|
1101
|
+
snapshot: {
|
|
1102
|
+
...record.snapshot,
|
|
1103
|
+
payload: Buffer.from(request.completion.readyPayload),
|
|
1104
|
+
allocation: { ...record.snapshot.allocation, state: 'active' },
|
|
1105
|
+
pendingCreation: undefined
|
|
1106
|
+
}
|
|
1107
|
+
}));
|
|
1108
|
+
return {
|
|
1109
|
+
kind: 'created',
|
|
1110
|
+
ready: authoritySnapshot(readyRecord.snapshot, version, result.storeNow),
|
|
1111
|
+
terminal: publishedTerminal
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async readCreationTerminal(operation, signal) {
|
|
1116
|
+
validateCreationOperation(operation);
|
|
1117
|
+
const result = await this.provider.read(creationTerminalKey(operation), signal);
|
|
1118
|
+
return result.kind === 'missing'
|
|
1119
|
+
? { kind: 'missing', storeNow: result.storeNow }
|
|
1120
|
+
: {
|
|
1121
|
+
kind: 'found',
|
|
1122
|
+
record: reviveCreationTerminal(decodeJson(result.value.bytes))
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
async claimOwnerLease(ownerId, leaseTtlMs, signal) {
|
|
1126
|
+
requireOwnerInput(ownerId, leaseTtlMs);
|
|
1127
|
+
for (;;) {
|
|
1128
|
+
const [owner, counter] = await Promise.all([
|
|
1129
|
+
this.provider.read(ownerKey(ownerId), signal),
|
|
1130
|
+
this.provider.read(OWNER_COUNTER_KEY, signal)
|
|
1131
|
+
]);
|
|
1132
|
+
if (owner.kind === 'found')
|
|
1133
|
+
return { kind: 'conflict' };
|
|
1134
|
+
const generation = counterNextValue(counter);
|
|
1135
|
+
// Counters retain the next value to issue. Issuing MAX_GENERATION would
|
|
1136
|
+
// require persisting MAX_GENERATION + 1, outside the stored range.
|
|
1137
|
+
if (generation >= MAX_GENERATION)
|
|
1138
|
+
return { kind: 'generationExhausted' };
|
|
1139
|
+
const token = { ownerId, leaseGeneration: generation };
|
|
1140
|
+
const conditions = [
|
|
1141
|
+
{ kind: 'missing', key: ownerKey(ownerId) },
|
|
1142
|
+
counter.kind === 'missing'
|
|
1143
|
+
? { kind: 'missing', key: OWNER_COUNTER_KEY }
|
|
1144
|
+
: { kind: 'version', key: OWNER_COUNTER_KEY, expected: counter.value.version }
|
|
1145
|
+
];
|
|
1146
|
+
const result = await this.provider.write({
|
|
1147
|
+
conditions,
|
|
1148
|
+
mutations: [
|
|
1149
|
+
{
|
|
1150
|
+
kind: 'put',
|
|
1151
|
+
key: ownerKey(ownerId),
|
|
1152
|
+
bytes: encodeOwnerRecord(ownerId, generation),
|
|
1153
|
+
retentionMs: leaseTtlMs
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
kind: 'put',
|
|
1157
|
+
key: OWNER_COUNTER_KEY,
|
|
1158
|
+
bytes: encodeText((generation + 1n).toString())
|
|
1159
|
+
}
|
|
1160
|
+
]
|
|
1161
|
+
}, signal);
|
|
1162
|
+
if (result.kind === 'conflict')
|
|
1163
|
+
continue;
|
|
1164
|
+
return {
|
|
1165
|
+
kind: 'claimed',
|
|
1166
|
+
token,
|
|
1167
|
+
leaseExpiresAt: new Date(result.storeNow.getTime() + leaseTtlMs),
|
|
1168
|
+
storeNow: result.storeNow
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
async readOwnerLease(ownerId, signal) {
|
|
1173
|
+
const result = await this.provider.read(ownerKey(ownerId), signal);
|
|
1174
|
+
if (result.kind === 'missing')
|
|
1175
|
+
return { kind: 'missing' };
|
|
1176
|
+
const record = decodeOwnerRecord(result.value.bytes);
|
|
1177
|
+
if (record.ownerId !== ownerId || result.value.expiresAt === undefined) {
|
|
1178
|
+
throw new Error('Location Store owner lease record is invalid.');
|
|
1179
|
+
}
|
|
1180
|
+
return {
|
|
1181
|
+
kind: 'found',
|
|
1182
|
+
token: { ownerId, leaseGeneration: BigInt(record.leaseGeneration) },
|
|
1183
|
+
leaseExpiresAt: result.value.expiresAt,
|
|
1184
|
+
storeNow: result.value.storeNow
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
async renewOwnerLease(token, leaseTtlMs, signal) {
|
|
1188
|
+
requireOwnerInput(token.ownerId, leaseTtlMs);
|
|
1189
|
+
const key = ownerKey(token.ownerId);
|
|
1190
|
+
const current = await this.provider.read(key, signal);
|
|
1191
|
+
if (current.kind === 'missing')
|
|
1192
|
+
return { kind: 'stale' };
|
|
1193
|
+
const record = decodeOwnerRecord(current.value.bytes);
|
|
1194
|
+
if (BigInt(record.leaseGeneration) !== token.leaseGeneration)
|
|
1195
|
+
return { kind: 'stale' };
|
|
1196
|
+
const result = await this.provider.write({
|
|
1197
|
+
conditions: [{ kind: 'version', key, expected: current.value.version }],
|
|
1198
|
+
mutations: [{
|
|
1199
|
+
kind: 'put',
|
|
1200
|
+
key,
|
|
1201
|
+
bytes: current.value.bytes,
|
|
1202
|
+
retentionMs: leaseTtlMs
|
|
1203
|
+
}]
|
|
1204
|
+
}, signal);
|
|
1205
|
+
if (result.kind === 'conflict')
|
|
1206
|
+
return { kind: 'stale' };
|
|
1207
|
+
return {
|
|
1208
|
+
kind: 'renewed',
|
|
1209
|
+
leaseExpiresAt: new Date(result.storeNow.getTime() + leaseTtlMs),
|
|
1210
|
+
storeNow: result.storeNow
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
async releaseOwnerLease(token, signal) {
|
|
1214
|
+
const key = ownerKey(token.ownerId);
|
|
1215
|
+
const current = await this.provider.read(key, signal);
|
|
1216
|
+
if (current.kind === 'missing')
|
|
1217
|
+
return 'stale';
|
|
1218
|
+
const record = decodeOwnerRecord(current.value.bytes);
|
|
1219
|
+
if (BigInt(record.leaseGeneration) !== token.leaseGeneration)
|
|
1220
|
+
return 'stale';
|
|
1221
|
+
const result = await this.provider.write({
|
|
1222
|
+
conditions: [{ kind: 'version', key, expected: current.value.version }],
|
|
1223
|
+
mutations: [{ kind: 'delete', key }]
|
|
1224
|
+
}, signal);
|
|
1225
|
+
return result.kind === 'applied' ? 'released' : 'stale';
|
|
1226
|
+
}
|
|
1227
|
+
async updateMeshNode(descriptor, intent, signal) {
|
|
1228
|
+
const leaseKey = ownerKey(descriptor.ownerId);
|
|
1229
|
+
const rowKey = meshKey(descriptor.meshName, descriptor.rid);
|
|
1230
|
+
const [lease, current] = await Promise.all([
|
|
1231
|
+
this.provider.read(leaseKey, signal),
|
|
1232
|
+
this.provider.read(rowKey, signal)
|
|
1233
|
+
]);
|
|
1234
|
+
if (lease.kind === 'missing')
|
|
1235
|
+
return rejected(lease.storeNow);
|
|
1236
|
+
if (liveOwnerLeaseGeneration(lease) !== descriptor.leaseGeneration) {
|
|
1237
|
+
return rejected(lease.value.storeNow);
|
|
1238
|
+
}
|
|
1239
|
+
let generation = 1n;
|
|
1240
|
+
let rowCondition = { kind: 'missing', key: rowKey };
|
|
1241
|
+
if (current.kind === 'found') {
|
|
1242
|
+
const record = decodeCanonicalDescriptorRecord(current.value.bytes);
|
|
1243
|
+
const stored = reviveMeshDescriptor(record.descriptor);
|
|
1244
|
+
generation = descriptorStoreGeneration(record, current.value.version.value);
|
|
1245
|
+
if (sameMeshDescriptor(stored, descriptor)) {
|
|
1246
|
+
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: current.value.storeNow };
|
|
1247
|
+
}
|
|
1248
|
+
const currentLease = await this.provider.read(ownerKey(stored.ownerId), signal);
|
|
1249
|
+
const currentLeaseGeneration = liveOwnerLeaseGeneration(currentLease);
|
|
1250
|
+
const takeover = canTakeOverStoredLocation(intent, stored.ownerId, stored.leaseGeneration, descriptor.ownerId, descriptor.leaseGeneration, currentLeaseGeneration);
|
|
1251
|
+
const renew = intent === 2
|
|
1252
|
+
&& stored.ownerId === descriptor.ownerId
|
|
1253
|
+
&& stored.leaseGeneration === descriptor.leaseGeneration
|
|
1254
|
+
&& stored.lifecycleGeneration === descriptor.lifecycleGeneration
|
|
1255
|
+
&& descriptor.descriptorRevision > stored.descriptorRevision;
|
|
1256
|
+
if (!takeover && !renew) {
|
|
1257
|
+
return {
|
|
1258
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1259
|
+
generation,
|
|
1260
|
+
updatedAt: current.value.storeNow
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
if (takeover)
|
|
1264
|
+
generation += 1n;
|
|
1265
|
+
rowCondition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1266
|
+
}
|
|
1267
|
+
else if (intent === 2) {
|
|
1268
|
+
return rejected(lease.value.storeNow);
|
|
1269
|
+
}
|
|
1270
|
+
const result = await this.provider.write({
|
|
1271
|
+
conditions: [
|
|
1272
|
+
{ kind: 'version', key: leaseKey, expected: lease.value.version },
|
|
1273
|
+
rowCondition
|
|
1274
|
+
],
|
|
1275
|
+
mutations: [{
|
|
1276
|
+
kind: 'put',
|
|
1277
|
+
key: rowKey,
|
|
1278
|
+
bytes: encodeCanonicalDescriptorRecord(generation, persistMeshDescriptor(descriptor))
|
|
1279
|
+
}]
|
|
1280
|
+
}, signal);
|
|
1281
|
+
if (result.kind === 'conflict')
|
|
1282
|
+
return rejected(result.storeNow);
|
|
1283
|
+
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow };
|
|
1284
|
+
}
|
|
1285
|
+
async removeMeshNode(key, owner, signal) {
|
|
1286
|
+
const rowKey = meshKey(key.meshName, key.rid);
|
|
1287
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1288
|
+
if (current.kind === 'missing')
|
|
1289
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1290
|
+
const descriptor = reviveMeshDescriptor(decodeCanonicalDescriptorRecord(current.value.bytes).descriptor);
|
|
1291
|
+
if (descriptor.ownerId !== owner.ownerId
|
|
1292
|
+
|| descriptor.leaseGeneration !== owner.leaseGeneration) {
|
|
1293
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1294
|
+
}
|
|
1295
|
+
const result = await this.provider.write({
|
|
1296
|
+
conditions: [{ kind: 'version', key: rowKey, expected: current.value.version }],
|
|
1297
|
+
mutations: [{ kind: 'delete', key: rowKey }]
|
|
1298
|
+
}, signal);
|
|
1299
|
+
return result.kind === 'applied' ? Locations_1.ZLinkLocationWriteStatus.Stored : Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1300
|
+
}
|
|
1301
|
+
async listMeshNodes(meshName, page = {}, signal) {
|
|
1302
|
+
const result = await this.provider.scan({
|
|
1303
|
+
prefix: meshPrefix(meshName),
|
|
1304
|
+
cursor: page.continuationToken === undefined
|
|
1305
|
+
? undefined
|
|
1306
|
+
: { value: page.continuationToken },
|
|
1307
|
+
limit: page.pageSize ?? 100
|
|
1308
|
+
}, signal);
|
|
1309
|
+
if (result.kind === 'expired') {
|
|
1310
|
+
throw new Error('Location Store scan snapshot expired.');
|
|
1311
|
+
}
|
|
1312
|
+
const items = await Promise.all(result.value.items.map(async (item) => {
|
|
1313
|
+
const descriptor = reviveMeshDescriptor(decodeCanonicalDescriptorRecord(item.value.bytes).descriptor);
|
|
1314
|
+
const capacityRead = await this.provider.read(capacityKey(descriptor.meshName, String(descriptor.rid)), signal);
|
|
1315
|
+
const capacity = capacityRead.kind === 'missing'
|
|
1316
|
+
? emptyCapacityRecord()
|
|
1317
|
+
: decodeJson(capacityRead.value.bytes);
|
|
1318
|
+
return {
|
|
1319
|
+
...descriptor,
|
|
1320
|
+
populationCapacity: {
|
|
1321
|
+
actors: {
|
|
1322
|
+
...descriptor.populationCapacity.actors,
|
|
1323
|
+
active: capacity.active.actors,
|
|
1324
|
+
reserved: capacity.pending.actors
|
|
1325
|
+
},
|
|
1326
|
+
spots: {
|
|
1327
|
+
...descriptor.populationCapacity.spots,
|
|
1328
|
+
active: capacity.active.spots,
|
|
1329
|
+
reserved: capacity.pending.spots
|
|
1330
|
+
},
|
|
1331
|
+
spotTypes: descriptor.populationCapacity.spotTypes.map(value => {
|
|
1332
|
+
const key = capacityTypeKey({
|
|
1333
|
+
objectKind: value.objectKind,
|
|
1334
|
+
stableType: value.stableType,
|
|
1335
|
+
count: 0
|
|
1336
|
+
});
|
|
1337
|
+
return {
|
|
1338
|
+
...value,
|
|
1339
|
+
active: capacity.active.spotTypes[key] ?? 0,
|
|
1340
|
+
reserved: capacity.pending.spotTypes[key] ?? 0
|
|
1341
|
+
};
|
|
1342
|
+
})
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
}));
|
|
1346
|
+
return {
|
|
1347
|
+
items,
|
|
1348
|
+
continuationToken: result.value.nextCursor?.value
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
async updateClientServer(descriptor, intent, signal) {
|
|
1352
|
+
validateClientServerDescriptor(descriptor);
|
|
1353
|
+
const normalized = reviveClientServerDescriptor(descriptor);
|
|
1354
|
+
return this.updateDescriptor(clientServerKey(normalized.channelName, normalized.serverRid), normalized, intent, reviveClientServerDescriptor, sameClientServerDescriptor, canRenewClientServer, signal);
|
|
1355
|
+
}
|
|
1356
|
+
async removeClientServer(key, owner, signal) {
|
|
1357
|
+
return this.removeDescriptor(clientServerKey(key.channelName, key.serverRid), owner, reviveClientServerDescriptor, signal);
|
|
1358
|
+
}
|
|
1359
|
+
async listClientServers(channelName, page = {}, signal) {
|
|
1360
|
+
return this.listDescriptors(clientServerPrefix(channelName), page, reviveClientServerDescriptor, signal);
|
|
1361
|
+
}
|
|
1362
|
+
async updateFanoutPublisher(descriptor, intent, signal) {
|
|
1363
|
+
validateFanoutPublisherDescriptor(descriptor);
|
|
1364
|
+
const normalized = reviveFanoutDescriptor(descriptor);
|
|
1365
|
+
return this.updateDescriptor(fanoutKey(normalized.channelName, normalized.publisherRid), normalized, intent, reviveFanoutDescriptor, sameFanoutDescriptor, canRenewFanout, signal);
|
|
1366
|
+
}
|
|
1367
|
+
async removeFanoutPublisher(key, owner, signal) {
|
|
1368
|
+
return this.removeDescriptor(fanoutKey(key.channelName, key.publisherRid), owner, reviveFanoutDescriptor, signal);
|
|
1369
|
+
}
|
|
1370
|
+
async listFanoutPublishers(channelName, page = {}, signal) {
|
|
1371
|
+
return this.listDescriptors(fanoutPrefix(channelName), page, reviveFanoutDescriptor, signal);
|
|
1372
|
+
}
|
|
1373
|
+
async updateSpot(location, intent, signal) {
|
|
1374
|
+
return this.updateOwnedLocation(spotKey(location.meshName, String(location.spotId)), normalizeSpot(location), intent, signal);
|
|
1375
|
+
}
|
|
1376
|
+
async removeSpot(key, owner, signal) {
|
|
1377
|
+
return this.removeOwnedLocation(spotKey(key.meshName, String(key.spotId)), owner, signal);
|
|
1378
|
+
}
|
|
1379
|
+
async resolveSpot(key, signal) {
|
|
1380
|
+
const value = await this.readOwnedLocation(spotKey(key.meshName, String(key.spotId)), signal);
|
|
1381
|
+
return value === undefined ? undefined : normalizeSpot(value);
|
|
1382
|
+
}
|
|
1383
|
+
async listSpots(filter, page = {}, signal) {
|
|
1384
|
+
return this.listOwnedLocations(`${PREFIX}spot:`, page, normalizeSpot, value => matchesSpot(value, filter), signal);
|
|
1385
|
+
}
|
|
1386
|
+
async updateActor(location, intent, signal) {
|
|
1387
|
+
return this.updateOwnedLocation(actorKey(location.meshName, location.actorId), normalizeActor(location), intent, signal);
|
|
1388
|
+
}
|
|
1389
|
+
async removeActor(key, owner, signal) {
|
|
1390
|
+
return this.removeOwnedLocation(actorKey(key.meshName, key.actorId), owner, signal);
|
|
1391
|
+
}
|
|
1392
|
+
async resolveActor(key, signal) {
|
|
1393
|
+
const value = await this.readOwnedLocation(actorKey(key.meshName, key.actorId), signal);
|
|
1394
|
+
return value === undefined ? undefined : normalizeActor(value);
|
|
1395
|
+
}
|
|
1396
|
+
async listActors(filter, page = {}, signal) {
|
|
1397
|
+
return this.listOwnedLocations(`${PREFIX}actor:`, page, normalizeActor, value => matchesActor(value, filter), signal);
|
|
1398
|
+
}
|
|
1399
|
+
async updateRoute(location, intent, signal) {
|
|
1400
|
+
const rowKey = routeKey(String(location.routeKind), location.routeKey);
|
|
1401
|
+
const normalized = normalizeRoute(location);
|
|
1402
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1403
|
+
let generation = 1n;
|
|
1404
|
+
let condition = { kind: 'missing', key: rowKey };
|
|
1405
|
+
if (current.kind === 'found') {
|
|
1406
|
+
const record = decodeJson(current.value.bytes);
|
|
1407
|
+
const stored = normalizeRoute(record.descriptor);
|
|
1408
|
+
generation = BigInt(record.generation);
|
|
1409
|
+
if (intent === 1) {
|
|
1410
|
+
const ownerLease = await this.provider.read(ownerKey(stored.ownerId), signal);
|
|
1411
|
+
if (ownerLease.kind === 'found') {
|
|
1412
|
+
return {
|
|
1413
|
+
status: Locations_1.ZLinkLocationWriteStatus.RejectedConflict,
|
|
1414
|
+
generation,
|
|
1415
|
+
updatedAt: current.value.storeNow
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
else if (intent === 2
|
|
1420
|
+
&& (stored.ownerId !== normalized.ownerId
|
|
1421
|
+
|| normalized.generation !== 0n && stored.generation !== normalized.generation)) {
|
|
1422
|
+
return {
|
|
1423
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1424
|
+
generation,
|
|
1425
|
+
updatedAt: current.value.storeNow
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
if (intent !== 2)
|
|
1429
|
+
generation += 1n;
|
|
1430
|
+
condition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1431
|
+
}
|
|
1432
|
+
else if (intent === 2) {
|
|
1433
|
+
return rejected(new Date());
|
|
1434
|
+
}
|
|
1435
|
+
const stored = { ...normalized, generation };
|
|
1436
|
+
const result = await this.provider.write({
|
|
1437
|
+
conditions: [condition],
|
|
1438
|
+
mutations: [{
|
|
1439
|
+
kind: 'put',
|
|
1440
|
+
key: rowKey,
|
|
1441
|
+
bytes: encodeJson({
|
|
1442
|
+
generation: generation.toString(),
|
|
1443
|
+
descriptor: stored
|
|
1444
|
+
})
|
|
1445
|
+
}]
|
|
1446
|
+
}, signal);
|
|
1447
|
+
return result.kind === 'applied'
|
|
1448
|
+
? { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow }
|
|
1449
|
+
: rejected(result.storeNow);
|
|
1450
|
+
}
|
|
1451
|
+
async removeRoute(key, owner, signal) {
|
|
1452
|
+
const rowKey = routeKey(String(key.routeKind), key.routeKey);
|
|
1453
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1454
|
+
if (current.kind === 'missing')
|
|
1455
|
+
return rejected(current.storeNow);
|
|
1456
|
+
const record = decodeJson(current.value.bytes);
|
|
1457
|
+
if (record.descriptor.ownerId !== owner.ownerId
|
|
1458
|
+
|| record.descriptor.generation !== owner.leaseGeneration) {
|
|
1459
|
+
return {
|
|
1460
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1461
|
+
generation: BigInt(record.generation),
|
|
1462
|
+
updatedAt: current.value.storeNow
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
const result = await this.provider.write({
|
|
1466
|
+
conditions: [{ kind: 'version', key: rowKey, expected: current.value.version }],
|
|
1467
|
+
mutations: [{ kind: 'delete', key: rowKey }]
|
|
1468
|
+
}, signal);
|
|
1469
|
+
return result.kind === 'applied'
|
|
1470
|
+
? { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation: BigInt(record.generation), updatedAt: result.storeNow }
|
|
1471
|
+
: rejected(result.storeNow);
|
|
1472
|
+
}
|
|
1473
|
+
async resolveRoute(key, signal) {
|
|
1474
|
+
const value = await this.readOwnedLocation(routeKey(String(key.routeKind), key.routeKey), signal);
|
|
1475
|
+
return value === undefined ? undefined : normalizeRoute(value);
|
|
1476
|
+
}
|
|
1477
|
+
async listRoutes(filter, page = {}, signal) {
|
|
1478
|
+
return this.listOwnedLocations(`${PREFIX}route:`, page, normalizeRoute, value => matchesRoute(value, filter), signal);
|
|
1479
|
+
}
|
|
1480
|
+
async removeAllByOwner(owner, signal) {
|
|
1481
|
+
const leaseKey = ownerKey(owner.ownerId);
|
|
1482
|
+
const lease = await this.provider.read(leaseKey, signal);
|
|
1483
|
+
if (liveOwnerLeaseGeneration(lease) !== owner.leaseGeneration)
|
|
1484
|
+
return 0n;
|
|
1485
|
+
let removed = await this.removeOwnedAuthorities(owner, signal);
|
|
1486
|
+
const candidates = [];
|
|
1487
|
+
for (const prefix of [
|
|
1488
|
+
'mesh-node\0',
|
|
1489
|
+
'client-server\0',
|
|
1490
|
+
'fanout-publisher\0',
|
|
1491
|
+
`${PREFIX}spot:`,
|
|
1492
|
+
`${PREFIX}actor:`,
|
|
1493
|
+
`${PREFIX}route:`
|
|
1494
|
+
]) {
|
|
1495
|
+
for (;;) {
|
|
1496
|
+
const prefixCandidates = [];
|
|
1497
|
+
let cursor;
|
|
1498
|
+
let expired = false;
|
|
1499
|
+
do {
|
|
1500
|
+
const result = await this.provider.scan({ prefix, cursor, limit: 1_000 }, signal);
|
|
1501
|
+
if (result.kind === 'expired') {
|
|
1502
|
+
expired = true;
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
for (const item of result.value.items) {
|
|
1506
|
+
prefixCandidates.push({ key: item.key, scanVersion: item.value.version });
|
|
1507
|
+
}
|
|
1508
|
+
cursor = result.value.nextCursor;
|
|
1509
|
+
} while (cursor !== undefined);
|
|
1510
|
+
if (expired) {
|
|
1511
|
+
// Snapshot scan expiry invalidates every page from this prefix.
|
|
1512
|
+
// Restart from its first page instead of silently leaving rows
|
|
1513
|
+
// behind after a partial scan.
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
candidates.push(...prefixCandidates);
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
for (let offset = 0; offset < candidates.length; offset += MAX_OWNER_CLEANUP_BATCH_ROWS) {
|
|
1521
|
+
removed += await this.removeOwnerCleanupBatch(owner, leaseKey, candidates.slice(offset, offset + MAX_OWNER_CLEANUP_BATCH_ROWS), signal);
|
|
1522
|
+
}
|
|
1523
|
+
return removed + await super.removeAllByOwner(owner);
|
|
1524
|
+
}
|
|
1525
|
+
async removeOwnedAuthorities(owner, signal) {
|
|
1526
|
+
const candidates = [];
|
|
1527
|
+
for (;;) {
|
|
1528
|
+
const pageCandidates = [];
|
|
1529
|
+
let cursor;
|
|
1530
|
+
let expired = false;
|
|
1531
|
+
do {
|
|
1532
|
+
const result = await this.provider.scan({
|
|
1533
|
+
prefix: AUTHORITY_PREIMAGE_PREFIX,
|
|
1534
|
+
cursor,
|
|
1535
|
+
limit: 1_000
|
|
1536
|
+
}, signal);
|
|
1537
|
+
if (result.kind === 'expired') {
|
|
1538
|
+
expired = true;
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
for (const item of result.value.items) {
|
|
1542
|
+
pageCandidates.push({ key: item.key, scanVersion: item.value.version });
|
|
1543
|
+
}
|
|
1544
|
+
cursor = result.value.nextCursor;
|
|
1545
|
+
} while (cursor !== undefined);
|
|
1546
|
+
if (expired) {
|
|
1547
|
+
candidates.length = 0;
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
candidates.push(...pageCandidates);
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1553
|
+
let removed = 0n;
|
|
1554
|
+
for (const candidate of candidates) {
|
|
1555
|
+
signal?.throwIfAborted();
|
|
1556
|
+
const current = await this.provider.read(candidate.key, signal);
|
|
1557
|
+
if (current.kind === 'missing'
|
|
1558
|
+
|| current.value.version.value !== candidate.scanVersion.value)
|
|
1559
|
+
continue;
|
|
1560
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
1561
|
+
if (record.snapshot.ownerId !== owner.ownerId
|
|
1562
|
+
|| record.snapshot.ownerLeaseGeneration !== owner.leaseGeneration
|
|
1563
|
+
|| record.aggregate !== undefined)
|
|
1564
|
+
continue;
|
|
1565
|
+
const encodedAuthority = authorityContractValueFromPreimage(candidate.key.value);
|
|
1566
|
+
const identity = (0, authority_key_codec_1.decodeAuthorityKey)({ value: encodedAuthority });
|
|
1567
|
+
if (record.snapshot.allocation.state === 'active') {
|
|
1568
|
+
const result = await this.compareExchangeAuthority({ value: encodedAuthority }, current.value.version, { kind: 'delete' }, signal);
|
|
1569
|
+
if (result.kind === 'deleted')
|
|
1570
|
+
removed += 1n;
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
if (record.reservationId === undefined)
|
|
1574
|
+
continue;
|
|
1575
|
+
const aborted = await this.abort({
|
|
1576
|
+
key: identity,
|
|
1577
|
+
reservationId: record.reservationId,
|
|
1578
|
+
expectedStoreVersion: current.value.version.value,
|
|
1579
|
+
target: {
|
|
1580
|
+
meshName: record.snapshot.allocation.descriptor.meshName,
|
|
1581
|
+
nodeRid: record.snapshot.allocation.descriptor.rid,
|
|
1582
|
+
nodeLifecycleGeneration: record.snapshot.allocation.descriptorLifecycleGeneration,
|
|
1583
|
+
owner: {
|
|
1584
|
+
ownerId: record.snapshot.ownerId,
|
|
1585
|
+
leaseGeneration: record.snapshot.ownerLeaseGeneration
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}, signal);
|
|
1589
|
+
if (aborted.kind === 'aborted')
|
|
1590
|
+
removed += 1n;
|
|
1591
|
+
}
|
|
1592
|
+
return removed;
|
|
1593
|
+
}
|
|
1594
|
+
async removeOwnerCleanupBatch(owner, leaseKey, candidates, signal) {
|
|
1595
|
+
if (candidates.length === 0)
|
|
1596
|
+
return 0n;
|
|
1597
|
+
const currentRows = await Promise.all(candidates.map(candidate => this.provider.read(candidate.key, signal)));
|
|
1598
|
+
const unchangedOwnedRows = [];
|
|
1599
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
1600
|
+
const candidate = candidates[index];
|
|
1601
|
+
const current = currentRows[index];
|
|
1602
|
+
if (current.kind === 'missing'
|
|
1603
|
+
|| current.value.version.value !== candidate.scanVersion.value) {
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1606
|
+
const record = decodeJson(current.value.bytes);
|
|
1607
|
+
const descriptor = record.descriptor;
|
|
1608
|
+
if (descriptor.ownerId !== owner.ownerId
|
|
1609
|
+
|| descriptorOwnerLeaseGeneration(descriptor) !== owner.leaseGeneration) {
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
unchangedOwnedRows.push({
|
|
1613
|
+
key: candidate.key,
|
|
1614
|
+
scanVersion: current.value.version
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
if (unchangedOwnedRows.length === 0)
|
|
1618
|
+
return 0n;
|
|
1619
|
+
const currentLease = await this.provider.read(leaseKey, signal);
|
|
1620
|
+
if (currentLease.kind === 'missing'
|
|
1621
|
+
|| liveOwnerLeaseGeneration(currentLease) !== owner.leaseGeneration) {
|
|
1622
|
+
return 0n;
|
|
1623
|
+
}
|
|
1624
|
+
const result = await this.provider.write({
|
|
1625
|
+
conditions: [
|
|
1626
|
+
{ kind: 'version', key: leaseKey, expected: currentLease.value.version },
|
|
1627
|
+
...unchangedOwnedRows.map(row => ({
|
|
1628
|
+
kind: 'version',
|
|
1629
|
+
key: row.key,
|
|
1630
|
+
expected: row.scanVersion
|
|
1631
|
+
}))
|
|
1632
|
+
],
|
|
1633
|
+
mutations: unchangedOwnedRows.map(row => ({
|
|
1634
|
+
kind: 'delete',
|
|
1635
|
+
key: row.key
|
|
1636
|
+
}))
|
|
1637
|
+
}, signal);
|
|
1638
|
+
if (result.kind === 'applied')
|
|
1639
|
+
return BigInt(unchangedOwnedRows.length);
|
|
1640
|
+
// A row may have changed while the page was being prepared. Split the
|
|
1641
|
+
// bounded batch so unaffected rows can still be removed atomically, while
|
|
1642
|
+
// the changed row is discarded by the next version re-read. A changed
|
|
1643
|
+
// owner lease causes every recursive branch to stop at the fence above.
|
|
1644
|
+
if (unchangedOwnedRows.length === 1)
|
|
1645
|
+
return 0n;
|
|
1646
|
+
const midpoint = Math.ceil(unchangedOwnedRows.length / 2);
|
|
1647
|
+
const left = await this.removeOwnerCleanupBatch(owner, leaseKey, unchangedOwnedRows.slice(0, midpoint), signal);
|
|
1648
|
+
const right = await this.removeOwnerCleanupBatch(owner, leaseKey, unchangedOwnedRows.slice(midpoint), signal);
|
|
1649
|
+
return left + right;
|
|
1650
|
+
}
|
|
1651
|
+
async updateDescriptor(rowKey, descriptor, intent, revive, same, canRenew, signal) {
|
|
1652
|
+
const leaseKey = ownerKey(descriptor.ownerId);
|
|
1653
|
+
const [lease, current] = await Promise.all([
|
|
1654
|
+
this.provider.read(leaseKey, signal),
|
|
1655
|
+
this.provider.read(rowKey, signal)
|
|
1656
|
+
]);
|
|
1657
|
+
if (lease.kind === 'missing')
|
|
1658
|
+
return rejected(lease.storeNow);
|
|
1659
|
+
if (liveOwnerLeaseGeneration(lease) !== descriptor.leaseGeneration) {
|
|
1660
|
+
return rejected(lease.value.storeNow);
|
|
1661
|
+
}
|
|
1662
|
+
let generation = 1n;
|
|
1663
|
+
let rowCondition = { kind: 'missing', key: rowKey };
|
|
1664
|
+
if (current.kind === 'found') {
|
|
1665
|
+
const record = decodeCanonicalDescriptorRecord(current.value.bytes);
|
|
1666
|
+
const stored = revive(record.descriptor);
|
|
1667
|
+
generation = descriptorStoreGeneration(record, current.value.version.value);
|
|
1668
|
+
if (same(stored, descriptor)) {
|
|
1669
|
+
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: current.value.storeNow };
|
|
1670
|
+
}
|
|
1671
|
+
const currentLease = await this.provider.read(ownerKey(stored.ownerId), signal);
|
|
1672
|
+
const currentLeaseGeneration = liveOwnerLeaseGeneration(currentLease);
|
|
1673
|
+
const takeover = canTakeOverStoredLocation(intent, stored.ownerId, stored.leaseGeneration, descriptor.ownerId, descriptor.leaseGeneration, currentLeaseGeneration);
|
|
1674
|
+
const renew = intent === 2 && canRenew(stored, descriptor);
|
|
1675
|
+
if (!takeover && !renew) {
|
|
1676
|
+
return {
|
|
1677
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1678
|
+
generation,
|
|
1679
|
+
updatedAt: current.value.storeNow
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
if (takeover)
|
|
1683
|
+
generation += 1n;
|
|
1684
|
+
rowCondition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1685
|
+
}
|
|
1686
|
+
else if (intent === 2) {
|
|
1687
|
+
return rejected(lease.value.storeNow);
|
|
1688
|
+
}
|
|
1689
|
+
const result = await this.provider.write({
|
|
1690
|
+
conditions: [
|
|
1691
|
+
{ kind: 'version', key: leaseKey, expected: lease.value.version },
|
|
1692
|
+
rowCondition
|
|
1693
|
+
],
|
|
1694
|
+
mutations: [{
|
|
1695
|
+
kind: 'put',
|
|
1696
|
+
key: rowKey,
|
|
1697
|
+
bytes: encodeCanonicalDescriptorRecord(generation, descriptor)
|
|
1698
|
+
}]
|
|
1699
|
+
}, signal);
|
|
1700
|
+
if (result.kind === 'conflict')
|
|
1701
|
+
return rejected(result.storeNow);
|
|
1702
|
+
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow };
|
|
1703
|
+
}
|
|
1704
|
+
async removeDescriptor(rowKey, owner, revive, signal) {
|
|
1705
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1706
|
+
if (current.kind === 'missing')
|
|
1707
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1708
|
+
const descriptor = revive(decodeCanonicalDescriptorRecord(current.value.bytes).descriptor);
|
|
1709
|
+
if (descriptor.ownerId !== owner.ownerId
|
|
1710
|
+
|| descriptor.leaseGeneration !== owner.leaseGeneration) {
|
|
1711
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1712
|
+
}
|
|
1713
|
+
const result = await this.provider.write({
|
|
1714
|
+
conditions: [{ kind: 'version', key: rowKey, expected: current.value.version }],
|
|
1715
|
+
mutations: [{ kind: 'delete', key: rowKey }]
|
|
1716
|
+
}, signal);
|
|
1717
|
+
return result.kind === 'applied' ? Locations_1.ZLinkLocationWriteStatus.Stored : Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1718
|
+
}
|
|
1719
|
+
async listDescriptors(prefix, page, revive, signal) {
|
|
1720
|
+
const result = await this.provider.scan({
|
|
1721
|
+
prefix,
|
|
1722
|
+
cursor: page.continuationToken === undefined
|
|
1723
|
+
? undefined
|
|
1724
|
+
: { value: page.continuationToken },
|
|
1725
|
+
limit: page.pageSize ?? 100
|
|
1726
|
+
}, signal);
|
|
1727
|
+
if (result.kind === 'expired') {
|
|
1728
|
+
throw new Error('Location Store scan snapshot expired.');
|
|
1729
|
+
}
|
|
1730
|
+
return {
|
|
1731
|
+
items: result.value.items.map(item => revive(decodeCanonicalDescriptorRecord(item.value.bytes).descriptor)),
|
|
1732
|
+
continuationToken: result.value.nextCursor?.value
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
async updateOwnedLocation(rowKey, location, intent, signal) {
|
|
1736
|
+
const leaseKey = ownerKey(location.ownerId);
|
|
1737
|
+
const [lease, current] = await Promise.all([
|
|
1738
|
+
this.provider.read(leaseKey, signal),
|
|
1739
|
+
this.provider.read(rowKey, signal)
|
|
1740
|
+
]);
|
|
1741
|
+
if (lease.kind === 'missing')
|
|
1742
|
+
return rejected(lease.storeNow);
|
|
1743
|
+
if (liveOwnerLeaseGeneration(lease) !== locationOwnerGeneration(location)) {
|
|
1744
|
+
return rejected(lease.value.storeNow);
|
|
1745
|
+
}
|
|
1746
|
+
let generation = 1n;
|
|
1747
|
+
let rowCondition = { kind: 'missing', key: rowKey };
|
|
1748
|
+
if (current.kind === 'found') {
|
|
1749
|
+
const record = decodeJson(current.value.bytes);
|
|
1750
|
+
generation = BigInt(record.generation);
|
|
1751
|
+
const stored = record.descriptor;
|
|
1752
|
+
if (intent === 2) {
|
|
1753
|
+
if (stored.ownerId !== location.ownerId
|
|
1754
|
+
|| locationOwnerGeneration(stored) !== locationOwnerGeneration(location)) {
|
|
1755
|
+
return {
|
|
1756
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1757
|
+
generation,
|
|
1758
|
+
updatedAt: current.value.storeNow
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
else {
|
|
1763
|
+
const currentLease = await this.provider.read(ownerKey(stored.ownerId), signal);
|
|
1764
|
+
const currentLeaseGeneration = liveOwnerLeaseGeneration(currentLease);
|
|
1765
|
+
const takeover = canTakeOverStoredLocation(intent, stored.ownerId, locationOwnerGeneration(stored), location.ownerId, locationOwnerGeneration(location), currentLeaseGeneration);
|
|
1766
|
+
if (!takeover) {
|
|
1767
|
+
return {
|
|
1768
|
+
status: Locations_1.ZLinkLocationWriteStatus.RejectedConflict,
|
|
1769
|
+
generation,
|
|
1770
|
+
updatedAt: current.value.storeNow
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
generation += 1n;
|
|
1774
|
+
}
|
|
1775
|
+
rowCondition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1776
|
+
}
|
|
1777
|
+
else if (intent === 2) {
|
|
1778
|
+
return rejected(lease.value.storeNow);
|
|
1779
|
+
}
|
|
1780
|
+
const result = await this.provider.write({
|
|
1781
|
+
conditions: [
|
|
1782
|
+
{ kind: 'version', key: leaseKey, expected: lease.value.version },
|
|
1783
|
+
rowCondition
|
|
1784
|
+
],
|
|
1785
|
+
mutations: [{
|
|
1786
|
+
kind: 'put',
|
|
1787
|
+
key: rowKey,
|
|
1788
|
+
bytes: encodeJson({
|
|
1789
|
+
generation: generation.toString(),
|
|
1790
|
+
descriptor: location
|
|
1791
|
+
})
|
|
1792
|
+
}]
|
|
1793
|
+
}, signal);
|
|
1794
|
+
return result.kind === 'applied'
|
|
1795
|
+
? { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow }
|
|
1796
|
+
: rejected(result.storeNow);
|
|
1797
|
+
}
|
|
1798
|
+
async removeOwnedLocation(rowKey, owner, signal) {
|
|
1799
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1800
|
+
if (current.kind === 'missing')
|
|
1801
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1802
|
+
const record = decodeJson(current.value.bytes);
|
|
1803
|
+
const descriptor = record.descriptor;
|
|
1804
|
+
const leaseGeneration = descriptor.leaseGeneration ?? BigInt(record.generation);
|
|
1805
|
+
if (descriptor.ownerId !== owner.ownerId
|
|
1806
|
+
|| leaseGeneration !== owner.leaseGeneration) {
|
|
1807
|
+
return Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1808
|
+
}
|
|
1809
|
+
const result = await this.provider.write({
|
|
1810
|
+
conditions: [{ kind: 'version', key: rowKey, expected: current.value.version }],
|
|
1811
|
+
mutations: [{ kind: 'delete', key: rowKey }]
|
|
1812
|
+
}, signal);
|
|
1813
|
+
return result.kind === 'applied' ? Locations_1.ZLinkLocationWriteStatus.Stored : Locations_1.ZLinkLocationWriteStatus.IgnoredStale;
|
|
1814
|
+
}
|
|
1815
|
+
async readOwnedLocation(rowKey, signal) {
|
|
1816
|
+
const result = await this.provider.read(rowKey, signal);
|
|
1817
|
+
return result.kind === 'missing'
|
|
1818
|
+
? undefined
|
|
1819
|
+
: decodeJson(result.value.bytes).descriptor;
|
|
1820
|
+
}
|
|
1821
|
+
async listOwnedLocations(prefix, page, revive, matches, signal) {
|
|
1822
|
+
const requested = page.pageSize ?? 100;
|
|
1823
|
+
let cursor = page.continuationToken === undefined
|
|
1824
|
+
? undefined
|
|
1825
|
+
: { value: page.continuationToken };
|
|
1826
|
+
const items = [];
|
|
1827
|
+
do {
|
|
1828
|
+
const result = await this.provider.scan({
|
|
1829
|
+
prefix,
|
|
1830
|
+
cursor,
|
|
1831
|
+
limit: requested
|
|
1832
|
+
}, signal);
|
|
1833
|
+
if (result.kind === 'expired') {
|
|
1834
|
+
throw new Error('Location Store scan snapshot expired.');
|
|
1835
|
+
}
|
|
1836
|
+
for (const item of result.value.items) {
|
|
1837
|
+
const value = revive(decodeJson(item.value.bytes).descriptor);
|
|
1838
|
+
if (matches(value))
|
|
1839
|
+
items.push(value);
|
|
1840
|
+
if (items.length === requested) {
|
|
1841
|
+
return {
|
|
1842
|
+
items,
|
|
1843
|
+
continuationToken: result.value.nextCursor?.value
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
cursor = result.value.nextCursor;
|
|
1848
|
+
} while (cursor !== undefined);
|
|
1849
|
+
return { items };
|
|
1850
|
+
}
|
|
1851
|
+
async projectAuthority(record, version, storeNow, signal) {
|
|
1852
|
+
const marker = record.aggregate;
|
|
1853
|
+
if (marker === undefined) {
|
|
1854
|
+
return authoritySnapshot(record.snapshot, record.visibleStoreVersion === undefined
|
|
1855
|
+
? version
|
|
1856
|
+
: { value: record.visibleStoreVersion }, storeNow);
|
|
1857
|
+
}
|
|
1858
|
+
const aggregateRead = await this.provider.read(aggregateKey(aggregateFence(aggregateId(marker.aggregateId), marker.aggregateGeneration)), signal);
|
|
1859
|
+
if (aggregateRead.kind === 'missing') {
|
|
1860
|
+
throw new Error('Aggregate participant references a missing authority record.');
|
|
1861
|
+
}
|
|
1862
|
+
const aggregate = decodeJson(aggregateRead.value.bytes);
|
|
1863
|
+
if (aggregate.state !== 'committed') {
|
|
1864
|
+
return authoritySnapshot(record.snapshot, { value: marker.expectedStoreVersion }, storeNow);
|
|
1865
|
+
}
|
|
1866
|
+
const fence = aggregateFence(aggregateId(marker.aggregateId), marker.aggregateGeneration);
|
|
1867
|
+
const payload = await requireProviderBytes(this.provider, aggregateParticipantPayloadKey(fence, marker.index), signal);
|
|
1868
|
+
if (sha256Hex(payload) !== marker.authorityPayloadSha256) {
|
|
1869
|
+
throw new Error('Committed aggregate participant payload checksum is invalid.');
|
|
1870
|
+
}
|
|
1871
|
+
const projected = marker.ownerTransition === 'newOwner'
|
|
1872
|
+
? {
|
|
1873
|
+
payload: Buffer.from(payload),
|
|
1874
|
+
objectGeneration: record.snapshot.objectGeneration,
|
|
1875
|
+
authorityOwnerGeneration: marker.targetAuthorityOwnerGeneration,
|
|
1876
|
+
ownerId: aggregate.targetOwner.ownerId,
|
|
1877
|
+
ownerLeaseGeneration: aggregate.targetOwner.leaseGeneration,
|
|
1878
|
+
allocation: {
|
|
1879
|
+
...record.snapshot.allocation,
|
|
1880
|
+
descriptor: { ...aggregate.targetDescriptor },
|
|
1881
|
+
descriptorLifecycleGeneration: aggregate.targetDescriptorLifecycleGeneration,
|
|
1882
|
+
capacity: cloneCapacity(record.snapshot.allocation.capacity)
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
: {
|
|
1886
|
+
...record.snapshot,
|
|
1887
|
+
payload: Buffer.from(payload)
|
|
1888
|
+
};
|
|
1889
|
+
return authoritySnapshot(projected, version, storeNow);
|
|
1890
|
+
}
|
|
1891
|
+
async normalizeCommittedAggregate(fence, aggregate, signal) {
|
|
1892
|
+
const entries = await this.aggregateInventory.read(fence, Buffer.from(aggregate.inventoryDigest), signal);
|
|
1893
|
+
await parallelForEach(entries, 64, async (entry, index) => {
|
|
1894
|
+
const rowKey = authorityKey(entry.authorityKey);
|
|
1895
|
+
for (;;) {
|
|
1896
|
+
const current = await this.provider.read(rowKey, signal);
|
|
1897
|
+
if (current.kind === 'missing') {
|
|
1898
|
+
throw new Error('Committed aggregate participant authority is missing.');
|
|
1899
|
+
}
|
|
1900
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
1901
|
+
if (record.aggregate === undefined) {
|
|
1902
|
+
if (!Buffer.from(record.snapshot.payload).equals(Buffer.from(await requireProviderBytes(this.provider, aggregateParticipantPayloadKey(fence, index), signal)))) {
|
|
1903
|
+
throw new Error('Normalized aggregate participant payload changed.');
|
|
1904
|
+
}
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (!sameAggregateMarkerEntry(record.aggregate, fence, index, entry)) {
|
|
1908
|
+
throw new Error('Committed aggregate participant fence changed.');
|
|
1909
|
+
}
|
|
1910
|
+
const payload = await requireProviderBytes(this.provider, aggregateParticipantPayloadKey(fence, index), signal);
|
|
1911
|
+
if (sha256Hex(payload) !== entry.authorityPayloadSha256) {
|
|
1912
|
+
throw new Error('Committed aggregate participant payload checksum is invalid.');
|
|
1913
|
+
}
|
|
1914
|
+
const snapshot = record.aggregate.ownerTransition === 'newOwner'
|
|
1915
|
+
? {
|
|
1916
|
+
payload: Buffer.from(payload),
|
|
1917
|
+
objectGeneration: record.snapshot.objectGeneration,
|
|
1918
|
+
authorityOwnerGeneration: record.aggregate.targetAuthorityOwnerGeneration,
|
|
1919
|
+
ownerId: aggregate.targetOwner.ownerId,
|
|
1920
|
+
ownerLeaseGeneration: aggregate.targetOwner.leaseGeneration,
|
|
1921
|
+
allocation: {
|
|
1922
|
+
...record.snapshot.allocation,
|
|
1923
|
+
descriptor: { ...aggregate.targetDescriptor },
|
|
1924
|
+
descriptorLifecycleGeneration: aggregate.targetDescriptorLifecycleGeneration,
|
|
1925
|
+
capacity: cloneCapacity(record.snapshot.allocation.capacity)
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
: {
|
|
1929
|
+
...record.snapshot,
|
|
1930
|
+
payload: Buffer.from(payload)
|
|
1931
|
+
};
|
|
1932
|
+
const normalized = {
|
|
1933
|
+
...record,
|
|
1934
|
+
snapshot,
|
|
1935
|
+
aggregate: undefined,
|
|
1936
|
+
visibleStoreVersion: undefined
|
|
1937
|
+
};
|
|
1938
|
+
const result = await this.provider.write({
|
|
1939
|
+
conditions: [{
|
|
1940
|
+
kind: 'version',
|
|
1941
|
+
key: rowKey,
|
|
1942
|
+
expected: current.value.version
|
|
1943
|
+
}],
|
|
1944
|
+
mutations: [{
|
|
1945
|
+
kind: 'put',
|
|
1946
|
+
key: rowKey,
|
|
1947
|
+
bytes: encodeAuthorityRecord(normalized)
|
|
1948
|
+
}]
|
|
1949
|
+
}, signal);
|
|
1950
|
+
if (result.kind === 'applied')
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
async putImmutable(key, bytes, signal) {
|
|
1956
|
+
if (bytes.byteLength > 1024 * 1024) {
|
|
1957
|
+
throw new RangeError('Aggregate participant staging value exceeds 1 MiB.');
|
|
1958
|
+
}
|
|
1959
|
+
const result = await this.provider.write({
|
|
1960
|
+
conditions: [{ kind: 'missing', key }],
|
|
1961
|
+
mutations: [{ kind: 'put', key, bytes: Buffer.from(bytes) }]
|
|
1962
|
+
}, signal);
|
|
1963
|
+
if (result.kind === 'applied')
|
|
1964
|
+
return;
|
|
1965
|
+
const current = await this.provider.read(key, signal);
|
|
1966
|
+
if (current.kind !== 'found'
|
|
1967
|
+
|| !Buffer.from(current.value.bytes).equals(Buffer.from(bytes))) {
|
|
1968
|
+
throw new Error(`Immutable aggregate participant value changed: ${key.value}.`);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
async abortAggregateStaging(fence, signal) {
|
|
1972
|
+
const key = aggregateKey(fence);
|
|
1973
|
+
for (;;) {
|
|
1974
|
+
const current = await this.provider.read(key, signal);
|
|
1975
|
+
if (current.kind === 'missing')
|
|
1976
|
+
return;
|
|
1977
|
+
const record = decodeJson(current.value.bytes);
|
|
1978
|
+
if (record.state === 'aborted') {
|
|
1979
|
+
await this.clearAggregateMarkersByScan(fence, signal);
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
if (record.state !== 'staging')
|
|
1983
|
+
return;
|
|
1984
|
+
const result = await this.provider.write({
|
|
1985
|
+
conditions: [{ kind: 'version', key, expected: current.value.version }],
|
|
1986
|
+
mutations: [{
|
|
1987
|
+
kind: 'put',
|
|
1988
|
+
key,
|
|
1989
|
+
bytes: encodeJson({ ...record, state: 'aborted' })
|
|
1990
|
+
}]
|
|
1991
|
+
}, signal);
|
|
1992
|
+
if (result.kind === 'applied') {
|
|
1993
|
+
await this.clearAggregateMarkersByScan(fence, signal);
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
async clearAggregateMarkers(fence, installed, signal) {
|
|
1999
|
+
await parallelForEach(installed, 64, async (value) => {
|
|
2000
|
+
await this.clearAggregateMarker(fence, value.key, signal);
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
async clearAggregateMarkersByScan(fence, signal) {
|
|
2004
|
+
let cursor;
|
|
2005
|
+
for (;;) {
|
|
2006
|
+
const result = await this.provider.scan({
|
|
2007
|
+
prefix: AUTHORITY_PREIMAGE_PREFIX,
|
|
2008
|
+
cursor,
|
|
2009
|
+
limit: 1_000
|
|
2010
|
+
}, signal);
|
|
2011
|
+
if (result.kind === 'expired') {
|
|
2012
|
+
cursor = undefined;
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
const matching = result.value.items.filter(item => {
|
|
2016
|
+
const record = decodeAuthorityRecord(item.value.bytes);
|
|
2017
|
+
return record.aggregate?.aggregateId === fence.aggregateId.value
|
|
2018
|
+
&& record.aggregate.aggregateGeneration === fence.aggregateGeneration;
|
|
2019
|
+
});
|
|
2020
|
+
await parallelForEach(matching, 64, async (item) => {
|
|
2021
|
+
await this.clearAggregateMarker(fence, item.key, signal);
|
|
2022
|
+
});
|
|
2023
|
+
if (result.value.nextCursor === undefined)
|
|
2024
|
+
return;
|
|
2025
|
+
cursor = result.value.nextCursor;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
async clearAggregateMarker(fence, key, signal) {
|
|
2029
|
+
for (;;) {
|
|
2030
|
+
const current = await this.provider.read(key, signal);
|
|
2031
|
+
if (current.kind === 'missing')
|
|
2032
|
+
return;
|
|
2033
|
+
const record = decodeAuthorityRecord(current.value.bytes);
|
|
2034
|
+
if (record.aggregate === undefined
|
|
2035
|
+
|| record.aggregate.aggregateId !== fence.aggregateId.value
|
|
2036
|
+
|| record.aggregate.aggregateGeneration !== fence.aggregateGeneration) {
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
const result = await this.provider.write({
|
|
2040
|
+
conditions: [{ kind: 'version', key, expected: current.value.version }],
|
|
2041
|
+
mutations: [{
|
|
2042
|
+
kind: 'put',
|
|
2043
|
+
key,
|
|
2044
|
+
bytes: encodeAuthorityRecord({ ...record, aggregate: undefined })
|
|
2045
|
+
}]
|
|
2046
|
+
}, signal);
|
|
2047
|
+
if (result.kind === 'applied')
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
exports.ZLinkLocationStoreRepository = ZLinkLocationStoreRepository;
|
|
2053
|
+
const AMBIGUOUS_WRITE_RECONCILIATION_TIMEOUT_MS = 5_000;
|
|
2054
|
+
/**
|
|
2055
|
+
* Keeps provider failures private while preserving the opaque Store contract.
|
|
2056
|
+
*
|
|
2057
|
+
* A write response can be lost after the provider applied the atomic batch.
|
|
2058
|
+
* The Framework confirms every mutation by an independent exact read. A put
|
|
2059
|
+
* must retain the requested bytes and a version different from the conditioned
|
|
2060
|
+
* version; a delete must remain absent. Any mismatch is a normal conflict, so
|
|
2061
|
+
* the domain repository can reread its authoritative record and classify the
|
|
2062
|
+
* operation without exposing a provider-specific retry API.
|
|
2063
|
+
*/
|
|
2064
|
+
class AmbiguousWriteReconcilingLocationStore {
|
|
2065
|
+
inner;
|
|
2066
|
+
constructor(inner) {
|
|
2067
|
+
this.inner = inner;
|
|
2068
|
+
}
|
|
2069
|
+
read(key, signal) {
|
|
2070
|
+
return this.inner.read(key, signal);
|
|
2071
|
+
}
|
|
2072
|
+
scan(request, signal) {
|
|
2073
|
+
return this.inner.scan(request, signal);
|
|
2074
|
+
}
|
|
2075
|
+
async write(request, signal) {
|
|
2076
|
+
try {
|
|
2077
|
+
return await this.inner.write(request, signal);
|
|
2078
|
+
}
|
|
2079
|
+
catch (failure) {
|
|
2080
|
+
try {
|
|
2081
|
+
return await this.reconcile(request);
|
|
2082
|
+
}
|
|
2083
|
+
catch {
|
|
2084
|
+
throw failure;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
async reconcile(request) {
|
|
2089
|
+
if (request.mutations.length === 0) {
|
|
2090
|
+
throw new Error('Cannot reconcile an opaque Store write without mutations.');
|
|
2091
|
+
}
|
|
2092
|
+
const reconciliationSignal = AbortSignal.timeout(AMBIGUOUS_WRITE_RECONCILIATION_TIMEOUT_MS);
|
|
2093
|
+
const reads = await Promise.all(request.mutations.map(mutation => this.inner.read(mutation.key, reconciliationSignal)));
|
|
2094
|
+
const putVersions = [];
|
|
2095
|
+
let storeNow = new Date(0);
|
|
2096
|
+
for (let index = 0; index < request.mutations.length; index += 1) {
|
|
2097
|
+
const mutation = request.mutations[index];
|
|
2098
|
+
const read = reads[index];
|
|
2099
|
+
storeNow = read.kind === 'found'
|
|
2100
|
+
? latestDate(storeNow, read.value.storeNow)
|
|
2101
|
+
: latestDate(storeNow, read.storeNow);
|
|
2102
|
+
if (mutation.kind === 'delete') {
|
|
2103
|
+
if (read.kind !== 'missing')
|
|
2104
|
+
return { kind: 'conflict', storeNow };
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
if (read.kind !== 'found'
|
|
2108
|
+
|| !Buffer.from(read.value.bytes).equals(Buffer.from(mutation.bytes))
|
|
2109
|
+
|| !versionAdvanced(request.conditions, mutation.key, read.value.version)) {
|
|
2110
|
+
return { kind: 'conflict', storeNow };
|
|
2111
|
+
}
|
|
2112
|
+
putVersions.push({ key: mutation.key, version: read.value.version });
|
|
2113
|
+
}
|
|
2114
|
+
return { kind: 'applied', putVersions, storeNow };
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
function versionAdvanced(conditions, key, current) {
|
|
2118
|
+
const condition = conditions.find(candidate => candidate.key.value === key.value);
|
|
2119
|
+
return condition?.kind !== 'version' || condition.expected.value !== current.value;
|
|
2120
|
+
}
|
|
2121
|
+
function latestDate(left, right) {
|
|
2122
|
+
return left.getTime() >= right.getTime() ? left : right;
|
|
2123
|
+
}
|
|
2124
|
+
function validateProviderAggregateRequest(request) {
|
|
2125
|
+
requireText(request.aggregateId.value, 'aggregate ID');
|
|
2126
|
+
if (request.aggregateGeneration < 1n || request.participants.length < 1) {
|
|
2127
|
+
throw new RangeError('Aggregate generation and participant count are invalid.');
|
|
2128
|
+
}
|
|
2129
|
+
if (request.inventoryDigest.byteLength !== 32) {
|
|
2130
|
+
throw new TypeError('Aggregate inventory digest must contain 32 bytes.');
|
|
2131
|
+
}
|
|
2132
|
+
const keys = request.participants.map(value => value.authorityKey.value);
|
|
2133
|
+
if (new Set(keys).size !== keys.length
|
|
2134
|
+
|| keys.some((key, index) => index > 0 && keys[index - 1].localeCompare(key) >= 0)) {
|
|
2135
|
+
throw new TypeError('Aggregate participants must be unique and canonically sorted.');
|
|
2136
|
+
}
|
|
2137
|
+
for (const participant of request.participants) {
|
|
2138
|
+
requireText(participant.authorityKey.value, 'aggregate authority key');
|
|
2139
|
+
requireText(participant.expectedStoreVersion.value, 'aggregate expected Store version');
|
|
2140
|
+
if (participant.authorityPayload.byteLength > 1024 * 1024
|
|
2141
|
+
|| participant.membershipMutation.byteLength > 1024 * 1024) {
|
|
2142
|
+
throw new RangeError('Aggregate participant value exceeds 1 MiB.');
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
function validateAggregateFence(fence) {
|
|
2147
|
+
requireText(fence.aggregateId.value, 'aggregate ID');
|
|
2148
|
+
if (fence.aggregateGeneration < 1n) {
|
|
2149
|
+
throw new RangeError('Aggregate generation must be positive.');
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
function aggregateRecord(request, requestFingerprint, state) {
|
|
2153
|
+
return {
|
|
2154
|
+
state,
|
|
2155
|
+
requestFingerprint,
|
|
2156
|
+
participantCount: request.participants.length,
|
|
2157
|
+
inventoryDigest: Buffer.from(request.inventoryDigest),
|
|
2158
|
+
targetDescriptor: { ...request.targetDescriptor },
|
|
2159
|
+
targetDescriptorLifecycleGeneration: request.targetDescriptorLifecycleGeneration,
|
|
2160
|
+
capacity: cloneCapacity(request.capacity),
|
|
2161
|
+
targetOwner: { ...request.targetOwner }
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function reconcileAggregatePrepare(record, fingerprint, fence) {
|
|
2165
|
+
if (record.requestFingerprint !== fingerprint)
|
|
2166
|
+
return { kind: 'conflict' };
|
|
2167
|
+
if (record.state === 'prepared')
|
|
2168
|
+
return { kind: 'alreadyPrepared', fence };
|
|
2169
|
+
if (record.state === 'committed')
|
|
2170
|
+
return { kind: 'stale' };
|
|
2171
|
+
if (record.state === 'aborted')
|
|
2172
|
+
return { kind: 'conflict' };
|
|
2173
|
+
return undefined;
|
|
2174
|
+
}
|
|
2175
|
+
function sameAggregateMarker(marker, fence, index, participant) {
|
|
2176
|
+
return marker !== undefined
|
|
2177
|
+
&& marker.aggregateId === fence.aggregateId.value
|
|
2178
|
+
&& marker.aggregateGeneration === fence.aggregateGeneration
|
|
2179
|
+
&& marker.index === index
|
|
2180
|
+
&& marker.expectedStoreVersion === participant.expectedStoreVersion.value
|
|
2181
|
+
&& marker.ownerTransition === participant.ownerTransition;
|
|
2182
|
+
}
|
|
2183
|
+
function sameAggregateMarkerEntry(marker, fence, index, entry) {
|
|
2184
|
+
return marker !== undefined
|
|
2185
|
+
&& marker.aggregateId === fence.aggregateId.value
|
|
2186
|
+
&& marker.aggregateGeneration === fence.aggregateGeneration
|
|
2187
|
+
&& marker.index === index
|
|
2188
|
+
&& marker.expectedStoreVersion === entry.expectedStoreVersion
|
|
2189
|
+
&& marker.ownerTransition === entry.ownerTransition
|
|
2190
|
+
&& marker.authorityPayloadSha256 === entry.authorityPayloadSha256
|
|
2191
|
+
&& marker.membershipMutationSha256 === entry.membershipMutationSha256;
|
|
2192
|
+
}
|
|
2193
|
+
function aggregateTarget(request) {
|
|
2194
|
+
return {
|
|
2195
|
+
meshName: request.targetDescriptor.meshName,
|
|
2196
|
+
nodeRid: request.targetDescriptor.rid,
|
|
2197
|
+
nodeLifecycleGeneration: request.targetDescriptorLifecycleGeneration,
|
|
2198
|
+
owner: { ...request.targetOwner }
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
function aggregateTargetFromRecord(record) {
|
|
2202
|
+
return {
|
|
2203
|
+
meshName: record.targetDescriptor.meshName,
|
|
2204
|
+
nodeRid: record.targetDescriptor.rid,
|
|
2205
|
+
nodeLifecycleGeneration: record.targetDescriptorLifecycleGeneration,
|
|
2206
|
+
owner: { ...record.targetOwner }
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
function addCapacityVector(left, right) {
|
|
2210
|
+
let spotType = left.spotType;
|
|
2211
|
+
if (right.spotType !== undefined) {
|
|
2212
|
+
if (spotType !== undefined
|
|
2213
|
+
&& (spotType.objectKind !== right.spotType.objectKind
|
|
2214
|
+
|| spotType.stableType !== right.spotType.stableType)) {
|
|
2215
|
+
throw new TypeError('Aggregate capacity contains more than one Spot type.');
|
|
2216
|
+
}
|
|
2217
|
+
spotType = {
|
|
2218
|
+
...right.spotType,
|
|
2219
|
+
count: (spotType?.count ?? 0) + right.spotType.count
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
return {
|
|
2223
|
+
actors: left.actors + right.actors,
|
|
2224
|
+
spots: left.spots + right.spots,
|
|
2225
|
+
...(spotType === undefined ? {} : { spotType })
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
async function requireProviderBytes(provider, key, signal) {
|
|
2229
|
+
const read = await provider.read(key, signal);
|
|
2230
|
+
if (read.kind === 'missing') {
|
|
2231
|
+
throw new Error(`Aggregate participant value is missing: ${key.value}.`);
|
|
2232
|
+
}
|
|
2233
|
+
return read.value.bytes;
|
|
2234
|
+
}
|
|
2235
|
+
async function parallelForEach(values, concurrency, operation) {
|
|
2236
|
+
let next = 0;
|
|
2237
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, async () => {
|
|
2238
|
+
while (next < values.length) {
|
|
2239
|
+
const index = next++;
|
|
2240
|
+
await operation(values[index], index);
|
|
2241
|
+
}
|
|
2242
|
+
}));
|
|
2243
|
+
}
|
|
2244
|
+
async function waitForAggregateCommitRetry(retryAttempt, deadlineAtMs, signal) {
|
|
2245
|
+
signal?.throwIfAborted();
|
|
2246
|
+
const remainingMs = deadlineAtMs - performance.now();
|
|
2247
|
+
if (remainingMs <= 0)
|
|
2248
|
+
return;
|
|
2249
|
+
const exponentialMs = Math.min(100, 2 << Math.min(retryAttempt, 5));
|
|
2250
|
+
const delayMs = Math.min(remainingMs, exponentialMs + Math.floor(Math.random() * (exponentialMs + 1)));
|
|
2251
|
+
await new Promise((resolve, reject) => {
|
|
2252
|
+
let timer;
|
|
2253
|
+
const aborted = () => {
|
|
2254
|
+
if (timer !== undefined)
|
|
2255
|
+
clearTimeout(timer);
|
|
2256
|
+
reject(signal?.reason ?? new Error('Aggregate commit was aborted.'));
|
|
2257
|
+
};
|
|
2258
|
+
signal?.addEventListener('abort', aborted, { once: true });
|
|
2259
|
+
timer = setTimeout(() => {
|
|
2260
|
+
signal?.removeEventListener('abort', aborted);
|
|
2261
|
+
resolve();
|
|
2262
|
+
}, delayMs);
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
function sha256Hex(bytes) {
|
|
2266
|
+
return (0, node_crypto_1.createHash)('sha256').update(bytes).digest('hex');
|
|
2267
|
+
}
|
|
2268
|
+
// Logical key preimages (21-location-runtime.md#2.4). Values are joined with
|
|
2269
|
+
// literal NUL bytes -- the same scheme the opaque record store hashes with
|
|
2270
|
+
// SHA-256 to derive the Redis key -- so a runtime in one language can read a
|
|
2271
|
+
// record another language wrote. Not used for framework-internal-only rows
|
|
2272
|
+
// (aggregate/capacity/creation-terminal/spot/actor/route), which stay on the
|
|
2273
|
+
// legacy PREFIX scheme since they aren't part of the public opaque contract.
|
|
2274
|
+
function ownerKey(ownerId) {
|
|
2275
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`owner-lease\0${requireNoNul(ownerId, 'OwnerId')}`);
|
|
2276
|
+
}
|
|
2277
|
+
const AUTHORITY_PREIMAGE_PREFIX = 'authority\0';
|
|
2278
|
+
function authorityKey(value) {
|
|
2279
|
+
return (0, in_memory_provider_location_store_1.storeKey)(authorityPreimage(value));
|
|
2280
|
+
}
|
|
2281
|
+
function authorityPreimage(value) {
|
|
2282
|
+
const decoded = (0, authority_key_codec_1.decodeAuthorityKey)({ value });
|
|
2283
|
+
// §2.3: Entry|User|Instance Spot kinds share one segment ("spot") -- one
|
|
2284
|
+
// Id has exactly one authority row regardless of spot kind.
|
|
2285
|
+
const segment = decoded.kind === 'actor' ? 'actor' : 'spot';
|
|
2286
|
+
return `${AUTHORITY_PREIMAGE_PREFIX}${segment}\0${requireNoNul(decoded.globalId, 'Authority Id')}`;
|
|
2287
|
+
}
|
|
2288
|
+
// Reverses authorityPreimage(): recovers the framework-internal zla1-encoded
|
|
2289
|
+
// ZLinkAuthorityKey contract value from a scanned opaque record's logical
|
|
2290
|
+
// key. Used by the authority list/cleanup scans below, which see raw
|
|
2291
|
+
// preimages (authority\0actor\0{id} | authority\0spot\0{id}) and must hand
|
|
2292
|
+
// callers back the same encodeAuthorityKey() representation authorityKey()
|
|
2293
|
+
// was given.
|
|
2294
|
+
function authorityContractValueFromPreimage(logicalKey) {
|
|
2295
|
+
if (!logicalKey.startsWith(AUTHORITY_PREIMAGE_PREFIX)) {
|
|
2296
|
+
throw new Error('Authority scan returned a non-authority logical key.');
|
|
2297
|
+
}
|
|
2298
|
+
const rest = logicalKey.slice(AUTHORITY_PREIMAGE_PREFIX.length);
|
|
2299
|
+
const separator = rest.indexOf('\0');
|
|
2300
|
+
if (separator < 0)
|
|
2301
|
+
throw new Error('Authority logical key preimage is malformed.');
|
|
2302
|
+
const segment = rest.slice(0, separator);
|
|
2303
|
+
const globalId = rest.slice(separator + 1);
|
|
2304
|
+
return (0, authority_key_codec_1.encodeAuthorityKey)(segment === 'actor' ? 'actor' : 'user_spot', globalId).value;
|
|
2305
|
+
}
|
|
2306
|
+
function requireNoNul(value, name) {
|
|
2307
|
+
if (value.includes('\0')) {
|
|
2308
|
+
throw new TypeError(`${name} must not contain a NUL byte.`);
|
|
2309
|
+
}
|
|
2310
|
+
return value;
|
|
2311
|
+
}
|
|
2312
|
+
function authorityContractKey(value) {
|
|
2313
|
+
return { value };
|
|
2314
|
+
}
|
|
2315
|
+
function aggregateId(value) {
|
|
2316
|
+
return { value };
|
|
2317
|
+
}
|
|
2318
|
+
function aggregateFence(id, generation) {
|
|
2319
|
+
return { aggregateId: id, aggregateGeneration: generation };
|
|
2320
|
+
}
|
|
2321
|
+
function aggregateKey(fence) {
|
|
2322
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}aggregate:${encodeURIComponent(fence.aggregateId.value)}:${fence.aggregateGeneration}`);
|
|
2323
|
+
}
|
|
2324
|
+
function aggregateParticipantPayloadKey(fence, index) {
|
|
2325
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}aggregate-participant:${encodeURIComponent(fence.aggregateId.value)}:`
|
|
2326
|
+
+ `${fence.aggregateGeneration}:${index}:authority`);
|
|
2327
|
+
}
|
|
2328
|
+
function aggregateParticipantMembershipKey(fence, index) {
|
|
2329
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}aggregate-participant:${encodeURIComponent(fence.aggregateId.value)}:`
|
|
2330
|
+
+ `${fence.aggregateGeneration}:${index}:membership`);
|
|
2331
|
+
}
|
|
2332
|
+
function capacityKey(meshName, nodeRid) {
|
|
2333
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}capacity:${encodeURIComponent(meshName)}:${encodeURIComponent(nodeRid)}`);
|
|
2334
|
+
}
|
|
2335
|
+
function creationTerminalKey(operation) {
|
|
2336
|
+
validateCreationOperation(operation);
|
|
2337
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}creation-terminal:${[
|
|
2338
|
+
String(operation.sourceNodeRid),
|
|
2339
|
+
operation.sourceNodeGeneration.toString(),
|
|
2340
|
+
operation.operationId.high.toString(16).padStart(16, '0'),
|
|
2341
|
+
operation.operationId.low.toString(16).padStart(16, '0')
|
|
2342
|
+
].map(encodeURIComponent).join(':')}`);
|
|
2343
|
+
}
|
|
2344
|
+
function meshPrefix(meshName) {
|
|
2345
|
+
return `mesh-node\0${requireNoNul(meshName, 'MeshName')}\0`;
|
|
2346
|
+
}
|
|
2347
|
+
function meshKey(meshName, nodeRid) {
|
|
2348
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${meshPrefix(meshName)}${routingIdHexSegment(nodeRid)}`);
|
|
2349
|
+
}
|
|
2350
|
+
function clientServerPrefix(channelName) {
|
|
2351
|
+
return `client-server\0${requireNoNul(channelName, 'ChannelName')}\0`;
|
|
2352
|
+
}
|
|
2353
|
+
function clientServerKey(channelName, serverRid) {
|
|
2354
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${clientServerPrefix(channelName)}${routingIdHexSegment(serverRid)}`);
|
|
2355
|
+
}
|
|
2356
|
+
// {hex(RoutingId)}: lowercase hex of the RoutingId's raw bytes
|
|
2357
|
+
// (21-location-runtime.md#2.4).
|
|
2358
|
+
function routingIdHexSegment(rid) {
|
|
2359
|
+
return (0, routing_id_1.encodeRoutingIdStorageHex)(rid).toLowerCase();
|
|
2360
|
+
}
|
|
2361
|
+
function fanoutPrefix(channelName) {
|
|
2362
|
+
return `fanout-publisher\0${requireNoNul(channelName, 'ChannelName')}\0`;
|
|
2363
|
+
}
|
|
2364
|
+
function fanoutKey(channelName, publisherRid) {
|
|
2365
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${fanoutPrefix(channelName)}${routingIdHexSegment(publisherRid)}`);
|
|
2366
|
+
}
|
|
2367
|
+
function spotKey(meshName, spotId) {
|
|
2368
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}spot:${encodeURIComponent(meshName)}:${encodeURIComponent(spotId)}`);
|
|
2369
|
+
}
|
|
2370
|
+
function actorKey(meshName, actorId) {
|
|
2371
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}actor:${encodeURIComponent(meshName)}:${encodeURIComponent(actorId)}`);
|
|
2372
|
+
}
|
|
2373
|
+
function routeKey(routeKind, value) {
|
|
2374
|
+
return (0, in_memory_provider_location_store_1.storeKey)(`${PREFIX}route:${encodeURIComponent(routeKind)}:${encodeURIComponent(value)}`);
|
|
2375
|
+
}
|
|
2376
|
+
function encodeText(value) {
|
|
2377
|
+
return Buffer.from(value, 'utf8');
|
|
2378
|
+
}
|
|
2379
|
+
function decodeText(value) {
|
|
2380
|
+
return Buffer.from(value).toString('utf8');
|
|
2381
|
+
}
|
|
2382
|
+
/** Counter rows are bare, canonical decimal next-to-issue values. */
|
|
2383
|
+
function counterNextValue(counter) {
|
|
2384
|
+
if (counter.kind === 'missing')
|
|
2385
|
+
return 1n;
|
|
2386
|
+
const text = decodeText(counter.value.bytes);
|
|
2387
|
+
if (!/^[1-9][0-9]*$/.test(text)) {
|
|
2388
|
+
throw new TypeError('Location Store counter is not canonical decimal.');
|
|
2389
|
+
}
|
|
2390
|
+
const value = BigInt(text);
|
|
2391
|
+
if (value > MAX_GENERATION) {
|
|
2392
|
+
throw new RangeError('Location Store counter is outside its valid range.');
|
|
2393
|
+
}
|
|
2394
|
+
return value;
|
|
2395
|
+
}
|
|
2396
|
+
function encodeJson(value) {
|
|
2397
|
+
return Buffer.from(JSON.stringify(value, (key, candidate) => {
|
|
2398
|
+
if (typeof candidate === 'bigint')
|
|
2399
|
+
return { $bigint: candidate.toString() };
|
|
2400
|
+
if (candidate instanceof Uint8Array) {
|
|
2401
|
+
return { $bytes: Buffer.from(candidate).toString('base64') };
|
|
2402
|
+
}
|
|
2403
|
+
if (key === 'spotTypes'
|
|
2404
|
+
&& candidate !== null
|
|
2405
|
+
&& typeof candidate === 'object'
|
|
2406
|
+
&& !Array.isArray(candidate)) {
|
|
2407
|
+
return Object.fromEntries(Object.entries(candidate)
|
|
2408
|
+
.sort(([left], [right]) => compareUtf16Ordinal(left, right)));
|
|
2409
|
+
}
|
|
2410
|
+
return candidate;
|
|
2411
|
+
}), 'utf8');
|
|
2412
|
+
}
|
|
2413
|
+
function compareUtf16Ordinal(left, right) {
|
|
2414
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
2415
|
+
}
|
|
2416
|
+
function decodeJson(value) {
|
|
2417
|
+
return JSON.parse(decodeText(value), (_key, candidate) => {
|
|
2418
|
+
if (candidate !== null
|
|
2419
|
+
&& typeof candidate === 'object'
|
|
2420
|
+
&& Object.keys(candidate).length === 1
|
|
2421
|
+
&& typeof candidate.$bigint === 'string') {
|
|
2422
|
+
return BigInt(candidate.$bigint);
|
|
2423
|
+
}
|
|
2424
|
+
if (candidate !== null
|
|
2425
|
+
&& typeof candidate === 'object'
|
|
2426
|
+
&& Object.keys(candidate).length === 1
|
|
2427
|
+
&& typeof candidate.$bytes === 'string') {
|
|
2428
|
+
return Buffer.from(candidate.$bytes, 'base64');
|
|
2429
|
+
}
|
|
2430
|
+
return candidate;
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
function requireRecordVersion(value, kind) {
|
|
2434
|
+
if (value === null || typeof value !== 'object'
|
|
2435
|
+
|| value.recordVersion !== 1) {
|
|
2436
|
+
throw new Error(`Location Store ${kind} record has an unrecognized recordVersion.`);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
function canonicalize(value) {
|
|
2440
|
+
if (typeof value === 'bigint')
|
|
2441
|
+
return value.toString();
|
|
2442
|
+
if (value instanceof Uint8Array)
|
|
2443
|
+
return Buffer.from(value).toString('base64');
|
|
2444
|
+
if (value instanceof Date)
|
|
2445
|
+
return value.toISOString();
|
|
2446
|
+
if (Array.isArray(value))
|
|
2447
|
+
return value.map(canonicalize);
|
|
2448
|
+
if (value !== null && typeof value === 'object') {
|
|
2449
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, canonicalize(item)]));
|
|
2450
|
+
}
|
|
2451
|
+
return value;
|
|
2452
|
+
}
|
|
2453
|
+
function reviveCanonical(value, key = '') {
|
|
2454
|
+
const generationKeys = new Set([
|
|
2455
|
+
'generation', 'leaseGeneration', 'ownerLeaseGeneration', 'objectGeneration',
|
|
2456
|
+
'authorityOwnerGeneration', 'aggregateGeneration', 'lifecycleGeneration', 'descriptorRevision',
|
|
2457
|
+
'nodeGeneration', 'applicationVersion', 'requestEncodedSize'
|
|
2458
|
+
]);
|
|
2459
|
+
if (typeof value === 'string' && generationKeys.has(key))
|
|
2460
|
+
return BigInt(value);
|
|
2461
|
+
if (Array.isArray(value))
|
|
2462
|
+
return value.map(item => reviveCanonical(item));
|
|
2463
|
+
if (value !== null && typeof value === 'object') {
|
|
2464
|
+
return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, reviveCanonical(item, name)]));
|
|
2465
|
+
}
|
|
2466
|
+
return value;
|
|
2467
|
+
}
|
|
2468
|
+
function decodeOwnerRecord(bytes) {
|
|
2469
|
+
const record = decodeJson(bytes);
|
|
2470
|
+
requireRecordVersion(record, 'owner lease');
|
|
2471
|
+
return record;
|
|
2472
|
+
}
|
|
2473
|
+
function encodeOwnerRecord(ownerId, leaseGeneration) {
|
|
2474
|
+
return encodeJson({ recordVersion: 1, ownerId, leaseGeneration: leaseGeneration.toString() });
|
|
2475
|
+
}
|
|
2476
|
+
function encodeCanonicalDescriptorRecord(generation, descriptor) {
|
|
2477
|
+
// `generation` is an old Node-local row counter. The opaque record's
|
|
2478
|
+
// cmsgpack version is the interoperable row version, so it is deliberately
|
|
2479
|
+
// not serialized (21-location-runtime §2.4).
|
|
2480
|
+
void generation;
|
|
2481
|
+
return Buffer.from(JSON.stringify({
|
|
2482
|
+
recordVersion: 1,
|
|
2483
|
+
ownerId: descriptor.ownerId,
|
|
2484
|
+
leaseGeneration: descriptor.leaseGeneration.toString(),
|
|
2485
|
+
descriptorRevision: descriptor.descriptorRevision.toString(),
|
|
2486
|
+
descriptor: canonicalDescriptor(descriptor)
|
|
2487
|
+
}), 'utf8');
|
|
2488
|
+
}
|
|
2489
|
+
function decodeCanonicalDescriptorRecord(bytes) {
|
|
2490
|
+
const record = JSON.parse(decodeText(bytes));
|
|
2491
|
+
requireRecordVersion(record, 'descriptor');
|
|
2492
|
+
return reviveCanonical(record);
|
|
2493
|
+
}
|
|
2494
|
+
function descriptorStoreGeneration(record, storeVersion) {
|
|
2495
|
+
return record.generation === undefined ? BigInt(storeVersion) : BigInt(record.generation);
|
|
2496
|
+
}
|
|
2497
|
+
function canonicalDescriptor(descriptor) {
|
|
2498
|
+
if ('rid' in descriptor)
|
|
2499
|
+
return canonicalMeshDescriptor(descriptor);
|
|
2500
|
+
if ('serverRid' in descriptor)
|
|
2501
|
+
return {
|
|
2502
|
+
channelName: descriptor.channelName,
|
|
2503
|
+
serverRoutingIdHex: (0, routing_id_1.encodeRoutingIdStorageHex)(descriptor.serverRid),
|
|
2504
|
+
lifecycleGeneration: descriptor.lifecycleGeneration.toString(),
|
|
2505
|
+
descriptorRevision: descriptor.descriptorRevision.toString(),
|
|
2506
|
+
endpoint: descriptor.endpoint,
|
|
2507
|
+
weight: descriptor.weight,
|
|
2508
|
+
state: canonicalRuntimeState(descriptor.state),
|
|
2509
|
+
securityIdentity: descriptor.securityIdentity,
|
|
2510
|
+
ownerId: descriptor.ownerId,
|
|
2511
|
+
leaseGeneration: descriptor.leaseGeneration.toString(),
|
|
2512
|
+
updatedAtEpochMs: descriptor.updatedAt.getTime().toString()
|
|
2513
|
+
};
|
|
2514
|
+
const publisher = descriptor;
|
|
2515
|
+
return {
|
|
2516
|
+
channelName: publisher.channelName,
|
|
2517
|
+
publisherRoutingIdHex: (0, routing_id_1.encodeRoutingIdStorageHex)(publisher.publisherRid),
|
|
2518
|
+
lifecycleGeneration: publisher.lifecycleGeneration.toString(),
|
|
2519
|
+
descriptorRevision: publisher.descriptorRevision.toString(),
|
|
2520
|
+
endpoint: publisher.endpoint,
|
|
2521
|
+
state: canonicalRuntimeState(publisher.state),
|
|
2522
|
+
securityIdentity: publisher.securityIdentity,
|
|
2523
|
+
ownerId: publisher.ownerId,
|
|
2524
|
+
leaseGeneration: publisher.leaseGeneration.toString(),
|
|
2525
|
+
updatedAtEpochMs: publisher.updatedAt.getTime().toString()
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
function canonicalMeshDescriptor(descriptor) {
|
|
2529
|
+
return {
|
|
2530
|
+
meshName: descriptor.meshName,
|
|
2531
|
+
routingIdHex: (0, routing_id_1.encodeRoutingIdStorageHex)(descriptor.rid),
|
|
2532
|
+
lifecycleGeneration: descriptor.lifecycleGeneration.toString(),
|
|
2533
|
+
descriptorRevision: descriptor.descriptorRevision.toString(),
|
|
2534
|
+
endpoint: descriptor.endpoint,
|
|
2535
|
+
entrySpotId: descriptor.entrySpotId ?? null,
|
|
2536
|
+
channelWeights: descriptor.channelWeights,
|
|
2537
|
+
applicationVersion: descriptor.applicationVersion.toString(),
|
|
2538
|
+
objectCapabilities: descriptor.objectCapabilities.map(capability => ({
|
|
2539
|
+
...capability,
|
|
2540
|
+
objectKind: canonicalObjectKind(capability.objectKind),
|
|
2541
|
+
policy: capability.policy === 'disabled' ? 'disabled' : capability.policy
|
|
2542
|
+
})),
|
|
2543
|
+
objectRole: descriptor.objectRole,
|
|
2544
|
+
placementWeight: descriptor.placementWeight,
|
|
2545
|
+
capacity: {
|
|
2546
|
+
actors: descriptor.populationCapacity.actors,
|
|
2547
|
+
spots: descriptor.populationCapacity.spots,
|
|
2548
|
+
spotTypes: descriptor.populationCapacity.spotTypes.map(capacity => ({
|
|
2549
|
+
...capacity,
|
|
2550
|
+
objectKind: canonicalObjectKind(capacity.objectKind)
|
|
2551
|
+
}))
|
|
2552
|
+
},
|
|
2553
|
+
activationConcurrency: descriptor.activationConcurrency,
|
|
2554
|
+
maintenanceWave: descriptor.maintenanceWave ?? null,
|
|
2555
|
+
state: canonicalRuntimeState(descriptor.state),
|
|
2556
|
+
securityIdentity: descriptor.securityIdentity,
|
|
2557
|
+
ownerId: descriptor.ownerId,
|
|
2558
|
+
leaseGeneration: descriptor.leaseGeneration.toString(),
|
|
2559
|
+
updatedAtEpochMs: descriptor.updatedAt.getTime().toString()
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2562
|
+
function reviveCanonicalCapacity(value) {
|
|
2563
|
+
return {
|
|
2564
|
+
actors: value.actors,
|
|
2565
|
+
spots: value.spots,
|
|
2566
|
+
spotTypes: value.spotTypes.map(capacity => ({
|
|
2567
|
+
...capacity,
|
|
2568
|
+
objectKind: runtimeSpotObjectKind(String(capacity.objectKind))
|
|
2569
|
+
}))
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function reviveCanonicalCapabilities(value) {
|
|
2573
|
+
return value.map(capability => ({
|
|
2574
|
+
...capability,
|
|
2575
|
+
objectKind: runtimeObjectKind(String(capability.objectKind))
|
|
2576
|
+
}));
|
|
2577
|
+
}
|
|
2578
|
+
function canonicalObjectKind(kind) {
|
|
2579
|
+
return kind === 'user_spot' ? 'userSpot' : kind === 'instance_spot' ? 'instanceSpot' : 'actor';
|
|
2580
|
+
}
|
|
2581
|
+
function runtimeObjectKind(kind) {
|
|
2582
|
+
if (kind === 'actor')
|
|
2583
|
+
return 'actor';
|
|
2584
|
+
if (kind === 'userSpot')
|
|
2585
|
+
return 'user_spot';
|
|
2586
|
+
if (kind === 'instanceSpot')
|
|
2587
|
+
return 'instance_spot';
|
|
2588
|
+
throw new TypeError(`Location Store descriptor has invalid objectKind '${kind}'.`);
|
|
2589
|
+
}
|
|
2590
|
+
function runtimeSpotObjectKind(kind) {
|
|
2591
|
+
const result = runtimeObjectKind(kind);
|
|
2592
|
+
if (result === 'actor')
|
|
2593
|
+
throw new TypeError('Location Store spot capacity cannot have actor objectKind.');
|
|
2594
|
+
return result;
|
|
2595
|
+
}
|
|
2596
|
+
function canonicalRuntimeState(state) {
|
|
2597
|
+
return ['preparing', 'serving', 'relocating', 'relocated', 'draining', 'stopped', 'error'][state]
|
|
2598
|
+
?? (() => { throw new TypeError(`Location Store descriptor has invalid state '${state}'.`); })();
|
|
2599
|
+
}
|
|
2600
|
+
function runtimeState(state) {
|
|
2601
|
+
const index = ['preparing', 'serving', 'relocating', 'relocated', 'draining', 'stopped', 'error'].indexOf(state);
|
|
2602
|
+
if (index < 0)
|
|
2603
|
+
throw new TypeError(`Location Store descriptor has invalid state '${state}'.`);
|
|
2604
|
+
return index;
|
|
2605
|
+
}
|
|
2606
|
+
function encodeAuthorityRecord(record) {
|
|
2607
|
+
const snapshot = record.snapshot;
|
|
2608
|
+
const allocation = snapshot.allocation;
|
|
2609
|
+
const envelope = {
|
|
2610
|
+
recordVersion: 1,
|
|
2611
|
+
payload: Buffer.from(snapshot.payload).toString('base64'),
|
|
2612
|
+
objectGeneration: snapshot.objectGeneration.toString(),
|
|
2613
|
+
authorityOwnerGeneration: snapshot.authorityOwnerGeneration.toString(),
|
|
2614
|
+
ownerId: snapshot.ownerId,
|
|
2615
|
+
ownerLeaseGeneration: snapshot.ownerLeaseGeneration.toString(),
|
|
2616
|
+
allocation: {
|
|
2617
|
+
state: allocation.state,
|
|
2618
|
+
objectKind: canonicalPlacementObjectKind(allocation.objectKind),
|
|
2619
|
+
stableType: allocation.stableType,
|
|
2620
|
+
descriptor: {
|
|
2621
|
+
meshName: allocation.descriptor.meshName,
|
|
2622
|
+
routingIdHex: (0, routing_id_1.encodeRoutingIdStorageHex)(allocation.descriptor.rid)
|
|
2623
|
+
},
|
|
2624
|
+
descriptorLifecycleGeneration: allocation.descriptorLifecycleGeneration.toString(),
|
|
2625
|
+
capacity: canonicalAuthorityCapacity(allocation.capacity)
|
|
2626
|
+
},
|
|
2627
|
+
pendingCreation: snapshot.pendingCreation === undefined ? null : {
|
|
2628
|
+
reservationId: snapshot.pendingCreation.reservationId,
|
|
2629
|
+
requestContentReference: snapshot.pendingCreation.requestContentReference,
|
|
2630
|
+
requestSha256: Buffer.from(snapshot.pendingCreation.requestSha256).toString('hex'),
|
|
2631
|
+
requestEncodedSize: Number(snapshot.pendingCreation.requestEncodedSize)
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
// These fields gate Node-only creation/aggregate recovery. They are absent
|
|
2635
|
+
// from normal authority values (including the golden vector), while the
|
|
2636
|
+
// interoperable envelope above remains byte-canonical.
|
|
2637
|
+
if (record.reservationId !== undefined)
|
|
2638
|
+
envelope.reservationId = record.reservationId;
|
|
2639
|
+
if (record.terminal !== undefined)
|
|
2640
|
+
envelope.terminal = record.terminal;
|
|
2641
|
+
if (record.aggregate !== undefined)
|
|
2642
|
+
envelope.aggregate = canonicalize(record.aggregate);
|
|
2643
|
+
if (record.visibleStoreVersion !== undefined)
|
|
2644
|
+
envelope.visibleStoreVersion = record.visibleStoreVersion;
|
|
2645
|
+
return Buffer.from(JSON.stringify(envelope), 'utf8');
|
|
2646
|
+
}
|
|
2647
|
+
function decodeAuthorityRecord(bytes) {
|
|
2648
|
+
const value = JSON.parse(decodeText(bytes));
|
|
2649
|
+
requireRecordVersion(value, 'authority');
|
|
2650
|
+
const allocation = value.allocation;
|
|
2651
|
+
const descriptor = allocation.descriptor;
|
|
2652
|
+
const capacity = allocation.capacity;
|
|
2653
|
+
const pending = value.pendingCreation;
|
|
2654
|
+
const objectKind = runtimePlacementObjectKind(String(allocation.objectKind));
|
|
2655
|
+
return {
|
|
2656
|
+
reservationId: typeof value.reservationId === 'string' ? value.reservationId : undefined,
|
|
2657
|
+
terminal: value.terminal,
|
|
2658
|
+
aggregate: value.aggregate === undefined
|
|
2659
|
+
? undefined
|
|
2660
|
+
: reviveCanonical(value.aggregate),
|
|
2661
|
+
visibleStoreVersion: typeof value.visibleStoreVersion === 'string'
|
|
2662
|
+
? value.visibleStoreVersion : undefined,
|
|
2663
|
+
snapshot: {
|
|
2664
|
+
payload: Buffer.from(String(value.payload), 'base64'),
|
|
2665
|
+
objectGeneration: BigInt(String(value.objectGeneration)),
|
|
2666
|
+
authorityOwnerGeneration: BigInt(String(value.authorityOwnerGeneration)),
|
|
2667
|
+
ownerId: String(value.ownerId),
|
|
2668
|
+
ownerLeaseGeneration: BigInt(String(value.ownerLeaseGeneration)),
|
|
2669
|
+
allocation: {
|
|
2670
|
+
state: allocation.state,
|
|
2671
|
+
objectKind: objectKind,
|
|
2672
|
+
stableType: String(allocation.stableType),
|
|
2673
|
+
descriptor: {
|
|
2674
|
+
meshName: String(descriptor.meshName),
|
|
2675
|
+
rid: (0, routing_id_1.decodeRoutingId)(String(descriptor.routingIdHex), descriptor.routingIdHex)
|
|
2676
|
+
},
|
|
2677
|
+
descriptorLifecycleGeneration: BigInt(String(allocation.descriptorLifecycleGeneration)),
|
|
2678
|
+
capacity: {
|
|
2679
|
+
actors: Number(capacity.actors),
|
|
2680
|
+
spots: Number(capacity.spots),
|
|
2681
|
+
spotType: capacity.spotType === null
|
|
2682
|
+
? undefined
|
|
2683
|
+
: decodeAuthoritySpotType(capacity.spotType)
|
|
2684
|
+
}
|
|
2685
|
+
},
|
|
2686
|
+
pendingCreation: pending === null ? undefined : {
|
|
2687
|
+
reservationId: String(pending.reservationId),
|
|
2688
|
+
requestContentReference: String(pending.requestContentReference),
|
|
2689
|
+
requestSha256: Buffer.from(String(pending.requestSha256), 'hex'),
|
|
2690
|
+
requestEncodedSize: BigInt(String(pending.requestEncodedSize))
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
function canonicalPlacementObjectKind(kind) {
|
|
2696
|
+
switch (kind) {
|
|
2697
|
+
case 'actor': return 'actor';
|
|
2698
|
+
case 'user_spot': return 'userSpot';
|
|
2699
|
+
case 'instance_spot': return 'instanceSpot';
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
function runtimePlacementObjectKind(kind) {
|
|
2703
|
+
switch (kind) {
|
|
2704
|
+
case 'actor': return 'actor';
|
|
2705
|
+
case 'userSpot': return 'user_spot';
|
|
2706
|
+
case 'instanceSpot': return 'instance_spot';
|
|
2707
|
+
default: throw new Error(`Location Store authority record has an invalid allocation objectKind '${kind}'.`);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
function canonicalAuthorityCapacity(capacity) {
|
|
2711
|
+
return {
|
|
2712
|
+
actors: capacity.actors,
|
|
2713
|
+
spots: capacity.spots,
|
|
2714
|
+
spotType: capacity.spotType === undefined
|
|
2715
|
+
? null
|
|
2716
|
+
: {
|
|
2717
|
+
objectKind: canonicalPlacementObjectKind(capacity.spotType.objectKind),
|
|
2718
|
+
stableType: capacity.spotType.stableType,
|
|
2719
|
+
count: capacity.spotType.count
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
function decodeAuthoritySpotType(value) {
|
|
2724
|
+
const spotType = value;
|
|
2725
|
+
return {
|
|
2726
|
+
objectKind: runtimeSpotPlacementObjectKind(String(spotType.objectKind)),
|
|
2727
|
+
stableType: String(spotType.stableType),
|
|
2728
|
+
count: Number(spotType.count)
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
function runtimeSpotPlacementObjectKind(kind) {
|
|
2732
|
+
const objectKind = runtimePlacementObjectKind(kind);
|
|
2733
|
+
if (objectKind === 'actor') {
|
|
2734
|
+
throw new Error('Location Store authority capacity spotType cannot be an actor.');
|
|
2735
|
+
}
|
|
2736
|
+
return objectKind;
|
|
2737
|
+
}
|
|
2738
|
+
function createTerminalRecord(request, now) {
|
|
2739
|
+
const publication = request.completion.terminal;
|
|
2740
|
+
validateCreationOperation(publication.operation);
|
|
2741
|
+
if (publication.terminalEnvelope.byteLength > MAX_CREATION_TERMINAL_BYTES
|
|
2742
|
+
|| publication.terminalEnvelopeSha256.byteLength !== 32) {
|
|
2743
|
+
throw new RangeError('Creation terminal envelope must not exceed 1 MiB and requires a SHA-256 digest.');
|
|
2744
|
+
}
|
|
2745
|
+
const actualSha = (0, node_crypto_1.createHash)('sha256').update(publication.terminalEnvelope).digest();
|
|
2746
|
+
if (!(0, node_crypto_1.timingSafeEqual)(actualSha, Buffer.from(publication.terminalEnvelopeSha256))) {
|
|
2747
|
+
throw new TypeError('Creation terminal envelope SHA-256 does not match its bytes.');
|
|
2748
|
+
}
|
|
2749
|
+
const deadlineMs = publication.operationDeadline.getTime();
|
|
2750
|
+
const expiresAtMs = deadlineMs + CREATION_TERMINAL_RETENTION_MS;
|
|
2751
|
+
if (!Number.isSafeInteger(deadlineMs)
|
|
2752
|
+
|| !Number.isSafeInteger(expiresAtMs)
|
|
2753
|
+
|| expiresAtMs <= now.getTime()) {
|
|
2754
|
+
throw new RangeError('Creation terminal expiry must be the live operation deadline plus five minutes.');
|
|
2755
|
+
}
|
|
2756
|
+
if (request.completion.kind === 'created') {
|
|
2757
|
+
validatePayloadSize(request.completion.readyPayload, 'Actor ready payload');
|
|
2758
|
+
}
|
|
2759
|
+
return {
|
|
2760
|
+
state: request.completion.kind,
|
|
2761
|
+
operation: {
|
|
2762
|
+
sourceNodeRid: publication.operation.sourceNodeRid,
|
|
2763
|
+
sourceNodeGeneration: publication.operation.sourceNodeGeneration,
|
|
2764
|
+
operationId: { ...publication.operation.operationId }
|
|
2765
|
+
},
|
|
2766
|
+
reservationId: requireText(request.reservationId, 'creation reservation ID'),
|
|
2767
|
+
objectKind: request.key.kind,
|
|
2768
|
+
terminalEnvelope: Buffer.from(publication.terminalEnvelope),
|
|
2769
|
+
terminalEnvelopeSha256: Buffer.from(publication.terminalEnvelopeSha256),
|
|
2770
|
+
expiresAt: new Date(expiresAtMs),
|
|
2771
|
+
storeNow: new Date(now)
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
function reviveCreationTerminal(record) {
|
|
2775
|
+
return {
|
|
2776
|
+
...record,
|
|
2777
|
+
operation: {
|
|
2778
|
+
...record.operation,
|
|
2779
|
+
sourceNodeRid: String(record.operation.sourceNodeRid),
|
|
2780
|
+
operationId: { ...record.operation.operationId }
|
|
2781
|
+
},
|
|
2782
|
+
terminalEnvelope: Buffer.from(record.terminalEnvelope),
|
|
2783
|
+
terminalEnvelopeSha256: Buffer.from(record.terminalEnvelopeSha256),
|
|
2784
|
+
expiresAt: reviveDate(record.expiresAt),
|
|
2785
|
+
storeNow: reviveDate(record.storeNow)
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function validateCreationOperation(operation) {
|
|
2789
|
+
const sourceRid = String(operation.sourceNodeRid);
|
|
2790
|
+
const sourceRidBytes = Buffer.byteLength(sourceRid, 'utf8');
|
|
2791
|
+
if (sourceRidBytes < 1 || sourceRidBytes > 255 || sourceRid.includes('\0')) {
|
|
2792
|
+
throw new TypeError('Creation terminal source node RID must contain 1..255 UTF-8 bytes without NUL.');
|
|
2793
|
+
}
|
|
2794
|
+
if (operation.sourceNodeGeneration < 1n
|
|
2795
|
+
|| operation.sourceNodeGeneration > MAX_GENERATION
|
|
2796
|
+
|| operation.operationId.high < 0n
|
|
2797
|
+
|| operation.operationId.high > MAX_U64
|
|
2798
|
+
|| operation.operationId.low < 0n
|
|
2799
|
+
|| operation.operationId.low > MAX_U64
|
|
2800
|
+
|| (operation.operationId.high === 0n && operation.operationId.low === 0n)) {
|
|
2801
|
+
throw new RangeError('Creation terminal source generation and operation ID are invalid.');
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
function validatePayloadSize(value, name) {
|
|
2805
|
+
if (value.byteLength > MAX_CREATION_TERMINAL_BYTES) {
|
|
2806
|
+
throw new RangeError(`${name} must not exceed 1 MiB.`);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
function requireText(value, name) {
|
|
2810
|
+
if (value.length === 0 || value.includes('\0')) {
|
|
2811
|
+
throw new TypeError(`${name} must be non-empty text without NUL.`);
|
|
2812
|
+
}
|
|
2813
|
+
return value;
|
|
2814
|
+
}
|
|
2815
|
+
function authoritySnapshot(snapshot, storeVersion, storeNow) {
|
|
2816
|
+
return {
|
|
2817
|
+
kind: 'snapshot',
|
|
2818
|
+
...snapshot,
|
|
2819
|
+
payload: Buffer.from(snapshot.payload),
|
|
2820
|
+
pendingCreation: snapshot.pendingCreation === undefined
|
|
2821
|
+
? undefined
|
|
2822
|
+
: {
|
|
2823
|
+
...snapshot.pendingCreation,
|
|
2824
|
+
requestSha256: Buffer.from(snapshot.pendingCreation.requestSha256)
|
|
2825
|
+
},
|
|
2826
|
+
storeVersion: { value: storeVersion.value },
|
|
2827
|
+
storeNow
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function liveTargetDescriptor(descriptorRead, leaseRead, target) {
|
|
2831
|
+
if (descriptorRead.kind === 'missing' || leaseRead.kind === 'missing')
|
|
2832
|
+
return undefined;
|
|
2833
|
+
const descriptor = reviveMeshDescriptor(decodeCanonicalDescriptorRecord(descriptorRead.value.bytes).descriptor);
|
|
2834
|
+
const lease = decodeOwnerRecord(leaseRead.value.bytes);
|
|
2835
|
+
return descriptor.meshName === target.meshName
|
|
2836
|
+
&& String(descriptor.rid) === String(target.nodeRid)
|
|
2837
|
+
&& descriptor.lifecycleGeneration === target.nodeLifecycleGeneration
|
|
2838
|
+
&& descriptor.ownerId === target.owner.ownerId
|
|
2839
|
+
&& descriptor.leaseGeneration === target.owner.leaseGeneration
|
|
2840
|
+
&& descriptor.objectRole === contracts_1.ZLinkObjectRole.Server
|
|
2841
|
+
&& descriptor.state === contracts_1.ZLinkFrameworkRuntimeState.Serving
|
|
2842
|
+
&& lease.ownerId === target.owner.ownerId
|
|
2843
|
+
&& BigInt(lease.leaseGeneration) === target.owner.leaseGeneration
|
|
2844
|
+
&& leaseRead.value.expiresAt !== undefined
|
|
2845
|
+
&& leaseRead.value.expiresAt.getTime() > leaseRead.value.storeNow.getTime()
|
|
2846
|
+
? descriptor
|
|
2847
|
+
: undefined;
|
|
2848
|
+
}
|
|
2849
|
+
function conditionFor(key, read) {
|
|
2850
|
+
return read.kind === 'missing'
|
|
2851
|
+
? { kind: 'missing', key }
|
|
2852
|
+
: { kind: 'version', key, expected: read.value.version };
|
|
2853
|
+
}
|
|
2854
|
+
function versionCondition(key, read) {
|
|
2855
|
+
if (read.kind === 'missing') {
|
|
2856
|
+
throw new Error(`Required provider row '${key}' is missing.`);
|
|
2857
|
+
}
|
|
2858
|
+
return { kind: 'version', key, expected: read.value.version };
|
|
2859
|
+
}
|
|
2860
|
+
function emptyCapacityRecord() {
|
|
2861
|
+
return {
|
|
2862
|
+
active: { actors: 0, spots: 0, spotTypes: {} },
|
|
2863
|
+
pending: { actors: 0, spots: 0, spotTypes: {} }
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function capacityAvailable(descriptor, requested, capacity) {
|
|
2867
|
+
const spotType = requested.spotType;
|
|
2868
|
+
const typeCapacity = spotType === undefined
|
|
2869
|
+
? undefined
|
|
2870
|
+
: descriptor.populationCapacity.spotTypes.find(candidate => candidate.objectKind === spotType.objectKind
|
|
2871
|
+
&& candidate.stableType === spotType.stableType);
|
|
2872
|
+
const typeKey = spotType === undefined ? undefined : capacityTypeKey(spotType);
|
|
2873
|
+
return (descriptor.populationCapacity.actors.limit === 0
|
|
2874
|
+
|| capacity.active.actors + capacity.pending.actors + requested.actors
|
|
2875
|
+
<= descriptor.populationCapacity.actors.limit)
|
|
2876
|
+
&& (descriptor.populationCapacity.spots.limit === 0
|
|
2877
|
+
|| capacity.active.spots + capacity.pending.spots + requested.spots
|
|
2878
|
+
<= descriptor.populationCapacity.spots.limit)
|
|
2879
|
+
&& (spotType === undefined
|
|
2880
|
+
|| typeCapacity !== undefined
|
|
2881
|
+
&& (typeCapacity.limit === 0
|
|
2882
|
+
|| (capacity.active.spotTypes[typeKey] ?? 0)
|
|
2883
|
+
+ (capacity.pending.spotTypes[typeKey] ?? 0)
|
|
2884
|
+
+ spotType.count <= typeCapacity.limit));
|
|
2885
|
+
}
|
|
2886
|
+
function addCapacity(usage, delta) {
|
|
2887
|
+
return adjustCapacity(usage, delta, 1);
|
|
2888
|
+
}
|
|
2889
|
+
function subtractCapacity(usage, delta) {
|
|
2890
|
+
return adjustCapacity(usage, delta, -1);
|
|
2891
|
+
}
|
|
2892
|
+
function adjustCapacity(usage, delta, direction) {
|
|
2893
|
+
const spotTypes = { ...usage.spotTypes };
|
|
2894
|
+
if (delta.spotType !== undefined) {
|
|
2895
|
+
const key = capacityTypeKey(delta.spotType);
|
|
2896
|
+
const next = (spotTypes[key] ?? 0) + direction * delta.spotType.count;
|
|
2897
|
+
if (next < 0)
|
|
2898
|
+
throw new Error('Provider capacity counter underflow.');
|
|
2899
|
+
if (next === 0)
|
|
2900
|
+
delete spotTypes[key];
|
|
2901
|
+
else
|
|
2902
|
+
spotTypes[key] = next;
|
|
2903
|
+
}
|
|
2904
|
+
const actors = usage.actors + direction * delta.actors;
|
|
2905
|
+
const spots = usage.spots + direction * delta.spots;
|
|
2906
|
+
if (actors < 0 || spots < 0)
|
|
2907
|
+
throw new Error('Provider capacity counter underflow.');
|
|
2908
|
+
return { actors, spots, spotTypes };
|
|
2909
|
+
}
|
|
2910
|
+
function capacityTypeKey(value) {
|
|
2911
|
+
return `${value.objectKind}\0${value.stableType}`;
|
|
2912
|
+
}
|
|
2913
|
+
function cloneCapacity(value) {
|
|
2914
|
+
return {
|
|
2915
|
+
actors: value.actors,
|
|
2916
|
+
spots: value.spots,
|
|
2917
|
+
spotType: value.spotType === undefined ? undefined : { ...value.spotType }
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
function sameCapacityVector(left, right) {
|
|
2921
|
+
return left.actors === right.actors
|
|
2922
|
+
&& left.spots === right.spots
|
|
2923
|
+
&& left.spotType?.objectKind === right.spotType?.objectKind
|
|
2924
|
+
&& left.spotType?.stableType === right.spotType?.stableType
|
|
2925
|
+
&& left.spotType?.count === right.spotType?.count;
|
|
2926
|
+
}
|
|
2927
|
+
function sameCreationTarget(snapshot, target) {
|
|
2928
|
+
return snapshot.allocation.descriptor.meshName === target.meshName
|
|
2929
|
+
&& String(snapshot.allocation.descriptor.rid) === String(target.nodeRid)
|
|
2930
|
+
&& snapshot.allocation.descriptorLifecycleGeneration
|
|
2931
|
+
=== target.nodeLifecycleGeneration
|
|
2932
|
+
&& snapshot.ownerId === target.owner.ownerId
|
|
2933
|
+
&& snapshot.ownerLeaseGeneration === target.owner.leaseGeneration;
|
|
2934
|
+
}
|
|
2935
|
+
function sameLiveOwner(lease, snapshot) {
|
|
2936
|
+
if (liveOwnerLeaseGeneration(lease) !== snapshot.ownerLeaseGeneration)
|
|
2937
|
+
return false;
|
|
2938
|
+
const owner = decodeOwnerRecord(lease.value.bytes);
|
|
2939
|
+
return owner.ownerId === snapshot.ownerId;
|
|
2940
|
+
}
|
|
2941
|
+
function liveOwnerLeaseGeneration(lease) {
|
|
2942
|
+
if (lease.kind === 'missing'
|
|
2943
|
+
|| lease.value.expiresAt === undefined
|
|
2944
|
+
|| lease.value.expiresAt.getTime() <= lease.value.storeNow.getTime()) {
|
|
2945
|
+
return undefined;
|
|
2946
|
+
}
|
|
2947
|
+
return BigInt(decodeOwnerRecord(lease.value.bytes).leaseGeneration);
|
|
2948
|
+
}
|
|
2949
|
+
function canTakeOverStoredLocation(intent, currentOwnerId, currentLeaseGeneration, nextOwnerId, nextLeaseGeneration, liveLeaseGeneration) {
|
|
2950
|
+
if (intent === internal_location_contracts_1.ZLinkLocationWriteIntent.Takeover) {
|
|
2951
|
+
// Takeover is an explicit handoff operation. The provider CAS still
|
|
2952
|
+
// fences concurrent stale writes while the next owner lease is checked.
|
|
2953
|
+
return true;
|
|
2954
|
+
}
|
|
2955
|
+
if (intent !== internal_location_contracts_1.ZLinkLocationWriteIntent.NewClaim) {
|
|
2956
|
+
return false;
|
|
2957
|
+
}
|
|
2958
|
+
if (liveLeaseGeneration === undefined)
|
|
2959
|
+
return true;
|
|
2960
|
+
return currentOwnerId === nextOwnerId
|
|
2961
|
+
&& currentLeaseGeneration !== nextLeaseGeneration
|
|
2962
|
+
&& liveLeaseGeneration === nextLeaseGeneration;
|
|
2963
|
+
}
|
|
2964
|
+
function persistMeshDescriptor(descriptor) {
|
|
2965
|
+
return descriptor;
|
|
2966
|
+
}
|
|
2967
|
+
function reviveMeshDescriptor(descriptor) {
|
|
2968
|
+
const value = descriptor;
|
|
2969
|
+
if ('routingIdHex' in value) {
|
|
2970
|
+
const capacity = value.capacity;
|
|
2971
|
+
return {
|
|
2972
|
+
meshName: String(value.meshName),
|
|
2973
|
+
rid: (0, routing_id_1.decodeRoutingId)(String(value.routingIdHex), value.routingIdHex),
|
|
2974
|
+
lifecycleGeneration: BigInt(String(value.lifecycleGeneration)),
|
|
2975
|
+
descriptorRevision: BigInt(String(value.descriptorRevision)),
|
|
2976
|
+
endpoint: String(value.endpoint),
|
|
2977
|
+
objectRole: value.objectRole,
|
|
2978
|
+
entrySpotId: value.entrySpotId === null ? undefined : value.entrySpotId,
|
|
2979
|
+
placementWeight: Number(value.placementWeight),
|
|
2980
|
+
populationCapacity: reviveCanonicalCapacity(capacity),
|
|
2981
|
+
activationConcurrency: value.activationConcurrency,
|
|
2982
|
+
channelWeights: value.channelWeights,
|
|
2983
|
+
applicationVersion: BigInt(String(value.applicationVersion)),
|
|
2984
|
+
spotTypes: [],
|
|
2985
|
+
objectCapabilities: reviveCanonicalCapabilities(value.objectCapabilities),
|
|
2986
|
+
maintenanceWave: value.maintenanceWave === null ? undefined : value.maintenanceWave,
|
|
2987
|
+
state: runtimeState(String(value.state)),
|
|
2988
|
+
securityIdentity: String(value.securityIdentity),
|
|
2989
|
+
ownerId: String(value.ownerId),
|
|
2990
|
+
leaseGeneration: BigInt(String(value.leaseGeneration)),
|
|
2991
|
+
updatedAt: new Date(Number(value.updatedAtEpochMs))
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
return {
|
|
2995
|
+
...descriptor,
|
|
2996
|
+
rid: descriptor.rid,
|
|
2997
|
+
updatedAt: descriptor.updatedAt instanceof Date
|
|
2998
|
+
? descriptor.updatedAt
|
|
2999
|
+
: new Date(descriptor.updatedAt)
|
|
3000
|
+
};
|
|
3001
|
+
}
|
|
3002
|
+
function reviveClientServerDescriptor(descriptor) {
|
|
3003
|
+
const value = descriptor;
|
|
3004
|
+
if ('serverRoutingIdHex' in value) {
|
|
3005
|
+
return {
|
|
3006
|
+
channelName: String(value.channelName),
|
|
3007
|
+
serverRid: (0, routing_id_1.decodeRoutingId)(String(value.serverRoutingIdHex), value.serverRoutingIdHex),
|
|
3008
|
+
lifecycleGeneration: BigInt(String(value.lifecycleGeneration)),
|
|
3009
|
+
descriptorRevision: BigInt(String(value.descriptorRevision)),
|
|
3010
|
+
endpoint: String(value.endpoint),
|
|
3011
|
+
weight: Number(value.weight),
|
|
3012
|
+
state: runtimeState(String(value.state)),
|
|
3013
|
+
securityIdentity: String(value.securityIdentity),
|
|
3014
|
+
ownerId: String(value.ownerId),
|
|
3015
|
+
leaseGeneration: BigInt(String(value.leaseGeneration)),
|
|
3016
|
+
updatedAt: new Date(Number(value.updatedAtEpochMs))
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
return {
|
|
3020
|
+
...descriptor,
|
|
3021
|
+
serverRid: descriptor.serverRid,
|
|
3022
|
+
updatedAt: reviveDate(descriptor.updatedAt)
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
function reviveFanoutDescriptor(descriptor) {
|
|
3026
|
+
const value = descriptor;
|
|
3027
|
+
if ('publisherRoutingIdHex' in value) {
|
|
3028
|
+
return {
|
|
3029
|
+
channelName: String(value.channelName),
|
|
3030
|
+
publisherRid: (0, routing_id_1.decodeRoutingId)(String(value.publisherRoutingIdHex), value.publisherRoutingIdHex),
|
|
3031
|
+
lifecycleGeneration: BigInt(String(value.lifecycleGeneration)),
|
|
3032
|
+
descriptorRevision: BigInt(String(value.descriptorRevision)),
|
|
3033
|
+
endpoint: String(value.endpoint),
|
|
3034
|
+
state: runtimeState(String(value.state)),
|
|
3035
|
+
securityIdentity: String(value.securityIdentity),
|
|
3036
|
+
ownerId: String(value.ownerId),
|
|
3037
|
+
leaseGeneration: BigInt(String(value.leaseGeneration)),
|
|
3038
|
+
updatedAt: new Date(Number(value.updatedAtEpochMs))
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
return {
|
|
3042
|
+
...descriptor,
|
|
3043
|
+
publisherRid: descriptor.publisherRid,
|
|
3044
|
+
updatedAt: reviveDate(descriptor.updatedAt)
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
function reviveDate(value) {
|
|
3048
|
+
return value instanceof Date ? value : new Date(value);
|
|
3049
|
+
}
|
|
3050
|
+
function normalizeSpot(value) {
|
|
3051
|
+
return {
|
|
3052
|
+
...value,
|
|
3053
|
+
spotId: String(value.spotId),
|
|
3054
|
+
ownerNodeRid: String(value.ownerNodeRid),
|
|
3055
|
+
updatedAt: reviveDate(value.updatedAt)
|
|
3056
|
+
};
|
|
3057
|
+
}
|
|
3058
|
+
function normalizeActor(value) {
|
|
3059
|
+
return {
|
|
3060
|
+
...value,
|
|
3061
|
+
actorRef: {
|
|
3062
|
+
...value.actorRef,
|
|
3063
|
+
nodeRid: String(value.actorRef.nodeRid)
|
|
3064
|
+
},
|
|
3065
|
+
ownerNodeRid: String(value.ownerNodeRid),
|
|
3066
|
+
spotId: String(value.spotId),
|
|
3067
|
+
updatedAt: reviveDate(value.updatedAt)
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
3070
|
+
function normalizeRoute(value) {
|
|
3071
|
+
return {
|
|
3072
|
+
...value,
|
|
3073
|
+
ownerNodeRid: String(value.ownerNodeRid),
|
|
3074
|
+
value: Buffer.from(value.value),
|
|
3075
|
+
updatedAt: reviveDate(value.updatedAt)
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
function matchesSpot(value, filter) {
|
|
3079
|
+
return (filter.meshName === undefined || value.meshName === filter.meshName)
|
|
3080
|
+
&& (filter.spotType === undefined || value.spotType === filter.spotType)
|
|
3081
|
+
&& (filter.nodeRid === undefined || String(value.ownerNodeRid) === String(filter.nodeRid))
|
|
3082
|
+
&& (filter.spotKind === undefined || value.spotKind === filter.spotKind);
|
|
3083
|
+
}
|
|
3084
|
+
function matchesActor(value, filter) {
|
|
3085
|
+
return (filter.actorType === undefined || value.actorType === filter.actorType)
|
|
3086
|
+
&& (filter.nodeRid === undefined || String(value.ownerNodeRid) === String(filter.nodeRid))
|
|
3087
|
+
&& (filter.spotId === undefined || String(value.spotId) === String(filter.spotId))
|
|
3088
|
+
&& (filter.locationKind === undefined || value.spotKind === filter.locationKind);
|
|
3089
|
+
}
|
|
3090
|
+
function matchesRoute(value, filter) {
|
|
3091
|
+
return (filter.routeKind === undefined || value.routeKind === filter.routeKind)
|
|
3092
|
+
&& (filter.ownerNodeRid === undefined
|
|
3093
|
+
|| String(value.ownerNodeRid) === String(filter.ownerNodeRid))
|
|
3094
|
+
&& (filter.ownerId === undefined || value.ownerId === filter.ownerId);
|
|
3095
|
+
}
|
|
3096
|
+
function locationOwnerGeneration(value) {
|
|
3097
|
+
return value.leaseGeneration;
|
|
3098
|
+
}
|
|
3099
|
+
function descriptorOwnerLeaseGeneration(value) {
|
|
3100
|
+
if ('leaseGeneration' in value) {
|
|
3101
|
+
return typeof value.leaseGeneration === 'bigint'
|
|
3102
|
+
? value.leaseGeneration
|
|
3103
|
+
: BigInt(value.leaseGeneration);
|
|
3104
|
+
}
|
|
3105
|
+
if ('generation' in value) {
|
|
3106
|
+
return typeof value.generation === 'bigint'
|
|
3107
|
+
? value.generation
|
|
3108
|
+
: BigInt(value.generation);
|
|
3109
|
+
}
|
|
3110
|
+
return undefined;
|
|
3111
|
+
}
|
|
3112
|
+
function sameMeshDescriptor(left, right) {
|
|
3113
|
+
return Buffer.from(encodeJson(persistMeshDescriptor(left))).equals(Buffer.from(encodeJson(persistMeshDescriptor(right))));
|
|
3114
|
+
}
|
|
3115
|
+
function sameClientServerDescriptor(left, right) {
|
|
3116
|
+
return descriptorFingerprint(left, ['updatedAt']) === descriptorFingerprint(right, ['updatedAt']);
|
|
3117
|
+
}
|
|
3118
|
+
function sameFanoutDescriptor(left, right) {
|
|
3119
|
+
return descriptorFingerprint(left, ['updatedAt']) === descriptorFingerprint(right, ['updatedAt']);
|
|
3120
|
+
}
|
|
3121
|
+
function canRenewClientServer(current, next) {
|
|
3122
|
+
return sameOwnerGeneration(current, next)
|
|
3123
|
+
&& next.descriptorRevision > current.descriptorRevision
|
|
3124
|
+
&& descriptorFingerprint(current, ['descriptorRevision', 'weight', 'state', 'updatedAt'])
|
|
3125
|
+
=== descriptorFingerprint(next, ['descriptorRevision', 'weight', 'state', 'updatedAt']);
|
|
3126
|
+
}
|
|
3127
|
+
function canRenewFanout(current, next) {
|
|
3128
|
+
return sameOwnerGeneration(current, next)
|
|
3129
|
+
&& next.descriptorRevision > current.descriptorRevision
|
|
3130
|
+
&& descriptorFingerprint(current, ['descriptorRevision', 'state', 'updatedAt'])
|
|
3131
|
+
=== descriptorFingerprint(next, ['descriptorRevision', 'state', 'updatedAt']);
|
|
3132
|
+
}
|
|
3133
|
+
function sameOwnerGeneration(left, right) {
|
|
3134
|
+
return left.ownerId === right.ownerId
|
|
3135
|
+
&& left.leaseGeneration === right.leaseGeneration
|
|
3136
|
+
&& left.lifecycleGeneration === right.lifecycleGeneration;
|
|
3137
|
+
}
|
|
3138
|
+
function descriptorFingerprint(descriptor, omitted) {
|
|
3139
|
+
// Descriptor rows are canonicalized through routing-id hex. Comparing the
|
|
3140
|
+
// in-memory shape here would distinguish a binding RoutingId instance from
|
|
3141
|
+
// the equivalent ID revived from a canonical row, incorrectly rejecting a
|
|
3142
|
+
// legitimate renew as stale.
|
|
3143
|
+
const canonical = canonicalDescriptor(descriptor);
|
|
3144
|
+
for (const key of omitted) {
|
|
3145
|
+
delete canonical[key === 'updatedAt' ? 'updatedAtEpochMs' : key];
|
|
3146
|
+
}
|
|
3147
|
+
return Buffer.from(encodeJson(canonical)).toString('base64');
|
|
3148
|
+
}
|
|
3149
|
+
function validateClientServerDescriptor(descriptor) {
|
|
3150
|
+
validateDescriptorIdentity([
|
|
3151
|
+
descriptor.channelName,
|
|
3152
|
+
String(descriptor.serverRid),
|
|
3153
|
+
descriptor.endpoint,
|
|
3154
|
+
descriptor.securityIdentity,
|
|
3155
|
+
descriptor.ownerId
|
|
3156
|
+
], 'ClientServer');
|
|
3157
|
+
validateDescriptorGenerations(descriptor, 'ClientServer');
|
|
3158
|
+
if (!Number.isInteger(descriptor.weight) || descriptor.weight < 0 || descriptor.weight > 10_000) {
|
|
3159
|
+
throw new RangeError('ClientServer descriptor weight must be an integer in 0..10000.');
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
function validateFanoutPublisherDescriptor(descriptor) {
|
|
3163
|
+
validateDescriptorIdentity([
|
|
3164
|
+
descriptor.channelName,
|
|
3165
|
+
String(descriptor.publisherRid),
|
|
3166
|
+
descriptor.endpoint,
|
|
3167
|
+
descriptor.securityIdentity,
|
|
3168
|
+
descriptor.ownerId
|
|
3169
|
+
], 'Fanout publisher');
|
|
3170
|
+
validateDescriptorGenerations(descriptor, 'Fanout publisher');
|
|
3171
|
+
}
|
|
3172
|
+
function validateDescriptorIdentity(values, kind) {
|
|
3173
|
+
if (values.some(value => Buffer.byteLength(value, 'utf8') < 1 || value.includes('\0'))) {
|
|
3174
|
+
throw new TypeError(`${kind} descriptor identity and endpoint are required.`);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
function validateDescriptorGenerations(descriptor, kind) {
|
|
3178
|
+
if (descriptor.lifecycleGeneration < 1n || descriptor.lifecycleGeneration > MAX_GENERATION
|
|
3179
|
+
|| descriptor.descriptorRevision < 1n || descriptor.descriptorRevision > MAX_GENERATION
|
|
3180
|
+
|| descriptor.leaseGeneration < 1n || descriptor.leaseGeneration > MAX_GENERATION) {
|
|
3181
|
+
throw new RangeError(`${kind} descriptor generations are invalid.`);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
function rejected(updatedAt) {
|
|
3185
|
+
return {
|
|
3186
|
+
status: Locations_1.ZLinkLocationWriteStatus.RejectedConflict,
|
|
3187
|
+
generation: 0n,
|
|
3188
|
+
updatedAt
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
function requireOwnerInput(ownerId, leaseTtlMs) {
|
|
3192
|
+
if (Buffer.byteLength(ownerId, 'utf8') < 1 || ownerId.includes('\0')) {
|
|
3193
|
+
throw new TypeError('Owner ID must be non-empty UTF-8 without NUL.');
|
|
3194
|
+
}
|
|
3195
|
+
if (!Number.isSafeInteger(leaseTtlMs) || leaseTtlMs < 1) {
|
|
3196
|
+
throw new RangeError('Owner lease TTL must be a positive safe integer.');
|
|
3197
|
+
}
|
|
3198
|
+
}
|