@shipstatic/ship 2.0.0-beta.2 → 2.0.0-beta.21

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.cts CHANGED
@@ -12,6 +12,28 @@ declare const DeploymentStatus: {
12
12
  readonly DELETING: "deleting";
13
13
  };
14
14
  type DeploymentStatusType = (typeof DeploymentStatus)[keyof typeof DeploymentStatus];
15
+ /**
16
+ * Which client made a deployment — the origin-tracking vocabulary.
17
+ *
18
+ * A closed set with many authors: the CLI, the SDK, the dashboard, both MCP
19
+ * transports, the GitHub Action, the n8n node and the VS Code extension each
20
+ * name themselves here. It lived in the API's config until 2026-08-06, where
21
+ * being server-side made it unenforceable in the one direction that matters —
22
+ * every client wrote a bare string, and a value outside the set was **silently
23
+ * dropped** by the server, so a typo did not fail anywhere. It stopped
24
+ * recording where deploys came from and said nothing.
25
+ */
26
+ declare const DeploymentVia: {
27
+ readonly WEB: "web";
28
+ readonly SDK: "sdk";
29
+ readonly CLI: "cli";
30
+ readonly MCP: "mcp";
31
+ readonly GIT: "git";
32
+ readonly N8N: "n8n";
33
+ readonly GPT: "gpt";
34
+ readonly VSC: "vsc";
35
+ };
36
+ type DeploymentViaType = (typeof DeploymentVia)[keyof typeof DeploymentVia];
15
37
  /**
16
38
  * Core deployment object - used in both API responses and SDK
17
39
  */
@@ -32,7 +54,15 @@ interface Deployment {
32
54
  readonly password: boolean;
33
55
  /** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
34
56
  labels: string[];
35
- /** The client/tool used to create this deployment (e.g., 'web', 'sdk', 'cli'), null if unknown */
57
+ /**
58
+ * The client/tool that created this deployment, null if unknown.
59
+ *
60
+ * Deliberately wider than {@link DeploymentViaType}: this is stored data,
61
+ * and rows predate the vocabulary being closed. Narrowing the ENTITY would
62
+ * be a claim about every row already in the database; narrowing the
63
+ * REQUEST option ({@link DeploymentUploadOptions.via}) is a claim about
64
+ * what a client may send, which is ours to make.
65
+ */
36
66
  readonly via: string | null;
37
67
  /** Unix timestamp (seconds) when deployment was created */
38
68
  readonly created: number;
@@ -49,16 +79,88 @@ interface DeploymentCreateResponse extends Deployment {
49
79
  /** Claim URL for public deployments. Present when deployed without credentials. */
50
80
  readonly claim?: string;
51
81
  }
82
+ /**
83
+ * The half of a list response that is identical on every list.
84
+ *
85
+ * `GET /<collection>` answers exactly two fields — the collection under its
86
+ * own plural noun, and this cursor — so the cursor is declared once here and
87
+ * each response below adds only its noun. `cursor: null` means last page and
88
+ * is the ENTIRE has-more signal, which is why there is no `has_more`.
89
+ *
90
+ * There is deliberately no `total`. A count is an aggregate over a
91
+ * collection, not a property of a page; producing one would cost a COUNT
92
+ * beside every page read, which is precisely what keyset pagination exists
93
+ * to avoid. Counts live on the resource that summarises the collection —
94
+ * `GET /account`'s `usage` for one caller, `GET /admin/stats` platform-wide.
95
+ */
96
+ interface ListResponse {
97
+ /** Opaque cursor from this page; `null` on the last page. */
98
+ cursor: string | null;
99
+ }
100
+ /**
101
+ * Pagination options for every list endpoint. The response's `cursor` feeds
102
+ * the next request; a `null` cursor means the last page. Omitting both
103
+ * returns the server's default first page.
104
+ *
105
+ * A list answers `{ <collection>, cursor }` and nothing else — `cursor`
106
+ * carries the entire has-more signal, so no redundant boolean, and no
107
+ * `total`. **A count is an aggregate over a collection, not a property of a
108
+ * page:** including one makes every read pay for a full scan it did not ask
109
+ * for, which is precisely the cost keyset pagination exists to avoid.
110
+ *
111
+ * Counts therefore live on the summary resource that owns them —
112
+ * `GET /account` (`usage`) for a caller's own totals, `GET /admin/stats` for
113
+ * platform-wide ones. Ask for a count when you want a count; ask for a page
114
+ * when you want a page.
115
+ */
116
+ interface ListOptions {
117
+ /** Maximum number of items to return in one page. */
118
+ limit?: number;
119
+ /** Opaque cursor from the previous page's response. */
120
+ cursor?: string;
121
+ }
52
122
  /**
53
123
  * Response for listing deployments
54
124
  */
55
- interface DeploymentListResponse {
125
+ interface DeploymentListResponse extends ListResponse {
56
126
  /** Array of deployments */
57
127
  deployments: Deployment[];
58
- /** Cursor for pagination, null if no more pages */
59
- cursor: string | null;
60
- /** Total number of deployments */
61
- total: number;
128
+ }
129
+ /**
130
+ * Acknowledgement of `DELETE /deployments/:deployment` — and the shape every
131
+ * mutation with no entity left to return follows.
132
+ *
133
+ * **The law:** a mutation answers with the resource it affected. If the
134
+ * resource still exists, that means the entity itself (`Deployment`,
135
+ * `Domain`, …). Otherwise it means this: the resource noun carrying the
136
+ * item's canonical key, plus the resource's own state field — and ONLY when
137
+ * the resource survived in a transitional state, as an async deletion's does.
138
+ * Where the resource is simply gone, the key alone is the whole answer
139
+ * ({@link DomainDeleteResponse}, {@link TokenDeleteResponse}).
140
+ *
141
+ * Put positively: **an acknowledgement is a projection of the resource** —
142
+ * its key, plus its own state field where the state changed. That is the
143
+ * test to apply, and it is sharper than "no constant", which this shape
144
+ * would fail on its own terms: `status` here is the literal `'deleting'` on
145
+ * every success, exactly as fixed as a `changed: true` would be.
146
+ *
147
+ * The difference is not how predictable the value is, it is what the field
148
+ * IS. `status` is the deployment's own field — the same one `GET
149
+ * /deployments/:deployment` returns — so this response is `Deployment`
150
+ * narrowed to two members, and a client renders it with the code it already
151
+ * has. `changed: true`, `queued: true` and `success: true` are not fields of
152
+ * any entity; they exist only to assert that the call worked, which the
153
+ * status code already said. Sync versus accepted is likewise the status
154
+ * code's job — 200 versus 202 — not a boolean's.
155
+ *
156
+ * No prose either (`message`): an acknowledgement is data, and each surface
157
+ * composes its own copy.
158
+ */
159
+ interface DeploymentDeleteResponse {
160
+ /** The deployment hostname that was marked for removal */
161
+ readonly deployment: string;
162
+ /** The state the deployment is in while background cleanup runs */
163
+ readonly status: DeploymentStatusType;
62
164
  }
63
165
  /**
64
166
  * Domain status constants
@@ -91,7 +193,7 @@ interface Domain {
91
193
  labels: string[];
92
194
  /** Unix timestamp (seconds) when domain was created */
93
195
  readonly created: number;
94
- /** When deployment was last linked (Unix timestamp), null if never linked */
196
+ /** Unix timestamp (seconds) when deployment was last linked, null if never linked */
95
197
  linked: number | null;
96
198
  /** Total deployment links */
97
199
  links: number;
@@ -113,13 +215,28 @@ interface DomainSetResult extends Domain {
113
215
  /**
114
216
  * Response for listing domains
115
217
  */
116
- interface DomainListResponse {
218
+ interface DomainListResponse extends ListResponse {
117
219
  /** Array of domains */
118
220
  domains: Domain[];
119
- /** Cursor for pagination, null if no more pages */
120
- cursor: string | null;
121
- /** Total number of domains */
122
- total: number;
221
+ }
222
+ /**
223
+ * Acknowledgement of `DELETE /domains/:domain`. The row is gone, so there is
224
+ * no state to state — the canonical domain name is the whole answer. See
225
+ * {@link DeploymentDeleteResponse} for the law.
226
+ */
227
+ interface DomainDeleteResponse {
228
+ /** The domain name that was removed, normalized */
229
+ readonly domain: string;
230
+ }
231
+ /**
232
+ * Acknowledgement of `POST /domains/:domain/verify` (202). The DNS check is
233
+ * queued, not performed — the accepted status code says so, and the domain's
234
+ * own status is unchanged until the check runs, which is why none is stated
235
+ * here. See {@link DeploymentDeleteResponse} for the law.
236
+ */
237
+ interface DomainVerifyResponse {
238
+ /** The domain whose DNS verification was queued, normalized */
239
+ readonly domain: string;
123
240
  }
124
241
  /**
125
242
  * DNS record types supported for domain configuration
@@ -146,16 +263,46 @@ interface DnsProvider {
146
263
  /**
147
264
  * Response for domain DNS provider lookup
148
265
  */
266
+ /**
267
+ * What a DNS lookup found for a domain. An envelope rather than a bare
268
+ * {@link DnsProvider} because a lookup can succeed and learn more than the
269
+ * provider later; the shape is named so a consumer can hold one.
270
+ */
271
+ interface DnsLookup {
272
+ /** The provider serving this domain's DNS, absent when unidentified */
273
+ provider?: DnsProvider;
274
+ }
275
+ /**
276
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
277
+ * "A report answers a question").
278
+ */
149
279
  interface DomainDnsResponse {
150
280
  /** The domain name */
151
281
  domain: string;
152
282
  /** DNS provider information, null if not yet looked up */
153
- dns: {
154
- provider?: DnsProvider;
155
- } | null;
283
+ dns: DnsLookup | null;
284
+ }
285
+ /**
286
+ * Response for `GET /domains/:domain/share` — the domain plus the salted
287
+ * hash that lets someone else complete its DNS setup without an account.
288
+ *
289
+ * `/admin/domains/:domain/share` answers the same shape, which is the admin
290
+ * law working: the operator surface is the public grammar with a prefix.
291
+ *
292
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
293
+ * "A report answers a question").
294
+ */
295
+ interface DomainShareResponse {
296
+ /** The domain the setup link is for */
297
+ readonly domain: string;
298
+ /** The salted setup hash that authorizes the share */
299
+ readonly hash: string;
156
300
  }
157
301
  /**
158
302
  * Response for domain DNS records
303
+ *
304
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
305
+ * "A report answers a question").
159
306
  */
160
307
  interface DomainRecordsResponse {
161
308
  /** The domain name */
@@ -166,7 +313,92 @@ interface DomainRecordsResponse {
166
313
  records: DnsRecord[];
167
314
  }
168
315
  /**
169
- * Response for domain validation
316
+ * The envelope an `Idempotency-Key` must fit, and how long a replay lasts.
317
+ *
318
+ * Format lives here rather than on the server alone by the format-vs-policy
319
+ * rule: a client can decide offline whether a key is well-formed, and the
320
+ * API would reject the same value the same way.
321
+ */
322
+ declare const IDEMPOTENCY_KEY_CONSTRAINTS: {
323
+ /**
324
+ * HTTP header name. Here for the same reason {@link CALLER.HEADER} is: a
325
+ * wire header has two ends, and the package that owns the value's format
326
+ * is the only place both ends can read its name from.
327
+ */
328
+ readonly HEADER: "Idempotency-Key";
329
+ readonly MAX_LENGTH: 256;
330
+ /** How long a stored 201 stays replayable. */
331
+ readonly WINDOW_SECONDS: number;
332
+ };
333
+ /**
334
+ * Normalize a `via` value from any transport — trimmed, lowercased, and a
335
+ * member of {@link DeploymentVia}, or `undefined`.
336
+ *
337
+ * A format rule by this package's own test: a client can decide offline
338
+ * whether a value is well-formed, and the API reaches the same verdict on the
339
+ * same input. It lived server-side until 2026-08-06, which meant clients could
340
+ * only learn their label was unusable by noticing analytics had gone quiet.
341
+ *
342
+ * **Not knowing your `via` is not an error** — an unrecognized value yields
343
+ * `undefined` rather than throwing, because origin tracking is telemetry and a
344
+ * deploy must never fail over it. A caller that has an honest default should
345
+ * prefer it (`normalizeVia(process.env.SHIP_VIA) ?? DeploymentVia.CLI`): the
346
+ * deploy really did come from the CLI, so recording that beats recording
347
+ * nothing.
348
+ */
349
+ declare function normalizeVia(value: unknown): DeploymentViaType | undefined;
350
+ /**
351
+ * Validate an idempotency key, returning the trimmed value or `undefined`
352
+ * when none was supplied. Throws {@link ShipError.validation} when the value
353
+ * cannot be sent — the same verdict the API would reach, reached earlier.
354
+ */
355
+ declare function validateIdempotencyKey(value: unknown): string | undefined;
356
+ /**
357
+ * Response for `GET /labels` — every label in use across the caller's
358
+ * deployments, domains and tokens, grouped and ordered by last use.
359
+ *
360
+ * The one plural noun outside the list contract, deliberately: labels have
361
+ * no identity, no row and no `created`, so there is nothing for a keyset
362
+ * cursor to resume after, and its consumer is an autocomplete that wants the
363
+ * whole set. Bounded by `PAGINATION.GLOBAL_LIMIT` rather than paginated.
364
+ *
365
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
366
+ * "A report answers a question").
367
+ */
368
+ interface LabelsResponse {
369
+ readonly labels: string[];
370
+ }
371
+ /**
372
+ * Response for `POST /setup` — the DNS instructions for one domain, written
373
+ * for a human to follow at their registrar.
374
+ *
375
+ * `custom` is the provider-specific walkthrough when the provider is known;
376
+ * `generic` always answers, so a caller never has nothing to show.
377
+ *
378
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
379
+ * "A report answers a question").
380
+ */
381
+ interface SetupInstructionsResponse {
382
+ /** The domain the instructions are for — a report names its subject */
383
+ readonly domain: string;
384
+ /** One-line summary of what to do */
385
+ readonly tldr: string;
386
+ /** Provider-specific instructions, null when the provider is unknown */
387
+ readonly custom: string | null;
388
+ /** Provider-agnostic instructions — always present */
389
+ readonly generic: string;
390
+ /** The identified DNS provider, null when unknown */
391
+ readonly provider: string | null;
392
+ }
393
+ /**
394
+ * `POST /domains/validate` — a report answering "is this name usable, and if
395
+ * not, why".
396
+ *
397
+ * An unusable name is a legitimate ANSWER, not a failure, so this is a 200 and
398
+ * the verdict rides the body. `reason` was named `error` until 2026-07-29,
399
+ * which collided with {@link ErrorResponse}'s reserved key — there `error` is
400
+ * an `ErrorType` a client branches on, here it is prose a client displays, and
401
+ * one key cannot mean both. See {@link DeploymentDeleteResponse} for the law.
170
402
  */
171
403
  interface DomainValidateResponse {
172
404
  /** Whether the domain is valid */
@@ -175,15 +407,17 @@ interface DomainValidateResponse {
175
407
  normalized: string | null;
176
408
  /** Whether the domain is available, null when invalid */
177
409
  available: boolean | null;
178
- /** Error message, null when valid */
179
- error: string | null;
410
+ /** Why the name is unusable, null when valid — displayed verbatim. */
411
+ reason: string | null;
180
412
  }
181
413
  /**
182
- * Token as returned by the list endpoint.
183
- * The secret is shown once at creation and never again — listings carry
184
- * only the management identifier and lifecycle metadata.
414
+ * Core deploy token object - used in both API responses and SDK.
415
+ *
416
+ * The secret is never here: it is shown once at creation
417
+ * ({@link TokenCreateResponse.secret}) and never again, so an entity read
418
+ * carries only the management identifier and lifecycle metadata.
185
419
  */
186
- interface TokenListItem {
420
+ interface Token {
187
421
  /** 7-char management identifier (e.g., "a1b2c3d") */
188
422
  readonly token: string;
189
423
  /** Labels for categorization and filtering. Always present, empty array when none. */
@@ -198,24 +432,28 @@ interface TokenListItem {
198
432
  /**
199
433
  * Response for listing tokens
200
434
  */
201
- interface TokenListResponse {
202
- /** Array of tokens (security-redacted for list display) */
203
- tokens: TokenListItem[];
204
- /** Total number of tokens */
205
- total: number;
435
+ interface TokenListResponse extends ListResponse {
436
+ /** Array of tokens (the secret is never among them) */
437
+ tokens: Token[];
206
438
  }
207
439
  /**
208
- * Response for token creation
440
+ * Response from token creation. Extends Token with the one field that
441
+ * exists only on creation — the same shape as
442
+ * {@link DeploymentCreateResponse}, because a 201 returns the resource it
443
+ * created plus whatever is knowable only once.
209
444
  */
210
- interface TokenCreateResponse {
211
- /** 7-char management identifier */
212
- token: string;
445
+ interface TokenCreateResponse extends Token {
213
446
  /** The raw credential value (shown once at creation, then never again) */
214
- secret: string;
215
- /** Labels for categorization and filtering. Always present, empty array when none. */
216
- labels: string[];
217
- /** Unix timestamp (seconds) when token expires, null for never */
218
- expires: number | null;
447
+ readonly secret: string;
448
+ }
449
+ /**
450
+ * Acknowledgement of `DELETE /tokens/:token`. The credential is revoked and
451
+ * its row is gone, so the management identifier is the whole answer. See
452
+ * {@link DeploymentDeleteResponse} for the law.
453
+ */
454
+ interface TokenDeleteResponse {
455
+ /** The 7-char management identifier that was revoked */
456
+ readonly token: string;
219
457
  }
220
458
  /**
221
459
  * Account plan constants
@@ -232,10 +470,34 @@ declare const AccountPlan: {
232
470
  type AccountPlanType = (typeof AccountPlan)[keyof typeof AccountPlan];
233
471
  /**
234
472
  * Account usage metrics — always available regardless of billing provider.
473
+ *
474
+ * This is where a caller's own totals live. Lists answer pages and carry no
475
+ * `total` (see {@link ListOptions}); a count is an aggregate over a
476
+ * collection, so it belongs to the summary resource that owns the
477
+ * collection. `GET /account` is that resource for one caller, `GET
478
+ * /admin/stats` for the platform.
479
+ *
480
+ * The counted dimensions are the ones the plan caps — deployments and
481
+ * domains (`PlatformLimits`) — plus the billable custom-domain subset, so a
482
+ * surface can render "3 of 10" without a second request.
235
483
  */
236
484
  interface AccountUsage {
237
485
  /** Number of active custom domains (excludes paused) */
238
486
  customDomains: number;
487
+ /**
488
+ * Deployments counted against the plan's deployment cap — every row
489
+ * whatever its status, because that is what the cap counts, so a surface
490
+ * renders "3 of 10" against the denominator the 403 divides by. (`GET
491
+ * /deployments` lists successful ones only; that is a different question
492
+ * asked of a different resource.) Optional by the additive-evolution law:
493
+ * an API predating this field omits it.
494
+ */
495
+ deployments?: number;
496
+ /**
497
+ * Domains counted against the plan's domain cap — every domain, platform
498
+ * and custom alike, unlike `customDomains`. Optional for the same reason.
499
+ */
500
+ domains?: number;
239
501
  }
240
502
  /**
241
503
  * Core account object - used in both API responses and SDK
@@ -258,6 +520,13 @@ interface Account {
258
520
  readonly activated: number | null;
259
521
  /** Last 4 characters of the API key for identification, null when no key generated */
260
522
  readonly hint: string | null;
523
+ /**
524
+ * Unix timestamp (seconds) of the API key's last use, null when never
525
+ * used or no key generated. Optional on the type by the additive-evolution
526
+ * law: published SDK versions may predate the field, so consumers read it
527
+ * when present rather than forcing a lockstep SDK release.
528
+ */
529
+ readonly used?: number | null;
261
530
  /** Grace period expiration (unix seconds), null if no grace period active */
262
531
  readonly grace: number | null;
263
532
  }
@@ -275,6 +544,35 @@ interface AccountGetResponse extends Account {
275
544
  /** Present only during read-only admin impersonation: the operator's account id. */
276
545
  readonly impersonatedBy?: string;
277
546
  }
547
+ /**
548
+ * Acknowledgement of `DELETE /account` (202). Termination is asynchronous —
549
+ * a cleanup consumer finishes the job — so the account survives long enough
550
+ * to state the plan it is transitioning through. `plan` is the account's
551
+ * state field, the way `status` is a deployment's. See
552
+ * {@link DeploymentDeleteResponse} for the law.
553
+ */
554
+ interface AccountDeleteResponse {
555
+ /** The account that was marked for termination */
556
+ readonly account: string;
557
+ /** The plan the account is in while cleanup runs */
558
+ readonly plan: AccountPlanType;
559
+ }
560
+ /**
561
+ * Response from `PUT /account/key` — the account's single API key, minted in
562
+ * place of whatever was there before.
563
+ *
564
+ * There is no entity to return: only the key's last-4 `hint` is durable
565
+ * (`Account.hint`), and the plaintext exists exactly once, in this response.
566
+ * The raw credential is `secret` on every surface that mints one — the same
567
+ * field `TokenCreateResponse` carries — because one concept gets one name.
568
+ *
569
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
570
+ * "A report answers a question").
571
+ */
572
+ interface AccountKeyResponse {
573
+ /** The raw API key (shown once at mint, then never again) */
574
+ readonly secret: string;
575
+ }
278
576
  /**
279
577
  * Account-specific configuration overrides
280
578
  * Allows per-account customization of limits without changing plan
@@ -291,6 +589,97 @@ interface AccountOverrides {
291
589
  /** Override for maximum total deployment size in bytes */
292
590
  totalSize?: number;
293
591
  }
592
+ /**
593
+ * Every path the public API answers on, declared once.
594
+ *
595
+ * The URL surface was written out in four places — the API's mounts, the
596
+ * SDK's client, the dashboard's client, and the post-deploy smoke — so a
597
+ * rename meant finding all four. The first three now read this table.
598
+ *
599
+ * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
600
+ * five of its nine paths are `/admin/*`, which this table excludes by
601
+ * design, and splitting one list between a registry and literals reads worse
602
+ * than keeping it uniform.
603
+ *
604
+ * **What this guarantees, exactly.** Collection paths are mounted from here,
605
+ * so producer and consumer cannot diverge. Item paths are declared here and
606
+ * consumed by clients, but the API spells them relative to their mount
607
+ * (`/:deployment/config`), so the table does not *generate* them — it is
608
+ * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
609
+ * any entry names a path no route answers. Some entries have no client yet
610
+ * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
611
+ * deliberately does not reach); the fence is what keeps those honest rather
612
+ * than merely asserted.
613
+ *
614
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
615
+ * to `web/my`, for the same reason its row types do: this package is
616
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
617
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
618
+ * promise to one dashboard.
619
+ *
620
+ * Item paths are functions rather than templates so the key is interpolated
621
+ * in one place, encoded the same way by every caller.
622
+ */
623
+ declare const API_PATHS: {
624
+ readonly DEPLOYMENTS: "/deployments";
625
+ readonly DEPLOYMENT: (deployment: string) => string;
626
+ readonly DEPLOYMENT_CONFIG: (deployment: string) => string;
627
+ readonly DOMAINS: "/domains";
628
+ readonly DOMAIN: (domain: string) => string;
629
+ readonly DOMAIN_VERIFY: (domain: string) => string;
630
+ readonly DOMAIN_DNS: (domain: string) => string;
631
+ readonly DOMAIN_RECORDS: (domain: string) => string;
632
+ readonly DOMAIN_SHARE: (domain: string) => string;
633
+ readonly DOMAIN_PROPAGATION: (domain: string) => string;
634
+ readonly DOMAINS_VALIDATE: "/domains/validate";
635
+ readonly TOKENS: "/tokens";
636
+ readonly TOKEN: (token: string) => string;
637
+ readonly ACCOUNT: "/account";
638
+ readonly ACCOUNT_KEY: "/account/key";
639
+ readonly ACCOUNT_CLAIM: "/account/claim";
640
+ readonly ACTIVITIES: "/activities";
641
+ readonly LABELS: "/labels";
642
+ readonly LIMITS: "/limits";
643
+ readonly PING: "/ping";
644
+ readonly SETUP: "/setup";
645
+ readonly SPA_CHECK: "/spa-check";
646
+ readonly UPLOAD: "/upload";
647
+ };
648
+ /**
649
+ * The deploy request's multipart field names — the other half of the wire
650
+ * surface beside {@link API_PATHS}. `POST /deployments` (and the first-party
651
+ * `/upload`) is multipart/form-data, and these are the names the API reads.
652
+ *
653
+ * Declared once because the body has three independent WRITERS — the SDK's
654
+ * Node and browser body builders, and the n8n community node's hand-rolled
655
+ * client (which cannot import this under n8n Cloud's zero-dependency rule,
656
+ * and fences its restated copy instead) — and until this export every writer
657
+ * restated the strings the API parses, with nothing comparing them.
658
+ *
659
+ * `FILES` carries one entry per file (the API reads it with `getAll`); every
660
+ * other field is single. The `@internal` flags are serialized as the literal
661
+ * string `'true'` and belong to first-party surfaces only.
662
+ */
663
+ declare const DEPLOY_FIELDS: {
664
+ /** One entry per file — read with `getAll`. */
665
+ readonly FILES: "files[]";
666
+ /** JSON array of MD5 hex digests, index-aligned with `FILES`. */
667
+ readonly CHECKSUMS: "checksums";
668
+ /** JSON array of label strings. */
669
+ readonly LABELS: "labels";
670
+ /** The deploying surface's {@link DeploymentVia} member. */
671
+ readonly VIA: "via";
672
+ /** Plaintext password — the API hashes it server-side. */
673
+ readonly PASSWORD: "password";
674
+ /** @internal Server-processing flag — first-party `/upload` only. */
675
+ readonly BUILD: "build";
676
+ /** @internal Server-processing flag — first-party `/upload` only. */
677
+ readonly PRERENDER: "prerender";
678
+ /** @internal Server-processing flag — first-party `/upload` only. */
679
+ readonly SPA: "spa";
680
+ /** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
681
+ readonly CAPTCHA: "captcha";
682
+ };
294
683
  /**
295
684
  * All possible error types in the ShipStatic platform.
296
685
  *
@@ -301,7 +690,15 @@ interface AccountOverrides {
301
690
  * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.
302
691
  */
303
692
  declare const ErrorType: {
304
- /** Validation failed (400). Input shape is wrong. */
693
+ /**
694
+ * Validation failed. Input shape is wrong.
695
+ *
696
+ * Carries 400 when an API judged it — including a client-side pre-check of a
697
+ * rule the server enforces too, which keeps the error identical wherever it
698
+ * was caught. **Statusless** when a client rejects something no API judges,
699
+ * such as a CLI's own command grammar: `status` is documented "(API
700
+ * contexts)" on `ErrorResponse`, so there is none to report.
701
+ */
305
702
  readonly Validation: "validation_failed";
306
703
  /** Resource not found (404). */
307
704
  readonly NotFound: "not_found";
@@ -315,6 +712,17 @@ declare const ErrorType: {
315
712
  readonly Business: "business_logic_error";
316
713
  /** API server error (500). Generic server-side fault. */
317
714
  readonly Api: "internal_server_error";
715
+ /**
716
+ * The platform is closed for maintenance (503). A deliberate operator
717
+ * state, not a fault — nothing errored; the API is refusing work on
718
+ * purpose, and deployed sites keep serving throughout.
719
+ *
720
+ * Distinct from `Api` at 503, which the platform already uses for a
721
+ * dependency that failed (moderation unavailable). A consumer has to tell
722
+ * "we closed the door" from "something broke": the two get opposite words
723
+ * and opposite retry behaviour.
724
+ */
725
+ readonly Maintenance: "maintenance";
318
726
  /** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
319
727
  readonly Network: "network_error";
320
728
  /** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
@@ -381,7 +789,8 @@ declare class ShipError extends Error {
381
789
  * Routing:
382
790
  * - Already a `ShipError` → returned as-is (caller's intent preserved)
383
791
  * - `AbortError` → `ShipError.cancelled(...)`
384
- * - `TypeError` whose message mentions "fetch" → `ShipError.network(...)`
792
+ * - A transport failure → `ShipError.network(...)` — see `isTransportFailure`
793
+ * for what each runtime offers as evidence
385
794
  * - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)
386
795
  * - Anything else (string, undefined, etc.) → `ShipError(Api, ...)`
387
796
  *
@@ -414,6 +823,28 @@ declare class ShipError extends Error {
414
823
  static file(message: string, details?: unknown): ShipError;
415
824
  static config(message: string, details?: unknown): ShipError;
416
825
  static api(message: string, status?: number, details?: unknown): ShipError;
826
+ /**
827
+ * The platform is closed for maintenance (503).
828
+ *
829
+ * `message` is REQUIRED and has no default here. The API is the only
830
+ * producer of that sentence, and a default in this file would be a second
831
+ * owner of one fact — see CLAUDE.md, "The Constellation Law" (stopping
832
+ * rule). It is also the one factory whose status is fixed rather than
833
+ * defaulted: a maintenance refusal is 503 or it is not this error.
834
+ */
835
+ static maintenance(message: string, details?: unknown): ShipError;
836
+ /**
837
+ * The caller is at fault — by HTTP's own definition of a 4xx, or by a type
838
+ * that is client-attributable without ever having a status (`Config`,
839
+ * `File`, raised locally by the SDK).
840
+ *
841
+ * Both arms are load-bearing, because type and status are independent
842
+ * axes. `fromHttpResponse` trusts `body.error` only when it names a
843
+ * server-producible type; a non-OK response without one is status-derived,
844
+ * so a CDN 404 or any intermediary error arrives as `Api` — a server-fault
845
+ * *type* carrying a client *status*. Judging by type alone would report it
846
+ * as a platform failure and bury the server's own message.
847
+ */
417
848
  isClientError(): boolean;
418
849
  isNetworkError(): boolean;
419
850
  isAuthError(): boolean;
@@ -440,6 +871,9 @@ declare function isShipError(error: unknown): error is ShipError;
440
871
  *
441
872
  * These are the *platform's* posted caps for the current account — server
442
873
  * truth delivered at runtime, never hard-coded on the client.
874
+ *
875
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
876
+ * "A report answers a question").
443
877
  */
444
878
  interface PlatformLimits {
445
879
  /** Maximum size in bytes for a single file. */
@@ -473,6 +907,28 @@ declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
473
907
  * isBlockedExtension('README') // false
474
908
  */
475
909
  declare function isBlockedExtension(filename: string): boolean;
910
+ /**
911
+ * The `accept` attribute value for a browser file picker offering web files.
912
+ *
913
+ * **This is a hint, never a rule.** `BLOCKED_EXTENSIONS` is the platform's
914
+ * gate and the only thing that decides what may be hosted; this constant
915
+ * decides what a *file dialog* shows first. The two are not two halves of one
916
+ * policy, and this one must never be consulted to accept or reject a file.
917
+ *
918
+ * The distinction is structural, not stylistic. `accept` can express only an
919
+ * allowlist, while the platform's rule is a blocklist — so this list is
920
+ * necessarily *narrower* than what the platform hosts, and reading it as
921
+ * authority would reject files the platform serves happily. It is also not
922
+ * enforcement in the browser's own terms: every file dialog offers an
923
+ * all-files escape, and **drag-and-drop ignores `accept` entirely**. The
924
+ * dropzone and the picker must reach the same verdict on the same files, and
925
+ * they do — because the verdict is `validateFiles`, downstream of both.
926
+ *
927
+ * Kept beside `BLOCKED_EXTENSIONS` so one file holds both, which is what lets
928
+ * `tests/validation-constants.test.ts` fence the invariant that matters: the
929
+ * picker must never offer a file the platform will refuse.
930
+ */
931
+ declare const WEB_FILE_ACCEPT: string;
476
932
  /**
477
933
  * Characters that are unsafe in filenames for static hosting.
478
934
  *
@@ -510,14 +966,29 @@ declare const UNBUILT_PROJECT_MARKERS: ReadonlySet<string>;
510
966
  */
511
967
  declare function hasUnbuiltMarker(filePath: string): boolean;
512
968
  /**
513
- * Simple ping response for health checks
969
+ * `GET /ping` a report of the server clock.
970
+ *
971
+ * Liveness is the STATUS CODE's answer, not a field's: a 200 means reachable,
972
+ * and any other outcome throws before a body is read. So the body carries the
973
+ * one thing a status code cannot — the server's own clock, which is what lets a
974
+ * client detect skew against a token expiry. It read `{ success: true,
975
+ * timestamp? }` until 2026-07-29, where `success` was a literal constant in the
976
+ * route (zero bits, and the platform's own named anti-pattern) while the field
977
+ * that IS the payload was optional. See {@link DeploymentDeleteResponse} for
978
+ * the law, and `tests/response-shapes.test.ts` for the fence that holds it.
514
979
  */
515
980
  interface PingResponse {
516
- /** Always true if service is healthy */
517
- success: boolean;
518
- /** Optional timestamp */
519
- timestamp?: number;
981
+ /** Server time in unix seconds the one wire unit for timestamps. */
982
+ readonly timestamp: number;
520
983
  }
984
+ /**
985
+ * Where human identity is mounted on the API host. The API mounts Better
986
+ * Auth at this path (sign-in, sign-out, session reads, admin impersonation)
987
+ * and the web console's auth client posts to it — shared here so the two
988
+ * halves of the auth pair agree by construction, the same way both sides
989
+ * already share the credential prefixes below.
990
+ */
991
+ declare const AUTH_BASE_PATH = "/auth";
521
992
  /**
522
993
  * How a request (or recorded activity) was authorized.
523
994
  *
@@ -630,6 +1101,56 @@ declare const SPA_DEFAULT_CONFIG: {
630
1101
  readonly destination: "/index.html";
631
1102
  }];
632
1103
  };
1104
+ /**
1105
+ * The `/spa-check` pre-flight's client-side envelope: which file is the
1106
+ * check's subject, and how large it may be before a client skips the call.
1107
+ *
1108
+ * One fact with three holders until this export — the API's config declared
1109
+ * the cap, the SDK's `checkSPA` hardcoded `100 * 1024`, and prose restated
1110
+ * "100KB". `INDEX_FILE` is the selection rule (the file whose content rides
1111
+ * `SPACheckRequest.index`), restated by every client that builds the request.
1112
+ *
1113
+ * Neither member is a validation boundary: a client over the cap simply
1114
+ * skips the pre-flight, because the server answers an oversized index
1115
+ * `isSPA: false` anyway. A consumer that cannot import this (n8n) needs no
1116
+ * size copy at all — outcome parity is the server's, not the client's.
1117
+ */
1118
+ declare const SPA_CHECK_CONSTRAINTS: {
1119
+ /** The file whose content is the check's subject. */
1120
+ readonly INDEX_FILE: "index.html";
1121
+ /** Skip the pre-flight above this size — the server would answer false. */
1122
+ readonly MAX_INDEX_BYTES: number;
1123
+ };
1124
+ /**
1125
+ * Assert that a ship.json file is *syntactically* loadable. Syntax only —
1126
+ * never schema.
1127
+ *
1128
+ * ship.json is validated and compiled on the server, deliberately: the schema
1129
+ * and the compiler evolve, and a client that judged them would reject configs
1130
+ * a newer platform accepts. That reasoning bounds what a client may check to
1131
+ * the properties which are true of *every* past and future schema:
1132
+ *
1133
+ * 1. it parses as JSON — JSON syntax is frozen (RFC 8259), so text that
1134
+ * does not parse can never be a valid config;
1135
+ * 2. its top level is an object — ship.json is `{ ... }` in every version.
1136
+ *
1137
+ * Both are monotonic: neither can ever reject something the server would
1138
+ * accept. Everything beyond them (field names, types, rule semantics, which
1139
+ * keys are permitted) stays server-side, where it can change.
1140
+ *
1141
+ * The payoff is the common case. Hand-edited JSON fails on a trailing comma,
1142
+ * a `//` comment, single quotes, unquoted keys, or smart quotes pasted from
1143
+ * documentation — mistakes that otherwise cost a full upload round-trip to
1144
+ * discover. A UTF-8 BOM (Windows editors, PowerShell redirects) is stripped
1145
+ * before parsing rather than rejected, because the server accepts it too;
1146
+ * diverging there would reintroduce exactly the false rejection this
1147
+ * function exists to avoid.
1148
+ *
1149
+ * @throws {ShipError} `ErrorType.Config` — the same type the server's own
1150
+ * config rejection carries, so the error contract is identical wherever the
1151
+ * failure is detected.
1152
+ */
1153
+ declare function assertShipJsonSyntax(text: string): void;
633
1154
  /**
634
1155
  * Validate API key format
635
1156
  */
@@ -672,16 +1193,26 @@ interface SPACheckRequest {
672
1193
  /**
673
1194
  * Response from SPA check endpoint
674
1195
  */
1196
+ /**
1197
+ * Which of the classifier's tiers reached the verdict, and why. Named rather
1198
+ * than inline so the API's own `checkSPA` can return `SPACheckResponse`
1199
+ * instead of restating its shape.
1200
+ */
1201
+ interface SPACheckDebug {
1202
+ /** Which tier made the detection */
1203
+ tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
1204
+ /** The reason for the detection result */
1205
+ reason: string;
1206
+ }
1207
+ /**
1208
+ * A report: it answers a question and carries only the answer (`CLAUDE.md`,
1209
+ * "A report answers a question").
1210
+ */
675
1211
  interface SPACheckResponse {
676
1212
  /** Whether the project is detected as a Single Page Application */
677
1213
  isSPA: boolean;
678
1214
  /** Debugging information about detection */
679
- debug: {
680
- /** Which tier made the detection: 'exclusions', 'inclusions', 'scoring', 'ai', or 'fallback' */
681
- tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
682
- /** The reason for the detection result */
683
- reason: string;
684
- };
1215
+ debug: SPACheckDebug;
685
1216
  }
686
1217
  /**
687
1218
  * Represents a file that has been processed and is ready for deploy.
@@ -712,22 +1243,52 @@ interface StaticFile {
712
1243
  /** The size of the file in bytes. */
713
1244
  size: number;
714
1245
  }
715
- /**
716
- * Progress information for deploy/upload operations.
717
- * Provides consistent percentage-based progress with byte-level details.
718
- */
719
- interface ProgressInfo {
720
- /** Progress percentage (0-100) */
721
- percent: number;
722
- /** Number of bytes loaded so far */
723
- loaded: number;
724
- /** Total number of bytes to load. May be 0 if unknown initially */
725
- total: number;
726
- /** Current file being processed (optional) */
727
- file?: string;
728
- }
729
1246
  /** Default API URL if not otherwise configured. */
730
1247
  declare const DEFAULT_API = "https://api.shipstatic.com";
1248
+ /**
1249
+ * The Node SDK's ambient configuration pair — the ONLY environment variables
1250
+ * the SDK reads, and therefore the COMPLETE list an embedding host must
1251
+ * scrub (per `npm/ship`'s strict-isolation contract, scrubbing is the host's
1252
+ * job, not the SDK's). A host that derives its scrub from this object's
1253
+ * values — as the VS Code extension's child-process env block does — picks
1254
+ * up a grown contract at the next pin bump instead of by remembered prose.
1255
+ *
1256
+ * Browser builds read no environment at all, and the CLI-only variables
1257
+ * (`SHIP_PASSWORD`, `SHIP_VIA`) are deliberately NOT here: they are the
1258
+ * CLI's operational levers, not the SDK's ambient contract — see
1259
+ * `npm/ship/CLAUDE.md`, "CLI-only env vars".
1260
+ */
1261
+ declare const SHIP_ENV: {
1262
+ /** The one credential slot — any platform token. */
1263
+ readonly TOKEN: "SHIP_TOKEN";
1264
+ /** The API endpoint override. */
1265
+ readonly API_URL: "SHIP_API_URL";
1266
+ };
1267
+ /**
1268
+ * Where a human creates an API key — the console deep link quoted by every
1269
+ * surface that teaches authentication (the CLI's config wizard, the VS Code
1270
+ * and n8n listings, the n8n rate-limit hint and credential copy). Written
1271
+ * out in five files across three repos until this export.
1272
+ *
1273
+ * Production-branded by design: published artifacts name the product, never
1274
+ * an environment (root `CLAUDE.md`, "Environment-Aware URLs").
1275
+ */
1276
+ declare const MY_API_KEY_URL = "https://my.shipstatic.com/api-key";
1277
+ /**
1278
+ * How long an anonymous deployment lives before it expires.
1279
+ *
1280
+ * The lifetime of the public tier, and one fact with several readers. The API
1281
+ * stamps a deployment's `expires` from it and gives a claim code exactly the
1282
+ * same window — a live site with a dead claim link is a coherence bug, so the
1283
+ * two are one constant rather than two that agree. Both MCP transports quote
1284
+ * the duration in prose an agent reads, and derive it from here rather than
1285
+ * writing it out, which they did in eight places until this export existed.
1286
+ *
1287
+ * Seconds, spelled in the name: this platform has both second- and
1288
+ * millisecond-valued durations, and the pair is only safe when each says which
1289
+ * it is.
1290
+ */
1291
+ declare const PUBLIC_DEPLOYMENT_TTL_SECONDS: number;
731
1292
  /**
732
1293
  * Universal deploy input — the union of every shape the SDK accepts.
733
1294
  *
@@ -746,8 +1307,12 @@ type DeployInput = File[] | string | string[];
746
1307
  interface DeploymentUploadOptions {
747
1308
  /** Optional labels for categorization and filtering */
748
1309
  labels?: string[];
749
- /** Client identifier (e.g., 'cli', 'sdk', 'web') */
750
- via?: string;
1310
+ /**
1311
+ * Which client is making this deploy. Closed, because the server silently
1312
+ * ignores anything outside the set — so an unchecked string turned a typo
1313
+ * into missing analytics rather than an error. See {@link DeploymentVia}.
1314
+ */
1315
+ via?: DeploymentViaType;
751
1316
  /**
752
1317
  * Optional password that protects this deployment.
753
1318
  *
@@ -767,40 +1332,81 @@ interface DeploymentUploadOptions {
767
1332
  spa?: boolean;
768
1333
  /** @internal reCAPTCHA proof for the anonymous human deploy channel. Only available via /upload endpoint. */
769
1334
  captcha?: string;
1335
+ /**
1336
+ * Makes this deploy replayable instead of repeatable.
1337
+ *
1338
+ * A deploy is not naturally idempotent: a client-side timeout on a slow
1339
+ * one leaves the caller unable to tell "it never landed" from "it landed
1340
+ * and the response was lost", and retrying produces a second deployment.
1341
+ * Send the same key on the retry and the platform replays the original
1342
+ * 201 verbatim rather than creating anything
1343
+ * ({@link IDEMPOTENCY_KEY_CONSTRAINTS.WINDOW_SECONDS}).
1344
+ *
1345
+ * **Agents are the audience.** A human notices a duplicate; an automated
1346
+ * retry does not. Pick a key that identifies the ATTEMPT — a run id, a
1347
+ * commit sha, a uuid minted before the first try — never one minted fresh
1348
+ * on each retry, which would defeat the point.
1349
+ *
1350
+ * The replay is per-caller, and it stores successes only: a failed deploy
1351
+ * retries fresh under the same key.
1352
+ */
1353
+ idempotencyKey?: string;
770
1354
  }
771
1355
  /**
772
- * Deployment resource interface - the contract all implementations must follow
1356
+ * What a caller may change on an existing deployment.
1357
+ *
1358
+ * Labels and nothing else: a deployment's content is immutable by design, so
1359
+ * this is the whole mutable surface rather than a subset someone chose.
773
1360
  */
774
- interface DeploymentResource {
775
- upload: (input: DeployInput, options?: DeploymentUploadOptions) => Promise<DeploymentCreateResponse>;
776
- list: () => Promise<DeploymentListResponse>;
1361
+ interface DeploymentSetOptions {
1362
+ labels: string[];
1363
+ }
1364
+ /**
1365
+ * What `domains.set()` may create or change. Every field is optional because
1366
+ * the call is a natural-key upsert: omitting `deployment` reserves the
1367
+ * domain, naming one links or re-points it, and labels travel either way.
1368
+ *
1369
+ * `deployment` is deliberately not nullable — unlinking is refused (400).
1370
+ * See `npm/ship/CLAUDE.md`, "Domain Write Semantics".
1371
+ */
1372
+ interface DomainSetOptions {
1373
+ deployment?: string;
1374
+ labels?: string[];
1375
+ }
1376
+ /** What a caller may set when minting a deploy token. */
1377
+ interface TokenCreateOptions {
1378
+ /** Seconds until expiry; omit for a token that never expires. */
1379
+ ttl?: number;
1380
+ labels?: string[];
1381
+ }
1382
+ /**
1383
+ * Deployment resource interface - the contract all implementations must follow.
1384
+ *
1385
+ * The interface defines the minimal wire contract; SDK implementations may
1386
+ * extend the upload options with runtime concerns (timeout, signal, progress
1387
+ * callbacks) by parameterizing: `DeploymentResource<MyUploadOptions>`. The
1388
+ * default keeps plain `DeploymentResource` valid for wire-only consumers.
1389
+ */
1390
+ interface DeploymentResource<UploadOptions extends DeploymentUploadOptions = DeploymentUploadOptions> {
1391
+ upload: (input: DeployInput, options?: UploadOptions) => Promise<DeploymentCreateResponse>;
1392
+ list: (options?: ListOptions) => Promise<DeploymentListResponse>;
777
1393
  get: (id: string) => Promise<Deployment>;
778
- set: (id: string, options: {
779
- labels: string[];
780
- }) => Promise<Deployment>;
781
- remove: (id: string) => Promise<void>;
1394
+ set: (id: string, options: DeploymentSetOptions) => Promise<Deployment>;
1395
+ delete: (id: string) => Promise<DeploymentDeleteResponse>;
782
1396
  }
783
1397
  /**
784
1398
  * Domain resource interface - the contract all implementations must follow
785
1399
  */
786
1400
  interface DomainResource {
787
- set: (name: string, options?: {
788
- deployment?: string;
789
- labels?: string[];
790
- }) => Promise<DomainSetResult>;
791
- list: () => Promise<DomainListResponse>;
1401
+ set: (name: string, options?: DomainSetOptions) => Promise<DomainSetResult>;
1402
+ list: (options?: ListOptions) => Promise<DomainListResponse>;
792
1403
  get: (name: string) => Promise<Domain>;
793
- remove: (name: string) => Promise<void>;
794
- verify: (name: string) => Promise<{
795
- message: string;
796
- }>;
1404
+ delete: (name: string) => Promise<DomainDeleteResponse>;
1405
+ verify: (name: string) => Promise<DomainVerifyResponse>;
797
1406
  validate: (name: string) => Promise<DomainValidateResponse>;
798
1407
  dns: (name: string) => Promise<DomainDnsResponse>;
799
1408
  records: (name: string) => Promise<DomainRecordsResponse>;
800
- share: (name: string) => Promise<{
801
- domain: string;
802
- hash: string;
803
- }>;
1409
+ share: (name: string) => Promise<DomainShareResponse>;
804
1410
  }
805
1411
  /**
806
1412
  * Account resource interface - the contract all implementations must follow
@@ -812,12 +1418,10 @@ interface AccountResource {
812
1418
  * Token resource interface - the contract all implementations must follow
813
1419
  */
814
1420
  interface TokenResource {
815
- create: (options?: {
816
- ttl?: number;
817
- labels?: string[];
818
- }) => Promise<TokenCreateResponse>;
819
- list: () => Promise<TokenListResponse>;
820
- remove: (token: string) => Promise<void>;
1421
+ create: (options?: TokenCreateOptions) => Promise<TokenCreateResponse>;
1422
+ list: (options?: ListOptions) => Promise<TokenListResponse>;
1423
+ get: (token: string) => Promise<Token>;
1424
+ delete: (token: string) => Promise<TokenDeleteResponse>;
821
1425
  }
822
1426
  /**
823
1427
  * Billing status response from GET /billing/status
@@ -837,6 +1441,25 @@ interface BillingStatus {
837
1441
  /** Link to Creem customer portal for billing management, null if unavailable */
838
1442
  portal: string | null;
839
1443
  }
1444
+ /**
1445
+ * Acknowledgement of `POST /billing/cancel`.
1446
+ *
1447
+ * Cancelling leaves no billing entity to return, so it answers with the
1448
+ * account and the one field of the account the call changed — the plan it
1449
+ * landed on. See {@link DeploymentDeleteResponse} for the law.
1450
+ *
1451
+ * This read `{ success: true, message: 'Subscription canceled successfully…' }`
1452
+ * until 2026-07-29, an anonymous shape that `web/my` redeclared inline and
1453
+ * whose prose no surface ever displayed: both callers await the promise and
1454
+ * discard the body, then compose their own toast. The message was written,
1455
+ * serialized, and thrown away on every cancellation.
1456
+ */
1457
+ interface BillingCancelResponse {
1458
+ /** The account whose subscription was cancelled */
1459
+ readonly account: string;
1460
+ /** The plan the account now holds — `free` on a successful cancellation */
1461
+ readonly plan: AccountPlanType;
1462
+ }
840
1463
  /**
841
1464
  * Checkout session response from POST /billing/checkout
842
1465
  */
@@ -848,11 +1471,11 @@ interface CheckoutSession {
848
1471
  * All activity event types logged in the system.
849
1472
  * Uses dot notation consistently: {resource}.{action}
850
1473
  */
851
- type ActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.paid' | 'account.plan.transition' | 'account.suspended' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'deployment.flagged' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume' | 'admin.account.plan.update' | 'admin.account.ref.update' | 'admin.account.billing.update' | 'admin.account.labels.update' | 'admin.deployment.delete' | 'admin.domain.delete' | 'admin.billing.sync' | 'admin.billing.terminated' | 'admin.impersonate' | 'billing.active' | 'billing.canceled' | 'billing.paused' | 'billing.expired' | 'billing.paid' | 'billing.trialing' | 'billing.scheduled_cancel' | 'billing.unpaid' | 'billing.update' | 'billing.past_due' | 'refund.created' | 'dispute.created' | 'billing.sync' | 'billing.stale' | 'billing.race';
1474
+ type ActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.paid' | 'account.plan.transition' | 'account.suspended' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'deployment.flagged' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume' | 'token.delete' | 'admin.account.plan.update' | 'admin.account.ref.update' | 'admin.account.billing.update' | 'admin.account.labels.update' | 'admin.deployment.delete' | 'admin.domain.delete' | 'admin.billing.sync' | 'admin.billing.terminated' | 'admin.impersonate' | 'billing.active' | 'billing.canceled' | 'billing.paused' | 'billing.expired' | 'billing.paid' | 'billing.trialing' | 'billing.scheduled_cancel' | 'billing.unpaid' | 'billing.update' | 'billing.past_due' | 'refund.created' | 'dispute.created' | 'billing.sync' | 'billing.stale' | 'billing.race';
852
1475
  /**
853
1476
  * Activity events visible to users in the dashboard
854
1477
  */
855
- type UserVisibleActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.transition' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume';
1478
+ type UserVisibleActivityEvent = 'account.create' | 'account.update' | 'account.delete' | 'account.key.generate' | 'account.plan.transition' | 'deployment.create' | 'deployment.update' | 'deployment.delete' | 'deployment.claim' | 'domain.create' | 'domain.update' | 'domain.delete' | 'domain.verify' | 'token.create' | 'token.consume' | 'token.delete';
856
1479
  /**
857
1480
  * Activity record returned from the API
858
1481
  */
@@ -871,6 +1494,12 @@ interface Activity {
871
1494
  /**
872
1495
  * Parsed activity metadata.
873
1496
  * Different events populate different fields.
1497
+ *
1498
+ * Naming convention: meta booleans are event-scoped predicates and carry
1499
+ * their prefix (`isUpdate`, `wasVerified`, `hasConfig`, `hasPassword`),
1500
+ * while entity booleans are bare nouns (`Deployment.config`,
1501
+ * `Deployment.password`). Two vocabularies, each internally consistent —
1502
+ * deliberate, not drift.
874
1503
  */
875
1504
  interface ActivityMeta {
876
1505
  /** Number of files in deployment */
@@ -905,7 +1534,7 @@ interface ActivityMeta {
905
1534
  /**
906
1535
  * Response from GET /activities endpoint
907
1536
  */
908
- interface ActivityListResponse {
1537
+ interface ActivityListResponse extends ListResponse {
909
1538
  /** Array of activities */
910
1539
  activities: Activity[];
911
1540
  }
@@ -1109,20 +1738,17 @@ declare function validatePassword(value: unknown): string | undefined;
1109
1738
  * Extends the API contract (DeploymentUploadOptions) with SDK-specific options.
1110
1739
  */
1111
1740
  interface DeploymentOptions extends DeploymentUploadOptions {
1112
- /** An AbortSignal to allow cancellation of the deploy operation. */
1741
+ /**
1742
+ * An AbortSignal to allow cancellation of the deploy operation. The one
1743
+ * cancellation mechanism — abort the signal and the request rejects with
1744
+ * a typed `Cancelled` error. Request timeouts are a client concern
1745
+ * (`ShipClientOptions.timeout`), not a per-deploy one.
1746
+ */
1113
1747
  signal?: AbortSignal;
1114
- /** Callback invoked if the deploy is cancelled via the AbortSignal. */
1115
- onCancel?: () => void;
1116
- /** Maximum number of concurrent operations. */
1117
- maxConcurrency?: number;
1118
- /** Timeout in milliseconds for the deploy request. */
1119
- timeout?: number;
1120
1748
  /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */
1121
1749
  pathDetect?: boolean;
1122
1750
  /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */
1123
1751
  spaDetect?: boolean;
1124
- /** Callback for deploy progress with detailed statistics. */
1125
- onProgress?: (info: ProgressInfo) => void;
1126
1752
  }
1127
1753
  type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;
1128
1754
  /**
@@ -1143,8 +1769,13 @@ interface DeployBodyContext {
1143
1769
  * `LABEL_CONSTRAINTS` (length and pattern, lowercased+trimmed).
1144
1770
  */
1145
1771
  labels?: string[];
1146
- /** Client identifier (`cli`, `sdk`, `web`). */
1147
- via?: string;
1772
+ /**
1773
+ * Which client is deploying — the same closed vocabulary the public option
1774
+ * carries, not a second `string`. This context receives an already-narrowed
1775
+ * value and passed it on widened, which made the narrowing stop one seam
1776
+ * short of the wire.
1777
+ */
1778
+ via?: DeploymentViaType;
1148
1779
  /**
1149
1780
  * Optional plaintext password to protect the deployment.
1150
1781
  * Length: `PASSWORD_CONSTRAINTS.MIN_LENGTH` to `PASSWORD_CONSTRAINTS.MAX_LENGTH`
@@ -1177,7 +1808,7 @@ type Fetch = typeof fetch;
1177
1808
  type TokenProvider = () => string | Promise<string>;
1178
1809
  /**
1179
1810
  * Options for configuring a `Ship` instance.
1180
- * Sets default API host, the client credential, progress callbacks, concurrency, and timeouts for the client.
1811
+ * Sets the API host, the client credential, the request timeout, and the transport.
1181
1812
  */
1182
1813
  interface ShipClientOptions {
1183
1814
  /** Default API URL for the client instance. */
@@ -1198,19 +1829,8 @@ interface ShipClientOptions {
1198
1829
  */
1199
1830
  token?: string | TokenProvider | undefined;
1200
1831
  /**
1201
- * Default callback for deploy progress for deploys made with this client.
1202
- * @param info - Progress information including percentage and byte counts.
1203
- */
1204
- onProgress?: ((info: ProgressInfo) => void) | undefined;
1205
- /**
1206
- * Default for maximum concurrent deploys.
1207
- * Used if an deploy operation doesn't specify its own `maxConcurrency`.
1208
- * Defaults to 4 if not set here or in the specific deploy call.
1209
- */
1210
- maxConcurrency?: number | undefined;
1211
- /**
1212
- * Default timeout in milliseconds for API requests made by this client instance.
1213
- * Used if an deploy operation doesn't specify its own timeout.
1832
+ * Timeout in milliseconds for every API request made by this client
1833
+ * instance. Defaults to 30 seconds.
1214
1834
  */
1215
1835
  timeout?: number | undefined;
1216
1836
  /**
@@ -1277,7 +1897,19 @@ interface ShipEvents {
1277
1897
  request: [url: string, init: RequestInit];
1278
1898
  /** Emitted after successful API response */
1279
1899
  response: [response: Response, url: string];
1280
- /** Emitted when API request fails */
1900
+ /**
1901
+ * Emitted when something fails. TWO populations arrive here, which is why
1902
+ * the type is `Error` and not `ShipError`:
1903
+ *
1904
+ * - a failed request — always a `ShipError` (`executeRequest` normalizes
1905
+ * every failure through `ShipError.fromFetchError` before emitting), so
1906
+ * `isShipError(error)` narrows and `.type` / `.status` are readable;
1907
+ * - a THROWING HANDLER of yours — `SimpleEvents.emit` evicts it and
1908
+ * re-emits the raw failure here, which is a plain `Error`.
1909
+ *
1910
+ * Narrowing this to `ShipError` was tried on 2026-07-27 and reverted: it
1911
+ * made the second population a lie.
1912
+ */
1281
1913
  error: [error: Error, url: string];
1282
1914
  }
1283
1915
 
@@ -1325,6 +1957,8 @@ declare class ApiHttp extends SimpleEvents {
1325
1957
  private readonly session;
1326
1958
  private readonly caller;
1327
1959
  private readonly timeout;
1960
+ private readonly deployTimeout;
1961
+ private readonly deployBuildTimeout;
1328
1962
  private readonly fetch;
1329
1963
  private readonly createDeployBody;
1330
1964
  private readonly deployEndpoint;
@@ -1352,30 +1986,26 @@ declare class ApiHttp extends SimpleEvents {
1352
1986
  private safeClone;
1353
1987
  private parseResponse;
1354
1988
  deploy(files: StaticFile[], options?: ApiDeployOptions): Promise<DeploymentCreateResponse>;
1355
- listDeployments(): Promise<DeploymentListResponse>;
1989
+ listDeployments(options?: ListOptions): Promise<DeploymentListResponse>;
1356
1990
  getDeployment(id: string): Promise<Deployment>;
1357
1991
  updateDeploymentLabels(id: string, labels: string[]): Promise<Deployment>;
1358
- removeDeployment(id: string): Promise<void>;
1992
+ deleteDeployment(id: string): Promise<DeploymentDeleteResponse>;
1359
1993
  setDomain(name: string, deployment?: string, labels?: string[]): Promise<DomainSetResult>;
1360
- listDomains(): Promise<DomainListResponse>;
1994
+ listDomains(options?: ListOptions): Promise<DomainListResponse>;
1361
1995
  getDomain(name: string): Promise<Domain>;
1362
- removeDomain(name: string): Promise<void>;
1363
- verifyDomain(name: string): Promise<{
1364
- message: string;
1365
- }>;
1996
+ deleteDomain(name: string): Promise<DomainDeleteResponse>;
1997
+ verifyDomain(name: string): Promise<DomainVerifyResponse>;
1366
1998
  getDomainDns(name: string): Promise<DomainDnsResponse>;
1367
1999
  getDomainRecords(name: string): Promise<DomainRecordsResponse>;
1368
- getDomainShare(name: string): Promise<{
1369
- domain: string;
1370
- hash: string;
1371
- }>;
2000
+ getDomainShare(name: string): Promise<DomainShareResponse>;
1372
2001
  validateDomain(name: string): Promise<DomainValidateResponse>;
1373
2002
  createToken(ttl?: number, labels?: string[]): Promise<TokenCreateResponse>;
1374
- listTokens(): Promise<TokenListResponse>;
1375
- removeToken(token: string): Promise<void>;
2003
+ listTokens(options?: ListOptions): Promise<TokenListResponse>;
2004
+ deleteToken(token: string): Promise<TokenDeleteResponse>;
2005
+ getToken(token: string): Promise<Token>;
1376
2006
  getAccount(): Promise<AccountGetResponse>;
1377
2007
  getLimits(): Promise<PlatformLimits>;
1378
- ping(): Promise<boolean>;
2008
+ ping(): Promise<PingResponse>;
1379
2009
  checkSPA(files: StaticFile[], _options?: ApiDeployOptions): Promise<boolean>;
1380
2010
  }
1381
2011
 
@@ -1395,7 +2025,6 @@ interface ResourceContext {
1395
2025
  */
1396
2026
  interface DeploymentResourceContext extends ResourceContext {
1397
2027
  processInput: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>;
1398
- clientDefaults?: ShipClientOptions;
1399
2028
  }
1400
2029
  /**
1401
2030
  * Upload deployment resource with all CRUD operations.
@@ -1405,7 +2034,7 @@ interface DeploymentResourceContext extends ResourceContext {
1405
2034
  * public-account agent identity per request (claim URL + expiry on the
1406
2035
  * response). The SDK stays a transparent pipe either way.
1407
2036
  */
1408
- declare function createDeploymentResource(ctx: DeploymentResourceContext): DeploymentResource;
2037
+ declare function createDeploymentResource(ctx: DeploymentResourceContext): DeploymentResource<DeploymentOptions>;
1409
2038
  /**
1410
2039
  * Create domain resource with all CRUD operations.
1411
2040
  *
@@ -1427,7 +2056,7 @@ declare function createTokenResource(ctx: ResourceContext): TokenResource;
1427
2056
  * Abstract base class for Ship SDK implementations.
1428
2057
  */
1429
2058
  declare abstract class Ship$1 {
1430
- readonly deployments: DeploymentResource;
2059
+ readonly deployments: DeploymentResource<DeploymentOptions>;
1431
2060
  readonly domains: DomainResource;
1432
2061
  readonly account: AccountResource;
1433
2062
  readonly tokens: TokenResource;
@@ -1446,13 +2075,20 @@ declare abstract class Ship$1 {
1446
2075
  protected ensureInitialized(): Promise<void>;
1447
2076
  private fetchPlatformLimits;
1448
2077
  /**
1449
- * Ping the API server to check connectivity.
2078
+ * Ping the API server, resolving its answer: `{ success, timestamp }`, where
2079
+ * `timestamp` is the server clock in unix SECONDS.
2080
+ *
2081
+ * It resolves the response rather than a bare `true` because every other
2082
+ * method here does — narrowing to a boolean discarded the one thing ping
2083
+ * carries beyond liveness, and made `success` mean a boolean on the wire and
2084
+ * something else by the time it reached a caller. A non-OK response throws in
2085
+ * transport, so a resolved value always means the API answered.
1450
2086
  */
1451
- ping(): Promise<boolean>;
2087
+ ping(): Promise<PingResponse>;
1452
2088
  /**
1453
2089
  * Deploy project (convenience shortcut to `ship.deployments.upload()`).
1454
2090
  */
1455
- deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment>;
2091
+ deploy(input: DeployInput, options?: DeploymentOptions): Promise<DeploymentCreateResponse>;
1456
2092
  /**
1457
2093
  * Get current account information (convenience shortcut to `ship.account.get()`).
1458
2094
  */
@@ -1494,30 +2130,6 @@ declare abstract class Ship$1 {
1494
2130
  private getAuthHeaders;
1495
2131
  }
1496
2132
 
1497
- /**
1498
- * @file Cross-platform configuration helpers.
1499
- *
1500
- * One pure helper used by the deployment resource:
1501
- *
1502
- * - `mergeDeployOptions(perCallOptions, clientDefaults)` — overlays
1503
- * instance-level defaults under per-call overrides for a single deploy.
1504
- *
1505
- * Deploy options are pure deploy concerns (progress, timeout, concurrency).
1506
- * Credentials, the API URL, and the caller identifier are client identity —
1507
- * they live on the instance, never per call: one client is one principal
1508
- * speaking for one end user against one API. Callers that need a different
1509
- * identity construct another Ship.
1510
- */
1511
-
1512
- /**
1513
- * Overlay client-level defaults under per-call deploy options.
1514
- *
1515
- * Per-call options always win — they're the explicit override for a single
1516
- * `deployments.upload()`. Defaults fill in only when the per-call option is
1517
- * `undefined` (an explicit `null` / empty value passes through).
1518
- */
1519
- declare function mergeDeployOptions(options: DeploymentOptions, clientDefaults: ShipClientOptions): DeploymentOptions;
1520
-
1521
2133
  /**
1522
2134
  * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.
1523
2135
  * Automatically strips common parent directories to create clean deployment URLs.
@@ -1775,7 +2387,7 @@ declare function pluralize(count: number, singular: string, plural: string, incl
1775
2387
  * @param paths - File or directory paths to scan and process.
1776
2388
  * @param options - Processing options (pathDetect, etc.).
1777
2389
  * @param platformLimits - Per-instance platform limits (file-size / count /
1778
- * total-size caps) from the originating Ship's `GET /config` fetch. Passed
2390
+ * total-size caps) from the originating Ship's `GET /limits` fetch. Passed
1779
2391
  * in rather than read from a module global so concurrent Ships against
1780
2392
  * different API URLs cannot clobber each other's caps.
1781
2393
  * @returns Promise resolving to an array of StaticFile objects.
@@ -1827,9 +2439,12 @@ declare class Ship extends Ship$1 {
1827
2439
  * intentional: the convenience shortcut narrows; the resource-layer
1828
2440
  * contract stays platform-neutral.
1829
2441
  */
1830
- deploy(input: string | string[], options?: DeploymentOptions): Promise<Deployment>;
2442
+ deploy(input: string | string[], options?: DeploymentOptions): Promise<DeploymentCreateResponse>;
1831
2443
  protected processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
1832
2444
  protected getDeployBodyCreator(): DeployBodyCreator;
1833
2445
  }
1834
2446
 
1835
- export { API_KEY, type Account, type AccountGetResponse, type AccountOverrides, AccountPlan, type AccountPlanType, type AccountResource, type AccountUsage, type Activity, type ActivityEvent, type ActivityListResponse, type ActivityMeta, type ApiDeployOptions, ApiHttp, type ApiHttpOptions, AuthMethod, type AuthMethodType, BLOCKED_EXTENSIONS, type BillingStatus, CALLER, type CheckoutSession, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_TOKEN, type DeployBody, type DeployBodyContext, type DeployBodyCreator, type DeployFile, type DeployInput, type Deployment, type DeploymentCreateResponse, type DeploymentListResponse, type DeploymentOptions, type DeploymentResource, type DeploymentResourceContext, DeploymentStatus, type DeploymentStatusType, type DeploymentUploadOptions, type DnsProvider, type DnsRecord, type DnsRecordType, type Domain, type DomainDnsResponse, type DomainListResponse, type DomainRecordsResponse, type DomainResource, type DomainSetResult, DomainStatus, type DomainStatusType, type DomainValidateResponse, type ErrorResponse, ErrorType, type ExecutionEnvironment, FileValidationStatus as FILE_VALIDATION_STATUS, type Fetch, type FileValidationResult, FileValidationStatus, type FileValidationStatusType, JUNK_DIRECTORIES, LABEL_CONSTRAINTS, LABEL_PATTERN, type MD5Result, OAuthScope, type OAuthScopeType, PASSWORD_CONSTRAINTS, type PingResponse, type PlatformLimits, type ProgressInfo, type ResourceContext, type SPACheckRequest, type SPACheckResponse, SPA_DEFAULT_CONFIG, Ship, type ShipClientOptions, ShipError, type ShipEvents, type StaticFile, type TokenCreateResponse, TokenKind, type TokenKindType, type TokenListItem, type TokenListResponse, type TokenProvider, type TokenResource, UNBUILT_PROJECT_MARKERS, UNSAFE_FILENAME_CHARS, type UploadedFile, type UserVisibleActivityEvent, type ValidatableFile, type ValidationIssue, __setTestEnvironment, allValidFilesReady, calculateMD5, classifyToken, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, Ship as default, deserializeLabels, extractSubdomain, filterJunk, formatFileSize, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isCustomDomain, isDeployment, isPlatformDomain, isShipError, mergeDeployOptions, optimizeDeployPaths, pluralize, processFilesForNode, serializeLabels, validateApiKey, validateApiUrl, validateCaller, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validatePassword, validateToken };
2447
+ declare namespace Ship {
2448
+ export { API_KEY, API_PATHS, AUTH_BASE_PATH, Account, AccountDeleteResponse, AccountGetResponse, AccountKeyResponse, AccountOverrides, AccountPlan, AccountPlanType, AccountResource, AccountUsage, Activity, ActivityEvent, ActivityListResponse, ActivityMeta, ApiDeployOptions, ApiHttp, ApiHttpOptions, AuthMethod, AuthMethodType, BLOCKED_EXTENSIONS, BillingCancelResponse, BillingStatus, CALLER, CheckoutSession, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_FIELDS, DEPLOY_TOKEN, DeployBody, DeployBodyContext, DeployBodyCreator, DeployFile, DeployInput, Deployment, DeploymentCreateResponse, DeploymentDeleteResponse, DeploymentListResponse, DeploymentOptions, DeploymentResource, DeploymentResourceContext, DeploymentSetOptions, DeploymentStatus, DeploymentStatusType, DeploymentUploadOptions, DeploymentVia, DeploymentViaType, DnsLookup, DnsProvider, DnsRecord, DnsRecordType, Domain, DomainDeleteResponse, DomainDnsResponse, DomainListResponse, DomainRecordsResponse, DomainResource, DomainSetOptions, DomainSetResult, DomainShareResponse, DomainStatus, DomainStatusType, DomainValidateResponse, DomainVerifyResponse, ErrorResponse, ErrorType, ExecutionEnvironment, FileValidationStatus as FILE_VALIDATION_STATUS, Fetch, FileValidationResult, FileValidationStatus, FileValidationStatusType, IDEMPOTENCY_KEY_CONSTRAINTS, JUNK_DIRECTORIES, LABEL_CONSTRAINTS, LABEL_PATTERN, LabelsResponse, ListOptions, ListResponse, MD5Result, MY_API_KEY_URL, OAuthScope, OAuthScopeType, PASSWORD_CONSTRAINTS, PUBLIC_DEPLOYMENT_TTL_SECONDS, PingResponse, PlatformLimits, ResourceContext, SHIP_ENV, SPACheckDebug, SPACheckRequest, SPACheckResponse, SPA_CHECK_CONSTRAINTS, SPA_DEFAULT_CONFIG, SetupInstructionsResponse, ShipClientOptions, ShipError, ShipEvents, StaticFile, Token, TokenCreateOptions, TokenCreateResponse, TokenDeleteResponse, TokenKind, TokenKindType, TokenListResponse, TokenProvider, TokenResource, UNBUILT_PROJECT_MARKERS, UNSAFE_FILENAME_CHARS, UploadedFile, UserVisibleActivityEvent, ValidatableFile, ValidationIssue, WEB_FILE_ACCEPT, __setTestEnvironment, allValidFilesReady, assertShipJsonSyntax, calculateMD5, classifyToken, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, deserializeLabels, extractSubdomain, filterJunk, formatFileSize, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isCustomDomain, isDeployment, isPlatformDomain, isShipError, normalizeVia, optimizeDeployPaths, pluralize, processFilesForNode, serializeLabels, validateApiKey, validateApiUrl, validateCaller, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validateIdempotencyKey, validatePassword, validateToken };
2449
+ }
2450
+ export = Ship;