@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.
@@ -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: 12 * 1024,
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 isCorestoreControlToken = (valueArg: unknown): valueArg is string => {
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
- return roundTrip === valueArg
176
- && encoded.byteLength >= corestoreCredentialRuntimeLimits.minimumControlTokenBytes
177
- && encoded.byteLength <= corestoreCredentialRuntimeLimits.maximumControlTokenBytes;
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
+ );