@shipstatic/ship 3.4.0-beta.1 → 3.4.0-beta.11

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
@@ -132,7 +132,7 @@ ship ping
132
132
  ```typescript
133
133
  ship.account.get() // → whoami
134
134
  ship.ping() // → { timestamp } (server clock; reachability is the absence of a throw)
135
- ship.getLimits() // → platform plan limits (cached)
135
+ ship.getLimits() // → the account's platform limits, read now
136
136
  ```
137
137
 
138
138
  ## CLI reference
@@ -216,7 +216,6 @@ Available on `ship <path>` and `ship deployments upload`:
216
216
  | `--password <password>` | Password-protect this deployment (6–128 chars) |
217
217
  | `--ttl <duration>` | Expire this deployment after that long — `3600`, `90s`, `30m`, `1h`, `7d`. Needs a token; cannot be combined with `--domain` |
218
218
  | `--no-path-detect` | Disable automatic path optimization |
219
- | `--no-spa-detect` | Disable automatic SPA detection |
220
219
 
221
220
  ### CLI environment variables
222
221
 
@@ -246,8 +245,9 @@ const ship = new Ship({ token: accessToken });
246
245
  // Token provider — invoked per request; refresh lives with you
247
246
  const ship = new Ship({ token: () => mintToken() });
248
247
 
249
- // Cookie session — first-party browser apps
248
+ // Cookie session: the platform's own apps, where every account request names its account
250
249
  const ship = new Ship({ session: true });
250
+ ship.setHeaders({ 'X-Account': account });
251
251
 
252
252
  // Set or rotate the token after construction
253
253
  ship.setToken('ship-your-api-key');
@@ -281,7 +281,6 @@ ship.deploy(input, {
281
281
  ttl?: number, // Seconds until it expires (needs a token; max 1 year)
282
282
  signal?: AbortSignal, // Abort to cancel the deploy
283
283
  pathDetect?: boolean, // Auto-optimize paths (default: true)
284
- spaDetect?: boolean, // Auto-detect SPA (default: true)
285
284
  via?: string, // Client identifier
286
285
  });
287
286
  ```
package/SKILL.md CHANGED
@@ -114,9 +114,9 @@ SHIP_PASSWORD="hunter22" ship ./dist # via env var
114
114
 
115
115
  Visitors get an unlock page until they enter the password. Length: 6–128 characters. Set per-deployment at upload time — cannot be added or changed later (deploy a new version to rotate). Works on both internal (`*.shipstatic.com`) and custom domains. **Always show the password to the user** if you set one — they need it to view the site.
116
116
 
117
- ### SPA routing
117
+ ### Client-side routing
118
118
 
119
- Ship auto-detects single-page apps from `index.html` content and configures client-side routing rewrites — all paths serve `index.html`. No action needed. Skipped if a `ship.json` config is already included in the deployment. Disable with `--no-spa-detect`.
119
+ A deployment whose only page is `index.html` serves it for a missing path that has no file extension, so a React, Vue, Svelte or Angular build works on its routes with nothing to configure; a missing asset, or a route whose last segment looks like a file name, is still a 404 (a rewrite covers those). A root `200.html` is served the same way by name; a root `404.html` of your own is served for a missing path instead; your `ship.json` rewrites run before the fallback.
120
120
 
121
121
  ## Authentication
122
122
 
@@ -324,7 +324,6 @@ ship tokens delete <token> # Delete (revokes immediately)
324
324
  | `--password <pwd>` | Password-protect deployment (6–128 chars) |
325
325
  | `--ttl <duration>` | Expire after that long — `3600`, `90s`, `1h`, `7d`. Needs a token; not with `--domain` |
326
326
  | `--no-path-detect` | Skip build output auto-detection |
327
- | `--no-spa-detect` | Skip SPA rewrite auto-configuration |
328
327
  | `--no-color` | Disable colors |
329
328
  | `--config <file>` | Custom config path |
330
329
 
@@ -335,7 +334,7 @@ Exit code is non-zero on failure, and with `--json` the error goes to **stderr**
335
334
  ```json
336
335
  {
337
336
  "error": "validation_failed",
338
- "message": "File \"index.html\" too large. Maximum 20 MB allowed.",
337
+ "message": "File \"setup.exe\" has an extension that is not allowed.",
339
338
  "status": 400
340
339
  }
341
340
  ```
@@ -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 3.8.0-beta.4
8
+ ## @shipstatic/types 3.8.0-beta.10
9
9
 
10
10
  License: MIT
11
11
 
package/dist/browser.d.ts CHANGED
@@ -724,7 +724,7 @@ type AccountPlanType = (typeof AccountPlan)[keyof typeof AccountPlan];
724
724
  * than the 403 uses.
725
725
  *
726
726
  * All three are counts plans SELL, and every plan publishes a number for each.
727
- * A platform subdomain (`my-app.shipstatic.com`) is among them: the namespace
727
+ * A platform domain (`my-app.shipstatic.com`) is among them: the namespace
728
728
  * is the platform's, so every plan bounds how many names one account may take
729
729
  * from it — which is not the address every deployment gets by construction
730
730
  * (`happy-cat-abc1234.shipstatic.com`), one per deployment and bounded by
@@ -734,8 +734,9 @@ type AccountPlanType = (typeof AccountPlan)[keyof typeof AccountPlan];
734
734
  * "unlimited" — so no consumer needs an "is it bounded?" branch. A cap of `0`
735
735
  * means the plan does not have the feature at all; a cap of `N` bounds
736
736
  * creation, and what an account already holds above a cap stays until a plan
737
- * TRANSITION fits it (excess paused, newest first — a domain is the only kind
738
- * that pauses).
737
+ * TRANSITION fits it: excess domains are paused, newest first, and members
738
+ * are paused together while the account holds more than its cap. Deployments
739
+ * never pause; above their cap they only block creation.
739
740
  *
740
741
  * A count is an aggregate over a collection, so it lives on the summary
741
742
  * resource that owns the collection: `GET /account` for one caller, `GET
@@ -1072,7 +1073,6 @@ declare const API_PATHS: {
1072
1073
  readonly PLANS: "/plans";
1073
1074
  readonly PING: "/ping";
1074
1075
  readonly SETUP: "/setup";
1075
- readonly SPA_CHECK: "/spa-check";
1076
1076
  readonly UPLOAD: "/upload";
1077
1077
  };
1078
1078
  /**
@@ -1111,8 +1111,6 @@ declare const DEPLOY_FIELDS: {
1111
1111
  readonly BUILD: "build";
1112
1112
  /** @internal Server-processing flag — first-party `/upload` only. */
1113
1113
  readonly PRERENDER: "prerender";
1114
- /** @internal Server-processing flag — first-party `/upload` only. */
1115
- readonly SPA: "spa";
1116
1114
  /**
1117
1115
  * @internal The build settings, first-party `/upload` only and meaningful
1118
1116
  * only with `BUILD`: the command to run instead of the manifest's build
@@ -1266,11 +1264,12 @@ interface ErrorResponse {
1266
1264
  /**
1267
1265
  * `details` of an `ErrorType.Build` error: the evidence behind the verdict.
1268
1266
  *
1269
- * The one `details` shape this file names, because it is the one a HUMAN
1270
- * surface renders rather than a machine reads. The sentence in `message`
1271
- * says what went wrong; the log is how the person fixing the project sees
1272
- * where. A surface that builds shows both, the log as a block rather than
1273
- * as prose.
1267
+ * Named, like {@link PlanChangeRefusalDetails}, because a surface reads it:
1268
+ * `ErrorResponse.details` stays `unknown`, and a shape a client narrows to is
1269
+ * owned here rather than restated at its read site. The sentence in
1270
+ * `message` says what went wrong; the log is how the person fixing the
1271
+ * project sees where. A surface that builds shows both, the log as a block
1272
+ * rather than as prose.
1274
1273
  */
1275
1274
  interface BuildFailureDetails {
1276
1275
  /**
@@ -1476,7 +1475,27 @@ interface PlatformLimits {
1476
1475
  * retire with it (tracked in root `backlog.md`).
1477
1476
  */
1478
1477
  readonly blockedExtensions?: readonly string[];
1478
+ /**
1479
+ * The way forward past each limit, in the API's own words, for the account
1480
+ * that asked: `"Upgrade to Pro for more."` where a sold plan allows more,
1481
+ * `"Please contact support if you need more."` where none does. The API
1482
+ * writes it with the same function its own refusals use, so a client that
1483
+ * refuses a file before uploading it appends this sentence to its refusal
1484
+ * verbatim and says exactly what the server would have said. Which plans
1485
+ * exist, which are sold and what an account's grant raises are the server's
1486
+ * to know; a client never learns a plan name from this field, only a
1487
+ * sentence. An anonymous caller is answered the free plan's sentence, as it
1488
+ * is the free plan's numbers.
1489
+ *
1490
+ * **Optional, and the absence is load-bearing**, as `blockedExtensions`
1491
+ * above: an API deployed before this field existed sends nothing, and a
1492
+ * client then appends nothing. It hardens to required at the entity's next
1493
+ * natural break, with the blocklist.
1494
+ */
1495
+ readonly suggestions?: Readonly<Record<PlatformLimitKey, string>>;
1479
1496
  }
1497
+ /** The three request limits, named as `PlatformLimits` names them. */
1498
+ type PlatformLimitKey = 'maxFileSize' | 'maxFilesCount' | 'maxTotalSize';
1480
1499
  /**
1481
1500
  * Whether a file is one the platform refuses to host.
1482
1501
  *
@@ -1808,33 +1827,6 @@ declare const OAuthScope: {
1808
1827
  };
1809
1828
  type OAuthScopeType = (typeof OAuthScope)[keyof typeof OAuthScope];
1810
1829
  declare const DEPLOYMENT_CONFIG_FILENAME = "ship.json";
1811
- /** Default ship.json config for SPA routing. Single source of truth — used by both API and SDK. */
1812
- declare const SPA_DEFAULT_CONFIG: {
1813
- readonly rewrites: readonly [{
1814
- readonly source: "/(.*)";
1815
- readonly destination: "/index.html";
1816
- }];
1817
- };
1818
- /**
1819
- * The `/spa-check` pre-flight's client-side envelope: which file is the
1820
- * check's subject, and how large it may be before a client skips the call.
1821
- *
1822
- * One fact with three holders until this export — the API's config declared
1823
- * the cap, the SDK's `checkSPA` hardcoded `100 * 1024`, and prose restated
1824
- * "100KB". `INDEX_FILE` is the selection rule (the file whose content rides
1825
- * `SPACheckRequest.index`), restated by every client that builds the request.
1826
- *
1827
- * Neither member is a validation boundary: a client over the cap simply
1828
- * skips the pre-flight, because the server answers an oversized index
1829
- * `isSPA: false` anyway. A consumer that cannot import this (n8n) needs no
1830
- * size copy at all — outcome parity is the server's, not the client's.
1831
- */
1832
- declare const SPA_CHECK_CONSTRAINTS: {
1833
- /** The file whose content is the check's subject. */
1834
- readonly INDEX_FILE: "index.html";
1835
- /** Skip the pre-flight above this size — the server would answer false. */
1836
- readonly MAX_INDEX_BYTES: number;
1837
- };
1838
1830
  /**
1839
1831
  * Assert that a ship.json file is *syntactically* loadable. Syntax only —
1840
1832
  * never schema.
@@ -1947,39 +1939,6 @@ declare const TTL_CONSTRAINTS: {
1947
1939
  * schema), the SDK's request boundary, and the CLI's parser.
1948
1940
  */
1949
1941
  declare function validateTtl(value: unknown): number | undefined;
1950
- /**
1951
- * Request payload for SPA check endpoint
1952
- */
1953
- interface SPACheckRequest {
1954
- /** Array of file paths */
1955
- files: string[];
1956
- /** HTML content of index.html file */
1957
- index: string;
1958
- }
1959
- /**
1960
- * Response from SPA check endpoint
1961
- */
1962
- /**
1963
- * Which of the classifier's tiers reached the verdict, and why. Named rather
1964
- * than inline so the API's own `checkSPA` can return `SPACheckResponse`
1965
- * instead of restating its shape.
1966
- */
1967
- interface SPACheckDebug {
1968
- /** Which tier made the detection */
1969
- tier: 'exclusions' | 'inclusions' | 'scoring' | 'ai' | 'fallback';
1970
- /** The reason for the detection result */
1971
- reason: string;
1972
- }
1973
- /**
1974
- * A report: it answers a question and carries only the answer (`CLAUDE.md`,
1975
- * "A report answers a question").
1976
- */
1977
- interface SPACheckResponse {
1978
- /** Whether the project is detected as a Single Page Application */
1979
- isSPA: boolean;
1980
- /** Debugging information about detection */
1981
- debug: SPACheckDebug;
1982
- }
1983
1942
  /**
1984
1943
  * Represents a file that has been processed and is ready for deploy.
1985
1944
  * Used across the platform (API, SDK, CLI) for file operations.
@@ -2014,10 +1973,12 @@ interface StaticFile {
2014
1973
  * console: under `/<account>/` and at the root.
2015
1974
  *
2016
1975
  * `owner` marks a section that means the person's OWN account: a bare path
2017
- * into it opens the person's own account rather than the one entered last,
2018
- * since a pricing link's `/upgrade/pro` and the published API-key link speak
2019
- * about the reader's plan and key. Modals inside a section carry their own
2020
- * owner rule in the console; this flag is the section's alone.
1976
+ * into it opens the person's own account rather than the one entered last.
1977
+ * A pricing link's `/upgrade/pro` and the published API-key link speak about
1978
+ * the reader's plan and key, and `/success` is where a purchase lands: only
1979
+ * an owner buys, and a person owns exactly one account. Modals inside a
1980
+ * section carry their own owner rule in the console; this flag is the
1981
+ * section's alone.
2021
1982
  */
2022
1983
  declare const SECTIONS: {
2023
1984
  readonly new: {
@@ -2036,7 +1997,7 @@ declare const SECTIONS: {
2036
1997
  readonly owner: true;
2037
1998
  };
2038
1999
  readonly success: {
2039
- readonly owner: false;
2000
+ readonly owner: true;
2040
2001
  };
2041
2002
  readonly 'api-key': {
2042
2003
  readonly owner: true;
@@ -2289,8 +2250,6 @@ interface DeploymentUploadOptions {
2289
2250
  build?: boolean;
2290
2251
  /** @internal Trigger server-side prerender. Only available via /upload endpoint. */
2291
2252
  prerender?: boolean;
2292
- /** @internal Trigger server-side SPA detection. Only available via /upload endpoint. */
2293
- spa?: boolean;
2294
2253
  /**
2295
2254
  * @internal The command the build runs instead of the manifest's own
2296
2255
  * `build` script (`npm run build:site`, `hugo`, …). Only with `build`, only
@@ -2484,11 +2443,29 @@ interface PlansResponse {
2484
2443
  * consent), and a billed account moving down gets a Stripe Subscription
2485
2444
  * Schedule that applies the change at period end. The answer says which
2486
2445
  * happened ({@link PlanChangeResponse}).
2446
+ *
2447
+ * A move down a tier is booked only by an account that fits it: until it
2448
+ * does, the door refuses (400) with a sentence naming what to remove, and
2449
+ * the same counts as {@link PlanChangeRefusalDetails}.
2487
2450
  */
2488
2451
  interface PlanChangeRequest {
2489
2452
  readonly plan: AccountPlanType;
2490
2453
  readonly interval: BillingInterval;
2491
2454
  }
2455
+ /**
2456
+ * `details` of the change door's refusal of a move down a tier the account
2457
+ * does not fit: the account holds more members, custom domains or platform
2458
+ * domains than the destination allows.
2459
+ *
2460
+ * `excess` counts what to REMOVE before asking again, per kind, keyed like
2461
+ * {@link Caps}; only the kinds over the destination's caps appear. The
2462
+ * door's `message` names the same counts in a sentence, and that sentence is
2463
+ * what a surface shows; the counts are for a client that acts on the
2464
+ * refusal.
2465
+ */
2466
+ interface PlanChangeRefusalDetails {
2467
+ readonly excess: Partial<Caps>;
2468
+ }
2492
2469
  /**
2493
2470
  * The pending plan change — a Stripe Subscription Schedule, mirrored onto the
2494
2471
  * account. `at` is when it applies (the current period's end, Unix seconds).
@@ -2755,7 +2732,7 @@ interface UploadedFile {
2755
2732
  */
2756
2733
  declare function isPlatformDomain(domain: string, platformDomain: string): boolean;
2757
2734
  /**
2758
- * Check if a domain is a custom domain (not a platform subdomain).
2735
+ * Check if a domain is a custom domain (not a platform domain).
2759
2736
  * Custom domains are billable and require DNS verification.
2760
2737
  *
2761
2738
  * @example isCustomDomain("example.com", "shipstatic.com") → true
@@ -2901,15 +2878,16 @@ declare function validatePassword(value: unknown): string | undefined;
2901
2878
  interface DeploymentOptions extends DeploymentUploadOptions {
2902
2879
  /**
2903
2880
  * An AbortSignal to allow cancellation of the deploy operation. The one
2904
- * cancellation mechanism — abort the signal and the request rejects with
2905
- * a typed `Cancelled` error. Request timeouts are a client concern
2881
+ * cancellation mechanism: it reaches every request the deploy makes (the
2882
+ * limits it validates against and the upload itself),
2883
+ * and aborting it rejects the deploy with a typed `Cancelled` error
2884
+ * wherever it stands. A deploy the server had already committed stays
2885
+ * committed. Request timeouts are a client concern
2906
2886
  * (`ShipClientOptions.timeout`), not a per-deploy one.
2907
2887
  */
2908
2888
  signal?: AbortSignal;
2909
2889
  /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */
2910
2890
  pathDetect?: boolean;
2911
- /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */
2912
- spaDetect?: boolean;
2913
2891
  }
2914
2892
  type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;
2915
2893
  /**
@@ -2947,7 +2925,6 @@ interface DeployBodyContext {
2947
2925
  flags?: {
2948
2926
  build?: boolean;
2949
2927
  prerender?: boolean;
2950
- spa?: boolean;
2951
2928
  buildCommand?: string;
2952
2929
  outputDir?: string;
2953
2930
  };
@@ -3062,7 +3039,7 @@ interface ShipClientOptions {
3062
3039
  * Override the deploy endpoint path. Defaults to `/deployments`.
3063
3040
  *
3064
3041
  * @internal First-party hook used by `web/my` and `web/www` to target the
3065
- * `/upload` route (which runs server-side build / SPA detection). External
3042
+ * `/upload` route (which runs the server-side build). External
3066
3043
  * SDK consumers must not set this — the `/deployments` endpoint is the
3067
3044
  * stable public contract. See `cloudflare/api/CLAUDE.md` for why the two
3068
3045
  * endpoints exist and what `/upload` does that `/deployments` doesn't.
@@ -3366,17 +3343,9 @@ declare abstract class Ship$1 {
3366
3343
  readonly tokens: TokenResource;
3367
3344
  private readonly http;
3368
3345
  private readonly clientOptions;
3369
- private initPromise;
3370
- protected platformLimits: PlatformLimits | null;
3371
3346
  private credential;
3372
3347
  constructor(options?: ShipClientOptions);
3373
- protected abstract processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
3374
- /**
3375
- * Lazy initialization — fetches platform limits (file size / count caps) once,
3376
- * on the first API call. Subsequent calls reuse the resolved promise.
3377
- */
3378
- protected ensureInitialized(): Promise<void>;
3379
- private fetchPlatformLimits;
3348
+ protected abstract processInput(input: DeployInput, options: DeploymentOptions, limits: PlatformLimits): Promise<StaticFile[]>;
3380
3349
  /**
3381
3350
  * Ping the API server, resolving its answer: `{ success, timestamp }`, where
3382
3351
  * `timestamp` is the server clock in unix SECONDS.
@@ -3397,11 +3366,14 @@ declare abstract class Ship$1 {
3397
3366
  */
3398
3367
  whoami(): Promise<Account>;
3399
3368
  /**
3400
- * Get platform limits (max file size, file count, total size).
3401
- * Reuses the response fetched during initialization. Per-instance state —
3402
- * does not leak between concurrent Ships against different API URLs.
3369
+ * Get the platform limits the server states now (max file size, file count,
3370
+ * total size, and the blocked extensions). Asked every time and never held,
3371
+ * since a plan move or an operator's grant changes them. A `signal` aborts
3372
+ * the request, rejecting with the typed `Cancelled` error.
3403
3373
  */
3404
- getLimits(): Promise<PlatformLimits>;
3374
+ getLimits(options?: {
3375
+ signal?: AbortSignal;
3376
+ }): Promise<PlatformLimits>;
3405
3377
  on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void;
3406
3378
  off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void;
3407
3379
  /**
@@ -3508,6 +3480,9 @@ declare function getENV(): ExecutionEnvironment;
3508
3480
  * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.
3509
3481
  *
3510
3482
  * Additional checks: path traversal, reserved names, leading/trailing dots or spaces.
3483
+ *
3484
+ * The reason is a complete sentence naming the file, so every surface shows it
3485
+ * as it is (`file-rules.ts` on why).
3511
3486
  */
3512
3487
  declare function validateFileName(filename: string): {
3513
3488
  valid: boolean;
@@ -3636,35 +3611,40 @@ declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>
3636
3611
  * @file One ordered table of deploy-file rules, and the single evaluation two
3637
3612
  * renderers share.
3638
3613
  *
3639
- * **The defect this closes:** one rule was rendering as three sentences. A
3640
- * file over the size cap said `File x is too large. Maximum allowed size is
3641
- * 20MB.` from the deploy pipelines, `File size (21 MB) exceeds limit of 20 MB`
3642
- * from `validateFiles`, and `File too large. Maximum 20971520 bytes allowed`
3643
- * from the API — against the dual-validation doctrine that an error reads the
3644
- * same wherever it was caught (root `CLAUDE.md`). Both pipelines also restated
3645
- * the whole ordered check, so node/browser parity was a comment.
3646
- *
3647
3614
  * **A rule states a predicate and a sentence; a renderer chooses only how to
3648
3615
  * DELIVER it.** That is the `SHAPES`-table move (`cli/formatters.ts`) applied
3649
3616
  * to validation: the throwing renderer raises the first broken rule, the
3650
3617
  * collecting renderer records it, and neither authors prose. Adding a rule is
3651
- * a row, and both surfaces get it in the same position by construction.
3652
- *
3653
- * **Wording follows the API where a choice existed**, so the deferred Phase B
3654
- * — promoting this table to `@shipstatic/types` with the API consuming it —
3655
- * has less to move. Two deliberate deviations, recorded rather than silent:
3656
- *
3657
- * - **Sizes are formatted, not raw bytes.** The API says `20971520 bytes`;
3658
- * a browser upload UI showing that is worse for the person reading it, and
3659
- * the unit is the smaller half of the sentence to reconcile later.
3660
- * - **The path is named.** The API has no path to name; the throwing renderer
3661
- * has nothing BUT the message, so dropping it would leave a CLI user asking
3662
- * which file.
3618
+ * a row, and both surfaces get it in the same position by construction. One
3619
+ * table is also what keeps a rule from reading three ways: the deploy
3620
+ * pipelines, `validateFiles` and the API once each worded the size cap
3621
+ * differently, against the dual-validation doctrine that an error reads the
3622
+ * same wherever it is caught (root `CLAUDE.md`).
3623
+ *
3624
+ * **Every sentence is complete on its own.** The SDK is where a refusal is
3625
+ * worded; drop, the console, the CLI and the MCPs show the sentence verbatim
3626
+ * and add nothing, so a sentence names its file exactly once and states the
3627
+ * limit, and reads without a prefix. The family is subject first: `File
3628
+ * "<path>" <verdict>. Maximum <limit> allowed.`, and `File name "<path>" …`
3629
+ * where the name is the subject. The two rules about the SET, the count and
3630
+ * the total, name no path. A limit's sentence ends with the suggestion the
3631
+ * API delivered for it (`withSuggestion`), so the whole reads `File "x" is
3632
+ * too large. Maximum 20 MB allowed. Upgrade to Pro for more.`
3633
+ *
3634
+ * **The API words the same rules the same way, in the same order**
3635
+ * (`cloudflare/api/src/lib/validation.ts`), so a refusal reads alike whether
3636
+ * the SDK made it before uploading or the boundary made it after. Sizes are
3637
+ * formatted on both sides, and the path is named on both. That is a
3638
+ * restatement, and it is fenced from the API's side: a test there imports
3639
+ * this package's `validateFiles` and compares every shared rule's sentence to
3640
+ * `validateUpload`'s. The fence dies the day the table is promoted to
3641
+ * `@shipstatic/types` and the API imports it (Phase B, `CLAUDE.md`).
3663
3642
  *
3664
3643
  * Out of scope, and left where they are: `validateDeployPath` (a rule about
3665
3644
  * the deploy PATH rather than the file, and pipelines-only), and
3666
- * `validateFiles`' UI-tier pre-checks — empty, negative, count, unbuilt
3667
- * marker, processing error — which have one holder each and no drift.
3645
+ * `validateFiles`' UI-tier pre-checks (empty, negative, unbuilt marker,
3646
+ * processing error), which have one holder each and no drift, and word their
3647
+ * sentences by the same family.
3668
3648
  */
3669
3649
 
3670
3650
  /** What a rule is asked about: one file, and the deploy so far. */
@@ -3716,6 +3696,15 @@ declare function validateDeployPath(deployPath: string, sourceIdentifier: string
3716
3696
  * @throws {ShipError} The first broken rule's sentence
3717
3697
  */
3718
3698
  declare function validateDeployFile(input: FileRuleInput, limits: PlatformLimits): void;
3699
+ /**
3700
+ * The throwing renderer of `COUNT_RULE`: the deploy's file count, judged over
3701
+ * the files the pipeline will send.
3702
+ *
3703
+ * @param count - How many files the deploy carries
3704
+ * @param limits - The platform's limits, from `/limits`
3705
+ * @throws {ShipError} The count rule's sentence
3706
+ */
3707
+ declare function validateDeployCount(count: number, limits: PlatformLimits): void;
3719
3708
 
3720
3709
  /**
3721
3710
  * Utility functions for string manipulation.
@@ -3754,10 +3743,9 @@ declare function pluralize(count: number, singular: string, plural: string, incl
3754
3743
  *
3755
3744
  * @param browserFiles - File[] to process for deploy.
3756
3745
  * @param options - Processing options including pathDetect for automatic path optimization.
3757
- * @param platformLimits - Per-instance platform limits (file-size / count / total-size caps)
3758
- * from the originating Ship's `GET /limits` fetch. Passed in rather than read from a
3759
- * module global so concurrent Ships against different API URLs cannot clobber each
3760
- * other's caps.
3746
+ * @param platformLimits - The platform limits (file-size / count / total-size caps and
3747
+ * the blocked extensions) the deploy read from `GET /limits` at its start. Passed in,
3748
+ * never held, so a validation always meets the limits the server states now.
3761
3749
  * @returns Promise resolving to an array of StaticFile objects.
3762
3750
  * @throws {ShipError} If called outside a browser or with invalid input.
3763
3751
  */
@@ -3798,7 +3786,7 @@ declare class Ship extends Ship$1 {
3798
3786
  * contract stays platform-neutral.
3799
3787
  */
3800
3788
  deploy(input: File[], options?: DeploymentOptions): Promise<DeploymentCreateResponse>;
3801
- protected processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
3789
+ protected processInput(input: DeployInput, options: DeploymentOptions, limits: PlatformLimits): Promise<StaticFile[]>;
3802
3790
  }
3803
3791
 
3804
- export { ACCOUNT_HEADER, API_KEY, API_PATHS, AUTH_BASE_PATH, type Account, type AccountAccess, type AccountDeleteResponse, type AccountKeyResponse, AccountPlan, type AccountPlanType, AccountRefusal, type AccountRefusalType, type AccountResource, type AccountRole, type Activity, type ActivityEvent, type ActivityListResponse, type ActivityMeta, type ApiDeployOptions, ApiHttp, type ApiHttpOptions, AuthMethod, type AuthMethodType, BUILD_SETTINGS, type BillingInterval, type BillingPortalSession, type BillingSyncResponse, type BuildFailureDetails, CALLER, type Caps, type ConsoleErrand, type ConsolePaths, type ConsoleSection, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_FIELDS, DEPLOY_FILE_GRAMMAR, DEPLOY_TOKEN, type DeployBodyContext, type DeployFile, type DeployFileEncoding, type DeployFileSpec, 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, DomainVerification, type DomainVerificationType, 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, OAUTH_TOKEN, OAuthScope, type OAuthScopeType, PASSWORD_CONSTRAINTS, PUBLIC_DEPLOYMENT_TTL_SECONDS, type PingResponse, type Plan, type PlanChangeRequest, type PlanChangeResponse, type PlanDestination, type PlansResponse, type PlatformLimits, type RequestResult, type ResourceContext, SECTIONS, SETUP_TROUBLESHOOTING_ANCHOR, SHIP_ENV, SHIP_VIA_ENV, SIGN_IN_RETURN_PARAM, type SPACheckDebug, type SPACheckRequest, type SPACheckResponse, SPA_CHECK_CONSTRAINTS, SPA_DEFAULT_CONFIG, type ScheduledChange, type SettingsFragment, 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, USER_VISIBLE_EVENTS, type UploadedFile, type UserVisibleActivityEvent, type ValidatableFile, type ValidationIssue, WEB_FILE_ACCEPT, __setTestEnvironment, accountOfPath, allValidFilesReady, assertShipJsonSyntax, billingReturnOf, calculateMD5, classifyToken, consolePaths, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, Ship as default, deserializeLabels, doors, errandOf, extractSubdomain, filterJunk, formatDuration, formatFileSize, formatTimeRemaining, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isClaimPath, isCustomDomain, isDeployment, isInvitationPath, isPlatformDomain, isShipError, isUpgradePath, normalizeVia, optimizeDeployPaths, personName, personShortName, pluralize, processFilesForBrowser, readBearerValue, serializeLabels, validateApiKey, validateApiUrl, validateBuildCommand, validateCaller, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validateIdempotencyKey, validateOAuthToken, validateOutputDir, validatePassword, validateToken, validateTtl };
3792
+ export { ACCOUNT_HEADER, API_KEY, API_PATHS, AUTH_BASE_PATH, type Account, type AccountAccess, type AccountDeleteResponse, type AccountKeyResponse, AccountPlan, type AccountPlanType, AccountRefusal, type AccountRefusalType, type AccountResource, type AccountRole, type Activity, type ActivityEvent, type ActivityListResponse, type ActivityMeta, type ApiDeployOptions, ApiHttp, type ApiHttpOptions, AuthMethod, type AuthMethodType, BUILD_SETTINGS, type BillingInterval, type BillingPortalSession, type BillingSyncResponse, type BuildFailureDetails, CALLER, type Caps, type ConsoleErrand, type ConsolePaths, type ConsoleSection, DEFAULT_API, DEPLOYMENT_CONFIG_FILENAME, DEPLOY_FIELDS, DEPLOY_FILE_GRAMMAR, DEPLOY_TOKEN, type DeployBodyContext, type DeployFile, type DeployFileEncoding, type DeployFileSpec, 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, DomainVerification, type DomainVerificationType, 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, OAUTH_TOKEN, OAuthScope, type OAuthScopeType, PASSWORD_CONSTRAINTS, PUBLIC_DEPLOYMENT_TTL_SECONDS, type PingResponse, type Plan, type PlanChangeRefusalDetails, type PlanChangeRequest, type PlanChangeResponse, type PlanDestination, type PlansResponse, type PlatformLimitKey, type PlatformLimits, type RequestResult, type ResourceContext, SECTIONS, SETUP_TROUBLESHOOTING_ANCHOR, SHIP_ENV, SHIP_VIA_ENV, SIGN_IN_RETURN_PARAM, type ScheduledChange, type SettingsFragment, 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, USER_VISIBLE_EVENTS, type UploadedFile, type UserVisibleActivityEvent, type ValidatableFile, type ValidationIssue, WEB_FILE_ACCEPT, __setTestEnvironment, accountOfPath, allValidFilesReady, assertShipJsonSyntax, billingReturnOf, calculateMD5, classifyToken, consolePaths, createAccountResource, createDeploymentResource, createDomainResource, createTokenResource, Ship as default, deserializeLabels, doors, errandOf, extractSubdomain, filterJunk, formatDuration, formatFileSize, formatTimeRemaining, generateDeploymentUrl, generateDomainUrl, getENV, getValidFiles, hasUnbuiltMarker, hasUnsafeChars, isBlockedExtension, isClaimPath, isCustomDomain, isDeployment, isInvitationPath, isPlatformDomain, isShipError, isUpgradePath, normalizeVia, optimizeDeployPaths, personName, personShortName, pluralize, processFilesForBrowser, readBearerValue, serializeLabels, validateApiKey, validateApiUrl, validateBuildCommand, validateCaller, validateDeployCount, validateDeployFile, validateDeployPath, validateDeployToken, validateFileName, validateFiles, validateIdempotencyKey, validateOAuthToken, validateOutputDir, validatePassword, validateToken, validateTtl };