@shipstatic/types 0.8.10 → 0.9.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/README.md CHANGED
@@ -21,7 +21,7 @@ npm install @shipstatic/types
21
21
  ```typescript
22
22
  import type {
23
23
  Deployment, DeploymentListResponse,
24
- Domain, DomainListResponse, DnsRecord, DomainDnsResponse, DomainRecordsResponse, DomainValidateResponse,
24
+ Domain, DomainSetResult, DomainListResponse, DnsRecord, DomainDnsResponse, DomainRecordsResponse, DomainValidateResponse,
25
25
  Token, TokenListItem, TokenListResponse, TokenCreateResponse,
26
26
  Account, AccountUsage, AccountOverrides,
27
27
  StaticFile
@@ -62,7 +62,7 @@ import {
62
62
 
63
63
  ```typescript
64
64
  import type {
65
- ConfigResponse,
65
+ PlatformLimits, // plan-based caps from /config (file size, file count, total size)
66
66
  BillingStatus,
67
67
  CheckoutSession,
68
68
  ActivityListResponse,
@@ -80,8 +80,6 @@ import type {
80
80
  DomainResource,
81
81
  AccountResource,
82
82
  TokenResource,
83
- BillingResource,
84
- KeysResource,
85
83
  } from '@shipstatic/types';
86
84
  ```
87
85
 
@@ -138,8 +136,8 @@ import {
138
136
  ```typescript
139
137
  import {
140
138
  DEFAULT_API,
141
- API_KEY_PREFIX,
142
- DEPLOY_TOKEN_PREFIX,
139
+ API_KEY, // { PREFIX, HEX_LENGTH, TOTAL_LENGTH, HINT_LENGTH }
140
+ DEPLOY_TOKEN, // { PREFIX, HEX_LENGTH, TOTAL_LENGTH }
143
141
  DEPLOYMENT_CONFIG_FILENAME,
144
142
  } from '@shipstatic/types';
145
143
  ```
package/dist/index.d.ts CHANGED
@@ -94,6 +94,20 @@ export interface Domain {
94
94
  /** Total deployment links */
95
95
  links: number;
96
96
  }
97
+ /**
98
+ * Return shape of `domains.set()` — `Domain` plus an SDK-derived flag indicating
99
+ * whether the underlying `PUT /domains/:name` created the record (HTTP 201) or
100
+ * updated an existing one (HTTP 200).
101
+ *
102
+ * `isCreate` is not part of the wire format — the API returns a plain `Domain`
103
+ * body. The SDK derives the flag from the HTTP status code so callers (notably
104
+ * the CLI) can format different output for the create vs repoint paths without
105
+ * a second round-trip.
106
+ */
107
+ export interface DomainSetResult extends Domain {
108
+ /** `true` when this call created a new domain; `false` when it updated an existing one. */
109
+ isCreate: boolean;
110
+ }
97
111
  /**
98
112
  * Response for listing domains
99
113
  */
@@ -282,31 +296,37 @@ export interface AccountOverrides {
282
296
  totalSize?: number;
283
297
  }
284
298
  /**
285
- * All possible error types in the ShipStatic platform
286
- * Names are developer-friendly while wire format stays consistent
299
+ * All possible error types in the ShipStatic platform.
300
+ *
301
+ * Developer-friendly key names map to stable wire-format string values.
302
+ * Both the value and the type are exported under the same name so callers
303
+ * can use `ErrorType.Validation` (value comparison) and `: ErrorType` (type
304
+ * annotation) without ceremony — matching the pattern other status objects
305
+ * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.
287
306
  */
288
- export declare enum ErrorType {
307
+ export declare const ErrorType: {
289
308
  /** Validation failed (400) */
290
- Validation = "validation_failed",
309
+ readonly Validation: "validation_failed";
291
310
  /** Resource not found (404) */
292
- NotFound = "not_found",
311
+ readonly NotFound: "not_found";
293
312
  /** Rate limit exceeded (429) */
294
- RateLimit = "rate_limit_exceeded",
313
+ readonly RateLimit: "rate_limit_exceeded";
295
314
  /** Authentication required (401) */
296
- Authentication = "authentication_failed",
315
+ readonly Authentication: "authentication_failed";
297
316
  /** Business logic error (400) */
298
- Business = "business_logic_error",
299
- /** API server error (500) - renamed from Internal for clarity */
300
- Api = "internal_server_error",
317
+ readonly Business: "business_logic_error";
318
+ /** API server error (500) */
319
+ readonly Api: "internal_server_error";
301
320
  /** Network/connection error */
302
- Network = "network_error",
321
+ readonly Network: "network_error";
303
322
  /** Operation was cancelled */
304
- Cancelled = "operation_cancelled",
323
+ readonly Cancelled: "operation_cancelled";
305
324
  /** File operation error */
306
- File = "file_error",
325
+ readonly File: "file_error";
307
326
  /** Configuration error */
308
- Config = "config_error"
309
- }
327
+ readonly Config: "config_error";
328
+ };
329
+ export type ErrorType = typeof ErrorType[keyof typeof ErrorType];
310
330
  /**
311
331
  * Standard error response format used everywhere
312
332
  */
@@ -366,12 +386,22 @@ export declare class ShipError extends Error {
366
386
  */
367
387
  export declare function isShipError(error: unknown): error is ShipError;
368
388
  /**
369
- * Dynamic platform configuration returned by the /config endpoint.
370
- * Contains plan-based limits that vary by account.
389
+ * Plan-based platform limits returned by the `/config` endpoint.
390
+ *
391
+ * The SDK fetches these once on first API call to drive client-side
392
+ * file-size / file-count / total-size validation that mirrors what the API
393
+ * would enforce server-side. Limits vary by account plan.
394
+ *
395
+ * Distinct from `ResolvedConfig` (which carries the *client's* credentials
396
+ * and API URL after defaulting); this one carries the *platform's* posted
397
+ * caps for the current account.
371
398
  */
372
- export interface ConfigResponse {
399
+ export interface PlatformLimits {
400
+ /** Maximum size in bytes for a single file. */
373
401
  maxFileSize: number;
402
+ /** Maximum number of files in a single deployment. */
374
403
  maxFilesCount: number;
404
+ /** Maximum total size in bytes across all files in a deployment. */
375
405
  maxTotalSize: number;
376
406
  }
377
407
  /**
@@ -443,13 +473,32 @@ export interface PingResponse {
443
473
  /** Optional timestamp */
444
474
  timestamp?: number;
445
475
  }
446
- export declare const API_KEY_PREFIX = "ship-";
447
- export declare const API_KEY_HEX_LENGTH = 64;
448
- export declare const API_KEY_TOTAL_LENGTH: number;
449
- export declare const API_KEY_HINT_LENGTH = 4;
450
- export declare const DEPLOY_TOKEN_PREFIX = "token-";
451
- export declare const DEPLOY_TOKEN_HEX_LENGTH = 64;
452
- export declare const DEPLOY_TOKEN_TOTAL_LENGTH: number;
476
+ /**
477
+ * Shape constants for API keys (`ship-{64 hex chars}`).
478
+ * Single source of truth used by validation utilities and auth middleware.
479
+ */
480
+ export declare const API_KEY: {
481
+ /** Prefix that identifies an API key. */
482
+ readonly PREFIX: "ship-";
483
+ /** Number of hex characters following the prefix. */
484
+ readonly HEX_LENGTH: 64;
485
+ /** Total length of an API key including prefix (`PREFIX.length + HEX_LENGTH = 69`). */
486
+ readonly TOTAL_LENGTH: 69;
487
+ /** Number of trailing characters used to display a redacted hint (e.g. last 4). */
488
+ readonly HINT_LENGTH: 4;
489
+ };
490
+ /**
491
+ * Shape constants for deploy tokens (`token-{64 hex chars}`).
492
+ * Single source of truth used by validation utilities and auth middleware.
493
+ */
494
+ export declare const DEPLOY_TOKEN: {
495
+ /** Prefix that identifies a deploy token. */
496
+ readonly PREFIX: "token-";
497
+ /** Number of hex characters following the prefix. */
498
+ readonly HEX_LENGTH: 64;
499
+ /** Total length of a deploy token including prefix (`PREFIX.length + HEX_LENGTH = 70`). */
500
+ readonly TOTAL_LENGTH: 70;
501
+ };
453
502
  export declare const AuthMethod: {
454
503
  readonly JWT: "jwt";
455
504
  readonly API_KEY: "apiKey";
@@ -536,23 +585,21 @@ export interface StaticFile {
536
585
  size: number;
537
586
  }
538
587
  /**
539
- * Standard platform configuration format used by all clients
540
- */
541
- export interface PlatformConfig {
542
- apiUrl?: string;
543
- deployToken?: string;
544
- apiKey?: string;
545
- }
546
- /**
547
- * Resolved configuration with required apiUrl.
548
- * This is the normalized config after merging options, env, and config files.
588
+ * Resolved client configuration with `apiUrl` defaulted.
589
+ *
590
+ * Produced by the SDK after layering its credential sources (constructor
591
+ * options on top of `SHIP_*` env vars in Node; constructor options only in
592
+ * Browser) and applying the `DEFAULT_API` fallback. File-based sources
593
+ * (`.shiprc`, `package.json` `"ship"` key) are the CLI's responsibility and
594
+ * are merged in *before* construction — by the time a `ResolvedConfig`
595
+ * exists, every source has already collapsed into the constructor argument.
549
596
  */
550
597
  export interface ResolvedConfig {
551
- /** API URL (always present after resolution, defaults to DEFAULT_API) */
598
+ /** API URL always present after resolution, defaults to `DEFAULT_API`. */
552
599
  apiUrl: string;
553
- /** API key for authenticated deployments */
600
+ /** API key for authenticated deployments. */
554
601
  apiKey?: string;
555
- /** Deploy token for single-use deployments */
602
+ /** Deploy token for single-use deployments. */
556
603
  deployToken?: string;
557
604
  }
558
605
  /**
@@ -572,10 +619,14 @@ export interface ProgressInfo {
572
619
  /** Default API URL if not otherwise configured. */
573
620
  export declare const DEFAULT_API = "https://api.shipstatic.com";
574
621
  /**
575
- * Deploy input type - environment-specific
622
+ * Universal deploy input the union of every shape the SDK accepts.
623
+ *
624
+ * - **Browser**: `File[]` (typically from `<input type="file">` or drag-and-drop)
625
+ * - **Node**: `string | string[]` (file or directory path(s) on disk; directories are walked)
576
626
  *
577
- * Browser: File[] - array of File objects
578
- * Node.js: string | string[] - file/directory paths
627
+ * Each platform's SDK narrows its `deploy()` signature to the relevant shape
628
+ * and rejects anything else at runtime. Use the structural types directly
629
+ * (`File[]`, `string | string[]`) when writing platform-specific code.
579
630
  */
580
631
  export type DeployInput = File[] | string | string[];
581
632
  /**
@@ -623,7 +674,7 @@ export interface DomainResource {
623
674
  set: (name: string, options?: {
624
675
  deployment?: string;
625
676
  labels?: string[];
626
- }) => Promise<Domain>;
677
+ }) => Promise<DomainSetResult>;
627
678
  list: () => Promise<DomainListResponse>;
628
679
  get: (name: string) => Promise<Domain>;
629
680
  remove: (name: string) => Promise<void>;
@@ -680,32 +731,6 @@ export interface CheckoutSession {
680
731
  /** URL to redirect user to Creem checkout page */
681
732
  url: string;
682
733
  }
683
- /**
684
- * Billing resource interface - the contract all implementations must follow
685
- *
686
- * IMPOSSIBLE SIMPLICITY: No sync() method needed!
687
- * Webhooks are the single source of truth. Frontend just polls status().
688
- */
689
- export interface BillingResource {
690
- /**
691
- * Create a checkout session
692
- * @returns Checkout session with URL to redirect user
693
- */
694
- checkout: () => Promise<CheckoutSession>;
695
- /**
696
- * Get current billing status
697
- * @returns Billing status and usage information
698
- */
699
- status: () => Promise<BillingStatus>;
700
- }
701
- /**
702
- * Keys resource interface - the contract all implementations must follow
703
- */
704
- export interface KeysResource {
705
- create: () => Promise<{
706
- apiKey: string;
707
- }>;
708
- }
709
734
  /**
710
735
  * All activity event types logged in the system.
711
736
  * Uses dot notation consistently: {resource}.{action}
package/dist/index.js CHANGED
@@ -50,34 +50,40 @@ export const AccountPlan = {
50
50
  // ERROR SYSTEM
51
51
  // =============================================================================
52
52
  /**
53
- * All possible error types in the ShipStatic platform
54
- * Names are developer-friendly while wire format stays consistent
53
+ * All possible error types in the ShipStatic platform.
54
+ *
55
+ * Developer-friendly key names map to stable wire-format string values.
56
+ * Both the value and the type are exported under the same name so callers
57
+ * can use `ErrorType.Validation` (value comparison) and `: ErrorType` (type
58
+ * annotation) without ceremony — matching the pattern other status objects
59
+ * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.
55
60
  */
56
- export var ErrorType;
57
- (function (ErrorType) {
61
+ export const ErrorType = {
58
62
  /** Validation failed (400) */
59
- ErrorType["Validation"] = "validation_failed";
63
+ Validation: 'validation_failed',
60
64
  /** Resource not found (404) */
61
- ErrorType["NotFound"] = "not_found";
65
+ NotFound: 'not_found',
62
66
  /** Rate limit exceeded (429) */
63
- ErrorType["RateLimit"] = "rate_limit_exceeded";
67
+ RateLimit: 'rate_limit_exceeded',
64
68
  /** Authentication required (401) */
65
- ErrorType["Authentication"] = "authentication_failed";
69
+ Authentication: 'authentication_failed',
66
70
  /** Business logic error (400) */
67
- ErrorType["Business"] = "business_logic_error";
68
- /** API server error (500) - renamed from Internal for clarity */
69
- ErrorType["Api"] = "internal_server_error";
71
+ Business: 'business_logic_error',
72
+ /** API server error (500) */
73
+ Api: 'internal_server_error',
70
74
  /** Network/connection error */
71
- ErrorType["Network"] = "network_error";
75
+ Network: 'network_error',
72
76
  /** Operation was cancelled */
73
- ErrorType["Cancelled"] = "operation_cancelled";
77
+ Cancelled: 'operation_cancelled',
74
78
  /** File operation error */
75
- ErrorType["File"] = "file_error";
79
+ File: 'file_error',
76
80
  /** Configuration error */
77
- ErrorType["Config"] = "config_error";
78
- })(ErrorType || (ErrorType = {}));
81
+ Config: 'config_error',
82
+ };
79
83
  /**
80
- * Categorizes error types for better type checking
84
+ * Categorizes error types for the `isClientError` / `isNetworkError` /
85
+ * `isAuthError` helpers. Each `Set` is typed against the wider `ErrorType`
86
+ * union so `.has(error.type)` accepts any value from the union.
81
87
  */
82
88
  const ERROR_CATEGORIES = {
83
89
  client: new Set([ErrorType.Business, ErrorType.Config, ErrorType.File, ErrorType.Validation]),
@@ -299,15 +305,32 @@ export function hasUnbuiltMarker(filePath) {
299
305
  const segments = filePath.replace(/\\/g, '/').split('/').filter(Boolean);
300
306
  return segments.some(s => UNBUILT_PROJECT_MARKERS.has(s));
301
307
  }
302
- // API Key Configuration
303
- export const API_KEY_PREFIX = 'ship-';
304
- export const API_KEY_HEX_LENGTH = 64;
305
- export const API_KEY_TOTAL_LENGTH = API_KEY_PREFIX.length + API_KEY_HEX_LENGTH; // 69
306
- export const API_KEY_HINT_LENGTH = 4;
307
- // Deploy Token Configuration
308
- export const DEPLOY_TOKEN_PREFIX = 'token-';
309
- export const DEPLOY_TOKEN_HEX_LENGTH = 64;
310
- export const DEPLOY_TOKEN_TOTAL_LENGTH = DEPLOY_TOKEN_PREFIX.length + DEPLOY_TOKEN_HEX_LENGTH; // 70
308
+ /**
309
+ * Shape constants for API keys (`ship-{64 hex chars}`).
310
+ * Single source of truth used by validation utilities and auth middleware.
311
+ */
312
+ export const API_KEY = {
313
+ /** Prefix that identifies an API key. */
314
+ PREFIX: 'ship-',
315
+ /** Number of hex characters following the prefix. */
316
+ HEX_LENGTH: 64,
317
+ /** Total length of an API key including prefix (`PREFIX.length + HEX_LENGTH = 69`). */
318
+ TOTAL_LENGTH: 69,
319
+ /** Number of trailing characters used to display a redacted hint (e.g. last 4). */
320
+ HINT_LENGTH: 4,
321
+ };
322
+ /**
323
+ * Shape constants for deploy tokens (`token-{64 hex chars}`).
324
+ * Single source of truth used by validation utilities and auth middleware.
325
+ */
326
+ export const DEPLOY_TOKEN = {
327
+ /** Prefix that identifies a deploy token. */
328
+ PREFIX: 'token-',
329
+ /** Number of hex characters following the prefix. */
330
+ HEX_LENGTH: 64,
331
+ /** Total length of a deploy token including prefix (`PREFIX.length + HEX_LENGTH = 70`). */
332
+ TOTAL_LENGTH: 70,
333
+ };
311
334
  // Authentication Method Constants
312
335
  export const AuthMethod = {
313
336
  JWT: 'jwt',
@@ -327,30 +350,30 @@ export const SPA_DEFAULT_CONFIG = { rewrites: [{ source: '/(.*)', destination: '
327
350
  * Validate API key format
328
351
  */
329
352
  export function validateApiKey(apiKey) {
330
- if (!apiKey.startsWith(API_KEY_PREFIX)) {
331
- throw ShipError.validation(`API key must start with "${API_KEY_PREFIX}"`);
353
+ if (!apiKey.startsWith(API_KEY.PREFIX)) {
354
+ throw ShipError.validation(`API key must start with "${API_KEY.PREFIX}"`);
332
355
  }
333
- if (apiKey.length !== API_KEY_TOTAL_LENGTH) {
334
- throw ShipError.validation(`API key must be ${API_KEY_TOTAL_LENGTH} characters total (${API_KEY_PREFIX} + ${API_KEY_HEX_LENGTH} hex chars)`);
356
+ if (apiKey.length !== API_KEY.TOTAL_LENGTH) {
357
+ throw ShipError.validation(`API key must be ${API_KEY.TOTAL_LENGTH} characters total (${API_KEY.PREFIX} + ${API_KEY.HEX_LENGTH} hex chars)`);
335
358
  }
336
- const hexPart = apiKey.slice(API_KEY_PREFIX.length);
359
+ const hexPart = apiKey.slice(API_KEY.PREFIX.length);
337
360
  if (!/^[a-f0-9]{64}$/i.test(hexPart)) {
338
- throw ShipError.validation(`API key must contain ${API_KEY_HEX_LENGTH} hexadecimal characters after "${API_KEY_PREFIX}" prefix`);
361
+ throw ShipError.validation(`API key must contain ${API_KEY.HEX_LENGTH} hexadecimal characters after "${API_KEY.PREFIX}" prefix`);
339
362
  }
340
363
  }
341
364
  /**
342
365
  * Validate deploy token format
343
366
  */
344
367
  export function validateDeployToken(deployToken) {
345
- if (!deployToken.startsWith(DEPLOY_TOKEN_PREFIX)) {
346
- throw ShipError.validation(`Deploy token must start with "${DEPLOY_TOKEN_PREFIX}"`);
368
+ if (!deployToken.startsWith(DEPLOY_TOKEN.PREFIX)) {
369
+ throw ShipError.validation(`Deploy token must start with "${DEPLOY_TOKEN.PREFIX}"`);
347
370
  }
348
- if (deployToken.length !== DEPLOY_TOKEN_TOTAL_LENGTH) {
349
- throw ShipError.validation(`Deploy token must be ${DEPLOY_TOKEN_TOTAL_LENGTH} characters total (${DEPLOY_TOKEN_PREFIX} + ${DEPLOY_TOKEN_HEX_LENGTH} hex chars)`);
371
+ if (deployToken.length !== DEPLOY_TOKEN.TOTAL_LENGTH) {
372
+ throw ShipError.validation(`Deploy token must be ${DEPLOY_TOKEN.TOTAL_LENGTH} characters total (${DEPLOY_TOKEN.PREFIX} + ${DEPLOY_TOKEN.HEX_LENGTH} hex chars)`);
350
373
  }
351
- const hexPart = deployToken.slice(DEPLOY_TOKEN_PREFIX.length);
374
+ const hexPart = deployToken.slice(DEPLOY_TOKEN.PREFIX.length);
352
375
  if (!/^[a-f0-9]{64}$/i.test(hexPart)) {
353
- throw ShipError.validation(`Deploy token must contain ${DEPLOY_TOKEN_HEX_LENGTH} hexadecimal characters after "${DEPLOY_TOKEN_PREFIX}" prefix`);
376
+ throw ShipError.validation(`Deploy token must contain ${DEPLOY_TOKEN.HEX_LENGTH} hexadecimal characters after "${DEPLOY_TOKEN.PREFIX}" prefix`);
354
377
  }
355
378
  }
356
379
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.8.10",
3
+ "version": "0.9.1",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -112,6 +112,21 @@ export interface Domain {
112
112
  links: number;
113
113
  }
114
114
 
115
+ /**
116
+ * Return shape of `domains.set()` — `Domain` plus an SDK-derived flag indicating
117
+ * whether the underlying `PUT /domains/:name` created the record (HTTP 201) or
118
+ * updated an existing one (HTTP 200).
119
+ *
120
+ * `isCreate` is not part of the wire format — the API returns a plain `Domain`
121
+ * body. The SDK derives the flag from the HTTP status code so callers (notably
122
+ * the CLI) can format different output for the create vs repoint paths without
123
+ * a second round-trip.
124
+ */
125
+ export interface DomainSetResult extends Domain {
126
+ /** `true` when this call created a new domain; `false` when it updated an existing one. */
127
+ isCreate: boolean;
128
+ }
129
+
115
130
  /**
116
131
  * Response for listing domains
117
132
  */
@@ -326,39 +341,48 @@ export interface AccountOverrides {
326
341
  // =============================================================================
327
342
 
328
343
  /**
329
- * All possible error types in the ShipStatic platform
330
- * Names are developer-friendly while wire format stays consistent
344
+ * All possible error types in the ShipStatic platform.
345
+ *
346
+ * Developer-friendly key names map to stable wire-format string values.
347
+ * Both the value and the type are exported under the same name so callers
348
+ * can use `ErrorType.Validation` (value comparison) and `: ErrorType` (type
349
+ * annotation) without ceremony — matching the pattern other status objects
350
+ * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.
331
351
  */
332
- export enum ErrorType {
352
+ export const ErrorType = {
333
353
  /** Validation failed (400) */
334
- Validation = "validation_failed",
354
+ Validation: 'validation_failed',
335
355
  /** Resource not found (404) */
336
- NotFound = "not_found",
356
+ NotFound: 'not_found',
337
357
  /** Rate limit exceeded (429) */
338
- RateLimit = "rate_limit_exceeded",
358
+ RateLimit: 'rate_limit_exceeded',
339
359
  /** Authentication required (401) */
340
- Authentication = "authentication_failed",
360
+ Authentication: 'authentication_failed',
341
361
  /** Business logic error (400) */
342
- Business = "business_logic_error",
343
- /** API server error (500) - renamed from Internal for clarity */
344
- Api = "internal_server_error",
362
+ Business: 'business_logic_error',
363
+ /** API server error (500) */
364
+ Api: 'internal_server_error',
345
365
  /** Network/connection error */
346
- Network = "network_error",
366
+ Network: 'network_error',
347
367
  /** Operation was cancelled */
348
- Cancelled = "operation_cancelled",
368
+ Cancelled: 'operation_cancelled',
349
369
  /** File operation error */
350
- File = "file_error",
370
+ File: 'file_error',
351
371
  /** Configuration error */
352
- Config = "config_error"
353
- }
372
+ Config: 'config_error',
373
+ } as const;
374
+
375
+ export type ErrorType = typeof ErrorType[keyof typeof ErrorType];
354
376
 
355
377
  /**
356
- * Categorizes error types for better type checking
378
+ * Categorizes error types for the `isClientError` / `isNetworkError` /
379
+ * `isAuthError` helpers. Each `Set` is typed against the wider `ErrorType`
380
+ * union so `.has(error.type)` accepts any value from the union.
357
381
  */
358
382
  const ERROR_CATEGORIES = {
359
- client: new Set([ErrorType.Business, ErrorType.Config, ErrorType.File, ErrorType.Validation]),
360
- network: new Set([ErrorType.Network]),
361
- auth: new Set([ErrorType.Authentication]),
383
+ client: new Set<ErrorType>([ErrorType.Business, ErrorType.Config, ErrorType.File, ErrorType.Validation]),
384
+ network: new Set<ErrorType>([ErrorType.Network]),
385
+ auth: new Set<ErrorType>([ErrorType.Authentication]),
362
386
  } as const;
363
387
 
364
388
  /**
@@ -521,12 +545,22 @@ export function isShipError(error: unknown): error is ShipError {
521
545
  // =============================================================================
522
546
 
523
547
  /**
524
- * Dynamic platform configuration returned by the /config endpoint.
525
- * Contains plan-based limits that vary by account.
548
+ * Plan-based platform limits returned by the `/config` endpoint.
549
+ *
550
+ * The SDK fetches these once on first API call to drive client-side
551
+ * file-size / file-count / total-size validation that mirrors what the API
552
+ * would enforce server-side. Limits vary by account plan.
553
+ *
554
+ * Distinct from `ResolvedConfig` (which carries the *client's* credentials
555
+ * and API URL after defaulting); this one carries the *platform's* posted
556
+ * caps for the current account.
526
557
  */
527
- export interface ConfigResponse {
558
+ export interface PlatformLimits {
559
+ /** Maximum size in bytes for a single file. */
528
560
  maxFileSize: number;
561
+ /** Maximum number of files in a single deployment. */
529
562
  maxFilesCount: number;
563
+ /** Maximum total size in bytes across all files in a deployment. */
530
564
  maxTotalSize: number;
531
565
  }
532
566
 
@@ -652,16 +686,33 @@ export interface PingResponse {
652
686
  timestamp?: number;
653
687
  }
654
688
 
655
- // API Key Configuration
656
- export const API_KEY_PREFIX = 'ship-';
657
- export const API_KEY_HEX_LENGTH = 64;
658
- export const API_KEY_TOTAL_LENGTH = API_KEY_PREFIX.length + API_KEY_HEX_LENGTH; // 69
659
- export const API_KEY_HINT_LENGTH = 4;
689
+ /**
690
+ * Shape constants for API keys (`ship-{64 hex chars}`).
691
+ * Single source of truth used by validation utilities and auth middleware.
692
+ */
693
+ export const API_KEY = {
694
+ /** Prefix that identifies an API key. */
695
+ PREFIX: 'ship-',
696
+ /** Number of hex characters following the prefix. */
697
+ HEX_LENGTH: 64,
698
+ /** Total length of an API key including prefix (`PREFIX.length + HEX_LENGTH = 69`). */
699
+ TOTAL_LENGTH: 69,
700
+ /** Number of trailing characters used to display a redacted hint (e.g. last 4). */
701
+ HINT_LENGTH: 4,
702
+ } as const;
660
703
 
661
- // Deploy Token Configuration
662
- export const DEPLOY_TOKEN_PREFIX = 'token-';
663
- export const DEPLOY_TOKEN_HEX_LENGTH = 64;
664
- export const DEPLOY_TOKEN_TOTAL_LENGTH = DEPLOY_TOKEN_PREFIX.length + DEPLOY_TOKEN_HEX_LENGTH; // 70
704
+ /**
705
+ * Shape constants for deploy tokens (`token-{64 hex chars}`).
706
+ * Single source of truth used by validation utilities and auth middleware.
707
+ */
708
+ export const DEPLOY_TOKEN = {
709
+ /** Prefix that identifies a deploy token. */
710
+ PREFIX: 'token-',
711
+ /** Number of hex characters following the prefix. */
712
+ HEX_LENGTH: 64,
713
+ /** Total length of a deploy token including prefix (`PREFIX.length + HEX_LENGTH = 70`). */
714
+ TOTAL_LENGTH: 70,
715
+ } as const;
665
716
 
666
717
  // Authentication Method Constants
667
718
  export const AuthMethod = {
@@ -688,17 +739,17 @@ export const SPA_DEFAULT_CONFIG = { rewrites: [{ source: '/(.*)', destination: '
688
739
  * Validate API key format
689
740
  */
690
741
  export function validateApiKey(apiKey: string): void {
691
- if (!apiKey.startsWith(API_KEY_PREFIX)) {
692
- throw ShipError.validation(`API key must start with "${API_KEY_PREFIX}"`);
742
+ if (!apiKey.startsWith(API_KEY.PREFIX)) {
743
+ throw ShipError.validation(`API key must start with "${API_KEY.PREFIX}"`);
693
744
  }
694
745
 
695
- if (apiKey.length !== API_KEY_TOTAL_LENGTH) {
696
- throw ShipError.validation(`API key must be ${API_KEY_TOTAL_LENGTH} characters total (${API_KEY_PREFIX} + ${API_KEY_HEX_LENGTH} hex chars)`);
746
+ if (apiKey.length !== API_KEY.TOTAL_LENGTH) {
747
+ throw ShipError.validation(`API key must be ${API_KEY.TOTAL_LENGTH} characters total (${API_KEY.PREFIX} + ${API_KEY.HEX_LENGTH} hex chars)`);
697
748
  }
698
749
 
699
- const hexPart = apiKey.slice(API_KEY_PREFIX.length);
750
+ const hexPart = apiKey.slice(API_KEY.PREFIX.length);
700
751
  if (!/^[a-f0-9]{64}$/i.test(hexPart)) {
701
- throw ShipError.validation(`API key must contain ${API_KEY_HEX_LENGTH} hexadecimal characters after "${API_KEY_PREFIX}" prefix`);
752
+ throw ShipError.validation(`API key must contain ${API_KEY.HEX_LENGTH} hexadecimal characters after "${API_KEY.PREFIX}" prefix`);
702
753
  }
703
754
  }
704
755
 
@@ -706,17 +757,17 @@ export function validateApiKey(apiKey: string): void {
706
757
  * Validate deploy token format
707
758
  */
708
759
  export function validateDeployToken(deployToken: string): void {
709
- if (!deployToken.startsWith(DEPLOY_TOKEN_PREFIX)) {
710
- throw ShipError.validation(`Deploy token must start with "${DEPLOY_TOKEN_PREFIX}"`);
760
+ if (!deployToken.startsWith(DEPLOY_TOKEN.PREFIX)) {
761
+ throw ShipError.validation(`Deploy token must start with "${DEPLOY_TOKEN.PREFIX}"`);
711
762
  }
712
763
 
713
- if (deployToken.length !== DEPLOY_TOKEN_TOTAL_LENGTH) {
714
- throw ShipError.validation(`Deploy token must be ${DEPLOY_TOKEN_TOTAL_LENGTH} characters total (${DEPLOY_TOKEN_PREFIX} + ${DEPLOY_TOKEN_HEX_LENGTH} hex chars)`);
764
+ if (deployToken.length !== DEPLOY_TOKEN.TOTAL_LENGTH) {
765
+ throw ShipError.validation(`Deploy token must be ${DEPLOY_TOKEN.TOTAL_LENGTH} characters total (${DEPLOY_TOKEN.PREFIX} + ${DEPLOY_TOKEN.HEX_LENGTH} hex chars)`);
715
766
  }
716
767
 
717
- const hexPart = deployToken.slice(DEPLOY_TOKEN_PREFIX.length);
768
+ const hexPart = deployToken.slice(DEPLOY_TOKEN.PREFIX.length);
718
769
  if (!/^[a-f0-9]{64}$/i.test(hexPart)) {
719
- throw ShipError.validation(`Deploy token must contain ${DEPLOY_TOKEN_HEX_LENGTH} hexadecimal characters after "${DEPLOY_TOKEN_PREFIX}" prefix`);
770
+ throw ShipError.validation(`Deploy token must contain ${DEPLOY_TOKEN.HEX_LENGTH} hexadecimal characters after "${DEPLOY_TOKEN.PREFIX}" prefix`);
720
771
  }
721
772
  }
722
773
 
@@ -822,24 +873,21 @@ export interface StaticFile {
822
873
  // =============================================================================
823
874
 
824
875
  /**
825
- * Standard platform configuration format used by all clients
826
- */
827
- export interface PlatformConfig {
828
- apiUrl?: string;
829
- deployToken?: string;
830
- apiKey?: string;
831
- }
832
-
833
- /**
834
- * Resolved configuration with required apiUrl.
835
- * This is the normalized config after merging options, env, and config files.
876
+ * Resolved client configuration with `apiUrl` defaulted.
877
+ *
878
+ * Produced by the SDK after layering its credential sources (constructor
879
+ * options on top of `SHIP_*` env vars in Node; constructor options only in
880
+ * Browser) and applying the `DEFAULT_API` fallback. File-based sources
881
+ * (`.shiprc`, `package.json` `"ship"` key) are the CLI's responsibility and
882
+ * are merged in *before* construction — by the time a `ResolvedConfig`
883
+ * exists, every source has already collapsed into the constructor argument.
836
884
  */
837
885
  export interface ResolvedConfig {
838
- /** API URL (always present after resolution, defaults to DEFAULT_API) */
886
+ /** API URL always present after resolution, defaults to `DEFAULT_API`. */
839
887
  apiUrl: string;
840
- /** API key for authenticated deployments */
888
+ /** API key for authenticated deployments. */
841
889
  apiKey?: string;
842
- /** Deploy token for single-use deployments */
890
+ /** Deploy token for single-use deployments. */
843
891
  deployToken?: string;
844
892
  }
845
893
 
@@ -874,10 +922,14 @@ export const DEFAULT_API = 'https://api.shipstatic.com';
874
922
  // =============================================================================
875
923
 
876
924
  /**
877
- * Deploy input type - environment-specific
925
+ * Universal deploy input the union of every shape the SDK accepts.
878
926
  *
879
- * Browser: File[] - array of File objects
880
- * Node.js: string | string[] - file/directory paths
927
+ * - **Browser**: `File[]` (typically from `<input type="file">` or drag-and-drop)
928
+ * - **Node**: `string | string[]` (file or directory path(s) on disk; directories are walked)
929
+ *
930
+ * Each platform's SDK narrows its `deploy()` signature to the relevant shape
931
+ * and rejects anything else at runtime. Use the structural types directly
932
+ * (`File[]`, `string | string[]`) when writing platform-specific code.
881
933
  */
882
934
  export type DeployInput = File[] | string | string[];
883
935
 
@@ -923,7 +975,7 @@ export interface DeploymentResource {
923
975
  * Domain resource interface - the contract all implementations must follow
924
976
  */
925
977
  export interface DomainResource {
926
- set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<Domain>;
978
+ set: (name: string, options?: { deployment?: string; labels?: string[] }) => Promise<DomainSetResult>;
927
979
  list: () => Promise<DomainListResponse>;
928
980
  get: (name: string) => Promise<Domain>;
929
981
  remove: (name: string) => Promise<void>;
@@ -982,34 +1034,6 @@ export interface CheckoutSession {
982
1034
  url: string;
983
1035
  }
984
1036
 
985
- /**
986
- * Billing resource interface - the contract all implementations must follow
987
- *
988
- * IMPOSSIBLE SIMPLICITY: No sync() method needed!
989
- * Webhooks are the single source of truth. Frontend just polls status().
990
- */
991
- export interface BillingResource {
992
- /**
993
- * Create a checkout session
994
- * @returns Checkout session with URL to redirect user
995
- */
996
- checkout: () => Promise<CheckoutSession>;
997
-
998
- /**
999
- * Get current billing status
1000
- * @returns Billing status and usage information
1001
- */
1002
- status: () => Promise<BillingStatus>;
1003
- }
1004
-
1005
-
1006
- /**
1007
- * Keys resource interface - the contract all implementations must follow
1008
- */
1009
- export interface KeysResource {
1010
- create: () => Promise<{ apiKey: string }>;
1011
- }
1012
-
1013
1037
  // =============================================================================
1014
1038
  // ACTIVITY TYPES
1015
1039
  // =============================================================================