1'use strict'; 2 3// Modeled very closely on the AbortController implementation 4// in https://github.com/mysticatea/abort-controller (MIT license) 5 6const { 7 ObjectAssign, 8 ObjectDefineProperties, 9 ObjectSetPrototypeOf, 10 ObjectDefineProperty, 11 Symbol, 12 SymbolToStringTag, 13 TypeError, 14} = primordials; 15 16const { 17 defineEventHandler, 18 EventTarget, 19 Event, 20 kTrustEvent 21} = require('internal/event_target'); 22const { 23 customInspectSymbol, 24 emitExperimentalWarning 25} = require('internal/util'); 26const { inspect } = require('internal/util/inspect'); 27const { 28 codes: { 29 ERR_INVALID_THIS, 30 } 31} = require('internal/errors'); 32 33const kAborted = Symbol('kAborted'); 34 35function customInspect(self, obj, depth, options) { 36 if (depth < 0) 37 return self; 38 39 const opts = ObjectAssign({}, options, { 40 depth: options.depth === null ? null : options.depth - 1 41 }); 42 43 return `${self.constructor.name} ${inspect(obj, opts)}`; 44} 45 46function validateAbortSignal(obj) { 47 if (obj?.[kAborted] === undefined) 48 throw new ERR_INVALID_THIS('AbortSignal'); 49} 50 51class AbortSignal extends EventTarget { 52 constructor() { 53 // eslint-disable-next-line no-restricted-syntax 54 throw new TypeError('Illegal constructor'); 55 } 56 57 get aborted() { 58 validateAbortSignal(this); 59 return !!this[kAborted]; 60 } 61 62 [customInspectSymbol](depth, options) { 63 return customInspect(this, { 64 aborted: this.aborted 65 }, depth, options); 66 } 67 68 static abort() { 69 return createAbortSignal(true); 70 } 71} 72 73ObjectDefineProperties(AbortSignal.prototype, { 74 aborted: { enumerable: true } 75}); 76 77ObjectDefineProperty(AbortSignal.prototype, SymbolToStringTag, { 78 writable: false, 79 enumerable: false, 80 configurable: true, 81 value: 'AbortSignal', 82}); 83 84defineEventHandler(AbortSignal.prototype, 'abort'); 85 86function createAbortSignal(aborted = false) { 87 const signal = new EventTarget(); 88 ObjectSetPrototypeOf(signal, AbortSignal.prototype); 89 signal[kAborted] = aborted; 90 return signal; 91} 92 93function abortSignal(signal) { 94 if (signal[kAborted]) return; 95 signal[kAborted] = true; 96 const event = new Event('abort', { 97 [kTrustEvent]: true 98 }); 99 signal.dispatchEvent(event); 100} 101 102// TODO(joyeecheung): V8 snapshot does not support instance member 103// initializers for now: 104// https://bugs.chromium.org/p/v8/issues/detail?id=10704 105const kSignal = Symbol('signal'); 106 107function validateAbortController(obj) { 108 if (obj?.[kSignal] === undefined) 109 throw new ERR_INVALID_THIS('AbortController'); 110} 111 112class AbortController { 113 constructor() { 114 this[kSignal] = createAbortSignal(); 115 emitExperimentalWarning('AbortController'); 116 } 117 118 get signal() { 119 validateAbortController(this); 120 return this[kSignal]; 121 } 122 123 abort() { 124 validateAbortController(this); 125 abortSignal(this[kSignal]); 126 } 127 128 [customInspectSymbol](depth, options) { 129 return customInspect(this, { 130 signal: this.signal 131 }, depth, options); 132 } 133} 134 135ObjectDefineProperties(AbortController.prototype, { 136 signal: { enumerable: true }, 137 abort: { enumerable: true } 138}); 139 140ObjectDefineProperty(AbortController.prototype, SymbolToStringTag, { 141 writable: false, 142 enumerable: false, 143 configurable: true, 144 value: 'AbortController', 145}); 146 147module.exports = { 148 AbortController, 149 AbortSignal, 150}; 151