Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Throw DomException with name AbortError #482

Merged
Merged
22 changes: 17 additions & 5 deletions source/errors/DOMException.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
const DOMException = globalThis.DOMException ?? Error;
// DOMException is supported on most modern browsers and Node.js 18+.
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException#browser_compatibility
const isDomExceptionSupported = Boolean(globalThis.DOMException);

export default DOMException;

// When targeting Node.js 18, use `signal.throwIfAborted()` (https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted)
// TODO: When targeting Node.js 18, use `signal.throwIfAborted()` (https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted)
export function composeAbortError(signal?: AbortSignal) {
return new DOMException(signal?.reason ?? 'The operation was aborted.');
/*
NOTE: Use DomException with AbortError name as specified in MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
> When abort() is called, the fetch() promise rejects with an Error of type DOMException, with name AbortError.
*/
if (isDomExceptionSupported) {
return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError');
}

// DOMException not supported. Fall back to use of error and override name.
const error = new Error(signal?.reason ?? 'The operation was aborted.');
error.name = 'AbortError';

return error;
}
8 changes: 5 additions & 3 deletions test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ test('ky.extend()', async t => {
await server.close();
});

test('throws DOMException when aborted by user', async t => {
test('throws DOMException/Error with name AbortError when aborted by user', async t => {
const server = await createHttpTestServer();
// eslint-disable-next-line @typescript-eslint/no-empty-function
server.get('/', () => {});
Expand All @@ -592,8 +592,10 @@ test('throws DOMException when aborted by user', async t => {
const response = ky(server.url, {signal});
abortController.abort();

const {name} = (await t.throwsAsync(response))!;
t.true(['DOMException', 'Error'].includes(name), `Expected DOMException or Error, got ${name}`);
const error = (await t.throwsAsync(response))!;

t.true(['DomException', 'Error'].includes(error.constructor.name), `Expected DOMException or Error, got ${error.constructor.name}`);
cristobal marked this conversation as resolved.
Show resolved Hide resolved
t.is(error.name, 'AbortError', `Expected AbortError, got ${error.name}`);
});

test('supports Request instance as input', async t => {
Expand Down