Skip to content

Commit 4af246d

Browse files
tags 2 labels
1 parent 883923e commit 4af246d

4 files changed

Lines changed: 75 additions & 75 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ interface DeploymentResource {
9999
create: (input: DeployInput, options?: DeploymentCreateOptions) => Promise<Deployment>;
100100
list: () => Promise<DeploymentListResponse>;
101101
get: (id: string) => Promise<Deployment>;
102-
set: (id: string, options: { tags: string[] }) => Promise<Deployment>;
102+
set: (id: string, options: { labels: string[] }) => Promise<Deployment>;
103103
remove: (id: string) => Promise<void>;
104104
}
105105

106106
interface DomainResource {
107-
set: (name: string, options?: { deployment?: string; tags?: string[] }) => Promise<Domain>;
107+
set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<Domain>;
108108
list: () => Promise<DomainListResponse>;
109109
get: (name: string) => Promise<Domain>;
110110
remove: (name: string) => Promise<void>;
@@ -113,7 +113,7 @@ interface DomainResource {
113113
}
114114

115115
interface TokenResource {
116-
create: (options?: { ttl?: number; tags?: string[] }) => Promise<TokenCreateResponse>;
116+
create: (options?: { ttl?: number; labels?: string[] }) => Promise<TokenCreateResponse>;
117117
list: () => Promise<TokenListResponse>;
118118
remove: (token: string) => Promise<void>;
119119
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@shipstatic/types",
3-
"version": "0.4.25",
3+
"version": "0.5.0",
44
"description": "Shared types for Shipstatic platform",
55
"type": "module",
66
"main": "./dist/index.js",

src/index.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export interface Deployment {
3333
status: DeploymentStatusType; // Mutable - can be updated
3434
/** Whether deployment has configuration */
3535
readonly config?: boolean;
36-
/** Optional array of tags for categorization and filtering (lowercase, alphanumeric with separators) */
37-
tags?: string[];
36+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
37+
labels?: string[];
3838
/** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli') */
3939
readonly via?: string;
4040
/** The deployment URL */
@@ -91,8 +91,8 @@ export interface Domain {
9191
deployment: string | null; // Mutable - can be updated to point to different deployment
9292
/** Current domain status */
9393
status: DomainStatusType; // Mutable - can be updated
94-
/** Optional array of tags for categorization and filtering (lowercase, alphanumeric with separators) */
95-
tags?: string[];
94+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
95+
labels?: string[];
9696
/** The domain URL - internal (subdomain) or external (custom domain) */
9797
readonly url: string;
9898
/** Unix timestamp (seconds) when domain was created */
@@ -204,8 +204,8 @@ export interface Token {
204204
readonly account: string;
205205
/** Optional IP address locking for security */
206206
readonly ip?: string;
207-
/** Optional array of tags for categorization and filtering (lowercase, alphanumeric with separators) */
208-
tags?: string[];
207+
/** Optional array of labels for categorization and filtering (lowercase, alphanumeric with separators) */
208+
labels?: string[];
209209
/** Unix timestamp (seconds) when token was created */
210210
readonly created: number;
211211
/** Unix timestamp (seconds) when token expires, or null for never */
@@ -1003,8 +1003,8 @@ export type DeployInput = File[] | string | string[];
10031003
* SDK implementations may extend with additional options (timeout, signal, callbacks, etc.).
10041004
*/
10051005
export interface DeploymentCreateOptions {
1006-
/** Optional tags for categorization and filtering */
1007-
tags?: string[];
1006+
/** Optional labels for categorization and filtering */
1007+
labels?: string[];
10081008
/** Optional subdomain suggestion for the deployment */
10091009
subdomain?: string;
10101010
/** Client identifier (e.g., 'cli', 'sdk', 'web') */
@@ -1018,15 +1018,15 @@ export interface DeploymentResource {
10181018
create: (input: DeployInput, options?: DeploymentCreateOptions) => Promise<Deployment>;
10191019
list: () => Promise<DeploymentListResponse>;
10201020
get: (id: string) => Promise<Deployment>;
1021-
set: (id: string, options: { tags: string[] }) => Promise<Deployment>;
1021+
set: (id: string, options: { labels: string[] }) => Promise<Deployment>;
10221022
remove: (id: string) => Promise<void>;
10231023
}
10241024

10251025
/**
10261026
* Domain resource interface - the contract all implementations must follow
10271027
*/
10281028
export interface DomainResource {
1029-
set: (name: string, options?: { deployment?: string; tags?: string[] }) => Promise<Domain>;
1029+
set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<Domain>;
10301030
list: () => Promise<DomainListResponse>;
10311031
get: (name: string) => Promise<Domain>;
10321032
remove: (name: string) => Promise<void>;
@@ -1048,7 +1048,7 @@ export interface AccountResource {
10481048
* Token resource interface - the contract all implementations must follow
10491049
*/
10501050
export interface TokenResource {
1051-
create: (options?: { ttl?: number; tags?: string[] }) => Promise<TokenCreateResponse>;
1051+
create: (options?: { ttl?: number; labels?: string[] }) => Promise<TokenCreateResponse>;
10521052
list: () => Promise<TokenListResponse>;
10531053
remove: (token: string) => Promise<void>;
10541054
}
@@ -1149,7 +1149,7 @@ export type ActivityEvent =
11491149
| 'admin.account.plan.update'
11501150
| 'admin.account.ref.update'
11511151
| 'admin.account.billing.update'
1152-
| 'admin.account.tags.update'
1152+
| 'admin.account.labels.update'
11531153
| 'admin.deployment.delete'
11541154
| 'admin.domain.delete'
11551155
| 'admin.billing.sync'
@@ -1224,8 +1224,8 @@ export interface ActivityMeta {
12241224
wasVerified?: boolean;
12251225
/** Previous deployment ID before relinking */
12261226
previousDeployment?: string;
1227-
/** Tags that were set/updated */
1228-
tags?: string[];
1227+
/** Labels that were set/updated */
1228+
labels?: string[];
12291229

12301230
// Account events
12311231
/** OAuth provider name */
@@ -1421,59 +1421,59 @@ export function generateDomainUrl(domain: string): string {
14211421
}
14221422

14231423
// =============================================================================
1424-
// TAG UTILITIES
1424+
// LABEL UTILITIES
14251425
// =============================================================================
14261426

14271427
/**
1428-
* Tag validation constraints shared across UI and API.
1429-
* These rules define the single source of truth for tag validation.
1428+
* Label validation constraints shared across UI and API.
1429+
* These rules define the single source of truth for label validation.
14301430
*/
1431-
export const TAG_CONSTRAINTS = {
1432-
/** Minimum tag length in characters */
1431+
export const LABEL_CONSTRAINTS = {
1432+
/** Minimum label length in characters */
14331433
MIN_LENGTH: 3,
1434-
/** Maximum tag length in characters (concise tags, matches Stack Overflow's original limit) */
1434+
/** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */
14351435
MAX_LENGTH: 25,
1436-
/** Maximum number of tags allowed per resource */
1436+
/** Maximum number of labels allowed per resource */
14371437
MAX_COUNT: 10,
1438-
/** Allowed separator characters between tag segments */
1438+
/** Allowed separator characters between label segments */
14391439
SEPARATORS: '._-',
14401440
} as const;
14411441

14421442
/**
1443-
* Tag validation pattern.
1443+
* Label validation pattern.
14441444
* Must start and end with alphanumeric (a-z, 0-9).
14451445
* Can contain separators (. _ -) between segments, but not consecutive.
14461446
*
14471447
* Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'
14481448
* Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)
14491449
*/
1450-
export const TAG_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
1450+
export const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
14511451

14521452
/**
1453-
* Serialize tags array to JSON string for database storage.
1453+
* Serialize labels array to JSON string for database storage.
14541454
* Returns null for empty or undefined arrays.
14551455
*
1456-
* @example serializeTags(['web', 'production']) → '["web","production"]'
1457-
* @example serializeTags([]) → null
1458-
* @example serializeTags(undefined) → null
1456+
* @example serializeLabels(['web', 'production']) → '["web","production"]'
1457+
* @example serializeLabels([]) → null
1458+
* @example serializeLabels(undefined) → null
14591459
*/
1460-
export function serializeTags(tags: string[] | undefined): string | null {
1461-
if (!tags || tags.length === 0) return null;
1462-
return JSON.stringify(tags);
1460+
export function serializeLabels(labels: string[] | undefined): string | null {
1461+
if (!labels || labels.length === 0) return null;
1462+
return JSON.stringify(labels);
14631463
}
14641464

14651465
/**
1466-
* Deserialize tags from JSON string to array.
1466+
* Deserialize labels from JSON string to array.
14671467
* Returns undefined for null/empty strings.
14681468
*
1469-
* @example deserializeTags('["web","production"]') → ['web', 'production']
1470-
* @example deserializeTags(null) → undefined
1471-
* @example deserializeTags('') → undefined
1469+
* @example deserializeLabels('["web","production"]') → ['web', 'production']
1470+
* @example deserializeLabels(null) → undefined
1471+
* @example deserializeLabels('') → undefined
14721472
*/
1473-
export function deserializeTags(tagsJson: string | null): string[] | undefined {
1474-
if (!tagsJson) return undefined;
1473+
export function deserializeLabels(labelsJson: string | null): string[] | undefined {
1474+
if (!labelsJson) return undefined;
14751475
try {
1476-
const parsed = JSON.parse(tagsJson);
1476+
const parsed = JSON.parse(labelsJson);
14771477
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
14781478
} catch {
14791479
return undefined;

tests/validation-constants.test.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
ALLOWED_MIME_TYPES,
44
isAllowedMimeType,
55
FileValidationStatus,
6-
TAG_PATTERN,
7-
TAG_CONSTRAINTS,
6+
LABEL_PATTERN,
7+
LABEL_CONSTRAINTS,
88
type FileValidationStatusType
99
} from '../src/index';
1010

@@ -199,8 +199,8 @@ describe('Validation Constants - @shipstatic/types', () => {
199199
});
200200
});
201201

202-
describe('TAG_PATTERN', () => {
203-
it('should match valid tags', () => {
202+
describe('LABEL_PATTERN', () => {
203+
it('should match valid labels', () => {
204204
const valid = [
205205
'prod',
206206
'staging',
@@ -211,12 +211,12 @@ describe('Validation Constants - @shipstatic/types', () => {
211211
'release.candidate',
212212
];
213213

214-
valid.forEach(tag => {
215-
expect(TAG_PATTERN.test(tag)).toBe(true);
214+
valid.forEach(label => {
215+
expect(LABEL_PATTERN.test(label)).toBe(true);
216216
});
217217
});
218218

219-
it('should reject invalid tag formats', () => {
219+
it('should reject invalid label formats', () => {
220220
// Pattern checks FORMAT only, not length
221221
const invalidFormat = [
222222
'-prod', // Starts with separator
@@ -225,61 +225,61 @@ describe('Validation Constants - @shipstatic/types', () => {
225225
'PROD', // Uppercase
226226
];
227227

228-
invalidFormat.forEach(tag => {
229-
expect(TAG_PATTERN.test(tag)).toBe(false);
228+
invalidFormat.forEach(label => {
229+
expect(LABEL_PATTERN.test(label)).toBe(false);
230230
});
231231
});
232232

233233
it('should not enforce length constraints in pattern', () => {
234-
// Pattern allows any length - length is enforced separately via TAG_CONSTRAINTS
235-
expect(TAG_PATTERN.test('a')).toBe(true); // 1 char - passes pattern
236-
expect(TAG_PATTERN.test('ab')).toBe(true); // 2 chars - passes pattern
237-
expect(TAG_PATTERN.test('abc')).toBe(true); // 3 chars (min) - passes pattern
234+
// Pattern allows any length - length is enforced separately via LABEL_CONSTRAINTS
235+
expect(LABEL_PATTERN.test('a')).toBe(true); // 1 char - passes pattern
236+
expect(LABEL_PATTERN.test('ab')).toBe(true); // 2 chars - passes pattern
237+
expect(LABEL_PATTERN.test('abc')).toBe(true); // 3 chars (min) - passes pattern
238238

239-
// Very long tags pass pattern (but would fail length validation)
240-
expect(TAG_PATTERN.test('a'.repeat(100))).toBe(true);
239+
// Very long labels pass pattern (but would fail length validation)
240+
expect(LABEL_PATTERN.test('a'.repeat(100))).toBe(true);
241241
});
242242

243243

244244
it('should handle separator variations', () => {
245245
// Pattern should allow these separators
246-
expect(TAG_PATTERN.test('my-tag')).toBe(true); // hyphen
247-
expect(TAG_PATTERN.test('my_tag')).toBe(true); // underscore
248-
expect(TAG_PATTERN.test('my.tag')).toBe(true); // dot
246+
expect(LABEL_PATTERN.test('my-label')).toBe(true); // hyphen
247+
expect(LABEL_PATTERN.test('my_label')).toBe(true); // underscore
248+
expect(LABEL_PATTERN.test('my.label')).toBe(true); // dot
249249
});
250250
});
251251

252-
describe('TAG_CONSTRAINTS', () => {
252+
describe('LABEL_CONSTRAINTS', () => {
253253
it('should define min length', () => {
254-
expect(TAG_CONSTRAINTS.MIN_LENGTH).toBeGreaterThan(0);
255-
expect(typeof TAG_CONSTRAINTS.MIN_LENGTH).toBe('number');
254+
expect(LABEL_CONSTRAINTS.MIN_LENGTH).toBeGreaterThan(0);
255+
expect(typeof LABEL_CONSTRAINTS.MIN_LENGTH).toBe('number');
256256
});
257257

258258
it('should define max length', () => {
259-
expect(TAG_CONSTRAINTS.MAX_LENGTH).toBeGreaterThan(TAG_CONSTRAINTS.MIN_LENGTH);
260-
expect(typeof TAG_CONSTRAINTS.MAX_LENGTH).toBe('number');
259+
expect(LABEL_CONSTRAINTS.MAX_LENGTH).toBeGreaterThan(LABEL_CONSTRAINTS.MIN_LENGTH);
260+
expect(typeof LABEL_CONSTRAINTS.MAX_LENGTH).toBe('number');
261261
});
262262

263263
it('should define max count', () => {
264-
expect(TAG_CONSTRAINTS.MAX_COUNT).toBeGreaterThan(0);
265-
expect(typeof TAG_CONSTRAINTS.MAX_COUNT).toBe('number');
264+
expect(LABEL_CONSTRAINTS.MAX_COUNT).toBeGreaterThan(0);
265+
expect(typeof LABEL_CONSTRAINTS.MAX_COUNT).toBe('number');
266266
});
267267

268268
it('should define allowed separators', () => {
269-
expect(TAG_CONSTRAINTS.SEPARATORS).toBeDefined();
270-
expect(TAG_CONSTRAINTS.SEPARATORS.length).toBeGreaterThan(0);
269+
expect(LABEL_CONSTRAINTS.SEPARATORS).toBeDefined();
270+
expect(LABEL_CONSTRAINTS.SEPARATORS.length).toBeGreaterThan(0);
271271
});
272272

273273
it('should have reasonable limits', () => {
274274
// Sanity checks
275-
expect(TAG_CONSTRAINTS.MIN_LENGTH).toBeGreaterThanOrEqual(1);
276-
expect(TAG_CONSTRAINTS.MIN_LENGTH).toBeLessThanOrEqual(10);
275+
expect(LABEL_CONSTRAINTS.MIN_LENGTH).toBeGreaterThanOrEqual(1);
276+
expect(LABEL_CONSTRAINTS.MIN_LENGTH).toBeLessThanOrEqual(10);
277277

278-
expect(TAG_CONSTRAINTS.MAX_LENGTH).toBeGreaterThanOrEqual(10);
279-
expect(TAG_CONSTRAINTS.MAX_LENGTH).toBeLessThanOrEqual(100);
278+
expect(LABEL_CONSTRAINTS.MAX_LENGTH).toBeGreaterThanOrEqual(10);
279+
expect(LABEL_CONSTRAINTS.MAX_LENGTH).toBeLessThanOrEqual(100);
280280

281-
expect(TAG_CONSTRAINTS.MAX_COUNT).toBeGreaterThanOrEqual(1);
282-
expect(TAG_CONSTRAINTS.MAX_COUNT).toBeLessThanOrEqual(100);
281+
expect(LABEL_CONSTRAINTS.MAX_COUNT).toBeGreaterThanOrEqual(1);
282+
expect(LABEL_CONSTRAINTS.MAX_COUNT).toBeLessThanOrEqual(100);
283283
});
284284
});
285285
});

0 commit comments

Comments
 (0)