@twinedo/app-error 1.0.2 → 1.0.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 +32 -0
- package/dist/index.cjs +64 -6
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +64 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -168,3 +168,35 @@ if (result.ok) {
|
|
|
168
168
|
console.error(result.error.message);
|
|
169
169
|
}
|
|
170
170
|
```
|
|
171
|
+
|
|
172
|
+
## FAQ
|
|
173
|
+
### 1. Why not just use `try/catch` with `err.message`?
|
|
174
|
+
Because real-world errors are inconsistent:
|
|
175
|
+
- fetch doesn’t throw on 4xx/5xx (you must check res.ok)
|
|
176
|
+
- axios errors have a different shape (error.response, error.request)
|
|
177
|
+
- network/timeout/runtime errors look different across environments
|
|
178
|
+
This library normalizes them into one predictable AppError.
|
|
179
|
+
|
|
180
|
+
### 2. Does `message` always contain the backend error message?
|
|
181
|
+
When possible, yes. Otherwise it falls back to a safe default.<br />
|
|
182
|
+
`message` is always a non-empty string. If the backend returns no usable message (e.g. `{}`, `null`), it becomes `"Something went wrong"`.
|
|
183
|
+
|
|
184
|
+
### 3. What about status?
|
|
185
|
+
`status` is set only when there is an actual HTTP response (kind "http"), e.g. `400`, `404`, `500`.<br />
|
|
186
|
+
For network/timeout/runtime `errors`, `status` is `undefined`.
|
|
187
|
+
|
|
188
|
+
### 4. Do I need to define a policy?
|
|
189
|
+
Only if your backend error shape is custom and you want more accurate extraction for:
|
|
190
|
+
- `message`
|
|
191
|
+
- `code`
|
|
192
|
+
- `requestId`<br />
|
|
193
|
+
If your backend already returns `{ message: "..." }`, you can usually skip policies.
|
|
194
|
+
|
|
195
|
+
### 5. Is it framework-agnostic?
|
|
196
|
+
Yes. It’s plain JS/TS and can be used in React, React Native, Vue, Svelte, Angular, Node, etc.
|
|
197
|
+
|
|
198
|
+
### 6. Does it add a lot to bundle size?
|
|
199
|
+
It’s dependency-free and tree-shakable in modern bundlers. Your app typically includes only what you import.
|
|
200
|
+
|
|
201
|
+
### 7. Is this a replacement for an HTTP client?
|
|
202
|
+
No. This library does not send requests or manage retries. It only normalizes errors.
|
package/dist/index.cjs
CHANGED
|
@@ -156,11 +156,30 @@ var defaultHttpRetryable = (status) => {
|
|
|
156
156
|
if (typeof status !== "number") return false;
|
|
157
157
|
return status >= 500 && status <= 599;
|
|
158
158
|
};
|
|
159
|
+
var DEFAULT_HTTP_SUGGESTIONS = {
|
|
160
|
+
400: "Please review your request and ensure all fields are correct.",
|
|
161
|
+
401: "Please ensure you have valid credentials and try again.",
|
|
162
|
+
403: "You do not have permission to perform this action.",
|
|
163
|
+
404: "The requested resource could not be found. Please verify your request.",
|
|
164
|
+
408: "The request took too long. Please try again shortly.",
|
|
165
|
+
409: "A conflict occurred. Please refresh and try again.",
|
|
166
|
+
422: "Some of the provided data is invalid. Please review your input.",
|
|
167
|
+
429: "Too many requests. Please wait a moment and try again.",
|
|
168
|
+
500: "An internal server error occurred. Please try again later or contact support.",
|
|
169
|
+
502: "The server received an invalid response. Please try again later.",
|
|
170
|
+
503: "The service is temporarily unavailable. Please try again later.",
|
|
171
|
+
504: "The server did not respond in time. Please try again later."
|
|
172
|
+
};
|
|
173
|
+
var defaultHttpSuggestion = (status) => {
|
|
174
|
+
if (typeof status !== "number") return void 0;
|
|
175
|
+
return DEFAULT_HTTP_SUGGESTIONS[status];
|
|
176
|
+
};
|
|
159
177
|
var DEFAULT_HTTP_POLICY = {
|
|
160
178
|
message: defaultHttpMessage,
|
|
161
179
|
code: defaultHttpCode,
|
|
162
180
|
requestId: defaultRequestId,
|
|
163
|
-
retryable: defaultHttpRetryable
|
|
181
|
+
retryable: defaultHttpRetryable,
|
|
182
|
+
suggestion: defaultHttpSuggestion
|
|
164
183
|
};
|
|
165
184
|
var defineErrorPolicy = (...configs) => {
|
|
166
185
|
const merged = {};
|
|
@@ -173,7 +192,8 @@ var defineErrorPolicy = (...configs) => {
|
|
|
173
192
|
message: merged.message ?? DEFAULT_HTTP_POLICY.message,
|
|
174
193
|
code: merged.code ?? DEFAULT_HTTP_POLICY.code,
|
|
175
194
|
requestId: merged.requestId ?? DEFAULT_HTTP_POLICY.requestId,
|
|
176
|
-
retryable: merged.retryable ?? DEFAULT_HTTP_POLICY.retryable
|
|
195
|
+
retryable: merged.retryable ?? DEFAULT_HTTP_POLICY.retryable,
|
|
196
|
+
suggestion: merged.suggestion ?? DEFAULT_HTTP_POLICY.suggestion
|
|
177
197
|
}
|
|
178
198
|
};
|
|
179
199
|
};
|
|
@@ -192,7 +212,9 @@ var isAppError = (value) => {
|
|
|
192
212
|
if (!isRecord2(value)) return false;
|
|
193
213
|
const kind = value.kind;
|
|
194
214
|
if (typeof kind !== "string" || !(kind in APP_ERROR_KINDS)) return false;
|
|
195
|
-
|
|
215
|
+
if (typeof value.message !== "string") return false;
|
|
216
|
+
if (typeof value.suggestion !== "string") return false;
|
|
217
|
+
return true;
|
|
196
218
|
};
|
|
197
219
|
|
|
198
220
|
// src/fromFetch.ts
|
|
@@ -216,20 +238,30 @@ var safeInvoke = (fn) => {
|
|
|
216
238
|
var fromFetch = (response, body, policy) => {
|
|
217
239
|
const resolvedPolicy = defineErrorPolicy(policy);
|
|
218
240
|
const status = typeof response.status === "number" ? response.status : void 0;
|
|
241
|
+
const httpResponse = {
|
|
242
|
+
...status !== void 0 ? { status } : {},
|
|
243
|
+
...response.statusText ? { statusText: response.statusText } : {},
|
|
244
|
+
...response.headers !== void 0 ? { headers: response.headers } : {}
|
|
245
|
+
};
|
|
219
246
|
const message = normalizeMessage(
|
|
220
|
-
safeInvoke(() => resolvedPolicy.http.message(body,
|
|
247
|
+
safeInvoke(() => resolvedPolicy.http.message(body, httpResponse))
|
|
221
248
|
) ?? DEFAULT_MESSAGE;
|
|
222
249
|
const code = normalizeMessage(
|
|
223
|
-
safeInvoke(() => resolvedPolicy.http.code(body,
|
|
250
|
+
safeInvoke(() => resolvedPolicy.http.code(body, httpResponse))
|
|
224
251
|
);
|
|
225
252
|
const requestId = normalizeMessage(
|
|
226
253
|
safeInvoke(() => resolvedPolicy.http.requestId(response.headers))
|
|
227
254
|
);
|
|
228
255
|
const retryable = safeInvoke(() => resolvedPolicy.http.retryable(status)) ?? defaultRetryable(status);
|
|
256
|
+
const DEFAULT_SUGGESTION = "An unexpected error occurred. Please try again or contact support.";
|
|
257
|
+
const suggestion = normalizeMessage(
|
|
258
|
+
safeInvoke(() => resolvedPolicy.http.suggestion(status, body, httpResponse))
|
|
259
|
+
) ?? DEFAULT_SUGGESTION;
|
|
229
260
|
return {
|
|
230
261
|
kind: "http",
|
|
231
262
|
message,
|
|
232
263
|
retryable,
|
|
264
|
+
suggestion,
|
|
233
265
|
...status !== void 0 ? { status } : {},
|
|
234
266
|
...code ? { code } : {},
|
|
235
267
|
...requestId ? { requestId } : {},
|
|
@@ -332,6 +364,14 @@ var toHttpResponseLike = (info) => {
|
|
|
332
364
|
|
|
333
365
|
// src/toAppError.ts
|
|
334
366
|
var DEFAULT_MESSAGE2 = "Something went wrong";
|
|
367
|
+
var DEFAULT_KIND_SUGGESTIONS = {
|
|
368
|
+
network: "Please check your internet connection and try again.",
|
|
369
|
+
timeout: "The request took too long. Please try again shortly.",
|
|
370
|
+
parse: "The server returned an unexpected response. Please try again or contact support.",
|
|
371
|
+
validation: "Please review your input and correct any errors.",
|
|
372
|
+
unknown: "An unexpected error occurred. Please try again or contact support.",
|
|
373
|
+
http: ""
|
|
374
|
+
};
|
|
335
375
|
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
336
376
|
"ENOTFOUND",
|
|
337
377
|
"ECONNREFUSED",
|
|
@@ -401,16 +441,19 @@ var isValidationError = (error, name) => {
|
|
|
401
441
|
var normalizeExisting = (error) => {
|
|
402
442
|
const message = normalizeMessage2(error.message) ?? DEFAULT_MESSAGE2;
|
|
403
443
|
const retryable = typeof error.retryable === "boolean" ? error.retryable : defaultRetryable2(error.kind, error.status);
|
|
444
|
+
const suggestion = normalizeMessage2(error.suggestion) ?? (DEFAULT_KIND_SUGGESTIONS[error.kind] || "An unexpected error occurred. Please try again or contact support.");
|
|
404
445
|
return {
|
|
405
446
|
...error,
|
|
406
447
|
message,
|
|
407
|
-
retryable
|
|
448
|
+
retryable,
|
|
449
|
+
suggestion
|
|
408
450
|
};
|
|
409
451
|
};
|
|
410
452
|
var buildAppError = (options) => ({
|
|
411
453
|
kind: options.kind,
|
|
412
454
|
message: options.message,
|
|
413
455
|
retryable: options.retryable,
|
|
456
|
+
suggestion: options.suggestion,
|
|
414
457
|
...options.status !== void 0 ? { status: options.status } : {},
|
|
415
458
|
...options.code ? { code: options.code } : {},
|
|
416
459
|
...options.requestId ? { requestId: options.requestId } : {},
|
|
@@ -433,9 +476,13 @@ var fromStatusObject = (error, policy) => {
|
|
|
433
476
|
const code = safeInvoke2(() => policy.http.code(details, response));
|
|
434
477
|
const requestId = safeInvoke2(() => policy.http.requestId(response.headers));
|
|
435
478
|
const retryable = safeInvoke2(() => policy.http.retryable(status)) ?? defaultRetryable2("http", status);
|
|
479
|
+
const suggestion = normalizeMessage2(
|
|
480
|
+
safeInvoke2(() => policy.http.suggestion(status, details, response))
|
|
481
|
+
) ?? DEFAULT_KIND_SUGGESTIONS.unknown;
|
|
436
482
|
return buildAppError({
|
|
437
483
|
kind: "http",
|
|
438
484
|
message,
|
|
485
|
+
suggestion,
|
|
439
486
|
status,
|
|
440
487
|
code: normalizeMessage2(code),
|
|
441
488
|
retryable,
|
|
@@ -462,9 +509,13 @@ var toAppError = (error, policy) => {
|
|
|
462
509
|
() => resolvedPolicy.http.requestId(axiosInfo.headers)
|
|
463
510
|
);
|
|
464
511
|
const retryable = safeInvoke2(() => resolvedPolicy.http.retryable(axiosInfo.status)) ?? defaultRetryable2("http", axiosInfo.status);
|
|
512
|
+
const suggestion = normalizeMessage2(
|
|
513
|
+
safeInvoke2(() => resolvedPolicy.http.suggestion(axiosInfo.status, axiosInfo.data, response))
|
|
514
|
+
) ?? DEFAULT_KIND_SUGGESTIONS.unknown;
|
|
465
515
|
return buildAppError({
|
|
466
516
|
kind: "http",
|
|
467
517
|
message: message2,
|
|
518
|
+
suggestion,
|
|
468
519
|
status: axiosInfo.status,
|
|
469
520
|
code: normalizeMessage2(code2),
|
|
470
521
|
retryable,
|
|
@@ -477,6 +528,7 @@ var toAppError = (error, policy) => {
|
|
|
477
528
|
return buildAppError({
|
|
478
529
|
kind: "timeout",
|
|
479
530
|
message: DEFAULT_MESSAGE2,
|
|
531
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.timeout,
|
|
480
532
|
retryable: false,
|
|
481
533
|
cause: error
|
|
482
534
|
});
|
|
@@ -485,6 +537,7 @@ var toAppError = (error, policy) => {
|
|
|
485
537
|
return buildAppError({
|
|
486
538
|
kind: "network",
|
|
487
539
|
message: DEFAULT_MESSAGE2,
|
|
540
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.network,
|
|
488
541
|
retryable: true,
|
|
489
542
|
cause: error
|
|
490
543
|
});
|
|
@@ -495,6 +548,7 @@ var toAppError = (error, policy) => {
|
|
|
495
548
|
return buildAppError({
|
|
496
549
|
kind: "timeout",
|
|
497
550
|
message: DEFAULT_MESSAGE2,
|
|
551
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.timeout,
|
|
498
552
|
retryable: false,
|
|
499
553
|
cause: error
|
|
500
554
|
});
|
|
@@ -503,6 +557,7 @@ var toAppError = (error, policy) => {
|
|
|
503
557
|
return buildAppError({
|
|
504
558
|
kind: "network",
|
|
505
559
|
message: DEFAULT_MESSAGE2,
|
|
560
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.network,
|
|
506
561
|
retryable: true,
|
|
507
562
|
cause: error
|
|
508
563
|
});
|
|
@@ -511,6 +566,7 @@ var toAppError = (error, policy) => {
|
|
|
511
566
|
return buildAppError({
|
|
512
567
|
kind: "parse",
|
|
513
568
|
message: DEFAULT_MESSAGE2,
|
|
569
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.parse,
|
|
514
570
|
retryable: false,
|
|
515
571
|
cause: error
|
|
516
572
|
});
|
|
@@ -519,6 +575,7 @@ var toAppError = (error, policy) => {
|
|
|
519
575
|
return buildAppError({
|
|
520
576
|
kind: "validation",
|
|
521
577
|
message: DEFAULT_MESSAGE2,
|
|
578
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.validation,
|
|
522
579
|
retryable: false,
|
|
523
580
|
cause: error,
|
|
524
581
|
details: error
|
|
@@ -531,6 +588,7 @@ var toAppError = (error, policy) => {
|
|
|
531
588
|
return buildAppError({
|
|
532
589
|
kind: "unknown",
|
|
533
590
|
message: DEFAULT_MESSAGE2,
|
|
591
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.unknown,
|
|
534
592
|
retryable: false,
|
|
535
593
|
cause: error
|
|
536
594
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ type AppErrorKind = "http" | "network" | "timeout" | "parse" | "validation" | "u
|
|
|
2
2
|
type AppError = {
|
|
3
3
|
kind: AppErrorKind;
|
|
4
4
|
message: string;
|
|
5
|
+
suggestion: string;
|
|
5
6
|
status?: number;
|
|
6
7
|
code?: string;
|
|
7
8
|
retryable?: boolean;
|
|
@@ -24,6 +25,7 @@ type HttpPolicy = {
|
|
|
24
25
|
code: (data: unknown, response?: HttpResponseLike) => string | undefined;
|
|
25
26
|
requestId: (headers?: HeadersLike) => string | undefined;
|
|
26
27
|
retryable: (status?: number) => boolean;
|
|
28
|
+
suggestion: (status?: number, data?: unknown, response?: HttpResponseLike) => string | undefined;
|
|
27
29
|
};
|
|
28
30
|
type ErrorPolicy = {
|
|
29
31
|
http?: Partial<HttpPolicy>;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ type AppErrorKind = "http" | "network" | "timeout" | "parse" | "validation" | "u
|
|
|
2
2
|
type AppError = {
|
|
3
3
|
kind: AppErrorKind;
|
|
4
4
|
message: string;
|
|
5
|
+
suggestion: string;
|
|
5
6
|
status?: number;
|
|
6
7
|
code?: string;
|
|
7
8
|
retryable?: boolean;
|
|
@@ -24,6 +25,7 @@ type HttpPolicy = {
|
|
|
24
25
|
code: (data: unknown, response?: HttpResponseLike) => string | undefined;
|
|
25
26
|
requestId: (headers?: HeadersLike) => string | undefined;
|
|
26
27
|
retryable: (status?: number) => boolean;
|
|
28
|
+
suggestion: (status?: number, data?: unknown, response?: HttpResponseLike) => string | undefined;
|
|
27
29
|
};
|
|
28
30
|
type ErrorPolicy = {
|
|
29
31
|
http?: Partial<HttpPolicy>;
|
package/dist/index.js
CHANGED
|
@@ -123,11 +123,30 @@ var defaultHttpRetryable = (status) => {
|
|
|
123
123
|
if (typeof status !== "number") return false;
|
|
124
124
|
return status >= 500 && status <= 599;
|
|
125
125
|
};
|
|
126
|
+
var DEFAULT_HTTP_SUGGESTIONS = {
|
|
127
|
+
400: "Please review your request and ensure all fields are correct.",
|
|
128
|
+
401: "Please ensure you have valid credentials and try again.",
|
|
129
|
+
403: "You do not have permission to perform this action.",
|
|
130
|
+
404: "The requested resource could not be found. Please verify your request.",
|
|
131
|
+
408: "The request took too long. Please try again shortly.",
|
|
132
|
+
409: "A conflict occurred. Please refresh and try again.",
|
|
133
|
+
422: "Some of the provided data is invalid. Please review your input.",
|
|
134
|
+
429: "Too many requests. Please wait a moment and try again.",
|
|
135
|
+
500: "An internal server error occurred. Please try again later or contact support.",
|
|
136
|
+
502: "The server received an invalid response. Please try again later.",
|
|
137
|
+
503: "The service is temporarily unavailable. Please try again later.",
|
|
138
|
+
504: "The server did not respond in time. Please try again later."
|
|
139
|
+
};
|
|
140
|
+
var defaultHttpSuggestion = (status) => {
|
|
141
|
+
if (typeof status !== "number") return void 0;
|
|
142
|
+
return DEFAULT_HTTP_SUGGESTIONS[status];
|
|
143
|
+
};
|
|
126
144
|
var DEFAULT_HTTP_POLICY = {
|
|
127
145
|
message: defaultHttpMessage,
|
|
128
146
|
code: defaultHttpCode,
|
|
129
147
|
requestId: defaultRequestId,
|
|
130
|
-
retryable: defaultHttpRetryable
|
|
148
|
+
retryable: defaultHttpRetryable,
|
|
149
|
+
suggestion: defaultHttpSuggestion
|
|
131
150
|
};
|
|
132
151
|
var defineErrorPolicy = (...configs) => {
|
|
133
152
|
const merged = {};
|
|
@@ -140,7 +159,8 @@ var defineErrorPolicy = (...configs) => {
|
|
|
140
159
|
message: merged.message ?? DEFAULT_HTTP_POLICY.message,
|
|
141
160
|
code: merged.code ?? DEFAULT_HTTP_POLICY.code,
|
|
142
161
|
requestId: merged.requestId ?? DEFAULT_HTTP_POLICY.requestId,
|
|
143
|
-
retryable: merged.retryable ?? DEFAULT_HTTP_POLICY.retryable
|
|
162
|
+
retryable: merged.retryable ?? DEFAULT_HTTP_POLICY.retryable,
|
|
163
|
+
suggestion: merged.suggestion ?? DEFAULT_HTTP_POLICY.suggestion
|
|
144
164
|
}
|
|
145
165
|
};
|
|
146
166
|
};
|
|
@@ -159,7 +179,9 @@ var isAppError = (value) => {
|
|
|
159
179
|
if (!isRecord2(value)) return false;
|
|
160
180
|
const kind = value.kind;
|
|
161
181
|
if (typeof kind !== "string" || !(kind in APP_ERROR_KINDS)) return false;
|
|
162
|
-
|
|
182
|
+
if (typeof value.message !== "string") return false;
|
|
183
|
+
if (typeof value.suggestion !== "string") return false;
|
|
184
|
+
return true;
|
|
163
185
|
};
|
|
164
186
|
|
|
165
187
|
// src/fromFetch.ts
|
|
@@ -183,20 +205,30 @@ var safeInvoke = (fn) => {
|
|
|
183
205
|
var fromFetch = (response, body, policy) => {
|
|
184
206
|
const resolvedPolicy = defineErrorPolicy(policy);
|
|
185
207
|
const status = typeof response.status === "number" ? response.status : void 0;
|
|
208
|
+
const httpResponse = {
|
|
209
|
+
...status !== void 0 ? { status } : {},
|
|
210
|
+
...response.statusText ? { statusText: response.statusText } : {},
|
|
211
|
+
...response.headers !== void 0 ? { headers: response.headers } : {}
|
|
212
|
+
};
|
|
186
213
|
const message = normalizeMessage(
|
|
187
|
-
safeInvoke(() => resolvedPolicy.http.message(body,
|
|
214
|
+
safeInvoke(() => resolvedPolicy.http.message(body, httpResponse))
|
|
188
215
|
) ?? DEFAULT_MESSAGE;
|
|
189
216
|
const code = normalizeMessage(
|
|
190
|
-
safeInvoke(() => resolvedPolicy.http.code(body,
|
|
217
|
+
safeInvoke(() => resolvedPolicy.http.code(body, httpResponse))
|
|
191
218
|
);
|
|
192
219
|
const requestId = normalizeMessage(
|
|
193
220
|
safeInvoke(() => resolvedPolicy.http.requestId(response.headers))
|
|
194
221
|
);
|
|
195
222
|
const retryable = safeInvoke(() => resolvedPolicy.http.retryable(status)) ?? defaultRetryable(status);
|
|
223
|
+
const DEFAULT_SUGGESTION = "An unexpected error occurred. Please try again or contact support.";
|
|
224
|
+
const suggestion = normalizeMessage(
|
|
225
|
+
safeInvoke(() => resolvedPolicy.http.suggestion(status, body, httpResponse))
|
|
226
|
+
) ?? DEFAULT_SUGGESTION;
|
|
196
227
|
return {
|
|
197
228
|
kind: "http",
|
|
198
229
|
message,
|
|
199
230
|
retryable,
|
|
231
|
+
suggestion,
|
|
200
232
|
...status !== void 0 ? { status } : {},
|
|
201
233
|
...code ? { code } : {},
|
|
202
234
|
...requestId ? { requestId } : {},
|
|
@@ -299,6 +331,14 @@ var toHttpResponseLike = (info) => {
|
|
|
299
331
|
|
|
300
332
|
// src/toAppError.ts
|
|
301
333
|
var DEFAULT_MESSAGE2 = "Something went wrong";
|
|
334
|
+
var DEFAULT_KIND_SUGGESTIONS = {
|
|
335
|
+
network: "Please check your internet connection and try again.",
|
|
336
|
+
timeout: "The request took too long. Please try again shortly.",
|
|
337
|
+
parse: "The server returned an unexpected response. Please try again or contact support.",
|
|
338
|
+
validation: "Please review your input and correct any errors.",
|
|
339
|
+
unknown: "An unexpected error occurred. Please try again or contact support.",
|
|
340
|
+
http: ""
|
|
341
|
+
};
|
|
302
342
|
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
303
343
|
"ENOTFOUND",
|
|
304
344
|
"ECONNREFUSED",
|
|
@@ -368,16 +408,19 @@ var isValidationError = (error, name) => {
|
|
|
368
408
|
var normalizeExisting = (error) => {
|
|
369
409
|
const message = normalizeMessage2(error.message) ?? DEFAULT_MESSAGE2;
|
|
370
410
|
const retryable = typeof error.retryable === "boolean" ? error.retryable : defaultRetryable2(error.kind, error.status);
|
|
411
|
+
const suggestion = normalizeMessage2(error.suggestion) ?? (DEFAULT_KIND_SUGGESTIONS[error.kind] || "An unexpected error occurred. Please try again or contact support.");
|
|
371
412
|
return {
|
|
372
413
|
...error,
|
|
373
414
|
message,
|
|
374
|
-
retryable
|
|
415
|
+
retryable,
|
|
416
|
+
suggestion
|
|
375
417
|
};
|
|
376
418
|
};
|
|
377
419
|
var buildAppError = (options) => ({
|
|
378
420
|
kind: options.kind,
|
|
379
421
|
message: options.message,
|
|
380
422
|
retryable: options.retryable,
|
|
423
|
+
suggestion: options.suggestion,
|
|
381
424
|
...options.status !== void 0 ? { status: options.status } : {},
|
|
382
425
|
...options.code ? { code: options.code } : {},
|
|
383
426
|
...options.requestId ? { requestId: options.requestId } : {},
|
|
@@ -400,9 +443,13 @@ var fromStatusObject = (error, policy) => {
|
|
|
400
443
|
const code = safeInvoke2(() => policy.http.code(details, response));
|
|
401
444
|
const requestId = safeInvoke2(() => policy.http.requestId(response.headers));
|
|
402
445
|
const retryable = safeInvoke2(() => policy.http.retryable(status)) ?? defaultRetryable2("http", status);
|
|
446
|
+
const suggestion = normalizeMessage2(
|
|
447
|
+
safeInvoke2(() => policy.http.suggestion(status, details, response))
|
|
448
|
+
) ?? DEFAULT_KIND_SUGGESTIONS.unknown;
|
|
403
449
|
return buildAppError({
|
|
404
450
|
kind: "http",
|
|
405
451
|
message,
|
|
452
|
+
suggestion,
|
|
406
453
|
status,
|
|
407
454
|
code: normalizeMessage2(code),
|
|
408
455
|
retryable,
|
|
@@ -429,9 +476,13 @@ var toAppError = (error, policy) => {
|
|
|
429
476
|
() => resolvedPolicy.http.requestId(axiosInfo.headers)
|
|
430
477
|
);
|
|
431
478
|
const retryable = safeInvoke2(() => resolvedPolicy.http.retryable(axiosInfo.status)) ?? defaultRetryable2("http", axiosInfo.status);
|
|
479
|
+
const suggestion = normalizeMessage2(
|
|
480
|
+
safeInvoke2(() => resolvedPolicy.http.suggestion(axiosInfo.status, axiosInfo.data, response))
|
|
481
|
+
) ?? DEFAULT_KIND_SUGGESTIONS.unknown;
|
|
432
482
|
return buildAppError({
|
|
433
483
|
kind: "http",
|
|
434
484
|
message: message2,
|
|
485
|
+
suggestion,
|
|
435
486
|
status: axiosInfo.status,
|
|
436
487
|
code: normalizeMessage2(code2),
|
|
437
488
|
retryable,
|
|
@@ -444,6 +495,7 @@ var toAppError = (error, policy) => {
|
|
|
444
495
|
return buildAppError({
|
|
445
496
|
kind: "timeout",
|
|
446
497
|
message: DEFAULT_MESSAGE2,
|
|
498
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.timeout,
|
|
447
499
|
retryable: false,
|
|
448
500
|
cause: error
|
|
449
501
|
});
|
|
@@ -452,6 +504,7 @@ var toAppError = (error, policy) => {
|
|
|
452
504
|
return buildAppError({
|
|
453
505
|
kind: "network",
|
|
454
506
|
message: DEFAULT_MESSAGE2,
|
|
507
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.network,
|
|
455
508
|
retryable: true,
|
|
456
509
|
cause: error
|
|
457
510
|
});
|
|
@@ -462,6 +515,7 @@ var toAppError = (error, policy) => {
|
|
|
462
515
|
return buildAppError({
|
|
463
516
|
kind: "timeout",
|
|
464
517
|
message: DEFAULT_MESSAGE2,
|
|
518
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.timeout,
|
|
465
519
|
retryable: false,
|
|
466
520
|
cause: error
|
|
467
521
|
});
|
|
@@ -470,6 +524,7 @@ var toAppError = (error, policy) => {
|
|
|
470
524
|
return buildAppError({
|
|
471
525
|
kind: "network",
|
|
472
526
|
message: DEFAULT_MESSAGE2,
|
|
527
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.network,
|
|
473
528
|
retryable: true,
|
|
474
529
|
cause: error
|
|
475
530
|
});
|
|
@@ -478,6 +533,7 @@ var toAppError = (error, policy) => {
|
|
|
478
533
|
return buildAppError({
|
|
479
534
|
kind: "parse",
|
|
480
535
|
message: DEFAULT_MESSAGE2,
|
|
536
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.parse,
|
|
481
537
|
retryable: false,
|
|
482
538
|
cause: error
|
|
483
539
|
});
|
|
@@ -486,6 +542,7 @@ var toAppError = (error, policy) => {
|
|
|
486
542
|
return buildAppError({
|
|
487
543
|
kind: "validation",
|
|
488
544
|
message: DEFAULT_MESSAGE2,
|
|
545
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.validation,
|
|
489
546
|
retryable: false,
|
|
490
547
|
cause: error,
|
|
491
548
|
details: error
|
|
@@ -498,6 +555,7 @@ var toAppError = (error, policy) => {
|
|
|
498
555
|
return buildAppError({
|
|
499
556
|
kind: "unknown",
|
|
500
557
|
message: DEFAULT_MESSAGE2,
|
|
558
|
+
suggestion: DEFAULT_KIND_SUGGESTIONS.unknown,
|
|
501
559
|
retryable: false,
|
|
502
560
|
cause: error
|
|
503
561
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinedo/app-error",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A configurable error normalization layer for fetch, axios-like, and runtime errors.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -40,4 +40,4 @@
|
|
|
40
40
|
"url": "https://github.com/twinedo/app-error/issues"
|
|
41
41
|
},
|
|
42
42
|
"homepage": "https://github.com/twinedo/app-error#readme"
|
|
43
|
-
}
|
|
43
|
+
}
|