@shipstatic/types 0.9.2 → 0.9.4
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 +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +26 -14
- package/package.json +1 -1
- package/src/index.ts +32 -16
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ if (!response.ok) {
|
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
`fromHttpResponse`
|
|
61
|
+
`fromHttpResponse` trusts the body's `error` field when it's a known `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`, 429 → `RateLimit`, else → `Api`). Body's `message` and `details` are preserved best-effort. The optional second arg is a fallback message used when the body has nothing usable.
|
|
62
62
|
|
|
63
63
|
### Status Constants
|
|
64
64
|
|
package/dist/index.d.ts
CHANGED
|
@@ -317,9 +317,9 @@ export declare const ErrorType: {
|
|
|
317
317
|
readonly Business: "business_logic_error";
|
|
318
318
|
/** API server error (500) */
|
|
319
319
|
readonly Api: "internal_server_error";
|
|
320
|
-
/** Network/connection error */
|
|
320
|
+
/** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
|
|
321
321
|
readonly Network: "network_error";
|
|
322
|
-
/** Operation was cancelled */
|
|
322
|
+
/** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
|
|
323
323
|
readonly Cancelled: "operation_cancelled";
|
|
324
324
|
/** File operation error */
|
|
325
325
|
readonly File: "file_error";
|
|
@@ -355,10 +355,13 @@ export declare class ShipError extends Error {
|
|
|
355
355
|
*
|
|
356
356
|
* Best-effort body parse for `{ message, error?, details? }`. Message
|
|
357
357
|
* resolution: `body.message` → `body.error` → `fallbackMessage` →
|
|
358
|
-
* `Request failed with status N`.
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
358
|
+
* `Request failed with status N`.
|
|
359
|
+
*
|
|
360
|
+
* Type resolution: trusts `body.error` when it's a known `ErrorType`
|
|
361
|
+
* (preserves the wire's intent — server's `ShipError.validation(...)`
|
|
362
|
+
* round-trips back to `ErrorType.Validation` on the client). Falls back to
|
|
363
|
+
* status-derived (401 → Authentication, 429 → RateLimit, else → Api) for
|
|
364
|
+
* non-API responses (CDN errors, intermediaries) or malformed bodies.
|
|
362
365
|
*
|
|
363
366
|
* Async because it reads the response body. Returns rather than throws so
|
|
364
367
|
* callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
|
|
@@ -374,8 +377,6 @@ export declare class ShipError extends Error {
|
|
|
374
377
|
static file(message: string, filePath?: string): ShipError;
|
|
375
378
|
static config(message: string, details?: any): ShipError;
|
|
376
379
|
static api(message: string, status?: number): ShipError;
|
|
377
|
-
static database(message: string, status?: number): ShipError;
|
|
378
|
-
static storage(message: string, status?: number): ShipError;
|
|
379
380
|
get filePath(): string | undefined;
|
|
380
381
|
isClientError(): boolean;
|
|
381
382
|
isNetworkError(): boolean;
|
package/dist/index.js
CHANGED
|
@@ -71,9 +71,9 @@ export const ErrorType = {
|
|
|
71
71
|
Business: 'business_logic_error',
|
|
72
72
|
/** API server error (500) */
|
|
73
73
|
Api: 'internal_server_error',
|
|
74
|
-
/** Network/connection error */
|
|
74
|
+
/** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
|
|
75
75
|
Network: 'network_error',
|
|
76
|
-
/** Operation was cancelled */
|
|
76
|
+
/** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
|
|
77
77
|
Cancelled: 'operation_cancelled',
|
|
78
78
|
/** File operation error */
|
|
79
79
|
File: 'file_error',
|
|
@@ -90,6 +90,17 @@ const ERROR_CATEGORIES = {
|
|
|
90
90
|
network: new Set([ErrorType.Network]),
|
|
91
91
|
auth: new Set([ErrorType.Authentication]),
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Lookup set of error types that legitimately appear on the wire — i.e.
|
|
95
|
+
* server-thrown types. Used by `ShipError.fromHttpResponse` to validate the
|
|
96
|
+
* body's `error` field before trusting it as the `ShipError.type`.
|
|
97
|
+
*
|
|
98
|
+
* Excludes `Network` and `Cancelled`, which are client-side-only by design:
|
|
99
|
+
* they originate on the client (fetch failure, abort) and should never be
|
|
100
|
+
* reconstructed from a server response, even if a misbehaving server were
|
|
101
|
+
* to send them. A defensive omission, not a theoretical concern.
|
|
102
|
+
*/
|
|
103
|
+
const SERVER_PRODUCIBLE_ERROR_TYPES = new Set(Object.values(ErrorType).filter(t => t !== ErrorType.Network && t !== ErrorType.Cancelled));
|
|
93
104
|
/**
|
|
94
105
|
* Simple unified error class for both API and SDK
|
|
95
106
|
*/
|
|
@@ -122,10 +133,13 @@ export class ShipError extends Error {
|
|
|
122
133
|
*
|
|
123
134
|
* Best-effort body parse for `{ message, error?, details? }`. Message
|
|
124
135
|
* resolution: `body.message` → `body.error` → `fallbackMessage` →
|
|
125
|
-
* `Request failed with status N`.
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
136
|
+
* `Request failed with status N`.
|
|
137
|
+
*
|
|
138
|
+
* Type resolution: trusts `body.error` when it's a known `ErrorType`
|
|
139
|
+
* (preserves the wire's intent — server's `ShipError.validation(...)`
|
|
140
|
+
* round-trips back to `ErrorType.Validation` on the client). Falls back to
|
|
141
|
+
* status-derived (401 → Authentication, 429 → RateLimit, else → Api) for
|
|
142
|
+
* non-API responses (CDN errors, intermediaries) or malformed bodies.
|
|
129
143
|
*
|
|
130
144
|
* Async because it reads the response body. Returns rather than throws so
|
|
131
145
|
* callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
|
|
@@ -133,6 +147,7 @@ export class ShipError extends Error {
|
|
|
133
147
|
static async fromHttpResponse(response, fallbackMessage) {
|
|
134
148
|
let message;
|
|
135
149
|
let details;
|
|
150
|
+
let bodyType;
|
|
136
151
|
try {
|
|
137
152
|
const contentType = response.headers.get('content-type');
|
|
138
153
|
if (contentType?.includes('application/json')) {
|
|
@@ -144,6 +159,9 @@ export class ShipError extends Error {
|
|
|
144
159
|
else if (typeof obj.error === 'string')
|
|
145
160
|
message = obj.error;
|
|
146
161
|
details = obj.details;
|
|
162
|
+
if (typeof obj.error === 'string' && SERVER_PRODUCIBLE_ERROR_TYPES.has(obj.error)) {
|
|
163
|
+
bodyType = obj.error;
|
|
164
|
+
}
|
|
147
165
|
}
|
|
148
166
|
}
|
|
149
167
|
else {
|
|
@@ -156,9 +174,9 @@ export class ShipError extends Error {
|
|
|
156
174
|
// Body unreadable; fall through to fallback.
|
|
157
175
|
}
|
|
158
176
|
message = message || fallbackMessage || `Request failed with status ${response.status}`;
|
|
159
|
-
const type = response.status === 401 ? ErrorType.Authentication :
|
|
177
|
+
const type = bodyType ?? (response.status === 401 ? ErrorType.Authentication :
|
|
160
178
|
response.status === 429 ? ErrorType.RateLimit :
|
|
161
|
-
ErrorType.Api;
|
|
179
|
+
ErrorType.Api);
|
|
162
180
|
return new ShipError(type, message, response.status, details);
|
|
163
181
|
}
|
|
164
182
|
// Factory methods for common errors
|
|
@@ -193,12 +211,6 @@ export class ShipError extends Error {
|
|
|
193
211
|
static api(message, status = 500) {
|
|
194
212
|
return new ShipError(ErrorType.Api, message, status);
|
|
195
213
|
}
|
|
196
|
-
static database(message, status = 500) {
|
|
197
|
-
return new ShipError(ErrorType.Api, message, status);
|
|
198
|
-
}
|
|
199
|
-
static storage(message, status = 500) {
|
|
200
|
-
return new ShipError(ErrorType.Api, message, status);
|
|
201
|
-
}
|
|
202
214
|
// Helper getter for accessing file path from details
|
|
203
215
|
get filePath() {
|
|
204
216
|
return this.details?.filePath;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -362,9 +362,9 @@ export const ErrorType = {
|
|
|
362
362
|
Business: 'business_logic_error',
|
|
363
363
|
/** API server error (500) */
|
|
364
364
|
Api: 'internal_server_error',
|
|
365
|
-
/** Network/connection error */
|
|
365
|
+
/** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */
|
|
366
366
|
Network: 'network_error',
|
|
367
|
-
/** Operation was cancelled */
|
|
367
|
+
/** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */
|
|
368
368
|
Cancelled: 'operation_cancelled',
|
|
369
369
|
/** File operation error */
|
|
370
370
|
File: 'file_error',
|
|
@@ -385,6 +385,22 @@ const ERROR_CATEGORIES = {
|
|
|
385
385
|
auth: new Set<ErrorType>([ErrorType.Authentication]),
|
|
386
386
|
} as const;
|
|
387
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Lookup set of error types that legitimately appear on the wire — i.e.
|
|
390
|
+
* server-thrown types. Used by `ShipError.fromHttpResponse` to validate the
|
|
391
|
+
* body's `error` field before trusting it as the `ShipError.type`.
|
|
392
|
+
*
|
|
393
|
+
* Excludes `Network` and `Cancelled`, which are client-side-only by design:
|
|
394
|
+
* they originate on the client (fetch failure, abort) and should never be
|
|
395
|
+
* reconstructed from a server response, even if a misbehaving server were
|
|
396
|
+
* to send them. A defensive omission, not a theoretical concern.
|
|
397
|
+
*/
|
|
398
|
+
const SERVER_PRODUCIBLE_ERROR_TYPES = new Set<string>(
|
|
399
|
+
Object.values(ErrorType).filter(
|
|
400
|
+
t => t !== ErrorType.Network && t !== ErrorType.Cancelled,
|
|
401
|
+
),
|
|
402
|
+
);
|
|
403
|
+
|
|
388
404
|
/**
|
|
389
405
|
* Standard error response format used everywhere
|
|
390
406
|
*/
|
|
@@ -433,10 +449,13 @@ export class ShipError extends Error {
|
|
|
433
449
|
*
|
|
434
450
|
* Best-effort body parse for `{ message, error?, details? }`. Message
|
|
435
451
|
* resolution: `body.message` → `body.error` → `fallbackMessage` →
|
|
436
|
-
* `Request failed with status N`.
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
452
|
+
* `Request failed with status N`.
|
|
453
|
+
*
|
|
454
|
+
* Type resolution: trusts `body.error` when it's a known `ErrorType`
|
|
455
|
+
* (preserves the wire's intent — server's `ShipError.validation(...)`
|
|
456
|
+
* round-trips back to `ErrorType.Validation` on the client). Falls back to
|
|
457
|
+
* status-derived (401 → Authentication, 429 → RateLimit, else → Api) for
|
|
458
|
+
* non-API responses (CDN errors, intermediaries) or malformed bodies.
|
|
440
459
|
*
|
|
441
460
|
* Async because it reads the response body. Returns rather than throws so
|
|
442
461
|
* callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.
|
|
@@ -447,6 +466,7 @@ export class ShipError extends Error {
|
|
|
447
466
|
): Promise<ShipError> {
|
|
448
467
|
let message: string | undefined;
|
|
449
468
|
let details: unknown;
|
|
469
|
+
let bodyType: ErrorType | undefined;
|
|
450
470
|
|
|
451
471
|
try {
|
|
452
472
|
const contentType = response.headers.get('content-type');
|
|
@@ -457,6 +477,9 @@ export class ShipError extends Error {
|
|
|
457
477
|
if (typeof obj.message === 'string') message = obj.message;
|
|
458
478
|
else if (typeof obj.error === 'string') message = obj.error;
|
|
459
479
|
details = obj.details;
|
|
480
|
+
if (typeof obj.error === 'string' && SERVER_PRODUCIBLE_ERROR_TYPES.has(obj.error)) {
|
|
481
|
+
bodyType = obj.error as ErrorType;
|
|
482
|
+
}
|
|
460
483
|
}
|
|
461
484
|
} else {
|
|
462
485
|
const text = await response.text();
|
|
@@ -468,10 +491,11 @@ export class ShipError extends Error {
|
|
|
468
491
|
|
|
469
492
|
message = message || fallbackMessage || `Request failed with status ${response.status}`;
|
|
470
493
|
|
|
471
|
-
const type =
|
|
494
|
+
const type = bodyType ?? (
|
|
472
495
|
response.status === 401 ? ErrorType.Authentication :
|
|
473
496
|
response.status === 429 ? ErrorType.RateLimit :
|
|
474
|
-
ErrorType.Api
|
|
497
|
+
ErrorType.Api
|
|
498
|
+
);
|
|
475
499
|
|
|
476
500
|
return new ShipError(type, message, response.status, details);
|
|
477
501
|
}
|
|
@@ -518,14 +542,6 @@ export class ShipError extends Error {
|
|
|
518
542
|
return new ShipError(ErrorType.Api, message, status);
|
|
519
543
|
}
|
|
520
544
|
|
|
521
|
-
static database(message: string, status: number = 500): ShipError {
|
|
522
|
-
return new ShipError(ErrorType.Api, message, status);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
static storage(message: string, status: number = 500): ShipError {
|
|
526
|
-
return new ShipError(ErrorType.Api, message, status);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
545
|
// Helper getter for accessing file path from details
|
|
530
546
|
get filePath(): string | undefined {
|
|
531
547
|
return this.details?.filePath;
|