I've developed Extension using Angular 9 which works well on Chrome but not on firefox, result in TypeError: "fetch" is read-only. I tried updating Angular 9.0.1 to 9.0.7 and also updating zone.js from 0.10.2 to 0.10.3 but no success. The stack of error as follows
Zone.__load_patch('ZoneAwarePromise', (global, Zone, api) => {
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjectDefineProperty = Object.defineProperty;
function readableObjectToString(obj) {
if (obj && obj.toString === Object.prototype.toString) {
const className = obj.constructor && obj.constructor.name;
return (className ? className : '') + ': ' + JSON.stringify(obj);
}
return obj ? obj.toString() : Object.prototype.toString.call(obj);
}
const __symbol__ = api.symbol;
const _uncaughtPromiseErrors = [];
const symbolPromise = __symbol__('Promise');
const symbolThen = __symbol__('then');
const creationTrace = '__creationTrace__';
api.onUnhandledError = (e) => {
if (api.showUncaughtError()) {
const rejection = e && e.rejection;
if (rejection) {
console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined);
}
else {
console.error(e);
}
}
};
api.microtaskDrainDone = () => {
while (_uncaughtPromiseErrors.length) {
while (_uncaughtPromiseErrors.length) {
const uncaughtPromiseError = _uncaughtPromiseErrors.shift();
try {
uncaughtPromiseError.zone.runGuarded(() => { throw uncaughtPromiseError; });
}
catch (error) {
handleUnhandledRejection(error);
}
}
}
};
const UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler');
function handleUnhandledRejection(e) {
api.onUnhandledError(e);
try {
const handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL];
if (handler && typeof handler === 'function') {
handler.call(this, e);
}
}
catch (err) {
}
}
function isThenable(value) { return value && value.then; }
function forwardResolution(value) { return value; }
function forwardRejection(rejection) { return ZoneAwarePromise.reject(rejection); }
const symbolState = __symbol__('state');
const symbolValue = __symbol__('value');
const symbolFinally = __symbol__('finally');
const symbolParentPromiseValue = __symbol__('parentPromiseValue');
const symbolParentPromiseState = __symbol__('parentPromiseState');
const source = 'Promise.then';
const UNRESOLVED = null;
const RESOLVED = true;
const REJECTED = false;
const REJECTED_NO_CATCH = 0;
function makeResolver(promise, state) {
return (v) => {
try {
resolvePromise(promise, state, v);
}
catch (err) {
resolvePromise(promise, false, err);
}
// Do not return value or you will break the Promise spec.
};
}
const once = function () {
let wasCalled = false;
return function wrapper(wrappedFunction) {
return function () {
if (wasCalled) {
return;
}
wasCalled = true;
wrappedFunction.apply(null, arguments);
};
};
};
const TYPE_ERROR = 'Promise resolved with itself';
const CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace');
// Promise Resolution
function resolvePromise(promise, state, value) {
const onceWrapper = once();
if (promise === value) {
throw new TypeError(TYPE_ERROR);
}
if (promise[symbolState] === UNRESOLVED) {
// should only get value.then once based on promise spec.
let then = null;
try {
if (typeof value === 'object' || typeof value === 'function') {
then = value && value.then;
}
}
catch (err) {
onceWrapper(() => { resolvePromise(promise, false, err); })();
return promise;
}
// if (value instanceof ZoneAwarePromise) {
if (state !== REJECTED && value instanceof ZoneAwarePromise &&
value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) &&
value[symbolState] !== UNRESOLVED) {
clearRejectedNoCatch(value);
resolvePromise(promise, value[symbolState], value[symbolValue]);
}
else if (state !== REJECTED && typeof then === 'function') {
try {
then.call(value, onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)));
}
catch (err) {
onceWrapper(() => { resolvePromise(promise, false, err); })();
}
}
else {
promise[symbolState] = state;
const queue = promise[symbolValue];
promise[symbolValue] = value;
if (promise[symbolFinally] === symbolFinally) {
// the promise is generated by Promise.prototype.finally
if (state === RESOLVED) {
// the state is resolved, should ignore the value
// and use parent promise value
promise[symbolState] = promise[symbolParentPromiseState];
promise[symbolValue] = promise[symbolParentPromiseValue];
}
}
// record task information in value when error occurs, so we can
// do some additional work such as render longStackTrace
if (state === REJECTED && value instanceof Error) {
// check if longStackTraceZone is here
const trace = Zone.currentTask && Zone.currentTask.data &&
Zone.currentTask.data[creationTrace];
if (trace) {
// only keep the long stack trace into error when in longStackTraceZone
ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { configurable: true, enumerable: false, writable: true, value: trace });
}
}
for (let i = 0; i < queue.length;) {
scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]);
}
if (queue.length == 0 && state == REJECTED) {
promise[symbolState] = REJECTED_NO_CATCH;
try {
// try to print more readable error log
throw new Error('Uncaught (in promise): ' + readableObjectToString(value) +
(value && value.stack ? '\n' + value.stack : ''));
}
catch (err) {
const error = err;
error.rejection = value;
error.promise = promise;
error.zone = Zone.current;
error.task = Zone.currentTask;
_uncaughtPromiseErrors.push(error);
api.scheduleMicroTask(); // to make sure that it is running
}
}
}
}
// Resolving an already resolved promise is a noop.
return promise;
}
const REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler');
function clearRejectedNoCatch(promise) {
if (promise[symbolState] === REJECTED_NO_CATCH) {
// if the promise is rejected no catch status
// and queue.length > 0, means there is a error handler
// here to handle the rejected promise, we should trigger
// windows.rejectionhandled eventHandler or nodejs rejectionHandled
// eventHandler
try {
const handler = Zone[REJECTION_HANDLED_HANDLER];
if (handler && typeof handler === 'function') {
handler.call(this, { rejection: promise[symbolValue], promise: promise });
}
}
catch (err) {
}
promise[symbolState] = REJECTED;
for (let i = 0; i < _uncaughtPromiseErrors.length; i++) {
if (promise === _uncaughtPromiseErrors[i].promise) {
_uncaughtPromiseErrors.splice(i, 1);
}
}
}
}
function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) {
clearRejectedNoCatch(promise);
const promiseState = promise[symbolState];
const delegate = promiseState ?
(typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
(typeof onRejected === 'function') ? onRejected : forwardRejection;
zone.scheduleMicroTask(source, () => {
try {
const parentPromiseValue = promise[symbolValue];
const isFinallyPromise = !!chainPromise && symbolFinally === chainPromise[symbolFinally];
if (isFinallyPromise) {
// if the promise is generated from finally call, keep parent promise's state and value
chainPromise[symbolParentPromiseValue] = parentPromiseValue;
chainPromise[symbolParentPromiseState] = promiseState;
}
// should not pass value to finally callback
const value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ?
[] :
[parentPromiseValue]);
resolvePromise(chainPromise, true, value);
}
catch (error) {
// if error occurs, should always return this error
resolvePromise(chainPromise, false, error);
}
}, chainPromise);
}
const ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }';
class ZoneAwarePromise {
constructor(executor) {
const promise = this;
if (!(promise instanceof ZoneAwarePromise)) {
throw new Error('Must be an instanceof Promise.');
}
promise[symbolState] = UNRESOLVED;
promise[symbolValue] = []; // queue;
try {
executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED));
}
catch (error) {
resolvePromise(promise, false, error);
}
}
static toString() { return ZONE_AWARE_PROMISE_TO_STRING; }
static resolve(value) {
return resolvePromise(new this(null), RESOLVED, value);
}
static reject(error) {
return resolvePromise(new this(null), REJECTED, error);
}
static race(values) {
let resolve;
let reject;
let promise = new this((res, rej) => {
resolve = res;
reject = rej;
});
function onResolve(value) { resolve(value); }
function onReject(error) { reject(error); }
for (let value of values) {
if (!isThenable(value)) {
value = this.resolve(value);
}
value.then(onResolve, onReject);
}
return promise;
}
static all(values) { return ZoneAwarePromise.allWithCallback(values); }
static allSettled(values) {
const P = this && this.prototype instanceof ZoneAwarePromise ? this : ZoneAwarePromise;
return P.allWithCallback(values, {
thenCallback: (value) => ({ status: 'fulfilled', value }),
errorCallback: (err) => ({ status: 'rejected', reason: err })
});
}
static allWithCallback(values, callback) {
let resolve;
let reject;
let promise = new this((res, rej) => {
resolve = res;
reject = rej;
});
// Start at 2 to prevent prematurely resolving if .then is called immediately.
let unresolvedCount = 2;
let valueIndex = 0;
const resolvedValues = [];
for (let value of values) {
if (!isThenable(value)) {
value = this.resolve(value);
}
const curValueIndex = valueIndex;
try {
value.then((value) => {
resolvedValues[curValueIndex] = callback ? callback.thenCallback(value) : value;
unresolvedCount--;
if (unresolvedCount === 0) {
resolve(resolvedValues);
}
}, (err) => {
if (!callback) {
reject(err);
}
else {
resolvedValues[curValueIndex] = callback.errorCallback(err);
unresolvedCount--;
if (unresolvedCount === 0) {
resolve(resolvedValues);
}
}
});
}
catch (thenErr) {
reject(thenErr);
}
unresolvedCount++;
valueIndex++;
}
// Make the unresolvedCount zero-based again.
unresolvedCount -= 2;
if (unresolvedCount === 0) {
resolve(resolvedValues);
}
return promise;
}
get [Symbol.toStringTag]() { return 'Promise'; }
then(onFulfilled, onRejected) {
const chainPromise = new this.constructor(null);
const zone = Zone.current;
if (this[symbolState] == UNRESOLVED) {
this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected);
}
else {
scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected);
}
return chainPromise;
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
const chainPromise = new this.constructor(null);
chainPromise[symbolFinally] = symbolFinally;
const zone = Zone.current;
if (this[symbolState] == UNRESOLVED) {
this[symbolValue].push(zone, chainPromise, onFinally, onFinally);
}
else {
scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally);
}
return chainPromise;
}
}
// Protect against aggressive optimizers dropping seemingly unused properties.
// E.g. Closure Compiler in advanced mode.
ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve;
ZoneAwarePromise['reject'] = ZoneAwarePromise.reject;
ZoneAwarePromise['race'] = ZoneAwarePromise.race;
ZoneAwarePromise['all'] = ZoneAwarePromise.all;
const NativePromise = global[symbolPromise] = global['Promise'];
const ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise');
let desc = ObjectGetOwnPropertyDescriptor(global, 'Promise');
if (!desc || desc.configurable) {
desc && delete desc.writable;
desc && delete desc.value;
if (!desc) {
desc = { configurable: true, enumerable: true };
}
desc.get = function () {
// if we already set ZoneAwarePromise, use patched one
// otherwise return native one.
return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise];
};
desc.set = function (NewNativePromise) {
if (NewNativePromise === ZoneAwarePromise) {
// if the NewNativePromise is ZoneAwarePromise
// save to global
global[ZONE_AWARE_PROMISE] = NewNativePromise;
}
else {
// if the NewNativePromise is not ZoneAwarePromise
// for example: after load zone.js, some library just
// set es6-promise to global, if we set it to global
// directly, assertZonePatched will fail and angular
// will not loaded, so we just set the NewNativePromise
// to global[symbolPromise], so the result is just like
// we load ES6 Promise before zone.js
global[symbolPromise] = NewNativePromise;
if (!NewNativePromise.prototype[symbolThen]) {
patchThen(NewNativePromise);
}
api.setNativePromise(NewNativePromise);
}
};
ObjectDefineProperty(global, 'Promise', desc);
}
global['Promise'] = ZoneAwarePromise;
const symbolThenPatched = __symbol__('thenPatched');
function patchThen(Ctor) {
const proto = Ctor.prototype;
const prop = ObjectGetOwnPropertyDescriptor(proto, 'then');
if (prop && (prop.writable === false || !prop.configurable)) {
// check Ctor.prototype.then propertyDescriptor is writable or not
// in meteor env, writable is false, we should ignore such case
return;
}
const originalThen = proto.then;
// Keep a reference to the original method.
proto[symbolThen] = originalThen;
Ctor.prototype.then = function (onResolve, onReject) {
const wrapped = new ZoneAwarePromise((resolve, reject) => { originalThen.call(this, resolve, reject); });
return wrapped.then(onResolve, onReject);
};
Ctor[symbolThenPatched] = true;
}
api.patchThen = patchThen;
function zoneify(fn) {
return function () {
let resultPromise = fn.apply(this, arguments);
if (resultPromise instanceof ZoneAwarePromise) {
return resultPromise;
}
let ctor = resultPromise.constructor;
if (!ctor[symbolThenPatched]) {
patchThen(ctor);
}
return resultPromise;
};
}
if (NativePromise) {
patchThen(NativePromise);
const fetch = global['fetch'];
if (typeof fetch == 'function') {
global[api.symbol('fetch')] = fetch;
global['fetch'] = zoneify(fetch);
}
}
// This is not part of public API, but it is useful for tests, so we expose it.
Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors;
return ZoneAwarePromise;
})
I've developed Extension using Angular 9 which works well on Chrome but not on firefox, result in TypeError: "fetch" is read-only. I tried updating Angular 9.0.1 to 9.0.7 and also updating zone.js from 0.10.2 to 0.10.3 but no success. The stack of error as follows