@shipstatic/types 2.7.0-beta.2 → 2.7.0-beta.3

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
@@ -78,9 +78,9 @@ try { response = await fetch(url); }
78
78
  catch (cause) { throw ShipError.fromFetchError(cause, 'Get account'); }
79
79
  ```
80
80
 
81
- `fromHttpResponse` trusts the body's `error` field when it's a known server-producible `ErrorType` — so a server's `ShipError.validation(...)` round-trips back to `ErrorType.Validation` on the client. For non-API responses (CDN errors, intermediaries) or malformed bodies it falls back to status-derived (401 → `Authentication`, 403 → `Forbidden`, 429 → `RateLimit`, else → `Api`). Client-only types (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the trusted set. Body's `message` and `details` are preserved best-effort.
81
+ `fromHttpResponse` trusts the body's `error` field when it's a known server-producible `ErrorType` — so a server's `ShipError.validation(...)` round-trips back to `ErrorType.Validation` on the client. For non-API responses (CDN errors, intermediaries) or malformed bodies it falls back to status-derived (401 → `Authentication`, 403 → `Forbidden`, 429 → `RateLimit`, else → `Api`). Client-only types (`Network`, `Timeout`, `Cancelled`, `File`, `Config`) are filtered out of the trusted set. Body's `message` and `details` are preserved best-effort.
82
82
 
83
- `fromFetchError` routes by the thrown cause: an existing `ShipError` is returned unchanged, `AbortError` becomes `Cancelled`, a fetch `TypeError` becomes `Network`, anything else becomes `Api` (with no HTTP status — the request never reached the server).
83
+ `fromFetchError` routes by the thrown cause: an existing `ShipError` is returned unchanged, `AbortError` becomes `Cancelled`, `TimeoutError` becomes `Timeout`, a fetch `TypeError` becomes `Network`, anything else becomes `Api` (with no HTTP status — the request never reached the server). `Timeout` is a distinct type inside the network category — `isNetworkError()` is true for it — so a surface can retry it like any transport failure while still saying "timed out" rather than "check your connection".
84
84
 
85
85
  Both helpers accept an optional operation-name string for contextual messages (`"Get account was cancelled"`, `"Get account failed: ..."`).
86
86
 
package/dist/index.d.ts CHANGED
@@ -725,6 +725,23 @@ export 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 @@ export 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 @@ export 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.network(...)` — a deadline expired, so
793
- * nothing was exchanged; the message names the timeout
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 @@ export 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;
package/dist/index.js CHANGED
@@ -271,6 +271,23 @@ export const ErrorType = {
271
271
  Maintenance: 'maintenance',
272
272
  /** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
273
273
  Network: 'network_error',
274
+ /**
275
+ * A deadline expired before the exchange completed. Client-side only — set
276
+ * by HTTP clients when a timeout signal fires; never produced server-side.
277
+ *
278
+ * A member of the NETWORK category rather than a sibling of it:
279
+ * `isNetworkError()` answers "nothing was exchanged", which is true of a
280
+ * deadline exactly as it is of a refused connection, so every consumer that
281
+ * retries, declines to report, or declines to relay a wire message on that
282
+ * category is already right about a timeout. The distinct TYPE exists for
283
+ * the one decision the category cannot make — what to SAY. "Check your
284
+ * internet connection" is the wrong sentence for a five-minute deploy
285
+ * ceiling, and a surface can only tell the two apart by type.
286
+ *
287
+ * The same relationship every comparable SDK ships:
288
+ * `APIConnectionTimeoutError extends APIConnectionError`.
289
+ */
290
+ Timeout: 'timeout_error',
274
291
  /** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
275
292
  Cancelled: 'operation_cancelled',
276
293
  /** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */
@@ -286,6 +303,7 @@ export const ErrorType = {
286
303
  */
287
304
  const CLIENT_ONLY_ERROR_TYPES = new Set([
288
305
  ErrorType.Network,
306
+ ErrorType.Timeout,
289
307
  ErrorType.Cancelled,
290
308
  ErrorType.File,
291
309
  ErrorType.Config,
@@ -301,12 +319,20 @@ const ERROR_CATEGORIES = {
301
319
  * over the statusless ones too — those are raised locally and have no
302
320
  * status for `isClientError`'s second arm to read, so omitting one makes it
303
321
  * read as a server fault. The rule is the membership test: every type in
304
- * `CLIENT_ONLY_ERROR_TYPES` except `Network` (which `isNetworkError` owns)
305
- * belongs here.
322
+ * `CLIENT_ONLY_ERROR_TYPES` except the two `isNetworkError` owns belongs
323
+ * here.
306
324
  *
307
325
  * `Cancelled` was missing until 2026-07-29, which is exactly that failure:
308
326
  * a caller who aborted their own deploy was told "server error: please try
309
327
  * again" — the CLI's fallback for everything this set does not claim.
328
+ *
329
+ * `Timeout` is deliberately NOT here, and it is the sharper case, because
330
+ * it is the one client-only type that is not the client's fault: the
331
+ * caller set a ceiling, but what exhausted it was the network or the
332
+ * server. Reading it as client-attributable would say the caller erred,
333
+ * and it would silently disarm every consumer whose retry predicate
334
+ * declines `isClientError()` — a deadline is precisely the failure worth
335
+ * a second attempt.
310
336
  */
311
337
  client: new Set([
312
338
  ErrorType.Business,
@@ -318,7 +344,14 @@ const ERROR_CATEGORIES = {
318
344
  ErrorType.RateLimit,
319
345
  ErrorType.Validation,
320
346
  ]),
321
- network: new Set([ErrorType.Network]),
347
+ /**
348
+ * The exchange never happened. Two types, one category: a refused
349
+ * connection and an expired deadline differ in what a surface should SAY
350
+ * and in nothing else a consumer decides on — both are retryable, neither
351
+ * carries a wire message to relay, neither is worth reporting as an
352
+ * incident. See `ErrorType.Timeout` for why the type is distinct anyway.
353
+ */
354
+ network: new Set([ErrorType.Network, ErrorType.Timeout]),
322
355
  auth: new Set([ErrorType.Authentication]),
323
356
  };
324
357
  /**
@@ -447,7 +480,7 @@ export class ShipError extends Error {
447
480
  * on the client). Falls back to status-derived (401 → Authentication,
448
481
  * 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses
449
482
  * (CDN errors, intermediaries) or malformed bodies. Client-only types
450
- * (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the
483
+ * (`Network`, `Timeout`, `Cancelled`, `File`, `Config`) are filtered out of the
451
484
  * trusted set — a misbehaving server claiming one of those is ignored.
452
485
  *
453
486
  * `operationName` (e.g. `"Get account"`) is used to compose the fallback
@@ -531,8 +564,9 @@ export class ShipError extends Error {
531
564
  * Routing:
532
565
  * - Already a `ShipError` → returned as-is (caller's intent preserved)
533
566
  * - `AbortError` → `ShipError.cancelled(...)` — someone stopped it on purpose
534
- * - `TimeoutError` → `ShipError.network(...)` — a deadline expired, so
535
- * nothing was exchanged; the message names the timeout
567
+ * - `TimeoutError` → `ShipError.timeout(...)` — a deadline expired; the
568
+ * message names the timeout, and the type is in the network CATEGORY
569
+ * because nothing was exchanged
536
570
  * - A transport failure → `ShipError.network(...)` — see `isTransportFailure`
537
571
  * for what each runtime offers as evidence
538
572
  * - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)
@@ -568,11 +602,14 @@ export class ShipError extends Error {
568
602
  return ShipError.cancelled(`${op} was cancelled`);
569
603
  }
570
604
  if (name === 'TimeoutError') {
571
- // A deadline, not a fault and not a cancellation: nothing was exchanged,
572
- // which is exactly what `Network` claims. The runtime's own sentence is
573
- // dropped here rather than relayed "The operation was aborted due to
574
- // timeout" is the mechanism, not the news.
575
- return ShipError.network(`${op} timed out`, { cause });
605
+ // A deadline: not a fault, not a cancellation, and since it has its
606
+ // own type no longer merely "network". Nothing was exchanged, which
607
+ // is what keeps it in the network CATEGORY and therefore retryable; the
608
+ // type is what lets a surface say "timed out" instead of sending
609
+ // someone to check their Wi-Fi. The runtime's own sentence is dropped
610
+ // rather than relayed: "The operation was aborted due to timeout" is
611
+ // the mechanism, not the news.
612
+ return ShipError.timeout(`${op} timed out`, { cause });
576
613
  }
577
614
  if (cause instanceof Error) {
578
615
  if (isTransportFailure(cause)) {
@@ -621,6 +658,16 @@ export class ShipError extends Error {
621
658
  static network(message, details) {
622
659
  return new ShipError(ErrorType.Network, message, undefined, details);
623
660
  }
661
+ /**
662
+ * A deadline expired before the exchange completed.
663
+ *
664
+ * Statusless like its four client-only siblings: no exchange completed, so
665
+ * there is no HTTP status to report. `isNetworkError()` is true — see
666
+ * `ErrorType.Timeout` for why the category is shared and the type is not.
667
+ */
668
+ static timeout(message, details) {
669
+ return new ShipError(ErrorType.Timeout, message, undefined, details);
670
+ }
624
671
  static cancelled(message, details) {
625
672
  return new ShipError(ErrorType.Cancelled, message, undefined, details);
626
673
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "2.7.0-beta.2",
3
+ "version": "2.7.0-beta.3",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -815,6 +815,23 @@ export const ErrorType = {
815
815
  Maintenance: 'maintenance',
816
816
  /** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
817
817
  Network: 'network_error',
818
+ /**
819
+ * A deadline expired before the exchange completed. Client-side only — set
820
+ * by HTTP clients when a timeout signal fires; never produced server-side.
821
+ *
822
+ * A member of the NETWORK category rather than a sibling of it:
823
+ * `isNetworkError()` answers "nothing was exchanged", which is true of a
824
+ * deadline exactly as it is of a refused connection, so every consumer that
825
+ * retries, declines to report, or declines to relay a wire message on that
826
+ * category is already right about a timeout. The distinct TYPE exists for
827
+ * the one decision the category cannot make — what to SAY. "Check your
828
+ * internet connection" is the wrong sentence for a five-minute deploy
829
+ * ceiling, and a surface can only tell the two apart by type.
830
+ *
831
+ * The same relationship every comparable SDK ships:
832
+ * `APIConnectionTimeoutError extends APIConnectionError`.
833
+ */
834
+ Timeout: 'timeout_error',
818
835
  /** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
819
836
  Cancelled: 'operation_cancelled',
820
837
  /** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */
@@ -833,6 +850,7 @@ export type ErrorType = (typeof ErrorType)[keyof typeof ErrorType];
833
850
  */
834
851
  const CLIENT_ONLY_ERROR_TYPES = new Set<string>([
835
852
  ErrorType.Network,
853
+ ErrorType.Timeout,
836
854
  ErrorType.Cancelled,
837
855
  ErrorType.File,
838
856
  ErrorType.Config,
@@ -849,12 +867,20 @@ const ERROR_CATEGORIES = {
849
867
  * over the statusless ones too — those are raised locally and have no
850
868
  * status for `isClientError`'s second arm to read, so omitting one makes it
851
869
  * read as a server fault. The rule is the membership test: every type in
852
- * `CLIENT_ONLY_ERROR_TYPES` except `Network` (which `isNetworkError` owns)
853
- * belongs here.
870
+ * `CLIENT_ONLY_ERROR_TYPES` except the two `isNetworkError` owns belongs
871
+ * here.
854
872
  *
855
873
  * `Cancelled` was missing until 2026-07-29, which is exactly that failure:
856
874
  * a caller who aborted their own deploy was told "server error: please try
857
875
  * again" — the CLI's fallback for everything this set does not claim.
876
+ *
877
+ * `Timeout` is deliberately NOT here, and it is the sharper case, because
878
+ * it is the one client-only type that is not the client's fault: the
879
+ * caller set a ceiling, but what exhausted it was the network or the
880
+ * server. Reading it as client-attributable would say the caller erred,
881
+ * and it would silently disarm every consumer whose retry predicate
882
+ * declines `isClientError()` — a deadline is precisely the failure worth
883
+ * a second attempt.
858
884
  */
859
885
  client: new Set<ErrorType>([
860
886
  ErrorType.Business,
@@ -866,7 +892,14 @@ const ERROR_CATEGORIES = {
866
892
  ErrorType.RateLimit,
867
893
  ErrorType.Validation,
868
894
  ]),
869
- network: new Set<ErrorType>([ErrorType.Network]),
895
+ /**
896
+ * The exchange never happened. Two types, one category: a refused
897
+ * connection and an expired deadline differ in what a surface should SAY
898
+ * and in nothing else a consumer decides on — both are retryable, neither
899
+ * carries a wire message to relay, neither is worth reporting as an
900
+ * incident. See `ErrorType.Timeout` for why the type is distinct anyway.
901
+ */
902
+ network: new Set<ErrorType>([ErrorType.Network, ErrorType.Timeout]),
870
903
  auth: new Set<ErrorType>([ErrorType.Authentication]),
871
904
  } as const;
872
905
 
@@ -1019,7 +1052,7 @@ export class ShipError extends Error {
1019
1052
  * on the client). Falls back to status-derived (401 → Authentication,
1020
1053
  * 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses
1021
1054
  * (CDN errors, intermediaries) or malformed bodies. Client-only types
1022
- * (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the
1055
+ * (`Network`, `Timeout`, `Cancelled`, `File`, `Config`) are filtered out of the
1023
1056
  * trusted set — a misbehaving server claiming one of those is ignored.
1024
1057
  *
1025
1058
  * `operationName` (e.g. `"Get account"`) is used to compose the fallback
@@ -1107,8 +1140,9 @@ export class ShipError extends Error {
1107
1140
  * Routing:
1108
1141
  * - Already a `ShipError` → returned as-is (caller's intent preserved)
1109
1142
  * - `AbortError` → `ShipError.cancelled(...)` — someone stopped it on purpose
1110
- * - `TimeoutError` → `ShipError.network(...)` — a deadline expired, so
1111
- * nothing was exchanged; the message names the timeout
1143
+ * - `TimeoutError` → `ShipError.timeout(...)` — a deadline expired; the
1144
+ * message names the timeout, and the type is in the network CATEGORY
1145
+ * because nothing was exchanged
1112
1146
  * - A transport failure → `ShipError.network(...)` — see `isTransportFailure`
1113
1147
  * for what each runtime offers as evidence
1114
1148
  * - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)
@@ -1145,11 +1179,14 @@ export class ShipError extends Error {
1145
1179
  return ShipError.cancelled(`${op} was cancelled`);
1146
1180
  }
1147
1181
  if (name === 'TimeoutError') {
1148
- // A deadline, not a fault and not a cancellation: nothing was exchanged,
1149
- // which is exactly what `Network` claims. The runtime's own sentence is
1150
- // dropped here rather than relayed "The operation was aborted due to
1151
- // timeout" is the mechanism, not the news.
1152
- return ShipError.network(`${op} timed out`, { cause });
1182
+ // A deadline: not a fault, not a cancellation, and since it has its
1183
+ // own type no longer merely "network". Nothing was exchanged, which
1184
+ // is what keeps it in the network CATEGORY and therefore retryable; the
1185
+ // type is what lets a surface say "timed out" instead of sending
1186
+ // someone to check their Wi-Fi. The runtime's own sentence is dropped
1187
+ // rather than relayed: "The operation was aborted due to timeout" is
1188
+ // the mechanism, not the news.
1189
+ return ShipError.timeout(`${op} timed out`, { cause });
1153
1190
  }
1154
1191
 
1155
1192
  if (cause instanceof Error) {
@@ -1209,6 +1246,17 @@ export class ShipError extends Error {
1209
1246
  return new ShipError(ErrorType.Network, message, undefined, details);
1210
1247
  }
1211
1248
 
1249
+ /**
1250
+ * A deadline expired before the exchange completed.
1251
+ *
1252
+ * Statusless like its four client-only siblings: no exchange completed, so
1253
+ * there is no HTTP status to report. `isNetworkError()` is true — see
1254
+ * `ErrorType.Timeout` for why the category is shared and the type is not.
1255
+ */
1256
+ static timeout(message: string, details?: unknown): ShipError {
1257
+ return new ShipError(ErrorType.Timeout, message, undefined, details);
1258
+ }
1259
+
1212
1260
  static cancelled(message: string, details?: unknown): ShipError {
1213
1261
  return new ShipError(ErrorType.Cancelled, message, undefined, details);
1214
1262
  }