@serve.zone/interfaces 27.1.1 → 27.3.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 +19 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +513 -5
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +1931 -8
- package/dist_ts/runtime.corestore.d.ts +91 -0
- package/dist_ts/runtime.corestore.js +403 -9
- package/package.json +1 -1
- package/readme.md +83 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/runtime.cloudlylegacydeploymentsettlement.ts +3751 -17
- package/ts/runtime.corestore.ts +707 -7
package/ts/runtime.corestore.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { validatePlatformObjectStorageBucketName } from './platform/objectstorag
|
|
|
23
23
|
import {
|
|
24
24
|
canonicalizeStrictJson,
|
|
25
25
|
createCanonicalJsonSha256Hex,
|
|
26
|
+
deepFreezeValue,
|
|
26
27
|
} from './private/canonicaljson.js';
|
|
27
28
|
|
|
28
29
|
export type TCorestoreCredentialCapability = 'database' | 'objectstorage';
|
|
@@ -44,7 +45,7 @@ export const corestoreCredentialRuntimeLimits = Object.freeze({
|
|
|
44
45
|
maximumSealedMaterialBytes: 64 * 1024,
|
|
45
46
|
minimumControlTokenBytes: 32,
|
|
46
47
|
maximumControlTokenBytes: 4096,
|
|
47
|
-
maximumSealedControlCredentialBytes:
|
|
48
|
+
maximumSealedControlCredentialBytes: 32 * 1024,
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
export const getCorestoreProviderConfigId = (
|
|
@@ -163,18 +164,110 @@ const isJwtIdentity = (valueArg: unknown): valueArg is IIdentityCredential => (
|
|
|
163
164
|
|
|
164
165
|
const utf8ByteLength = (valueArg: string): number => new TextEncoder().encode(valueArg).byteLength;
|
|
165
166
|
|
|
166
|
-
const
|
|
167
|
+
const isJsonWhitespaceCodePoint = (codePointArg: number): boolean => (
|
|
168
|
+
(codePointArg >= 0x0009 && codePointArg <= 0x000d)
|
|
169
|
+
|| codePointArg === 0x0020
|
|
170
|
+
|| codePointArg === 0x00a0
|
|
171
|
+
|| codePointArg === 0x1680
|
|
172
|
+
|| (codePointArg >= 0x2000 && codePointArg <= 0x200a)
|
|
173
|
+
|| codePointArg === 0x2028
|
|
174
|
+
|| codePointArg === 0x2029
|
|
175
|
+
|| codePointArg === 0x202f
|
|
176
|
+
|| codePointArg === 0x205f
|
|
177
|
+
|| codePointArg === 0x3000
|
|
178
|
+
|| codePointArg === 0xfeff
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const isCorestoreControlTokenBytes = (valueArg: Uint8Array): boolean => {
|
|
182
|
+
if (valueArg.byteLength < corestoreCredentialRuntimeLimits.minimumControlTokenBytes
|
|
183
|
+
|| valueArg.byteLength > corestoreCredentialRuntimeLimits.maximumControlTokenBytes) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
for (let index = 0; index < valueArg.length;) {
|
|
187
|
+
const first = valueArg[index]!;
|
|
188
|
+
let codePoint: number;
|
|
189
|
+
if (first <= 0x7f) {
|
|
190
|
+
codePoint = first;
|
|
191
|
+
index++;
|
|
192
|
+
} else if (first >= 0xc2 && first <= 0xdf
|
|
193
|
+
&& index + 1 < valueArg.length
|
|
194
|
+
&& valueArg[index + 1]! >= 0x80 && valueArg[index + 1]! <= 0xbf) {
|
|
195
|
+
codePoint = ((first & 0x1f) << 6) | (valueArg[index + 1]! & 0x3f);
|
|
196
|
+
index += 2;
|
|
197
|
+
} else if (first >= 0xe0 && first <= 0xef
|
|
198
|
+
&& index + 2 < valueArg.length
|
|
199
|
+
&& valueArg[index + 1]! >= (first === 0xe0 ? 0xa0 : 0x80)
|
|
200
|
+
&& valueArg[index + 1]! <= (first === 0xed ? 0x9f : 0xbf)
|
|
201
|
+
&& valueArg[index + 2]! >= 0x80 && valueArg[index + 2]! <= 0xbf) {
|
|
202
|
+
codePoint = ((first & 0x0f) << 12)
|
|
203
|
+
| ((valueArg[index + 1]! & 0x3f) << 6)
|
|
204
|
+
| (valueArg[index + 2]! & 0x3f);
|
|
205
|
+
index += 3;
|
|
206
|
+
} else if (first >= 0xf0 && first <= 0xf4
|
|
207
|
+
&& index + 3 < valueArg.length
|
|
208
|
+
&& valueArg[index + 1]! >= (first === 0xf0 ? 0x90 : 0x80)
|
|
209
|
+
&& valueArg[index + 1]! <= (first === 0xf4 ? 0x8f : 0xbf)
|
|
210
|
+
&& valueArg[index + 2]! >= 0x80 && valueArg[index + 2]! <= 0xbf
|
|
211
|
+
&& valueArg[index + 3]! >= 0x80 && valueArg[index + 3]! <= 0xbf) {
|
|
212
|
+
codePoint = ((first & 0x07) << 18)
|
|
213
|
+
| ((valueArg[index + 1]! & 0x3f) << 12)
|
|
214
|
+
| ((valueArg[index + 2]! & 0x3f) << 6)
|
|
215
|
+
| (valueArg[index + 3]! & 0x3f);
|
|
216
|
+
index += 4;
|
|
217
|
+
} else {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
if (isJsonWhitespaceCodePoint(codePoint)) return false;
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const isCorestoreControlToken = (valueArg: unknown): valueArg is string => {
|
|
167
226
|
if (typeof valueArg !== 'string' || valueArg.length === 0 || /\s/u.test(valueArg)) return false;
|
|
168
227
|
const encoded = new TextEncoder().encode(valueArg);
|
|
169
|
-
let roundTrip: string;
|
|
170
228
|
try {
|
|
171
|
-
roundTrip = new TextDecoder('utf-8', { fatal: true }).decode(encoded);
|
|
229
|
+
const roundTrip = new TextDecoder('utf-8', { fatal: true }).decode(encoded);
|
|
230
|
+
return roundTrip === valueArg
|
|
231
|
+
&& encoded.byteLength >= corestoreCredentialRuntimeLimits.minimumControlTokenBytes
|
|
232
|
+
&& encoded.byteLength <= corestoreCredentialRuntimeLimits.maximumControlTokenBytes;
|
|
172
233
|
} catch {
|
|
173
234
|
return false;
|
|
235
|
+
} finally {
|
|
236
|
+
encoded.fill(0);
|
|
174
237
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export const createCorestoreControlCredentialPlaintextBytes = (
|
|
241
|
+
tokenBytesArg: Uint8Array,
|
|
242
|
+
): Uint8Array | undefined => {
|
|
243
|
+
if (!isCorestoreControlTokenBytes(tokenBytesArg)) return undefined;
|
|
244
|
+
const prefix = new TextEncoder().encode(
|
|
245
|
+
`{"schemaVersion":1,"key":"${corestoreControlCredentialKey}","environment":"${
|
|
246
|
+
corestoreCredentialEnvironment
|
|
247
|
+
}","value":"`,
|
|
248
|
+
);
|
|
249
|
+
const suffix = new Uint8Array([0x22, 0x7d]);
|
|
250
|
+
const escaped = new Uint8Array(prefix.length + tokenBytesArg.length * 6 + suffix.length);
|
|
251
|
+
let offset = prefix.length;
|
|
252
|
+
escaped.set(prefix);
|
|
253
|
+
for (const byte of tokenBytesArg) {
|
|
254
|
+
if (byte === 0x22 || byte === 0x5c) {
|
|
255
|
+
escaped[offset++] = 0x5c;
|
|
256
|
+
escaped[offset++] = byte;
|
|
257
|
+
} else if (byte < 0x20) {
|
|
258
|
+
escaped.set([0x5c, 0x75, 0x30, 0x30], offset);
|
|
259
|
+
offset += 4;
|
|
260
|
+
escaped[offset++] = '0123456789abcdef'.charCodeAt(byte >> 4);
|
|
261
|
+
escaped[offset++] = '0123456789abcdef'.charCodeAt(byte & 0x0f);
|
|
262
|
+
} else {
|
|
263
|
+
escaped[offset++] = byte;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
escaped.set(suffix, offset);
|
|
267
|
+
offset += suffix.length;
|
|
268
|
+
const plaintext = escaped.slice(0, offset);
|
|
269
|
+
escaped.fill(0);
|
|
270
|
+
return plaintext;
|
|
178
271
|
};
|
|
179
272
|
|
|
180
273
|
export const validateGetCorestoreControlCredentialMaterialRequest = (
|
|
@@ -1174,3 +1267,610 @@ export const getCorestoreCredentialPublicationErrorMetadata = (
|
|
|
1174
1267
|
code: codeArg,
|
|
1175
1268
|
retryable: corestoreCredentialPublicationErrorMetadata[codeArg].retryable,
|
|
1176
1269
|
});
|
|
1270
|
+
|
|
1271
|
+
const corestoreDatabaseBackupMaximumSnapshotPayloadBytes = 96 * 1024 * 1024;
|
|
1272
|
+
|
|
1273
|
+
export const corestoreDatabaseBackupRuntimeLimits = Object.freeze({
|
|
1274
|
+
maximumControlBytes: 16 * 1024,
|
|
1275
|
+
maximumClosureBytes: 256 * 1024 * 1024,
|
|
1276
|
+
maximumVerifiedPlaintextBytes: 128 * 1024 * 1024,
|
|
1277
|
+
maximumSnapshotPayloadBytes: corestoreDatabaseBackupMaximumSnapshotPayloadBytes,
|
|
1278
|
+
maximumSnapshotOriginalBytes: corestoreDatabaseBackupMaximumSnapshotPayloadBytes + 1,
|
|
1279
|
+
maximumClosureObjects: 4096,
|
|
1280
|
+
maximumClosureChunks: 1_000_000,
|
|
1281
|
+
maximumServiceNameBytes: 1024,
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
export interface ICorestoreDatabaseAllocationReference {
|
|
1285
|
+
purpose: 'scratch-rehearsal';
|
|
1286
|
+
rehearsalId: string;
|
|
1287
|
+
allocationId: string;
|
|
1288
|
+
generation: number;
|
|
1289
|
+
bindingSha256: string;
|
|
1290
|
+
receiptSha256: string;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
export interface ICorestoreDatabaseBackupSnapshotTags {
|
|
1294
|
+
corestore: 'resource';
|
|
1295
|
+
serviceId: string;
|
|
1296
|
+
serviceName: string;
|
|
1297
|
+
capability: 'database';
|
|
1298
|
+
resourceName: string;
|
|
1299
|
+
snapshotName: string;
|
|
1300
|
+
backupId: string;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
export interface ICorestoreDatabaseBackupSnapshot {
|
|
1304
|
+
capability: 'database';
|
|
1305
|
+
resourceName: string;
|
|
1306
|
+
snapshotId: string;
|
|
1307
|
+
snapshotName: string;
|
|
1308
|
+
originalSize: number;
|
|
1309
|
+
storedSize: number;
|
|
1310
|
+
createdAt: number;
|
|
1311
|
+
tags: ICorestoreDatabaseBackupSnapshotTags;
|
|
1312
|
+
databaseName: string;
|
|
1313
|
+
databaseAllocation?: ICorestoreDatabaseAllocationReference;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
export interface ICorestoreDatabaseBackupClosureReceipt {
|
|
1317
|
+
formatVersion: 1;
|
|
1318
|
+
snapshotIds: [string];
|
|
1319
|
+
objectCount: number;
|
|
1320
|
+
packCount: number;
|
|
1321
|
+
chunkCount: number;
|
|
1322
|
+
payloadBytes: number;
|
|
1323
|
+
archiveBytes: number;
|
|
1324
|
+
sha256: string;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
export interface ICorestoreDatabaseBackupReceipt {
|
|
1328
|
+
schemaVersion: 1;
|
|
1329
|
+
format: 'corestore.database.backup.v1';
|
|
1330
|
+
backupId: string;
|
|
1331
|
+
sourceServiceId: string;
|
|
1332
|
+
createdAt: number;
|
|
1333
|
+
snapshot: ICorestoreDatabaseBackupSnapshot;
|
|
1334
|
+
closure: ICorestoreDatabaseBackupClosureReceipt;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
export interface ICorestoreDatabaseBackupRestoreRequest {
|
|
1338
|
+
schemaVersion: 1;
|
|
1339
|
+
targetServiceId: string;
|
|
1340
|
+
receipt: ICorestoreDatabaseBackupReceipt;
|
|
1341
|
+
expectedDatabaseAllocation?: ICorestoreDatabaseAllocationReference;
|
|
1342
|
+
/** Caller-owned idempotency identity. A new value forces a new fenced restore attempt. */
|
|
1343
|
+
restoreAttemptId?: string;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
export interface ICorestoreDatabaseBackupRestoreResponse {
|
|
1347
|
+
schemaVersion: 1;
|
|
1348
|
+
ok: true;
|
|
1349
|
+
backupId: string;
|
|
1350
|
+
sourceServiceId: string;
|
|
1351
|
+
targetServiceId: string;
|
|
1352
|
+
snapshotId: string;
|
|
1353
|
+
restoreId: string;
|
|
1354
|
+
requestSha256: string;
|
|
1355
|
+
replayed: boolean;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
const corestoreDatabaseBackupIdentifierPattern =
|
|
1359
|
+
/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
1360
|
+
const corestoreDatabaseAllocationRehearsalIdPattern =
|
|
1361
|
+
/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,191}$/;
|
|
1362
|
+
|
|
1363
|
+
const failCorestoreDatabaseBackup = (reasonArg: string): never => {
|
|
1364
|
+
throw new Error(reasonArg);
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1367
|
+
const readCorestoreDatabaseBackupRecord = (
|
|
1368
|
+
valueArg: unknown,
|
|
1369
|
+
pathArg: string,
|
|
1370
|
+
): Record<string, unknown> => {
|
|
1371
|
+
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
|
|
1372
|
+
return failCorestoreDatabaseBackup(`${pathArg} must be a plain object`);
|
|
1373
|
+
}
|
|
1374
|
+
const prototype = Object.getPrototypeOf(valueArg);
|
|
1375
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
1376
|
+
return failCorestoreDatabaseBackup(`${pathArg} must be a plain object`);
|
|
1377
|
+
}
|
|
1378
|
+
const value = valueArg as Record<string, unknown>;
|
|
1379
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1380
|
+
if (typeof key !== 'string') {
|
|
1381
|
+
return failCorestoreDatabaseBackup(`${pathArg} must contain string keys only`);
|
|
1382
|
+
}
|
|
1383
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1384
|
+
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) {
|
|
1385
|
+
return failCorestoreDatabaseBackup(`${pathArg} must contain data properties only`);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return value;
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
const assertCorestoreDatabaseBackupExactKeys = (
|
|
1392
|
+
valueArg: Record<string, unknown>,
|
|
1393
|
+
keysArg: readonly string[],
|
|
1394
|
+
pathArg: string,
|
|
1395
|
+
): void => {
|
|
1396
|
+
if (!hasExactKeys(valueArg, keysArg)) {
|
|
1397
|
+
failCorestoreDatabaseBackup(`${pathArg} must use its exact schema`);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
const readCorestoreDatabaseBackupIdentifier = (
|
|
1402
|
+
valueArg: unknown,
|
|
1403
|
+
pathArg: string,
|
|
1404
|
+
): string => {
|
|
1405
|
+
if (typeof valueArg !== 'string' || !corestoreDatabaseBackupIdentifierPattern.test(valueArg)) {
|
|
1406
|
+
return failCorestoreDatabaseBackup(`${pathArg} must be a canonical identifier`);
|
|
1407
|
+
}
|
|
1408
|
+
return valueArg;
|
|
1409
|
+
};
|
|
1410
|
+
|
|
1411
|
+
const readCorestoreDatabaseBackupSha256 = (
|
|
1412
|
+
valueArg: unknown,
|
|
1413
|
+
pathArg: string,
|
|
1414
|
+
): string => {
|
|
1415
|
+
if (typeof valueArg !== 'string' || !bareSha256Pattern.test(valueArg)) {
|
|
1416
|
+
return failCorestoreDatabaseBackup(`${pathArg} must be bare lowercase SHA-256`);
|
|
1417
|
+
}
|
|
1418
|
+
return valueArg;
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
const readCorestoreDatabaseBackupInteger = (
|
|
1422
|
+
valueArg: unknown,
|
|
1423
|
+
pathArg: string,
|
|
1424
|
+
minimumArg: number,
|
|
1425
|
+
maximumArg = Number.MAX_SAFE_INTEGER,
|
|
1426
|
+
): number => {
|
|
1427
|
+
if (!Number.isSafeInteger(valueArg)
|
|
1428
|
+
|| Object.is(valueArg, -0)
|
|
1429
|
+
|| (valueArg as number) < minimumArg
|
|
1430
|
+
|| (valueArg as number) > maximumArg) {
|
|
1431
|
+
return failCorestoreDatabaseBackup(`${pathArg} must be a bounded safe integer`);
|
|
1432
|
+
}
|
|
1433
|
+
return valueArg as number;
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
const finalizeCorestoreDatabaseBackup = <T>(
|
|
1437
|
+
valueArg: T,
|
|
1438
|
+
pathArg: string,
|
|
1439
|
+
): Readonly<T> => {
|
|
1440
|
+
const canonical = canonicalizeStrictJson(valueArg, failCorestoreDatabaseBackup, pathArg);
|
|
1441
|
+
if (utf8ByteLength(canonical) > corestoreDatabaseBackupRuntimeLimits.maximumControlBytes) {
|
|
1442
|
+
failCorestoreDatabaseBackup(`${pathArg} exceeds its canonical byte limit`);
|
|
1443
|
+
}
|
|
1444
|
+
return deepFreezeValue(valueArg);
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
const normalizeCorestoreDatabaseAllocationReferenceInternal = (
|
|
1448
|
+
valueArg: unknown,
|
|
1449
|
+
pathArg: string,
|
|
1450
|
+
): ICorestoreDatabaseAllocationReference => {
|
|
1451
|
+
const value = readCorestoreDatabaseBackupRecord(valueArg, pathArg);
|
|
1452
|
+
assertCorestoreDatabaseBackupExactKeys(value, [
|
|
1453
|
+
'purpose',
|
|
1454
|
+
'rehearsalId',
|
|
1455
|
+
'allocationId',
|
|
1456
|
+
'generation',
|
|
1457
|
+
'bindingSha256',
|
|
1458
|
+
'receiptSha256',
|
|
1459
|
+
], pathArg);
|
|
1460
|
+
if (value.purpose !== 'scratch-rehearsal'
|
|
1461
|
+
|| typeof value.rehearsalId !== 'string'
|
|
1462
|
+
|| !corestoreDatabaseAllocationRehearsalIdPattern.test(value.rehearsalId)) {
|
|
1463
|
+
failCorestoreDatabaseBackup(`${pathArg} rehearsal authority must be canonical`);
|
|
1464
|
+
}
|
|
1465
|
+
const rehearsalId = value.rehearsalId as string;
|
|
1466
|
+
return {
|
|
1467
|
+
purpose: 'scratch-rehearsal',
|
|
1468
|
+
rehearsalId,
|
|
1469
|
+
allocationId: readCorestoreDatabaseBackupSha256(
|
|
1470
|
+
value.allocationId,
|
|
1471
|
+
`${pathArg}.allocationId`,
|
|
1472
|
+
),
|
|
1473
|
+
generation: readCorestoreDatabaseBackupInteger(
|
|
1474
|
+
value.generation,
|
|
1475
|
+
`${pathArg}.generation`,
|
|
1476
|
+
1,
|
|
1477
|
+
),
|
|
1478
|
+
bindingSha256: readCorestoreDatabaseBackupSha256(
|
|
1479
|
+
value.bindingSha256,
|
|
1480
|
+
`${pathArg}.bindingSha256`,
|
|
1481
|
+
),
|
|
1482
|
+
receiptSha256: readCorestoreDatabaseBackupSha256(
|
|
1483
|
+
value.receiptSha256,
|
|
1484
|
+
`${pathArg}.receiptSha256`,
|
|
1485
|
+
),
|
|
1486
|
+
};
|
|
1487
|
+
};
|
|
1488
|
+
|
|
1489
|
+
export const normalizeCorestoreDatabaseAllocationReference = (
|
|
1490
|
+
valueArg: unknown,
|
|
1491
|
+
): Readonly<ICorestoreDatabaseAllocationReference> => finalizeCorestoreDatabaseBackup(
|
|
1492
|
+
normalizeCorestoreDatabaseAllocationReferenceInternal(valueArg, 'databaseAllocation'),
|
|
1493
|
+
'databaseAllocation',
|
|
1494
|
+
);
|
|
1495
|
+
|
|
1496
|
+
const normalizeCorestoreDatabaseBackupReceiptInternal = (
|
|
1497
|
+
valueArg: unknown,
|
|
1498
|
+
pathArg: string,
|
|
1499
|
+
): ICorestoreDatabaseBackupReceipt => {
|
|
1500
|
+
const value = readCorestoreDatabaseBackupRecord(valueArg, pathArg);
|
|
1501
|
+
assertCorestoreDatabaseBackupExactKeys(value, [
|
|
1502
|
+
'schemaVersion',
|
|
1503
|
+
'format',
|
|
1504
|
+
'backupId',
|
|
1505
|
+
'sourceServiceId',
|
|
1506
|
+
'createdAt',
|
|
1507
|
+
'snapshot',
|
|
1508
|
+
'closure',
|
|
1509
|
+
], pathArg);
|
|
1510
|
+
if (value.schemaVersion !== 1 || value.format !== 'corestore.database.backup.v1') {
|
|
1511
|
+
failCorestoreDatabaseBackup(`${pathArg} version must be canonical`);
|
|
1512
|
+
}
|
|
1513
|
+
const backupId = readCorestoreDatabaseBackupIdentifier(value.backupId, `${pathArg}.backupId`);
|
|
1514
|
+
const sourceServiceId = readCorestoreDatabaseBackupIdentifier(
|
|
1515
|
+
value.sourceServiceId,
|
|
1516
|
+
`${pathArg}.sourceServiceId`,
|
|
1517
|
+
);
|
|
1518
|
+
const createdAt = readCorestoreDatabaseBackupInteger(value.createdAt, `${pathArg}.createdAt`, 1);
|
|
1519
|
+
const snapshotValue = readCorestoreDatabaseBackupRecord(value.snapshot, `${pathArg}.snapshot`);
|
|
1520
|
+
const hasDatabaseAllocation = Object.hasOwn(snapshotValue, 'databaseAllocation');
|
|
1521
|
+
assertCorestoreDatabaseBackupExactKeys(snapshotValue, [
|
|
1522
|
+
'capability',
|
|
1523
|
+
'resourceName',
|
|
1524
|
+
'snapshotId',
|
|
1525
|
+
'snapshotName',
|
|
1526
|
+
'originalSize',
|
|
1527
|
+
'storedSize',
|
|
1528
|
+
'createdAt',
|
|
1529
|
+
'tags',
|
|
1530
|
+
'databaseName',
|
|
1531
|
+
...(hasDatabaseAllocation ? ['databaseAllocation'] : []),
|
|
1532
|
+
], `${pathArg}.snapshot`);
|
|
1533
|
+
if (snapshotValue.capability !== 'database') {
|
|
1534
|
+
failCorestoreDatabaseBackup(`${pathArg}.snapshot capability must be database`);
|
|
1535
|
+
}
|
|
1536
|
+
const resourceName = readCorestoreDatabaseBackupIdentifier(
|
|
1537
|
+
snapshotValue.resourceName,
|
|
1538
|
+
`${pathArg}.snapshot.resourceName`,
|
|
1539
|
+
);
|
|
1540
|
+
const snapshotId = readCorestoreDatabaseBackupIdentifier(
|
|
1541
|
+
snapshotValue.snapshotId,
|
|
1542
|
+
`${pathArg}.snapshot.snapshotId`,
|
|
1543
|
+
);
|
|
1544
|
+
const snapshotName = readCorestoreDatabaseBackupIdentifier(
|
|
1545
|
+
snapshotValue.snapshotName,
|
|
1546
|
+
`${pathArg}.snapshot.snapshotName`,
|
|
1547
|
+
);
|
|
1548
|
+
const databaseName = readCorestoreDatabaseBackupIdentifier(
|
|
1549
|
+
snapshotValue.databaseName,
|
|
1550
|
+
`${pathArg}.snapshot.databaseName`,
|
|
1551
|
+
);
|
|
1552
|
+
const snapshotCreatedAt = readCorestoreDatabaseBackupInteger(
|
|
1553
|
+
snapshotValue.createdAt,
|
|
1554
|
+
`${pathArg}.snapshot.createdAt`,
|
|
1555
|
+
1,
|
|
1556
|
+
);
|
|
1557
|
+
if (snapshotName !== backupId || snapshotCreatedAt !== createdAt) {
|
|
1558
|
+
failCorestoreDatabaseBackup(`${pathArg}.snapshot identity must match its receipt`);
|
|
1559
|
+
}
|
|
1560
|
+
const tagsValue = readCorestoreDatabaseBackupRecord(
|
|
1561
|
+
snapshotValue.tags,
|
|
1562
|
+
`${pathArg}.snapshot.tags`,
|
|
1563
|
+
);
|
|
1564
|
+
assertCorestoreDatabaseBackupExactKeys(tagsValue, [
|
|
1565
|
+
'corestore',
|
|
1566
|
+
'serviceId',
|
|
1567
|
+
'serviceName',
|
|
1568
|
+
'capability',
|
|
1569
|
+
'resourceName',
|
|
1570
|
+
'snapshotName',
|
|
1571
|
+
'backupId',
|
|
1572
|
+
], `${pathArg}.snapshot.tags`);
|
|
1573
|
+
if (tagsValue.corestore !== 'resource'
|
|
1574
|
+
|| tagsValue.serviceId !== sourceServiceId
|
|
1575
|
+
|| tagsValue.capability !== 'database'
|
|
1576
|
+
|| tagsValue.resourceName !== resourceName
|
|
1577
|
+
|| tagsValue.snapshotName !== backupId
|
|
1578
|
+
|| tagsValue.backupId !== backupId
|
|
1579
|
+
|| typeof tagsValue.serviceName !== 'string'
|
|
1580
|
+
|| tagsValue.serviceName.length > 256
|
|
1581
|
+
|| utf8ByteLength(tagsValue.serviceName)
|
|
1582
|
+
> corestoreDatabaseBackupRuntimeLimits.maximumServiceNameBytes) {
|
|
1583
|
+
failCorestoreDatabaseBackup(`${pathArg}.snapshot.tags ownership must be canonical`);
|
|
1584
|
+
}
|
|
1585
|
+
const serviceName = tagsValue.serviceName as string;
|
|
1586
|
+
const closureValue = readCorestoreDatabaseBackupRecord(value.closure, `${pathArg}.closure`);
|
|
1587
|
+
assertCorestoreDatabaseBackupExactKeys(closureValue, [
|
|
1588
|
+
'formatVersion',
|
|
1589
|
+
'snapshotIds',
|
|
1590
|
+
'objectCount',
|
|
1591
|
+
'packCount',
|
|
1592
|
+
'chunkCount',
|
|
1593
|
+
'payloadBytes',
|
|
1594
|
+
'archiveBytes',
|
|
1595
|
+
'sha256',
|
|
1596
|
+
], `${pathArg}.closure`);
|
|
1597
|
+
if (!Array.isArray(closureValue.snapshotIds)
|
|
1598
|
+
|| Object.getPrototypeOf(closureValue.snapshotIds) !== Array.prototype
|
|
1599
|
+
|| closureValue.snapshotIds.length !== 1
|
|
1600
|
+
|| !Object.hasOwn(closureValue.snapshotIds, 0)
|
|
1601
|
+
|| Reflect.ownKeys(closureValue.snapshotIds).some((keyArg) => keyArg !== 'length' && keyArg !== '0')) {
|
|
1602
|
+
failCorestoreDatabaseBackup(`${pathArg}.closure snapshot coverage must be exact`);
|
|
1603
|
+
}
|
|
1604
|
+
const snapshotIdDescriptor = Object.getOwnPropertyDescriptor(closureValue.snapshotIds, '0');
|
|
1605
|
+
if (!snapshotIdDescriptor
|
|
1606
|
+
|| !snapshotIdDescriptor.enumerable
|
|
1607
|
+
|| !Object.hasOwn(snapshotIdDescriptor, 'value')
|
|
1608
|
+
|| snapshotIdDescriptor.value !== snapshotId) {
|
|
1609
|
+
failCorestoreDatabaseBackup(`${pathArg}.closure snapshot coverage must use data entries`);
|
|
1610
|
+
}
|
|
1611
|
+
const objectCount = readCorestoreDatabaseBackupInteger(
|
|
1612
|
+
closureValue.objectCount,
|
|
1613
|
+
`${pathArg}.closure.objectCount`,
|
|
1614
|
+
1,
|
|
1615
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureObjects,
|
|
1616
|
+
);
|
|
1617
|
+
const packCount = readCorestoreDatabaseBackupInteger(
|
|
1618
|
+
closureValue.packCount,
|
|
1619
|
+
`${pathArg}.closure.packCount`,
|
|
1620
|
+
1,
|
|
1621
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureObjects,
|
|
1622
|
+
);
|
|
1623
|
+
const chunkCount = readCorestoreDatabaseBackupInteger(
|
|
1624
|
+
closureValue.chunkCount,
|
|
1625
|
+
`${pathArg}.closure.chunkCount`,
|
|
1626
|
+
1,
|
|
1627
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureChunks,
|
|
1628
|
+
);
|
|
1629
|
+
if (packCount > objectCount) {
|
|
1630
|
+
failCorestoreDatabaseBackup(`${pathArg}.closure pack coverage must be canonical`);
|
|
1631
|
+
}
|
|
1632
|
+
const originalSize = readCorestoreDatabaseBackupInteger(
|
|
1633
|
+
snapshotValue.originalSize,
|
|
1634
|
+
`${pathArg}.snapshot.originalSize`,
|
|
1635
|
+
1,
|
|
1636
|
+
corestoreDatabaseBackupRuntimeLimits.maximumSnapshotOriginalBytes,
|
|
1637
|
+
);
|
|
1638
|
+
const storedSize = readCorestoreDatabaseBackupInteger(
|
|
1639
|
+
snapshotValue.storedSize,
|
|
1640
|
+
`${pathArg}.snapshot.storedSize`,
|
|
1641
|
+
1,
|
|
1642
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes,
|
|
1643
|
+
);
|
|
1644
|
+
const databaseAllocation = hasDatabaseAllocation
|
|
1645
|
+
? normalizeCorestoreDatabaseAllocationReferenceInternal(
|
|
1646
|
+
snapshotValue.databaseAllocation,
|
|
1647
|
+
`${pathArg}.snapshot.databaseAllocation`,
|
|
1648
|
+
)
|
|
1649
|
+
: undefined;
|
|
1650
|
+
return {
|
|
1651
|
+
schemaVersion: 1,
|
|
1652
|
+
format: 'corestore.database.backup.v1',
|
|
1653
|
+
backupId,
|
|
1654
|
+
sourceServiceId,
|
|
1655
|
+
createdAt,
|
|
1656
|
+
snapshot: {
|
|
1657
|
+
capability: 'database',
|
|
1658
|
+
resourceName,
|
|
1659
|
+
snapshotId,
|
|
1660
|
+
snapshotName,
|
|
1661
|
+
originalSize,
|
|
1662
|
+
storedSize,
|
|
1663
|
+
createdAt: snapshotCreatedAt,
|
|
1664
|
+
tags: {
|
|
1665
|
+
corestore: 'resource',
|
|
1666
|
+
serviceId: sourceServiceId,
|
|
1667
|
+
serviceName,
|
|
1668
|
+
capability: 'database',
|
|
1669
|
+
resourceName,
|
|
1670
|
+
snapshotName,
|
|
1671
|
+
backupId,
|
|
1672
|
+
},
|
|
1673
|
+
databaseName,
|
|
1674
|
+
...(databaseAllocation ? { databaseAllocation } : {}),
|
|
1675
|
+
},
|
|
1676
|
+
closure: {
|
|
1677
|
+
formatVersion: closureValue.formatVersion === 1
|
|
1678
|
+
? 1
|
|
1679
|
+
: failCorestoreDatabaseBackup(`${pathArg}.closure formatVersion must be 1`),
|
|
1680
|
+
snapshotIds: [snapshotId],
|
|
1681
|
+
objectCount,
|
|
1682
|
+
packCount,
|
|
1683
|
+
chunkCount,
|
|
1684
|
+
payloadBytes: readCorestoreDatabaseBackupInteger(
|
|
1685
|
+
closureValue.payloadBytes,
|
|
1686
|
+
`${pathArg}.closure.payloadBytes`,
|
|
1687
|
+
1,
|
|
1688
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes,
|
|
1689
|
+
),
|
|
1690
|
+
archiveBytes: readCorestoreDatabaseBackupInteger(
|
|
1691
|
+
closureValue.archiveBytes,
|
|
1692
|
+
`${pathArg}.closure.archiveBytes`,
|
|
1693
|
+
1,
|
|
1694
|
+
corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes,
|
|
1695
|
+
),
|
|
1696
|
+
sha256: readCorestoreDatabaseBackupSha256(
|
|
1697
|
+
closureValue.sha256,
|
|
1698
|
+
`${pathArg}.closure.sha256`,
|
|
1699
|
+
),
|
|
1700
|
+
},
|
|
1701
|
+
};
|
|
1702
|
+
};
|
|
1703
|
+
|
|
1704
|
+
export const normalizeCorestoreDatabaseBackupReceipt = (
|
|
1705
|
+
valueArg: unknown,
|
|
1706
|
+
): Readonly<ICorestoreDatabaseBackupReceipt> => finalizeCorestoreDatabaseBackup(
|
|
1707
|
+
normalizeCorestoreDatabaseBackupReceiptInternal(valueArg, 'databaseBackupReceipt'),
|
|
1708
|
+
'databaseBackupReceipt',
|
|
1709
|
+
);
|
|
1710
|
+
|
|
1711
|
+
export const normalizeCorestoreDatabaseBackupRestoreRequest = (
|
|
1712
|
+
valueArg: unknown,
|
|
1713
|
+
): Readonly<ICorestoreDatabaseBackupRestoreRequest> => {
|
|
1714
|
+
const value = readCorestoreDatabaseBackupRecord(valueArg, 'databaseBackupRestoreRequest');
|
|
1715
|
+
const hasAllocation = Object.hasOwn(value, 'expectedDatabaseAllocation');
|
|
1716
|
+
const hasRestoreAttemptId = Object.hasOwn(value, 'restoreAttemptId');
|
|
1717
|
+
assertCorestoreDatabaseBackupExactKeys(value, [
|
|
1718
|
+
'schemaVersion',
|
|
1719
|
+
'targetServiceId',
|
|
1720
|
+
'receipt',
|
|
1721
|
+
...(hasAllocation ? ['expectedDatabaseAllocation'] : []),
|
|
1722
|
+
...(hasRestoreAttemptId ? ['restoreAttemptId'] : []),
|
|
1723
|
+
], 'databaseBackupRestoreRequest');
|
|
1724
|
+
if (value.schemaVersion !== 1) {
|
|
1725
|
+
failCorestoreDatabaseBackup('databaseBackupRestoreRequest schemaVersion must be 1');
|
|
1726
|
+
}
|
|
1727
|
+
const receipt = normalizeCorestoreDatabaseBackupReceiptInternal(
|
|
1728
|
+
value.receipt,
|
|
1729
|
+
'databaseBackupRestoreRequest.receipt',
|
|
1730
|
+
);
|
|
1731
|
+
const expectedDatabaseAllocation = hasAllocation
|
|
1732
|
+
? normalizeCorestoreDatabaseAllocationReferenceInternal(
|
|
1733
|
+
value.expectedDatabaseAllocation,
|
|
1734
|
+
'databaseBackupRestoreRequest.expectedDatabaseAllocation',
|
|
1735
|
+
)
|
|
1736
|
+
: undefined;
|
|
1737
|
+
const restoreAttemptId = hasRestoreAttemptId
|
|
1738
|
+
? readCorestoreDatabaseBackupIdentifier(
|
|
1739
|
+
value.restoreAttemptId,
|
|
1740
|
+
'databaseBackupRestoreRequest.restoreAttemptId',
|
|
1741
|
+
)
|
|
1742
|
+
: undefined;
|
|
1743
|
+
return finalizeCorestoreDatabaseBackup({
|
|
1744
|
+
schemaVersion: 1,
|
|
1745
|
+
targetServiceId: readCorestoreDatabaseBackupIdentifier(
|
|
1746
|
+
value.targetServiceId,
|
|
1747
|
+
'databaseBackupRestoreRequest.targetServiceId',
|
|
1748
|
+
),
|
|
1749
|
+
receipt,
|
|
1750
|
+
...(expectedDatabaseAllocation ? { expectedDatabaseAllocation } : {}),
|
|
1751
|
+
...(restoreAttemptId ? { restoreAttemptId } : {}),
|
|
1752
|
+
}, 'databaseBackupRestoreRequest');
|
|
1753
|
+
};
|
|
1754
|
+
|
|
1755
|
+
export const normalizeCorestoreDatabaseBackupRestoreResponse = (
|
|
1756
|
+
valueArg: unknown,
|
|
1757
|
+
): Readonly<ICorestoreDatabaseBackupRestoreResponse> => {
|
|
1758
|
+
const value = readCorestoreDatabaseBackupRecord(valueArg, 'databaseBackupRestoreResponse');
|
|
1759
|
+
assertCorestoreDatabaseBackupExactKeys(value, [
|
|
1760
|
+
'schemaVersion',
|
|
1761
|
+
'ok',
|
|
1762
|
+
'backupId',
|
|
1763
|
+
'sourceServiceId',
|
|
1764
|
+
'targetServiceId',
|
|
1765
|
+
'snapshotId',
|
|
1766
|
+
'restoreId',
|
|
1767
|
+
'requestSha256',
|
|
1768
|
+
'replayed',
|
|
1769
|
+
], 'databaseBackupRestoreResponse');
|
|
1770
|
+
if (value.schemaVersion !== 1 || value.ok !== true || typeof value.replayed !== 'boolean') {
|
|
1771
|
+
failCorestoreDatabaseBackup('databaseBackupRestoreResponse constants must be canonical');
|
|
1772
|
+
}
|
|
1773
|
+
const replayed = value.replayed as boolean;
|
|
1774
|
+
return finalizeCorestoreDatabaseBackup({
|
|
1775
|
+
schemaVersion: 1,
|
|
1776
|
+
ok: true,
|
|
1777
|
+
backupId: readCorestoreDatabaseBackupIdentifier(
|
|
1778
|
+
value.backupId,
|
|
1779
|
+
'databaseBackupRestoreResponse.backupId',
|
|
1780
|
+
),
|
|
1781
|
+
sourceServiceId: readCorestoreDatabaseBackupIdentifier(
|
|
1782
|
+
value.sourceServiceId,
|
|
1783
|
+
'databaseBackupRestoreResponse.sourceServiceId',
|
|
1784
|
+
),
|
|
1785
|
+
targetServiceId: readCorestoreDatabaseBackupIdentifier(
|
|
1786
|
+
value.targetServiceId,
|
|
1787
|
+
'databaseBackupRestoreResponse.targetServiceId',
|
|
1788
|
+
),
|
|
1789
|
+
snapshotId: readCorestoreDatabaseBackupIdentifier(
|
|
1790
|
+
value.snapshotId,
|
|
1791
|
+
'databaseBackupRestoreResponse.snapshotId',
|
|
1792
|
+
),
|
|
1793
|
+
restoreId: readCorestoreDatabaseBackupIdentifier(
|
|
1794
|
+
value.restoreId,
|
|
1795
|
+
'databaseBackupRestoreResponse.restoreId',
|
|
1796
|
+
),
|
|
1797
|
+
requestSha256: readCorestoreDatabaseBackupSha256(
|
|
1798
|
+
value.requestSha256,
|
|
1799
|
+
'databaseBackupRestoreResponse.requestSha256',
|
|
1800
|
+
),
|
|
1801
|
+
replayed,
|
|
1802
|
+
}, 'databaseBackupRestoreResponse');
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
const createCorestoreDatabaseBackupCanonicalJson = (
|
|
1806
|
+
valueArg: unknown,
|
|
1807
|
+
pathArg: string,
|
|
1808
|
+
): string => canonicalizeStrictJson(valueArg, failCorestoreDatabaseBackup, pathArg);
|
|
1809
|
+
|
|
1810
|
+
export const encodeCorestoreDatabaseAllocationReference = (
|
|
1811
|
+
valueArg: unknown,
|
|
1812
|
+
): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson(
|
|
1813
|
+
normalizeCorestoreDatabaseAllocationReference(valueArg),
|
|
1814
|
+
'databaseAllocation',
|
|
1815
|
+
));
|
|
1816
|
+
|
|
1817
|
+
export const encodeCorestoreDatabaseBackupReceipt = (
|
|
1818
|
+
valueArg: unknown,
|
|
1819
|
+
): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson(
|
|
1820
|
+
normalizeCorestoreDatabaseBackupReceipt(valueArg),
|
|
1821
|
+
'databaseBackupReceipt',
|
|
1822
|
+
));
|
|
1823
|
+
|
|
1824
|
+
export const encodeCorestoreDatabaseBackupRestoreRequest = (
|
|
1825
|
+
valueArg: unknown,
|
|
1826
|
+
): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson(
|
|
1827
|
+
normalizeCorestoreDatabaseBackupRestoreRequest(valueArg),
|
|
1828
|
+
'databaseBackupRestoreRequest',
|
|
1829
|
+
));
|
|
1830
|
+
|
|
1831
|
+
export const encodeCorestoreDatabaseBackupRestoreResponse = (
|
|
1832
|
+
valueArg: unknown,
|
|
1833
|
+
): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson(
|
|
1834
|
+
normalizeCorestoreDatabaseBackupRestoreResponse(valueArg),
|
|
1835
|
+
'databaseBackupRestoreResponse',
|
|
1836
|
+
));
|
|
1837
|
+
|
|
1838
|
+
export const computeCorestoreDatabaseAllocationReferenceSha256 = async (
|
|
1839
|
+
valueArg: unknown,
|
|
1840
|
+
): Promise<string> => createCanonicalJsonSha256Hex(
|
|
1841
|
+
createCorestoreDatabaseBackupCanonicalJson(
|
|
1842
|
+
normalizeCorestoreDatabaseAllocationReference(valueArg),
|
|
1843
|
+
'databaseAllocation',
|
|
1844
|
+
),
|
|
1845
|
+
failCorestoreDatabaseBackup,
|
|
1846
|
+
);
|
|
1847
|
+
|
|
1848
|
+
export const computeCorestoreDatabaseBackupReceiptSha256 = async (
|
|
1849
|
+
valueArg: unknown,
|
|
1850
|
+
): Promise<string> => createCanonicalJsonSha256Hex(
|
|
1851
|
+
createCorestoreDatabaseBackupCanonicalJson(
|
|
1852
|
+
normalizeCorestoreDatabaseBackupReceipt(valueArg),
|
|
1853
|
+
'databaseBackupReceipt',
|
|
1854
|
+
),
|
|
1855
|
+
failCorestoreDatabaseBackup,
|
|
1856
|
+
);
|
|
1857
|
+
|
|
1858
|
+
export const computeCorestoreDatabaseBackupRestoreRequestSha256 = async (
|
|
1859
|
+
valueArg: unknown,
|
|
1860
|
+
): Promise<string> => createCanonicalJsonSha256Hex(
|
|
1861
|
+
createCorestoreDatabaseBackupCanonicalJson(
|
|
1862
|
+
normalizeCorestoreDatabaseBackupRestoreRequest(valueArg),
|
|
1863
|
+
'databaseBackupRestoreRequest',
|
|
1864
|
+
),
|
|
1865
|
+
failCorestoreDatabaseBackup,
|
|
1866
|
+
);
|
|
1867
|
+
|
|
1868
|
+
export const computeCorestoreDatabaseBackupRestoreResponseSha256 = async (
|
|
1869
|
+
valueArg: unknown,
|
|
1870
|
+
): Promise<string> => createCanonicalJsonSha256Hex(
|
|
1871
|
+
createCorestoreDatabaseBackupCanonicalJson(
|
|
1872
|
+
normalizeCorestoreDatabaseBackupRestoreResponse(valueArg),
|
|
1873
|
+
'databaseBackupRestoreResponse',
|
|
1874
|
+
),
|
|
1875
|
+
failCorestoreDatabaseBackup,
|
|
1876
|
+
);
|