@shipstatic/types 0.4.25 → 0.5.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/dist/index.d.ts +33 -33
- package/dist/index.js +24 -24
- package/package.json +1 -1
- package/src/index.ts +38 -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 */
|
|
@@ -581,8 +581,8 @@ export type DeployInput = File[] | string | string[];
|
|
|
581
581
|
* SDK implementations may extend with additional options (timeout, signal, callbacks, etc.).
|
|
582
582
|
*/
|
|
583
583
|
export interface DeploymentCreateOptions {
|
|
584
|
-
/** Optional
|
|
585
|
-
|
|
584
|
+
/** Optional labels for categorization and filtering */
|
|
585
|
+
labels?: string[];
|
|
586
586
|
/** Optional subdomain suggestion for the deployment */
|
|
587
587
|
subdomain?: string;
|
|
588
588
|
/** Client identifier (e.g., 'cli', 'sdk', 'web') */
|
|
@@ -596,7 +596,7 @@ export interface DeploymentResource {
|
|
|
596
596
|
list: () => Promise<DeploymentListResponse>;
|
|
597
597
|
get: (id: string) => Promise<Deployment>;
|
|
598
598
|
set: (id: string, options: {
|
|
599
|
-
|
|
599
|
+
labels: string[];
|
|
600
600
|
}) => Promise<Deployment>;
|
|
601
601
|
remove: (id: string) => Promise<void>;
|
|
602
602
|
}
|
|
@@ -606,7 +606,7 @@ export interface DeploymentResource {
|
|
|
606
606
|
export interface DomainResource {
|
|
607
607
|
set: (name: string, options?: {
|
|
608
608
|
deployment?: string;
|
|
609
|
-
|
|
609
|
+
labels?: string[];
|
|
610
610
|
}) => Promise<Domain>;
|
|
611
611
|
list: () => Promise<DomainListResponse>;
|
|
612
612
|
get: (name: string) => Promise<Domain>;
|
|
@@ -634,7 +634,7 @@ export interface AccountResource {
|
|
|
634
634
|
export interface TokenResource {
|
|
635
635
|
create: (options?: {
|
|
636
636
|
ttl?: number;
|
|
637
|
-
|
|
637
|
+
labels?: string[];
|
|
638
638
|
}) => Promise<TokenCreateResponse>;
|
|
639
639
|
list: () => Promise<TokenListResponse>;
|
|
640
640
|
remove: (token: string) => Promise<void>;
|
|
@@ -696,7 +696,7 @@ export interface KeysResource {
|
|
|
696
696
|
* All activity event types logged in the system.
|
|
697
697
|
* Uses dot notation consistently: {resource}.{action}
|
|
698
698
|
*/
|
|
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.
|
|
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.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
700
|
/**
|
|
701
701
|
* Activity events visible to users in the dashboard
|
|
702
702
|
*/
|
|
@@ -733,8 +733,8 @@ export interface ActivityMeta {
|
|
|
733
733
|
wasVerified?: boolean;
|
|
734
734
|
/** Previous deployment ID before relinking */
|
|
735
735
|
previousDeployment?: string;
|
|
736
|
-
/**
|
|
737
|
-
|
|
736
|
+
/** Labels that were set/updated */
|
|
737
|
+
labels?: string[];
|
|
738
738
|
/** OAuth provider name */
|
|
739
739
|
provider?: string;
|
|
740
740
|
/** Account email */
|
|
@@ -883,43 +883,43 @@ export declare function generateDeploymentUrl(deployment: string, platformDomain
|
|
|
883
883
|
*/
|
|
884
884
|
export declare function generateDomainUrl(domain: string): string;
|
|
885
885
|
/**
|
|
886
|
-
*
|
|
887
|
-
* These rules define the single source of truth for
|
|
886
|
+
* Label validation constraints shared across UI and API.
|
|
887
|
+
* These rules define the single source of truth for label validation.
|
|
888
888
|
*/
|
|
889
|
-
export declare const
|
|
890
|
-
/** Minimum
|
|
889
|
+
export declare const LABEL_CONSTRAINTS: {
|
|
890
|
+
/** Minimum label length in characters */
|
|
891
891
|
readonly MIN_LENGTH: 3;
|
|
892
|
-
/** Maximum
|
|
892
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
893
893
|
readonly MAX_LENGTH: 25;
|
|
894
|
-
/** Maximum number of
|
|
894
|
+
/** Maximum number of labels allowed per resource */
|
|
895
895
|
readonly MAX_COUNT: 10;
|
|
896
|
-
/** Allowed separator characters between
|
|
896
|
+
/** Allowed separator characters between label segments */
|
|
897
897
|
readonly SEPARATORS: "._-";
|
|
898
898
|
};
|
|
899
899
|
/**
|
|
900
|
-
*
|
|
900
|
+
* Label validation pattern.
|
|
901
901
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
902
902
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
903
903
|
*
|
|
904
904
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
905
905
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
906
906
|
*/
|
|
907
|
-
export declare const
|
|
907
|
+
export declare const LABEL_PATTERN: RegExp;
|
|
908
908
|
/**
|
|
909
|
-
* Serialize
|
|
909
|
+
* Serialize labels array to JSON string for database storage.
|
|
910
910
|
* Returns null for empty or undefined arrays.
|
|
911
911
|
*
|
|
912
|
-
* @example
|
|
913
|
-
* @example
|
|
914
|
-
* @example
|
|
912
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
913
|
+
* @example serializeLabels([]) → null
|
|
914
|
+
* @example serializeLabels(undefined) → null
|
|
915
915
|
*/
|
|
916
|
-
export declare function
|
|
916
|
+
export declare function serializeLabels(labels: string[] | undefined): string | null;
|
|
917
917
|
/**
|
|
918
|
-
* Deserialize
|
|
918
|
+
* Deserialize labels from JSON string to array.
|
|
919
919
|
* Returns undefined for null/empty strings.
|
|
920
920
|
*
|
|
921
|
-
* @example
|
|
922
|
-
* @example
|
|
923
|
-
* @example
|
|
921
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
922
|
+
* @example deserializeLabels(null) → undefined
|
|
923
|
+
* @example deserializeLabels('') → undefined
|
|
924
924
|
*/
|
|
925
|
-
export declare function
|
|
925
|
+
export declare function deserializeLabels(labelsJson: string | null): string[] | undefined;
|
package/dist/index.js
CHANGED
|
@@ -564,57 +564,57 @@ export function generateDomainUrl(domain) {
|
|
|
564
564
|
return `https://${domain}`;
|
|
565
565
|
}
|
|
566
566
|
// =============================================================================
|
|
567
|
-
//
|
|
567
|
+
// LABEL UTILITIES
|
|
568
568
|
// =============================================================================
|
|
569
569
|
/**
|
|
570
|
-
*
|
|
571
|
-
* These rules define the single source of truth for
|
|
570
|
+
* Label validation constraints shared across UI and API.
|
|
571
|
+
* These rules define the single source of truth for label validation.
|
|
572
572
|
*/
|
|
573
|
-
export const
|
|
574
|
-
/** Minimum
|
|
573
|
+
export const LABEL_CONSTRAINTS = {
|
|
574
|
+
/** Minimum label length in characters */
|
|
575
575
|
MIN_LENGTH: 3,
|
|
576
|
-
/** Maximum
|
|
576
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
577
577
|
MAX_LENGTH: 25,
|
|
578
|
-
/** Maximum number of
|
|
578
|
+
/** Maximum number of labels allowed per resource */
|
|
579
579
|
MAX_COUNT: 10,
|
|
580
|
-
/** Allowed separator characters between
|
|
580
|
+
/** Allowed separator characters between label segments */
|
|
581
581
|
SEPARATORS: '._-',
|
|
582
582
|
};
|
|
583
583
|
/**
|
|
584
|
-
*
|
|
584
|
+
* Label validation pattern.
|
|
585
585
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
586
586
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
587
587
|
*
|
|
588
588
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
589
589
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
590
590
|
*/
|
|
591
|
-
export const
|
|
591
|
+
export const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
592
592
|
/**
|
|
593
|
-
* Serialize
|
|
593
|
+
* Serialize labels array to JSON string for database storage.
|
|
594
594
|
* Returns null for empty or undefined arrays.
|
|
595
595
|
*
|
|
596
|
-
* @example
|
|
597
|
-
* @example
|
|
598
|
-
* @example
|
|
596
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
597
|
+
* @example serializeLabels([]) → null
|
|
598
|
+
* @example serializeLabels(undefined) → null
|
|
599
599
|
*/
|
|
600
|
-
export function
|
|
601
|
-
if (!
|
|
600
|
+
export function serializeLabels(labels) {
|
|
601
|
+
if (!labels || labels.length === 0)
|
|
602
602
|
return null;
|
|
603
|
-
return JSON.stringify(
|
|
603
|
+
return JSON.stringify(labels);
|
|
604
604
|
}
|
|
605
605
|
/**
|
|
606
|
-
* Deserialize
|
|
606
|
+
* Deserialize labels from JSON string to array.
|
|
607
607
|
* Returns undefined for null/empty strings.
|
|
608
608
|
*
|
|
609
|
-
* @example
|
|
610
|
-
* @example
|
|
611
|
-
* @example
|
|
609
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
610
|
+
* @example deserializeLabels(null) → undefined
|
|
611
|
+
* @example deserializeLabels('') → undefined
|
|
612
612
|
*/
|
|
613
|
-
export function
|
|
614
|
-
if (!
|
|
613
|
+
export function deserializeLabels(labelsJson) {
|
|
614
|
+
if (!labelsJson)
|
|
615
615
|
return undefined;
|
|
616
616
|
try {
|
|
617
|
-
const parsed = JSON.parse(
|
|
617
|
+
const parsed = JSON.parse(labelsJson);
|
|
618
618
|
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
|
|
619
619
|
}
|
|
620
620
|
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 */
|
|
@@ -1003,8 +1003,8 @@ export type DeployInput = File[] | string | string[];
|
|
|
1003
1003
|
* SDK implementations may extend with additional options (timeout, signal, callbacks, etc.).
|
|
1004
1004
|
*/
|
|
1005
1005
|
export interface DeploymentCreateOptions {
|
|
1006
|
-
/** Optional
|
|
1007
|
-
|
|
1006
|
+
/** Optional labels for categorization and filtering */
|
|
1007
|
+
labels?: string[];
|
|
1008
1008
|
/** Optional subdomain suggestion for the deployment */
|
|
1009
1009
|
subdomain?: string;
|
|
1010
1010
|
/** Client identifier (e.g., 'cli', 'sdk', 'web') */
|
|
@@ -1018,7 +1018,7 @@ export interface DeploymentResource {
|
|
|
1018
1018
|
create: (input: DeployInput, options?: DeploymentCreateOptions) => Promise<Deployment>;
|
|
1019
1019
|
list: () => Promise<DeploymentListResponse>;
|
|
1020
1020
|
get: (id: string) => Promise<Deployment>;
|
|
1021
|
-
set: (id: string, options: {
|
|
1021
|
+
set: (id: string, options: { labels: string[] }) => Promise<Deployment>;
|
|
1022
1022
|
remove: (id: string) => Promise<void>;
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
@@ -1026,7 +1026,7 @@ export interface DeploymentResource {
|
|
|
1026
1026
|
* Domain resource interface - the contract all implementations must follow
|
|
1027
1027
|
*/
|
|
1028
1028
|
export interface DomainResource {
|
|
1029
|
-
set: (name: string, options?: { deployment?: string;
|
|
1029
|
+
set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<Domain>;
|
|
1030
1030
|
list: () => Promise<DomainListResponse>;
|
|
1031
1031
|
get: (name: string) => Promise<Domain>;
|
|
1032
1032
|
remove: (name: string) => Promise<void>;
|
|
@@ -1048,7 +1048,7 @@ export interface AccountResource {
|
|
|
1048
1048
|
* Token resource interface - the contract all implementations must follow
|
|
1049
1049
|
*/
|
|
1050
1050
|
export interface TokenResource {
|
|
1051
|
-
create: (options?: { ttl?: number;
|
|
1051
|
+
create: (options?: { ttl?: number; labels?: string[] }) => Promise<TokenCreateResponse>;
|
|
1052
1052
|
list: () => Promise<TokenListResponse>;
|
|
1053
1053
|
remove: (token: string) => Promise<void>;
|
|
1054
1054
|
}
|
|
@@ -1149,7 +1149,7 @@ export type ActivityEvent =
|
|
|
1149
1149
|
| 'admin.account.plan.update'
|
|
1150
1150
|
| 'admin.account.ref.update'
|
|
1151
1151
|
| 'admin.account.billing.update'
|
|
1152
|
-
| 'admin.account.
|
|
1152
|
+
| 'admin.account.labels.update'
|
|
1153
1153
|
| 'admin.deployment.delete'
|
|
1154
1154
|
| 'admin.domain.delete'
|
|
1155
1155
|
| 'admin.billing.sync'
|
|
@@ -1224,8 +1224,8 @@ export interface ActivityMeta {
|
|
|
1224
1224
|
wasVerified?: boolean;
|
|
1225
1225
|
/** Previous deployment ID before relinking */
|
|
1226
1226
|
previousDeployment?: string;
|
|
1227
|
-
/**
|
|
1228
|
-
|
|
1227
|
+
/** Labels that were set/updated */
|
|
1228
|
+
labels?: string[];
|
|
1229
1229
|
|
|
1230
1230
|
// Account events
|
|
1231
1231
|
/** OAuth provider name */
|
|
@@ -1421,59 +1421,59 @@ export function generateDomainUrl(domain: string): string {
|
|
|
1421
1421
|
}
|
|
1422
1422
|
|
|
1423
1423
|
// =============================================================================
|
|
1424
|
-
//
|
|
1424
|
+
// LABEL UTILITIES
|
|
1425
1425
|
// =============================================================================
|
|
1426
1426
|
|
|
1427
1427
|
/**
|
|
1428
|
-
*
|
|
1429
|
-
* These rules define the single source of truth for
|
|
1428
|
+
* Label validation constraints shared across UI and API.
|
|
1429
|
+
* These rules define the single source of truth for label validation.
|
|
1430
1430
|
*/
|
|
1431
|
-
export const
|
|
1432
|
-
/** Minimum
|
|
1431
|
+
export const LABEL_CONSTRAINTS = {
|
|
1432
|
+
/** Minimum label length in characters */
|
|
1433
1433
|
MIN_LENGTH: 3,
|
|
1434
|
-
/** Maximum
|
|
1434
|
+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
|
|
1435
1435
|
MAX_LENGTH: 25,
|
|
1436
|
-
/** Maximum number of
|
|
1436
|
+
/** Maximum number of labels allowed per resource */
|
|
1437
1437
|
MAX_COUNT: 10,
|
|
1438
|
-
/** Allowed separator characters between
|
|
1438
|
+
/** Allowed separator characters between label segments */
|
|
1439
1439
|
SEPARATORS: '._-',
|
|
1440
1440
|
} as const;
|
|
1441
1441
|
|
|
1442
1442
|
/**
|
|
1443
|
-
*
|
|
1443
|
+
* Label validation pattern.
|
|
1444
1444
|
* Must start and end with alphanumeric (a-z, 0-9).
|
|
1445
1445
|
* Can contain separators (. _ -) between segments, but not consecutive.
|
|
1446
1446
|
*
|
|
1447
1447
|
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
|
|
1448
1448
|
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
|
|
1449
1449
|
*/
|
|
1450
|
-
export const
|
|
1450
|
+
export const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
1451
1451
|
|
|
1452
1452
|
/**
|
|
1453
|
-
* Serialize
|
|
1453
|
+
* Serialize labels array to JSON string for database storage.
|
|
1454
1454
|
* Returns null for empty or undefined arrays.
|
|
1455
1455
|
*
|
|
1456
|
-
* @example
|
|
1457
|
-
* @example
|
|
1458
|
-
* @example
|
|
1456
|
+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
|
|
1457
|
+
* @example serializeLabels([]) → null
|
|
1458
|
+
* @example serializeLabels(undefined) → null
|
|
1459
1459
|
*/
|
|
1460
|
-
export function
|
|
1461
|
-
if (!
|
|
1462
|
-
return JSON.stringify(
|
|
1460
|
+
export function serializeLabels(labels: string[] | undefined): string | null {
|
|
1461
|
+
if (!labels || labels.length === 0) return null;
|
|
1462
|
+
return JSON.stringify(labels);
|
|
1463
1463
|
}
|
|
1464
1464
|
|
|
1465
1465
|
/**
|
|
1466
|
-
* Deserialize
|
|
1466
|
+
* Deserialize labels from JSON string to array.
|
|
1467
1467
|
* Returns undefined for null/empty strings.
|
|
1468
1468
|
*
|
|
1469
|
-
* @example
|
|
1470
|
-
* @example
|
|
1471
|
-
* @example
|
|
1469
|
+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
|
|
1470
|
+
* @example deserializeLabels(null) → undefined
|
|
1471
|
+
* @example deserializeLabels('') → undefined
|
|
1472
1472
|
*/
|
|
1473
|
-
export function
|
|
1474
|
-
if (!
|
|
1473
|
+
export function deserializeLabels(labelsJson: string | null): string[] | undefined {
|
|
1474
|
+
if (!labelsJson) return undefined;
|
|
1475
1475
|
try {
|
|
1476
|
-
const parsed = JSON.parse(
|
|
1476
|
+
const parsed = JSON.parse(labelsJson);
|
|
1477
1477
|
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
|
|
1478
1478
|
} catch {
|
|
1479
1479
|
return undefined;
|