@shipstatic/types 0.4.25 → 0.5.1
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/dist/index.d.ts +36 -33
- package/dist/index.js +25 -24
- package/package.json +1 -1
- package/src/index.ts +41 -38
package/dist/index.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export interface Deployment {
|
|
|
26
26
|
status: DeploymentStatusType;
|
|
27
27
|
/** Whether deployment has configuration */
|
|
28
28
|
readonly config?: boolean;
|
|
29
|
-
/** Optional array of
|
|
30
|
-
|
|
29
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
30
|
+
labels?: string[];
|
|
31
31
|
/** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli') */
|
|
32
32
|
readonly via?: string;
|
|
33
33
|
/** The deployment URL */
|
|
@@ -75,8 +75,8 @@ export interface Domain {
|
|
|
75
75
|
deployment: string | null;
|
|
76
76
|
/** Current domain status */
|
|
77
77
|
status: DomainStatusType;
|
|
78
|
-
/** Optional array of
|
|
79
|
-
|
|
78
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
79
|
+
labels?: string[];
|
|
80
80
|
/** The domain URL - internal (subdomain) or external (custom domain) */
|
|
81
81
|
readonly url: string;
|
|
82
82
|
/** Unix timestamp (seconds) when domain was created */
|
|
@@ -177,8 +177,8 @@ export interface Token {
|
|
|
177
177
|
readonly account: string;
|
|
178
178
|
/** Optional IP address locking for security */
|
|
179
179
|
readonly ip?: string;
|
|
180
|
-
/** Optional array of
|
|
181
|
-
|
|
180
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
181
|
+
labels?: string[];
|
|
182
182
|
/** Unix timestamp (seconds) when token was created */
|
|
183
183
|
readonly created: number;
|
|
184
184
|
/** Unix timestamp (seconds) when token expires, or null for never */
|
|
@@ -236,6 +236,8 @@ export interface Account {
|
|
|
236
236
|
readonly created: number;
|
|
237
237
|
/** Unix timestamp (seconds) when account was activated (first deployment) */
|
|
238
238
|
readonly activated?: number;
|
|
239
|
+
/** Last 4 characters of the API key for identification (null when no key generated) */
|
|
240
|
+
readonly hint?: string;
|
|
239
241
|
}
|
|
240
242
|
/**
|
|
241
243
|
* Account-specific configuration overrides
|
|
@@ -452,6 +454,7 @@ export interface PingResponse {
|
|
|
452
454
|
export declare const API_KEY_PREFIX = "ship-";
|
|
453
455
|
export declare const API_KEY_HEX_LENGTH = 64;
|
|
454
456
|
export declare const API_KEY_TOTAL_LENGTH: number;
|
|
457
|
+
export declare const API_KEY_HINT_LENGTH = 4;
|
|
455
458
|
export declare const DEPLOY_TOKEN_PREFIX = "token-";
|
|
456
459
|
export declare const DEPLOY_TOKEN_HEX_LENGTH = 64;
|
|
457
460
|
export declare const DEPLOY_TOKEN_TOTAL_LENGTH: number;
|
|
@@ -581,8 +584,8 @@ export type DeployInput = File[] | string | string[];
|
|
|
581
584
|
* SDK implementations may extend with additional options (timeout, signal, callbacks, etc.).
|
|
582
585
|
*/
|
|
583
586
|
export interface DeploymentCreateOptions {
|
|
584
|
-
/** Optional
|
|
585
|
-
|
|
587
|
+
/** Optional labels for categorization and filtering */
|
|
588
|
+
labels?: string[];
|
|
586
589
|
/** Optional subdomain suggestion for the deployment */
|
|
587
590
|
subdomain?: string;
|
|
588
591
|
/** Client identifier (e.g., 'cli', 'sdk', 'web') */
|
|
@@ -596,7 +599,7 @@ export interface DeploymentResource {
|
|
|
596
599
|
list: () => Promise<DeploymentListResponse>;
|
|
597
600
|
get: (id: string) => Promise<Deployment>;
|
|
598
601
|
set: (id: string, options: {
|
|
599
|
-
|
|
602
|
+
labels: string[];
|
|
600
603
|
}) => Promise<Deployment>;
|
|
601
604
|
remove: (id: string) => Promise<void>;
|
|
602
605
|
}
|
|
@@ -606,7 +609,7 @@ export interface DeploymentResource {
|
|
|
606
609
|
export interface DomainResource {
|
|
607
610
|
set: (name: string, options?: {
|
|
608
611
|
deployment?: string;
|
|
609
|
-
|
|
612
|
+
labels?: string[];
|
|
610
613
|
}) => Promise<Domain>;
|
|
611
614
|
list: () => Promise<DomainListResponse>;
|
|
612
615
|
get: (name: string) => Promise<Domain>;
|
|
@@ -634,7 +637,7 @@ export interface AccountResource {
|
|
|
634
637
|
export interface TokenResource {
|
|
635
638
|
create: (options?: {
|
|
636
639
|
ttl?: number;
|
|
637
|
-
|
|
640
|
+
labels?: string[];
|
|
638
641
|
}) => Promise<TokenCreateResponse>;
|
|
639
642
|
list: () => Promise<TokenListResponse>;
|
|
640
643
|
remove: (token: string) => Promise<void>;
|
|
@@ -696,7 +699,7 @@ export interface KeysResource {
|
|
|
696
699
|
* All activity event types logged in the system.
|
|
697
700
|
* Uses dot notation consistently: {resource}.{action}
|
|
698
701
|
*/
|
|
699
|
-
export type ActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.paid' | 'account.plan.transition' | 'account.suspended' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume' | 'admin.account.plan.update' | 'admin.account.ref.update' | 'admin.account.billing.update' | 'admin.account.
|
|
702
|
+
export type ActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.paid' | 'account.plan.transition' | 'account.suspended' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume' | 'admin.account.plan.update' | 'admin.account.ref.update' | 'admin.account.billing.update' | 'admin.account.labels.update' | 'admin.deployment.delete' | 'admin.domain.delete' | 'admin.billing.sync' | 'admin.billing.terminated' | 'billing.active' | 'billing.canceled' | 'billing.paused' | 'billing.expired' | 'billing.paid' | 'billing.trialing' | 'billing.scheduled_cancel' | 'billing.unpaid' | 'billing.update' | 'billing.past_due' | 'refund.created' | 'dispute.created';
|
|
700
703
|
/**
|
|
701
704
|
* Activity events visible to users in the dashboard
|
|
702
705
|
*/
|
|
@@ -733,8 +736,8 @@ export interface ActivityMeta {
|
|
|
733
736
|
wasVerified?: boolean;
|
|
734
737
|
/** Previous deployment ID before relinking */
|
|
735
738
|
previousDeployment?: string;
|
|
736
|
-
/**
|
|
737
|
-
|
|
739
|
+
/** Labels that were set/updated */
|
|
740
|
+
labels?: string[];
|
|
738
741
|
/** OAuth provider name */
|
|
739
742
|
provider?: string;
|
|
740
743
|
/** Account email */
|
|
@@ -883,43 +886,43 @@ export declare function generateDeploymentUrl(deployment: string, platformDomain
|
|
|
883
886
|
*/
|
|
884
887
|
export declare function generateDomainUrl(domain: string): string;
|
|
885
888
|
/**
|
|
886
|
-
*
|
|
887
|
-
* These rules define the single source of truth for
|
|
889
|
+
* Label validation constraints shared across UI and API.
|
|
890
|
+
* These rules define the single source of truth for label validation.
|
|
888
891
|
*/
|
|
889
|
-
export declare const
|
|
890
|
-
/** Minimum
|
|
892
|
+
export declare const LABEL_CONSTRAINTS: {
|
|
893
|
+
/** Minimum label length in characters */
|
|
891
894
|
readonly MIN_LENGTH: 3;
|
|
892
|
-
/** Maximum
|
|
895
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
893
896
|
readonly MAX_LENGTH: 25;
|
|
894
|
-
/** Maximum number of
|
|
897
|
+
/** Maximum number of labels allowed per resource */
|
|
895
898
|
readonly MAX_COUNT: 10;
|
|
896
|
-
/** Allowed separator characters between
|
|
899
|
+
/** Allowed separator characters between label segments */
|
|
897
900
|
readonly SEPARATORS: "._-";
|
|
898
901
|
};
|
|
899
902
|
/**
|
|
900
|
-
*
|
|
903
|
+
* Label validation pattern.
|
|
901
904
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
902
905
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
903
906
|
*
|
|
904
907
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
905
908
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
906
909
|
*/
|
|
907
|
-
export declare const
|
|
910
|
+
export declare const LABEL_PATTERN: RegExp;
|
|
908
911
|
/**
|
|
909
|
-
* Serialize
|
|
912
|
+
* Serialize labels array to JSON string for database storage.
|
|
910
913
|
* Returns null for empty or undefined arrays.
|
|
911
914
|
*
|
|
912
|
-
* @example
|
|
913
|
-
* @example
|
|
914
|
-
* @example
|
|
915
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
916
|
+
* @example serializeLabels([]) → null
|
|
917
|
+
* @example serializeLabels(undefined) → null
|
|
915
918
|
*/
|
|
916
|
-
export declare function
|
|
919
|
+
export declare function serializeLabels(labels: string[] | undefined): string | null;
|
|
917
920
|
/**
|
|
918
|
-
* Deserialize
|
|
921
|
+
* Deserialize labels from JSON string to array.
|
|
919
922
|
* Returns undefined for null/empty strings.
|
|
920
923
|
*
|
|
921
|
-
* @example
|
|
922
|
-
* @example
|
|
923
|
-
* @example
|
|
924
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
925
|
+
* @example deserializeLabels(null) → undefined
|
|
926
|
+
* @example deserializeLabels('') → undefined
|
|
924
927
|
*/
|
|
925
|
-
export declare function
|
|
928
|
+
export declare function deserializeLabels(labelsJson: string | null): string[] | undefined;
|
package/dist/index.js
CHANGED
|
@@ -413,6 +413,7 @@ export function isAllowedMimeType(mimeType) {
|
|
|
413
413
|
export const API_KEY_PREFIX = 'ship-';
|
|
414
414
|
export const API_KEY_HEX_LENGTH = 64;
|
|
415
415
|
export const API_KEY_TOTAL_LENGTH = API_KEY_PREFIX.length + API_KEY_HEX_LENGTH; // 69
|
|
416
|
+
export const API_KEY_HINT_LENGTH = 4;
|
|
416
417
|
// Deploy Token Configuration
|
|
417
418
|
export const DEPLOY_TOKEN_PREFIX = 'token-';
|
|
418
419
|
export const DEPLOY_TOKEN_HEX_LENGTH = 64;
|
|
@@ -564,57 +565,57 @@ export function generateDomainUrl(domain) {
|
|
|
564
565
|
return `https://${domain}`;
|
|
565
566
|
}
|
|
566
567
|
// =============================================================================
|
|
567
|
-
//
|
|
568
|
+
// LABEL UTILITIES
|
|
568
569
|
// =============================================================================
|
|
569
570
|
/**
|
|
570
|
-
*
|
|
571
|
-
* These rules define the single source of truth for
|
|
571
|
+
* Label validation constraints shared across UI and API.
|
|
572
|
+
* These rules define the single source of truth for label validation.
|
|
572
573
|
*/
|
|
573
|
-
export const
|
|
574
|
-
/** Minimum
|
|
574
|
+
export const LABEL_CONSTRAINTS = {
|
|
575
|
+
/** Minimum label length in characters */
|
|
575
576
|
MIN_LENGTH: 3,
|
|
576
|
-
/** Maximum
|
|
577
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
577
578
|
MAX_LENGTH: 25,
|
|
578
|
-
/** Maximum number of
|
|
579
|
+
/** Maximum number of labels allowed per resource */
|
|
579
580
|
MAX_COUNT: 10,
|
|
580
|
-
/** Allowed separator characters between
|
|
581
|
+
/** Allowed separator characters between label segments */
|
|
581
582
|
SEPARATORS: '._-',
|
|
582
583
|
};
|
|
583
584
|
/**
|
|
584
|
-
*
|
|
585
|
+
* Label validation pattern.
|
|
585
586
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
586
587
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
587
588
|
*
|
|
588
589
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
589
590
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
590
591
|
*/
|
|
591
|
-
export const
|
|
592
|
+
export const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
592
593
|
/**
|
|
593
|
-
* Serialize
|
|
594
|
+
* Serialize labels array to JSON string for database storage.
|
|
594
595
|
* Returns null for empty or undefined arrays.
|
|
595
596
|
*
|
|
596
|
-
* @example
|
|
597
|
-
* @example
|
|
598
|
-
* @example
|
|
597
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
598
|
+
* @example serializeLabels([]) → null
|
|
599
|
+
* @example serializeLabels(undefined) → null
|
|
599
600
|
*/
|
|
600
|
-
export function
|
|
601
|
-
if (!
|
|
601
|
+
export function serializeLabels(labels) {
|
|
602
|
+
if (!labels || labels.length === 0)
|
|
602
603
|
return null;
|
|
603
|
-
return JSON.stringify(
|
|
604
|
+
return JSON.stringify(labels);
|
|
604
605
|
}
|
|
605
606
|
/**
|
|
606
|
-
* Deserialize
|
|
607
|
+
* Deserialize labels from JSON string to array.
|
|
607
608
|
* Returns undefined for null/empty strings.
|
|
608
609
|
*
|
|
609
|
-
* @example
|
|
610
|
-
* @example
|
|
611
|
-
* @example
|
|
610
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
611
|
+
* @example deserializeLabels(null) → undefined
|
|
612
|
+
* @example deserializeLabels('') → undefined
|
|
612
613
|
*/
|
|
613
|
-
export function
|
|
614
|
-
if (!
|
|
614
|
+
export function deserializeLabels(labelsJson) {
|
|
615
|
+
if (!labelsJson)
|
|
615
616
|
return undefined;
|
|
616
617
|
try {
|
|
617
|
-
const parsed = JSON.parse(
|
|
618
|
+
const parsed = JSON.parse(labelsJson);
|
|
618
619
|
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
|
|
619
620
|
}
|
|
620
621
|
catch {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -33,8 +33,8 @@ export interface Deployment {
|
|
|
33
33
|
status: DeploymentStatusType; // Mutable - can be updated
|
|
34
34
|
/** Whether deployment has configuration */
|
|
35
35
|
readonly config?: boolean;
|
|
36
|
-
/** Optional array of
|
|
37
|
-
|
|
36
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
37
|
+
labels?: string[];
|
|
38
38
|
/** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli') */
|
|
39
39
|
readonly via?: string;
|
|
40
40
|
/** The deployment URL */
|
|
@@ -91,8 +91,8 @@ export interface Domain {
|
|
|
91
91
|
deployment: string | null; // Mutable - can be updated to point to different deployment
|
|
92
92
|
/** Current domain status */
|
|
93
93
|
status: DomainStatusType; // Mutable - can be updated
|
|
94
|
-
/** Optional array of
|
|
95
|
-
|
|
94
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
95
|
+
labels?: string[];
|
|
96
96
|
/** The domain URL - internal (subdomain) or external (custom domain) */
|
|
97
97
|
readonly url: string;
|
|
98
98
|
/** Unix timestamp (seconds) when domain was created */
|
|
@@ -204,8 +204,8 @@ export interface Token {
|
|
|
204
204
|
readonly account: string;
|
|
205
205
|
/** Optional IP address locking for security */
|
|
206
206
|
readonly ip?: string;
|
|
207
|
-
/** Optional array of
|
|
208
|
-
|
|
207
|
+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
|
|
208
|
+
labels?: string[];
|
|
209
209
|
/** Unix timestamp (seconds) when token was created */
|
|
210
210
|
readonly created: number;
|
|
211
211
|
/** Unix timestamp (seconds) when token expires, or null for never */
|
|
@@ -272,6 +272,8 @@ export interface Account {
|
|
|
272
272
|
readonly created: number;
|
|
273
273
|
/** Unix timestamp (seconds) when account was activated (first deployment) */
|
|
274
274
|
readonly activated?: number;
|
|
275
|
+
/** Last 4 characters of the API key for identification (null when no key generated) */
|
|
276
|
+
readonly hint?: string;
|
|
275
277
|
}
|
|
276
278
|
|
|
277
279
|
/**
|
|
@@ -777,6 +779,7 @@ export interface PingResponse {
|
|
|
777
779
|
export const API_KEY_PREFIX = 'ship-';
|
|
778
780
|
export const API_KEY_HEX_LENGTH = 64;
|
|
779
781
|
export const API_KEY_TOTAL_LENGTH = API_KEY_PREFIX.length + API_KEY_HEX_LENGTH; // 69
|
|
782
|
+
export const API_KEY_HINT_LENGTH = 4;
|
|
780
783
|
|
|
781
784
|
// Deploy Token Configuration
|
|
782
785
|
export const DEPLOY_TOKEN_PREFIX = 'token-';
|
|
@@ -1003,8 +1006,8 @@ export type DeployInput = File[] | string | string[];
|
|
|
1003
1006
|
* SDK implementations may extend with additional options (timeout, signal, callbacks, etc.).
|
|
1004
1007
|
*/
|
|
1005
1008
|
export interface DeploymentCreateOptions {
|
|
1006
|
-
/** Optional
|
|
1007
|
-
|
|
1009
|
+
/** Optional labels for categorization and filtering */
|
|
1010
|
+
labels?: string[];
|
|
1008
1011
|
/** Optional subdomain suggestion for the deployment */
|
|
1009
1012
|
subdomain?: string;
|
|
1010
1013
|
/** Client identifier (e.g., 'cli', 'sdk', 'web') */
|
|
@@ -1018,7 +1021,7 @@ export interface DeploymentResource {
|
|
|
1018
1021
|
create: (input: DeployInput, options?: DeploymentCreateOptions) => Promise<Deployment>;
|
|
1019
1022
|
list: () => Promise<DeploymentListResponse>;
|
|
1020
1023
|
get: (id: string) => Promise<Deployment>;
|
|
1021
|
-
set: (id: string, options: {
|
|
1024
|
+
set: (id: string, options: { labels: string[] }) => Promise<Deployment>;
|
|
1022
1025
|
remove: (id: string) => Promise<void>;
|
|
1023
1026
|
}
|
|
1024
1027
|
|
|
@@ -1026,7 +1029,7 @@ export interface DeploymentResource {
|
|
|
1026
1029
|
* Domain resource interface - the contract all implementations must follow
|
|
1027
1030
|
*/
|
|
1028
1031
|
export interface DomainResource {
|
|
1029
|
-
set: (name: string, options?: { deployment?: string;
|
|
1032
|
+
set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<Domain>;
|
|
1030
1033
|
list: () => Promise<DomainListResponse>;
|
|
1031
1034
|
get: (name: string) => Promise<Domain>;
|
|
1032
1035
|
remove: (name: string) => Promise<void>;
|
|
@@ -1048,7 +1051,7 @@ export interface AccountResource {
|
|
|
1048
1051
|
* Token resource interface - the contract all implementations must follow
|
|
1049
1052
|
*/
|
|
1050
1053
|
export interface TokenResource {
|
|
1051
|
-
create: (options?: { ttl?: number;
|
|
1054
|
+
create: (options?: { ttl?: number; labels?: string[] }) => Promise<TokenCreateResponse>;
|
|
1052
1055
|
list: () => Promise<TokenListResponse>;
|
|
1053
1056
|
remove: (token: string) => Promise<void>;
|
|
1054
1057
|
}
|
|
@@ -1149,7 +1152,7 @@ export type ActivityEvent =
|
|
|
1149
1152
|
| 'admin.account.plan.update'
|
|
1150
1153
|
| 'admin.account.ref.update'
|
|
1151
1154
|
| 'admin.account.billing.update'
|
|
1152
|
-
| 'admin.account.
|
|
1155
|
+
| 'admin.account.labels.update'
|
|
1153
1156
|
| 'admin.deployment.delete'
|
|
1154
1157
|
| 'admin.domain.delete'
|
|
1155
1158
|
| 'admin.billing.sync'
|
|
@@ -1224,8 +1227,8 @@ export interface ActivityMeta {
|
|
|
1224
1227
|
wasVerified?: boolean;
|
|
1225
1228
|
/** Previous deployment ID before relinking */
|
|
1226
1229
|
previousDeployment?: string;
|
|
1227
|
-
/**
|
|
1228
|
-
|
|
1230
|
+
/** Labels that were set/updated */
|
|
1231
|
+
labels?: string[];
|
|
1229
1232
|
|
|
1230
1233
|
// Account events
|
|
1231
1234
|
/** OAuth provider name */
|
|
@@ -1421,59 +1424,59 @@ export function generateDomainUrl(domain: string): string {
|
|
|
1421
1424
|
}
|
|
1422
1425
|
|
|
1423
1426
|
// =============================================================================
|
|
1424
|
-
//
|
|
1427
|
+
// LABEL UTILITIES
|
|
1425
1428
|
// =============================================================================
|
|
1426
1429
|
|
|
1427
1430
|
/**
|
|
1428
|
-
*
|
|
1429
|
-
* These rules define the single source of truth for
|
|
1431
|
+
* Label validation constraints shared across UI and API.
|
|
1432
|
+
* These rules define the single source of truth for label validation.
|
|
1430
1433
|
*/
|
|
1431
|
-
export const
|
|
1432
|
-
/** Minimum
|
|
1434
|
+
export const LABEL_CONSTRAINTS = {
|
|
1435
|
+
/** Minimum label length in characters */
|
|
1433
1436
|
MIN_LENGTH: 3,
|
|
1434
|
-
/** Maximum
|
|
1437
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
1435
1438
|
MAX_LENGTH: 25,
|
|
1436
|
-
/** Maximum number of
|
|
1439
|
+
/** Maximum number of labels allowed per resource */
|
|
1437
1440
|
MAX_COUNT: 10,
|
|
1438
|
-
/** Allowed separator characters between
|
|
1441
|
+
/** Allowed separator characters between label segments */
|
|
1439
1442
|
SEPARATORS: '._-',
|
|
1440
1443
|
} as const;
|
|
1441
1444
|
|
|
1442
1445
|
/**
|
|
1443
|
-
*
|
|
1446
|
+
* Label validation pattern.
|
|
1444
1447
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
1445
1448
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
1446
1449
|
*
|
|
1447
1450
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
1448
1451
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
1449
1452
|
*/
|
|
1450
|
-
export const
|
|
1453
|
+
export const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
1451
1454
|
|
|
1452
1455
|
/**
|
|
1453
|
-
* Serialize
|
|
1456
|
+
* Serialize labels array to JSON string for database storage.
|
|
1454
1457
|
* Returns null for empty or undefined arrays.
|
|
1455
1458
|
*
|
|
1456
|
-
* @example
|
|
1457
|
-
* @example
|
|
1458
|
-
* @example
|
|
1459
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
1460
|
+
* @example serializeLabels([]) → null
|
|
1461
|
+
* @example serializeLabels(undefined) → null
|
|
1459
1462
|
*/
|
|
1460
|
-
export function
|
|
1461
|
-
if (!
|
|
1462
|
-
return JSON.stringify(
|
|
1463
|
+
export function serializeLabels(labels: string[] | undefined): string | null {
|
|
1464
|
+
if (!labels || labels.length === 0) return null;
|
|
1465
|
+
return JSON.stringify(labels);
|
|
1463
1466
|
}
|
|
1464
1467
|
|
|
1465
1468
|
/**
|
|
1466
|
-
* Deserialize
|
|
1469
|
+
* Deserialize labels from JSON string to array.
|
|
1467
1470
|
* Returns undefined for null/empty strings.
|
|
1468
1471
|
*
|
|
1469
|
-
* @example
|
|
1470
|
-
* @example
|
|
1471
|
-
* @example
|
|
1472
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
1473
|
+
* @example deserializeLabels(null) → undefined
|
|
1474
|
+
* @example deserializeLabels('') → undefined
|
|
1472
1475
|
*/
|
|
1473
|
-
export function
|
|
1474
|
-
if (!
|
|
1476
|
+
export function deserializeLabels(labelsJson: string | null): string[] | undefined {
|
|
1477
|
+
if (!labelsJson) return undefined;
|
|
1475
1478
|
try {
|
|
1476
|
-
const parsed = JSON.parse(
|
|
1479
|
+
const parsed = JSON.parse(labelsJson);
|
|
1477
1480
|
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
|
|
1478
1481
|
} catch {
|
|
1479
1482
|
return undefined;
|