@shipstatic/types 0.5.1 → 0.5.3

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 CHANGED
@@ -25,19 +25,17 @@ export interface Deployment {
25
25
  /** Current deployment status */
26
26
  status: DeploymentStatusType;
27
27
  /** Whether deployment has configuration */
28
- readonly config?: boolean;
29
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
30
- labels?: string[];
31
- /** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli') */
32
- readonly via?: string;
28
+ readonly config: boolean;
29
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
30
+ labels: string[];
31
+ /** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli'), null if unknown */
32
+ readonly via: string | null;
33
33
  /** The deployment URL */
34
34
  readonly url: string;
35
35
  /** Unix timestamp (seconds) when deployment was created */
36
36
  readonly created: number;
37
- /** Unix timestamp (seconds) when deployment expires */
38
- expires?: number;
39
- /** Short-lived JWT token for claiming this deployment (only present for public deployments) */
40
- claimToken?: string;
37
+ /** Unix timestamp (seconds) when deployment expires, null if never */
38
+ expires: number | null;
41
39
  }
42
40
  /**
43
41
  * Response for listing deployments
@@ -45,10 +43,10 @@ export interface Deployment {
45
43
  export interface DeploymentListResponse {
46
44
  /** Array of deployments */
47
45
  deployments: Deployment[];
48
- /** Optional cursor for pagination */
49
- cursor?: string;
50
- /** Total number of deployments if available */
51
- total?: number;
46
+ /** Cursor for pagination, null if no more pages */
47
+ cursor: string | null;
48
+ /** Total number of deployments */
49
+ total: number;
52
50
  }
53
51
  /**
54
52
  * Domain status constants
@@ -75,18 +73,16 @@ export interface Domain {
75
73
  deployment: string | null;
76
74
  /** Current domain status */
77
75
  status: DomainStatusType;
78
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
79
- labels?: string[];
76
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
77
+ labels: string[];
80
78
  /** The domain URL - internal (subdomain) or external (custom domain) */
81
79
  readonly url: string;
82
80
  /** Unix timestamp (seconds) when domain was created */
83
81
  readonly created: number;
84
- /** Whether this was a create (201) or update (200) operation */
85
- readonly isCreate?: boolean;
86
- /** When deployment was last linked (Unix timestamp, null if never) */
87
- linked?: number;
82
+ /** When deployment was last linked (Unix timestamp), null if never linked */
83
+ linked: number | null;
88
84
  /** Total deployment links */
89
- links?: number;
85
+ links: number;
90
86
  }
91
87
  /**
92
88
  * Response for listing domains
@@ -94,10 +90,10 @@ export interface Domain {
94
90
  export interface DomainListResponse {
95
91
  /** Array of domains */
96
92
  domains: Domain[];
97
- /** Optional cursor for pagination */
98
- cursor?: string;
99
- /** Total number of domains if available */
100
- total?: number;
93
+ /** Cursor for pagination, null if no more pages */
94
+ cursor: string | null;
95
+ /** Total number of domains */
96
+ total: number;
101
97
  }
102
98
  /**
103
99
  * DNS record types supported for domain configuration
@@ -118,8 +114,8 @@ export interface DnsRecord {
118
114
  * DNS provider information for a domain
119
115
  */
120
116
  export interface DnsProvider {
121
- /** Provider name (e.g., "Cloudflare", "GoDaddy") */
122
- name?: string;
117
+ /** Provider name (e.g., "Cloudflare", "GoDaddy"), null if unknown */
118
+ name: string | null;
123
119
  }
124
120
  /**
125
121
  * Response for domain DNS provider lookup
@@ -149,23 +145,12 @@ export interface DomainRecordsResponse {
149
145
  export interface DomainValidateResponse {
150
146
  /** Whether the domain is valid */
151
147
  valid: boolean;
152
- /** Normalized domain name (only present when valid) */
153
- normalized?: string;
154
- /** Whether the domain is available (only present when valid) */
155
- available?: boolean;
156
- /** Error message (only present when invalid) */
157
- error?: string;
158
- }
159
- /**
160
- * Response for deployment removal
161
- */
162
- export interface DeploymentRemoveResponse {
163
- /** Operation success status */
164
- success: boolean;
165
- /** The deployment ID */
166
- deployment: string;
167
- /** Human-readable message */
168
- message?: string;
148
+ /** Normalized domain name, null when invalid */
149
+ normalized: string | null;
150
+ /** Whether the domain is available, null when invalid */
151
+ available: boolean | null;
152
+ /** Error message, null when valid */
153
+ error: string | null;
169
154
  }
170
155
  /**
171
156
  * Deployment token for automated deployments
@@ -175,25 +160,41 @@ export interface Token {
175
160
  readonly token: string;
176
161
  /** The account this token belongs to */
177
162
  readonly account: string;
178
- /** Optional IP address locking for security */
179
- readonly ip?: string;
180
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
181
- labels?: string[];
163
+ /** IP address locking for security, null if not locked */
164
+ readonly ip: string | null;
165
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
166
+ labels: string[];
182
167
  /** Unix timestamp (seconds) when token was created */
183
168
  readonly created: number;
184
- /** Unix timestamp (seconds) when token expires, or null for never */
185
- readonly expires?: number;
186
- /** Unix timestamp (seconds) when token was last used */
187
- readonly used?: number;
169
+ /** Unix timestamp (seconds) when token expires, null for never */
170
+ readonly expires: number | null;
171
+ /** Unix timestamp (seconds) when token was last used, null if never used */
172
+ readonly used: number | null;
173
+ }
174
+ /**
175
+ * Token as returned by the list endpoint.
176
+ * Security-redacted: shows truncated prefix instead of full hash, omits account.
177
+ */
178
+ export interface TokenListItem {
179
+ /** Truncated token prefix for identification (e.g., "hash12345678...") */
180
+ readonly token: string;
181
+ /** Labels for categorization and filtering. Always present, empty array when none. */
182
+ labels: string[];
183
+ /** Unix timestamp (seconds) when token was created */
184
+ readonly created: number;
185
+ /** Unix timestamp (seconds) when token expires, null for never */
186
+ readonly expires: number | null;
187
+ /** Unix timestamp (seconds) when token was last used, null if never used */
188
+ readonly used: number | null;
188
189
  }
189
190
  /**
190
191
  * Response for listing tokens
191
192
  */
192
193
  export interface TokenListResponse {
193
- /** Array of tokens */
194
- tokens: Token[];
195
- /** Total count of tokens */
196
- count?: number;
194
+ /** Array of tokens (security-redacted for list display) */
195
+ tokens: TokenListItem[];
196
+ /** Total number of tokens */
197
+ total: number;
197
198
  }
198
199
  /**
199
200
  * Response for token creation
@@ -201,10 +202,10 @@ export interface TokenListResponse {
201
202
  export interface TokenCreateResponse {
202
203
  /** The actual token value (only returned on creation) */
203
204
  token: string;
204
- /** Unix timestamp (seconds) when token expires, or null for never */
205
- expires?: number;
206
- /** Success message */
207
- message?: string;
205
+ /** Labels for categorization and filtering. Always present, empty array when none. */
206
+ labels: string[];
207
+ /** Unix timestamp (seconds) when token expires, null for never */
208
+ expires: number | null;
208
209
  }
209
210
  /**
210
211
  * Account plan constants
@@ -226,18 +227,18 @@ export type AccountPlanType = typeof AccountPlan[keyof typeof AccountPlan];
226
227
  export interface Account {
227
228
  /** User email address */
228
229
  readonly email: string;
229
- /** User display name */
230
- readonly name: string;
231
- /** User profile picture URL */
232
- readonly picture?: string;
230
+ /** User display name, null if not set */
231
+ readonly name: string | null;
232
+ /** User profile picture URL, null if not set */
233
+ readonly picture: string | null;
233
234
  /** Account plan status */
234
235
  readonly plan: AccountPlanType;
235
236
  /** Unix timestamp (seconds) when account was created */
236
237
  readonly created: number;
237
- /** Unix timestamp (seconds) when account was activated (first deployment) */
238
- readonly activated?: number;
239
- /** Last 4 characters of the API key for identification (null when no key generated) */
240
- readonly hint?: string;
238
+ /** Unix timestamp (seconds) when account was activated (first deployment), null if not yet activated */
239
+ readonly activated: number | null;
240
+ /** Last 4 characters of the API key for identification, null when no key generated */
241
+ readonly hint: string | null;
241
242
  }
242
243
  /**
243
244
  * Account-specific configuration overrides
@@ -433,15 +434,6 @@ export declare const ALLOWED_MIME_TYPES: readonly ["text/html", "text/css", "tex
433
434
  * isAllowedMimeType('application/wasm') // false (not allowed)
434
435
  */
435
436
  export declare function isAllowedMimeType(mimeType: string): boolean;
436
- /**
437
- * Generic success response wrapper
438
- */
439
- export interface SuccessResponse<T = any> {
440
- /** Always true for success */
441
- success: true;
442
- /** Response data */
443
- data: T;
444
- }
445
437
  /**
446
438
  * Simple ping response for health checks
447
439
  */
@@ -653,14 +645,14 @@ export interface TokenResource {
653
645
  export interface BillingStatus {
654
646
  /** Creem billing ID, or null if no active billing */
655
647
  billing: string | null;
656
- /** Number of billing units (1 unit = 1 custom domain) */
657
- units?: number;
658
- /** Number of custom domains currently in use */
659
- usage?: number;
660
- /** Billing status from Creem (active, trialing, canceled, etc.) */
661
- status?: string;
662
- /** Link to Creem customer portal for billing management */
663
- portal?: string | null;
648
+ /** Number of billing units (1 unit = 1 custom domain), null if no billing */
649
+ units: number | null;
650
+ /** Number of custom domains currently in use, null if no billing */
651
+ usage: number | null;
652
+ /** Billing status from Creem (active, trialing, canceled, etc.), null if no billing */
653
+ status: string | null;
654
+ /** Link to Creem customer portal for billing management, null if unavailable */
655
+ portal: string | null;
664
656
  }
665
657
  /**
666
658
  * Checkout session response from POST /billing/checkout
@@ -786,17 +778,6 @@ export interface ValidationIssue {
786
778
  /** Display-ready message explaining the issue */
787
779
  message: string;
788
780
  }
789
- /**
790
- * Legacy validation error structure
791
- *
792
- * @deprecated Use ValidationIssue[] from FileValidationResult instead
793
- */
794
- export interface ValidationError {
795
- error: string;
796
- details: string;
797
- errors: string[];
798
- isClientError: true;
799
- }
800
781
  /**
801
782
  * Minimal file interface required for validation
802
783
  */
@@ -804,7 +785,7 @@ export interface ValidatableFile {
804
785
  name: string;
805
786
  size: number;
806
787
  type: string;
807
- status?: string;
788
+ status?: FileValidationStatusType;
808
789
  statusMessage?: string;
809
790
  }
810
791
  /**
@@ -919,10 +900,10 @@ export declare const LABEL_PATTERN: RegExp;
919
900
  export declare function serializeLabels(labels: string[] | undefined): string | null;
920
901
  /**
921
902
  * Deserialize labels from JSON string to array.
922
- * Returns undefined for null/empty strings.
903
+ * Always returns an array — empty array for null/empty/invalid input.
923
904
  *
924
905
  * @example deserializeLabels('["web","production"]') → ['web', 'production']
925
- * @example deserializeLabels(null) → undefined
926
- * @example deserializeLabels('') → undefined
906
+ * @example deserializeLabels(null) → []
907
+ * @example deserializeLabels('') → []
927
908
  */
928
- export declare function deserializeLabels(labelsJson: string | null): string[] | undefined;
909
+ export declare function deserializeLabels(labelsJson: string | null): string[];
package/dist/index.js CHANGED
@@ -605,20 +605,20 @@ export function serializeLabels(labels) {
605
605
  }
606
606
  /**
607
607
  * Deserialize labels from JSON string to array.
608
- * Returns undefined for null/empty strings.
608
+ * Always returns an array — empty array for null/empty/invalid input.
609
609
  *
610
610
  * @example deserializeLabels('["web","production"]') → ['web', 'production']
611
- * @example deserializeLabels(null) → undefined
612
- * @example deserializeLabels('') → undefined
611
+ * @example deserializeLabels(null) → []
612
+ * @example deserializeLabels('') → []
613
613
  */
614
614
  export function deserializeLabels(labelsJson) {
615
615
  if (!labelsJson)
616
- return undefined;
616
+ return [];
617
617
  try {
618
618
  const parsed = JSON.parse(labelsJson);
619
- return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
619
+ return Array.isArray(parsed) ? parsed : [];
620
620
  }
621
621
  catch {
622
- return undefined;
622
+ return [];
623
623
  }
624
624
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Shared types for Shipstatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -32,19 +32,17 @@ export interface Deployment {
32
32
  /** Current deployment status */
33
33
  status: DeploymentStatusType; // Mutable - can be updated
34
34
  /** Whether deployment has configuration */
35
- readonly config?: boolean;
36
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
37
- labels?: string[];
38
- /** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli') */
39
- readonly via?: string;
35
+ readonly config: boolean;
36
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
37
+ labels: string[];
38
+ /** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli'), null if unknown */
39
+ readonly via: string | null;
40
40
  /** The deployment URL */
41
41
  readonly url: string;
42
42
  /** Unix timestamp (seconds) when deployment was created */
43
43
  readonly created: number;
44
- /** Unix timestamp (seconds) when deployment expires */
45
- expires?: number; // Mutable - can be updated
46
- /** Short-lived JWT token for claiming this deployment (only present for public deployments) */
47
- claimToken?: string; // Mutable - can be updated
44
+ /** Unix timestamp (seconds) when deployment expires, null if never */
45
+ expires: number | null; // Mutable - can be updated
48
46
  }
49
47
 
50
48
 
@@ -54,10 +52,10 @@ export interface Deployment {
54
52
  export interface DeploymentListResponse {
55
53
  /** Array of deployments */
56
54
  deployments: Deployment[];
57
- /** Optional cursor for pagination */
58
- cursor?: string;
59
- /** Total number of deployments if available */
60
- total?: number;
55
+ /** Cursor for pagination, null if no more pages */
56
+ cursor: string | null;
57
+ /** Total number of deployments */
58
+ total: number;
61
59
  }
62
60
 
63
61
  // =============================================================================
@@ -91,18 +89,16 @@ export interface Domain {
91
89
  deployment: string | null; // Mutable - can be updated to point to different deployment
92
90
  /** Current domain status */
93
91
  status: DomainStatusType; // Mutable - can be updated
94
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
95
- labels?: string[];
92
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
93
+ labels: string[];
96
94
  /** The domain URL - internal (subdomain) or external (custom domain) */
97
95
  readonly url: string;
98
96
  /** Unix timestamp (seconds) when domain was created */
99
97
  readonly created: number;
100
- /** Whether this was a create (201) or update (200) operation */
101
- readonly isCreate?: boolean;
102
- /** When deployment was last linked (Unix timestamp, null if never) */
103
- linked?: number;
98
+ /** When deployment was last linked (Unix timestamp), null if never linked */
99
+ linked: number | null;
104
100
  /** Total deployment links */
105
- links?: number;
101
+ links: number;
106
102
  }
107
103
 
108
104
  /**
@@ -111,10 +107,10 @@ export interface Domain {
111
107
  export interface DomainListResponse {
112
108
  /** Array of domains */
113
109
  domains: Domain[];
114
- /** Optional cursor for pagination */
115
- cursor?: string;
116
- /** Total number of domains if available */
117
- total?: number;
110
+ /** Cursor for pagination, null if no more pages */
111
+ cursor: string | null;
112
+ /** Total number of domains */
113
+ total: number;
118
114
  }
119
115
 
120
116
  /**
@@ -138,8 +134,8 @@ export interface DnsRecord {
138
134
  * DNS provider information for a domain
139
135
  */
140
136
  export interface DnsProvider {
141
- /** Provider name (e.g., "Cloudflare", "GoDaddy") */
142
- name?: string;
137
+ /** Provider name (e.g., "Cloudflare", "GoDaddy"), null if unknown */
138
+ name: string | null;
143
139
  }
144
140
 
145
141
  /**
@@ -170,24 +166,12 @@ export interface DomainRecordsResponse {
170
166
  export interface DomainValidateResponse {
171
167
  /** Whether the domain is valid */
172
168
  valid: boolean;
173
- /** Normalized domain name (only present when valid) */
174
- normalized?: string;
175
- /** Whether the domain is available (only present when valid) */
176
- available?: boolean;
177
- /** Error message (only present when invalid) */
178
- error?: string;
179
- }
180
-
181
- /**
182
- * Response for deployment removal
183
- */
184
- export interface DeploymentRemoveResponse {
185
- /** Operation success status */
186
- success: boolean;
187
- /** The deployment ID */
188
- deployment: string;
189
- /** Human-readable message */
190
- message?: string;
169
+ /** Normalized domain name, null when invalid */
170
+ normalized: string | null;
171
+ /** Whether the domain is available, null when invalid */
172
+ available: boolean | null;
173
+ /** Error message, null when valid */
174
+ error: string | null;
191
175
  }
192
176
 
193
177
  // =============================================================================
@@ -202,26 +186,43 @@ export interface Token {
202
186
  readonly token: string;
203
187
  /** The account this token belongs to */
204
188
  readonly account: string;
205
- /** Optional IP address locking for security */
206
- readonly ip?: string;
207
- /** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
208
- labels?: string[];
189
+ /** IP address locking for security, null if not locked */
190
+ readonly ip: string | null;
191
+ /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
192
+ labels: string[];
209
193
  /** Unix timestamp (seconds) when token was created */
210
194
  readonly created: number;
211
- /** Unix timestamp (seconds) when token expires, or null for never */
212
- readonly expires?: number;
213
- /** Unix timestamp (seconds) when token was last used */
214
- readonly used?: number;
195
+ /** Unix timestamp (seconds) when token expires, null for never */
196
+ readonly expires: number | null;
197
+ /** Unix timestamp (seconds) when token was last used, null if never used */
198
+ readonly used: number | null;
199
+ }
200
+
201
+ /**
202
+ * Token as returned by the list endpoint.
203
+ * Security-redacted: shows truncated prefix instead of full hash, omits account.
204
+ */
205
+ export interface TokenListItem {
206
+ /** Truncated token prefix for identification (e.g., "hash12345678...") */
207
+ readonly token: string;
208
+ /** Labels for categorization and filtering. Always present, empty array when none. */
209
+ labels: string[];
210
+ /** Unix timestamp (seconds) when token was created */
211
+ readonly created: number;
212
+ /** Unix timestamp (seconds) when token expires, null for never */
213
+ readonly expires: number | null;
214
+ /** Unix timestamp (seconds) when token was last used, null if never used */
215
+ readonly used: number | null;
215
216
  }
216
217
 
217
218
  /**
218
219
  * Response for listing tokens
219
220
  */
220
221
  export interface TokenListResponse {
221
- /** Array of tokens */
222
- tokens: Token[];
223
- /** Total count of tokens */
224
- count?: number;
222
+ /** Array of tokens (security-redacted for list display) */
223
+ tokens: TokenListItem[];
224
+ /** Total number of tokens */
225
+ total: number;
225
226
  }
226
227
 
227
228
  /**
@@ -230,10 +231,10 @@ export interface TokenListResponse {
230
231
  export interface TokenCreateResponse {
231
232
  /** The actual token value (only returned on creation) */
232
233
  token: string;
233
- /** Unix timestamp (seconds) when token expires, or null for never */
234
- expires?: number;
235
- /** Success message */
236
- message?: string;
234
+ /** Labels for categorization and filtering. Always present, empty array when none. */
235
+ labels: string[];
236
+ /** Unix timestamp (seconds) when token expires, null for never */
237
+ expires: number | null;
237
238
  }
238
239
 
239
240
  // =============================================================================
@@ -262,18 +263,18 @@ export type AccountPlanType = typeof AccountPlan[keyof typeof AccountPlan];
262
263
  export interface Account {
263
264
  /** User email address */
264
265
  readonly email: string;
265
- /** User display name */
266
- readonly name: string;
267
- /** User profile picture URL */
268
- readonly picture?: string;
266
+ /** User display name, null if not set */
267
+ readonly name: string | null;
268
+ /** User profile picture URL, null if not set */
269
+ readonly picture: string | null;
269
270
  /** Account plan status */
270
271
  readonly plan: AccountPlanType;
271
272
  /** Unix timestamp (seconds) when account was created */
272
273
  readonly created: number;
273
- /** Unix timestamp (seconds) when account was activated (first deployment) */
274
- readonly activated?: number;
275
- /** Last 4 characters of the API key for identification (null when no key generated) */
276
- readonly hint?: string;
274
+ /** Unix timestamp (seconds) when account was activated (first deployment), null if not yet activated */
275
+ readonly activated: number | null;
276
+ /** Last 4 characters of the API key for identification, null when no key generated */
277
+ readonly hint: string | null;
277
278
  }
278
279
 
279
280
  /**
@@ -755,16 +756,6 @@ export function isAllowedMimeType(mimeType: string): boolean {
755
756
  // COMMON RESPONSE PATTERNS
756
757
  // =============================================================================
757
758
 
758
- /**
759
- * Generic success response wrapper
760
- */
761
- export interface SuccessResponse<T = any> {
762
- /** Always true for success */
763
- success: true;
764
- /** Response data */
765
- data: T;
766
- }
767
-
768
759
  /**
769
760
  * Simple ping response for health checks
770
761
  */
@@ -1071,14 +1062,14 @@ export interface TokenResource {
1071
1062
  export interface BillingStatus {
1072
1063
  /** Creem billing ID, or null if no active billing */
1073
1064
  billing: string | null;
1074
- /** Number of billing units (1 unit = 1 custom domain) */
1075
- units?: number;
1076
- /** Number of custom domains currently in use */
1077
- usage?: number;
1078
- /** Billing status from Creem (active, trialing, canceled, etc.) */
1079
- status?: string;
1080
- /** Link to Creem customer portal for billing management */
1081
- portal?: string | null;
1065
+ /** Number of billing units (1 unit = 1 custom domain), null if no billing */
1066
+ units: number | null;
1067
+ /** Number of custom domains currently in use, null if no billing */
1068
+ usage: number | null;
1069
+ /** Billing status from Creem (active, trialing, canceled, etc.), null if no billing */
1070
+ status: string | null;
1071
+ /** Link to Creem customer portal for billing management, null if unavailable */
1072
+ portal: string | null;
1082
1073
  }
1083
1074
 
1084
1075
 
@@ -1293,18 +1284,6 @@ export interface ValidationIssue {
1293
1284
  message: string;
1294
1285
  }
1295
1286
 
1296
- /**
1297
- * Legacy validation error structure
1298
- *
1299
- * @deprecated Use ValidationIssue[] from FileValidationResult instead
1300
- */
1301
- export interface ValidationError {
1302
- error: string;
1303
- details: string;
1304
- errors: string[];
1305
- isClientError: true;
1306
- }
1307
-
1308
1287
  /**
1309
1288
  * Minimal file interface required for validation
1310
1289
  */
@@ -1312,7 +1291,7 @@ export interface ValidatableFile {
1312
1291
  name: string;
1313
1292
  size: number;
1314
1293
  type: string;
1315
- status?: string;
1294
+ status?: FileValidationStatusType;
1316
1295
  statusMessage?: string;
1317
1296
  }
1318
1297
 
@@ -1467,18 +1446,18 @@ export function serializeLabels(labels: string[] | undefined): string | null {
1467
1446
 
1468
1447
  /**
1469
1448
  * Deserialize labels from JSON string to array.
1470
- * Returns undefined for null/empty strings.
1449
+ * Always returns an array — empty array for null/empty/invalid input.
1471
1450
  *
1472
1451
  * @example deserializeLabels('["web","production"]') → ['web', 'production']
1473
- * @example deserializeLabels(null) → undefined
1474
- * @example deserializeLabels('') → undefined
1452
+ * @example deserializeLabels(null) → []
1453
+ * @example deserializeLabels('') → []
1475
1454
  */
1476
- export function deserializeLabels(labelsJson: string | null): string[] | undefined {
1477
- if (!labelsJson) return undefined;
1455
+ export function deserializeLabels(labelsJson: string | null): string[] {
1456
+ if (!labelsJson) return [];
1478
1457
  try {
1479
1458
  const parsed = JSON.parse(labelsJson);
1480
- return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
1459
+ return Array.isArray(parsed) ? parsed : [];
1481
1460
  } catch {
1482
- return undefined;
1461
+ return [];
1483
1462
  }
1484
1463
  }