@serve.zone/interfaces 32.25.0 → 32.26.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/changelog.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-25 - 32.26.0
4
+
5
+ ### Features
6
+
7
+ - Add the service runtime reasons for the object-storage binding environment, and the helper that
8
+ reports them.
9
+ - `TServiceRuntimeWaitingReason` gains `objectstorage-retention-pending` (a binding declares
10
+ compliance retention whose evidence has not arrived). `TServiceRuntimeRefusal` gains
11
+ `objectstorage-endpoint-missing` (the binding's node states no valid `corestoreObjectStorage`),
12
+ `objectstorage-binding-invalid` (`createObjectStorageBindingEnvironment()` refuses the binding)
13
+ and `objectstorage-environment-conflict` (the service's own environment or another binding
14
+ already sets a name). An object-storage binding placed on no node keeps `bindings-pending`.
15
+ - `composeObjectStorageServiceEnvironment(serviceEnvironment, sources)` composes a service's
16
+ environment from `IObjectStorageBindingEnvironmentSource { binding, endpoint }` and answers a
17
+ `TObjectStorageServiceEnvironmentComposition`: the environment, or the first refusal, otherwise
18
+ the first wait, in binding id order, with the binding id and the contract's message. A binding
19
+ waiting for evidence already claims its names, so a collision with it is refused at once.
20
+ `TObjectStorageEnvironmentWaitingReason` and `TObjectStorageEnvironmentRefusal` name its
21
+ reasons. `createObjectStorageBindingEnvironment()` refuses exactly as before, with the same
22
+ messages.
23
+
24
+ ### Maintenance
25
+
26
+ - Bring the release-time tooling current: `@git.zone/tstest` 6.2.0 → 6.2.1; pnpm stays 12.5.0 and `@types/node` 26.6.1 by the seven-day rule; the other @git.zone tools are current.
27
+
3
28
  ## 2026-09-25 - 32.25.0
4
29
 
5
30
  ### Features
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '32.25.0',
6
+ version: '32.26.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
@@ -71,16 +71,19 @@ export declare const serviceRuntimeContract: Readonly<{
71
71
  /** The largest `limit` `getClusterRuntimeAssignments` accepts; Cloudly refuses a larger one. */
72
72
  readonly maximumAssignmentPageSize: 256;
73
73
  }>;
74
- declare const serviceRuntimeWaitingReasons: readonly ["node-network-pending", "conflict", "failure-backoff", "storage-import-pending", "bindings-pending", "legacy-adoption-pending"];
75
- declare const serviceRuntimeRefusals: readonly ["cluster-runtime-missing", "organization-missing", "image-plan-missing", "platform-unsupported", "secrets-unsupported", "volumes-unsupported", "port-protocol-unsupported", "ports-too-many", "readiness-target-undeclared", "environment-too-large", "no-eligible-node", "replica-limit", "node-authority-unsupported", "network-policy-missing", "network-policy-mismatch", "network-workload-pool-missing", "network-workload-pool-exhausted", "volume-driver-unsupported", "volume-options-unsupported", "volume-shared-unsupported", "volume-placement-unsupported", "volume-initialization-unknown", "volume-ownership-unknown"];
74
+ declare const serviceRuntimeWaitingReasons: readonly ["node-network-pending", "conflict", "failure-backoff", "storage-import-pending", "bindings-pending", "legacy-adoption-pending", "objectstorage-retention-pending"];
75
+ declare const serviceRuntimeRefusals: readonly ["cluster-runtime-missing", "organization-missing", "image-plan-missing", "platform-unsupported", "secrets-unsupported", "volumes-unsupported", "port-protocol-unsupported", "ports-too-many", "readiness-target-undeclared", "environment-too-large", "no-eligible-node", "replica-limit", "node-authority-unsupported", "network-policy-missing", "network-policy-mismatch", "network-workload-pool-missing", "network-workload-pool-exhausted", "volume-driver-unsupported", "volume-options-unsupported", "volume-shared-unsupported", "volume-placement-unsupported", "volume-initialization-unknown", "volume-ownership-unknown", "objectstorage-endpoint-missing", "objectstorage-binding-invalid", "objectstorage-environment-conflict"];
76
76
  /**
77
77
  * Why the producer is not converging yet, though nothing needs an administrator:
78
78
  * `node-network-pending` waits for a node's network readiness, `conflict` for a sweep over inputs
79
79
  * that changed while it ran, `failure-backoff` for a failed replica's backoff to elapse,
80
80
  * `storage-import-pending` for an `import` local storage claim whose bytes have not arrived on its
81
- * node, `bindings-pending` for the secret manifest to cover every enabled binding of the service, and
82
- * `legacy-adoption-pending` for a Corestore binding whose legacy resource waits for the offline
83
- * adoption Corestore requires (the binding's `statusReason` of the same name).
81
+ * node, `bindings-pending` for the secret manifest to cover every enabled binding of the service (and
82
+ * for an object-storage binding placed on no node), `legacy-adoption-pending` for a Corestore binding
83
+ * whose legacy resource waits for the offline adoption Corestore requires (the binding's
84
+ * `statusReason` of the same name), and `objectstorage-retention-pending` for an object-storage
85
+ * binding that declares compliance retention whose evidence has not arrived: its workload is not
86
+ * handed the bucket as if it were ordinary storage.
84
87
  */
85
88
  export type TServiceRuntimeWaitingReason = typeof serviceRuntimeWaitingReasons[number];
86
89
  /**
@@ -100,6 +103,12 @@ export type TServiceRuntimeWaitingReason = typeof serviceRuntimeWaitingReasons[n
100
103
  * `volume-initialization-unknown` is a volume without an initialization and
101
104
  * `volume-ownership-unknown` a `run-as` ownership under `runAs: null`
102
105
  * (`deriveServiceVolumeClaimInitialization`); neither is defaulted.
106
+ *
107
+ * The object-storage refusals name why a binding's environment cannot be composed
108
+ * (`composeObjectStorageServiceEnvironment`): `objectstorage-endpoint-missing` a binding whose node
109
+ * states no valid `corestoreObjectStorage`, `objectstorage-binding-invalid` a binding
110
+ * `createObjectStorageBindingEnvironment` refuses, and `objectstorage-environment-conflict` a name
111
+ * the service's own environment or another binding already sets.
103
112
  */
104
113
  export type TServiceRuntimeRefusal = typeof serviceRuntimeRefusals[number];
105
114
  interface IServiceRuntimeStatusBase {
@@ -16,7 +16,7 @@ export const serviceRuntimeContract = Object.freeze({
16
16
  });
17
17
  const serviceRuntimeWaitingReasons = [
18
18
  'node-network-pending', 'conflict', 'failure-backoff', 'storage-import-pending', 'bindings-pending',
19
- 'legacy-adoption-pending',
19
+ 'legacy-adoption-pending', 'objectstorage-retention-pending',
20
20
  ];
21
21
  const serviceRuntimeRefusals = [
22
22
  'cluster-runtime-missing', 'organization-missing', 'image-plan-missing', 'platform-unsupported',
@@ -26,6 +26,7 @@ const serviceRuntimeRefusals = [
26
26
  'network-workload-pool-missing', 'network-workload-pool-exhausted',
27
27
  'volume-driver-unsupported', 'volume-options-unsupported', 'volume-shared-unsupported',
28
28
  'volume-placement-unsupported', 'volume-initialization-unknown', 'volume-ownership-unknown',
29
+ 'objectstorage-endpoint-missing', 'objectstorage-binding-invalid', 'objectstorage-environment-conflict',
29
30
  ];
30
31
  /** Sorted, duplicate-free and read by the vocabulary the ids belong to: a node's, or the canonical one. */
31
32
  const sortedIds = (value, maximum, read) => networkSorted(networkList(value, maximum, read), id => id);
@@ -99,4 +100,4 @@ export const snapshotServiceRuntimeStatus = (value) => producerSafe(() => {
99
100
  requireNetwork(item.state === 'refused' && isOneOf(serviceRuntimeRefusals, item.reason));
100
101
  return { ...base, state: item.state, reason: item.reason };
101
102
  });
102
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZXJ1bnRpbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3NlcnZpY2VydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlBLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsY0FBYyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQzVILGNBQWMsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNoRyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQ2hILG9CQUFvQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUF3RDVELE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDbEQsc0JBQXNCLEVBQUUsRUFBRTtJQUMxQiwrRkFBK0Y7SUFDL0YsZUFBZSxFQUFFLEVBQUU7SUFDbkIsb0hBQW9IO0lBQ3BILGtCQUFrQixFQUFFLCtCQUErQixDQUFDLHdCQUF3QjtJQUM1RSx3QkFBd0IsRUFBRSwrQkFBK0IsQ0FBQyx3QkFBd0I7SUFDbEYsMEZBQTBGO0lBQzFGLG1CQUFtQixFQUFFLEdBQUc7SUFDeEIsZ0dBQWdHO0lBQ2hHLHlCQUF5QixFQUFFLEdBQUc7Q0FDdEIsQ0FBQyxDQUFDO0FBRVosTUFBTSw0QkFBNEIsR0FBRztJQUNuQyxzQkFBc0IsRUFBRSxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsd0JBQXdCLEVBQUUsa0JBQWtCO0lBQ25HLHlCQUF5QjtDQUNqQixDQUFDO0FBQ1gsTUFBTSxzQkFBc0IsR0FBRztJQUM3Qix5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxvQkFBb0IsRUFBRSxzQkFBc0I7SUFDL0YscUJBQXFCLEVBQUUscUJBQXFCLEVBQUUsMkJBQTJCLEVBQUUsZ0JBQWdCO0lBQzNGLDZCQUE2QixFQUFFLHVCQUF1QixFQUFFLGtCQUFrQixFQUFFLGVBQWU7SUFDM0YsNEJBQTRCLEVBQUUsd0JBQXdCLEVBQUUseUJBQXlCO0lBQ2pGLCtCQUErQixFQUFFLGlDQUFpQztJQUNsRSwyQkFBMkIsRUFBRSw0QkFBNEIsRUFBRSwyQkFBMkI7SUFDdEYsOEJBQThCLEVBQUUsK0JBQStCLEVBQUUsMEJBQTBCO0NBQ25GLENBQUM7QUF1RVgsMkdBQTJHO0FBQzNHLE1BQU0sU0FBUyxHQUFHLENBQUMsS0FBYyxFQUFFLE9BQWUsRUFBRSxJQUErQixFQUFZLEVBQUUsQ0FDL0YsYUFBYSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7QUFFN0QsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFjLEVBQTRCLEVBQUU7SUFDL0QsTUFBTSxJQUFJLEdBQUcsU0FBUyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztJQUN0QyxJQUFJLElBQUksS0FBSyxZQUFZLEVBQUUsQ0FBQztRQUMxQixhQUFhLENBQUMsS0FBSyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMvQixPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDbEIsQ0FBQztJQUNELGNBQWMsQ0FBQyxJQUFJLEtBQUssT0FBTyxDQUFDLENBQUM7SUFDakMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsc0JBQXNCLENBQUMsa0JBQWtCLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDdkksY0FBYyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDbkMsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQztBQUMzQixDQUFDLENBQUM7QUFFRixNQUFNLE9BQU8sR0FBRyxDQUFDLEtBQWMsRUFBbUMsRUFBRTtJQUNsRSxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDaEMsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ3JELGNBQWMsQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksdUJBQXVCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDMUYsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7QUFDaEQsQ0FBQyxDQUFDO0FBRUYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxLQUFjLEVBQTBCLEVBQUU7SUFDM0QsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLFdBQVcsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2hILGNBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksb0JBQW9CLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQztXQUN0RixrQkFBa0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksT0FBTyxJQUFJLENBQUMsY0FBYyxLQUFLLFNBQVMsQ0FBQyxDQUFDO0lBQ2hGLDRHQUE0RztJQUM1RyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksRUFBRSxZQUFZLENBQUMsQ0FBQztJQUNqRSxNQUFNLEVBQUUsZ0JBQWdCLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztJQUMxRSxPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXO1FBQy9ELFNBQVMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBNEI7UUFDbEYsSUFBSSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztBQUNySSxDQUFDLENBQUM7QUFFRixNQUFNLFNBQVMsR0FBRyxDQUFDLEtBQWMsRUFBMEIsRUFBRTtJQUMzRCxNQUFNLElBQUksR0FBRyxTQUFTLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3RDLElBQUksSUFBSSxLQUFLLFVBQVUsRUFBRSxDQUFDO1FBQ3hCLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQy9CLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNsQixDQUFDO0lBQ0QsY0FBYyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQztJQUNwQyxNQUFNLElBQUksR0FBRyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUMsTUFBTSxFQUFFLGNBQWMsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7SUFDbkYsY0FBYyxDQUFDLE9BQU8sSUFBSSxDQUFDLFlBQVksS0FBSyxTQUFTLENBQUMsQ0FBQztJQUN2RCxPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxJQUFJLENBQUMsWUFBWTtRQUM1QyxtQkFBbUIsRUFBRSxTQUFTLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLHNCQUFzQixDQUFDLHdCQUF3QixFQUFFLFNBQVMsQ0FBQyxFQUFFLENBQUM7QUFDM0gsQ0FBQyxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsQ0FBQyxLQUFjLEVBQXVCLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxFQUFFO0lBQ25HLE1BQU0sSUFBSSxHQUFHLGFBQWEsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLFdBQVc7UUFDakcsaUJBQWlCLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztJQUNoRSxjQUFjLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDLEVBQUUsc0JBQXNCLENBQUMsc0JBQXNCLENBQUM7V0FDbEcsT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLFNBQVMsQ0FBQyxDQUFDO0lBQ3JDLE9BQU8sRUFBRSxFQUFFLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxjQUFjLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxTQUFTLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDbkgsU0FBUyxFQUFFLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO1FBQzlGLE9BQU8sRUFBRSxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLFFBQVEsRUFBRSxjQUFjLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7QUFDbEgsQ0FBQyxDQUFDLENBQUM7QUFFSCx3RkFBd0Y7QUFDeEYsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQUcsQ0FBQyxLQUFjLEVBQXlCLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxFQUFFO0lBQ3ZHLE1BQU0sSUFBSSxHQUFHLGFBQWEsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsY0FBYyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUMxRyxNQUFNLElBQUksR0FBOEIsRUFBRSxFQUFFLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxZQUFZLEVBQUUsY0FBYyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUM7UUFDL0csU0FBUyxFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztJQUM5QyxJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssV0FBVyxJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssWUFBWSxFQUFFLENBQUM7UUFDOUQsY0FBYyxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLENBQUM7UUFDckMsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUN0RCxDQUFDO0lBQ0QsSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQzdCLGNBQWMsQ0FBQyxPQUFPLENBQUMsNEJBQTRCLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDbkUsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDN0QsQ0FBQztJQUNELGNBQWMsQ0FBQyxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsSUFBSSxPQUFPLENBQUMsc0JBQXNCLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekYsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7QUFDN0QsQ0FBQyxDQUFDLENBQUMifQ==
103
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZXJ1bnRpbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3NlcnZpY2VydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlBLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsY0FBYyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQzVILGNBQWMsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNoRyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQ2hILG9CQUFvQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUF3RDVELE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDbEQsc0JBQXNCLEVBQUUsRUFBRTtJQUMxQiwrRkFBK0Y7SUFDL0YsZUFBZSxFQUFFLEVBQUU7SUFDbkIsb0hBQW9IO0lBQ3BILGtCQUFrQixFQUFFLCtCQUErQixDQUFDLHdCQUF3QjtJQUM1RSx3QkFBd0IsRUFBRSwrQkFBK0IsQ0FBQyx3QkFBd0I7SUFDbEYsMEZBQTBGO0lBQzFGLG1CQUFtQixFQUFFLEdBQUc7SUFDeEIsZ0dBQWdHO0lBQ2hHLHlCQUF5QixFQUFFLEdBQUc7Q0FDdEIsQ0FBQyxDQUFDO0FBRVosTUFBTSw0QkFBNEIsR0FBRztJQUNuQyxzQkFBc0IsRUFBRSxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsd0JBQXdCLEVBQUUsa0JBQWtCO0lBQ25HLHlCQUF5QixFQUFFLGlDQUFpQztDQUNwRCxDQUFDO0FBQ1gsTUFBTSxzQkFBc0IsR0FBRztJ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
@@ -1,5 +1,6 @@
1
1
  import type { IPlatformBinding } from './types.js';
2
2
  import type { IPlatformObjectStorageEndpoint, TObjectStorageEndpointEnvironment, TObjectStorageEndpointEnvironmentKey } from './objectstorage.js';
3
+ import type { TServiceRuntimeRefusal, TServiceRuntimeWaitingReason } from '../data/serviceruntime.js';
3
4
  import type { TObjectStorageRetentionEnvironment, TObjectStorageRetentionEnvironmentKey } from './objectstorageretention.js';
4
5
  /**
5
6
  * Every non-secret environment name an object-storage binding can give a workload: the nine
@@ -32,6 +33,9 @@ export type TObjectStorageBindingEnvironment = TObjectStorageEndpointEnvironment
32
33
  * The evidence's reconciliation generation and binding-request digest are read from its own
33
34
  * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
34
35
  * does not repeat them. Everything the binding does state is checked again here.
36
+ *
37
+ * `composeObjectStorageServiceEnvironment()` answers the same questions with a service runtime
38
+ * reason instead of an error.
35
39
  */
36
40
  export declare const createObjectStorageBindingEnvironment: (bindingArg: IPlatformBinding, endpointArg: IPlatformObjectStorageEndpoint) => Promise<TObjectStorageBindingEnvironment>;
37
41
  /**
@@ -47,3 +51,58 @@ export declare const validateObjectStorageBindingEnvironment: (environmentArg: u
47
51
  * would otherwise have one value silently replace the other. The inputs are not changed.
48
52
  */
49
53
  export declare const mergeObjectStorageBindingEnvironment: (environmentArg: Readonly<Record<string, string>>, bindingEnvironmentArg: TObjectStorageBindingEnvironment) => Record<string, string>;
54
+ /** One enabled object-storage binding of a service and the endpoint of the node it is placed on. */
55
+ export interface IObjectStorageBindingEnvironmentSource {
56
+ binding: IPlatformBinding;
57
+ /**
58
+ * The `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on, `null`
59
+ * when that node states none. A binding placed on no node is not a source: its service waits as
60
+ * `bindings-pending`.
61
+ */
62
+ endpoint: IPlatformObjectStorageEndpoint | null;
63
+ }
64
+ /**
65
+ * The service runtime waiting reason an object-storage environment reports:
66
+ * `objectstorage-retention-pending`, a binding that declares compliance retention whose evidence
67
+ * has not arrived.
68
+ */
69
+ export type TObjectStorageEnvironmentWaitingReason = Extract<TServiceRuntimeWaitingReason, 'objectstorage-retention-pending'>;
70
+ /**
71
+ * The service runtime refusals an object-storage environment reports.
72
+ * `objectstorage-endpoint-missing` is a binding whose node states no valid
73
+ * `corestoreObjectStorage`; `objectstorage-binding-invalid` a binding
74
+ * `createObjectStorageBindingEnvironment()` refuses (not `objectstorage`, no canonical bucket, no
75
+ * canonical retention intent, or evidence that does not prove this binding);
76
+ * `objectstorage-environment-conflict` a name the service's own environment or another binding
77
+ * already sets.
78
+ */
79
+ export type TObjectStorageEnvironmentRefusal = Extract<TServiceRuntimeRefusal, 'objectstorage-endpoint-missing' | 'objectstorage-binding-invalid' | 'objectstorage-environment-conflict'>;
80
+ /** A service's environment with every object-storage binding added, or the one reason it is not. */
81
+ export type TObjectStorageServiceEnvironmentComposition = {
82
+ status: 'composed';
83
+ environment: Record<string, string>;
84
+ } | {
85
+ status: 'waiting';
86
+ reason: TObjectStorageEnvironmentWaitingReason;
87
+ bindingId: string;
88
+ message: string;
89
+ } | {
90
+ status: 'refused';
91
+ reason: TObjectStorageEnvironmentRefusal;
92
+ bindingId: string;
93
+ message: string;
94
+ };
95
+ /**
96
+ * The service's own environment with the environment of every enabled object-storage binding
97
+ * added, or the service runtime reason it cannot be — so a producer reports the contract's
98
+ * verdict instead of classifying its errors.
99
+ *
100
+ * Each binding's names are `createObjectStorageBindingEnvironment()`'s from its node's endpoint,
101
+ * added as `mergeObjectStorageBindingEnvironment()` adds them. Bindings are taken in id order, so
102
+ * the verdict does not depend on the order of `sourcesArg`. A refusal outranks a wait, because a
103
+ * wait resolves on its own and a refusal never does: a binding waiting for retention evidence
104
+ * already claims all twenty of its names, so a collision with it is refused now, not once its
105
+ * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
106
+ * composed environment. The inputs are not changed.
107
+ */
108
+ export declare const composeObjectStorageServiceEnvironment: (serviceEnvironmentArg: Readonly<Record<string, string>>, sourcesArg: readonly IObjectStorageBindingEnvironmentSource[]) => Promise<TObjectStorageServiceEnvironmentComposition>;
@@ -1,5 +1,5 @@
1
- import { createObjectStorageEndpointEnvironment, objectStorageEndpointEnvironmentKeys, validateObjectStorageEndpointEnvironment, validatePlatformObjectStorageBucketName, } from './objectstorage.js';
2
- import { createObjectStorageRetentionEnvironment, objectStorageRetentionEnvironmentKeys, validateObjectStorageRetentionEnvironment, validateObjectStorageRetentionIntent, } from './objectstorageretention.js';
1
+ import { createObjectStorageEndpointEnvironment, objectStorageEndpointEnvironmentKeys, validateObjectStorageEndpointEnvironment, validatePlatformObjectStorageBucketName, validatePlatformObjectStorageEndpoint, } from './objectstorage.js';
2
+ import { createObjectStorageRetentionEnvironment, objectStorageRetentionEnvironmentKeys, validateObjectStorageRetentionEnvironment, validateObjectStorageRetentionEvidence, validateObjectStorageRetentionIntent, } from './objectstorageretention.js';
3
3
  import { isPlainRecord, readDataProperties } from '../private/dataproperties.js';
4
4
  /**
5
5
  * Every non-secret environment name an object-storage binding can give a workload: the nine
@@ -10,65 +10,98 @@ export const objectStorageBindingEnvironmentKeys = Object.freeze([
10
10
  ...objectStorageRetentionEnvironmentKeys,
11
11
  ]);
12
12
  /**
13
- * The environment Cloudly composes into a workload's runtime configuration for one binding.
14
- *
15
- * `bindingArg` is the Cloudly binding document and `endpointArg` the
16
- * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
17
- * name, never guessed:
18
- *
19
- * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
20
- * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
21
- * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
22
- * not proven yet, so the workload is not given an environment that would let it run as if its
23
- * bucket were ordinary storage; the caller waits for the evidence.
24
- * - evidence that does not prove the binding's own service, binding id, bucket and intent.
25
- *
26
- * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
27
- * retention, so there is nothing to state.
28
- *
29
- * The evidence's reconciliation generation and binding-request digest are read from its own
30
- * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
31
- * does not repeat them. Everything the binding does state is checked again here.
13
+ * The one place a binding's environment is decided. `createObjectStorageBindingEnvironment()`
14
+ * throws its message, `composeObjectStorageServiceEnvironment()` reports its reason.
32
15
  */
33
- export const createObjectStorageBindingEnvironment = async (bindingArg, endpointArg) => {
16
+ const resolveBindingEnvironment = async (bindingArg, endpointArg) => {
34
17
  const binding = readDataProperties(bindingArg);
35
18
  if (binding === null || binding.capability !== 'objectstorage') {
36
- throw new Error('objectstorage binding environment requires an objectstorage binding');
19
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
20
+ message: 'objectstorage binding environment requires an objectstorage binding' };
37
21
  }
38
22
  const bindingName = typeof binding.id === 'string' ? binding.id : '(unnamed)';
39
23
  if (validatePlatformObjectStorageBucketName(binding.objectstorageBucketName).length > 0) {
40
- throw new Error(`objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns it and `
41
- + 'no environment invents one');
24
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
25
+ message: `objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns `
26
+ + 'it and no environment invents one' };
27
+ }
28
+ if (endpointArg === null) {
29
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing',
30
+ message: `objectstorage binding ${bindingName} is placed on a node that states no `
31
+ + 'corestoreObjectStorage endpoint' };
32
+ }
33
+ const endpointErrors = validatePlatformObjectStorageEndpoint(endpointArg);
34
+ if (endpointErrors.length > 0) {
35
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing', message: endpointErrors[0] };
42
36
  }
43
37
  const bucketName = binding.objectstorageBucketName;
44
38
  const endpointEnvironment = createObjectStorageEndpointEnvironment(endpointArg, bucketName);
45
39
  const retention = binding.objectstorageRetention;
46
40
  if (retention === undefined) {
47
- return endpointEnvironment;
41
+ return { status: 'composed', environment: endpointEnvironment };
48
42
  }
49
43
  const retentionBinding = readDataProperties(retention);
50
44
  if (retentionBinding === null
51
45
  || validateObjectStorageRetentionIntent(retentionBinding.intent).length > 0) {
52
- throw new Error(`objectstorage binding ${bindingName} must declare canonical compliance-retention intent`);
46
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
47
+ message: `objectstorage binding ${bindingName} must declare canonical compliance-retention `
48
+ + 'intent' };
53
49
  }
54
50
  const evidence = retentionBinding.evidence;
55
51
  if (evidence === undefined) {
56
- throw new Error(`objectstorage binding ${bindingName} declares compliance retention without verified `
57
- + 'evidence; its workload environment waits for the evidence');
52
+ return { status: 'waiting', reason: 'objectstorage-retention-pending',
53
+ message: `objectstorage binding ${bindingName} declares compliance retention without `
54
+ + 'verified evidence; its workload environment waits for the evidence' };
58
55
  }
59
56
  const authority = isPlainRecord(evidence) ? readDataProperties(evidence.authority) : null;
60
57
  if (authority === null) {
61
- throw new Error('objectstorage retention evidence must use its exact schema');
58
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
59
+ message: 'objectstorage retention evidence must use its exact schema' };
62
60
  }
63
- const retentionEnvironment = await createObjectStorageRetentionEnvironment(evidence, {
61
+ const expectation = {
64
62
  serviceId: binding.serviceId,
65
63
  bindingId: binding.id,
66
64
  reconciliationGeneration: authority.reconciliationGeneration,
67
65
  bindingRequestDigest: authority.bindingRequestDigest,
68
66
  bucketName,
69
67
  intent: retentionBinding.intent,
70
- });
71
- return { ...endpointEnvironment, ...retentionEnvironment };
68
+ };
69
+ const evidenceErrors = await validateObjectStorageRetentionEvidence(evidence, expectation);
70
+ if (evidenceErrors.length > 0) {
71
+ return { status: 'refused', reason: 'objectstorage-binding-invalid', message: evidenceErrors[0] };
72
+ }
73
+ const retentionEnvironment = await createObjectStorageRetentionEnvironment(evidence, expectation);
74
+ return { status: 'composed', environment: { ...endpointEnvironment, ...retentionEnvironment } };
75
+ };
76
+ /**
77
+ * The environment Cloudly composes into a workload's runtime configuration for one binding.
78
+ *
79
+ * `bindingArg` is the Cloudly binding document and `endpointArg` the
80
+ * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
81
+ * name, never guessed:
82
+ *
83
+ * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
84
+ * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
85
+ * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
86
+ * not proven yet, so the workload is not given an environment that would let it run as if its
87
+ * bucket were ordinary storage; the caller waits for the evidence.
88
+ * - evidence that does not prove the binding's own service, binding id, bucket and intent.
89
+ *
90
+ * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
91
+ * retention, so there is nothing to state.
92
+ *
93
+ * The evidence's reconciliation generation and binding-request digest are read from its own
94
+ * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
95
+ * does not repeat them. Everything the binding does state is checked again here.
96
+ *
97
+ * `composeObjectStorageServiceEnvironment()` answers the same questions with a service runtime
98
+ * reason instead of an error.
99
+ */
100
+ export const createObjectStorageBindingEnvironment = async (bindingArg, endpointArg) => {
101
+ const resolution = await resolveBindingEnvironment(bindingArg, endpointArg);
102
+ if (resolution.status !== 'composed')
103
+ throw new Error(resolution.message);
104
+ return resolution.environment;
72
105
  };
73
106
  /**
74
107
  * Validates one binding's environment: the nine endpoint names, the eleven retention names all or
@@ -99,6 +132,9 @@ export const validateObjectStorageBindingEnvironment = async (environmentArg, pa
99
132
  }
100
133
  return errors;
101
134
  };
135
+ const findEnvironmentCollisions = (claimedKeysArg, keysArg) => keysArg.filter((keyArg) => claimedKeysArg.has(keyArg)).sort();
136
+ const describeEnvironmentCollisions = (collisionsArg) => (`objectstorage binding environment collides with ${collisionsArg.join(', ')} already set on the `
137
+ + 'workload');
102
138
  /**
103
139
  * A workload environment with one binding's environment added.
104
140
  *
@@ -107,13 +143,52 @@ export const validateObjectStorageBindingEnvironment = async (environmentArg, pa
107
143
  * would otherwise have one value silently replace the other. The inputs are not changed.
108
144
  */
109
145
  export const mergeObjectStorageBindingEnvironment = (environmentArg, bindingEnvironmentArg) => {
110
- const collisions = Object.keys(bindingEnvironmentArg)
111
- .filter((keyArg) => Object.hasOwn(environmentArg, keyArg))
112
- .sort();
113
- if (collisions.length > 0) {
114
- throw new Error(`objectstorage binding environment collides with ${collisions.join(', ')} already set on the `
115
- + 'workload');
116
- }
146
+ const collisions = findEnvironmentCollisions(new Set(Object.keys(environmentArg)), Object.keys(bindingEnvironmentArg));
147
+ if (collisions.length > 0)
148
+ throw new Error(describeEnvironmentCollisions(collisions));
117
149
  return { ...environmentArg, ...bindingEnvironmentArg };
118
150
  };
119
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZWVudmlyb25tZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcGxhdGZvcm0vb2JqZWN0c3RvcmFnZWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1BLE9BQU8sRUFDTCxzQ0FBc0MsRUFDdEMsb0NBQW9DLEVBQ3BDLHdDQUF3QyxFQUN4Qyx1Q0FBdUMsR0FDeEMsTUFBTSxvQkFBb0IsQ0FBQztBQU81QixPQUFPLEVBQ0wsdUNBQXVDLEVBQ3ZDLHFDQUFxQyxFQUNyQyx5Q0FBeUMsRUFDekMsb0NBQW9DLEdBQ3JDLE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxFQUFFLGFBQWEsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRWpGOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1DQUFtQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDL0QsR0FBRyxvQ0FBb0M7SUFDdkMsR0FBRyxxQ0FBcUM7Q0FDaEMsQ0FBQyxDQUFDO0FBY1o7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEdBQUcsS0FBSyxFQUN4RCxVQUE0QixFQUM1QixXQUEyQyxFQUNBLEVBQUU7SUFDN0MsTUFBTSxPQUFPLEdBQUcsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDL0MsSUFBSSxPQUFPLEtBQUssSUFBSSxJQUFJLE9BQU8sQ0FBQyxVQUFVLEtBQUssZUFBZSxFQUFFLENBQUM7UUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxxRUFBcUUsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7SUFDRCxNQUFNLFdBQVcsR0FBRyxPQUFPLE9BQU8sQ0FBQyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUM7SUFDOUUsSUFBSSx1Q0FBdUMsQ0FBQyxPQUFPLENBQUMsdUJB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
151
+ /**
152
+ * The service's own environment with the environment of every enabled object-storage binding
153
+ * added, or the service runtime reason it cannot be — so a producer reports the contract's
154
+ * verdict instead of classifying its errors.
155
+ *
156
+ * Each binding's names are `createObjectStorageBindingEnvironment()`'s from its node's endpoint,
157
+ * added as `mergeObjectStorageBindingEnvironment()` adds them. Bindings are taken in id order, so
158
+ * the verdict does not depend on the order of `sourcesArg`. A refusal outranks a wait, because a
159
+ * wait resolves on its own and a refusal never does: a binding waiting for retention evidence
160
+ * already claims all twenty of its names, so a collision with it is refused now, not once its
161
+ * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
162
+ * composed environment. The inputs are not changed.
163
+ */
164
+ export const composeObjectStorageServiceEnvironment = async (serviceEnvironmentArg, sourcesArg) => {
165
+ const sources = [...sourcesArg].sort((leftArg, rightArg) => (leftArg.binding.id < rightArg.binding.id ? -1 : leftArg.binding.id > rightArg.binding.id ? 1 : 0));
166
+ let environment = { ...serviceEnvironmentArg };
167
+ const claimedKeys = new Set(Object.keys(environment));
168
+ let wait = null;
169
+ for (const source of sources) {
170
+ const bindingId = source.binding.id;
171
+ const resolution = await resolveBindingEnvironment(source.binding, source.endpoint);
172
+ if (resolution.status === 'refused') {
173
+ return { status: 'refused', reason: resolution.reason, bindingId, message: resolution.message };
174
+ }
175
+ const keys = resolution.status === 'composed'
176
+ ? Object.keys(resolution.environment)
177
+ : objectStorageBindingEnvironmentKeys;
178
+ const collisions = findEnvironmentCollisions(claimedKeys, keys);
179
+ if (collisions.length > 0) {
180
+ return { status: 'refused', reason: 'objectstorage-environment-conflict', bindingId,
181
+ message: describeEnvironmentCollisions(collisions) };
182
+ }
183
+ for (const key of keys)
184
+ claimedKeys.add(key);
185
+ if (resolution.status === 'composed') {
186
+ environment = { ...environment, ...resolution.environment };
187
+ }
188
+ else {
189
+ wait ??= { status: 'waiting', reason: resolution.reason, bindingId, message: resolution.message };
190
+ }
191
+ }
192
+ return wait ?? { status: 'composed', environment };
193
+ };
194
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZWVudmlyb25tZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcGxhdGZvcm0vb2JqZWN0c3RvcmFnZWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1BLE9BQU8sRUFDTCxzQ0FBc0MsRUFDdEMsb0NBQW9DLEVBQ3BDLHdDQUF3QyxFQUN4Qyx1Q0FBdUMsRUFDdkMscUNBQXFDLEdBQ3RDLE1BQU0sb0JBQW9CLENBQUM7QUFTNUIsT0FBTyxFQUNMLHVDQUF1QyxFQUN2QyxxQ0FBcUMsRUFDckMseUNBQXlDLEVBQ3pDLHNDQUFzQyxFQUN0QyxvQ0FBb0MsR0FDckMsTUFBTSw2QkFBNkIsQ0FBQztBQUNyQyxPQUFPLEVBQUUsYUFBYSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFakY7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUMvRCxHQUFHLG9DQUFvQztJQUN2QyxHQUFHLHFDQUFxQztDQUNoQyxDQUFDLENBQUM7QUF3Qlo7OztHQUdHO0FBQ0gsTUFBTSx5QkFBeUIsR0FBRyxLQUFLLEVBQ3JDLFVBQTRCLEVBQzVCLFdBQWtELEVBQ1YsRUFBRTtJQUMxQyxNQUFNLE9BQU8sR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUMvQyxJQUFJLE9BQU8sS0FBSyxJQUFJLElBQUksT0FBTyxDQUFDLFVBQVUsS0FBSyxlQUFlLEVBQUUsQ0FBQztRQUMvRCxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsK0JBQStCO1lBQ2pFLE9BQU8sRUFBRSxxRUFBcUUsRUFBRSxDQUFDO0lBQ3JGLENBQUM7SUFDRCxNQUFNLFdBQVcsR0FBRyxPQUFPLE9BQU8sQ0FBQyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxXQUFX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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/interfaces",
3
- "version": "32.25.0",
3
+ "version": "32.26.0",
4
4
  "private": false,
5
5
  "description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
6
6
  "exports": {
@@ -30,7 +30,7 @@
30
30
  "@git.zone/tsbuild": "^5.0.0",
31
31
  "@git.zone/tsdoc": "^2.2.2",
32
32
  "@git.zone/tsrun": "^3.0.0",
33
- "@git.zone/tstest": "6.2.0",
33
+ "@git.zone/tstest": "6.2.1",
34
34
  "@types/node": "26.6.1"
35
35
  },
36
36
  "files": [
package/readme.md CHANGED
@@ -1419,21 +1419,25 @@ on one spec revision, and each reason belongs to exactly one state. `converged`
1419
1419
  carry `reason: null`. `waiting` carries a `TServiceRuntimeWaitingReason`: `node-network-pending`,
1420
1420
  `conflict`, `failure-backoff`, `storage-import-pending` (an `import` local storage claim whose bytes
1421
1421
  have not arrived on its node), `bindings-pending` (the secret manifest does not cover every enabled
1422
- binding of the service yet) or `legacy-adoption-pending` (a Corestore binding of the service waits for
1423
- the offline adoption of its legacy resource, see
1424
- [Corestore Legacy Resource Adoption](#corestore-legacy-resource-adoption)). `refused` carries a
1425
- `TServiceRuntimeRefusal`:
1422
+ binding of the service yet, or an object-storage binding is placed on no node),
1423
+ `legacy-adoption-pending` (a Corestore binding of the service waits for the offline adoption of its
1424
+ legacy resource, see [Corestore Legacy Resource Adoption](#corestore-legacy-resource-adoption)) or
1425
+ `objectstorage-retention-pending` (an object-storage binding declares compliance retention whose
1426
+ evidence has not arrived). `refused` carries a `TServiceRuntimeRefusal`:
1426
1427
  `cluster-runtime-missing`, `organization-missing`, `image-plan-missing`, `platform-unsupported`,
1427
1428
  `secrets-unsupported`, `volumes-unsupported`, `port-protocol-unsupported`, `ports-too-many`,
1428
1429
  `readiness-target-undeclared`, `environment-too-large`, `no-eligible-node`, `replica-limit`,
1429
1430
  `node-authority-unsupported`, `network-policy-missing`, `network-policy-mismatch`,
1430
1431
  `network-workload-pool-missing`, `network-workload-pool-exhausted`, `volume-driver-unsupported`,
1431
1432
  `volume-options-unsupported`, `volume-shared-unsupported`, `volume-placement-unsupported`,
1432
- `volume-initialization-unknown` or `volume-ownership-unknown`. The first four volume refusals name
1433
- what a local storage claim cannot express: a volume driver other than a node-local directory, driver
1434
- or mount options, one volume mounted by more than one replica or service, and a placement that does
1435
- not pin each volume to one node. The last two name what Cloudly will not guess, see
1436
- [Service Volume Initialization](#service-volume-initialization). `ports-too-many` is a service declaring more target ports than the
1433
+ `volume-initialization-unknown`, `volume-ownership-unknown`, `objectstorage-endpoint-missing`,
1434
+ `objectstorage-binding-invalid` or `objectstorage-environment-conflict`. The first four volume
1435
+ refusals name what a local storage claim cannot express: a volume driver other than a node-local
1436
+ directory, driver or mount options, one volume mounted by more than one replica or service, and a
1437
+ placement that does not pin each volume to one node. The other two name what Cloudly will not guess,
1438
+ see [Service Volume Initialization](#service-volume-initialization). The three object-storage
1439
+ refusals name why a binding's environment cannot be composed, see
1440
+ [Object-Storage Binding Environment](#object-storage-binding-environment). `ports-too-many` is a service declaring more target ports than the
1437
1441
  `runtimeConfigContract.maximumTargetPorts` (64) a config carries, where `port-protocol-unsupported`
1438
1442
  is about one port's protocol; `node-authority-unsupported` is a node whose session resolved fewer
1439
1443
  workload authorities than the spec needs, refused at the admit step rather than handed to a node
@@ -3551,6 +3555,28 @@ to a workload environment and refuses, naming every key, a name that is already
3551
3555
  service itself or by a second object-storage binding — instead of letting one value replace
3552
3556
  another.
3553
3557
 
3558
+ **Reporting.** `composeObjectStorageServiceEnvironment(serviceEnvironment, sources)` composes a
3559
+ whole service and answers in the service runtime vocabulary, so a producer never classifies the
3560
+ errors above. Each `IObjectStorageBindingEnvironmentSource { binding, endpoint }` is an enabled
3561
+ object-storage binding and the `corestoreObjectStorage` of the node it is placed on, `null` when
3562
+ that node states none; a binding placed on no node is not a source, and its service waits as
3563
+ `bindings-pending`. The answer is a `TObjectStorageServiceEnvironmentComposition`:
3564
+
3565
+ | `status` | `reason` | When |
3566
+ | --- | --- | --- |
3567
+ | `composed` | — | `environment` is the service's own with every binding's names added. |
3568
+ | `waiting` | `objectstorage-retention-pending` | A binding declares compliance retention whose evidence has not arrived. |
3569
+ | `refused` | `objectstorage-endpoint-missing` | The binding's node states no valid `corestoreObjectStorage`. |
3570
+ | `refused` | `objectstorage-binding-invalid` | `createObjectStorageBindingEnvironment()` refuses the binding: not `objectstorage`, no canonical bucket, no canonical retention intent, or evidence that does not prove this binding. |
3571
+ | `refused` | `objectstorage-environment-conflict` | The service's own environment or another binding already sets one of the binding's names. |
3572
+
3573
+ `waiting` and `refused` carry the `bindingId` and the contract's `message`. Bindings are taken in
3574
+ id order, so the verdict does not depend on the order of the sources, and a refusal outranks a
3575
+ wait: a binding waiting for its evidence already claims all twenty names, so a collision with it
3576
+ is refused now rather than once the evidence arrives. The reasons are
3577
+ `TObjectStorageEnvironmentWaitingReason` and `TObjectStorageEnvironmentRefusal`, members of
3578
+ `TServiceRuntimeWaitingReason` and `TServiceRuntimeRefusal`.
3579
+
3554
3580
  **Validating.** `validateObjectStorageBindingEnvironment()` accepts exactly the endpoint names and
3555
3581
  all or none of the retention names, each value the one its endpoint and evidence derive, and
3556
3582
  refuses any other name — so a credential alias in the public environment is refused by name.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '32.25.0',
6
+ version: '32.26.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  }
@@ -78,7 +78,7 @@ export const serviceRuntimeContract = Object.freeze({
78
78
 
79
79
  const serviceRuntimeWaitingReasons = [
80
80
  'node-network-pending', 'conflict', 'failure-backoff', 'storage-import-pending', 'bindings-pending',
81
- 'legacy-adoption-pending',
81
+ 'legacy-adoption-pending', 'objectstorage-retention-pending',
82
82
  ] as const;
83
83
  const serviceRuntimeRefusals = [
84
84
  'cluster-runtime-missing', 'organization-missing', 'image-plan-missing', 'platform-unsupported',
@@ -88,6 +88,7 @@ const serviceRuntimeRefusals = [
88
88
  'network-workload-pool-missing', 'network-workload-pool-exhausted',
89
89
  'volume-driver-unsupported', 'volume-options-unsupported', 'volume-shared-unsupported',
90
90
  'volume-placement-unsupported', 'volume-initialization-unknown', 'volume-ownership-unknown',
91
+ 'objectstorage-endpoint-missing', 'objectstorage-binding-invalid', 'objectstorage-environment-conflict',
91
92
  ] as const;
92
93
 
93
94
  /**
@@ -95,9 +96,12 @@ const serviceRuntimeRefusals = [
95
96
  * `node-network-pending` waits for a node's network readiness, `conflict` for a sweep over inputs
96
97
  * that changed while it ran, `failure-backoff` for a failed replica's backoff to elapse,
97
98
  * `storage-import-pending` for an `import` local storage claim whose bytes have not arrived on its
98
- * node, `bindings-pending` for the secret manifest to cover every enabled binding of the service, and
99
- * `legacy-adoption-pending` for a Corestore binding whose legacy resource waits for the offline
100
- * adoption Corestore requires (the binding's `statusReason` of the same name).
99
+ * node, `bindings-pending` for the secret manifest to cover every enabled binding of the service (and
100
+ * for an object-storage binding placed on no node), `legacy-adoption-pending` for a Corestore binding
101
+ * whose legacy resource waits for the offline adoption Corestore requires (the binding's
102
+ * `statusReason` of the same name), and `objectstorage-retention-pending` for an object-storage
103
+ * binding that declares compliance retention whose evidence has not arrived: its workload is not
104
+ * handed the bucket as if it were ordinary storage.
101
105
  */
102
106
  export type TServiceRuntimeWaitingReason = typeof serviceRuntimeWaitingReasons[number];
103
107
 
@@ -118,6 +122,12 @@ export type TServiceRuntimeWaitingReason = typeof serviceRuntimeWaitingReasons[n
118
122
  * `volume-initialization-unknown` is a volume without an initialization and
119
123
  * `volume-ownership-unknown` a `run-as` ownership under `runAs: null`
120
124
  * (`deriveServiceVolumeClaimInitialization`); neither is defaulted.
125
+ *
126
+ * The object-storage refusals name why a binding's environment cannot be composed
127
+ * (`composeObjectStorageServiceEnvironment`): `objectstorage-endpoint-missing` a binding whose node
128
+ * states no valid `corestoreObjectStorage`, `objectstorage-binding-invalid` a binding
129
+ * `createObjectStorageBindingEnvironment` refuses, and `objectstorage-environment-conflict` a name
130
+ * the service's own environment or another binding already sets.
121
131
  */
122
132
  export type TServiceRuntimeRefusal = typeof serviceRuntimeRefusals[number];
123
133
 
@@ -9,9 +9,12 @@ import {
9
9
  objectStorageEndpointEnvironmentKeys,
10
10
  validateObjectStorageEndpointEnvironment,
11
11
  validatePlatformObjectStorageBucketName,
12
+ validatePlatformObjectStorageEndpoint,
12
13
  } from './objectstorage.js';
14
+ import type { TServiceRuntimeRefusal, TServiceRuntimeWaitingReason } from '../data/serviceruntime.js';
13
15
  import type {
14
16
  IObjectStorageRetentionEvidence,
17
+ IObjectStorageRetentionEvidenceExpectation,
15
18
  IObjectStorageRetentionIntent,
16
19
  TObjectStorageRetentionEnvironment,
17
20
  TObjectStorageRetentionEnvironmentKey,
@@ -20,6 +23,7 @@ import {
20
23
  createObjectStorageRetentionEnvironment,
21
24
  objectStorageRetentionEnvironmentKeys,
22
25
  validateObjectStorageRetentionEnvironment,
26
+ validateObjectStorageRetentionEvidence,
23
27
  validateObjectStorageRetentionIntent,
24
28
  } from './objectstorageretention.js';
25
29
  import { isPlainRecord, readDataProperties } from '../private/dataproperties.js';
@@ -45,78 +49,118 @@ export type TObjectStorageBindingEnvironment =
45
49
  | TObjectStorageEndpointEnvironment
46
50
  | (TObjectStorageEndpointEnvironment & TObjectStorageRetentionEnvironment);
47
51
 
52
+ /** How one binding's environment resolves, in the service runtime vocabulary. */
53
+ type TBindingEnvironmentResolution =
54
+ | { status: 'composed'; environment: TObjectStorageBindingEnvironment }
55
+ | { status: 'waiting'; reason: TObjectStorageEnvironmentWaitingReason; message: string }
56
+ | {
57
+ status: 'refused';
58
+ reason: Exclude<TObjectStorageEnvironmentRefusal, 'objectstorage-environment-conflict'>;
59
+ message: string;
60
+ };
61
+
48
62
  /**
49
- * The environment Cloudly composes into a workload's runtime configuration for one binding.
50
- *
51
- * `bindingArg` is the Cloudly binding document and `endpointArg` the
52
- * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
53
- * name, never guessed:
54
- *
55
- * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
56
- * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
57
- * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
58
- * not proven yet, so the workload is not given an environment that would let it run as if its
59
- * bucket were ordinary storage; the caller waits for the evidence.
60
- * - evidence that does not prove the binding's own service, binding id, bucket and intent.
61
- *
62
- * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
63
- * retention, so there is nothing to state.
64
- *
65
- * The evidence's reconciliation generation and binding-request digest are read from its own
66
- * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
67
- * does not repeat them. Everything the binding does state is checked again here.
63
+ * The one place a binding's environment is decided. `createObjectStorageBindingEnvironment()`
64
+ * throws its message, `composeObjectStorageServiceEnvironment()` reports its reason.
68
65
  */
69
- export const createObjectStorageBindingEnvironment = async (
66
+ const resolveBindingEnvironment = async (
70
67
  bindingArg: IPlatformBinding,
71
- endpointArg: IPlatformObjectStorageEndpoint,
72
- ): Promise<TObjectStorageBindingEnvironment> => {
68
+ endpointArg: IPlatformObjectStorageEndpoint | null,
69
+ ): Promise<TBindingEnvironmentResolution> => {
73
70
  const binding = readDataProperties(bindingArg);
74
71
  if (binding === null || binding.capability !== 'objectstorage') {
75
- throw new Error('objectstorage binding environment requires an objectstorage binding');
72
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
73
+ message: 'objectstorage binding environment requires an objectstorage binding' };
76
74
  }
77
75
  const bindingName = typeof binding.id === 'string' ? binding.id : '(unnamed)';
78
76
  if (validatePlatformObjectStorageBucketName(binding.objectstorageBucketName).length > 0) {
79
- throw new Error(
80
- `objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns it and `
81
- + 'no environment invents one',
82
- );
77
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
78
+ message: `objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns `
79
+ + 'it and no environment invents one' };
80
+ }
81
+ if (endpointArg === null) {
82
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing',
83
+ message: `objectstorage binding ${bindingName} is placed on a node that states no `
84
+ + 'corestoreObjectStorage endpoint' };
85
+ }
86
+ const endpointErrors = validatePlatformObjectStorageEndpoint(endpointArg);
87
+ if (endpointErrors.length > 0) {
88
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing', message: endpointErrors[0] };
83
89
  }
84
90
  const bucketName = binding.objectstorageBucketName as string;
85
91
  const endpointEnvironment = createObjectStorageEndpointEnvironment(endpointArg, bucketName);
86
92
  const retention = binding.objectstorageRetention;
87
93
  if (retention === undefined) {
88
- return endpointEnvironment;
94
+ return { status: 'composed', environment: endpointEnvironment };
89
95
  }
90
96
  const retentionBinding = readDataProperties(retention);
91
97
  if (retentionBinding === null
92
98
  || validateObjectStorageRetentionIntent(retentionBinding.intent).length > 0) {
93
- throw new Error(
94
- `objectstorage binding ${bindingName} must declare canonical compliance-retention intent`,
95
- );
99
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
100
+ message: `objectstorage binding ${bindingName} must declare canonical compliance-retention `
101
+ + 'intent' };
96
102
  }
97
103
  const evidence = retentionBinding.evidence;
98
104
  if (evidence === undefined) {
99
- throw new Error(
100
- `objectstorage binding ${bindingName} declares compliance retention without verified `
101
- + 'evidence; its workload environment waits for the evidence',
102
- );
105
+ return { status: 'waiting', reason: 'objectstorage-retention-pending',
106
+ message: `objectstorage binding ${bindingName} declares compliance retention without `
107
+ + 'verified evidence; its workload environment waits for the evidence' };
103
108
  }
104
109
  const authority = isPlainRecord(evidence) ? readDataProperties(evidence.authority) : null;
105
110
  if (authority === null) {
106
- throw new Error('objectstorage retention evidence must use its exact schema');
111
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
112
+ message: 'objectstorage retention evidence must use its exact schema' };
113
+ }
114
+ const expectation: IObjectStorageRetentionEvidenceExpectation = {
115
+ serviceId: binding.serviceId as string,
116
+ bindingId: binding.id as string,
117
+ reconciliationGeneration: authority.reconciliationGeneration as number,
118
+ bindingRequestDigest: authority.bindingRequestDigest as string,
119
+ bucketName,
120
+ intent: retentionBinding.intent as IObjectStorageRetentionIntent,
121
+ };
122
+ const evidenceErrors = await validateObjectStorageRetentionEvidence(evidence, expectation);
123
+ if (evidenceErrors.length > 0) {
124
+ return { status: 'refused', reason: 'objectstorage-binding-invalid', message: evidenceErrors[0] };
107
125
  }
108
126
  const retentionEnvironment = await createObjectStorageRetentionEnvironment(
109
127
  evidence as IObjectStorageRetentionEvidence,
110
- {
111
- serviceId: binding.serviceId as string,
112
- bindingId: binding.id as string,
113
- reconciliationGeneration: authority.reconciliationGeneration as number,
114
- bindingRequestDigest: authority.bindingRequestDigest as string,
115
- bucketName,
116
- intent: retentionBinding.intent as IObjectStorageRetentionIntent,
117
- },
128
+ expectation,
118
129
  );
119
- return { ...endpointEnvironment, ...retentionEnvironment };
130
+ return { status: 'composed', environment: { ...endpointEnvironment, ...retentionEnvironment } };
131
+ };
132
+
133
+ /**
134
+ * The environment Cloudly composes into a workload's runtime configuration for one binding.
135
+ *
136
+ * `bindingArg` is the Cloudly binding document and `endpointArg` the
137
+ * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
138
+ * name, never guessed:
139
+ *
140
+ * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
141
+ * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
142
+ * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
143
+ * not proven yet, so the workload is not given an environment that would let it run as if its
144
+ * bucket were ordinary storage; the caller waits for the evidence.
145
+ * - evidence that does not prove the binding's own service, binding id, bucket and intent.
146
+ *
147
+ * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
148
+ * retention, so there is nothing to state.
149
+ *
150
+ * The evidence's reconciliation generation and binding-request digest are read from its own
151
+ * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
152
+ * does not repeat them. Everything the binding does state is checked again here.
153
+ *
154
+ * `composeObjectStorageServiceEnvironment()` answers the same questions with a service runtime
155
+ * reason instead of an error.
156
+ */
157
+ export const createObjectStorageBindingEnvironment = async (
158
+ bindingArg: IPlatformBinding,
159
+ endpointArg: IPlatformObjectStorageEndpoint,
160
+ ): Promise<TObjectStorageBindingEnvironment> => {
161
+ const resolution = await resolveBindingEnvironment(bindingArg, endpointArg);
162
+ if (resolution.status !== 'composed') throw new Error(resolution.message);
163
+ return resolution.environment;
120
164
  };
121
165
 
122
166
  /**
@@ -152,6 +196,16 @@ export const validateObjectStorageBindingEnvironment = async (
152
196
  return errors;
153
197
  };
154
198
 
199
+ const findEnvironmentCollisions = (
200
+ claimedKeysArg: ReadonlySet<string>,
201
+ keysArg: readonly string[],
202
+ ): string[] => keysArg.filter((keyArg) => claimedKeysArg.has(keyArg)).sort();
203
+
204
+ const describeEnvironmentCollisions = (collisionsArg: readonly string[]): string => (
205
+ `objectstorage binding environment collides with ${collisionsArg.join(', ')} already set on the `
206
+ + 'workload'
207
+ );
208
+
155
209
  /**
156
210
  * A workload environment with one binding's environment added.
157
211
  *
@@ -163,14 +217,105 @@ export const mergeObjectStorageBindingEnvironment = (
163
217
  environmentArg: Readonly<Record<string, string>>,
164
218
  bindingEnvironmentArg: TObjectStorageBindingEnvironment,
165
219
  ): Record<string, string> => {
166
- const collisions = Object.keys(bindingEnvironmentArg)
167
- .filter((keyArg) => Object.hasOwn(environmentArg, keyArg))
168
- .sort();
169
- if (collisions.length > 0) {
170
- throw new Error(
171
- `objectstorage binding environment collides with ${collisions.join(', ')} already set on the `
172
- + 'workload',
173
- );
174
- }
220
+ const collisions = findEnvironmentCollisions(
221
+ new Set(Object.keys(environmentArg)),
222
+ Object.keys(bindingEnvironmentArg),
223
+ );
224
+ if (collisions.length > 0) throw new Error(describeEnvironmentCollisions(collisions));
175
225
  return { ...environmentArg, ...bindingEnvironmentArg };
176
226
  };
227
+
228
+ /** One enabled object-storage binding of a service and the endpoint of the node it is placed on. */
229
+ export interface IObjectStorageBindingEnvironmentSource {
230
+ binding: IPlatformBinding;
231
+ /**
232
+ * The `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on, `null`
233
+ * when that node states none. A binding placed on no node is not a source: its service waits as
234
+ * `bindings-pending`.
235
+ */
236
+ endpoint: IPlatformObjectStorageEndpoint | null;
237
+ }
238
+
239
+ /**
240
+ * The service runtime waiting reason an object-storage environment reports:
241
+ * `objectstorage-retention-pending`, a binding that declares compliance retention whose evidence
242
+ * has not arrived.
243
+ */
244
+ export type TObjectStorageEnvironmentWaitingReason = Extract<
245
+ TServiceRuntimeWaitingReason,
246
+ 'objectstorage-retention-pending'
247
+ >;
248
+
249
+ /**
250
+ * The service runtime refusals an object-storage environment reports.
251
+ * `objectstorage-endpoint-missing` is a binding whose node states no valid
252
+ * `corestoreObjectStorage`; `objectstorage-binding-invalid` a binding
253
+ * `createObjectStorageBindingEnvironment()` refuses (not `objectstorage`, no canonical bucket, no
254
+ * canonical retention intent, or evidence that does not prove this binding);
255
+ * `objectstorage-environment-conflict` a name the service's own environment or another binding
256
+ * already sets.
257
+ */
258
+ export type TObjectStorageEnvironmentRefusal = Extract<
259
+ TServiceRuntimeRefusal,
260
+ | 'objectstorage-endpoint-missing'
261
+ | 'objectstorage-binding-invalid'
262
+ | 'objectstorage-environment-conflict'
263
+ >;
264
+
265
+ /** A service's environment with every object-storage binding added, or the one reason it is not. */
266
+ export type TObjectStorageServiceEnvironmentComposition =
267
+ | { status: 'composed'; environment: Record<string, string> }
268
+ | {
269
+ status: 'waiting';
270
+ reason: TObjectStorageEnvironmentWaitingReason;
271
+ bindingId: string;
272
+ message: string;
273
+ }
274
+ | { status: 'refused'; reason: TObjectStorageEnvironmentRefusal; bindingId: string; message: string };
275
+
276
+ /**
277
+ * The service's own environment with the environment of every enabled object-storage binding
278
+ * added, or the service runtime reason it cannot be — so a producer reports the contract's
279
+ * verdict instead of classifying its errors.
280
+ *
281
+ * Each binding's names are `createObjectStorageBindingEnvironment()`'s from its node's endpoint,
282
+ * added as `mergeObjectStorageBindingEnvironment()` adds them. Bindings are taken in id order, so
283
+ * the verdict does not depend on the order of `sourcesArg`. A refusal outranks a wait, because a
284
+ * wait resolves on its own and a refusal never does: a binding waiting for retention evidence
285
+ * already claims all twenty of its names, so a collision with it is refused now, not once its
286
+ * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
287
+ * composed environment. The inputs are not changed.
288
+ */
289
+ export const composeObjectStorageServiceEnvironment = async (
290
+ serviceEnvironmentArg: Readonly<Record<string, string>>,
291
+ sourcesArg: readonly IObjectStorageBindingEnvironmentSource[],
292
+ ): Promise<TObjectStorageServiceEnvironmentComposition> => {
293
+ const sources = [...sourcesArg].sort((leftArg, rightArg) => (
294
+ leftArg.binding.id < rightArg.binding.id ? -1 : leftArg.binding.id > rightArg.binding.id ? 1 : 0
295
+ ));
296
+ let environment: Record<string, string> = { ...serviceEnvironmentArg };
297
+ const claimedKeys = new Set(Object.keys(environment));
298
+ let wait: Extract<TObjectStorageServiceEnvironmentComposition, { status: 'waiting' }> | null = null;
299
+ for (const source of sources) {
300
+ const bindingId = source.binding.id;
301
+ const resolution = await resolveBindingEnvironment(source.binding, source.endpoint);
302
+ if (resolution.status === 'refused') {
303
+ return { status: 'refused', reason: resolution.reason, bindingId, message: resolution.message };
304
+ }
305
+ const keys: readonly string[] = resolution.status === 'composed'
306
+ ? Object.keys(resolution.environment)
307
+ : objectStorageBindingEnvironmentKeys;
308
+ const collisions = findEnvironmentCollisions(claimedKeys, keys);
309
+ if (collisions.length > 0) {
310
+ return { status: 'refused', reason: 'objectstorage-environment-conflict', bindingId,
311
+ message: describeEnvironmentCollisions(collisions) };
312
+ }
313
+ for (const key of keys) claimedKeys.add(key);
314
+ if (resolution.status === 'composed') {
315
+ environment = { ...environment, ...resolution.environment };
316
+ } else {
317
+ wait ??= { status: 'waiting', reason: resolution.reason, bindingId, message: resolution.message };
318
+ }
319
+ }
320
+ return wait ?? { status: 'composed', environment };
321
+ };