@zlink-systems/framework 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActorClient.d.ts +1 -1
  3. package/dist/contracts/Channels/Calls.d.ts +1 -1
  4. package/dist/contracts/Configuration/Builders.d.ts +2 -1
  5. package/dist/contracts/Configuration/Configs.d.ts +0 -1
  6. package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
  7. package/dist/contracts/Configuration/InternalDefaults.js +4 -1
  8. package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
  9. package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
  10. package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
  11. package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -5
  12. package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
  13. package/dist/contracts/Spots/Contracts.d.ts +1 -1
  14. package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
  15. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
  16. package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
  17. package/dist/runtime/actors/actor-client.js +7 -10
  18. package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
  19. package/dist/runtime/actors/actor-mailbox.js +2 -2
  20. package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
  21. package/dist/runtime/actors/actor-request-deadline.js +4 -29
  22. package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
  23. package/dist/runtime/backend/contracts/index.d.ts +1 -4
  24. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
  25. package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
  26. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  27. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
  28. package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
  29. package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
  30. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  31. package/dist/runtime/channels/channel-clients.js +8 -10
  32. package/dist/runtime/channels/channel-envelope.d.ts +1 -1
  33. package/dist/runtime/channels/channel-envelope.js +4 -10
  34. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  35. package/dist/runtime/channels/channel-outbound-operations.js +20 -36
  36. package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
  37. package/dist/runtime/channels/channel-runtime-manager.js +8 -8
  38. package/dist/runtime/channels/channel-socket-options.js +0 -10
  39. package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
  40. package/dist/runtime/channels/channel-socket-registry.js +3 -14
  41. package/dist/runtime/channels/channel-transports.d.ts +10 -14
  42. package/dist/runtime/channels/channel-transports.js +11 -14
  43. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  44. package/dist/runtime/channels/local-spot-route-transport.js +1 -3
  45. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
  46. package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
  47. package/dist/runtime/channels/source-spot-router.d.ts +1 -1
  48. package/dist/runtime/channels/source-spot-router.js +2 -4
  49. package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
  50. package/dist/runtime/channels/spot-node-route-transport.js +2 -3
  51. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
  52. package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
  53. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
  54. package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
  55. package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
  56. package/dist/runtime/execution/serial-execution-queue.js +116 -28
  57. package/dist/runtime/foundation/operation-registry.d.ts +3 -2
  58. package/dist/runtime/foundation/operation-registry.js +5 -1
  59. package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
  60. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
  61. package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
  62. package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
  63. package/dist/runtime/foundation/service-stateful-registry.js +5 -2
  64. package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
  65. package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
  66. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
  67. package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
  68. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
  69. package/dist/runtime/foundation/service-topology-registry.js +16 -17
  70. package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
  71. package/dist/runtime/host/actor-packet-relay.js +8 -10
  72. package/dist/runtime/host/index.js +1 -15
  73. package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
  74. package/dist/runtime/host/instance-activation-authority.js +46 -97
  75. package/dist/runtime/host/remote-bound-session-relay.js +8 -8
  76. package/dist/runtime/host/spot-address-transport.d.ts +1 -9
  77. package/dist/runtime/host/spot-address-transport.js +47 -87
  78. package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
  79. package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
  80. package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
  81. package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
  82. package/dist/runtime/locations/in-memory-authority-store.js +22 -8
  83. package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
  84. package/dist/runtime/locations/in-memory-location-store.js +3 -0
  85. package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
  86. package/dist/runtime/locations/location-store-repository.d.ts +1 -0
  87. package/dist/runtime/locations/location-store-repository.js +98 -53
  88. package/dist/runtime/messaging/request-deadline.d.ts +3 -0
  89. package/dist/runtime/messaging/request-deadline.js +39 -0
  90. package/dist/runtime/messaging/submission-result.d.ts +2 -1
  91. package/dist/runtime/messaging/submission-result.js +11 -4
  92. package/dist/runtime/spots/spot-outbound.d.ts +3 -5
  93. package/dist/runtime/spots/spot-outbound.js +12 -15
  94. package/dist/runtime/spots/spot-serial-executor.js +4 -1
  95. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  96. package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
  97. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
  98. package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
  99. package/dist/runtime/streams/bound-session-service.js +1 -1
  100. package/dist/runtime/streams/index.d.ts +2 -3
  101. package/dist/runtime/streams/index.js +6 -5
  102. package/dist/runtime/streams/managed-stream.d.ts +2 -2
  103. package/dist/runtime/streams/managed-stream.js +3 -5
  104. package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
  105. package/dist/runtime/streams/protocol.js +0 -9
  106. package/dist/runtime/streams/session-calls.d.ts +5 -7
  107. package/dist/runtime/streams/session-calls.js +6 -23
  108. package/dist/runtime/streams/session-context.d.ts +4 -8
  109. package/dist/runtime/streams/session-context.js +3 -32
  110. package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
  111. package/dist/runtime/streams/session-serial-executor.js +0 -3
  112. package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
  113. package/dist/runtime/streams/stream-message-utils.js +0 -20
  114. package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
  115. package/dist/runtime/streams/stream-session-runtime.js +40 -85
  116. package/package.json +3 -3
  117. package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
  118. package/src/contracts/Channels/Calls.ts +1 -1
  119. package/src/contracts/Configuration/Builders.ts +2 -1
  120. package/src/contracts/Configuration/Configs.ts +0 -1
  121. package/src/contracts/Configuration/InternalDefaults.ts +4 -0
  122. package/src/contracts/Configuration/RegistrationBuilders.ts +13 -3
  123. package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
  124. package/src/contracts/Configuration/RegistrationTypes.ts +3 -5
  125. package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
  126. package/src/contracts/Spots/Contracts.ts +1 -1
  127. package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
  128. package/src/contracts/Streams/IZLinkSession.ts +2 -3
  129. package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
  130. package/src/runtime/actors/actor-client.ts +9 -15
  131. package/src/runtime/actors/actor-mailbox.ts +7 -2
  132. package/src/runtime/actors/actor-request-deadline.ts +4 -49
  133. package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
  134. package/src/runtime/backend/contracts/index.ts +1 -8
  135. package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
  136. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
  137. package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
  138. package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
  139. package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
  140. package/src/runtime/channels/channel-clients.ts +9 -19
  141. package/src/runtime/channels/channel-envelope.ts +16 -28
  142. package/src/runtime/channels/channel-outbound-operations.ts +36 -38
  143. package/src/runtime/channels/channel-runtime-manager.ts +6 -28
  144. package/src/runtime/channels/channel-socket-options.ts +0 -13
  145. package/src/runtime/channels/channel-socket-registry.ts +3 -17
  146. package/src/runtime/channels/channel-transports.ts +7 -33
  147. package/src/runtime/channels/local-spot-route-transport.ts +1 -5
  148. package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
  149. package/src/runtime/channels/source-spot-router.ts +2 -9
  150. package/src/runtime/channels/spot-node-route-transport.ts +2 -7
  151. package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
  152. package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
  153. package/src/runtime/execution/serial-execution-queue.ts +117 -29
  154. package/src/runtime/foundation/operation-registry.ts +16 -9
  155. package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
  156. package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
  157. package/src/runtime/foundation/service-stateful-registry.ts +10 -5
  158. package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
  159. package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
  160. package/src/runtime/foundation/service-topology-registry.ts +30 -20
  161. package/src/runtime/host/actor-packet-relay.ts +8 -15
  162. package/src/runtime/host/index.ts +2 -26
  163. package/src/runtime/host/instance-activation-authority.ts +58 -125
  164. package/src/runtime/host/remote-bound-session-relay.ts +8 -16
  165. package/src/runtime/host/spot-address-transport.ts +107 -166
  166. package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
  167. package/src/runtime/locations/domain-store-contract.ts +5 -0
  168. package/src/runtime/locations/in-memory-authority-store.ts +42 -8
  169. package/src/runtime/locations/in-memory-location-store.ts +8 -0
  170. package/src/runtime/locations/internal-store-contracts.ts +1 -0
  171. package/src/runtime/locations/location-store-repository.ts +130 -68
  172. package/src/runtime/messaging/request-deadline.ts +58 -0
  173. package/src/runtime/messaging/submission-result.ts +18 -4
  174. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  175. package/src/runtime/spots/spot-outbound.ts +14 -24
  176. package/src/runtime/spots/spot-serial-executor.ts +4 -1
  177. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
  178. package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
  179. package/src/runtime/streams/bound-session-service.ts +1 -1
  180. package/src/runtime/streams/index.ts +5 -5
  181. package/src/runtime/streams/managed-stream.ts +3 -13
  182. package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
  183. package/src/runtime/streams/protocol.ts +0 -13
  184. package/src/runtime/streams/session-calls.ts +8 -34
  185. package/src/runtime/streams/session-context.ts +6 -47
  186. package/src/runtime/streams/session-serial-executor.ts +0 -8
  187. package/src/runtime/streams/stream-message-utils.ts +1 -27
  188. package/src/runtime/streams/stream-session-runtime.ts +54 -97
  189. package/dist/runtime/streams/session-requests.d.ts +0 -25
  190. package/dist/runtime/streams/session-requests.js +0 -101
  191. package/src/runtime/streams/session-requests.ts +0 -110
@@ -1,3 +1,4 @@
1
+ import { ServiceRelocationAuthorityPayloadCodec } from '../foundation/service-relocation-runtime';
1
2
  import { UINT64_MAX } from '@zlink-systems/stream-wire';
2
3
  import { createHash, randomUUID } from 'node:crypto';
3
4
  import { zlinkRuntimeDefaultLocationOptions } from '../../contracts/Locations/Options';
@@ -17,6 +18,8 @@ import {
17
18
  type ZLinkStoreScanCursor,
18
19
  type ZLinkStoreVersion,
19
20
  type ZLinkStoreWriteRequest,
21
+ ZLinkFrameworkException,
22
+ ZLinkFrameworkErrorKind,
20
23
  ZLinkFrameworkRuntimeState,
21
24
  ZLinkObjectRole
22
25
  } from '../../contracts';
@@ -204,7 +207,30 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
204
207
  private readonly nowProvider: () => Date = () => new Date()
205
208
  ) {
206
209
  super(nowProvider);
207
- this.provider = provider;
210
+ // Classify only failures of the opaque SPI, before interpreting its records.
211
+ this.provider = {
212
+ async read(key, signal) {
213
+ try {
214
+ return await provider.read(key, signal);
215
+ } catch (error) {
216
+ throw locationStoreProviderFailure(error, signal);
217
+ }
218
+ },
219
+ async write(request, signal) {
220
+ try {
221
+ return await provider.write(request, signal);
222
+ } catch (error) {
223
+ throw locationStoreProviderFailure(error, signal);
224
+ }
225
+ },
226
+ async scan(request, signal) {
227
+ try {
228
+ return await provider.scan(request, signal);
229
+ } catch (error) {
230
+ throw locationStoreProviderFailure(error, signal);
231
+ }
232
+ }
233
+ };
208
234
  this.aggregateInventory = new ZLinkAggregateInventoryStore(this.provider);
209
235
  }
210
236
 
@@ -1099,6 +1125,77 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1099
1125
  }
1100
1126
  }
1101
1127
 
1128
+ override async releaseEndedReservation(
1129
+ key: ZLinkAuthorityKey,
1130
+ expectedStoreVersion: string,
1131
+ signal?: AbortSignal
1132
+ ): Promise<boolean> {
1133
+ const rowKey = authorityKey(key.value);
1134
+ const current = await this.provider.read(rowKey, signal);
1135
+ if (current.kind === 'missing' || current.value.version.value !== expectedStoreVersion)
1136
+ return false;
1137
+ const record = decodeAuthorityRecord(current.value.bytes);
1138
+ const snapshot = record.snapshot;
1139
+ if (
1140
+ snapshot.allocation.state !== 'reserved' ||
1141
+ snapshot.pendingCreation === undefined ||
1142
+ record.aggregate !== undefined ||
1143
+ hasRelocationAuthority(snapshot.payload)
1144
+ )
1145
+ return false;
1146
+ const leaseKey = ownerKey(snapshot.ownerId);
1147
+ const descriptorKey = meshKey(
1148
+ snapshot.allocation.descriptor.meshName,
1149
+ snapshot.allocation.descriptor.rid
1150
+ );
1151
+ const capacityRowKey = capacityKey(
1152
+ snapshot.allocation.descriptor.meshName,
1153
+ String(snapshot.allocation.descriptor.rid)
1154
+ );
1155
+ const [lease, descriptor, capacity] = await Promise.all([
1156
+ this.provider.read(leaseKey, signal),
1157
+ this.provider.read(descriptorKey, signal),
1158
+ this.provider.read(capacityRowKey, signal)
1159
+ ]);
1160
+ if (lease.kind === 'found' && lease.value.expiresAt === undefined)
1161
+ throw new Error('Location Store owner lease record is invalid.');
1162
+ if (
1163
+ sameLiveOwner(lease, snapshot) &&
1164
+ descriptor.kind === 'found' &&
1165
+ reviveMeshDescriptor(
1166
+ decodeCanonicalDescriptorRecord<ZLinkMeshNodeDescriptor>(descriptor.value.bytes).descriptor
1167
+ ).lifecycleGeneration === snapshot.allocation.descriptorLifecycleGeneration
1168
+ )
1169
+ return false;
1170
+ const mutations: ZLinkStoreWriteRequest['mutations'][number][] = [
1171
+ { kind: 'delete', key: rowKey }
1172
+ ];
1173
+ if (capacity.kind === 'found') {
1174
+ const stored = decodeJson<CapacityRecord>(capacity.value.bytes);
1175
+ mutations.push({
1176
+ kind: 'put',
1177
+ key: capacityRowKey,
1178
+ bytes: encodeJson({
1179
+ active: stored.active,
1180
+ pending: subtractCapacity(stored.pending, snapshot.allocation.capacity)
1181
+ } satisfies CapacityRecord)
1182
+ });
1183
+ }
1184
+ const result = await this.provider.write(
1185
+ {
1186
+ conditions: [
1187
+ { kind: 'version', key: rowKey, expected: current.value.version },
1188
+ conditionFor(leaseKey, lease),
1189
+ conditionFor(descriptorKey, descriptor),
1190
+ conditionFor(capacityRowKey, capacity)
1191
+ ],
1192
+ mutations
1193
+ },
1194
+ signal
1195
+ );
1196
+ return result.kind === 'applied';
1197
+ }
1198
+
1102
1199
  override async reserve(
1103
1200
  request: ZLinkObjectReserveRequest,
1104
1201
  signal?: AbortSignal,
@@ -1130,62 +1227,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1130
1227
  return { kind: 'alreadyExists', current: snapshot };
1131
1228
  }
1132
1229
  if (
1133
- record.aggregate !== undefined ||
1134
- isCanonicalAuthorityPayload(record.snapshot.payload)
1135
- ) {
1136
- // A relocation record carries its own recovery protocol. Reserve
1137
- // may reclaim only an unfinished plain creation.
1138
- return { kind: 'conflict', current: snapshot };
1139
- }
1140
- // The row is Reserved by another owner. An unfinished creation
1141
- // whose owner lease ended is cancellable, so reclaim it and retry
1142
- // instead of blocking the key forever.
1143
- const staleOwnerKey = ownerKey(snapshot.ownerId);
1144
- const staleCapacityKey = capacityKey(
1145
- snapshot.allocation.descriptor.meshName,
1146
- String(snapshot.allocation.descriptor.rid)
1147
- );
1148
- const [staleOwnerRead, staleCapacityRead] = await Promise.all([
1149
- this.provider.read(staleOwnerKey, signal),
1150
- this.provider.read(staleCapacityKey, signal)
1151
- ]);
1152
- if (staleOwnerRead.kind === 'found' && staleOwnerRead.value.expiresAt === undefined) {
1153
- // A lease is always written with a positive TTL, so a missing
1154
- // expiry is a corrupt record. Reclaiming deletes authority state,
1155
- // so refuse rather than read the absence as expiry.
1156
- throw new Error('Location Store owner lease record is invalid.');
1157
- }
1158
- if (sameLiveOwner(staleOwnerRead, record.snapshot)) {
1230
+ !(await this.releaseEndedReservation(
1231
+ encodedAuthorityKey,
1232
+ current.value.version.value,
1233
+ signal
1234
+ ))
1235
+ )
1159
1236
  return { kind: 'conflict', current: snapshot };
1160
- }
1161
- const staleCapacity =
1162
- staleCapacityRead.kind === 'missing'
1163
- ? emptyCapacityRecord()
1164
- : decodeJson<CapacityRecord>(staleCapacityRead.value.bytes);
1165
- await this.provider.write(
1166
- {
1167
- conditions: [
1168
- { kind: 'version', key: rowKey, expected: current.value.version },
1169
- versionCondition(staleOwnerKey, staleOwnerRead),
1170
- conditionFor(staleCapacityKey, staleCapacityRead)
1171
- ],
1172
- mutations: [
1173
- { kind: 'delete', key: rowKey },
1174
- {
1175
- kind: 'put',
1176
- key: staleCapacityKey,
1177
- bytes: encodeJson({
1178
- active: staleCapacity.active,
1179
- pending: subtractCapacity(
1180
- staleCapacity.pending,
1181
- record.snapshot.allocation.capacity
1182
- )
1183
- } satisfies CapacityRecord)
1184
- }
1185
- ]
1186
- },
1187
- signal
1188
- );
1189
1237
  continue;
1190
1238
  }
1191
1239
  const [descriptorRead, leaseRead] = await Promise.all([
@@ -1193,7 +1241,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1193
1241
  this.provider.read(leaseKey, signal)
1194
1242
  ]);
1195
1243
  const descriptor = liveTargetDescriptor(descriptorRead, leaseRead, request.target);
1196
- if (descriptor === undefined) {
1244
+ if (descriptor === undefined || descriptor.state !== ZLinkFrameworkRuntimeState.Serving) {
1197
1245
  return { kind: 'conflict', current: { kind: 'missing', storeNow: current.storeNow } };
1198
1246
  }
1199
1247
  const [capacityRead, objectGenerationRead, authorityOwnerGenerationRead] = await Promise.all([
@@ -1394,9 +1442,11 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1394
1442
  ) {
1395
1443
  return { kind: 'stale' };
1396
1444
  }
1445
+ const descriptorKey = meshKey(request.target.meshName, request.target.nodeRid);
1397
1446
  const leaseKey = ownerKey(request.target.owner.ownerId);
1398
1447
  const capacityRowKey = capacityKey(request.target.meshName, String(request.target.nodeRid));
1399
- const [leaseRead, capacityRead] = await Promise.all([
1448
+ const [descriptorRead, leaseRead, capacityRead] = await Promise.all([
1449
+ this.provider.read(descriptorKey, signal),
1400
1450
  this.provider.read(leaseKey, signal),
1401
1451
  this.provider.read(capacityRowKey, signal)
1402
1452
  ]);
@@ -1406,7 +1456,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1406
1456
  // rather than read the absence as expiry.
1407
1457
  throw new Error('Location Store owner lease record is invalid.');
1408
1458
  }
1409
- if (!sameLiveOwner(leaseRead, record.snapshot)) return { kind: 'stale' };
1459
+ if (liveTargetDescriptor(descriptorRead, leaseRead, request.target) === undefined)
1460
+ return { kind: 'stale' };
1410
1461
  const capacity =
1411
1462
  capacityRead.kind === 'missing'
1412
1463
  ? emptyCapacityRecord()
@@ -1415,6 +1466,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1415
1466
  {
1416
1467
  conditions: [
1417
1468
  { kind: 'version', key: rowKey, expected: current.value.version },
1469
+ versionCondition(descriptorKey, descriptorRead),
1418
1470
  leaseValueCondition(leaseKey, {
1419
1471
  ownerId: record.snapshot.ownerId,
1420
1472
  leaseGeneration: record.snapshot.ownerLeaseGeneration
@@ -3904,7 +3956,6 @@ function liveTargetDescriptor(
3904
3956
  descriptor.ownerId === target.owner.ownerId &&
3905
3957
  descriptor.leaseGeneration === target.owner.leaseGeneration &&
3906
3958
  descriptor.objectRole === ZLinkObjectRole.Server &&
3907
- descriptor.state === ZLinkFrameworkRuntimeState.Serving &&
3908
3959
  lease.ownerId === target.owner.ownerId &&
3909
3960
  BigInt(lease.leaseGeneration) === target.owner.leaseGeneration &&
3910
3961
  leaseRead.value.expiresAt !== undefined &&
@@ -4034,15 +4085,10 @@ function sameCreationTarget(
4034
4085
  );
4035
4086
  }
4036
4087
 
4037
- const CANONICAL_AUTHORITY_MAGIC = Buffer.from('ZLAU');
4088
+ const relocationAuthorityCodec = new ServiceRelocationAuthorityPayloadCodec();
4038
4089
 
4039
- function isCanonicalAuthorityPayload(payload: Uint8Array): boolean {
4040
- return (
4041
- payload.byteLength >= CANONICAL_AUTHORITY_MAGIC.byteLength &&
4042
- Buffer.from(payload.buffer, payload.byteOffset, CANONICAL_AUTHORITY_MAGIC.byteLength).equals(
4043
- CANONICAL_AUTHORITY_MAGIC
4044
- )
4045
- );
4090
+ function hasRelocationAuthority(payload: Uint8Array): boolean {
4091
+ return relocationAuthorityCodec.read(payload) !== undefined;
4046
4092
  }
4047
4093
 
4048
4094
  function sameLiveOwner(lease: ZLinkStoreReadResult, snapshot: StoredAuthoritySnapshot): boolean {
@@ -4438,3 +4484,19 @@ function throwIfOperationExpired(signal?: AbortSignal, deadlineUnixMs?: bigint):
4438
4484
  throw new DOMException('Operation deadline exceeded.', 'TimeoutError');
4439
4485
  }
4440
4486
  }
4487
+
4488
+ // Store SPI §6: preserve caller failures and classify provider failures once.
4489
+ function locationStoreProviderFailure(error: unknown, signal?: AbortSignal): unknown {
4490
+ if (
4491
+ error instanceof ZLinkFrameworkException ||
4492
+ error instanceof TypeError ||
4493
+ error instanceof RangeError ||
4494
+ signal?.aborted === true
4495
+ )
4496
+ return error;
4497
+ return new ZLinkFrameworkException(
4498
+ ZLinkFrameworkErrorKind.Unavailable,
4499
+ 'Location Store provider is unavailable.',
4500
+ error
4501
+ );
4502
+ }
@@ -0,0 +1,58 @@
1
+ import { awaitWithAbort } from '../abort';
2
+ import {
3
+ createInternalFrameworkException,
4
+ ZLinkFrameworkInternalErrorKind
5
+ } from '../framework-errors-internal';
6
+
7
+ export function remainingRequestTimeout(
8
+ operation: string,
9
+ deadlineMs: number | undefined
10
+ ): number | undefined {
11
+ if (deadlineMs === undefined) return undefined;
12
+ const remaining = deadlineMs - performance.now();
13
+ if (remaining <= 0) {
14
+ throw createInternalFrameworkException(
15
+ ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
16
+ `${operation} exceeded its deadline.`
17
+ );
18
+ }
19
+ return Math.max(1, Math.ceil(remaining));
20
+ }
21
+
22
+ /** Bounds caller waiting across outbound admission and reply; late completions stay observed. */
23
+ export async function waitRequestReply<T>(
24
+ reply: Promise<T>,
25
+ operation: string,
26
+ deadlineMs: number | undefined,
27
+ signal?: AbortSignal,
28
+ endWait?: () => void,
29
+ discardLateReply?: (reply: T) => void
30
+ ): Promise<T> {
31
+ let timer: ReturnType<typeof setTimeout> | undefined;
32
+ let waiting = reply;
33
+ if (deadlineMs !== undefined) {
34
+ const timeout = new Promise<never>((_resolve, reject) => {
35
+ const remaining = remainingRequestTimeout(operation, deadlineMs);
36
+ timer = setTimeout(
37
+ () =>
38
+ reject(
39
+ createInternalFrameworkException(
40
+ ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
41
+ `${operation} exceeded its deadline.`
42
+ )
43
+ ),
44
+ remaining
45
+ );
46
+ });
47
+ waiting = Promise.race([reply, timeout]);
48
+ }
49
+ try {
50
+ return await awaitWithAbort(waiting, signal);
51
+ } catch (error) {
52
+ endWait?.();
53
+ if (discardLateReply !== undefined) void reply.then(discardLateReply, () => undefined);
54
+ throw error;
55
+ } finally {
56
+ clearTimeout(timer);
57
+ }
58
+ }
@@ -18,7 +18,7 @@ export interface ZLinkSubmitResult {
18
18
  readonly status: ZLinkSubmitStatus;
19
19
  }
20
20
 
21
- /** Projects the Core submit result once; phase only determines capacity refusal meaning. */
21
+ /** Projects immediate refusal and pending admission failure from their Core phase. */
22
22
  export function submitToRequestResult(result: number, phase: 'submit' | 'completion'): number {
23
23
  switch (result) {
24
24
  case SubmitResult.Ok:
@@ -28,7 +28,7 @@ export function submitToRequestResult(result: number, phase: 'submit' | 'complet
28
28
  case SubmitResult.NotConnected:
29
29
  return RequestResult.NotConnected;
30
30
  case SubmitResult.NotFound:
31
- return RequestResult.NotFound;
31
+ return phase === 'submit' ? RequestResult.NotFound : RequestResult.NotConnected;
32
32
  case SubmitResult.NotAdmitted:
33
33
  return RequestResult.Rejected;
34
34
  case SubmitResult.InvalidHandle:
@@ -49,7 +49,7 @@ export function submitToRequestResult(result: number, phase: 'submit' | 'complet
49
49
  export function classifySubmitResult(
50
50
  result: number,
51
51
  operation: string,
52
- phase: 'submit' | 'completion' = 'completion'
52
+ phase: 'submit' | 'completion' = 'submit'
53
53
  ): ZLinkSubmitResult {
54
54
  const terminal = submitToRequestResult(result, phase);
55
55
  switch (terminal) {
@@ -80,12 +80,17 @@ export function requireOneWayCompletion(
80
80
  case ZLinkSubmitStatus.Submitted:
81
81
  return;
82
82
  case ZLinkSubmitStatus.TimedOut:
83
- case ZLinkSubmitStatus.Backpressured:
84
83
  throw createInternalFrameworkException(
85
84
  ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
86
85
  `${operation} did not obtain admission before its deadline.`,
87
86
  true
88
87
  );
88
+ case ZLinkSubmitStatus.Backpressured:
89
+ throw createInternalFrameworkException(
90
+ ZLinkFrameworkInternalErrorKind.RouteNotConnected,
91
+ `${operation} has no admission wait token.`,
92
+ true
93
+ );
89
94
  case ZLinkSubmitStatus.TargetNotFound:
90
95
  throw createInternalFrameworkException(notFoundKind, `${operation} target was not found.`);
91
96
  case ZLinkSubmitStatus.RouteNotConnected:
@@ -106,6 +111,15 @@ export function requirePublishCompletion(result: ZLinkSubmitResult, operation: s
106
111
  requireOneWayCompletion(result, operation);
107
112
  }
108
113
 
114
+ export function requireClassicFanoutCompletion(result: ZLinkSubmitResult, operation: string): void {
115
+ requireOneWayCompletion(
116
+ result.status === ZLinkSubmitStatus.Backpressured
117
+ ? { status: ZLinkSubmitStatus.TimedOut }
118
+ : result,
119
+ operation
120
+ );
121
+ }
122
+
109
123
  export function throwAlreadySubmitted(operation: string): never {
110
124
  throw createInternalFrameworkException(
111
125
  ZLinkFrameworkInternalErrorKind.AlreadySubmitted,
@@ -1,4 +1,4 @@
1
- // <auto-generated> DO NOT EDIT. schema-sha256: 77748ba8f585cb70ad337fa3203b5f9b9aa7a89e7f95f7fdc20159c30987be0f
1
+ // <auto-generated> DO NOT EDIT. schema-sha256: 818fcd2f767793be8b5f626e5ac688807e671e546af2b11786e6fbaa02010ba5
2
2
  export interface ServiceWireRouteFence { readonly id: string; readonly generation: bigint; readonly targetNodeRid: Uint8Array; readonly targetNodeGeneration: bigint; readonly expectedAuthorityOwnerGeneration: bigint; readonly expectedOwnerLeaseGeneration: bigint; }
3
3
  export interface ApplicationPayloadEnvelopeV1 { readonly packetName: string; readonly contentType: string; readonly payload: Uint8Array; }
4
4
  export interface ActorJoin28 { readonly correlation: bigint; readonly actor: ServiceWireRouteFence; readonly entry: boolean; readonly targetSpot: ServiceWireRouteFence; readonly payload?: ApplicationPayloadEnvelopeV1; }
@@ -191,7 +191,7 @@ export interface ZLinkSpotAddressTransport {
191
191
  sendToSpotAddress(
192
192
  spotId: RoutingId,
193
193
  message: unknown,
194
- options: ZLinkSpotAddressCallOptions
194
+ options: Omit<ZLinkSpotAddressCallOptions, 'timeoutMs' | 'signal'>
195
195
  ): Promise<ZLinkSubmitResult>;
196
196
  requestToSpotAddress<TReply = unknown>(
197
197
  spotId: RoutingId,
@@ -228,14 +228,12 @@ export interface ZLinkSpotRoutedTransport {
228
228
  export interface ZLinkSpotRoutedSendOptions {
229
229
  readonly instanceSpot?: boolean;
230
230
  readonly packetName?: string;
231
- /** Internal end-to-end budget remaining for route admission. */
232
- readonly timeoutMs?: number;
233
- readonly signal?: AbortSignal;
234
231
  readonly metadata?: ReadonlyMap<string, string>;
235
232
  }
236
233
 
237
234
  export interface ZLinkSpotRoutedRequestOptions extends ZLinkSpotRoutedSendOptions {
238
235
  readonly timeoutMs?: number;
236
+ readonly signal?: AbortSignal;
239
237
  }
240
238
 
241
239
  interface MutableAddressCallOptions {
@@ -283,13 +281,13 @@ function createAddressedSpotSendCall(
283
281
  );
284
282
  return this;
285
283
  },
286
- async submit(signal?: AbortSignal): Promise<void> {
284
+ async submit(): Promise<void> {
287
285
  markSubmitted(options);
288
286
  const pending = startRequestOnSerial(serial, () => ({
289
287
  pending: transport.sendToSpotAddress(
290
288
  spotId,
291
289
  message,
292
- freezeAddressCallOptions(options, signal, sourceSpotProvider?.())
290
+ freezeAddressCallOptions(options, sourceSpotProvider?.())
293
291
  )
294
292
  }));
295
293
  const result = await (serial.isCurrentTurn ? pending : deliverOnSerial(serial, pending));
@@ -314,11 +312,11 @@ function createAddressedSpotRequestCall(
314
312
  markSubmitted(options);
315
313
  rejectSameSpotAsyncRequest(serial, spotId, waitPolicy);
316
314
  return startRequestOnSerial<TReply>(serial, () => ({
317
- pending: transport.requestToSpotAddress<TReply>(
318
- spotId,
319
- request,
320
- freezeAddressCallOptions(options, signal, sourceSpotProvider?.())
321
- )
315
+ pending: transport.requestToSpotAddress<TReply>(spotId, request, {
316
+ ...freezeAddressCallOptions(options, sourceSpotProvider?.()),
317
+ timeoutMs: options.timeoutMs,
318
+ signal
319
+ })
322
320
  }));
323
321
  };
324
322
  return {
@@ -422,7 +420,6 @@ function markSubmitted(options: MutableAddressCallOptions): void {
422
420
 
423
421
  function freezeAddressCallOptions(
424
422
  options: MutableAddressCallOptions,
425
- signal: AbortSignal | undefined,
426
423
  sourceSpot: ZLinkBackendSpot | undefined
427
424
  ): ZLinkSpotAddressCallOptions {
428
425
  return {
@@ -430,8 +427,6 @@ function freezeAddressCallOptions(
430
427
  instanceSpot: options.instanceSpot,
431
428
  instanceSpotType: options.instanceSpotType,
432
429
  initialMeshName: options.initialMeshName,
433
- timeoutMs: options.timeoutMs,
434
- signal,
435
430
  sourceSpot
436
431
  };
437
432
  }
@@ -453,8 +448,8 @@ function wrapSendCall(serial: ZLinkSpotSerialTurnExecutor, inner: ZLinkSendCall)
453
448
  else inner.metadata(key);
454
449
  return this;
455
450
  },
456
- async submit(signal?: AbortSignal) {
457
- const result = await runInternalTransportStart(serial, () => inner.submit(signal));
451
+ async submit() {
452
+ const result = await runInternalTransportStart(serial, () => inner.submit());
458
453
  return result;
459
454
  }
460
455
  };
@@ -548,11 +543,10 @@ function wrapRoutedSpotSendCall(
548
543
  }
549
544
  return this;
550
545
  },
551
- async submit(signal?: AbortSignal): Promise<void> {
546
+ async submit(): Promise<void> {
552
547
  const pending = startRequestOnSerial(serial, () => ({
553
548
  pending: sendToSpotHandle(transport, spot, message, {
554
549
  metadata,
555
- signal,
556
550
  spotRouterChannelIdForMesh,
557
551
  sourceSpot: sourceSpotProvider?.()
558
552
  })
@@ -645,7 +639,7 @@ export async function sendToSpotHandle(
645
639
  transport: ZLinkSpotRoutedTransport,
646
640
  spot: SpotHandle,
647
641
  message: unknown,
648
- options: ZLinkSpotHandleCallOptions = {}
642
+ options: Omit<ZLinkSpotHandleCallOptions, 'timeoutMs' | 'signal'> = {}
649
643
  ): Promise<ZLinkSubmitResult> {
650
644
  const packetName = resolveFrameworkPacketName(message, undefined, 'SPOT');
651
645
  const sendResolved = async (resolved: ResolvedSpotHandle): Promise<ZLinkSubmitResult> => {
@@ -653,20 +647,16 @@ export async function sendToSpotHandle(
653
647
  if (options.sourceSpot !== undefined && transport.sendFromSpotToSpot !== undefined) {
654
648
  return transport.sendFromSpotToSpot(options.sourceSpot, target, message, {
655
649
  packetName,
656
- timeoutMs: options.timeoutMs,
657
- signal: options.signal,
658
650
  metadata: options.metadata
659
651
  });
660
652
  }
661
653
  return transport.sendToSpot(target, message, {
662
654
  packetName,
663
- timeoutMs: options.timeoutMs,
664
- signal: options.signal,
665
655
  metadata: options.metadata
666
656
  });
667
657
  };
668
658
 
669
- return sendResolved(await requireSpotRef(spot, options.signal));
659
+ return sendResolved(await requireSpotRef(spot));
670
660
  }
671
661
 
672
662
  export async function requestToSpotHandle<TReply = unknown>(
@@ -134,7 +134,10 @@ export class ZLinkSpotSerialExecutor {
134
134
  this.throwIfClosing();
135
135
  let executor = this.actorExecutors.get(actorId);
136
136
  if (executor === undefined) {
137
- executor = new ZLinkActorSerialExecutor(actorId, this.sourceSpotId, this.actorOptions);
137
+ executor =
138
+ this.executionMode === ZLinkUserSpotExecutionMode.SpotWide
139
+ ? this.spotSerial.createActorExecutor(actorId, this.sourceSpotId, this.actorOptions)
140
+ : new ZLinkActorSerialExecutor(actorId, this.sourceSpotId, this.actorOptions);
138
141
  this.actorExecutors.set(actorId, executor);
139
142
  }
140
143
  return executor;
@@ -19,6 +19,7 @@ import {
19
19
  type ZLinkSerialWorkRecord
20
20
  } from '../execution/serial-execution-queue';
21
21
  import type { ZLinkSerialLifecycleContext } from '../execution/serial-execution-queue';
22
+ import { ZLinkActorSerialExecutor } from '../actors/actor-mailbox';
22
23
 
23
24
  export type ZLinkSpotApplicationTurnContext = ZLinkExecutionBarrierClaim & {
24
25
  readonly replay?: (failure?: unknown) => Promise<unknown> | unknown;
@@ -53,6 +54,15 @@ export class ZLinkSpotSerialTurnExecutor {
53
54
  return this.depth > 0;
54
55
  }
55
56
 
57
+ /** Creates a passive Actor mailbox consumed by this Spot's scheduler. */
58
+ createActorExecutor(
59
+ actorId: string,
60
+ sourceSpotId: unknown,
61
+ options?: ZLinkSerialSchedulerOptions
62
+ ): ZLinkActorSerialExecutor {
63
+ return new ZLinkActorSerialExecutor(actorId, sourceSpotId, options, this.scheduler);
64
+ }
65
+
56
66
  get isCurrentTurn(): boolean {
57
67
  return this.depth > 0 && isCurrentZLinkSpotSerialTurn(this);
58
68
  }