@dungarees/core 0.11.2 → 0.11.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/error.d.ts +2 -0
- package/error.js +17 -0
- package/error.test.js +29 -1
- package/package.json +1 -1
package/error.d.ts
CHANGED
|
@@ -4,3 +4,5 @@ export declare const createCausedError: ({ message, cause }: {
|
|
|
4
4
|
cause: unknown;
|
|
5
5
|
}) => Error;
|
|
6
6
|
export declare const getThrownError: (run: () => unknown) => Error;
|
|
7
|
+
export declare const isAbortError: (cause: unknown) => boolean;
|
|
8
|
+
export declare const isNetworkError: (cause: unknown) => boolean;
|
package/error.js
CHANGED
|
@@ -12,3 +12,20 @@ export const getThrownError = (run) => {
|
|
|
12
12
|
}
|
|
13
13
|
throw new Error('Expected a throw, but nothing was thrown');
|
|
14
14
|
};
|
|
15
|
+
// Reads the property without asserting a shape, so a rejection carrying a plain object is treated
|
|
16
|
+
// the same as one carrying an Error.
|
|
17
|
+
const readStringProperty = (value, property) => {
|
|
18
|
+
if (typeof value !== 'object' || value === null || !(property in value)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const read = Reflect.get(value, property);
|
|
22
|
+
return typeof read === 'string' ? read : undefined;
|
|
23
|
+
};
|
|
24
|
+
// The message is checked as well as the name because a cancelled request is often re-thrown
|
|
25
|
+
// wrapped, which keeps the reason in the message but resets the name to plain 'Error'.
|
|
26
|
+
export const isAbortError = (cause) => readStringProperty(cause, 'name') === 'AbortError' ||
|
|
27
|
+
/AbortError/i.test(readStringProperty(cause, 'message') ?? '');
|
|
28
|
+
// A fetch that never reached the network rejects with a TypeError whose message is the only thing
|
|
29
|
+
// distinguishing it from a programming error, and each engine words it differently.
|
|
30
|
+
const NETWORK_FAILURE_MESSAGE = /Load failed/i;
|
|
31
|
+
export const isNetworkError = (cause) => cause instanceof TypeError && NETWORK_FAILURE_MESSAGE.test(cause.message);
|
package/error.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createCausedError, getErrorMessage, getThrownError } from './error.js';
|
|
1
|
+
import { createCausedError, getErrorMessage, getThrownError, isAbortError, isNetworkError, } from './error.js';
|
|
2
2
|
import { expect, test } from 'vitest';
|
|
3
3
|
test('getErrorMessage reads the message off an Error', () => {
|
|
4
4
|
expect(getErrorMessage(new Error('it broke'))).toBe('it broke');
|
|
@@ -42,3 +42,31 @@ test('getThrownError fails when the thrown value is not an Error', () => {
|
|
|
42
42
|
throw notAnError;
|
|
43
43
|
})).toThrow('Expected an Error to be thrown, got: a bare string');
|
|
44
44
|
});
|
|
45
|
+
test('isAbortError recognises the DOMException a real abort produces', () => {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
controller.abort();
|
|
48
|
+
expect(isAbortError(controller.signal.reason)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
test('isAbortError recognises an error that only names AbortError in its message', () => {
|
|
51
|
+
expect(isAbortError(new Error('Fetch failed: AbortError'))).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
test('isAbortError rejects an ordinary error', () => {
|
|
54
|
+
expect(isAbortError(new Error('it broke'))).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
test('isAbortError rejects values that carry no name or message at all', () => {
|
|
57
|
+
expect(isAbortError(null)).toBe(false);
|
|
58
|
+
expect(isAbortError(undefined)).toBe(false);
|
|
59
|
+
expect(isAbortError('AbortError')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
test('isNetworkError recognises the TypeError a failed fetch throws', () => {
|
|
62
|
+
expect(isNetworkError(new TypeError('Load failed'))).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
test('isNetworkError rejects a TypeError thrown for any other reason', () => {
|
|
65
|
+
expect(isNetworkError(new TypeError('x is not a function'))).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
test('isNetworkError rejects a matching message on an error that is not a TypeError', () => {
|
|
68
|
+
expect(isNetworkError(new Error('Load failed'))).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
test('isNetworkError rejects a nullish value', () => {
|
|
71
|
+
expect(isNetworkError(null)).toBe(false);
|
|
72
|
+
});
|