@shipstatic/types 0.9.1 → 0.9.2

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
@@ -46,6 +46,20 @@ if (error.isClientError()) { /* Business | Config | File | Validation */ }
46
46
  if (error.isAuthError()) { /* handle auth */ }
47
47
  ```
48
48
 
49
+ **HTTP client integration.** Both producer and consumer sides of the wire have first-class helpers, so every HTTP client across the platform reconstructs the same `ShipError` shape:
50
+
51
+ ```typescript
52
+ // Producer side (API workers): serialize a ShipError to JSON
53
+ return c.json(error.toResponse(), error.status ?? 500);
54
+
55
+ // Consumer side (SDK, web app): rehydrate from any error Response
56
+ if (!response.ok) {
57
+ throw await ShipError.fromHttpResponse(response, 'Get account failed');
58
+ }
59
+ ```
60
+
61
+ `fromHttpResponse` derives the error type from HTTP status (401 → `Authentication`, 429 → `RateLimit`, else → `Api`), preserving the body's `message`, `error`, and `details` best-effort. The optional second arg is a fallback message used when the body has nothing usable.
62
+
49
63
  ### Status Constants
50
64
 
51
65
  ```typescript
@@ -62,7 +76,7 @@ import {
62
76
 
63
77
  ```typescript
64
78
  import type {
65
- PlatformLimits, // plan-based caps from /config (file size, file count, total size)
79
+ PlatformLimits, // plan-based caps from /limits (file size, file count, total size)
66
80
  BillingStatus,
67
81
  CheckoutSession,
68
82
  ActivityListResponse,
package/dist/index.d.ts CHANGED
@@ -350,8 +350,20 @@ export declare class ShipError extends Error {
350
350
  constructor(type: ErrorType, message: string, status?: number | undefined, details?: any | undefined);
351
351
  /** Convert to wire format */
352
352
  toResponse(): ErrorResponse;
353
- /** Create from wire format */
354
- static fromResponse(response: ErrorResponse): ShipError;
353
+ /**
354
+ * Construct a `ShipError` from an HTTP error response.
355
+ *
356
+ * Best-effort body parse for `{ message, error?, details? }`. Message
357
+ * resolution: `body.message` → `body.error` → `fallbackMessage` →
358
+ * `Request failed with status N`. Status drives the error type — same
359
+ * convention used by the SDK and web console — so `error.status === 429`
360
+ * always lines up with `ErrorType.RateLimit`, etc., regardless of what the
361
+ * body's `error` field claims.
362
+ *
363
+ * Async because it reads the response body. Returns rather than throws so
364
+ * callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
365
+ */
366
+ static fromHttpResponse(response: Response, fallbackMessage?: string): Promise<ShipError>;
355
367
  static validation(message: string, details?: any): ShipError;
356
368
  static notFound(resource: string, id?: string): ShipError;
357
369
  static rateLimit(message?: string): ShipError;
package/dist/index.js CHANGED
@@ -117,9 +117,49 @@ export class ShipError extends Error {
117
117
  details
118
118
  };
119
119
  }
120
- /** Create from wire format */
121
- static fromResponse(response) {
122
- return new ShipError(response.error, response.message, response.status, response.details);
120
+ /**
121
+ * Construct a `ShipError` from an HTTP error response.
122
+ *
123
+ * Best-effort body parse for `{ message, error?, details? }`. Message
124
+ * resolution: `body.message` → `body.error` → `fallbackMessage` →
125
+ * `Request failed with status N`. Status drives the error type — same
126
+ * convention used by the SDK and web console — so `error.status === 429`
127
+ * always lines up with `ErrorType.RateLimit`, etc., regardless of what the
128
+ * body's `error` field claims.
129
+ *
130
+ * Async because it reads the response body. Returns rather than throws so
131
+ * callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
132
+ */
133
+ static async fromHttpResponse(response, fallbackMessage) {
134
+ let message;
135
+ let details;
136
+ try {
137
+ const contentType = response.headers.get('content-type');
138
+ if (contentType?.includes('application/json')) {
139
+ const json = await response.json();
140
+ if (json && typeof json === 'object') {
141
+ const obj = json;
142
+ if (typeof obj.message === 'string')
143
+ message = obj.message;
144
+ else if (typeof obj.error === 'string')
145
+ message = obj.error;
146
+ details = obj.details;
147
+ }
148
+ }
149
+ else {
150
+ const text = await response.text();
151
+ if (text)
152
+ message = text;
153
+ }
154
+ }
155
+ catch {
156
+ // Body unreadable; fall through to fallback.
157
+ }
158
+ message = message || fallbackMessage || `Request failed with status ${response.status}`;
159
+ const type = response.status === 401 ? ErrorType.Authentication :
160
+ response.status === 429 ? ErrorType.RateLimit :
161
+ ErrorType.Api;
162
+ return new ShipError(type, message, response.status, details);
123
163
  }
124
164
  // Factory methods for common errors
125
165
  static validation(message, details) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -428,9 +428,52 @@ export class ShipError extends Error {
428
428
  };
429
429
  }
430
430
 
431
- /** Create from wire format */
432
- static fromResponse(response: ErrorResponse): ShipError {
433
- return new ShipError(response.error, response.message, response.status, response.details);
431
+ /**
432
+ * Construct a `ShipError` from an HTTP error response.
433
+ *
434
+ * Best-effort body parse for `{ message, error?, details? }`. Message
435
+ * resolution: `body.message` → `body.error` → `fallbackMessage` →
436
+ * `Request failed with status N`. Status drives the error type — same
437
+ * convention used by the SDK and web console — so `error.status === 429`
438
+ * always lines up with `ErrorType.RateLimit`, etc., regardless of what the
439
+ * body's `error` field claims.
440
+ *
441
+ * Async because it reads the response body. Returns rather than throws so
442
+ * callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
443
+ */
444
+ static async fromHttpResponse(
445
+ response: Response,
446
+ fallbackMessage?: string,
447
+ ): Promise<ShipError> {
448
+ let message: string | undefined;
449
+ let details: unknown;
450
+
451
+ try {
452
+ const contentType = response.headers.get('content-type');
453
+ if (contentType?.includes('application/json')) {
454
+ const json: unknown = await response.json();
455
+ if (json && typeof json === 'object') {
456
+ const obj = json as Record<string, unknown>;
457
+ if (typeof obj.message === 'string') message = obj.message;
458
+ else if (typeof obj.error === 'string') message = obj.error;
459
+ details = obj.details;
460
+ }
461
+ } else {
462
+ const text = await response.text();
463
+ if (text) message = text;
464
+ }
465
+ } catch {
466
+ // Body unreadable; fall through to fallback.
467
+ }
468
+
469
+ message = message || fallbackMessage || `Request failed with status ${response.status}`;
470
+
471
+ const type =
472
+ response.status === 401 ? ErrorType.Authentication :
473
+ response.status === 429 ? ErrorType.RateLimit :
474
+ ErrorType.Api;
475
+
476
+ return new ShipError(type, message, response.status, details);
434
477
  }
435
478
 
436
479
  // Factory methods for common errors