@serve.zone/interfaces 32.0.2 → 32.2.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 +37 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/noderetirement.d.ts +39 -0
- package/dist_ts/data/noderetirement.js +31 -0
- package/dist_ts/data/serviceruntime.d.ts +10 -2
- package/dist_ts/data/serviceruntime.js +4 -3
- package/dist_ts/platform/storage.d.ts +29 -2
- package/dist_ts/platform/storage.golden.d.ts +31 -0
- package/dist_ts/platform/storage.golden.js +84 -0
- package/dist_ts/platform/storage.js +37 -1
- package/dist_ts/platform/storagemigration.js +11 -287
- package/dist_ts/private/storagecontract.d.ts +40 -0
- package/dist_ts/private/storagecontract.js +399 -0
- package/dist_ts/requests/node.d.ts +25 -0
- package/package.json +1 -1
- package/readme.md +40 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/index.ts +1 -0
- package/ts/data/noderetirement.ts +63 -0
- package/ts/data/serviceruntime.ts +12 -3
- package/ts/platform/storage.golden.ts +97 -0
- package/ts/platform/storage.ts +59 -2
- package/ts/platform/storagemigration.ts +31 -440
- package/ts/private/storagecontract.ts +623 -0
- package/ts/requests/node.ts +29 -0
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
deepFreezeValue,
|
|
9
9
|
strictCanonicalJsonRules,
|
|
10
10
|
} from '../private/canonicaljson.js';
|
|
11
|
+
import {
|
|
12
|
+
createStorageContractReaders,
|
|
13
|
+
storageContractStringLimits,
|
|
14
|
+
} from '../private/storagecontract.js';
|
|
11
15
|
import type {
|
|
12
16
|
IResolvedObjectStorageBinding,
|
|
13
17
|
IResolvedStoragePolicyRef,
|
|
@@ -16,11 +20,13 @@ import type {
|
|
|
16
20
|
|
|
17
21
|
export const storageMigrationContractLimits = Object.freeze({
|
|
18
22
|
maximumControlJsonBytes: 256 * 1024,
|
|
19
|
-
maximumConfiguredStringBytes:
|
|
23
|
+
maximumConfiguredStringBytes:
|
|
24
|
+
storageContractStringLimits.maximumConfiguredStringBytes,
|
|
20
25
|
maximumIssueMessageBytes: 4096,
|
|
21
26
|
maximumAbortReasonBytes: 4096,
|
|
22
|
-
maximumEnvironmentKeyBytes:
|
|
23
|
-
|
|
27
|
+
maximumEnvironmentKeyBytes:
|
|
28
|
+
storageContractStringLimits.maximumEnvironmentKeyBytes,
|
|
29
|
+
maximumSecretPathBytes: storageContractStringLimits.maximumSecretPathBytes,
|
|
24
30
|
});
|
|
25
31
|
|
|
26
32
|
export const storageMigrationCanonicalJsonRules = strictCanonicalJsonRules;
|
|
@@ -408,9 +414,28 @@ const fail = (reasonArg: string): never => {
|
|
|
408
414
|
throw new StorageMigrationContractError(reasonArg);
|
|
409
415
|
};
|
|
410
416
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
/**
|
|
418
|
+
* The storage contract's readers, reporting every refusal as a migration contract error. The
|
|
419
|
+
* request a migration targets is the same request a control plane reconciles, so it is judged by
|
|
420
|
+
* the same rules and, through them, digests to the same bytes.
|
|
421
|
+
*/
|
|
422
|
+
const {
|
|
423
|
+
utf8ByteLength,
|
|
424
|
+
readRecord,
|
|
425
|
+
readDenseArray,
|
|
426
|
+
assertKeys,
|
|
427
|
+
assertExactKeys,
|
|
428
|
+
readString,
|
|
429
|
+
readIdentifier,
|
|
430
|
+
readSha256,
|
|
431
|
+
readSafeInteger,
|
|
432
|
+
readBoolean,
|
|
433
|
+
readLiteral,
|
|
434
|
+
readCapacityQuantity,
|
|
435
|
+
capacityQuantityBytes,
|
|
436
|
+
normalizeDelivery,
|
|
437
|
+
normalizeObjectStorageRequest,
|
|
438
|
+
} = createStorageContractReaders(fail);
|
|
414
439
|
|
|
415
440
|
/**
|
|
416
441
|
* Enforce this against Content-Length and the running request byte count
|
|
@@ -427,200 +452,6 @@ export const assertStorageMigrationControlIngressByteLength = (
|
|
|
427
452
|
);
|
|
428
453
|
};
|
|
429
454
|
|
|
430
|
-
const readRecord = (
|
|
431
|
-
valueArg: unknown,
|
|
432
|
-
fieldNameArg: string,
|
|
433
|
-
): Record<string, unknown> => {
|
|
434
|
-
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
|
|
435
|
-
return fail(`${fieldNameArg} must be a plain object`);
|
|
436
|
-
}
|
|
437
|
-
const prototype = Object.getPrototypeOf(valueArg);
|
|
438
|
-
if (prototype !== Object.prototype && prototype !== null) {
|
|
439
|
-
return fail(`${fieldNameArg} must be a plain object`);
|
|
440
|
-
}
|
|
441
|
-
const record = valueArg as Record<string, unknown>;
|
|
442
|
-
const ownKeys = Reflect.ownKeys(record);
|
|
443
|
-
if (ownKeys.some((keyArg) => typeof keyArg !== 'string')) {
|
|
444
|
-
return fail(`${fieldNameArg} must not contain symbol keys`);
|
|
445
|
-
}
|
|
446
|
-
for (const key of ownKeys as string[]) {
|
|
447
|
-
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
448
|
-
if (
|
|
449
|
-
!descriptor ||
|
|
450
|
-
!descriptor.enumerable ||
|
|
451
|
-
!Object.hasOwn(descriptor, 'value') ||
|
|
452
|
-
descriptor.value === undefined
|
|
453
|
-
) {
|
|
454
|
-
return fail(
|
|
455
|
-
`${fieldNameArg} must contain enumerable JSON data properties only`,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return record;
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const readDenseArray = (
|
|
463
|
-
valueArg: unknown,
|
|
464
|
-
fieldNameArg: string,
|
|
465
|
-
minimumLengthArg: number,
|
|
466
|
-
maximumLengthArg: number,
|
|
467
|
-
): unknown[] => {
|
|
468
|
-
if (
|
|
469
|
-
!Array.isArray(valueArg) ||
|
|
470
|
-
Object.getPrototypeOf(valueArg) !== Array.prototype
|
|
471
|
-
) {
|
|
472
|
-
return fail(`${fieldNameArg} must be a dense plain array`);
|
|
473
|
-
}
|
|
474
|
-
if (
|
|
475
|
-
valueArg.length < minimumLengthArg ||
|
|
476
|
-
valueArg.length > maximumLengthArg
|
|
477
|
-
) {
|
|
478
|
-
return fail(
|
|
479
|
-
`${fieldNameArg} must contain between ${minimumLengthArg} and ${maximumLengthArg} entries`,
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
for (let index = 0; index < valueArg.length; index++) {
|
|
483
|
-
if (!Object.hasOwn(valueArg, index)) {
|
|
484
|
-
return fail(`${fieldNameArg} must not contain sparse entries`);
|
|
485
|
-
}
|
|
486
|
-
const descriptor = Object.getOwnPropertyDescriptor(valueArg, String(index));
|
|
487
|
-
if (
|
|
488
|
-
!descriptor ||
|
|
489
|
-
!descriptor.enumerable ||
|
|
490
|
-
!Object.hasOwn(descriptor, 'value') ||
|
|
491
|
-
descriptor.value === undefined
|
|
492
|
-
) {
|
|
493
|
-
return fail(
|
|
494
|
-
`${fieldNameArg} must contain enumerable JSON data entries only`,
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
for (const key of Reflect.ownKeys(valueArg)) {
|
|
499
|
-
if (key === 'length') continue;
|
|
500
|
-
if (
|
|
501
|
-
typeof key !== 'string' ||
|
|
502
|
-
!/^(?:0|[1-9][0-9]*)$/.test(key) ||
|
|
503
|
-
Number(key) >= valueArg.length
|
|
504
|
-
) {
|
|
505
|
-
return fail(`${fieldNameArg} must not contain extra properties`);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return valueArg;
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
const assertKeys = (
|
|
512
|
-
recordArg: Record<string, unknown>,
|
|
513
|
-
requiredKeysArg: readonly string[],
|
|
514
|
-
optionalKeysArg: readonly string[],
|
|
515
|
-
fieldNameArg: string,
|
|
516
|
-
): void => {
|
|
517
|
-
const actualKeys = Object.keys(recordArg).sort();
|
|
518
|
-
const allowedKeys = new Set([...requiredKeysArg, ...optionalKeysArg]);
|
|
519
|
-
if (
|
|
520
|
-
requiredKeysArg.some((keyArg) => !Object.hasOwn(recordArg, keyArg)) ||
|
|
521
|
-
actualKeys.some((keyArg) => !allowedKeys.has(keyArg))
|
|
522
|
-
) {
|
|
523
|
-
fail(`${fieldNameArg} does not match its exact schema`);
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const assertExactKeys = (
|
|
528
|
-
recordArg: Record<string, unknown>,
|
|
529
|
-
expectedKeysArg: readonly string[],
|
|
530
|
-
fieldNameArg: string,
|
|
531
|
-
): void => {
|
|
532
|
-
assertKeys(recordArg, expectedKeysArg, [], fieldNameArg);
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
const readString = (
|
|
536
|
-
valueArg: unknown,
|
|
537
|
-
fieldNameArg: string,
|
|
538
|
-
maximumBytesArg: number =
|
|
539
|
-
storageMigrationContractLimits.maximumConfiguredStringBytes,
|
|
540
|
-
): string => {
|
|
541
|
-
if (
|
|
542
|
-
typeof valueArg !== 'string' ||
|
|
543
|
-
valueArg.length === 0 ||
|
|
544
|
-
utf8ByteLength(valueArg) > maximumBytesArg ||
|
|
545
|
-
/[\u0000-\u001f\u007f]/u.test(valueArg)
|
|
546
|
-
) {
|
|
547
|
-
return fail(`${fieldNameArg} must be a bounded non-empty JSON string`);
|
|
548
|
-
}
|
|
549
|
-
for (let index = 0; index < valueArg.length; index++) {
|
|
550
|
-
const codeUnit = valueArg.charCodeAt(index);
|
|
551
|
-
if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
552
|
-
const following = valueArg.charCodeAt(index + 1);
|
|
553
|
-
if (following < 0xdc00 || following > 0xdfff) {
|
|
554
|
-
return fail(`${fieldNameArg} contains an unpaired UTF-16 surrogate`);
|
|
555
|
-
}
|
|
556
|
-
index++;
|
|
557
|
-
} else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
558
|
-
return fail(`${fieldNameArg} contains an unpaired UTF-16 surrogate`);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return valueArg;
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
const canonicalIdPattern = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,191}$/;
|
|
565
|
-
const environmentKeyPattern = /^[A-Z_][A-Z0-9_]{0,255}$/;
|
|
566
|
-
const sha256Pattern = /^[a-f0-9]{64}$/;
|
|
567
|
-
const capacityPattern = /^[1-9][0-9]*(?:KiB|MiB|GiB|TiB)$/;
|
|
568
|
-
|
|
569
|
-
const readIdentifier = (valueArg: unknown, fieldNameArg: string): string => {
|
|
570
|
-
if (typeof valueArg !== 'string' || !canonicalIdPattern.test(valueArg)) {
|
|
571
|
-
return fail(`${fieldNameArg} must be a canonical identifier`);
|
|
572
|
-
}
|
|
573
|
-
return valueArg;
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const readSha256 = (valueArg: unknown, fieldNameArg: string): string => {
|
|
577
|
-
if (typeof valueArg !== 'string' || !sha256Pattern.test(valueArg)) {
|
|
578
|
-
return fail(`${fieldNameArg} must be a bare lowercase 64-hex SHA-256 digest`);
|
|
579
|
-
}
|
|
580
|
-
return valueArg;
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
const readSafeInteger = (
|
|
584
|
-
valueArg: unknown,
|
|
585
|
-
fieldNameArg: string,
|
|
586
|
-
minimumArg: number,
|
|
587
|
-
maximumArg = Number.MAX_SAFE_INTEGER,
|
|
588
|
-
): number => {
|
|
589
|
-
if (
|
|
590
|
-
typeof valueArg !== 'number' ||
|
|
591
|
-
!Number.isSafeInteger(valueArg) ||
|
|
592
|
-
Object.is(valueArg, -0) ||
|
|
593
|
-
valueArg < minimumArg ||
|
|
594
|
-
valueArg > maximumArg
|
|
595
|
-
) {
|
|
596
|
-
return fail(
|
|
597
|
-
`${fieldNameArg} must be a safe integer between ${minimumArg} and ${maximumArg}`,
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
return valueArg;
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
const readBoolean = (valueArg: unknown, fieldNameArg: string): boolean => {
|
|
604
|
-
if (typeof valueArg !== 'boolean') {
|
|
605
|
-
return fail(`${fieldNameArg} must be a boolean`);
|
|
606
|
-
}
|
|
607
|
-
return valueArg;
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
const readLiteral = <TLiteral extends string>(
|
|
611
|
-
valueArg: unknown,
|
|
612
|
-
allowedArg: readonly TLiteral[],
|
|
613
|
-
fieldNameArg: string,
|
|
614
|
-
): TLiteral => {
|
|
615
|
-
if (
|
|
616
|
-
typeof valueArg !== 'string' ||
|
|
617
|
-
!allowedArg.includes(valueArg as TLiteral)
|
|
618
|
-
) {
|
|
619
|
-
return fail(`${fieldNameArg} is outside the supported values`);
|
|
620
|
-
}
|
|
621
|
-
return valueArg as TLiteral;
|
|
622
|
-
};
|
|
623
|
-
|
|
624
455
|
const canonicalize = (valueArg: unknown): string => {
|
|
625
456
|
return canonicalizeStrictJson(valueArg, fail);
|
|
626
457
|
};
|
|
@@ -777,50 +608,6 @@ const normalizeStorageClass = (
|
|
|
777
608
|
};
|
|
778
609
|
};
|
|
779
610
|
|
|
780
|
-
const readCapacityQuantity = (
|
|
781
|
-
valueArg: unknown,
|
|
782
|
-
fieldNameArg: string,
|
|
783
|
-
): string => {
|
|
784
|
-
if (typeof valueArg !== 'string' || !capacityPattern.test(valueArg)) {
|
|
785
|
-
return fail(`${fieldNameArg} must be a canonical base-2 capacity quantity`);
|
|
786
|
-
}
|
|
787
|
-
const numericPart = valueArg.replace(/(?:KiB|MiB|GiB|TiB)$/, '');
|
|
788
|
-
if (!Number.isSafeInteger(Number(numericPart))) {
|
|
789
|
-
return fail(`${fieldNameArg} exceeds the safe integer quantity range`);
|
|
790
|
-
}
|
|
791
|
-
return valueArg;
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
const capacityQuantityBytes = (valueArg: string): bigint => {
|
|
795
|
-
const units: Record<string, bigint> = {
|
|
796
|
-
KiB: 1024n,
|
|
797
|
-
MiB: 1024n ** 2n,
|
|
798
|
-
GiB: 1024n ** 3n,
|
|
799
|
-
TiB: 1024n ** 4n,
|
|
800
|
-
};
|
|
801
|
-
const unit = valueArg.slice(-3);
|
|
802
|
-
return BigInt(valueArg.slice(0, -3)) * units[unit];
|
|
803
|
-
};
|
|
804
|
-
|
|
805
|
-
const normalizeCapacityRequest = (
|
|
806
|
-
valueArg: unknown,
|
|
807
|
-
fieldNameArg: string,
|
|
808
|
-
): NonNullable<IAppStoreObjectStorageRequest['capacity']> => {
|
|
809
|
-
const value = readRecord(valueArg, fieldNameArg);
|
|
810
|
-
assertKeys(value, ['request'], ['limit'], fieldNameArg);
|
|
811
|
-
const request = readCapacityQuantity(value.request, `${fieldNameArg}.request`);
|
|
812
|
-
const limit = Object.hasOwn(value, 'limit')
|
|
813
|
-
? readCapacityQuantity(value.limit, `${fieldNameArg}.limit`)
|
|
814
|
-
: undefined;
|
|
815
|
-
if (limit && capacityQuantityBytes(limit) < capacityQuantityBytes(request)) {
|
|
816
|
-
return fail(`${fieldNameArg}.limit must be at least request`);
|
|
817
|
-
}
|
|
818
|
-
return {
|
|
819
|
-
request,
|
|
820
|
-
...(limit ? { limit } : {}),
|
|
821
|
-
};
|
|
822
|
-
};
|
|
823
|
-
|
|
824
611
|
const normalizeResolvedCapacity = (
|
|
825
612
|
valueArg: unknown,
|
|
826
613
|
fieldNameArg: string,
|
|
@@ -867,102 +654,6 @@ const normalizeResolvedCapacity = (
|
|
|
867
654
|
};
|
|
868
655
|
};
|
|
869
656
|
|
|
870
|
-
const readEnvironmentKey = (
|
|
871
|
-
valueArg: unknown,
|
|
872
|
-
fieldNameArg: string,
|
|
873
|
-
): string => {
|
|
874
|
-
if (
|
|
875
|
-
typeof valueArg !== 'string' ||
|
|
876
|
-
utf8ByteLength(valueArg) >
|
|
877
|
-
storageMigrationContractLimits.maximumEnvironmentKeyBytes ||
|
|
878
|
-
!environmentKeyPattern.test(valueArg)
|
|
879
|
-
) {
|
|
880
|
-
return fail(`${fieldNameArg} must be a canonical environment key`);
|
|
881
|
-
}
|
|
882
|
-
return valueArg;
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
const normalizeDelivery = (
|
|
886
|
-
valueArg: unknown,
|
|
887
|
-
fieldNameArg: string,
|
|
888
|
-
): IResolvedObjectStorageBinding['delivery'] => {
|
|
889
|
-
const value = readRecord(valueArg, fieldNameArg);
|
|
890
|
-
if (value.type === 'launcher-environment') {
|
|
891
|
-
assertExactKeys(value, ['type', 'keys'], fieldNameArg);
|
|
892
|
-
const keys = readRecord(value.keys, `${fieldNameArg}.keys`);
|
|
893
|
-
assertKeys(
|
|
894
|
-
keys,
|
|
895
|
-
['endpoint', 'bucket', 'region', 'accessKeyId', 'secretAccessKey'],
|
|
896
|
-
['sessionToken'],
|
|
897
|
-
`${fieldNameArg}.keys`,
|
|
898
|
-
);
|
|
899
|
-
const normalizedKeys = {
|
|
900
|
-
endpoint: readEnvironmentKey(
|
|
901
|
-
keys.endpoint,
|
|
902
|
-
`${fieldNameArg}.keys.endpoint`,
|
|
903
|
-
),
|
|
904
|
-
bucket: readEnvironmentKey(keys.bucket, `${fieldNameArg}.keys.bucket`),
|
|
905
|
-
region: readEnvironmentKey(keys.region, `${fieldNameArg}.keys.region`),
|
|
906
|
-
accessKeyId: readEnvironmentKey(
|
|
907
|
-
keys.accessKeyId,
|
|
908
|
-
`${fieldNameArg}.keys.accessKeyId`,
|
|
909
|
-
),
|
|
910
|
-
secretAccessKey: readEnvironmentKey(
|
|
911
|
-
keys.secretAccessKey,
|
|
912
|
-
`${fieldNameArg}.keys.secretAccessKey`,
|
|
913
|
-
),
|
|
914
|
-
...(Object.hasOwn(keys, 'sessionToken')
|
|
915
|
-
? {
|
|
916
|
-
sessionToken: readEnvironmentKey(
|
|
917
|
-
keys.sessionToken,
|
|
918
|
-
`${fieldNameArg}.keys.sessionToken`,
|
|
919
|
-
),
|
|
920
|
-
}
|
|
921
|
-
: {}),
|
|
922
|
-
};
|
|
923
|
-
if (
|
|
924
|
-
new Set(Object.values(normalizedKeys)).size !==
|
|
925
|
-
Object.keys(normalizedKeys).length
|
|
926
|
-
) {
|
|
927
|
-
return fail(`${fieldNameArg}.keys values must be distinct`);
|
|
928
|
-
}
|
|
929
|
-
return { type: 'launcher-environment', keys: normalizedKeys };
|
|
930
|
-
}
|
|
931
|
-
if (value.type === 'file') {
|
|
932
|
-
assertExactKeys(value, ['type', 'targetPath', 'format', 'uid', 'gid', 'mode'], fieldNameArg);
|
|
933
|
-
const targetPath = readString(
|
|
934
|
-
value.targetPath,
|
|
935
|
-
`${fieldNameArg}.targetPath`,
|
|
936
|
-
storageMigrationContractLimits.maximumSecretPathBytes,
|
|
937
|
-
);
|
|
938
|
-
if (
|
|
939
|
-
!targetPath.startsWith('/run/secrets/') ||
|
|
940
|
-
targetPath.includes('\\') ||
|
|
941
|
-
targetPath.split('/').some((segmentArg) =>
|
|
942
|
-
segmentArg === '.' || segmentArg === '..'
|
|
943
|
-
)
|
|
944
|
-
) {
|
|
945
|
-
return fail(
|
|
946
|
-
`${fieldNameArg}.targetPath must be a canonical path below /run/secrets`,
|
|
947
|
-
);
|
|
948
|
-
}
|
|
949
|
-
if (value.format !== 'servezone-object-storage') {
|
|
950
|
-
return fail(`${fieldNameArg}.format is unsupported`);
|
|
951
|
-
}
|
|
952
|
-
return {
|
|
953
|
-
type: 'file',
|
|
954
|
-
targetPath,
|
|
955
|
-
format: 'servezone-object-storage',
|
|
956
|
-
uid: readSafeInteger(value.uid, `${fieldNameArg}.uid`, 0, 2_147_483_647),
|
|
957
|
-
gid: readSafeInteger(value.gid, `${fieldNameArg}.gid`, 0, 2_147_483_647),
|
|
958
|
-
mode: value.mode === 0o400 || value.mode === 0o440
|
|
959
|
-
? value.mode
|
|
960
|
-
: fail(`${fieldNameArg}.mode must be 0400 or 0440`),
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
return fail(`${fieldNameArg}.type is unsupported`);
|
|
964
|
-
};
|
|
965
|
-
|
|
966
657
|
const normalizeCredentialManagementScope = (
|
|
967
658
|
valueArg: unknown,
|
|
968
659
|
fieldNameArg: string,
|
|
@@ -974,106 +665,6 @@ const normalizeCredentialManagementScope = (
|
|
|
974
665
|
return value as IResolvedObjectStorageBinding['credentialManagementScope'];
|
|
975
666
|
};
|
|
976
667
|
|
|
977
|
-
const normalizeObjectStorageProtection = (
|
|
978
|
-
valueArg: unknown,
|
|
979
|
-
fieldNameArg: string,
|
|
980
|
-
): NonNullable<IAppStoreObjectStorageRequest['protection']> => {
|
|
981
|
-
const value = readRecord(valueArg, fieldNameArg);
|
|
982
|
-
assertKeys(
|
|
983
|
-
value,
|
|
984
|
-
[],
|
|
985
|
-
['backup', 'versioning', 'retentionDays'],
|
|
986
|
-
fieldNameArg,
|
|
987
|
-
);
|
|
988
|
-
if (Object.keys(value).length === 0) {
|
|
989
|
-
return fail(`${fieldNameArg} must not be empty`);
|
|
990
|
-
}
|
|
991
|
-
return {
|
|
992
|
-
...(Object.hasOwn(value, 'backup')
|
|
993
|
-
? {
|
|
994
|
-
backup: value.backup === 'required'
|
|
995
|
-
? 'required' as const
|
|
996
|
-
: fail(`${fieldNameArg}.backup must be required`),
|
|
997
|
-
}
|
|
998
|
-
: {}),
|
|
999
|
-
...(Object.hasOwn(value, 'versioning')
|
|
1000
|
-
? {
|
|
1001
|
-
versioning: value.versioning === 'required'
|
|
1002
|
-
? 'required' as const
|
|
1003
|
-
: fail(`${fieldNameArg}.versioning must be required`),
|
|
1004
|
-
}
|
|
1005
|
-
: {}),
|
|
1006
|
-
...(Object.hasOwn(value, 'retentionDays')
|
|
1007
|
-
? {
|
|
1008
|
-
retentionDays: readSafeInteger(
|
|
1009
|
-
value.retentionDays,
|
|
1010
|
-
`${fieldNameArg}.retentionDays`,
|
|
1011
|
-
1,
|
|
1012
|
-
365_000,
|
|
1013
|
-
),
|
|
1014
|
-
}
|
|
1015
|
-
: {}),
|
|
1016
|
-
};
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
const normalizeObjectStorageRequest = (
|
|
1020
|
-
valueArg: unknown,
|
|
1021
|
-
fieldNameArg: string,
|
|
1022
|
-
): IAppStoreObjectStorageRequest => {
|
|
1023
|
-
const value = readRecord(valueArg, fieldNameArg);
|
|
1024
|
-
assertKeys(
|
|
1025
|
-
value,
|
|
1026
|
-
[
|
|
1027
|
-
'id',
|
|
1028
|
-
'kind',
|
|
1029
|
-
'storageClass',
|
|
1030
|
-
'accessMode',
|
|
1031
|
-
'reclaimPolicy',
|
|
1032
|
-
'delivery',
|
|
1033
|
-
],
|
|
1034
|
-
['capacity', 'protection'],
|
|
1035
|
-
fieldNameArg,
|
|
1036
|
-
);
|
|
1037
|
-
if (value.kind !== 'objectStorage') {
|
|
1038
|
-
return fail(`${fieldNameArg}.kind must be objectStorage`);
|
|
1039
|
-
}
|
|
1040
|
-
return {
|
|
1041
|
-
id: readIdentifier(value.id, `${fieldNameArg}.id`),
|
|
1042
|
-
kind: 'objectStorage',
|
|
1043
|
-
storageClass: readIdentifier(
|
|
1044
|
-
value.storageClass,
|
|
1045
|
-
`${fieldNameArg}.storageClass`,
|
|
1046
|
-
),
|
|
1047
|
-
accessMode: readLiteral(
|
|
1048
|
-
value.accessMode,
|
|
1049
|
-
['readOnly', 'readWrite'] as const,
|
|
1050
|
-
`${fieldNameArg}.accessMode`,
|
|
1051
|
-
),
|
|
1052
|
-
reclaimPolicy: readLiteral(
|
|
1053
|
-
value.reclaimPolicy,
|
|
1054
|
-
['retain', 'delete'] as const,
|
|
1055
|
-
`${fieldNameArg}.reclaimPolicy`,
|
|
1056
|
-
),
|
|
1057
|
-
delivery: normalizeDelivery(value.delivery, `${fieldNameArg}.delivery`),
|
|
1058
|
-
...(Object.hasOwn(value, 'capacity')
|
|
1059
|
-
? {
|
|
1060
|
-
capacity: normalizeCapacityRequest(
|
|
1061
|
-
value.capacity,
|
|
1062
|
-
`${fieldNameArg}.capacity`,
|
|
1063
|
-
),
|
|
1064
|
-
}
|
|
1065
|
-
: {}),
|
|
1066
|
-
...(Object.hasOwn(value, 'protection')
|
|
1067
|
-
? {
|
|
1068
|
-
protection: normalizeObjectStorageProtection(
|
|
1069
|
-
value.protection,
|
|
1070
|
-
`${fieldNameArg}.protection`,
|
|
1071
|
-
),
|
|
1072
|
-
}
|
|
1073
|
-
: {}),
|
|
1074
|
-
};
|
|
1075
|
-
};
|
|
1076
|
-
|
|
1077
668
|
const normalizeCapabilities = (
|
|
1078
669
|
valueArg: unknown,
|
|
1079
670
|
fieldNameArg: string,
|