text: 'use strict';
  
  class CancelError extends Error {
  	constructor(reason) {
  		super(reason || 'Promise was canceled');
  		this.name = 'CancelError';
  	}
  
  	get isCanceled() {
  		return true;
  	}
  }
  
  class PCancelable {
  	static fn(userFn) {
  		return (...arguments_) => {
  			return new PCancelable((resolve, reject, onCancel) => {
  				arguments_.push(onCancel);
  				// eslint-disable-next-line promise/prefer-await-to-then
  				userFn(...arguments_).then(resolve, reject);
  			});
  		};
  	}
  
  	constructor(executor) {
  		this._cancelHandlers = [];
  		this._isPending = true;
  		this._isCanceled = false;
  		this._rejectOnCancel = true;
  
  		this._promise = new Promise((resolve, reject) => {
  			this._reject = reject;
  
  			const onResolve = value => {
  				this._isPending = false;
  				resolve(value);
  			};
  
  			const onReject = error => {
  				this._isPending = false;
  				reject(error);
  			};
  
  			const onCancel = handler => {
  				if (!this._isPending) {
  					throw new Error('The `onCancel` handler was attached after the promise settled.');
  				}
  
  				this._cancelHandlers.push(handler);
  			};
  
  			Object.defineProperties(onCancel, {
  				shouldReject: {
  					get: () => this._rejectOnCancel,
  					set: boolean => {
  						this._rejectOnCancel = boolean;
  					}
  				}
  			});
  
  			return executor(onResolve, onReject, onCancel);
  		});
  	}
  
  	then(onFulfilled, onRejected) {
  		// eslint-disable-next-line promise/prefer-await-to-then
  		return this._promise.then(onFulfilled, onRejected);
  	}
  
  	catch(onRejected) {
  		return this._promise.catch(onRejected);
  	}
  
  	finally(onFinally) {
  		return this._promise.finally(onFinally);
  	}
  
  	cancel(reason) {
  		if (!this._isPending || this._isCanceled) {
  			return;
  		}
  
  		if (this._cancelHandlers.length > 0) {
  			try {
  				for (const handler of this._cancelHandlers) {
  					handler();
  				}
  			} catch (error) {
  				this._reject(error);
  			}
  		}
  
  		this._isCanceled = true;
  		if (this._rejectOnCancel) {
  			this._reject(new CancelError(reason));
  		}
  	}
  
  	get isCanceled() {
  		return this._isCanceled;
  	}
  }
  
  Object.setPrototypeOf(PCancelable.prototype, Promise.prototype);
  
  module.exports = PCancelable;
  module.exports.CancelError = CancelError;
  