@serve.zone/interfaces 31.4.0 → 31.6.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 +78 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/backup.d.ts +13 -0
- package/dist_ts/data/cluster.d.ts +44 -9
- package/dist_ts/data/cluster.js +35 -12
- package/dist_ts/data/clusternode.d.ts +49 -0
- package/dist_ts/data/clusternode.js +47 -1
- package/dist_ts/data/corestore.d.ts +27 -0
- package/dist_ts/data/corestore.js +11 -0
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/isolatedrestore.js +42 -2
- package/dist_ts/requests/backup.d.ts +223 -0
- package/dist_ts/requests/cluster.d.ts +17 -0
- package/dist_ts/requests/corestore.d.ts +57 -0
- package/dist_ts/requests/corestore.js +102 -1
- package/dist_ts/requests/platform.d.ts +30 -0
- package/dist_ts/runtime.corestore.d.ts +7 -10
- package/dist_ts/runtime.corestore.js +1 -1
- package/dist_ts/runtime.corestore.relay.d.ts +47 -0
- package/dist_ts/runtime.corestore.relay.js +57 -0
- package/dist_ts/runtime.d.ts +1 -0
- package/dist_ts/runtime.js +2 -1
- package/package.json +1 -1
- package/readme.md +120 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/backup.ts +14 -0
- package/ts/data/cluster.ts +76 -17
- package/ts/data/clusternode.ts +93 -0
- package/ts/data/corestore.ts +28 -0
- package/ts/data/index.ts +1 -0
- package/ts/data/isolatedrestore.ts +53 -1
- package/ts/requests/backup.ts +253 -0
- package/ts/requests/cluster.ts +21 -0
- package/ts/requests/corestore.ts +183 -0
- package/ts/requests/platform.ts +36 -0
- package/ts/runtime.corestore.relay.ts +111 -0
- package/ts/runtime.corestore.ts +7 -8
- package/ts/runtime.ts +1 -0
package/ts/requests/corestore.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface ICorestoreServiceResources {
|
|
|
22
22
|
/**
|
|
23
23
|
* Corestore inventory of one node (each coreflow node runs its own corestore;
|
|
24
24
|
* the data is node-local, which is what makes node placement matter).
|
|
25
|
+
*
|
|
26
|
+
* Superseded by `IClusterCorestoreInventory`, which names the node by its
|
|
27
|
+
* cluster-document id instead of by the Swarm hostnames a connection tagged
|
|
28
|
+
* itself with.
|
|
25
29
|
*/
|
|
26
30
|
export interface ICorestoreNodeInventory {
|
|
27
31
|
/** Swarm node hostnames of the reporting coreflow. */
|
|
@@ -83,6 +87,156 @@ export interface ICoreflowInventoryRegistration {
|
|
|
83
87
|
registeredAt: number;
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
/**
|
|
91
|
+
* What one node's Corestore holds, as the cluster relay read it.
|
|
92
|
+
*
|
|
93
|
+
* Node identity is the cluster document's node id: one relay serves every node
|
|
94
|
+
* of its cluster, so which node an inventory describes is stated, never derived
|
|
95
|
+
* from which connection answered. An unreachable node names why — `errorCode`
|
|
96
|
+
* is required whenever `reachable` is false and refused whenever it is true —
|
|
97
|
+
* because a node whose Corestore cannot be reached is a fact the control plane
|
|
98
|
+
* needs, and a silent empty inventory would read as "nothing provisioned",
|
|
99
|
+
* which is the opposite.
|
|
100
|
+
*/
|
|
101
|
+
export interface IClusterCorestoreInventory {
|
|
102
|
+
nodeId: string;
|
|
103
|
+
checkedAt: number;
|
|
104
|
+
reachable: boolean;
|
|
105
|
+
errorCode?: 'CORESTORE_ENDPOINT_UNKNOWN' | 'CORESTORE_UNREACHABLE';
|
|
106
|
+
services: ICorestoreServiceResources[];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const clusterCorestoreInventoryIdentifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
|
|
110
|
+
|
|
111
|
+
const isClusterCorestoreRecord = (
|
|
112
|
+
valueArg: unknown,
|
|
113
|
+
): valueArg is Record<string, unknown> => (
|
|
114
|
+
Boolean(valueArg)
|
|
115
|
+
&& typeof valueArg === 'object'
|
|
116
|
+
&& !Array.isArray(valueArg)
|
|
117
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
118
|
+
|| Object.getPrototypeOf(valueArg) === null)
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const hasClusterCorestoreKeys = (
|
|
122
|
+
valueArg: Record<string, unknown>,
|
|
123
|
+
keysArg: readonly string[],
|
|
124
|
+
optionalKeysArg: readonly string[] = [],
|
|
125
|
+
): boolean => {
|
|
126
|
+
const actualKeys = Object.keys(valueArg);
|
|
127
|
+
return keysArg.every((keyArg) => actualKeys.includes(keyArg))
|
|
128
|
+
&& actualKeys.every((keyArg) => keysArg.includes(keyArg) || optionalKeysArg.includes(keyArg));
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const validateClusterCorestoreServiceResources = (
|
|
132
|
+
valueArg: unknown,
|
|
133
|
+
pathArg: string,
|
|
134
|
+
): string[] => {
|
|
135
|
+
if (!isClusterCorestoreRecord(valueArg)
|
|
136
|
+
|| !hasClusterCorestoreKeys(
|
|
137
|
+
valueArg,
|
|
138
|
+
['serviceId', 'resources', 'updatedAt'],
|
|
139
|
+
['serviceName'],
|
|
140
|
+
)) {
|
|
141
|
+
return [`${pathArg} must use its exact schema`];
|
|
142
|
+
}
|
|
143
|
+
const errors: string[] = [];
|
|
144
|
+
if (typeof valueArg.serviceId !== 'string'
|
|
145
|
+
|| !clusterCorestoreInventoryIdentifierPattern.test(valueArg.serviceId)) {
|
|
146
|
+
errors.push(`${pathArg} serviceId must be a bounded identifier`);
|
|
147
|
+
}
|
|
148
|
+
if (!Number.isSafeInteger(valueArg.updatedAt) || (valueArg.updatedAt as number) < 0) {
|
|
149
|
+
errors.push(`${pathArg} updatedAt must be a non-negative safe integer`);
|
|
150
|
+
}
|
|
151
|
+
if (!Array.isArray(valueArg.resources)) {
|
|
152
|
+
errors.push(`${pathArg} resources must be an array`);
|
|
153
|
+
return errors;
|
|
154
|
+
}
|
|
155
|
+
for (const [index, resource] of valueArg.resources.entries()) {
|
|
156
|
+
const resourcePath = `${pathArg} resources[${index}]`;
|
|
157
|
+
if (!isClusterCorestoreRecord(resource)
|
|
158
|
+
|| !hasClusterCorestoreKeys(
|
|
159
|
+
resource,
|
|
160
|
+
['capability', 'provider', 'resourceName', 'createdAt'],
|
|
161
|
+
)) {
|
|
162
|
+
errors.push(`${resourcePath} must use its exact schema`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (resource.capability !== 'database' && resource.capability !== 'objectstorage') {
|
|
166
|
+
errors.push(`${resourcePath} capability must be database or objectstorage`);
|
|
167
|
+
}
|
|
168
|
+
if (typeof resource.provider !== 'string' || resource.provider.length === 0) {
|
|
169
|
+
errors.push(`${resourcePath} provider must be a non-empty string`);
|
|
170
|
+
}
|
|
171
|
+
if (typeof resource.resourceName !== 'string' || resource.resourceName.length === 0) {
|
|
172
|
+
errors.push(`${resourcePath} resourceName must be a non-empty string`);
|
|
173
|
+
}
|
|
174
|
+
if (!Number.isSafeInteger(resource.createdAt) || (resource.createdAt as number) < 0) {
|
|
175
|
+
errors.push(`${resourcePath} createdAt must be a non-negative safe integer`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return errors;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Validates one cluster Corestore inventory and names every reason it is
|
|
183
|
+
* refused.
|
|
184
|
+
*
|
|
185
|
+
* Shape only. Whether that node belongs to the cluster, and whether a resource
|
|
186
|
+
* should exist, are decisions for the control plane that asked; an inventory
|
|
187
|
+
* that passes here has claimed neither. A reachable inventory that names an
|
|
188
|
+
* error, and an unreachable one that lists resources, are both refused: an
|
|
189
|
+
* answer has to be one of the two things it can be.
|
|
190
|
+
*/
|
|
191
|
+
export const validateClusterCorestoreInventory = (
|
|
192
|
+
valueArg: unknown,
|
|
193
|
+
pathArg = 'cluster corestore inventory',
|
|
194
|
+
): string[] => {
|
|
195
|
+
if (!isClusterCorestoreRecord(valueArg)
|
|
196
|
+
|| !hasClusterCorestoreKeys(
|
|
197
|
+
valueArg,
|
|
198
|
+
['nodeId', 'checkedAt', 'reachable', 'services'],
|
|
199
|
+
['errorCode'],
|
|
200
|
+
)) {
|
|
201
|
+
return [`${pathArg} must use its exact schema`];
|
|
202
|
+
}
|
|
203
|
+
const errors: string[] = [];
|
|
204
|
+
if (typeof valueArg.nodeId !== 'string'
|
|
205
|
+
|| !clusterCorestoreInventoryIdentifierPattern.test(valueArg.nodeId)) {
|
|
206
|
+
errors.push(`${pathArg} nodeId must be a bounded identifier`);
|
|
207
|
+
}
|
|
208
|
+
if (!Number.isSafeInteger(valueArg.checkedAt) || (valueArg.checkedAt as number) < 0) {
|
|
209
|
+
errors.push(`${pathArg} checkedAt must be a non-negative safe integer`);
|
|
210
|
+
}
|
|
211
|
+
if (typeof valueArg.reachable !== 'boolean') {
|
|
212
|
+
errors.push(`${pathArg} reachable must be a boolean`);
|
|
213
|
+
}
|
|
214
|
+
if (valueArg.errorCode !== undefined
|
|
215
|
+
&& valueArg.errorCode !== 'CORESTORE_ENDPOINT_UNKNOWN'
|
|
216
|
+
&& valueArg.errorCode !== 'CORESTORE_UNREACHABLE') {
|
|
217
|
+
errors.push(`${pathArg} errorCode must name why the node could not be read`);
|
|
218
|
+
}
|
|
219
|
+
if (valueArg.reachable === true && valueArg.errorCode !== undefined) {
|
|
220
|
+
errors.push(`${pathArg} must not carry an errorCode while it reports the node reachable`);
|
|
221
|
+
}
|
|
222
|
+
if (valueArg.reachable === false && valueArg.errorCode === undefined) {
|
|
223
|
+
// An unreachable node with no reason reads like a node holding nothing,
|
|
224
|
+
// which is the one confusion this shape exists to prevent.
|
|
225
|
+
errors.push(`${pathArg} must name why the node could not be read while it reports it unreachable`);
|
|
226
|
+
}
|
|
227
|
+
if (!Array.isArray(valueArg.services)) {
|
|
228
|
+
errors.push(`${pathArg} services must be an array`);
|
|
229
|
+
return errors;
|
|
230
|
+
}
|
|
231
|
+
if (valueArg.reachable === false && valueArg.services.length > 0) {
|
|
232
|
+
errors.push(`${pathArg} must list no services while it reports the node unreachable`);
|
|
233
|
+
}
|
|
234
|
+
for (const [index, service] of valueArg.services.entries()) {
|
|
235
|
+
errors.push(...validateClusterCorestoreServiceResources(service, `${pathArg} services[${index}]`));
|
|
236
|
+
}
|
|
237
|
+
return errors;
|
|
238
|
+
};
|
|
239
|
+
|
|
86
240
|
export interface IReq_Any_Cloudly_GetCorestoreInventory
|
|
87
241
|
extends plugins.typedrequestInterfaces.implementsTR<
|
|
88
242
|
plugins.typedrequestInterfaces.ITypedRequest,
|
|
@@ -95,6 +249,35 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
95
249
|
response: ICorestoreInventoryReport;
|
|
96
250
|
}
|
|
97
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Cloudly asks the relay what one exact node of its cluster holds.
|
|
254
|
+
*
|
|
255
|
+
* The node is a request field because one relay serves them all: the connection
|
|
256
|
+
* that answers proves the cluster, never the node. A greenfield cluster answers
|
|
257
|
+
* with an empty `services` list and `reachable: true`, which is the proof that
|
|
258
|
+
* a namespace is blank — distinct from `reachable: false`, which proves
|
|
259
|
+
* nothing at all.
|
|
260
|
+
*
|
|
261
|
+
* Named apart from `getCorestoreInventory` on purpose: that method is Cloudly's
|
|
262
|
+
* own operator-facing read and lives on the other end of this socket. One name
|
|
263
|
+
* on both ends would leave direction as the only thing telling two different
|
|
264
|
+
* bodies apart.
|
|
265
|
+
*/
|
|
266
|
+
export interface IReq_Cloudly_Relay_GetClusterCorestoreInventory
|
|
267
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
268
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
269
|
+
IReq_Cloudly_Relay_GetClusterCorestoreInventory
|
|
270
|
+
> {
|
|
271
|
+
method: 'getClusterCorestoreInventory';
|
|
272
|
+
request: {
|
|
273
|
+
nodeId: string;
|
|
274
|
+
};
|
|
275
|
+
response: {
|
|
276
|
+
inventory: IClusterCorestoreInventory;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Superseded by `getClusterCorestoreInventory`, which names the node. */
|
|
98
281
|
export interface IReq_Cloudly_Coreflow_GetCorestoreInventory
|
|
99
282
|
extends plugins.typedrequestInterfaces.implementsTR<
|
|
100
283
|
plugins.typedrequestInterfaces.ITypedRequest,
|
package/ts/requests/platform.ts
CHANGED
|
@@ -23,6 +23,42 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* One platform binding and the node its Corestore resources live on.
|
|
28
|
+
*
|
|
29
|
+
* Corestore is node-local, so a binding without a node is a database nobody can
|
|
30
|
+
* place: while every cluster ran one coreflow per node, "this node" was whoever
|
|
31
|
+
* answered, and a cluster relay serving every node has no such implicit answer.
|
|
32
|
+
* The placement is Cloudly's, derived from where the service is assigned.
|
|
33
|
+
*/
|
|
34
|
+
export interface IClusterPlatformBindingPlacement {
|
|
35
|
+
nodeId: string;
|
|
36
|
+
binding: IPlatformBinding;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The desired platform state for the relay's own cluster, node by node.
|
|
41
|
+
*
|
|
42
|
+
* Its session decides which cluster it speaks for, so the request carries no
|
|
43
|
+
* cluster of its own. It replaces `getPlatformDesiredState` for a cluster
|
|
44
|
+
* relay: the same capabilities and provider configs, and bindings that state
|
|
45
|
+
* where they belong instead of leaving it to be inferred.
|
|
46
|
+
*/
|
|
47
|
+
export interface IReq_Relay_Cloudly_GetClusterPlatformDesiredState
|
|
48
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
49
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
50
|
+
IReq_Relay_Cloudly_GetClusterPlatformDesiredState
|
|
51
|
+
> {
|
|
52
|
+
method: 'getClusterPlatformDesiredState';
|
|
53
|
+
request: Record<string, never>;
|
|
54
|
+
response: {
|
|
55
|
+
capabilities: IPlatformCapability[];
|
|
56
|
+
providerConfigs: IPlatformProviderConfig[];
|
|
57
|
+
/** Sorted by node, then by binding id, so two reads compare directly. */
|
|
58
|
+
bindings: IClusterPlatformBindingPlacement[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
26
62
|
export interface IReq_Any_Cloudly_GetPlatformCapabilities
|
|
27
63
|
extends plugins.typedrequestInterfaces.implementsTR<
|
|
28
64
|
plugins.typedrequestInterfaces.ITypedRequest,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ICorestoreDatabaseBackupReceipt } from './runtime.corestore.js';
|
|
2
|
+
import {
|
|
3
|
+
corestoreDatabaseBackupRuntimeLimits,
|
|
4
|
+
normalizeCorestoreDatabaseBackupReceipt,
|
|
5
|
+
} from './runtime.corestore.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Carrying a Corestore database closure through the cluster relay.
|
|
9
|
+
*
|
|
10
|
+
* A cluster is outbound-only, so Cloudly never reads Corestore itself: the
|
|
11
|
+
* relay exports the closure from its cluster's Corestore and streams it to
|
|
12
|
+
* Cloudly, and streams it back for a restore. The bytes travel as a
|
|
13
|
+
* VirtualStream on the relay's own session; this descriptor is what the parent
|
|
14
|
+
* TypedRequest carries about them.
|
|
15
|
+
*
|
|
16
|
+
* No version suffix and no schema field: the protocol version is the installed
|
|
17
|
+
* `@serve.zone/interfaces` version.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What a peer states about the closure it is about to send or serve.
|
|
22
|
+
*
|
|
23
|
+
* `size` is mandatory, and it is the reason this descriptor exists at all:
|
|
24
|
+
* Corestore's database backup restore requires one exact `Content-Length`,
|
|
25
|
+
* identity transfer encoding and no content encoding, and it refuses a body
|
|
26
|
+
* whose byte length differs from its receipt. A stream that only learned its
|
|
27
|
+
* length at EOF could therefore never be restored — the receiver has to open
|
|
28
|
+
* the Corestore request before the first byte arrives.
|
|
29
|
+
*
|
|
30
|
+
* `sha256` is the digest of those exact bytes, and `receipt` is Corestore's own
|
|
31
|
+
* closure receipt, which binds the backup, the source service, the snapshot and
|
|
32
|
+
* the closure's identity. The two are cross-checked here rather than trusted
|
|
33
|
+
* separately: a descriptor whose `size` or `sha256` disagrees with its receipt
|
|
34
|
+
* describes a closure nobody can restore.
|
|
35
|
+
*/
|
|
36
|
+
export interface ICorestoreDatabaseClosureDescriptor {
|
|
37
|
+
/** Exact byte length of the closure, as Corestore's `Content-Length` needs it. */
|
|
38
|
+
size: number;
|
|
39
|
+
/** Lowercase hex SHA-256 over those exact bytes. */
|
|
40
|
+
sha256: string;
|
|
41
|
+
/** Corestore's own receipt for this closure; its identity and integrity authority. */
|
|
42
|
+
receipt: ICorestoreDatabaseBackupReceipt;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const bareSha256Pattern = /^[a-f0-9]{64}$/;
|
|
46
|
+
|
|
47
|
+
const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
|
|
48
|
+
Boolean(valueArg)
|
|
49
|
+
&& typeof valueArg === 'object'
|
|
50
|
+
&& !Array.isArray(valueArg)
|
|
51
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
52
|
+
|| Object.getPrototypeOf(valueArg) === null)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const hasExactKeys = (
|
|
56
|
+
valueArg: Record<string, unknown>,
|
|
57
|
+
keysArg: readonly string[],
|
|
58
|
+
): boolean => {
|
|
59
|
+
const actualKeys = Object.keys(valueArg).sort();
|
|
60
|
+
const expectedKeys = [...keysArg].sort();
|
|
61
|
+
return actualKeys.length === expectedKeys.length
|
|
62
|
+
&& actualKeys.every((keyArg, indexArg) => keyArg === expectedKeys[indexArg]);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validates one closure descriptor and names every reason it is refused.
|
|
67
|
+
*
|
|
68
|
+
* Shape and self-consistency only: whether the closure exists, whether the
|
|
69
|
+
* backup is this cluster's and whether the target database may be replaced are
|
|
70
|
+
* decisions for the party that holds them. A descriptor that passes here has
|
|
71
|
+
* claimed none of them. No reason quotes closure bytes, because a reason
|
|
72
|
+
* travels into logs and the closure is tenant data.
|
|
73
|
+
*/
|
|
74
|
+
export const validateCorestoreDatabaseClosureDescriptor = (
|
|
75
|
+
valueArg: unknown,
|
|
76
|
+
pathArg = 'corestore database closure descriptor',
|
|
77
|
+
): string[] => {
|
|
78
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['size', 'sha256', 'receipt'])) {
|
|
79
|
+
return [`${pathArg} must use its exact schema`];
|
|
80
|
+
}
|
|
81
|
+
const errors: string[] = [];
|
|
82
|
+
const maximumBytes = corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes;
|
|
83
|
+
if (!Number.isSafeInteger(valueArg.size)
|
|
84
|
+
|| (valueArg.size as number) < 1
|
|
85
|
+
|| (valueArg.size as number) > maximumBytes) {
|
|
86
|
+
errors.push(`${pathArg} size must be an integer from 1 to ${maximumBytes}`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof valueArg.sha256 !== 'string' || !bareSha256Pattern.test(valueArg.sha256)) {
|
|
89
|
+
errors.push(`${pathArg} sha256 must be a lowercase hex SHA-256 digest`);
|
|
90
|
+
}
|
|
91
|
+
let receipt: ICorestoreDatabaseBackupReceipt | undefined;
|
|
92
|
+
try {
|
|
93
|
+
receipt = normalizeCorestoreDatabaseBackupReceipt(valueArg.receipt);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Corestore's own normalizer names the field it refused and never the
|
|
96
|
+
// material behind it, so its reason is worth more than its class.
|
|
97
|
+
errors.push(`${pathArg} receipt is not canonical: ${(error as Error).message}`);
|
|
98
|
+
}
|
|
99
|
+
if (!receipt || errors.length > 0) {
|
|
100
|
+
return errors;
|
|
101
|
+
}
|
|
102
|
+
if (valueArg.size !== receipt.closure.archiveBytes) {
|
|
103
|
+
errors.push(
|
|
104
|
+
`${pathArg} size must equal the ${receipt.closure.archiveBytes} bytes its receipt states`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (valueArg.sha256 !== receipt.closure.sha256) {
|
|
108
|
+
errors.push(`${pathArg} sha256 must equal the digest its receipt states`);
|
|
109
|
+
}
|
|
110
|
+
return errors;
|
|
111
|
+
};
|
package/ts/runtime.corestore.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import type {
|
|
3
3
|
IActiveSecretRecipientMetadata,
|
|
4
|
+
ICorestoreDatabaseAllocationReference,
|
|
4
5
|
IIdentityCredential,
|
|
5
6
|
ISecretVersionReference,
|
|
6
7
|
} from './data/index.js';
|
|
@@ -1281,14 +1282,12 @@ export const corestoreDatabaseBackupRuntimeLimits = Object.freeze({
|
|
|
1281
1282
|
maximumServiceNameBytes: 1024,
|
|
1282
1283
|
});
|
|
1283
1284
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
receiptSha256: string;
|
|
1291
|
-
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Declared in the data tree, because an isolated restore's canonical resource
|
|
1287
|
+
* mappings bind the exact same reference and that tree carries no runtime
|
|
1288
|
+
* import. Re-exported here so this module's readers keep one import site.
|
|
1289
|
+
*/
|
|
1290
|
+
export type { ICorestoreDatabaseAllocationReference };
|
|
1292
1291
|
|
|
1293
1292
|
export interface ICorestoreDatabaseBackupSnapshotTags {
|
|
1294
1293
|
corestore: 'resource';
|
package/ts/runtime.ts
CHANGED
|
@@ -42,6 +42,7 @@ export * from './runtime.workloadinit.js';
|
|
|
42
42
|
export * from './runtime.networkprojection.js';
|
|
43
43
|
export * from './runtime.corestore.js';
|
|
44
44
|
export * from './runtime.corestore.databaseexportpackage.js';
|
|
45
|
+
export * from './runtime.corestore.relay.js';
|
|
45
46
|
export * from './runtime.cloudlylegacydeploymentsettlement.js';
|
|
46
47
|
export * from './runtime.cloudlylegacydeploymentsettlement.offlineexport.js';
|
|
47
48
|
|