@serve.zone/interfaces 22.0.0 → 23.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.
Files changed (75) hide show
  1. package/changelog.md +12 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/appstore/types.d.ts +31 -16
  4. package/dist_ts/appstore/types.js +208 -11
  5. package/dist_ts/data/deploymentoperation.js +6 -20
  6. package/dist_ts/data/deploymentpreflight.d.ts +2 -6
  7. package/dist_ts/data/deploymentpreflight.js +1 -1
  8. package/dist_ts/data/hostedapp.d.ts +40 -14
  9. package/dist_ts/data/hostedapp.js +90 -1
  10. package/dist_ts/data/immutableimage.d.ts +30 -1
  11. package/dist_ts/data/immutableimage.js +103 -1
  12. package/dist_ts/data/index.d.ts +0 -2
  13. package/dist_ts/data/index.js +1 -3
  14. package/dist_ts/data/secret.d.ts +186 -13
  15. package/dist_ts/data/secret.js +543 -45
  16. package/dist_ts/data/service.d.ts +0 -39
  17. package/dist_ts/data/service.js +1 -77
  18. package/dist_ts/data/settings.d.ts +62 -43
  19. package/dist_ts/data/settings.js +34 -2
  20. package/dist_ts/platform/storage.d.ts +13 -14
  21. package/dist_ts/platform/storage.js +1 -1
  22. package/dist_ts/platform/storagemigration.d.ts +8 -8
  23. package/dist_ts/platform/storagemigration.golden.d.ts +2 -2
  24. package/dist_ts/platform/storagemigration.golden.js +19 -88
  25. package/dist_ts/platform/storagemigration.js +46 -58
  26. package/dist_ts/platform/types.d.ts +5 -19
  27. package/dist_ts/plugins.d.ts +2 -1
  28. package/dist_ts/plugins.js +3 -2
  29. package/dist_ts/requests/hostedapp.d.ts +12 -9
  30. package/dist_ts/requests/hostedapp.js +38 -1
  31. package/dist_ts/requests/index.d.ts +1 -3
  32. package/dist_ts/requests/index.js +2 -4
  33. package/dist_ts/requests/platform.d.ts +0 -1
  34. package/dist_ts/requests/secret.d.ts +23 -17
  35. package/dist_ts/requests/secret.js +161 -2
  36. package/dist_ts/requests/service.d.ts +0 -13
  37. package/dist_ts/requests/settings.d.ts +3 -3
  38. package/dist_ts/runtime.d.ts +78 -26
  39. package/dist_ts/runtime.js +219 -19
  40. package/package.json +2 -1
  41. package/readme.md +106 -28
  42. package/ts/00_commitinfo_data.ts +1 -1
  43. package/ts/appstore/types.ts +251 -25
  44. package/ts/data/deploymentoperation.ts +7 -31
  45. package/ts/data/deploymentpreflight.ts +2 -5
  46. package/ts/data/hostedapp.ts +144 -18
  47. package/ts/data/immutableimage.ts +150 -1
  48. package/ts/data/index.ts +0 -2
  49. package/ts/data/secret.ts +775 -53
  50. package/ts/data/service.ts +0 -118
  51. package/ts/data/settings.ts +70 -80
  52. package/ts/platform/storage.ts +15 -16
  53. package/ts/platform/storagemigration.golden.ts +18 -95
  54. package/ts/platform/storagemigration.ts +57 -87
  55. package/ts/platform/types.ts +6 -19
  56. package/ts/plugins.ts +2 -0
  57. package/ts/requests/hostedapp.ts +50 -9
  58. package/ts/requests/index.ts +0 -4
  59. package/ts/requests/platform.ts +0 -1
  60. package/ts/requests/secret.ts +204 -9
  61. package/ts/requests/service.ts +0 -16
  62. package/ts/requests/settings.ts +3 -3
  63. package/ts/runtime.ts +314 -41
  64. package/dist_ts/data/secretbundle.d.ts +0 -57
  65. package/dist_ts/data/secretbundle.js +0 -2
  66. package/dist_ts/data/secretgroup.d.ts +0 -52
  67. package/dist_ts/data/secretgroup.js +0 -2
  68. package/dist_ts/requests/secretbundle.d.ts +0 -75
  69. package/dist_ts/requests/secretbundle.js +0 -4
  70. package/dist_ts/requests/secretgroup.d.ts +0 -52
  71. package/dist_ts/requests/secretgroup.js +0 -4
  72. package/ts/data/secretbundle.ts +0 -65
  73. package/ts/data/secretgroup.ts +0 -58
  74. package/ts/requests/secretbundle.ts +0 -103
  75. package/ts/requests/secretgroup.ts +0 -74
@@ -1,4 +1,5 @@
1
1
  import type * as plugins from '../plugins.js';
2
+ import * as secretData from '../data/secret.js';
2
3
  import type {
3
4
  IResolvedSecretManifestEntry,
4
5
  ISecretMetadata,
@@ -7,19 +8,27 @@ import type {
7
8
  ISecretSetMetadata,
8
9
  ISecretVersionMetadata,
9
10
  ISecretVersionRetentionReference,
11
+ TSecretIngressRecipient,
12
+ TSecretMutationTarget,
13
+ TSecretValueInput,
10
14
  TSecretDelivery,
11
15
  TSecretEnvironment,
12
16
  } from '../data/secret.js';
13
17
  import type { IIdentityCredential } from '../data/user.js';
14
18
 
15
- export type TSecretMutationTarget =
16
- | { kind: 'service'; serviceId: string }
17
- | { kind: 'secret-set'; secretSetId: string };
18
-
19
- export type TSecretValueInput =
20
- | { mode: 'provided'; value: string }
21
- /** Server-generated runtime-only value; plaintext is never returned. */
22
- | { mode: 'generated'; bytes: 32 | 48 | 64 };
19
+ export interface IReq_GetSecretIngressRecipient
20
+ extends plugins.typedrequestInterfaces.implementsTR<
21
+ plugins.typedrequestInterfaces.ITypedRequest,
22
+ IReq_GetSecretIngressRecipient
23
+ > {
24
+ method: 'getSecretIngressRecipient';
25
+ request: {
26
+ identity: IIdentityCredential;
27
+ };
28
+ response: {
29
+ recipient: TSecretIngressRecipient;
30
+ };
31
+ }
23
32
 
24
33
  export interface IReq_ListSecrets extends plugins.typedrequestInterfaces.implementsTR<
25
34
  plugins.typedrequestInterfaces.ITypedRequest,
@@ -28,6 +37,7 @@ export interface IReq_ListSecrets extends plugins.typedrequestInterfaces.impleme
28
37
  method: 'listSecrets';
29
38
  request: {
30
39
  identity: IIdentityCredential;
40
+ mutationId: string;
31
41
  target: TSecretMutationTarget;
32
42
  environment?: TSecretEnvironment;
33
43
  };
@@ -45,6 +55,7 @@ export interface IReq_GetSecretMetadata extends plugins.typedrequestInterfaces.i
45
55
  method: 'getSecretMetadata';
46
56
  request: {
47
57
  identity: IIdentityCredential;
58
+ mutationId: string;
48
59
  secretId: string;
49
60
  };
50
61
  response: {
@@ -60,6 +71,7 @@ export interface IReq_CreateSecret extends plugins.typedrequestInterfaces.implem
60
71
  method: 'createSecret';
61
72
  request: {
62
73
  identity: IIdentityCredential;
74
+ mutationId: string;
63
75
  target: TSecretMutationTarget;
64
76
  key: string;
65
77
  environment: TSecretEnvironment;
@@ -85,14 +97,18 @@ export interface IReq_RotateSecret extends plugins.typedrequestInterfaces.implem
85
97
  method: 'rotateSecret';
86
98
  request: {
87
99
  identity: IIdentityCredential;
100
+ mutationId: string;
88
101
  secretId: string;
89
102
  valueInput: TSecretValueInput;
90
103
  expectedSecretRevision: number;
91
- expectedActiveVersionId?: string;
104
+ expectedActiveVersionId: string;
105
+ /** Aggregate owner fence incremented by every secret mutation. */
106
+ expectedTargetSecretsRevision: number;
92
107
  };
93
108
  response: {
94
109
  secret: ISecretMetadata;
95
110
  version: ISecretVersionMetadata;
111
+ targetSecretsRevision: number;
96
112
  };
97
113
  }
98
114
 
@@ -106,9 +122,11 @@ export interface IReq_ChangeSecretLifecycle extends plugins.typedrequestInterfac
106
122
  secretId: string;
107
123
  action: 'retire' | 'revoke' | 'delete' | 'request-purge';
108
124
  expectedSecretRevision: number;
125
+ expectedTargetSecretsRevision: number;
109
126
  };
110
127
  response: {
111
128
  secret: ISecretMetadata;
129
+ targetSecretsRevision: number;
112
130
  };
113
131
  }
114
132
 
@@ -187,6 +205,8 @@ extends plugins.typedrequestInterfaces.implementsTR<
187
205
  secretSetId: string;
188
206
  action: 'retire' | 'delete';
189
207
  expectedRevision: number;
208
+ /** Prevents lifecycle changes racing any secret mutation in this set. */
209
+ expectedSecretsRevision: number;
190
210
  };
191
211
  response: {
192
212
  secretSet: ISecretSetMetadata;
@@ -254,3 +274,178 @@ extends plugins.typedrequestInterfaces.implementsTR<
254
274
  collisions: ISecretResolutionPreviewCollision[];
255
275
  };
256
276
  }
277
+
278
+ const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
279
+ Boolean(valueArg) && typeof valueArg === 'object' && !Array.isArray(valueArg)
280
+ );
281
+
282
+ export const validateCreateSecretRequest = async (
283
+ requestArg: unknown,
284
+ activeIngressRecipientArg: TSecretIngressRecipient,
285
+ ): Promise<string[]> => {
286
+ if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
287
+ return ['create secret request must be an object'];
288
+ }
289
+ const request = requestArg as Record<string, unknown>;
290
+ const errors = secretData.validateSecretMutationId(request.mutationId);
291
+ const allowedKeys = new Set([
292
+ 'identity',
293
+ 'mutationId',
294
+ 'target',
295
+ 'key',
296
+ 'environment',
297
+ 'name',
298
+ 'description',
299
+ 'tags',
300
+ 'delivery',
301
+ 'valueInput',
302
+ 'expectedTargetSecretsRevision',
303
+ ]);
304
+ if (Object.keys(request).some((keyArg) => !allowedKeys.has(keyArg))) {
305
+ errors.push('create secret request contains fields outside its schema');
306
+ }
307
+ if (!request.identity || typeof request.identity !== 'object'
308
+ || typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
309
+ errors.push('create secret identity must be a JWT credential');
310
+ }
311
+ if (!request.target || typeof request.target !== 'object' || Array.isArray(request.target)) {
312
+ errors.push('create secret target must be an owner object');
313
+ } else {
314
+ errors.push(...secretData.validateSecretOwner(request.target));
315
+ }
316
+ if (typeof request.key !== 'string' || !/^[A-Z_][A-Z0-9_]{0,252}$/.test(request.key)) {
317
+ errors.push('create secret key must be canonical');
318
+ }
319
+ if (typeof request.environment !== 'string'
320
+ || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(request.environment)) {
321
+ errors.push('create secret environment must be canonical');
322
+ }
323
+ if (typeof request.name !== 'string'
324
+ || request.name.trim() !== request.name
325
+ || request.name.length < 1
326
+ || request.name.length > 200) {
327
+ errors.push('create secret name must be a bounded non-empty string');
328
+ }
329
+ if (request.description !== undefined
330
+ && (typeof request.description !== 'string' || request.description.length > 2000)) {
331
+ errors.push('create secret description must be a bounded string');
332
+ }
333
+ if (request.tags !== undefined) {
334
+ if (!Array.isArray(request.tags) || request.tags.length > 64) {
335
+ errors.push('create secret tags must be a bounded array');
336
+ } else {
337
+ const tagKeys: string[] = [];
338
+ request.tags.forEach((tagArg, indexArg) => {
339
+ if (!isRecord(tagArg)
340
+ || JSON.stringify(Object.keys(tagArg).sort()) !== JSON.stringify(['key', 'value'])
341
+ || typeof tagArg.key !== 'string'
342
+ || tagArg.key.length < 1
343
+ || tagArg.key.length > 100
344
+ || typeof tagArg.value !== 'string'
345
+ || tagArg.value.length > 500) {
346
+ errors.push(`create secret tags[${indexArg}] must use the bounded key/value schema`);
347
+ } else {
348
+ tagKeys.push(tagArg.key);
349
+ }
350
+ });
351
+ if (new Set(tagKeys).size !== tagKeys.length) {
352
+ errors.push('create secret tag keys must be unique');
353
+ }
354
+ }
355
+ }
356
+ const deliveryErrors = secretData.validateSecretDelivery(request.delivery);
357
+ errors.push(...deliveryErrors);
358
+ if (!Number.isSafeInteger(request.expectedTargetSecretsRevision)
359
+ || (request.expectedTargetSecretsRevision as number) < 0) {
360
+ errors.push('create secret target revision must be a non-negative integer');
361
+ }
362
+ const canBuildContext = secretData.validateSecretMutationId(request.mutationId).length === 0
363
+ && secretData.validateSecretOwner(request.target).length === 0
364
+ && typeof request.key === 'string'
365
+ && /^[A-Z_][A-Z0-9_]{0,252}$/.test(request.key)
366
+ && typeof request.environment === 'string'
367
+ && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(request.environment)
368
+ && deliveryErrors.length === 0
369
+ && Number.isSafeInteger(request.expectedTargetSecretsRevision)
370
+ && (request.expectedTargetSecretsRevision as number) >= 0;
371
+ const expectedContext = canBuildContext
372
+ ? secretData.createSecretCreateEnvelopeContext({
373
+ mutationId: request.mutationId as string,
374
+ target: request.target as TSecretMutationTarget,
375
+ key: request.key as string,
376
+ environment: request.environment as TSecretEnvironment,
377
+ delivery: request.delivery as TSecretDelivery,
378
+ expectedTargetSecretsRevision: request.expectedTargetSecretsRevision as number,
379
+ })
380
+ : new Uint8Array();
381
+ errors.push(...await secretData.validateSecretValueInputForRecipient(
382
+ request.valueInput,
383
+ activeIngressRecipientArg,
384
+ expectedContext,
385
+ ));
386
+ return errors;
387
+ };
388
+
389
+ export const validateRotateSecretRequest = async (
390
+ requestArg: unknown,
391
+ activeIngressRecipientArg: TSecretIngressRecipient,
392
+ ): Promise<string[]> => {
393
+ if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
394
+ return ['rotate secret request must be an object'];
395
+ }
396
+ const request = requestArg as Record<string, unknown>;
397
+ const errors = secretData.validateSecretMutationId(request.mutationId);
398
+ if (JSON.stringify(Object.keys(request).sort()) !== JSON.stringify([
399
+ 'expectedActiveVersionId',
400
+ 'expectedSecretRevision',
401
+ 'expectedTargetSecretsRevision',
402
+ 'identity',
403
+ 'mutationId',
404
+ 'secretId',
405
+ 'valueInput',
406
+ ])) {
407
+ errors.push('rotate secret request must use its exact schema');
408
+ }
409
+ if (!request.identity || typeof request.identity !== 'object'
410
+ || typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
411
+ errors.push('rotate secret identity must be a JWT credential');
412
+ }
413
+ for (const field of ['secretId', 'expectedActiveVersionId'] as const) {
414
+ if (typeof request[field] !== 'string'
415
+ || !/^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request[field] as string)) {
416
+ errors.push(`rotate secret ${field} must be canonical`);
417
+ }
418
+ }
419
+ if (!Number.isSafeInteger(request.expectedSecretRevision)
420
+ || (request.expectedSecretRevision as number) < 1) {
421
+ errors.push('rotate secret revision must be a positive integer');
422
+ }
423
+ if (!Number.isSafeInteger(request.expectedTargetSecretsRevision)
424
+ || (request.expectedTargetSecretsRevision as number) < 0) {
425
+ errors.push('rotate secret target revision must be a non-negative integer');
426
+ }
427
+ const canBuildContext = secretData.validateSecretMutationId(request.mutationId).length === 0
428
+ && typeof request.secretId === 'string'
429
+ && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request.secretId)
430
+ && typeof request.expectedActiveVersionId === 'string'
431
+ && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request.expectedActiveVersionId)
432
+ && Number.isSafeInteger(request.expectedSecretRevision)
433
+ && (request.expectedSecretRevision as number) >= 1
434
+ && Number.isSafeInteger(request.expectedTargetSecretsRevision)
435
+ && (request.expectedTargetSecretsRevision as number) >= 0;
436
+ const expectedContext = canBuildContext
437
+ ? secretData.createSecretRotateEnvelopeContext({
438
+ mutationId: request.mutationId as string,
439
+ secretId: request.secretId as string,
440
+ expectedSecretRevision: request.expectedSecretRevision as number,
441
+ expectedActiveVersionId: request.expectedActiveVersionId as string,
442
+ expectedTargetSecretsRevision: request.expectedTargetSecretsRevision as number,
443
+ })
444
+ : new Uint8Array();
445
+ errors.push(...await secretData.validateSecretValueInputForRecipient(
446
+ request.valueInput,
447
+ activeIngressRecipientArg,
448
+ expectedContext,
449
+ ));
450
+ return errors;
451
+ };
@@ -368,19 +368,3 @@ extends plugins.typedrequestInterfaces.implementsTR<
368
368
  success: boolean;
369
369
  };
370
370
  }
371
-
372
- export interface IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
373
- extends plugins.typedrequestInterfaces.implementsTR<
374
- plugins.typedrequestInterfaces.ITypedRequest,
375
- IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
376
- > {
377
- method: 'getServiceSecretBundlesAsFlatObject';
378
- request: {
379
- identity: IIdentity;
380
- serviceId: string;
381
- environment: string;
382
- };
383
- response: {
384
- flatKeyValueObject: {[key: string]: string};
385
- };
386
- }
@@ -1,5 +1,5 @@
1
1
  import * as plugins from '../plugins.js';
2
- import type { ICloudlySettings, ICloudlySettingsMasked } from '../data/settings.js';
2
+ import type { ICloudlySettings } from '../data/settings.js';
3
3
  import type { IIdentity } from '../data/user.js';
4
4
 
5
5
  // Get Settings
@@ -9,7 +9,7 @@ export interface IRequest_GetSettings extends plugins.typedrequestInterfaces.ITy
9
9
  identity?: IIdentity;
10
10
  };
11
11
  response: {
12
- settings: ICloudlySettingsMasked;
12
+ settings: ICloudlySettings;
13
13
  };
14
14
  }
15
15
 
@@ -61,6 +61,6 @@ export interface IRequest_GetSetting extends plugins.typedrequestInterfaces.ITyp
61
61
  key: keyof ICloudlySettings;
62
62
  };
63
63
  response: {
64
- value: string | undefined;
64
+ value: ICloudlySettings[keyof ICloudlySettings] | undefined;
65
65
  };
66
66
  }