@shipstatic/ship 2.2.0-beta.5 → 2.2.0-beta.7
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 +10 -4
- package/THIRD-PARTY-LICENSES.md +1 -1
- package/dist/browser.d.ts +142 -28
- package/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/cli.cjs +43 -43
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -28
- package/dist/index.d.ts +142 -28
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -725,6 +725,23 @@ declare const ErrorType: {
|
|
|
725
725
|
readonly Maintenance: "maintenance";
|
|
726
726
|
/** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
|
|
727
727
|
readonly Network: "network_error";
|
|
728
|
+
/**
|
|
729
|
+
* A deadline expired before the exchange completed. Client-side only — set
|
|
730
|
+
* by HTTP clients when a timeout signal fires; never produced server-side.
|
|
731
|
+
*
|
|
732
|
+
* A member of the NETWORK category rather than a sibling of it:
|
|
733
|
+
* `isNetworkError()` answers "nothing was exchanged", which is true of a
|
|
734
|
+
* deadline exactly as it is of a refused connection, so every consumer that
|
|
735
|
+
* retries, declines to report, or declines to relay a wire message on that
|
|
736
|
+
* category is already right about a timeout. The distinct TYPE exists for
|
|
737
|
+
* the one decision the category cannot make — what to SAY. "Check your
|
|
738
|
+
* internet connection" is the wrong sentence for a five-minute deploy
|
|
739
|
+
* ceiling, and a surface can only tell the two apart by type.
|
|
740
|
+
*
|
|
741
|
+
* The same relationship every comparable SDK ships:
|
|
742
|
+
* `APIConnectionTimeoutError extends APIConnectionError`.
|
|
743
|
+
*/
|
|
744
|
+
readonly Timeout: "timeout_error";
|
|
728
745
|
/** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
|
|
729
746
|
readonly Cancelled: "operation_cancelled";
|
|
730
747
|
/** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */
|
|
@@ -769,7 +786,7 @@ declare class ShipError extends Error {
|
|
|
769
786
|
* on the client). Falls back to status-derived (401 → Authentication,
|
|
770
787
|
* 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses
|
|
771
788
|
* (CDN errors, intermediaries) or malformed bodies. Client-only types
|
|
772
|
-
* (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the
|
|
789
|
+
* (`Network`, `Timeout`, `Cancelled`, `File`, `Config`) are filtered out of the
|
|
773
790
|
* trusted set — a misbehaving server claiming one of those is ignored.
|
|
774
791
|
*
|
|
775
792
|
* `operationName` (e.g. `"Get account"`) is used to compose the fallback
|
|
@@ -789,8 +806,9 @@ declare class ShipError extends Error {
|
|
|
789
806
|
* Routing:
|
|
790
807
|
* - Already a `ShipError` → returned as-is (caller's intent preserved)
|
|
791
808
|
* - `AbortError` → `ShipError.cancelled(...)` — someone stopped it on purpose
|
|
792
|
-
* - `TimeoutError` → `ShipError.
|
|
793
|
-
*
|
|
809
|
+
* - `TimeoutError` → `ShipError.timeout(...)` — a deadline expired; the
|
|
810
|
+
* message names the timeout, and the type is in the network CATEGORY
|
|
811
|
+
* because nothing was exchanged
|
|
794
812
|
* - A transport failure → `ShipError.network(...)` — see `isTransportFailure`
|
|
795
813
|
* for what each runtime offers as evidence
|
|
796
814
|
* - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)
|
|
@@ -837,6 +855,14 @@ declare class ShipError extends Error {
|
|
|
837
855
|
static authentication(message?: string, details?: unknown): ShipError;
|
|
838
856
|
static business(message: string, status?: number, details?: unknown): ShipError;
|
|
839
857
|
static network(message: string, details?: unknown): ShipError;
|
|
858
|
+
/**
|
|
859
|
+
* A deadline expired before the exchange completed.
|
|
860
|
+
*
|
|
861
|
+
* Statusless like its four client-only siblings: no exchange completed, so
|
|
862
|
+
* there is no HTTP status to report. `isNetworkError()` is true — see
|
|
863
|
+
* `ErrorType.Timeout` for why the category is shared and the type is not.
|
|
864
|
+
*/
|
|
865
|
+
static timeout(message: string, details?: unknown): ShipError;
|
|
840
866
|
static cancelled(message: string, details?: unknown): ShipError;
|
|
841
867
|
static file(message: string, details?: unknown): ShipError;
|
|
842
868
|
static config(message: string, details?: unknown): ShipError;
|
|
@@ -1964,17 +1990,45 @@ interface ShipClientOptions {
|
|
|
1964
1990
|
deployEndpoint?: string | undefined;
|
|
1965
1991
|
}
|
|
1966
1992
|
/**
|
|
1967
|
-
* Event map for Ship SDK events
|
|
1968
|
-
*
|
|
1993
|
+
* Event map for Ship SDK events.
|
|
1994
|
+
*
|
|
1995
|
+
* **Every failure is visible, and the event NAME says whether it ended the
|
|
1996
|
+
* call.** One call emits `retry* (error | response)` — so the stream is
|
|
1997
|
+
* unambiguous at every prefix, and a consumer never has to wait to find out
|
|
1998
|
+
* what it is watching.
|
|
1999
|
+
*
|
|
2000
|
+
* `request` counts what went out; `retry` counts what failed and will be
|
|
2001
|
+
* tried again; `error` and `response` are the two terminal answers, exactly
|
|
2002
|
+
* one of which arrives.
|
|
1969
2003
|
*/
|
|
1970
2004
|
interface ShipEvents {
|
|
1971
|
-
/** Emitted before each API request */
|
|
2005
|
+
/** Emitted before each API request — once per ATTEMPT, so it counts what actually went out. */
|
|
1972
2006
|
request: [url: string, init: RequestInit];
|
|
1973
|
-
/** Emitted after successful API response */
|
|
2007
|
+
/** Emitted after successful API response — once, on the attempt that worked. */
|
|
1974
2008
|
response: [response: Response, url: string];
|
|
1975
2009
|
/**
|
|
1976
|
-
* Emitted when
|
|
1977
|
-
* the
|
|
2010
|
+
* Emitted when an attempt failed and the client is going to try again.
|
|
2011
|
+
* Carries the same normalized `ShipError` the terminal `error` would, plus
|
|
2012
|
+
* `attempt` — the number of the attempt that just failed, counting from 1,
|
|
2013
|
+
* which matches the arithmetic the docs use ("two retries by default, so
|
|
2014
|
+
* three attempts"). Under that numbering the value reads both ways at once:
|
|
2015
|
+
* attempt N failing IS retry N, so `retry 1 of ${maxRetries}` needs no
|
|
2016
|
+
* adjustment.
|
|
2017
|
+
*
|
|
2018
|
+
* This event exists so `error` can keep meaning what it always meant. When
|
|
2019
|
+
* retries landed, `error` fired per attempt — honest about what happened,
|
|
2020
|
+
* but it silently redefined the event: a consumer seeing `error, error,
|
|
2021
|
+
* response` could not tell "failed, retrying" from "failed, terminally" at
|
|
2022
|
+
* any prefix, and counting `error`s no longer counted failed calls. Two
|
|
2023
|
+
* names, two meanings, and nothing lost: every failure is still announced.
|
|
2024
|
+
*
|
|
2025
|
+
* A failure the loop will NOT retry is terminal and emits `error` directly,
|
|
2026
|
+
* never this. So is an abort that lands mid-backoff.
|
|
2027
|
+
*/
|
|
2028
|
+
retry: [error: Error, url: string, attempt: number];
|
|
2029
|
+
/**
|
|
2030
|
+
* Emitted when the CALL failed — terminally, exactly once. TWO populations
|
|
2031
|
+
* arrive here, which is why the type is `Error` and not `ShipError`:
|
|
1978
2032
|
*
|
|
1979
2033
|
* - a failed request — always a `ShipError` (`executeRequest` normalizes
|
|
1980
2034
|
* every failure through `ShipError.fromFetchError` before emitting), so
|
|
@@ -2052,9 +2106,16 @@ declare class ApiHttp extends SimpleEvents {
|
|
|
2052
2106
|
* for headers, the timeout signal, the events and error normalization — so
|
|
2053
2107
|
* an attempt is a whole request and nothing has to be undone between two.
|
|
2054
2108
|
*
|
|
2055
|
-
* **
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2109
|
+
* **Every failure is visible, and the event NAME says whether it ended the
|
|
2110
|
+
* call.** One call emits `retry* (error | response)`: `request` fires per
|
|
2111
|
+
* attempt, so a consumer counting requests sees what actually went out; a
|
|
2112
|
+
* failure that will be tried again is a `retry`; `error` and `response` are
|
|
2113
|
+
* the two terminal answers, exactly one of which arrives.
|
|
2114
|
+
*
|
|
2115
|
+
* The failure events are emitted HERE rather than in `attemptOnce`, and
|
|
2116
|
+
* that placement is the whole mechanism: terminality is a property of the
|
|
2117
|
+
* loop — of `isRetryable` and the attempt budget — so it is knowable only
|
|
2118
|
+
* at the one point that owns both. An attempt cannot name its own failure.
|
|
2058
2119
|
*
|
|
2059
2120
|
* **The caller's `timeout` governs an ATTEMPT, not the wall clock.** Each
|
|
2060
2121
|
* attempt is an honest request and deserves the ceiling the caller named;
|
|
@@ -2073,7 +2134,12 @@ declare class ApiHttp extends SimpleEvents {
|
|
|
2073
2134
|
/** Did this request carry the header that makes a repeat safe? */
|
|
2074
2135
|
private hasIdempotencyKey;
|
|
2075
2136
|
/**
|
|
2076
|
-
* One attempt: headers, timeout signal, events, and
|
|
2137
|
+
* One attempt: headers, timeout signal, the `request`/`response` events, and
|
|
2138
|
+
* error normalization.
|
|
2139
|
+
*
|
|
2140
|
+
* It does NOT emit a failure event. An attempt cannot know whether its own
|
|
2141
|
+
* failure ended the call — that is `executeRequest`'s question — so it
|
|
2142
|
+
* normalizes and throws, and the loop names what happened.
|
|
2077
2143
|
*/
|
|
2078
2144
|
private attemptOnce;
|
|
2079
2145
|
/**
|
|
@@ -2436,6 +2502,56 @@ interface MD5Result {
|
|
|
2436
2502
|
}
|
|
2437
2503
|
declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>;
|
|
2438
2504
|
|
|
2505
|
+
/**
|
|
2506
|
+
* @file One ordered table of deploy-file rules, and the single evaluation two
|
|
2507
|
+
* renderers share.
|
|
2508
|
+
*
|
|
2509
|
+
* **The defect this closes:** one rule was rendering as three sentences. A
|
|
2510
|
+
* file over the size cap said `File x is too large. Maximum allowed size is
|
|
2511
|
+
* 20MB.` from the deploy pipelines, `File size (21 MB) exceeds limit of 20 MB`
|
|
2512
|
+
* from `validateFiles`, and `File too large. Maximum 20971520 bytes allowed`
|
|
2513
|
+
* from the API — against the dual-validation doctrine that an error reads the
|
|
2514
|
+
* same wherever it was caught (root `CLAUDE.md`). Both pipelines also restated
|
|
2515
|
+
* the whole ordered check, so node/browser parity was a comment.
|
|
2516
|
+
*
|
|
2517
|
+
* **A rule states a predicate and a sentence; a renderer chooses only how to
|
|
2518
|
+
* DELIVER it.** That is the `SHAPES`-table move (`cli/formatters.ts`) applied
|
|
2519
|
+
* to validation: the throwing renderer raises the first broken rule, the
|
|
2520
|
+
* collecting renderer records it, and neither authors prose. Adding a rule is
|
|
2521
|
+
* a row, and both surfaces get it in the same position by construction.
|
|
2522
|
+
*
|
|
2523
|
+
* **Wording follows the API where a choice existed**, so the deferred Phase B
|
|
2524
|
+
* — promoting this table to `@shipstatic/types` with the API consuming it —
|
|
2525
|
+
* has less to move. Two deliberate deviations, recorded rather than silent:
|
|
2526
|
+
*
|
|
2527
|
+
* - **Sizes are formatted, not raw bytes.** The API says `20971520 bytes`;
|
|
2528
|
+
* a browser upload UI showing that is worse for the person reading it, and
|
|
2529
|
+
* the unit is the smaller half of the sentence to reconcile later.
|
|
2530
|
+
* - **The path is named.** The API has no path to name; the throwing renderer
|
|
2531
|
+
* has nothing BUT the message, so dropping it would leave a CLI user asking
|
|
2532
|
+
* which file.
|
|
2533
|
+
*
|
|
2534
|
+
* Out of scope, and left where they are: `validateDeployPath` (a rule about
|
|
2535
|
+
* the deploy PATH rather than the file, and pipelines-only), and
|
|
2536
|
+
* `validateFiles`' UI-tier pre-checks — empty, negative, count, unbuilt
|
|
2537
|
+
* marker, processing error — which have one holder each and no drift.
|
|
2538
|
+
*/
|
|
2539
|
+
|
|
2540
|
+
/** What a rule is asked about: one file, and the deploy so far. */
|
|
2541
|
+
interface FileRuleInput {
|
|
2542
|
+
/** The path this file will be served at. */
|
|
2543
|
+
readonly path: string;
|
|
2544
|
+
/** This file's size in bytes. */
|
|
2545
|
+
readonly size: number;
|
|
2546
|
+
/** Bytes accumulated INCLUDING this file — the total rule's subject. */
|
|
2547
|
+
readonly totalSize: number;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
/**
|
|
2551
|
+
* @file Shared security validation for the deploy pipeline.
|
|
2552
|
+
* Used by both Node.js and browser file processing pipelines.
|
|
2553
|
+
*/
|
|
2554
|
+
|
|
2439
2555
|
/**
|
|
2440
2556
|
* Validate a deploy path for security concerns.
|
|
2441
2557
|
* Rejects paths containing path traversal patterns or null bytes.
|
|
@@ -2454,24 +2570,22 @@ declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>
|
|
|
2454
2570
|
*/
|
|
2455
2571
|
declare function validateDeployPath(deployPath: string, sourceIdentifier: string): void;
|
|
2456
2572
|
/**
|
|
2457
|
-
*
|
|
2458
|
-
* Rejects unsafe filenames (shell/URL-dangerous chars, reserved names)
|
|
2459
|
-
* and file extensions the platform refuses to host.
|
|
2573
|
+
* The THROWING renderer of `FILE_RULES` — the deploy pipelines' shape.
|
|
2460
2574
|
*
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2463
|
-
*
|
|
2464
|
-
*
|
|
2465
|
-
* in either direction. Callers pass `[]` when the API sent no list (one that
|
|
2466
|
-
* predates the field): the check then does nothing and the API refuses the
|
|
2467
|
-
* file at the boundary, which is the correct place for it to be refused.
|
|
2575
|
+
* It raises the first rule the file breaks and nothing else: the rules, their
|
|
2576
|
+
* order and their sentences all live in `file-rules.ts`, so this function
|
|
2577
|
+
* cannot re-order, skip or reword one. That is what makes node/browser parity
|
|
2578
|
+
* structural — both pipelines call this, and this calls the one table.
|
|
2468
2579
|
*
|
|
2469
|
-
*
|
|
2470
|
-
*
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2580
|
+
* Its counterpart is the collecting renderer in `file-validation.ts`
|
|
2581
|
+
* (`validateFiles`), which reaches the same verdict and reports it as a list
|
|
2582
|
+
* instead of a throw.
|
|
2583
|
+
*
|
|
2584
|
+
* @param input - The file and the deploy so far (`totalSize` INCLUDES it)
|
|
2585
|
+
* @param limits - The platform's limits, from `/limits`
|
|
2586
|
+
* @throws {ShipError} The first broken rule's sentence
|
|
2473
2587
|
*/
|
|
2474
|
-
declare function validateDeployFile(
|
|
2588
|
+
declare function validateDeployFile(input: FileRuleInput, limits: PlatformLimits): void;
|
|
2475
2589
|
|
|
2476
2590
|
/**
|
|
2477
2591
|
* Utility functions for string manipulation.
|
package/dist/index.d.ts
CHANGED
|
@@ -725,6 +725,23 @@ declare const ErrorType: {
|
|
|
725
725
|
readonly Maintenance: "maintenance";
|
|
726
726
|
/** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
|
|
727
727
|
readonly Network: "network_error";
|
|
728
|
+
/**
|
|
729
|
+
* A deadline expired before the exchange completed. Client-side only — set
|
|
730
|
+
* by HTTP clients when a timeout signal fires; never produced server-side.
|
|
731
|
+
*
|
|
732
|
+
* A member of the NETWORK category rather than a sibling of it:
|
|
733
|
+
* `isNetworkError()` answers "nothing was exchanged", which is true of a
|
|
734
|
+
* deadline exactly as it is of a refused connection, so every consumer that
|
|
735
|
+
* retries, declines to report, or declines to relay a wire message on that
|
|
736
|
+
* category is already right about a timeout. The distinct TYPE exists for
|
|
737
|
+
* the one decision the category cannot make — what to SAY. "Check your
|
|
738
|
+
* internet connection" is the wrong sentence for a five-minute deploy
|
|
739
|
+
* ceiling, and a surface can only tell the two apart by type.
|
|
740
|
+
*
|
|
741
|
+
* The same relationship every comparable SDK ships:
|
|
742
|
+
* `APIConnectionTimeoutError extends APIConnectionError`.
|
|
743
|
+
*/
|
|
744
|
+
readonly Timeout: "timeout_error";
|
|
728
745
|
/** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
|
|
729
746
|
readonly Cancelled: "operation_cancelled";
|
|
730
747
|
/** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */
|
|
@@ -769,7 +786,7 @@ declare class ShipError extends Error {
|
|
|
769
786
|
* on the client). Falls back to status-derived (401 → Authentication,
|
|
770
787
|
* 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses
|
|
771
788
|
* (CDN errors, intermediaries) or malformed bodies. Client-only types
|
|
772
|
-
* (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the
|
|
789
|
+
* (`Network`, `Timeout`, `Cancelled`, `File`, `Config`) are filtered out of the
|
|
773
790
|
* trusted set — a misbehaving server claiming one of those is ignored.
|
|
774
791
|
*
|
|
775
792
|
* `operationName` (e.g. `"Get account"`) is used to compose the fallback
|
|
@@ -789,8 +806,9 @@ declare class ShipError extends Error {
|
|
|
789
806
|
* Routing:
|
|
790
807
|
* - Already a `ShipError` → returned as-is (caller's intent preserved)
|
|
791
808
|
* - `AbortError` → `ShipError.cancelled(...)` — someone stopped it on purpose
|
|
792
|
-
* - `TimeoutError` → `ShipError.
|
|
793
|
-
*
|
|
809
|
+
* - `TimeoutError` → `ShipError.timeout(...)` — a deadline expired; the
|
|
810
|
+
* message names the timeout, and the type is in the network CATEGORY
|
|
811
|
+
* because nothing was exchanged
|
|
794
812
|
* - A transport failure → `ShipError.network(...)` — see `isTransportFailure`
|
|
795
813
|
* for what each runtime offers as evidence
|
|
796
814
|
* - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)
|
|
@@ -837,6 +855,14 @@ declare class ShipError extends Error {
|
|
|
837
855
|
static authentication(message?: string, details?: unknown): ShipError;
|
|
838
856
|
static business(message: string, status?: number, details?: unknown): ShipError;
|
|
839
857
|
static network(message: string, details?: unknown): ShipError;
|
|
858
|
+
/**
|
|
859
|
+
* A deadline expired before the exchange completed.
|
|
860
|
+
*
|
|
861
|
+
* Statusless like its four client-only siblings: no exchange completed, so
|
|
862
|
+
* there is no HTTP status to report. `isNetworkError()` is true — see
|
|
863
|
+
* `ErrorType.Timeout` for why the category is shared and the type is not.
|
|
864
|
+
*/
|
|
865
|
+
static timeout(message: string, details?: unknown): ShipError;
|
|
840
866
|
static cancelled(message: string, details?: unknown): ShipError;
|
|
841
867
|
static file(message: string, details?: unknown): ShipError;
|
|
842
868
|
static config(message: string, details?: unknown): ShipError;
|
|
@@ -1964,17 +1990,45 @@ interface ShipClientOptions {
|
|
|
1964
1990
|
deployEndpoint?: string | undefined;
|
|
1965
1991
|
}
|
|
1966
1992
|
/**
|
|
1967
|
-
* Event map for Ship SDK events
|
|
1968
|
-
*
|
|
1993
|
+
* Event map for Ship SDK events.
|
|
1994
|
+
*
|
|
1995
|
+
* **Every failure is visible, and the event NAME says whether it ended the
|
|
1996
|
+
* call.** One call emits `retry* (error | response)` — so the stream is
|
|
1997
|
+
* unambiguous at every prefix, and a consumer never has to wait to find out
|
|
1998
|
+
* what it is watching.
|
|
1999
|
+
*
|
|
2000
|
+
* `request` counts what went out; `retry` counts what failed and will be
|
|
2001
|
+
* tried again; `error` and `response` are the two terminal answers, exactly
|
|
2002
|
+
* one of which arrives.
|
|
1969
2003
|
*/
|
|
1970
2004
|
interface ShipEvents {
|
|
1971
|
-
/** Emitted before each API request */
|
|
2005
|
+
/** Emitted before each API request — once per ATTEMPT, so it counts what actually went out. */
|
|
1972
2006
|
request: [url: string, init: RequestInit];
|
|
1973
|
-
/** Emitted after successful API response */
|
|
2007
|
+
/** Emitted after successful API response — once, on the attempt that worked. */
|
|
1974
2008
|
response: [response: Response, url: string];
|
|
1975
2009
|
/**
|
|
1976
|
-
* Emitted when
|
|
1977
|
-
* the
|
|
2010
|
+
* Emitted when an attempt failed and the client is going to try again.
|
|
2011
|
+
* Carries the same normalized `ShipError` the terminal `error` would, plus
|
|
2012
|
+
* `attempt` — the number of the attempt that just failed, counting from 1,
|
|
2013
|
+
* which matches the arithmetic the docs use ("two retries by default, so
|
|
2014
|
+
* three attempts"). Under that numbering the value reads both ways at once:
|
|
2015
|
+
* attempt N failing IS retry N, so `retry 1 of ${maxRetries}` needs no
|
|
2016
|
+
* adjustment.
|
|
2017
|
+
*
|
|
2018
|
+
* This event exists so `error` can keep meaning what it always meant. When
|
|
2019
|
+
* retries landed, `error` fired per attempt — honest about what happened,
|
|
2020
|
+
* but it silently redefined the event: a consumer seeing `error, error,
|
|
2021
|
+
* response` could not tell "failed, retrying" from "failed, terminally" at
|
|
2022
|
+
* any prefix, and counting `error`s no longer counted failed calls. Two
|
|
2023
|
+
* names, two meanings, and nothing lost: every failure is still announced.
|
|
2024
|
+
*
|
|
2025
|
+
* A failure the loop will NOT retry is terminal and emits `error` directly,
|
|
2026
|
+
* never this. So is an abort that lands mid-backoff.
|
|
2027
|
+
*/
|
|
2028
|
+
retry: [error: Error, url: string, attempt: number];
|
|
2029
|
+
/**
|
|
2030
|
+
* Emitted when the CALL failed — terminally, exactly once. TWO populations
|
|
2031
|
+
* arrive here, which is why the type is `Error` and not `ShipError`:
|
|
1978
2032
|
*
|
|
1979
2033
|
* - a failed request — always a `ShipError` (`executeRequest` normalizes
|
|
1980
2034
|
* every failure through `ShipError.fromFetchError` before emitting), so
|
|
@@ -2052,9 +2106,16 @@ declare class ApiHttp extends SimpleEvents {
|
|
|
2052
2106
|
* for headers, the timeout signal, the events and error normalization — so
|
|
2053
2107
|
* an attempt is a whole request and nothing has to be undone between two.
|
|
2054
2108
|
*
|
|
2055
|
-
* **
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2109
|
+
* **Every failure is visible, and the event NAME says whether it ended the
|
|
2110
|
+
* call.** One call emits `retry* (error | response)`: `request` fires per
|
|
2111
|
+
* attempt, so a consumer counting requests sees what actually went out; a
|
|
2112
|
+
* failure that will be tried again is a `retry`; `error` and `response` are
|
|
2113
|
+
* the two terminal answers, exactly one of which arrives.
|
|
2114
|
+
*
|
|
2115
|
+
* The failure events are emitted HERE rather than in `attemptOnce`, and
|
|
2116
|
+
* that placement is the whole mechanism: terminality is a property of the
|
|
2117
|
+
* loop — of `isRetryable` and the attempt budget — so it is knowable only
|
|
2118
|
+
* at the one point that owns both. An attempt cannot name its own failure.
|
|
2058
2119
|
*
|
|
2059
2120
|
* **The caller's `timeout` governs an ATTEMPT, not the wall clock.** Each
|
|
2060
2121
|
* attempt is an honest request and deserves the ceiling the caller named;
|
|
@@ -2073,7 +2134,12 @@ declare class ApiHttp extends SimpleEvents {
|
|
|
2073
2134
|
/** Did this request carry the header that makes a repeat safe? */
|
|
2074
2135
|
private hasIdempotencyKey;
|
|
2075
2136
|
/**
|
|
2076
|
-
* One attempt: headers, timeout signal, events, and
|
|
2137
|
+
* One attempt: headers, timeout signal, the `request`/`response` events, and
|
|
2138
|
+
* error normalization.
|
|
2139
|
+
*
|
|
2140
|
+
* It does NOT emit a failure event. An attempt cannot know whether its own
|
|
2141
|
+
* failure ended the call — that is `executeRequest`'s question — so it
|
|
2142
|
+
* normalizes and throws, and the loop names what happened.
|
|
2077
2143
|
*/
|
|
2078
2144
|
private attemptOnce;
|
|
2079
2145
|
/**
|
|
@@ -2436,6 +2502,56 @@ interface MD5Result {
|
|
|
2436
2502
|
}
|
|
2437
2503
|
declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>;
|
|
2438
2504
|
|
|
2505
|
+
/**
|
|
2506
|
+
* @file One ordered table of deploy-file rules, and the single evaluation two
|
|
2507
|
+
* renderers share.
|
|
2508
|
+
*
|
|
2509
|
+
* **The defect this closes:** one rule was rendering as three sentences. A
|
|
2510
|
+
* file over the size cap said `File x is too large. Maximum allowed size is
|
|
2511
|
+
* 20MB.` from the deploy pipelines, `File size (21 MB) exceeds limit of 20 MB`
|
|
2512
|
+
* from `validateFiles`, and `File too large. Maximum 20971520 bytes allowed`
|
|
2513
|
+
* from the API — against the dual-validation doctrine that an error reads the
|
|
2514
|
+
* same wherever it was caught (root `CLAUDE.md`). Both pipelines also restated
|
|
2515
|
+
* the whole ordered check, so node/browser parity was a comment.
|
|
2516
|
+
*
|
|
2517
|
+
* **A rule states a predicate and a sentence; a renderer chooses only how to
|
|
2518
|
+
* DELIVER it.** That is the `SHAPES`-table move (`cli/formatters.ts`) applied
|
|
2519
|
+
* to validation: the throwing renderer raises the first broken rule, the
|
|
2520
|
+
* collecting renderer records it, and neither authors prose. Adding a rule is
|
|
2521
|
+
* a row, and both surfaces get it in the same position by construction.
|
|
2522
|
+
*
|
|
2523
|
+
* **Wording follows the API where a choice existed**, so the deferred Phase B
|
|
2524
|
+
* — promoting this table to `@shipstatic/types` with the API consuming it —
|
|
2525
|
+
* has less to move. Two deliberate deviations, recorded rather than silent:
|
|
2526
|
+
*
|
|
2527
|
+
* - **Sizes are formatted, not raw bytes.** The API says `20971520 bytes`;
|
|
2528
|
+
* a browser upload UI showing that is worse for the person reading it, and
|
|
2529
|
+
* the unit is the smaller half of the sentence to reconcile later.
|
|
2530
|
+
* - **The path is named.** The API has no path to name; the throwing renderer
|
|
2531
|
+
* has nothing BUT the message, so dropping it would leave a CLI user asking
|
|
2532
|
+
* which file.
|
|
2533
|
+
*
|
|
2534
|
+
* Out of scope, and left where they are: `validateDeployPath` (a rule about
|
|
2535
|
+
* the deploy PATH rather than the file, and pipelines-only), and
|
|
2536
|
+
* `validateFiles`' UI-tier pre-checks — empty, negative, count, unbuilt
|
|
2537
|
+
* marker, processing error — which have one holder each and no drift.
|
|
2538
|
+
*/
|
|
2539
|
+
|
|
2540
|
+
/** What a rule is asked about: one file, and the deploy so far. */
|
|
2541
|
+
interface FileRuleInput {
|
|
2542
|
+
/** The path this file will be served at. */
|
|
2543
|
+
readonly path: string;
|
|
2544
|
+
/** This file's size in bytes. */
|
|
2545
|
+
readonly size: number;
|
|
2546
|
+
/** Bytes accumulated INCLUDING this file — the total rule's subject. */
|
|
2547
|
+
readonly totalSize: number;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
/**
|
|
2551
|
+
* @file Shared security validation for the deploy pipeline.
|
|
2552
|
+
* Used by both Node.js and browser file processing pipelines.
|
|
2553
|
+
*/
|
|
2554
|
+
|
|
2439
2555
|
/**
|
|
2440
2556
|
* Validate a deploy path for security concerns.
|
|
2441
2557
|
* Rejects paths containing path traversal patterns or null bytes.
|
|
@@ -2454,24 +2570,22 @@ declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>
|
|
|
2454
2570
|
*/
|
|
2455
2571
|
declare function validateDeployPath(deployPath: string, sourceIdentifier: string): void;
|
|
2456
2572
|
/**
|
|
2457
|
-
*
|
|
2458
|
-
* Rejects unsafe filenames (shell/URL-dangerous chars, reserved names)
|
|
2459
|
-
* and file extensions the platform refuses to host.
|
|
2573
|
+
* The THROWING renderer of `FILE_RULES` — the deploy pipelines' shape.
|
|
2460
2574
|
*
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2463
|
-
*
|
|
2464
|
-
*
|
|
2465
|
-
* in either direction. Callers pass `[]` when the API sent no list (one that
|
|
2466
|
-
* predates the field): the check then does nothing and the API refuses the
|
|
2467
|
-
* file at the boundary, which is the correct place for it to be refused.
|
|
2575
|
+
* It raises the first rule the file breaks and nothing else: the rules, their
|
|
2576
|
+
* order and their sentences all live in `file-rules.ts`, so this function
|
|
2577
|
+
* cannot re-order, skip or reword one. That is what makes node/browser parity
|
|
2578
|
+
* structural — both pipelines call this, and this calls the one table.
|
|
2468
2579
|
*
|
|
2469
|
-
*
|
|
2470
|
-
*
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2580
|
+
* Its counterpart is the collecting renderer in `file-validation.ts`
|
|
2581
|
+
* (`validateFiles`), which reaches the same verdict and reports it as a list
|
|
2582
|
+
* instead of a throw.
|
|
2583
|
+
*
|
|
2584
|
+
* @param input - The file and the deploy so far (`totalSize` INCLUDES it)
|
|
2585
|
+
* @param limits - The platform's limits, from `/limits`
|
|
2586
|
+
* @throws {ShipError} The first broken rule's sentence
|
|
2473
2587
|
*/
|
|
2474
|
-
declare function validateDeployFile(
|
|
2588
|
+
declare function validateDeployFile(input: FileRuleInput, limits: PlatformLimits): void;
|
|
2475
2589
|
|
|
2476
2590
|
/**
|
|
2477
2591
|
* Utility functions for string manipulation.
|