polyfill abort signal methods

This commit is contained in:
Samuel Newman
2026-08-26 17:14:42 +01:00
parent ed4126f5a2
commit 551b8df117
+73
View File
@@ -1,3 +1,76 @@
import 'fast-text-encoding'
/*
* React Native installs abort-controller@3 as its AbortController global. That
* package predates AbortSignal.reason and AbortSignal.throwIfAborted(), while
* newer libraries such as @atproto/lex-client expect the modern DOM API.
*
* Patch the shared prototype once so every signal - including signals created
* by dependencies - has browser-compatible abort behavior. The reason is
* stored before dispatching the abort event so listeners can read it
* synchronously, and the first abort reason wins as required by the DOM spec.
*/
const abortReasons = new WeakMap<AbortSignal, unknown>()
const abortSignalPrototype = AbortSignal.prototype as AbortSignal & {
readonly reason?: unknown
throwIfAborted?: () => void
}
function createDefaultAbortReason(): unknown {
if (typeof DOMException !== 'undefined') {
return new DOMException('This operation was aborted', 'AbortError')
}
const error = new Error('This operation was aborted')
error.name = 'AbortError'
return error
}
if (!('reason' in abortSignalPrototype)) {
const originalAbort = AbortController.prototype.abort
Object.defineProperty(AbortController.prototype, 'abort', {
configurable: true,
writable: true,
value: function abort(this: AbortController, reason?: unknown) {
const signal = this.signal
if (!signal.aborted) {
abortReasons.set(
signal,
reason === undefined ? createDefaultAbortReason() : reason,
)
}
try {
originalAbort.call(this)
} catch (error) {
if (!signal.aborted) abortReasons.delete(signal)
throw error
}
},
})
Object.defineProperty(AbortSignal.prototype, 'reason', {
configurable: true,
get(this: AbortSignal) {
if (!this.aborted) return undefined
let reason = abortReasons.get(this)
if (reason === undefined) {
reason = createDefaultAbortReason()
abortReasons.set(this, reason)
}
return reason
},
})
}
if (typeof abortSignalPrototype.throwIfAborted !== 'function') {
Object.defineProperty(AbortSignal.prototype, 'throwIfAborted', {
configurable: true,
writable: true,
value: function throwIfAborted(this: AbortSignal) {
if (!this.aborted) return
throw (this as AbortSignal & {readonly reason: unknown}).reason
},
})
}
export {}