@shipstatic/types 2.5.0-beta.11 → 2.5.0-beta.12

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
@@ -20,13 +20,17 @@ npm install @shipstatic/types
20
20
 
21
21
  ```typescript
22
22
  import type {
23
- ListResponse,
24
- Deployment, DeploymentListResponse, DeploymentDeleteResponse,
25
- Domain, DomainSetResult, DomainListResponse, DnsRecord, DomainDnsResponse, DomainRecordsResponse, DomainValidateResponse, DomainDeleteResponse, DomainVerifyResponse,
26
- Token, TokenListResponse, TokenCreateResponse, TokenDeleteResponse,
23
+ ListResponse, ListOptions,
24
+ Deployment, DeploymentListResponse, DeploymentDeleteResponse, DeploymentSetOptions,
25
+ Domain, DomainSetResult, DomainSetOptions, DomainListResponse, DnsRecord, DnsLookup, DomainDnsResponse, DomainRecordsResponse, DomainShareResponse, DomainValidateResponse, DomainDeleteResponse, DomainVerifyResponse,
26
+ Token, TokenListResponse, TokenCreateResponse, TokenCreateOptions, TokenDeleteResponse,
27
27
  Account, AccountUsage, AccountOverrides, AccountDeleteResponse, AccountKeyResponse,
28
+ LabelsResponse, SetupInstructionsResponse,
28
29
  StaticFile
29
30
  } from '@shipstatic/types';
31
+
32
+ // Every public path, declared once — the API mounts from it, clients request against it.
33
+ import { API_PATHS } from '@shipstatic/types';
30
34
  ```
31
35
 
32
36
  A mutation answers with the resource it affected — the entity when it
package/dist/index.d.ts CHANGED
@@ -49,6 +49,49 @@ export interface DeploymentCreateResponse extends Deployment {
49
49
  /** Claim URL for public deployments. Present when deployed without credentials. */
50
50
  readonly claim?: string;
51
51
  }
52
+ /**
53
+ * Every path the public API answers on, declared once.
54
+ *
55
+ * The URL surface used to be written out in four places — the API's mounts,
56
+ * the SDK's client, the dashboard's client, and the post-deploy smoke — so a
57
+ * rename meant finding all four. Here it is one table that the producer
58
+ * mounts from and the consumers request against, which is the only way a
59
+ * path and its handler cannot drift apart.
60
+ *
61
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
62
+ * to `web/my`, for the same reason its row types do: this package is
63
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
64
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
65
+ * promise to one dashboard.
66
+ *
67
+ * Item paths are functions rather than templates so the key is interpolated
68
+ * in one place, encoded the same way by every caller.
69
+ */
70
+ export declare const API_PATHS: {
71
+ readonly DEPLOYMENTS: "/deployments";
72
+ readonly DEPLOYMENT: (deployment: string) => string;
73
+ readonly DEPLOYMENT_CONFIG: (deployment: string) => string;
74
+ readonly DOMAINS: "/domains";
75
+ readonly DOMAIN: (domain: string) => string;
76
+ readonly DOMAIN_VERIFY: (domain: string) => string;
77
+ readonly DOMAIN_DNS: (domain: string) => string;
78
+ readonly DOMAIN_RECORDS: (domain: string) => string;
79
+ readonly DOMAIN_SHARE: (domain: string) => string;
80
+ readonly DOMAIN_PROPAGATION: (domain: string) => string;
81
+ readonly DOMAINS_VALIDATE: "/domains/validate";
82
+ readonly TOKENS: "/tokens";
83
+ readonly TOKEN: (token: string) => string;
84
+ readonly ACCOUNT: "/account";
85
+ readonly ACCOUNT_KEY: "/account/key";
86
+ readonly ACCOUNT_CLAIM: "/account/claim";
87
+ readonly ACTIVITIES: "/activities";
88
+ readonly LABELS: "/labels";
89
+ readonly LIMITS: "/limits";
90
+ readonly PING: "/ping";
91
+ readonly SETUP: "/setup";
92
+ readonly SPA_CHECK: "/spa-check";
93
+ readonly UPLOAD: "/upload";
94
+ };
52
95
  /**
53
96
  * The half of a list response that is identical on every list.
54
97
  *
@@ -86,12 +129,23 @@ export interface DeploymentListResponse extends ListResponse {
86
129
  * Where the resource is simply gone, the key alone is the whole answer
87
130
  * ({@link DomainDeleteResponse}, {@link TokenDeleteResponse}).
88
131
  *
89
- * Nothing else rides along. No prose (`message`), because an acknowledgement
90
- * is data and each surface composes its own copy; and no constant
91
- * (`changed: true`, `queued: true`, `success: true`), because a field whose
92
- * value the type already fixes tells a caller nothing it did not know before
93
- * it made the request. Sync versus accepted is the HTTP status code's job —
94
- * 200 versus 202 — not a boolean's.
132
+ * Put positively: **an acknowledgement is a projection of the resource** —
133
+ * its key, plus its own state field where the state changed. That is the
134
+ * test to apply, and it is sharper than "no constant", which this shape
135
+ * would fail on its own terms: `status` here is the literal `'deleting'` on
136
+ * every success, exactly as fixed as a `changed: true` would be.
137
+ *
138
+ * The difference is not how predictable the value is, it is what the field
139
+ * IS. `status` is the deployment's own field — the same one `GET
140
+ * /deployments/:deployment` returns — so this response is `Deployment`
141
+ * narrowed to two members, and a client renders it with the code it already
142
+ * has. `changed: true`, `queued: true` and `success: true` are not fields of
143
+ * any entity; they exist only to assert that the call worked, which the
144
+ * status code already said. Sync versus accepted is likewise the status
145
+ * code's job — 200 versus 202 — not a boolean's.
146
+ *
147
+ * No prose either (`message`): an acknowledgement is data, and each surface
148
+ * composes its own copy.
95
149
  */
96
150
  export interface DeploymentDeleteResponse {
97
151
  /** The deployment hostname that was marked for removal */
@@ -200,13 +254,33 @@ export interface DnsProvider {
200
254
  /**
201
255
  * Response for domain DNS provider lookup
202
256
  */
257
+ /**
258
+ * What a DNS lookup found for a domain. An envelope rather than a bare
259
+ * {@link DnsProvider} because a lookup can succeed and learn more than the
260
+ * provider later; the shape is named so a consumer can hold one.
261
+ */
262
+ export interface DnsLookup {
263
+ /** The provider serving this domain's DNS, absent when unidentified */
264
+ provider?: DnsProvider;
265
+ }
203
266
  export interface DomainDnsResponse {
204
267
  /** The domain name */
205
268
  domain: string;
206
269
  /** DNS provider information, null if not yet looked up */
207
- dns: {
208
- provider?: DnsProvider;
209
- } | null;
270
+ dns: DnsLookup | null;
271
+ }
272
+ /**
273
+ * Response for `GET /domains/:domain/share` — the domain plus the salted
274
+ * hash that lets someone else complete its DNS setup without an account.
275
+ *
276
+ * `/admin/domains/:domain/share` answers the same shape, which is the admin
277
+ * law working: the operator surface is the public grammar with a prefix.
278
+ */
279
+ export interface DomainShareResponse {
280
+ /** The domain the setup link is for */
281
+ readonly domain: string;
282
+ /** The salted setup hash that authorizes the share */
283
+ readonly hash: string;
210
284
  }
211
285
  /**
212
286
  * Response for domain DNS records
@@ -219,6 +293,35 @@ export interface DomainRecordsResponse {
219
293
  /** Required DNS records for configuration */
220
294
  records: DnsRecord[];
221
295
  }
296
+ /**
297
+ * Response for `GET /labels` — every label in use across the caller's
298
+ * deployments, domains and tokens, grouped and ordered by last use.
299
+ *
300
+ * The one plural noun outside the list contract, deliberately: labels have
301
+ * no identity, no row and no `created`, so there is nothing for a keyset
302
+ * cursor to resume after, and its consumer is an autocomplete that wants the
303
+ * whole set. Bounded by `PAGINATION.GLOBAL_LIMIT` rather than paginated.
304
+ */
305
+ export interface LabelsResponse {
306
+ readonly labels: string[];
307
+ }
308
+ /**
309
+ * Response for `POST /setup` — the DNS instructions for one domain, written
310
+ * for a human to follow at their registrar.
311
+ *
312
+ * `custom` is the provider-specific walkthrough when the provider is known;
313
+ * `generic` always answers, so a caller never has nothing to show.
314
+ */
315
+ export interface SetupInstructionsResponse {
316
+ /** One-line summary of what to do */
317
+ readonly tldr: string;
318
+ /** Provider-specific instructions, null when the provider is unknown */
319
+ readonly custom: string | null;
320
+ /** Provider-agnostic instructions — always present */
321
+ readonly generic: string;
322
+ /** The identified DNS provider, null when unknown */
323
+ readonly provider: string | null;
324
+ }
222
325
  /**
223
326
  * Response for domain validation
224
327
  */
@@ -839,16 +942,22 @@ export interface SPACheckRequest {
839
942
  /**
840
943
  * Response from SPA check endpoint
841
944
  */
945
+ /**
946
+ * Which of the classifier's tiers reached the verdict, and why. Named rather
947
+ * than inline so the API's own `checkSPA` can return `SPACheckResponse`
948
+ * instead of restating its shape.
949
+ */
950
+ export interface SPACheckDebug {
951
+ /** Which tier made the detection */
952
+ tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
953
+ /** The reason for the detection result */
954
+ reason: string;
955
+ }
842
956
  export interface SPACheckResponse {
843
957
  /** Whether the project is detected as a Single Page Application */
844
958
  isSPA: boolean;
845
959
  /** Debugging information about detection */
846
- debug: {
847
- /** Which tier made the detection: 'exclusions', 'inclusions', 'scoring', 'ai', or 'fallback' */
848
- tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
849
- /** The reason for the detection result */
850
- reason: string;
851
- };
960
+ debug: SPACheckDebug;
852
961
  }
853
962
  /**
854
963
  * Represents a file that has been processed and is ready for deploy.
@@ -943,6 +1052,33 @@ export interface ListOptions {
943
1052
  /** Opaque cursor from the previous page's response. */
944
1053
  cursor?: string;
945
1054
  }
1055
+ /**
1056
+ * What a caller may change on an existing deployment.
1057
+ *
1058
+ * Labels and nothing else: a deployment's content is immutable by design, so
1059
+ * this is the whole mutable surface rather than a subset someone chose.
1060
+ */
1061
+ export interface DeploymentSetOptions {
1062
+ labels: string[];
1063
+ }
1064
+ /**
1065
+ * What `domains.set()` may create or change. Every field is optional because
1066
+ * the call is a natural-key upsert: omitting `deployment` reserves the
1067
+ * domain, naming one links or re-points it, and labels travel either way.
1068
+ *
1069
+ * `deployment` is deliberately not nullable — unlinking is refused (400).
1070
+ * See `npm/ship/CLAUDE.md`, "Domain Write Semantics".
1071
+ */
1072
+ export interface DomainSetOptions {
1073
+ deployment?: string;
1074
+ labels?: string[];
1075
+ }
1076
+ /** What a caller may set when minting a deploy token. */
1077
+ export interface TokenCreateOptions {
1078
+ /** Seconds until expiry; omit for a token that never expires. */
1079
+ ttl?: number;
1080
+ labels?: string[];
1081
+ }
946
1082
  /**
947
1083
  * Deployment resource interface - the contract all implementations must follow.
948
1084
  *
@@ -955,30 +1091,22 @@ export interface DeploymentResource<UploadOptions extends DeploymentUploadOption
955
1091
  upload: (input: DeployInput, options?: UploadOptions) => Promise<DeploymentCreateResponse>;
956
1092
  list: (options?: ListOptions) => Promise<DeploymentListResponse>;
957
1093
  get: (id: string) => Promise<Deployment>;
958
- set: (id: string, options: {
959
- labels: string[];
960
- }) => Promise<Deployment>;
961
- remove: (id: string) => Promise<void>;
1094
+ set: (id: string, options: DeploymentSetOptions) => Promise<Deployment>;
1095
+ remove: (id: string) => Promise<DeploymentDeleteResponse>;
962
1096
  }
963
1097
  /**
964
1098
  * Domain resource interface - the contract all implementations must follow
965
1099
  */
966
1100
  export interface DomainResource {
967
- set: (name: string, options?: {
968
- deployment?: string;
969
- labels?: string[];
970
- }) => Promise<DomainSetResult>;
1101
+ set: (name: string, options?: DomainSetOptions) => Promise<DomainSetResult>;
971
1102
  list: (options?: ListOptions) => Promise<DomainListResponse>;
972
1103
  get: (name: string) => Promise<Domain>;
973
- remove: (name: string) => Promise<void>;
1104
+ remove: (name: string) => Promise<DomainDeleteResponse>;
974
1105
  verify: (name: string) => Promise<DomainVerifyResponse>;
975
1106
  validate: (name: string) => Promise<DomainValidateResponse>;
976
1107
  dns: (name: string) => Promise<DomainDnsResponse>;
977
1108
  records: (name: string) => Promise<DomainRecordsResponse>;
978
- share: (name: string) => Promise<{
979
- domain: string;
980
- hash: string;
981
- }>;
1109
+ share: (name: string) => Promise<DomainShareResponse>;
982
1110
  }
983
1111
  /**
984
1112
  * Account resource interface - the contract all implementations must follow
@@ -990,12 +1118,10 @@ export interface AccountResource {
990
1118
  * Token resource interface - the contract all implementations must follow
991
1119
  */
992
1120
  export interface TokenResource {
993
- create: (options?: {
994
- ttl?: number;
995
- labels?: string[];
996
- }) => Promise<TokenCreateResponse>;
1121
+ create: (options?: TokenCreateOptions) => Promise<TokenCreateResponse>;
997
1122
  list: (options?: ListOptions) => Promise<TokenListResponse>;
998
- remove: (token: string) => Promise<void>;
1123
+ get: (token: string) => Promise<Token>;
1124
+ remove: (token: string) => Promise<TokenDeleteResponse>;
999
1125
  }
1000
1126
  /**
1001
1127
  * Billing status response from GET /billing/status
package/dist/index.js CHANGED
@@ -14,6 +14,49 @@ export const DeploymentStatus = {
14
14
  FAILED: 'failed',
15
15
  DELETING: 'deleting',
16
16
  };
17
+ /**
18
+ * Every path the public API answers on, declared once.
19
+ *
20
+ * The URL surface used to be written out in four places — the API's mounts,
21
+ * the SDK's client, the dashboard's client, and the post-deploy smoke — so a
22
+ * rename meant finding all four. Here it is one table that the producer
23
+ * mounts from and the consumers request against, which is the only way a
24
+ * path and its handler cannot drift apart.
25
+ *
26
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
27
+ * to `web/my`, for the same reason its row types do: this package is
28
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
29
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
30
+ * promise to one dashboard.
31
+ *
32
+ * Item paths are functions rather than templates so the key is interpolated
33
+ * in one place, encoded the same way by every caller.
34
+ */
35
+ export const API_PATHS = {
36
+ DEPLOYMENTS: '/deployments',
37
+ DEPLOYMENT: (deployment) => `/deployments/${deployment}`,
38
+ DEPLOYMENT_CONFIG: (deployment) => `/deployments/${deployment}/config`,
39
+ DOMAINS: '/domains',
40
+ DOMAIN: (domain) => `/domains/${domain}`,
41
+ DOMAIN_VERIFY: (domain) => `/domains/${domain}/verify`,
42
+ DOMAIN_DNS: (domain) => `/domains/${domain}/dns`,
43
+ DOMAIN_RECORDS: (domain) => `/domains/${domain}/records`,
44
+ DOMAIN_SHARE: (domain) => `/domains/${domain}/share`,
45
+ DOMAIN_PROPAGATION: (domain) => `/domains/${domain}/propagation`,
46
+ DOMAINS_VALIDATE: '/domains/validate',
47
+ TOKENS: '/tokens',
48
+ TOKEN: (token) => `/tokens/${token}`,
49
+ ACCOUNT: '/account',
50
+ ACCOUNT_KEY: '/account/key',
51
+ ACCOUNT_CLAIM: '/account/claim',
52
+ ACTIVITIES: '/activities',
53
+ LABELS: '/labels',
54
+ LIMITS: '/limits',
55
+ PING: '/ping',
56
+ SETUP: '/setup',
57
+ SPA_CHECK: '/spa-check',
58
+ UPLOAD: '/upload',
59
+ };
17
60
  // =============================================================================
18
61
  // DOMAIN TYPES
19
62
  // =============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "2.5.0-beta.11",
3
+ "version": "2.5.0-beta.12",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -58,6 +58,50 @@ export interface DeploymentCreateResponse extends Deployment {
58
58
  readonly claim?: string;
59
59
  }
60
60
 
61
+ /**
62
+ * Every path the public API answers on, declared once.
63
+ *
64
+ * The URL surface used to be written out in four places — the API's mounts,
65
+ * the SDK's client, the dashboard's client, and the post-deploy smoke — so a
66
+ * rename meant finding all four. Here it is one table that the producer
67
+ * mounts from and the consumers request against, which is the only way a
68
+ * path and its handler cannot drift apart.
69
+ *
70
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
71
+ * to `web/my`, for the same reason its row types do: this package is
72
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
73
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
74
+ * promise to one dashboard.
75
+ *
76
+ * Item paths are functions rather than templates so the key is interpolated
77
+ * in one place, encoded the same way by every caller.
78
+ */
79
+ export const API_PATHS = {
80
+ DEPLOYMENTS: '/deployments',
81
+ DEPLOYMENT: (deployment: string) => `/deployments/${deployment}`,
82
+ DEPLOYMENT_CONFIG: (deployment: string) => `/deployments/${deployment}/config`,
83
+ DOMAINS: '/domains',
84
+ DOMAIN: (domain: string) => `/domains/${domain}`,
85
+ DOMAIN_VERIFY: (domain: string) => `/domains/${domain}/verify`,
86
+ DOMAIN_DNS: (domain: string) => `/domains/${domain}/dns`,
87
+ DOMAIN_RECORDS: (domain: string) => `/domains/${domain}/records`,
88
+ DOMAIN_SHARE: (domain: string) => `/domains/${domain}/share`,
89
+ DOMAIN_PROPAGATION: (domain: string) => `/domains/${domain}/propagation`,
90
+ DOMAINS_VALIDATE: '/domains/validate',
91
+ TOKENS: '/tokens',
92
+ TOKEN: (token: string) => `/tokens/${token}`,
93
+ ACCOUNT: '/account',
94
+ ACCOUNT_KEY: '/account/key',
95
+ ACCOUNT_CLAIM: '/account/claim',
96
+ ACTIVITIES: '/activities',
97
+ LABELS: '/labels',
98
+ LIMITS: '/limits',
99
+ PING: '/ping',
100
+ SETUP: '/setup',
101
+ SPA_CHECK: '/spa-check',
102
+ UPLOAD: '/upload',
103
+ } as const;
104
+
61
105
  /**
62
106
  * The half of a list response that is identical on every list.
63
107
  *
@@ -97,12 +141,23 @@ export interface DeploymentListResponse extends ListResponse {
97
141
  * Where the resource is simply gone, the key alone is the whole answer
98
142
  * ({@link DomainDeleteResponse}, {@link TokenDeleteResponse}).
99
143
  *
100
- * Nothing else rides along. No prose (`message`), because an acknowledgement
101
- * is data and each surface composes its own copy; and no constant
102
- * (`changed: true`, `queued: true`, `success: true`), because a field whose
103
- * value the type already fixes tells a caller nothing it did not know before
104
- * it made the request. Sync versus accepted is the HTTP status code's job —
105
- * 200 versus 202 — not a boolean's.
144
+ * Put positively: **an acknowledgement is a projection of the resource** —
145
+ * its key, plus its own state field where the state changed. That is the
146
+ * test to apply, and it is sharper than "no constant", which this shape
147
+ * would fail on its own terms: `status` here is the literal `'deleting'` on
148
+ * every success, exactly as fixed as a `changed: true` would be.
149
+ *
150
+ * The difference is not how predictable the value is, it is what the field
151
+ * IS. `status` is the deployment's own field — the same one `GET
152
+ * /deployments/:deployment` returns — so this response is `Deployment`
153
+ * narrowed to two members, and a client renders it with the code it already
154
+ * has. `changed: true`, `queued: true` and `success: true` are not fields of
155
+ * any entity; they exist only to assert that the call worked, which the
156
+ * status code already said. Sync versus accepted is likewise the status
157
+ * code's job — 200 versus 202 — not a boolean's.
158
+ *
159
+ * No prose either (`message`): an acknowledgement is data, and each surface
160
+ * composes its own copy.
106
161
  */
107
162
  export interface DeploymentDeleteResponse {
108
163
  /** The deployment hostname that was marked for removal */
@@ -226,11 +281,35 @@ export interface DnsProvider {
226
281
  /**
227
282
  * Response for domain DNS provider lookup
228
283
  */
284
+ /**
285
+ * What a DNS lookup found for a domain. An envelope rather than a bare
286
+ * {@link DnsProvider} because a lookup can succeed and learn more than the
287
+ * provider later; the shape is named so a consumer can hold one.
288
+ */
289
+ export interface DnsLookup {
290
+ /** The provider serving this domain's DNS, absent when unidentified */
291
+ provider?: DnsProvider;
292
+ }
293
+
229
294
  export interface DomainDnsResponse {
230
295
  /** The domain name */
231
296
  domain: string;
232
297
  /** DNS provider information, null if not yet looked up */
233
- dns: { provider?: DnsProvider } | null;
298
+ dns: DnsLookup | null;
299
+ }
300
+
301
+ /**
302
+ * Response for `GET /domains/:domain/share` — the domain plus the salted
303
+ * hash that lets someone else complete its DNS setup without an account.
304
+ *
305
+ * `/admin/domains/:domain/share` answers the same shape, which is the admin
306
+ * law working: the operator surface is the public grammar with a prefix.
307
+ */
308
+ export interface DomainShareResponse {
309
+ /** The domain the setup link is for */
310
+ readonly domain: string;
311
+ /** The salted setup hash that authorizes the share */
312
+ readonly hash: string;
234
313
  }
235
314
 
236
315
  /**
@@ -245,6 +324,37 @@ export interface DomainRecordsResponse {
245
324
  records: DnsRecord[];
246
325
  }
247
326
 
327
+ /**
328
+ * Response for `GET /labels` — every label in use across the caller's
329
+ * deployments, domains and tokens, grouped and ordered by last use.
330
+ *
331
+ * The one plural noun outside the list contract, deliberately: labels have
332
+ * no identity, no row and no `created`, so there is nothing for a keyset
333
+ * cursor to resume after, and its consumer is an autocomplete that wants the
334
+ * whole set. Bounded by `PAGINATION.GLOBAL_LIMIT` rather than paginated.
335
+ */
336
+ export interface LabelsResponse {
337
+ readonly labels: string[];
338
+ }
339
+
340
+ /**
341
+ * Response for `POST /setup` — the DNS instructions for one domain, written
342
+ * for a human to follow at their registrar.
343
+ *
344
+ * `custom` is the provider-specific walkthrough when the provider is known;
345
+ * `generic` always answers, so a caller never has nothing to show.
346
+ */
347
+ export interface SetupInstructionsResponse {
348
+ /** One-line summary of what to do */
349
+ readonly tldr: string;
350
+ /** Provider-specific instructions, null when the provider is unknown */
351
+ readonly custom: string | null;
352
+ /** Provider-agnostic instructions — always present */
353
+ readonly generic: string;
354
+ /** The identified DNS provider, null when unknown */
355
+ readonly provider: string | null;
356
+ }
357
+
248
358
  /**
249
359
  * Response for domain validation
250
360
  */
@@ -1320,16 +1430,23 @@ export interface SPACheckRequest {
1320
1430
  /**
1321
1431
  * Response from SPA check endpoint
1322
1432
  */
1433
+ /**
1434
+ * Which of the classifier's tiers reached the verdict, and why. Named rather
1435
+ * than inline so the API's own `checkSPA` can return `SPACheckResponse`
1436
+ * instead of restating its shape.
1437
+ */
1438
+ export interface SPACheckDebug {
1439
+ /** Which tier made the detection */
1440
+ tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
1441
+ /** The reason for the detection result */
1442
+ reason: string;
1443
+ }
1444
+
1323
1445
  export interface SPACheckResponse {
1324
1446
  /** Whether the project is detected as a Single Page Application */
1325
1447
  isSPA: boolean;
1326
1448
  /** Debugging information about detection */
1327
- debug: {
1328
- /** Which tier made the detection: 'exclusions', 'inclusions', 'scoring', 'ai', or 'fallback' */
1329
- tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
1330
- /** The reason for the detection result */
1331
- reason: string;
1332
- };
1449
+ debug: SPACheckDebug;
1333
1450
  }
1334
1451
 
1335
1452
  // =============================================================================
@@ -1442,6 +1559,36 @@ export interface ListOptions {
1442
1559
  cursor?: string;
1443
1560
  }
1444
1561
 
1562
+ /**
1563
+ * What a caller may change on an existing deployment.
1564
+ *
1565
+ * Labels and nothing else: a deployment's content is immutable by design, so
1566
+ * this is the whole mutable surface rather than a subset someone chose.
1567
+ */
1568
+ export interface DeploymentSetOptions {
1569
+ labels: string[];
1570
+ }
1571
+
1572
+ /**
1573
+ * What `domains.set()` may create or change. Every field is optional because
1574
+ * the call is a natural-key upsert: omitting `deployment` reserves the
1575
+ * domain, naming one links or re-points it, and labels travel either way.
1576
+ *
1577
+ * `deployment` is deliberately not nullable — unlinking is refused (400).
1578
+ * See `npm/ship/CLAUDE.md`, "Domain Write Semantics".
1579
+ */
1580
+ export interface DomainSetOptions {
1581
+ deployment?: string;
1582
+ labels?: string[];
1583
+ }
1584
+
1585
+ /** What a caller may set when minting a deploy token. */
1586
+ export interface TokenCreateOptions {
1587
+ /** Seconds until expiry; omit for a token that never expires. */
1588
+ ttl?: number;
1589
+ labels?: string[];
1590
+ }
1591
+
1445
1592
  /**
1446
1593
  * Deployment resource interface - the contract all implementations must follow.
1447
1594
  *
@@ -1456,26 +1603,23 @@ export interface DeploymentResource<
1456
1603
  upload: (input: DeployInput, options?: UploadOptions) => Promise<DeploymentCreateResponse>;
1457
1604
  list: (options?: ListOptions) => Promise<DeploymentListResponse>;
1458
1605
  get: (id: string) => Promise<Deployment>;
1459
- set: (id: string, options: { labels: string[] }) => Promise<Deployment>;
1460
- remove: (id: string) => Promise<void>;
1606
+ set: (id: string, options: DeploymentSetOptions) => Promise<Deployment>;
1607
+ remove: (id: string) => Promise<DeploymentDeleteResponse>;
1461
1608
  }
1462
1609
 
1463
1610
  /**
1464
1611
  * Domain resource interface - the contract all implementations must follow
1465
1612
  */
1466
1613
  export interface DomainResource {
1467
- set: (
1468
- name: string,
1469
- options?: { deployment?: string; labels?: string[] },
1470
- ) => Promise<DomainSetResult>;
1614
+ set: (name: string, options?: DomainSetOptions) => Promise<DomainSetResult>;
1471
1615
  list: (options?: ListOptions) => Promise<DomainListResponse>;
1472
1616
  get: (name: string) => Promise<Domain>;
1473
- remove: (name: string) => Promise<void>;
1617
+ remove: (name: string) => Promise<DomainDeleteResponse>;
1474
1618
  verify: (name: string) => Promise<DomainVerifyResponse>;
1475
1619
  validate: (name: string) => Promise<DomainValidateResponse>;
1476
1620
  dns: (name: string) => Promise<DomainDnsResponse>;
1477
1621
  records: (name: string) => Promise<DomainRecordsResponse>;
1478
- share: (name: string) => Promise<{ domain: string; hash: string }>;
1622
+ share: (name: string) => Promise<DomainShareResponse>;
1479
1623
  }
1480
1624
 
1481
1625
  /**
@@ -1489,9 +1633,10 @@ export interface AccountResource {
1489
1633
  * Token resource interface - the contract all implementations must follow
1490
1634
  */
1491
1635
  export interface TokenResource {
1492
- create: (options?: { ttl?: number; labels?: string[] }) => Promise<TokenCreateResponse>;
1636
+ create: (options?: TokenCreateOptions) => Promise<TokenCreateResponse>;
1493
1637
  list: (options?: ListOptions) => Promise<TokenListResponse>;
1494
- remove: (token: string) => Promise<void>;
1638
+ get: (token: string) => Promise<Token>;
1639
+ remove: (token: string) => Promise<TokenDeleteResponse>;
1495
1640
  }
1496
1641
 
1497
1642
  // =============================================================================