@shipstatic/ship 2.2.0-beta.7 → 2.2.0-beta.9

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
@@ -48,6 +48,7 @@ const ship = new Ship({ token: 'ship-...' });
48
48
  ```bash
49
49
  ship ./dist # Deploy (shortcut)
50
50
  ship ./dist --domain www.example.com # Deploy and serve it there
51
+ ship ./dist --ttl 1h # Expires in an hour
51
52
  ship ./dist --label production --label v1.0.0 # Deploy with labels
52
53
  ship deployments list
53
54
  ship deployments list --limit 20 # Page size; a hint shows the next cursor
@@ -106,7 +107,7 @@ ship.domains.set('www.münchen.de'); // → Unicode supported
106
107
  ### Tokens
107
108
 
108
109
  ```bash
109
- ship tokens create --ttl 3600 --label ci
110
+ ship tokens create --ttl 30d --label ci # Or 3600, 90s, 1h — one grammar
110
111
  ship tokens list
111
112
  ship tokens get <token>
112
113
  ship tokens delete <token>
@@ -153,6 +154,27 @@ open https://$(ship ./dist -q)
153
154
  ship deployments list -q | xargs -I{} ship deployments delete {} -q
154
155
  ```
155
156
 
157
+ ### Ephemeral deployments
158
+
159
+ ```bash
160
+ ship ./dist --ttl 1h # gone in an hour
161
+ ship ./dist --ttl 7d # a week-long preview
162
+ ship ./dist --ttl 3600 # bare seconds work too
163
+ ```
164
+
165
+ The platform reclaims the deployment when the time is up. Seconds, or a
166
+ `<n><unit>` duration (`s`/`m`/`h`/`d`) — the same grammar `ship tokens create
167
+ --ttl` uses. Bounded at one year.
168
+
169
+ Two rules, both refused before anything uploads. It **needs a token**: an
170
+ anonymous deployment already expires on the platform's own schedule, so there
171
+ is no deployer to choose a different one. And it **cannot be combined with
172
+ `--domain`**: a domain is a commitment and a deadline is its opposite, so the
173
+ API refuses to point a domain at a deployment that expires.
174
+
175
+ To keep something longer, deploy it again — there is no way to extend a
176
+ deployment's life, and no way to shorten it after the fact.
177
+
156
178
  `--domain` is the same two calls as one command:
157
179
 
158
180
  ```bash
@@ -192,6 +214,7 @@ Available on `ship <path>` and `ship deployments upload`:
192
214
  | `--domain <domain>` | Serve this deployment at that domain — creates or repoints it. Needs a token |
193
215
  | `--label <label>` | Add label (repeatable) |
194
216
  | `--password <password>` | Password-protect this deployment (6–128 chars) |
217
+ | `--ttl <duration>` | Expire this deployment after that long — `3600`, `90s`, `30m`, `1h`, `7d`. Needs a token; cannot be combined with `--domain` |
195
218
  | `--no-path-detect` | Disable automatic path optimization |
196
219
  | `--no-spa-detect` | Disable automatic SPA detection |
197
220
 
@@ -255,6 +278,7 @@ own `signal` (`AbortSignal.timeout(ms)`), which is never retried past.
255
278
  ship.deploy(input, {
256
279
  labels?: string[],
257
280
  password?: string, // Password-protect the deployment (6–128 chars)
281
+ ttl?: number, // Seconds until it expires (needs a token; max 1 year)
258
282
  signal?: AbortSignal, // Abort to cancel the deploy
259
283
  pathDetect?: boolean, // Auto-optimize paths (default: true)
260
284
  spaDetect?: boolean, // Auto-detect SPA (default: true)
@@ -262,6 +286,17 @@ ship.deploy(input, {
262
286
  });
263
287
  ```
264
288
 
289
+ #### Expiring deployments
290
+
291
+ Pass `ttl` in **seconds** and the platform reclaims the deployment when the time is up — 1 second to one year. The wire carries the duration and the API stamps `expires` against its own clock, so the answer says when:
292
+
293
+ ```typescript
294
+ const result = await ship.deploy('./dist', { ttl: 3600 });
295
+ // result.expires → unix seconds, one hour after result.created
296
+ ```
297
+
298
+ Needs a credential — an anonymous deployment already expires on the platform's own schedule. And a deployment carrying a ttl cannot be linked to a domain: the API refuses, which is what stops a domain pointing at something that is about to be reclaimed. There is no way to extend or shorten a deployment after the fact; redeploy instead.
299
+
265
300
  #### Password protection
266
301
 
267
302
  Pass `password` (6–128 characters) to gate the deployment behind a prompt. Visitors are asked for the password before they can view the site, including on any custom domains pointing at it. To remove protection, redeploy without a password.
package/SKILL.md CHANGED
@@ -43,6 +43,27 @@ Without credentials, deployments are public and expire in 3 days. **Always show
43
43
 
44
44
  The deployment ID **is** the URL hostname. Use the full ID (e.g. `happy-cat-abc1234.shipstatic.com`) as the argument to all other commands. The site lives at `https://<deployment>`.
45
45
 
46
+ ### Deployments that clean themselves up
47
+
48
+ ```bash
49
+ ship ./dist --ttl 1h # gone in an hour
50
+ ship ./dist --ttl 7d # a week-long preview
51
+ ```
52
+
53
+ For a preview nobody needs to keep — a draft, a diff, a one-off render. The
54
+ platform reclaims it when the time is up, so nothing accumulates in the user's
55
+ account and nobody has to remember to delete it. Seconds or a `<n><unit>`
56
+ duration (`s`/`m`/`h`/`d`), up to a year.
57
+
58
+ **It needs a token.** Without one the deploy is anonymous and already expires
59
+ in 3 days on the platform's own schedule — there is no deployer to choose a
60
+ different lifetime, and the CLI refuses before uploading anything. **It cannot
61
+ be combined with `--domain`**, because a domain must not point at something
62
+ about to be reclaimed.
63
+
64
+ The response's `expires` is the answer, in unix seconds — read it there rather
65
+ than computing it, since the platform stamps it against its own clock.
66
+
46
67
  ### Parsing output
47
68
 
48
69
  ```bash
@@ -238,6 +259,7 @@ List commands return `{"<resource>s": [...], "cursor": null}`. A non-null `curso
238
259
  ```bash
239
260
  ship ./dist # Deploy (shortcut)
240
261
  ship ./dist --domain <name> # Deploy and serve it at that domain
262
+ ship ./dist --ttl 1h # Expires in an hour (needs a token)
241
263
  ship deployments upload <path> # Deploy (explicit)
242
264
  ship deployments list # List all
243
265
  ship deployments get <deployment> # Details
@@ -265,7 +287,7 @@ ship domains delete <name> # Delete
265
287
  ship whoami # Account info
266
288
  ship ping # Connectivity check
267
289
  ship tokens create # New deploy token (shown once)
268
- ship tokens create --ttl 3600 # With expiry (seconds)
290
+ ship tokens create --ttl 30d # With expiry — 3600, 90s, 1h, 30d
269
291
  ship tokens list # List tokens
270
292
  ship tokens get <token> # Details for one token
271
293
  ship tokens delete <token> # Delete (revokes immediately)
@@ -281,6 +303,7 @@ ship tokens delete <token> # Delete (revokes immediately)
281
303
  | `--domain <domain>` | Deploy and serve it there — creates or repoints. Needs a token |
282
304
  | `--label <label>` | Set label (repeatable, replaces all) |
283
305
  | `--password <pwd>` | Password-protect deployment (6–128 chars) |
306
+ | `--ttl <duration>` | Expire after that long — `3600`, `90s`, `1h`, `7d`. Needs a token; not with `--domain` |
284
307
  | `--no-path-detect` | Skip build output auto-detection |
285
308
  | `--no-spa-detect` | Skip SPA rewrite auto-configuration |
286
309
  | `--no-color` | Disable colors |
@@ -295,6 +318,8 @@ ship tokens delete <token> # Delete (revokes immediately)
295
318
  | `not found` | No such resource | Verify the ID/name |
296
319
  | `path does not exist` | Bad deploy path | Check file/directory |
297
320
  | `invalid domain name` | Not a subdomain | Use `www.example.com`, not `example.com` |
321
+ | `--ttl sets an expiry, which needs a token` | `--ttl` without credentials | Set an API key, or drop `--ttl` |
322
+ | `--ttl and --domain cannot be combined` | Both flags given | A domain cannot point at an expiring deployment — pick one |
298
323
  | `<resource> limit reached` | Plan caps hit (deployments, domains) | Suggest upgrading the plan; do not retry |
299
324
  | `Account has been deleted` / `Account terminated` | Account is gone | Stop; the account cannot deploy |
300
325
  | `DNS information is only available for external domains` | DNS op on internal domain | Only custom domains need DNS |
@@ -5,7 +5,7 @@ Their copyright notices travel with that copy, and are reproduced here in
5
5
  full. This file is GENERATED from the build's own metafile — edit the
6
6
  bundle, not this list.
7
7
 
8
- ## @shipstatic/types 2.7.0-beta.3
8
+ ## @shipstatic/types 2.7.0-beta.4
9
9
 
10
10
  License: MIT
11
11
 
@@ -160,62 +160,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
160
160
  THE SOFTWARE.
161
161
  ```
162
162
 
163
- ## form-data-encoder 4.1.0
164
-
165
- License: MIT
166
-
167
- ```
168
- The MIT License (MIT)
169
-
170
- Copyright (c) 2021-present Nick K.
171
-
172
- Permission is hereby granted, free of charge, to any person obtaining a copy
173
- of this software and associated documentation files (the "Software"), to deal
174
- in the Software without restriction, including without limitation the rights
175
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
176
- copies of the Software, and to permit persons to whom the Software is
177
- furnished to do so, subject to the following conditions:
178
-
179
- The above copyright notice and this permission notice shall be included in all
180
- copies or substantial portions of the Software.
181
-
182
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
183
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
185
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
186
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
187
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
188
- SOFTWARE.
189
- ```
190
-
191
- ## formdata-node 6.0.3
192
-
193
- License: MIT
194
-
195
- ```
196
- The MIT License (MIT)
197
-
198
- Copyright (c) 2017-present Nick K.
199
-
200
- Permission is hereby granted, free of charge, to any person obtaining a copy
201
- of this software and associated documentation files (the "Software"), to deal
202
- in the Software without restriction, including without limitation the rights
203
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
204
- copies of the Software, and to permit persons to whom the Software is
205
- furnished to do so, subject to the following conditions:
206
-
207
- The above copyright notice and this permission notice shall be included in all
208
- copies or substantial portions of the Software.
209
-
210
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
211
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
212
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
213
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
214
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
215
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
216
- SOFTWARE.
217
- ```
218
-
219
163
  ## junk 4.0.1
220
164
 
221
165
  License: MIT
package/dist/browser.d.ts CHANGED
@@ -671,6 +671,12 @@ declare const DEPLOY_FIELDS: {
671
671
  readonly VIA: "via";
672
672
  /** Plaintext password — the API hashes it server-side. */
673
673
  readonly PASSWORD: "password";
674
+ /**
675
+ * Requested lifetime in SECONDS — a duration, never an instant. The API
676
+ * computes and stores the expiry, so the wire carries no client clock.
677
+ * See {@link validateTtl}.
678
+ */
679
+ readonly TTL: "ttl";
674
680
  /** @internal Server-processing flag — first-party `/upload` only. */
675
681
  readonly BUILD: "build";
676
682
  /** @internal Server-processing flag — first-party `/upload` only. */
@@ -1259,6 +1265,47 @@ declare function validateApiUrl(apiUrl: string): void;
1259
1265
  * Example: "happy-cat-abc1234.shipstatic.com"
1260
1266
  */
1261
1267
  declare function isDeployment(input: string): boolean;
1268
+ /**
1269
+ * The envelope a requested lifetime must fit — one word, one grammar, wherever
1270
+ * the platform lets a caller choose how long something lives.
1271
+ *
1272
+ * Two resources wear it: `TokenCreateOptions.ttl` and
1273
+ * `DeploymentUploadOptions.ttl`. It lives here rather than on the server by
1274
+ * the format-vs-policy rule — a client can decide offline whether a duration
1275
+ * is well-formed, and the API rejects the same value the same way. What is
1276
+ * NOT here is any per-plan ceiling: no such policy exists, and one delivered
1277
+ * speculatively through `/limits` would be an owner for a decision nobody has
1278
+ * made.
1279
+ */
1280
+ declare const TTL_CONSTRAINTS: {
1281
+ /**
1282
+ * Shortest requestable lifetime, in seconds. One rather than zero: a
1283
+ * deployment that expires the instant it is created is not a shorter lease,
1284
+ * it is a deploy that was never live, and `0` is how an unset variable
1285
+ * arrives.
1286
+ */
1287
+ readonly MIN_SECONDS: 1;
1288
+ /** Longest requestable lifetime, in seconds — one year. */
1289
+ readonly MAX_SECONDS: number;
1290
+ };
1291
+ /**
1292
+ * Validate a requested lifetime in SECONDS and return it, or `undefined` when
1293
+ * none was asked for.
1294
+ *
1295
+ * **A duration, never an instant.** The caller says how long; the server owns
1296
+ * what time it is and stamps the expiry — so a client's clock, however wrong,
1297
+ * cannot shorten or extend a lease. That is the tokens precedent, and it is
1298
+ * why this rule measures a count of seconds rather than checking a timestamp
1299
+ * against `now`.
1300
+ *
1301
+ * Fractions are refused rather than rounded: a caller who wrote `1.5` meant
1302
+ * something the wire cannot carry, and silently choosing `1` or `2` for them
1303
+ * is a decision the platform has no standing to make.
1304
+ *
1305
+ * Single source of truth shared by the API (the tokens route and the deploy
1306
+ * schema), the SDK's request boundary, and the CLI's parser.
1307
+ */
1308
+ declare function validateTtl(value: unknown): number | undefined;
1262
1309
  /**
1263
1310
  * Request payload for SPA check endpoint
1264
1311
  */
@@ -1391,6 +1438,27 @@ interface DeploymentUploadOptions {
1391
1438
  * into missing analytics rather than an error. See {@link DeploymentVia}.
1392
1439
  */
1393
1440
  via?: DeploymentViaType;
1441
+ /**
1442
+ * Seconds until this deployment expires; omit for one that never does.
1443
+ *
1444
+ * The platform reclaims it when the time is up — an ephemeral deployment,
1445
+ * chosen by the deployer rather than by the identity. The same word and the
1446
+ * same grammar as {@link TokenCreateOptions.ttl}, bounded by
1447
+ * {@link TTL_CONSTRAINTS}.
1448
+ *
1449
+ * **Requires a credential.** An anonymous deploy has no deployer, and the
1450
+ * platform owns anonymous lifetime as policy
1451
+ * ({@link PUBLIC_DEPLOYMENT_TTL_SECONDS}) — so a ttl on one is refused
1452
+ * rather than honoured or ignored.
1453
+ *
1454
+ * **A deployment carrying one cannot be linked to a domain.** A domain is a
1455
+ * commitment and a deadline is its opposite; the API refuses the link, which
1456
+ * is what keeps the reaper from tearing a live domain's target away.
1457
+ *
1458
+ * Immutable, like every other field of a deployment: to keep something
1459
+ * longer, redeploy.
1460
+ */
1461
+ ttl?: number;
1394
1462
  /**
1395
1463
  * Optional password that protects this deployment.
1396
1464
  *
@@ -1829,14 +1897,6 @@ interface DeploymentOptions extends DeploymentUploadOptions {
1829
1897
  spaDetect?: boolean;
1830
1898
  }
1831
1899
  type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;
1832
- /**
1833
- * Prepared request body for deployment.
1834
- * Created by platform-specific code, consumed by HTTP client.
1835
- */
1836
- interface DeployBody {
1837
- body: FormData | ArrayBuffer;
1838
- headers: Record<string, string>;
1839
- }
1840
1900
  /**
1841
1901
  * Context passed to the deploy body creator — everything that becomes a
1842
1902
  * form field alongside the files themselves.
@@ -1860,6 +1920,11 @@ interface DeployBodyContext {
1860
1920
  * characters. Whitespace is preserved verbatim — significant.
1861
1921
  */
1862
1922
  password?: string;
1923
+ /**
1924
+ * Requested lifetime in SECONDS — a duration, never an instant, bounded by
1925
+ * `TTL_CONSTRAINTS`. The API stamps the expiry against its own clock.
1926
+ */
1927
+ ttl?: number;
1863
1928
  /** @internal Server-side processing flags. */
1864
1929
  flags?: {
1865
1930
  build?: boolean;
@@ -1869,11 +1934,6 @@ interface DeployBodyContext {
1869
1934
  /** @internal reCAPTCHA proof for the anonymous human deploy channel (/upload). */
1870
1935
  captcha?: string;
1871
1936
  }
1872
- /**
1873
- * Function that creates a deploy request body from files.
1874
- * Implemented differently for Node.js and Browser.
1875
- */
1876
- type DeployBodyCreator = (files: StaticFile[], context?: DeployBodyContext) => Promise<DeployBody>;
1877
1937
  /** Standard `fetch` signature — the type of the `fetch` client option. */
1878
1938
  type Fetch = typeof fetch;
1879
1939
  /**
@@ -2071,28 +2131,72 @@ declare class SimpleEvents {
2071
2131
  emit<K extends keyof ShipEvents>(event: K, ...args: ShipEvents[K]): void;
2072
2132
  }
2073
2133
 
2074
- /**
2075
- * @file HTTP client for Ship API.
2076
- */
2077
-
2078
2134
  interface ApiHttpOptions extends ShipClientOptions {
2079
2135
  /** Resolves the credential slot per request — async so token providers can mint/refresh. */
2080
2136
  getAuthHeaders: () => Record<string, string> | Promise<Record<string, string>>;
2081
- createDeployBody: DeployBodyCreator;
2082
2137
  }
2083
- declare class ApiHttp extends SimpleEvents {
2138
+ interface RequestResult<T> {
2139
+ data: T;
2140
+ status: number;
2141
+ }
2142
+ /**
2143
+ * The deploy's CARRIAGE — the two facts about a deploy that are transport's
2144
+ * rather than the deployment resource's.
2145
+ *
2146
+ * The numbers are transport's because a budget for how long to wait on a wire
2147
+ * is nothing else, and the endpoint is transport's because `deployEndpoint` is
2148
+ * a client option that redirects the route. The CHOICE between the two
2149
+ * ceilings is the resource's, because only it knows that `build`/`prerender`
2150
+ * wait on work the server does after the upload lands.
2151
+ */
2152
+ interface DeployTransport {
2153
+ /** `/deployments`, or `/upload` where the `@internal` option redirects it. */
2154
+ readonly endpoint: string;
2155
+ /** The ordinary deploy ceiling. */
2156
+ readonly timeout: number;
2157
+ /** The ceiling when the server will also build. */
2158
+ readonly buildTimeout: number;
2159
+ }
2160
+ /**
2161
+ * What a resource may ask of the transport: carry this request, and tell me
2162
+ * what came back.
2163
+ *
2164
+ * This interface is the whole seam. `resources.ts` states WHICH request — the
2165
+ * path, the verb, the body, the response type — and hands it here; nothing
2166
+ * above this line knows the base URL, the credential, the retry policy or the
2167
+ * event vocabulary, and nothing below knows what a domain is.
2168
+ */
2169
+ interface Transport {
2170
+ request<T>(path: string, options: ShipRequestInit, operationName: string, timeoutMs?: number): Promise<T>;
2171
+ requestWithStatus<T>(path: string, options: ShipRequestInit, operationName: string): Promise<RequestResult<T>>;
2172
+ readonly deploy: DeployTransport;
2173
+ }
2174
+ /**
2175
+ * A request as THIS client composes one.
2176
+ *
2177
+ * Identical to `RequestInit` but for the headers, which are narrowed from the
2178
+ * DOM's three-shaped `HeadersInit` to the one shape every call site here
2179
+ * actually builds. That narrowing is load-bearing twice over: `mergeHeaders`
2180
+ * used to reach its record through an `as` cast, and `hasIdempotencyKey` used
2181
+ * to walk all three shapes to find a key that only ever arrives in one of
2182
+ * them. Narrowing at RUNTIME instead would have turned an unreachable case
2183
+ * into a SILENT no-retry — a deploy that quietly stopped replaying because
2184
+ * someone handed the transport a `Headers`. Here that is a compile error.
2185
+ */
2186
+ type ShipRequestInit = Omit<RequestInit, 'headers'> & {
2187
+ headers?: Record<string, string>;
2188
+ };
2189
+ declare class ApiHttp extends SimpleEvents implements Transport {
2084
2190
  private readonly apiUrl;
2085
2191
  private readonly getAuthHeadersCallback;
2086
2192
  private readonly session;
2087
2193
  private readonly caller;
2088
2194
  private readonly timeout;
2089
2195
  private readonly maxRetries;
2090
- private readonly deployTimeout;
2091
- private readonly deployBuildTimeout;
2092
2196
  private readonly fetch;
2093
- private readonly createDeployBody;
2094
- private readonly deployEndpoint;
2095
2197
  private globalHeaders;
2198
+ /** @see DeployTransport — the carriage facts the deployment resource reads. */
2199
+ readonly deploy: DeployTransport;
2096
2200
  constructor(options: ApiHttpOptions);
2097
2201
  /**
2098
2202
  * Set global headers included in every request.
@@ -2131,7 +2235,13 @@ declare class ApiHttp extends SimpleEvents {
2131
2235
  * is one that may be sent twice.
2132
2236
  */
2133
2237
  private isRetryable;
2134
- /** Did this request carry the header that makes a repeat safe? */
2238
+ /**
2239
+ * Did this request carry the header that makes a repeat safe?
2240
+ *
2241
+ * Case-insensitively, because HTTP field names are — the CLI's env tier and
2242
+ * the SDK option both spell it canonically, but a caller composing headers
2243
+ * by hand is entitled not to.
2244
+ */
2135
2245
  private hasIdempotencyKey;
2136
2246
  /**
2137
2247
  * One attempt: headers, timeout signal, the `request`/`response` events, and
@@ -2143,51 +2253,56 @@ declare class ApiHttp extends SimpleEvents {
2143
2253
  */
2144
2254
  private attemptOnce;
2145
2255
  /**
2146
- * Simple request - returns data only
2256
+ * Send it; resolve what came back.
2257
+ *
2258
+ * Takes a PATH, not a URL: the base is this client's and nothing above needs
2259
+ * to know it. Twenty-two call sites wrote `${this.apiUrl}${API_PATHS.X}` by
2260
+ * hand before the endpoints moved out, which is twenty-two chances to
2261
+ * assemble it differently.
2147
2262
  */
2148
- private request;
2263
+ request<T>(path: string, options: ShipRequestInit, operationName: string, timeoutMs?: number): Promise<T>;
2149
2264
  /**
2150
- * Request with status - returns data and HTTP status code
2265
+ * The same, plus the HTTP status for the one operation where the status IS
2266
+ * the answer: a domain upsert says create-or-update in its 201/200 and
2267
+ * nowhere else in the response.
2151
2268
  */
2152
- private requestWithStatus;
2269
+ requestWithStatus<T>(path: string, options: ShipRequestInit, operationName: string): Promise<RequestResult<T>>;
2153
2270
  private mergeHeaders;
2154
2271
  private createTimeoutSignal;
2155
2272
  private safeClone;
2156
2273
  private parseResponse;
2157
- deploy(files: StaticFile[], options?: ApiDeployOptions): Promise<DeploymentCreateResponse>;
2158
- listDeployments(options?: ListOptions): Promise<DeploymentListResponse>;
2159
- getDeployment(id: string): Promise<Deployment>;
2160
- updateDeploymentLabels(id: string, labels: string[]): Promise<Deployment>;
2161
- deleteDeployment(id: string): Promise<DeploymentDeleteResponse>;
2162
- setDomain(name: string, deployment?: string, labels?: string[]): Promise<DomainSetResult>;
2163
- listDomains(options?: ListOptions): Promise<DomainListResponse>;
2164
- getDomain(name: string): Promise<Domain>;
2165
- deleteDomain(name: string): Promise<DomainDeleteResponse>;
2166
- verifyDomain(name: string): Promise<DomainVerifyResponse>;
2167
- getDomainDns(name: string): Promise<DomainDnsResponse>;
2168
- getDomainRecords(name: string): Promise<DomainRecordsResponse>;
2169
- getDomainShare(name: string): Promise<DomainShareResponse>;
2170
- validateDomain(name: string): Promise<DomainValidateResponse>;
2171
- createToken(ttl?: number, labels?: string[]): Promise<TokenCreateResponse>;
2172
- listTokens(options?: ListOptions): Promise<TokenListResponse>;
2173
- deleteToken(token: string): Promise<TokenDeleteResponse>;
2174
- getToken(token: string): Promise<Token>;
2175
- getAccount(): Promise<AccountGetResponse>;
2176
- getLimits(): Promise<PlatformLimits>;
2177
- ping(): Promise<PingResponse>;
2178
- checkSPA(files: StaticFile[], _options?: ApiDeployOptions): Promise<boolean>;
2179
2274
  }
2180
2275
 
2181
2276
  /**
2182
- * Ship SDK resource factory functions.
2277
+ * @file The SDK's vocabulary every request it can make, stated once.
2278
+ *
2279
+ * A resource method IS its endpoint: the path, the verb, the body, the
2280
+ * response type. It hands that to the transport, which knows how to carry a
2281
+ * request and nothing about what one means.
2282
+ *
2283
+ * **These were two layers until 2026-08-12.** `ApiHttp` carried eighteen
2284
+ * endpoint methods and every factory below wrapped one of them 1:1 —
2285
+ * `get: async (name) => getApi().getDomain(name)` — because this SDK mirrors
2286
+ * the wire one method per endpoint BY DESIGN (see CLAUDE.md, "Recorded
2287
+ * absences"). That design is exactly what made the second layer a restatement
2288
+ * rather than an adapter: the two could not diverge without one of them being
2289
+ * wrong. Folding DOWN rather than up is what keeps the public grouping and the
2290
+ * transport separate, which was the whole point of having two files.
2291
+ *
2292
+ * The `*Resource` interfaces come from `@shipstatic/types` and did not move.
2293
+ * They are the published contract; this file is how it is met.
2183
2294
  */
2184
2295
 
2185
2296
  /**
2186
2297
  * Shared context for all resource factories.
2298
+ *
2299
+ * A factory receives the callbacks it needs and nothing else — which is what
2300
+ * lets `getApi()` be a THUNK rather than an instance: the transport is built
2301
+ * once in the constructor, but reading it lazily is what keeps the resources
2302
+ * constructible before it exists and swappable in tests.
2187
2303
  */
2188
2304
  interface ResourceContext {
2189
- getApi: () => ApiHttp;
2190
- ensureInit: () => Promise<void>;
2305
+ getApi: () => Transport;
2191
2306
  }
2192
2307
  /**
2193
2308
  * Extended context for deployment resource.
@@ -2236,7 +2351,6 @@ declare abstract class Ship$1 {
2236
2351
  private credential;
2237
2352
  constructor(options?: ShipClientOptions);
2238
2353
  protected abstract processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
2239
- protected abstract getDeployBodyCreator(): DeployBodyCreator;
2240
2354
  /**
2241
2355
  * Lazy initialization — fetches platform limits (file size / count caps) once,
2242
2356
  * on the first API call. Subsequent calls reuse the resolved promise.
@@ -2601,23 +2715,26 @@ declare function validateDeployFile(input: FileRuleInput, limits: PlatformLimits
2601
2715
  declare function pluralize(count: number, singular: string, plural: string, includeCount?: boolean): string;
2602
2716
 
2603
2717
  /**
2604
- * @file Browser-specific file utilities for the Ship SDK.
2605
- * Provides helpers for processing browser files into deploy-ready objects.
2718
+ * @file The browser half of the deploy pipeline: reading paths off `File`s.
2606
2719
  *
2607
- * Two modes:
2608
- * - **Deploy** (default): Full validation pipeline security, extensions, sizes, counts.
2609
- * - **Server-processed** (build/prerender): Source files destined for server-side build.
2610
- * Junk filtering and MD5 checksums only — the build service validates the output.
2720
+ * Everything after is shared (`shared/core/deploy-files.ts`) — path
2721
+ * optimization, junk filtering, the platform's rules, the checksums. What is
2722
+ * genuinely browser here is one line: a directory drop carries
2723
+ * `webkitRelativePath`, a file picker does not.
2611
2724
  *
2612
- * Both modes share: environment check extract paths optimize paths filter junk → MD5.
2725
+ * There is no size to look up and no content to fetch, because a `File`
2726
+ * already is both. That is the whole of this platform's collection step, and
2727
+ * it is why the seam the Node side needs (`size` before `read()`) costs
2728
+ * nothing on this one.
2613
2729
  */
2614
2730
 
2615
2731
  /**
2616
2732
  * Processes browser files into an array of StaticFile objects ready for deploy.
2617
- * Calculates MD5, filters junk files, validates sizes, and applies path optimization.
2618
2733
  *
2619
- * For server-processed uploads (build/prerender), client-side deploy validation is
2620
- * skipped — the build service produces and validates the actual deployment output.
2734
+ * For server-processed uploads (`build`/`prerender`), the shared processor
2735
+ * skips deploy validation — the build service produces and validates the
2736
+ * actual deployment output. Those flags are `@internal` and this is their only
2737
+ * platform: `web/my` and `web/www` set them through `/upload`.
2621
2738
  *
2622
2739
  * @param browserFiles - File[] to process for deploy.
2623
2740
  * @param options - Processing options including pathDetect for automatic path optimization.
@@ -2666,7 +2783,6 @@ declare class Ship extends Ship$1 {
2666
2783
  */
2667
2784
  deploy(input: File[], options?: DeploymentOptions): Promise<DeploymentCreateResponse>;
2668
2785
  protected processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
2669
- protected getDeployBodyCreator(): DeployBodyCreator;
2670
2786
  }
2671
2787
 
2672
- export { API_KEY, API_PATHS, AUTH_BASE_PATH, type Account, type AccountDeleteResponse, type AccountGetResponse, type AccountKeyResponse, 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, type BillingCancelResponse, type BillingStatus, CALLER, type CheckoutSession, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_FIELDS, DEPLOY_TOKEN, type DeployBody, type DeployBodyContext, type DeployBodyCreator, type DeployFile, type DeployInput, type Deployment, type DeploymentCreateResponse, type DeploymentDeleteResponse, type DeploymentListResponse, type DeploymentOptions, type DeploymentResource, type DeploymentResourceContext, type DeploymentSetOptions, DeploymentStatus, type DeploymentStatusType, type DeploymentUploadOptions, DeploymentVia, type DeploymentViaType, type DnsLookup, type DnsProvider, type DnsRecord, type DnsRecordType, type Domain, type DomainDeleteResponse, type DomainDnsResponse, type DomainListResponse, type DomainRecordsResponse, type DomainResource, type DomainSetOptions, type DomainSetResult, type DomainShareResponse, DomainStatus, type DomainStatusType, type DomainValidateResponse, type DomainVerifyResponse, type ErrorResponse, ErrorType, type ExecutionEnvironment, FileValidationStatus as FILE_VALIDATION_STATUS, type Fetch, type FileValidationResult, FileValidationStatus, type FileValidationStatusType, IDEMPOTENCY_KEY_CONSTRAINTS, JUNK_DIRECTORIES, LABEL_CONSTRAINTS, LABEL_PATTERN, type LabelsResponse, type ListOptions, type ListResponse, type MD5Result, MY_API_KEY_URL, OAuthScope, type OAuthScopeType, PASSWORD_CONSTRAINTS, PUBLIC_DEPLOYMENT_TTL_SECONDS, type PingResponse, type PlatformLimits, type ResourceContext, SHIP_ENV, type SPACheckDebug, type SPACheckRequest, type SPACheckResponse, SPA_CHECK_CONSTRAINTS, SPA_DEFAULT_CONFIG, type SetupInstructionsResponse, Ship, type ShipClientOptions, ShipError, type ShipEvents, type StaticFile, type Token, type TokenCreateOptions, type TokenCreateResponse, type TokenDeleteResponse, TokenKind, type TokenKindType, type TokenListResponse, type TokenProvider, type TokenResource, UNBUILT_PROJECT_MARKERS, UNSAFE_FILENAME_CHARS, type UploadedFile, type UserVisibleActivityEvent, type ValidatableFile, type ValidationIssue, WEB_FILE_ACCEPT, __setTestEnvironment, allValidFilesReady, assertShipJsonSyntax, calculateMD5, classifyToken, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, Ship as default, deserializeLabels, extractSubdomain, filterJunk, formatFileSize, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isCustomDomain, isDeployment, isPlatformDomain, isShipError, normalizeVia, optimizeDeployPaths, pluralize, processFilesForBrowser, serializeLabels, validateApiKey, validateApiUrl, validateCaller, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validateIdempotencyKey, validatePassword, validateToken };
2788
+ export { API_KEY, API_PATHS, AUTH_BASE_PATH, type Account, type AccountDeleteResponse, type AccountGetResponse, type AccountKeyResponse, 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, type BillingCancelResponse, type BillingStatus, CALLER, type CheckoutSession, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_FIELDS, DEPLOY_TOKEN, type DeployBodyContext, type DeployFile, type DeployInput, type DeployTransport, type Deployment, type DeploymentCreateResponse, type DeploymentDeleteResponse, type DeploymentListResponse, type DeploymentOptions, type DeploymentResource, type DeploymentResourceContext, type DeploymentSetOptions, DeploymentStatus, type DeploymentStatusType, type DeploymentUploadOptions, DeploymentVia, type DeploymentViaType, type DnsLookup, type DnsProvider, type DnsRecord, type DnsRecordType, type Domain, type DomainDeleteResponse, type DomainDnsResponse, type DomainListResponse, type DomainRecordsResponse, type DomainResource, type DomainSetOptions, type DomainSetResult, type DomainShareResponse, DomainStatus, type DomainStatusType, type DomainValidateResponse, type DomainVerifyResponse, type ErrorResponse, ErrorType, type ExecutionEnvironment, FileValidationStatus as FILE_VALIDATION_STATUS, type Fetch, type FileValidationResult, FileValidationStatus, type FileValidationStatusType, IDEMPOTENCY_KEY_CONSTRAINTS, JUNK_DIRECTORIES, LABEL_CONSTRAINTS, LABEL_PATTERN, type LabelsResponse, type ListOptions, type ListResponse, type MD5Result, MY_API_KEY_URL, OAuthScope, type OAuthScopeType, PASSWORD_CONSTRAINTS, PUBLIC_DEPLOYMENT_TTL_SECONDS, type PingResponse, type PlatformLimits, type RequestResult, type ResourceContext, SHIP_ENV, type SPACheckDebug, type SPACheckRequest, type SPACheckResponse, SPA_CHECK_CONSTRAINTS, SPA_DEFAULT_CONFIG, type SetupInstructionsResponse, Ship, type ShipClientOptions, ShipError, type ShipEvents, type ShipRequestInit, type StaticFile, TTL_CONSTRAINTS, type Token, type TokenCreateOptions, type TokenCreateResponse, type TokenDeleteResponse, TokenKind, type TokenKindType, type TokenListResponse, type TokenProvider, type TokenResource, type Transport, UNBUILT_PROJECT_MARKERS, UNSAFE_FILENAME_CHARS, type UploadedFile, type UserVisibleActivityEvent, type ValidatableFile, type ValidationIssue, WEB_FILE_ACCEPT, __setTestEnvironment, allValidFilesReady, assertShipJsonSyntax, calculateMD5, classifyToken, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, Ship as default, deserializeLabels, extractSubdomain, filterJunk, formatFileSize, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isCustomDomain, isDeployment, isPlatformDomain, isShipError, normalizeVia, optimizeDeployPaths, pluralize, processFilesForBrowser, serializeLabels, validateApiKey, validateApiUrl, validateCaller, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validateIdempotencyKey, validatePassword, validateToken, validateTtl };