@serve.zone/interfaces 23.2.0 → 24.0.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 +9 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/appstore/types.d.ts +2 -2
- package/dist_ts/appstore/types.js +1 -1
- package/dist_ts/data/clusternode.d.ts +85 -0
- package/dist_ts/data/clusternode.js +355 -1
- package/dist_ts/data/secret.d.ts +51 -4
- package/dist_ts/data/secret.js +326 -10
- package/dist_ts/requests/secret.d.ts +33 -6
- package/dist_ts/requests/secret.js +198 -1
- package/dist_ts/runtime.d.ts +52 -29
- package/dist_ts/runtime.js +239 -94
- package/dist_ts/runtime.workloadinit.d.ts +129 -0
- package/dist_ts/runtime.workloadinit.js +405 -0
- package/package.json +1 -1
- package/readme.md +45 -17
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/appstore/types.ts +2 -2
- package/ts/data/clusternode.ts +513 -0
- package/ts/data/secret.ts +402 -16
- package/ts/requests/secret.ts +248 -6
- package/ts/runtime.ts +366 -141
- package/ts/runtime.workloadinit.ts +582 -0
package/ts/requests/secret.ts
CHANGED
|
@@ -2,13 +2,15 @@ import type * as plugins from '../plugins.js';
|
|
|
2
2
|
import * as secretData from '../data/secret.js';
|
|
3
3
|
import type {
|
|
4
4
|
IResolvedSecretManifestEntry,
|
|
5
|
+
IActiveSecretRecipientMetadata,
|
|
5
6
|
ISecretMetadata,
|
|
6
7
|
ISecretSetAttachment,
|
|
7
8
|
ISecretSetConsumerRolloutStatus,
|
|
8
9
|
ISecretSetMetadata,
|
|
9
10
|
ISecretVersionMetadata,
|
|
11
|
+
ISecretVersionPurgeIntentV1,
|
|
10
12
|
ISecretVersionRetentionReference,
|
|
11
|
-
|
|
13
|
+
TSecretVersionPurgeOperationV1,
|
|
12
14
|
TSecretMutationTarget,
|
|
13
15
|
TSecretValueInput,
|
|
14
16
|
TSecretDelivery,
|
|
@@ -26,7 +28,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
26
28
|
identity: IIdentityCredential;
|
|
27
29
|
};
|
|
28
30
|
response: {
|
|
29
|
-
recipient:
|
|
31
|
+
recipient: IActiveSecretRecipientMetadata;
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -120,7 +122,7 @@ export interface IReq_ChangeSecretLifecycle extends plugins.typedrequestInterfac
|
|
|
120
122
|
request: {
|
|
121
123
|
identity: IIdentityCredential;
|
|
122
124
|
secretId: string;
|
|
123
|
-
action: 'retire' | 'revoke' | 'delete'
|
|
125
|
+
action: 'retire' | 'revoke' | 'delete';
|
|
124
126
|
expectedSecretRevision: number;
|
|
125
127
|
expectedTargetSecretsRevision: number;
|
|
126
128
|
};
|
|
@@ -140,10 +142,44 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
140
142
|
identity: IIdentityCredential;
|
|
141
143
|
secretId: string;
|
|
142
144
|
secretVersionId: string;
|
|
145
|
+
cursor?: string;
|
|
146
|
+
limit?: number;
|
|
143
147
|
};
|
|
144
148
|
response: {
|
|
145
|
-
|
|
149
|
+
schemaVersion: 1;
|
|
150
|
+
secret: ISecretMetadata;
|
|
151
|
+
version: ISecretVersionMetadata;
|
|
152
|
+
targetSecretsRevision: number;
|
|
153
|
+
eligible: boolean;
|
|
154
|
+
blockers: TSecretVersionPurgeBlocker[];
|
|
155
|
+
blockingReferenceCount: number;
|
|
146
156
|
references: ISecretVersionRetentionReference[];
|
|
157
|
+
nextCursor?: string;
|
|
158
|
+
referencesTruncated: boolean;
|
|
159
|
+
operation?: TSecretVersionPurgeOperationV1;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type TSecretVersionPurgeBlocker =
|
|
164
|
+
| 'version-active'
|
|
165
|
+
| 'retention-reference'
|
|
166
|
+
| 'purge-pending'
|
|
167
|
+
| 'already-purged';
|
|
168
|
+
|
|
169
|
+
export interface IReq_PurgeSecretVersion
|
|
170
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
171
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
172
|
+
IReq_PurgeSecretVersion
|
|
173
|
+
> {
|
|
174
|
+
method: 'purgeSecretVersion';
|
|
175
|
+
request: ISecretVersionPurgeIntentV1 & {
|
|
176
|
+
identity: IIdentityCredential;
|
|
177
|
+
};
|
|
178
|
+
response: {
|
|
179
|
+
secret: ISecretMetadata;
|
|
180
|
+
version: ISecretVersionMetadata;
|
|
181
|
+
operation: TSecretVersionPurgeOperationV1;
|
|
182
|
+
targetSecretsRevision: number;
|
|
147
183
|
};
|
|
148
184
|
}
|
|
149
185
|
|
|
@@ -281,7 +317,7 @@ const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
|
|
|
281
317
|
|
|
282
318
|
export const validateCreateSecretRequest = async (
|
|
283
319
|
requestArg: unknown,
|
|
284
|
-
activeIngressRecipientArg:
|
|
320
|
+
activeIngressRecipientArg: IActiveSecretRecipientMetadata,
|
|
285
321
|
): Promise<string[]> => {
|
|
286
322
|
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
287
323
|
return ['create secret request must be an object'];
|
|
@@ -388,7 +424,7 @@ export const validateCreateSecretRequest = async (
|
|
|
388
424
|
|
|
389
425
|
export const validateRotateSecretRequest = async (
|
|
390
426
|
requestArg: unknown,
|
|
391
|
-
activeIngressRecipientArg:
|
|
427
|
+
activeIngressRecipientArg: IActiveSecretRecipientMetadata,
|
|
392
428
|
): Promise<string[]> => {
|
|
393
429
|
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
394
430
|
return ['rotate secret request must be an object'];
|
|
@@ -449,3 +485,209 @@ export const validateRotateSecretRequest = async (
|
|
|
449
485
|
));
|
|
450
486
|
return errors;
|
|
451
487
|
};
|
|
488
|
+
|
|
489
|
+
export const validatePurgeSecretVersionRequest = (
|
|
490
|
+
requestArg: unknown,
|
|
491
|
+
): string[] => {
|
|
492
|
+
try {
|
|
493
|
+
if (!isRecord(requestArg)) return ['purge secret version request must be an object'];
|
|
494
|
+
const request = requestArg as Record<string, unknown>;
|
|
495
|
+
const intent = Object.fromEntries(
|
|
496
|
+
Object.entries(request).filter(([keyArg]) => keyArg !== 'identity'),
|
|
497
|
+
);
|
|
498
|
+
const errors = secretData.validateSecretVersionPurgeIntent(intent);
|
|
499
|
+
if (!request.identity || typeof request.identity !== 'object' || Array.isArray(request.identity)
|
|
500
|
+
|| JSON.stringify(Object.keys(request.identity).sort()) !== JSON.stringify(['jwt'])
|
|
501
|
+
|| typeof (request.identity as Record<string, unknown>).jwt !== 'string'
|
|
502
|
+
|| !(request.identity as Record<string, unknown>).jwt) {
|
|
503
|
+
errors.push('purge secret version request requires only a JWT identity');
|
|
504
|
+
}
|
|
505
|
+
return errors;
|
|
506
|
+
} catch {
|
|
507
|
+
return ['purge secret version request must be safely inspectable'];
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
export const validateGetSecretVersionPurgePreflightRequest = (
|
|
512
|
+
requestArg: unknown,
|
|
513
|
+
): string[] => {
|
|
514
|
+
try {
|
|
515
|
+
if (!isRecord(requestArg)) {
|
|
516
|
+
return ['secret version purge preflight request must be an object'];
|
|
517
|
+
}
|
|
518
|
+
const expectedKeys = [
|
|
519
|
+
'identity',
|
|
520
|
+
'secretId',
|
|
521
|
+
'secretVersionId',
|
|
522
|
+
...(requestArg.cursor === undefined ? [] : ['cursor']),
|
|
523
|
+
...(requestArg.limit === undefined ? [] : ['limit']),
|
|
524
|
+
];
|
|
525
|
+
const errors: string[] = [];
|
|
526
|
+
if (JSON.stringify(Object.keys(requestArg).sort())
|
|
527
|
+
!== JSON.stringify(expectedKeys.sort())) {
|
|
528
|
+
errors.push('secret version purge preflight request must use its exact schema');
|
|
529
|
+
}
|
|
530
|
+
if (!isRecord(requestArg.identity)
|
|
531
|
+
|| JSON.stringify(Object.keys(requestArg.identity).sort()) !== JSON.stringify(['jwt'])
|
|
532
|
+
|| typeof requestArg.identity.jwt !== 'string'
|
|
533
|
+
|| !requestArg.identity.jwt) {
|
|
534
|
+
errors.push('secret version purge preflight request requires only a JWT identity');
|
|
535
|
+
}
|
|
536
|
+
for (const field of ['secretId', 'secretVersionId'] as const) {
|
|
537
|
+
if (typeof requestArg[field] !== 'string'
|
|
538
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(requestArg[field])) {
|
|
539
|
+
errors.push(`secret version purge preflight ${field} must be canonical`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (requestArg.cursor !== undefined
|
|
543
|
+
&& (typeof requestArg.cursor !== 'string' || requestArg.cursor.length === 0)) {
|
|
544
|
+
errors.push('secret version purge preflight cursor must be opaque and non-empty');
|
|
545
|
+
}
|
|
546
|
+
if (requestArg.limit !== undefined
|
|
547
|
+
&& (!Number.isSafeInteger(requestArg.limit)
|
|
548
|
+
|| (requestArg.limit as number) < 1
|
|
549
|
+
|| (requestArg.limit as number)
|
|
550
|
+
> secretData.secretVersionRetentionReferenceLimits.pageSizeMaximum)) {
|
|
551
|
+
errors.push('secret version purge preflight limit must be bounded');
|
|
552
|
+
}
|
|
553
|
+
return errors;
|
|
554
|
+
} catch {
|
|
555
|
+
return ['secret version purge preflight request must be safely inspectable'];
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
export const validateSecretVersionPurgePreflightResponse = async (
|
|
560
|
+
responseArg: unknown,
|
|
561
|
+
): Promise<string[]> => {
|
|
562
|
+
try {
|
|
563
|
+
if (!isRecord(responseArg)) {
|
|
564
|
+
return ['secret version purge preflight response must be an object'];
|
|
565
|
+
}
|
|
566
|
+
const response = responseArg as Record<string, unknown>;
|
|
567
|
+
const expectedKeys = [
|
|
568
|
+
'schemaVersion',
|
|
569
|
+
'secret',
|
|
570
|
+
'version',
|
|
571
|
+
'targetSecretsRevision',
|
|
572
|
+
'eligible',
|
|
573
|
+
'blockers',
|
|
574
|
+
'blockingReferenceCount',
|
|
575
|
+
'references',
|
|
576
|
+
'referencesTruncated',
|
|
577
|
+
...(response.nextCursor === undefined ? [] : ['nextCursor']),
|
|
578
|
+
...(response.operation === undefined ? [] : ['operation']),
|
|
579
|
+
];
|
|
580
|
+
const errors: string[] = [];
|
|
581
|
+
if (JSON.stringify(Object.keys(response).sort())
|
|
582
|
+
!== JSON.stringify([...expectedKeys].sort())) {
|
|
583
|
+
errors.push('secret version purge preflight response must use its exact schema');
|
|
584
|
+
}
|
|
585
|
+
if (response.schemaVersion !== 1) {
|
|
586
|
+
errors.push('secret version purge preflight response schemaVersion must be 1');
|
|
587
|
+
}
|
|
588
|
+
if (secretData.validateSecretVersionMetadata(response.version).length > 0) {
|
|
589
|
+
errors.push('secret version purge preflight version must be canonical');
|
|
590
|
+
}
|
|
591
|
+
if (secretData.validateSecretMetadata(response.secret).length > 0) {
|
|
592
|
+
errors.push('secret version purge preflight secret must be canonical');
|
|
593
|
+
}
|
|
594
|
+
if (isRecord(response.secret)
|
|
595
|
+
&& isRecord(response.version)
|
|
596
|
+
&& response.version.secretId !== response.secret.id) {
|
|
597
|
+
errors.push('secret version purge preflight version must belong to its secret');
|
|
598
|
+
}
|
|
599
|
+
if (!Number.isSafeInteger(response.targetSecretsRevision)
|
|
600
|
+
|| (response.targetSecretsRevision as number) < 0) {
|
|
601
|
+
errors.push('secret version purge preflight target revision must be non-negative');
|
|
602
|
+
}
|
|
603
|
+
const allowedBlockers = new Set<TSecretVersionPurgeBlocker>([
|
|
604
|
+
'version-active',
|
|
605
|
+
'retention-reference',
|
|
606
|
+
'purge-pending',
|
|
607
|
+
'already-purged',
|
|
608
|
+
]);
|
|
609
|
+
if (!Array.isArray(response.blockers)
|
|
610
|
+
|| response.blockers.some((blockerArg) => !allowedBlockers.has(
|
|
611
|
+
blockerArg as TSecretVersionPurgeBlocker,
|
|
612
|
+
))
|
|
613
|
+
|| new Set(response.blockers).size !== response.blockers.length
|
|
614
|
+
|| JSON.stringify(response.blockers) !== JSON.stringify([...response.blockers].sort())) {
|
|
615
|
+
errors.push('secret version purge preflight blockers must be uniquely sorted');
|
|
616
|
+
}
|
|
617
|
+
if (typeof response.eligible !== 'boolean'
|
|
618
|
+
|| response.eligible !== (Array.isArray(response.blockers) && response.blockers.length === 0)) {
|
|
619
|
+
errors.push('secret version purge preflight eligibility must be derived from blockers');
|
|
620
|
+
}
|
|
621
|
+
if (!Number.isSafeInteger(response.blockingReferenceCount)
|
|
622
|
+
|| (response.blockingReferenceCount as number) < 0) {
|
|
623
|
+
errors.push('secret version purge preflight reference count must be non-negative');
|
|
624
|
+
}
|
|
625
|
+
errors.push(...secretData.validateSecretVersionRetentionReferences(response.references));
|
|
626
|
+
if (typeof response.referencesTruncated !== 'boolean') {
|
|
627
|
+
errors.push('secret version purge preflight referencesTruncated must be boolean');
|
|
628
|
+
}
|
|
629
|
+
if (response.nextCursor !== undefined
|
|
630
|
+
&& (typeof response.nextCursor !== 'string' || response.nextCursor.length === 0)) {
|
|
631
|
+
errors.push('secret version purge preflight nextCursor must be opaque and non-empty');
|
|
632
|
+
}
|
|
633
|
+
if (response.referencesTruncated !== (response.nextCursor !== undefined)) {
|
|
634
|
+
errors.push('secret version purge preflight truncation must match nextCursor presence');
|
|
635
|
+
}
|
|
636
|
+
if (Array.isArray(response.references)
|
|
637
|
+
&& Number.isSafeInteger(response.blockingReferenceCount)
|
|
638
|
+
&& (response.blockingReferenceCount as number) < response.references.length) {
|
|
639
|
+
errors.push('secret version purge preflight reference count cannot be smaller than its page');
|
|
640
|
+
}
|
|
641
|
+
if (Array.isArray(response.blockers)
|
|
642
|
+
&& isRecord(response.version)
|
|
643
|
+
&& Number.isSafeInteger(response.blockingReferenceCount)) {
|
|
644
|
+
const expectedBlockers: TSecretVersionPurgeBlocker[] = [];
|
|
645
|
+
if (response.version.lifecycleState === 'active') expectedBlockers.push('version-active');
|
|
646
|
+
if ((response.blockingReferenceCount as number) > 0) {
|
|
647
|
+
expectedBlockers.push('retention-reference');
|
|
648
|
+
}
|
|
649
|
+
if (response.version.lifecycleState === 'purge-pending') {
|
|
650
|
+
expectedBlockers.push('purge-pending');
|
|
651
|
+
}
|
|
652
|
+
if (response.version.lifecycleState === 'purged') expectedBlockers.push('already-purged');
|
|
653
|
+
expectedBlockers.sort();
|
|
654
|
+
if (JSON.stringify(response.blockers) !== JSON.stringify(expectedBlockers)) {
|
|
655
|
+
errors.push('secret version purge preflight blockers do not match current state');
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (response.operation !== undefined) {
|
|
659
|
+
errors.push(...await secretData.validateSecretVersionPurgeOperation(response.operation));
|
|
660
|
+
if (isRecord(response.operation)
|
|
661
|
+
&& isRecord(response.operation.intent)
|
|
662
|
+
&& isRecord(response.secret)
|
|
663
|
+
&& isRecord(response.version)
|
|
664
|
+
&& (
|
|
665
|
+
response.operation.id !== response.version.purgeOperationId
|
|
666
|
+
|| response.operation.intent.secretId !== response.secret.id
|
|
667
|
+
|| response.operation.intent.secretVersionId !== response.version.id
|
|
668
|
+
)) {
|
|
669
|
+
errors.push('secret version purge preflight operation does not match its version');
|
|
670
|
+
}
|
|
671
|
+
if (isRecord(response.operation) && isRecord(response.version)) {
|
|
672
|
+
if (response.version.lifecycleState === 'purge-pending'
|
|
673
|
+
&& response.operation.state === 'succeeded') {
|
|
674
|
+
errors.push('purge-pending version cannot have a succeeded purge operation');
|
|
675
|
+
}
|
|
676
|
+
if (response.version.lifecycleState === 'purged'
|
|
677
|
+
&& (response.operation.state !== 'succeeded'
|
|
678
|
+
|| response.operation.purgedAt !== response.version.purgedAt)) {
|
|
679
|
+
errors.push('purged version requires the matching succeeded purge operation');
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (isRecord(response.version)
|
|
684
|
+
&& (response.version.lifecycleState === 'purge-pending'
|
|
685
|
+
|| response.version.lifecycleState === 'purged')
|
|
686
|
+
&& response.operation === undefined) {
|
|
687
|
+
errors.push('secret version purge preflight purging state requires its operation');
|
|
688
|
+
}
|
|
689
|
+
return errors;
|
|
690
|
+
} catch {
|
|
691
|
+
return ['secret version purge preflight response must be safely inspectable'];
|
|
692
|
+
}
|
|
693
|
+
};
|