From 6f09b53880f65753d6ae5fa467f058ded100c6ea Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Tue, 1 Sep 2020 15:36:25 -0500 Subject: [PATCH 1/6] fix(errors): Custom RxJS errors now all have a call stack NOTE: Because we have to workaround compilation-to-ES5 inadequacies until call stack locations will show as being inside of the constructor implementation at the top level. This will go away as we move the community to ES2015+ BREAKING CHANGE: Tests that are written with naive expectations against errors may fail now that errors have a proper `stack` property. In some testing frameworks, a deep equality check on two error instances will check the values in `stack`, which could be different. fixes #4250 --- spec/helpers/test-helper.ts | 12 +---- spec/operators/concatWith-spec.ts | 5 ++- spec/operators/single-spec.ts | 4 +- spec/operators/takeLast-spec.ts | 4 +- spec/util/ArgumentOutOfRangeError-spec.ts | 3 ++ spec/util/EmptyError-spec.ts | 3 ++ spec/util/ObjectUnsubscribedError-spec.ts | 3 ++ spec/util/TimeoutError-spec.ts | 3 ++ spec/util/UnsubscriptionError-spec.ts | 1 + src/internal/observable/dom/AjaxObservable.ts | 45 +++++++++---------- src/internal/operators/timeout.ts | 21 +++------ src/internal/util/ArgumentOutOfRangeError.ts | 25 ++++------- src/internal/util/EmptyError.ts | 19 +++----- src/internal/util/NotFoundError.ts | 25 ++++------- src/internal/util/ObjectUnsubscribedError.ts | 25 ++++------- src/internal/util/SequenceError.ts | 25 ++++------- src/internal/util/UnsubscriptionError.ts | 30 +++++-------- src/internal/util/createErrorClass.ts | 15 +++++++ 18 files changed, 113 insertions(+), 155 deletions(-) create mode 100644 src/internal/util/createErrorClass.ts diff --git a/spec/helpers/test-helper.ts b/spec/helpers/test-helper.ts index f8598aa375..1b64ac1676 100644 --- a/spec/helpers/test-helper.ts +++ b/spec/helpers/test-helper.ts @@ -60,14 +60,4 @@ export const createObservableInputs = (value: T) => of( /** * Used to signify no subscriptions took place to `expectSubscriptions` assertions. */ -export const NO_SUBS: string[] = []; - -/** - * Does a deep equality assertion. Used to set up {@link TestScheduler}, so that - * trees of marbles can be compared. - * @param actual The value to run the expectation against. - * @param expected The value expected. - */ -export function assertDeepEquals (actual: any, expected: any) { - expect(actual).to.deep.equal(expected); -} +export const NO_SUBS: string[] = []; \ No newline at end of file diff --git a/spec/operators/concatWith-spec.ts b/spec/operators/concatWith-spec.ts index 7dcd9762c8..2cdfc14c2d 100644 --- a/spec/operators/concatWith-spec.ts +++ b/spec/operators/concatWith-spec.ts @@ -2,14 +2,15 @@ import { expect } from 'chai'; import { of, Observable } from 'rxjs'; import { concatWith, mergeMap, take } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; -import { assertDeepEquals, NO_SUBS } from '../helpers/test-helper'; +import { NO_SUBS } from '../helpers/test-helper'; +import { observableMatcher } from '../helpers/observableMatcher'; /** @test {concat} */ describe('concat operator', () => { let rxTest: TestScheduler; beforeEach(() => { - rxTest = new TestScheduler(assertDeepEquals); + rxTest = new TestScheduler(observableMatcher); }); it('should concatenate two cold observables', () => { diff --git a/spec/operators/single-spec.ts b/spec/operators/single-spec.ts index 433bdc019d..994caa74bd 100644 --- a/spec/operators/single-spec.ts +++ b/spec/operators/single-spec.ts @@ -2,14 +2,14 @@ import { expect } from 'chai'; import { single, mergeMap, tap } from 'rxjs/operators'; import { of, EmptyError, SequenceError, NotFoundError, Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { assertDeepEquals } from '../helpers/test-helper'; +import { observableMatcher } from '../helpers/observableMatcher'; /** @test {single} */ describe('single operator', () => { let rxTest: TestScheduler; beforeEach(() => { - rxTest = new TestScheduler(assertDeepEquals); + rxTest = new TestScheduler(observableMatcher); }); it('should raise error from empty predicate if observable emits multiple time', () => { diff --git a/spec/operators/takeLast-spec.ts b/spec/operators/takeLast-spec.ts index 009aa92c3e..3966096dc6 100644 --- a/spec/operators/takeLast-spec.ts +++ b/spec/operators/takeLast-spec.ts @@ -2,14 +2,14 @@ import { expect } from 'chai'; import { takeLast, mergeMap } from 'rxjs/operators'; import { range, ArgumentOutOfRangeError, of } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { assertDeepEquals } from '../helpers/test-helper'; +import { observableMatcher } from '../helpers/observableMatcher'; /** @test {takeLast} */ describe('takeLast operator', () => { let rxTest: TestScheduler; beforeEach(() => { - rxTest = new TestScheduler(assertDeepEquals); + rxTest = new TestScheduler(observableMatcher); }); it('should error for invalid arguments', () => { diff --git a/spec/util/ArgumentOutOfRangeError-spec.ts b/spec/util/ArgumentOutOfRangeError-spec.ts index fe0d6fa482..30fd780fda 100644 --- a/spec/util/ArgumentOutOfRangeError-spec.ts +++ b/spec/util/ArgumentOutOfRangeError-spec.ts @@ -10,4 +10,7 @@ describe('ArgumentOutOfRangeError', () => { it('Should have a message', () => { expect(error.message).to.be.equal('argument out of range'); }); + it('Should have a stack', () => { + expect(error.stack).to.be.a('string'); + }); }); diff --git a/spec/util/EmptyError-spec.ts b/spec/util/EmptyError-spec.ts index 669251780f..64a2253b9e 100644 --- a/spec/util/EmptyError-spec.ts +++ b/spec/util/EmptyError-spec.ts @@ -10,4 +10,7 @@ describe('EmptyError', () => { it('Should have a message', () => { expect(error.message).to.be.equal('no elements in sequence'); }); + it('Should have a stack', () => { + expect(error.stack).to.be.a('string'); + }); }); diff --git a/spec/util/ObjectUnsubscribedError-spec.ts b/spec/util/ObjectUnsubscribedError-spec.ts index d879af3f02..37c40b7ea9 100644 --- a/spec/util/ObjectUnsubscribedError-spec.ts +++ b/spec/util/ObjectUnsubscribedError-spec.ts @@ -10,4 +10,7 @@ describe('ObjectUnsubscribedError', () => { it('Should have a message', () => { expect(error.message).to.be.equal('object unsubscribed'); }); + it('Should have a stack', () => { + expect(error.stack).to.be.a('string'); + }); }); diff --git a/spec/util/TimeoutError-spec.ts b/spec/util/TimeoutError-spec.ts index a520d92c54..56f5c2996e 100644 --- a/spec/util/TimeoutError-spec.ts +++ b/spec/util/TimeoutError-spec.ts @@ -10,4 +10,7 @@ describe('TimeoutError', () => { it('Should have a message', () => { expect(error.message).to.be.equal('Timeout has occurred'); }); + it('Should have a stack', () => { + expect(error.stack).to.be.a('string'); + }); }); diff --git a/spec/util/UnsubscriptionError-spec.ts b/spec/util/UnsubscriptionError-spec.ts index 2799d680f5..fc54c83332 100644 --- a/spec/util/UnsubscriptionError-spec.ts +++ b/spec/util/UnsubscriptionError-spec.ts @@ -19,6 +19,7 @@ describe('UnsubscriptionError', () => { expect(err instanceof UnsubscriptionError).to.equal(true); expect(err.errors).to.deep.equal([err1, err2]); expect(err.name).to.equal('UnsubscriptionError'); + expect(err.stack).to.be.a('string'); } }); }); diff --git a/src/internal/observable/dom/AjaxObservable.ts b/src/internal/observable/dom/AjaxObservable.ts index 2fde701569..e65add9aaf 100644 --- a/src/internal/observable/dom/AjaxObservable.ts +++ b/src/internal/observable/dom/AjaxObservable.ts @@ -2,6 +2,7 @@ import { Observable } from '../../Observable'; import { Subscriber } from '../../Subscriber'; import { TeardownLogic, PartialObserver } from '../../types'; +import { createErrorClass } from '../../util/createErrorClass'; export interface AjaxRequest { url?: string; @@ -322,28 +323,6 @@ export interface AjaxErrorCtor { new (message: string, xhr: XMLHttpRequest, request: AjaxRequest): AjaxError; } -const AjaxErrorImpl = (() => { - function AjaxErrorImpl(this: any, message: string, xhr: XMLHttpRequest, request: AjaxRequest): AjaxError { - Error.call(this); - this.message = message; - this.name = 'AjaxError'; - this.xhr = xhr; - this.request = request; - this.status = xhr.status; - this.responseType = xhr.responseType; - let response: any; - try { - response = getXHRResponse(xhr); - } catch (err) { - response = xhr.responseText; - } - this.response = response; - return this; - } - AjaxErrorImpl.prototype = Object.create(Error.prototype); - return AjaxErrorImpl; -})(); - /** * Thrown when an error occurs during an AJAX request. * This is only exported because it is useful for checking to see if an error @@ -353,7 +332,25 @@ const AjaxErrorImpl = (() => { * @class AjaxError * @see ajax */ -export const AjaxError: AjaxErrorCtor = AjaxErrorImpl as any; +export const AjaxError: AjaxErrorCtor = createErrorClass('AjaxError', function ( + this: any, + message: string, + xhr: XMLHttpRequest, + request: AjaxRequest +) { + this.message = message; + this.xhr = xhr; + this.request = request; + this.status = xhr.status; + this.responseType = xhr.responseType; + let response: any; + try { + response = getXHRResponse(xhr); + } catch (err) { + response = xhr.responseText; + } + this.response = response; +}); function getXHRResponse(xhr: XMLHttpRequest) { switch (xhr.responseType) { @@ -391,6 +388,8 @@ export interface AjaxTimeoutErrorCtor { new (xhr: XMLHttpRequest, request: AjaxRequest): AjaxTimeoutError; } +// NOTE: We are not using createErrorClass here, because we're deriving this from +// the AjaxError we defined above. const AjaxTimeoutErrorImpl = (() => { function AjaxTimeoutErrorImpl(this: any, xhr: XMLHttpRequest, request: AjaxRequest) { AjaxError.call(this, 'ajax timeout', xhr, request); diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index fc40e971ce..757a01e4c3 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -7,6 +7,7 @@ import { Subscription } from '../Subscription'; import { lift } from '../util/lift'; import { Observable } from '../Observable'; import { from } from '../observable/from'; +import { createErrorClass } from '../util/createErrorClass'; export interface TimeoutConfig { /** @@ -72,20 +73,6 @@ export interface TimeoutErrorCtor { new (info?: TimeoutInfo): TimeoutError; } -const TimeoutErrorImpl = (() => { - function TimeoutErrorImpl(this: any, info: TimeoutInfo | null = null) { - Error.call(this); - this.message = 'Timeout has occurred'; - this.name = 'TimeoutError'; - this.info = info; - return this; - } - - TimeoutErrorImpl.prototype = Object.create(Error.prototype); - - return TimeoutErrorImpl; -})(); - /** * An error thrown by the {@link operators/timeout} operator. * @@ -98,7 +85,11 @@ const TimeoutErrorImpl = (() => { * * @class TimeoutError */ -export const TimeoutError: TimeoutErrorCtor = TimeoutErrorImpl as any; +export const TimeoutError: TimeoutErrorCtor = createErrorClass('TimeoutError', function (info: TimeoutInfo | null = null) { + this.message = 'Timeout has occurred'; + this.name = 'TimeoutError'; + (this as any).info = info; +}); /** * If `with` is provided, this will return an observable that will switch to a different observable if the source diff --git a/src/internal/util/ArgumentOutOfRangeError.ts b/src/internal/util/ArgumentOutOfRangeError.ts index 983ce709aa..6cb576f790 100644 --- a/src/internal/util/ArgumentOutOfRangeError.ts +++ b/src/internal/util/ArgumentOutOfRangeError.ts @@ -1,23 +1,12 @@ -export interface ArgumentOutOfRangeError extends Error { -} +/** @prettier */ +import { createErrorClass } from './createErrorClass'; + +export interface ArgumentOutOfRangeError extends Error {} export interface ArgumentOutOfRangeErrorCtor { - new(): ArgumentOutOfRangeError; + new (): ArgumentOutOfRangeError; } -const ArgumentOutOfRangeErrorImpl = (() => { - function ArgumentOutOfRangeErrorImpl(this: Error) { - Error.call(this); - this.message = 'argument out of range'; - this.name = 'ArgumentOutOfRangeError'; - return this; - } - - ArgumentOutOfRangeErrorImpl.prototype = Object.create(Error.prototype); - - return ArgumentOutOfRangeErrorImpl; -})(); - /** * An error thrown when an element was queried at a certain index of an * Observable, but no such index or position exists in that sequence. @@ -28,4 +17,6 @@ const ArgumentOutOfRangeErrorImpl = (() => { * * @class ArgumentOutOfRangeError */ -export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = ArgumentOutOfRangeErrorImpl as any; \ No newline at end of file +export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = createErrorClass('ArgumentOutOfRangeError', function () { + this.message = 'argument out of range'; +}); diff --git a/src/internal/util/EmptyError.ts b/src/internal/util/EmptyError.ts index f760908378..492f6e41ca 100644 --- a/src/internal/util/EmptyError.ts +++ b/src/internal/util/EmptyError.ts @@ -1,3 +1,5 @@ +import { createErrorClass } from './createErrorClass'; + export interface EmptyError extends Error { } @@ -5,19 +7,6 @@ export interface EmptyErrorCtor { new(): EmptyError; } -const EmptyErrorImpl = (() => { - function EmptyErrorImpl(this: Error) { - Error.call(this); - this.message = 'no elements in sequence'; - this.name = 'EmptyError'; - return this; - } - - EmptyErrorImpl.prototype = Object.create(Error.prototype); - - return EmptyErrorImpl; -})(); - /** * An error thrown when an Observable or a sequence was queried but has no * elements. @@ -28,4 +17,6 @@ const EmptyErrorImpl = (() => { * * @class EmptyError */ -export const EmptyError: EmptyErrorCtor = EmptyErrorImpl as any; \ No newline at end of file +export const EmptyError: EmptyErrorCtor = createErrorClass('EmptyError', function () { + this.message = 'no elements in sequence'; +}); \ No newline at end of file diff --git a/src/internal/util/NotFoundError.ts b/src/internal/util/NotFoundError.ts index f97c0ce8ed..c198aa761f 100644 --- a/src/internal/util/NotFoundError.ts +++ b/src/internal/util/NotFoundError.ts @@ -1,23 +1,12 @@ -export interface NotFoundError extends Error { -} +/** @prettier */ +import { createErrorClass } from './createErrorClass'; + +export interface NotFoundError extends Error {} export interface NotFoundErrorCtor { - new(message: string): NotFoundError; + new (message: string): NotFoundError; } -const NotFoundErrorImpl = (() => { - function NotFoundErrorImpl(this: Error, message: string) { - Error.call(this); - this.message = message; - this.name = 'NotFoundError'; - return this; - } - - NotFoundErrorImpl.prototype = Object.create(Error.prototype); - - return NotFoundErrorImpl; -})(); - /** * An error thrown when a value or values are missing from an * observable sequence. @@ -26,4 +15,6 @@ const NotFoundErrorImpl = (() => { * * @class NotFoundError */ -export const NotFoundError: NotFoundErrorCtor = NotFoundErrorImpl as any; +export const NotFoundError: NotFoundErrorCtor = createErrorClass('NotFoundError', function (message: string) { + this.message = message; +}); diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index 1d603e4cff..33432d4af8 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -1,23 +1,12 @@ -export interface ObjectUnsubscribedError extends Error { -} +/** @prettier */ +import { createErrorClass } from './createErrorClass'; + +export interface ObjectUnsubscribedError extends Error {} export interface ObjectUnsubscribedErrorCtor { - new(): ObjectUnsubscribedError; + new (): ObjectUnsubscribedError; } -const ObjectUnsubscribedErrorImpl = (() => { - function ObjectUnsubscribedErrorImpl(this: Error) { - Error.call(this); - this.message = 'object unsubscribed'; - this.name = 'ObjectUnsubscribedError'; - return this; - } - - ObjectUnsubscribedErrorImpl.prototype = Object.create(Error.prototype); - - return ObjectUnsubscribedErrorImpl; -})(); - /** * An error thrown when an action is invalid because the object has been * unsubscribed. @@ -27,4 +16,6 @@ const ObjectUnsubscribedErrorImpl = (() => { * * @class ObjectUnsubscribedError */ -export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any; \ No newline at end of file +export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass('ObjectUnsubscribedError', function () { + this.message = 'object unsubscribed'; +}); diff --git a/src/internal/util/SequenceError.ts b/src/internal/util/SequenceError.ts index 01379d7e71..6a43250aa2 100644 --- a/src/internal/util/SequenceError.ts +++ b/src/internal/util/SequenceError.ts @@ -1,23 +1,12 @@ -export interface SequenceError extends Error { -} +/** @prettier */ +import { createErrorClass } from './createErrorClass'; + +export interface SequenceError extends Error {} export interface SequenceErrorCtor { - new(message: string): SequenceError; + new (message: string): SequenceError; } -const SequenceErrorImpl = (() => { - function SequenceErrorImpl(this: Error, message: string) { - Error.call(this); - this.message = message; - this.name = 'SequenceError'; - return this; - } - - SequenceErrorImpl.prototype = Object.create(Error.prototype); - - return SequenceErrorImpl; -})(); - /** * An error thrown when something is wrong with the sequence of * values arriving on the observable. @@ -26,4 +15,6 @@ const SequenceErrorImpl = (() => { * * @class SequenceError */ -export const SequenceError: SequenceErrorCtor = SequenceErrorImpl as any; +export const SequenceError: SequenceErrorCtor = createErrorClass('SequenceError', function (message: string) { + this.message = message; +}); diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 1e1e9ed788..48b5c2b660 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -1,29 +1,23 @@ +/** @prettier */ +import { createErrorClass } from './createErrorClass'; + export interface UnsubscriptionError extends Error { readonly errors: any[]; } export interface UnsubscriptionErrorCtor { - new(errors: any[]): UnsubscriptionError; + new (errors: any[]): UnsubscriptionError; } -const UnsubscriptionErrorImpl = (() => { - function UnsubscriptionErrorImpl(this: Error, errors: (Error|string)[]) { - Error.call(this); - this.message = errors ? - `${errors.length} errors occurred during unsubscription: -${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\n ')}` : ''; - this.name = 'UnsubscriptionError'; - (this as any).errors = errors; - return this; - } - - UnsubscriptionErrorImpl.prototype = Object.create(Error.prototype); - - return UnsubscriptionErrorImpl; -})(); - /** * An error thrown when one or more errors have occurred during the * `unsubscribe` of a {@link Subscription}. */ -export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorImpl as any; +export const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass('', function (errors: (Error | string)[]) { + this.message = errors + ? `${errors.length} errors occurred during unsubscription: +${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\n ')}` + : ''; + this.name = 'UnsubscriptionError'; + (this as any).errors = errors; +}); diff --git a/src/internal/util/createErrorClass.ts b/src/internal/util/createErrorClass.ts new file mode 100644 index 0000000000..3453c002c9 --- /dev/null +++ b/src/internal/util/createErrorClass.ts @@ -0,0 +1,15 @@ +/** @prettier */ + +export function createErrorClass(name: string, setup: (this: Error, ...args: any[]) => void): T { + function ErrorImpl(this: Error, ...args: any[]) { + Error.call(this); + this.stack = new Error().stack; + this.name = name; + setup.apply(this, args); + return this; + } + + ErrorImpl.prototype = Object.create(Error.prototype); + + return ErrorImpl as any; +} From cbd869977fe673cb89f1ead3952f5cc52e54aac1 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Tue, 1 Sep 2020 16:38:56 -0500 Subject: [PATCH 2/6] refactor: Improve error class creator - Adds tests --- spec/util/createErrorClass-spec.ts | 28 ++++++++++++++ src/internal/observable/dom/AjaxObservable.ts | 38 +++++++++---------- src/internal/operators/timeout.ts | 15 +++++--- src/internal/util/ArgumentOutOfRangeError.ts | 11 ++++-- src/internal/util/EmptyError.ts | 4 +- src/internal/util/NotFoundError.ts | 11 ++++-- src/internal/util/ObjectUnsubscribedError.ts | 11 ++++-- src/internal/util/SequenceError.ts | 11 ++++-- src/internal/util/UnsubscriptionError.ts | 19 ++++++---- src/internal/util/createErrorClass.ts | 31 +++++++++------ 10 files changed, 124 insertions(+), 55 deletions(-) create mode 100644 spec/util/createErrorClass-spec.ts diff --git a/spec/util/createErrorClass-spec.ts b/spec/util/createErrorClass-spec.ts new file mode 100644 index 0000000000..fc75a1f53e --- /dev/null +++ b/spec/util/createErrorClass-spec.ts @@ -0,0 +1,28 @@ +/** @prettier */ +import { createErrorClass } from 'rxjs/internal/util/createErrorClass'; +import { expect } from 'chai'; + +describe('createErrorClass', () => { + it('should create a class that subclasses error and has the right properties', () => { + const MySpecialError: any = createErrorClass( + (_super) => + function MySpecialError(this: any, arg1: number, arg2: string) { + _super(this); + this.message = 'Super special error!'; + this.arg1 = arg1; + this.arg2 = arg2; + } + ); + + expect(MySpecialError).to.be.a('function'); + const err = new MySpecialError(123, 'Test'); + expect(err).to.be.an.instanceOf(Error); + expect(err).to.be.an.instanceOf(MySpecialError); + expect(err.name).to.equal('MySpecialError'); + expect(err.constructor).to.equal(MySpecialError); + expect(err.stack).to.be.a('string'); + expect(err.message).to.equal('Super special error!'); + expect(err.arg1).to.equal(123); + expect(err.arg2).to.equal('Test'); + }); +}); diff --git a/src/internal/observable/dom/AjaxObservable.ts b/src/internal/observable/dom/AjaxObservable.ts index e65add9aaf..01d8fe966c 100644 --- a/src/internal/observable/dom/AjaxObservable.ts +++ b/src/internal/observable/dom/AjaxObservable.ts @@ -332,25 +332,25 @@ export interface AjaxErrorCtor { * @class AjaxError * @see ajax */ -export const AjaxError: AjaxErrorCtor = createErrorClass('AjaxError', function ( - this: any, - message: string, - xhr: XMLHttpRequest, - request: AjaxRequest -) { - this.message = message; - this.xhr = xhr; - this.request = request; - this.status = xhr.status; - this.responseType = xhr.responseType; - let response: any; - try { - response = getXHRResponse(xhr); - } catch (err) { - response = xhr.responseText; - } - this.response = response; -}); +export const AjaxError: AjaxErrorCtor = createErrorClass( + (_super) => + function AjaxError(this: any, message: string, xhr: XMLHttpRequest, request: AjaxRequest) { + _super(this); + this.message = message; + this.xhr = xhr; + this.request = request; + this.status = xhr.status; + this.responseType = xhr.responseType; + let response: any; + try { + response = getXHRResponse(xhr); + } catch (err) { + response = xhr.responseText; + } + this.response = response; + return this; + } +); function getXHRResponse(xhr: XMLHttpRequest) { switch (xhr.responseType) { diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index 757a01e4c3..7d3b0b429a 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -85,11 +85,16 @@ export interface TimeoutErrorCtor { * * @class TimeoutError */ -export const TimeoutError: TimeoutErrorCtor = createErrorClass('TimeoutError', function (info: TimeoutInfo | null = null) { - this.message = 'Timeout has occurred'; - this.name = 'TimeoutError'; - (this as any).info = info; -}); +export const TimeoutError: TimeoutErrorCtor = createErrorClass( + (_super) => + function TimeoutError(this: any, info: TimeoutInfo | null = null) { + _super(this); + this.message = 'Timeout has occurred'; + this.name = 'TimeoutError'; + this.info = info; + return this; + } +); /** * If `with` is provided, this will return an observable that will switch to a different observable if the source diff --git a/src/internal/util/ArgumentOutOfRangeError.ts b/src/internal/util/ArgumentOutOfRangeError.ts index 6cb576f790..55927c58c3 100644 --- a/src/internal/util/ArgumentOutOfRangeError.ts +++ b/src/internal/util/ArgumentOutOfRangeError.ts @@ -17,6 +17,11 @@ export interface ArgumentOutOfRangeErrorCtor { * * @class ArgumentOutOfRangeError */ -export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = createErrorClass('ArgumentOutOfRangeError', function () { - this.message = 'argument out of range'; -}); +export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = createErrorClass( + (_super) => + function ArgumentOutOfRangeError(this: any) { + _super(this); + this.message = 'argument out of range'; + return this; + } +); diff --git a/src/internal/util/EmptyError.ts b/src/internal/util/EmptyError.ts index 492f6e41ca..0f37a4a900 100644 --- a/src/internal/util/EmptyError.ts +++ b/src/internal/util/EmptyError.ts @@ -17,6 +17,8 @@ export interface EmptyErrorCtor { * * @class EmptyError */ -export const EmptyError: EmptyErrorCtor = createErrorClass('EmptyError', function () { +export const EmptyError: EmptyErrorCtor = createErrorClass((_super) => function EmptyError(this: any) { + _super(this); this.message = 'no elements in sequence'; + return this; }); \ No newline at end of file diff --git a/src/internal/util/NotFoundError.ts b/src/internal/util/NotFoundError.ts index c198aa761f..6be839c3eb 100644 --- a/src/internal/util/NotFoundError.ts +++ b/src/internal/util/NotFoundError.ts @@ -15,6 +15,11 @@ export interface NotFoundErrorCtor { * * @class NotFoundError */ -export const NotFoundError: NotFoundErrorCtor = createErrorClass('NotFoundError', function (message: string) { - this.message = message; -}); +export const NotFoundError: NotFoundErrorCtor = createErrorClass( + (_super) => + function NotFoundError(this: any, message: string) { + _super(this); + this.message = message; + return this; + } +); diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index 33432d4af8..4b63dcda97 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -16,6 +16,11 @@ export interface ObjectUnsubscribedErrorCtor { * * @class ObjectUnsubscribedError */ -export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass('ObjectUnsubscribedError', function () { - this.message = 'object unsubscribed'; -}); +export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass( + (_super) => + function ObjectUnsubscribedError(this: any) { + _super(this); + this.message = 'object unsubscribed'; + return this; + } +); diff --git a/src/internal/util/SequenceError.ts b/src/internal/util/SequenceError.ts index 6a43250aa2..671c05786c 100644 --- a/src/internal/util/SequenceError.ts +++ b/src/internal/util/SequenceError.ts @@ -15,6 +15,11 @@ export interface SequenceErrorCtor { * * @class SequenceError */ -export const SequenceError: SequenceErrorCtor = createErrorClass('SequenceError', function (message: string) { - this.message = message; -}); +export const SequenceError: SequenceErrorCtor = createErrorClass( + (_super) => + function SequenceError(this: any, message: string) { + _super(this); + this.message = message; + return this; + } +); diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 48b5c2b660..455bf73813 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -13,11 +13,16 @@ export interface UnsubscriptionErrorCtor { * An error thrown when one or more errors have occurred during the * `unsubscribe` of a {@link Subscription}. */ -export const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass('', function (errors: (Error | string)[]) { - this.message = errors - ? `${errors.length} errors occurred during unsubscription: +export const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass( + (_super) => + function UnsubscriptionError(this: any, errors: (Error | string)[]) { + _super(this); + this.message = errors + ? `${errors.length} errors occurred during unsubscription: ${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\n ')}` - : ''; - this.name = 'UnsubscriptionError'; - (this as any).errors = errors; -}); + : ''; + this.name = 'UnsubscriptionError'; + (this as any).errors = errors; + return this; + } +); diff --git a/src/internal/util/createErrorClass.ts b/src/internal/util/createErrorClass.ts index 3453c002c9..9dc2841121 100644 --- a/src/internal/util/createErrorClass.ts +++ b/src/internal/util/createErrorClass.ts @@ -1,15 +1,24 @@ /** @prettier */ -export function createErrorClass(name: string, setup: (this: Error, ...args: any[]) => void): T { - function ErrorImpl(this: Error, ...args: any[]) { - Error.call(this); - this.stack = new Error().stack; - this.name = name; - setup.apply(this, args); - return this; - } +/** + * Used to create Error subclasses until the community moves away from ES5. + * + * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors + * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123 + * + * @param createImpl A factory function to create the actual constructor implementation. The returned + * function should be a named function that calls `_super` internally. The name of the function + * will be the name of the error. + */ +export function createErrorClass(createImpl: (_super: any) => any): T { + const _super = (instance: any) => { + Error.call(instance); + instance.name = instance.constructor.name; + instance.stack = new Error().stack; + }; - ErrorImpl.prototype = Object.create(Error.prototype); - - return ErrorImpl as any; + const ctorFunc = createImpl(_super); + ctorFunc.prototype = Object.create(Error.prototype); + ctorFunc.prototype.constructor = ctorFunc; + return ctorFunc; } From a84affe9ff366e786d4ff12783ed63e2bba92a03 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Tue, 1 Sep 2020 16:49:02 -0500 Subject: [PATCH 3/6] chore: update side-effects golden files --- .../side-effects/snapshots/esm5/ajax.js | 671 +++++++++++++++++- 1 file changed, 670 insertions(+), 1 deletion(-) diff --git a/integration/side-effects/snapshots/esm5/ajax.js b/integration/side-effects/snapshots/esm5/ajax.js index 5d63e0559f..65387f8ecd 100644 --- a/integration/side-effects/snapshots/esm5/ajax.js +++ b/integration/side-effects/snapshots/esm5/ajax.js @@ -1 +1,670 @@ -import "tslib"; +import { __spreadArrays, __extends } from "tslib"; + +function isFunction(x) { + return "function" === typeof x; +} + +var _enable_super_gross_mode_that_will_cause_bad_things = false; + +var _enable_deoptimized_subscriber_creation = false; + +var config = { + quietBadConfig: false, + Promise: void 0, + set useDeprecatedSynchronousErrorHandling(value) { + if (!this.quietBadConfig) if (value) { + var error = new Error(); + console.warn("DEPRECATED! RxJS was set to use deprecated synchronous error handling behavior by code at: \n" + error.stack); + } + _enable_super_gross_mode_that_will_cause_bad_things = value; + }, + get useDeprecatedSynchronousErrorHandling() { + return _enable_super_gross_mode_that_will_cause_bad_things; + }, + set useDeprecatedNextContext(value) { + if (!this.quietBadConfig) if (value) { + var error = new Error(); + console.warn("DEPRECATED! RxJS was set to use deprecated next context. This will result in deoptimizations when creating any new subscription. \n" + error.stack); + } + _enable_deoptimized_subscriber_creation = value; + }, + get useDeprecatedNextContext() { + return _enable_deoptimized_subscriber_creation; + } +}; + +function hostReportError(err) { + setTimeout(function() { + throw err; + }, 0); +} + +var empty = { + closed: true, + next: function(value) {}, + error: function(err) { + if (config.useDeprecatedSynchronousErrorHandling) throw err; else hostReportError(err); + }, + complete: function() {} +}; + +function createErrorClass(createImpl) { + var _super = function(instance) { + Error.call(instance); + instance.name = instance.constructor.name; + instance.stack = new Error().stack; + }; + var ctorFunc = createImpl(_super); + ctorFunc.prototype = Object.create(Error.prototype); + ctorFunc.prototype.constructor = ctorFunc; + return ctorFunc; +} + +var UnsubscriptionError = createErrorClass(function(_super) { + return function UnsubscriptionError(errors) { + _super(this); + this.message = errors ? errors.length + " errors occurred during unsubscription:\n" + errors.map(function(err, i) { + return i + 1 + ") " + err.toString(); + }).join("\n ") : ""; + this.name = "UnsubscriptionError"; + this.errors = errors; + return this; + }; +}); + +var Subscription = function() { + function Subscription(initialTeardown) { + this.initialTeardown = initialTeardown; + this.closed = false; + this._singleParent = null; + this._parents = null; + this._teardowns = null; + } + Subscription.prototype.unsubscribe = function() { + var errors; + if (!this.closed) { + this.closed = true; + var _singleParent = this._singleParent; + var _parents = void 0; + if (_singleParent) { + this._singleParent = null; + _singleParent.remove(this); + } else if (_parents = this._parents) { + this._parents = null; + for (var _i = 0, _parents_1 = _parents; _i < _parents_1.length; _i++) { + var parent_1 = _parents_1[_i]; + parent_1.remove(this); + } + } + var initialTeardown = this.initialTeardown; + if (isFunction(initialTeardown)) try { + initialTeardown(); + } catch (e) { + errors = e instanceof UnsubscriptionError ? e.errors : [ e ]; + } + var _teardowns = this._teardowns; + this._teardowns = null; + if (_teardowns) for (var _a = 0, _teardowns_1 = _teardowns; _a < _teardowns_1.length; _a++) { + var teardown_1 = _teardowns_1[_a]; + try { + if ("function" === typeof teardown_1) teardown_1(); else teardown_1.unsubscribe(); + } catch (err) { + errors = null !== errors && void 0 !== errors ? errors : []; + if (err instanceof UnsubscriptionError) errors = __spreadArrays(errors, err.errors); else errors.push(err); + } + } + if (errors) throw new UnsubscriptionError(errors); + } + }; + Subscription.prototype.add = function(teardown) { + var _a; + if (teardown && teardown !== this) if (this.closed) if ("function" === typeof teardown) teardown(); else teardown.unsubscribe(); else { + if (teardown instanceof Subscription) { + if (teardown.closed || teardown._hasParent(this)) return; + teardown._addParent(this); + } + this._teardowns = null !== (_a = this._teardowns) && void 0 !== _a ? _a : []; + this._teardowns.push(teardown); + } + }; + Subscription.prototype._hasParent = function(parent) { + var _a; + return this._singleParent === parent || (null === (_a = this._parents) || void 0 === _a ? void 0 : _a.includes(parent)) || false; + }; + Subscription.prototype._addParent = function(parent) { + var _singleParent = this._singleParent; + var _parents; + if (_singleParent) { + this._parents = [ _singleParent, parent ]; + this._singleParent = null; + } else if (_parents = this._parents) _parents.push(parent); else this._singleParent = parent; + }; + Subscription.prototype._removeParent = function(parent) { + var _singleParent = this._singleParent; + var _parents; + if (_singleParent) { + if (_singleParent === parent) this._singleParent = null; + } else if (_parents = this._parents) { + var index = _parents.indexOf(parent); + if (index >= 0) _parents.splice(index, 1); + } + }; + Subscription.prototype.remove = function(teardown) { + var _teardowns = this._teardowns; + if (_teardowns) { + var index = _teardowns.indexOf(teardown); + if (index >= 0) _teardowns.splice(index, 1); + } + if (teardown instanceof Subscription) teardown._removeParent(this); + }; + Subscription.EMPTY = function(empty) { + empty.closed = true; + return empty; + }(new Subscription()); + return Subscription; +}(); + +function isSubscription(value) { + return value instanceof Subscription || value && "closed" in value && "function" === typeof value.remove && "function" === typeof value.add && "function" === typeof value.unsubscribe; +} + +var Subscriber = function(_super) { + __extends(Subscriber, _super); + function Subscriber(destinationOrNext, error, complete) { + var _this = _super.call(this) || this; + _this.syncErrorValue = null; + _this.syncErrorThrown = false; + _this.syncErrorThrowable = false; + _this.isStopped = false; + switch (arguments.length) { + case 0: + _this.destination = empty; + break; + + case 1: + if (!destinationOrNext) { + _this.destination = empty; + break; + } + if ("object" === typeof destinationOrNext) { + if (destinationOrNext instanceof Subscriber) { + _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; + _this.destination = destinationOrNext; + destinationOrNext.add(_this); + } else { + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext); + } + break; + } + + default: + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); + break; + } + return _this; + } + Subscriber.create = function(next, error, complete) { + var subscriber = new Subscriber(next, error, complete); + subscriber.syncErrorThrowable = false; + return subscriber; + }; + Subscriber.prototype.next = function(value) { + if (!this.isStopped) this._next(value); + }; + Subscriber.prototype.error = function(err) { + if (!this.isStopped) { + this.isStopped = true; + this._error(err); + } + }; + Subscriber.prototype.complete = function() { + if (!this.isStopped) { + this.isStopped = true; + this._complete(); + } + }; + Subscriber.prototype.unsubscribe = function() { + if (!this.closed) { + this.isStopped = true; + _super.prototype.unsubscribe.call(this); + } + }; + Subscriber.prototype._next = function(value) { + this.destination.next(value); + }; + Subscriber.prototype._error = function(err) { + this.destination.error(err); + this.unsubscribe(); + }; + Subscriber.prototype._complete = function() { + this.destination.complete(); + this.unsubscribe(); + }; + return Subscriber; +}(Subscription); + +var SafeSubscriber = function(_super) { + __extends(SafeSubscriber, _super); + function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { + var _this = _super.call(this) || this; + _this._parentSubscriber = _parentSubscriber; + var next; + if (isFunction(observerOrNext)) next = observerOrNext; else if (observerOrNext) { + next = observerOrNext.next; + error = observerOrNext.error; + complete = observerOrNext.complete; + if (observerOrNext !== empty) { + var context_1; + if (config.useDeprecatedNextContext) { + context_1 = Object.create(observerOrNext); + context_1.unsubscribe = _this.unsubscribe.bind(_this); + } else context_1 = observerOrNext; + next = next && next.bind(context_1); + error = error && error.bind(context_1); + complete = complete && complete.bind(context_1); + if (isSubscription(observerOrNext)) observerOrNext.add(_this.unsubscribe.bind(_this)); + } + } + _this._next = next; + _this._error = error; + _this._complete = complete; + return _this; + } + SafeSubscriber.prototype.next = function(value) { + if (!this.isStopped && this._next) try { + this._next(value); + } catch (err) { + this._throw(err); + } + }; + SafeSubscriber.prototype.error = function(err) { + if (!this.isStopped) if (this._error) { + try { + this._error(err); + } catch (err) { + this._throw(err); + return; + } + this.unsubscribe(); + } else this._throw(err); + }; + SafeSubscriber.prototype._throw = function(err) { + this.unsubscribe(); + if (config.useDeprecatedSynchronousErrorHandling) { + var _parentSubscriber = this._parentSubscriber; + if (null === _parentSubscriber || void 0 === _parentSubscriber ? void 0 : _parentSubscriber.syncErrorThrowable) { + _parentSubscriber.syncErrorValue = err; + _parentSubscriber.syncErrorThrown = true; + } else throw err; + } else hostReportError(err); + }; + SafeSubscriber.prototype.complete = function() { + if (!this.isStopped) { + if (this._complete) try { + this._complete(); + } catch (err) { + this._throw(err); + return; + } + this.unsubscribe(); + } + }; + SafeSubscriber.prototype.unsubscribe = function() { + if (!this.closed) { + var _parentSubscriber = this._parentSubscriber; + this._parentSubscriber = null; + _parentSubscriber.unsubscribe(); + _super.prototype.unsubscribe.call(this); + } + }; + return SafeSubscriber; +}(Subscriber); + +function toSubscriber(nextOrObserver, error, complete) { + if (nextOrObserver) { + if (isSubscriber(nextOrObserver)) return nextOrObserver; + if (isObserver(nextOrObserver)) return new FullObserverSubscriber(nextOrObserver); + } + if (!nextOrObserver && !error && !complete) return new Subscriber(empty); + return new Subscriber(nextOrObserver, error, complete); +} + +function isObserver(value) { + return value && "function" === typeof value.next && "function" === typeof value.error && "function" === typeof value.complete; +} + +function isSubscriber(value) { + return value instanceof Subscriber || isObserver(value) && isSubscription(value); +} + +var FullObserverSubscriber = function(_super) { + __extends(FullObserverSubscriber, _super); + function FullObserverSubscriber(destination) { + var _this = _super.call(this) || this; + _this.destination = destination; + return _this; + } + return FullObserverSubscriber; +}(Subscriber); + +var observable = function() { + return "function" === typeof Symbol && Symbol.observable || "@@observable"; +}(); + +function identity(x) { + return x; +} + +function pipeFromArray(fns) { + if (0 === fns.length) return identity; + if (1 === fns.length) return fns[0]; + return function piped(input) { + return fns.reduce(function(prev, fn) { + return fn(prev); + }, input); + }; +} + +var Observable = function() { + function Observable(subscribe) { + if (subscribe) this._subscribe = subscribe; + } + Observable.prototype.lift = function(operator) { + var observable = new Observable(); + observable.source = this; + observable.operator = operator; + return observable; + }; + Observable.prototype.subscribe = function(observerOrNext, error, complete) { + var operator = this.operator; + var sink = toSubscriber(observerOrNext, error, complete); + if (operator) sink.add(operator.call(sink, this.source)); else sink.add(this.source || config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable ? this._subscribe(sink) : this._trySubscribe(sink)); + if (config.useDeprecatedSynchronousErrorHandling) if (sink.syncErrorThrowable) { + sink.syncErrorThrowable = false; + if (sink.syncErrorThrown) throw sink.syncErrorValue; + } + return sink; + }; + Observable.prototype._trySubscribe = function(sink) { + try { + return this._subscribe(sink); + } catch (err) { + if (config.useDeprecatedSynchronousErrorHandling) { + sink.syncErrorThrown = true; + sink.syncErrorValue = err; + } else if (canReportError(sink)) sink.error(err); else console.warn(err); + } + }; + Observable.prototype.forEach = function(next, promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function(resolve, reject) { + var subscription; + subscription = _this.subscribe(function(value) { + try { + next(value); + } catch (err) { + reject(err); + if (subscription) subscription.unsubscribe(); + } + }, reject, resolve); + }); + }; + Observable.prototype._subscribe = function(subscriber) { + var source = this.source; + return source && source.subscribe(subscriber); + }; + Observable.prototype[observable] = function() { + return this; + }; + Observable.prototype.pipe = function() { + var operations = []; + for (var _i = 0; _i < arguments.length; _i++) operations[_i] = arguments[_i]; + if (0 === operations.length) return this; + return pipeFromArray(operations)(this); + }; + Observable.prototype.toPromise = function(promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function(resolve, reject) { + var value; + _this.subscribe(function(x) { + return value = x; + }, function(err) { + return reject(err); + }, function() { + return resolve(value); + }); + }); + }; + Observable.create = function(subscribe) { + return new Observable(subscribe); + }; + return Observable; +}(); + +function getPromiseCtor(promiseCtor) { + if (!promiseCtor) promiseCtor = Promise; + if (!promiseCtor) throw new Error("no Promise impl found"); + return promiseCtor; +} + +function canReportError(subscriber) { + while (subscriber) { + var _a = subscriber, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped; + if (closed_1 || isStopped) return false; else if (destination && destination instanceof Subscriber) subscriber = destination; else subscriber = null; + } + return true; +} + +function isFormData(body) { + return "undefined" !== typeof FormData && body instanceof FormData; +} + +var AjaxObservable = function(_super_1) { + __extends(AjaxObservable, _super_1); + function AjaxObservable(urlOrRequest) { + var _this = _super_1.call(this) || this; + var request = { + async: true, + createXHR: function() { + return new XMLHttpRequest(); + }, + crossDomain: true, + withCredentials: false, + headers: {}, + method: "GET", + responseType: "json", + timeout: 0 + }; + if ("string" === typeof urlOrRequest) request.url = urlOrRequest; else for (var prop in urlOrRequest) if (urlOrRequest.hasOwnProperty(prop)) request[prop] = urlOrRequest[prop]; + _this.request = request; + return _this; + } + AjaxObservable.prototype._subscribe = function(subscriber) { + return new AjaxSubscriber(subscriber, this.request); + }; + return AjaxObservable; +}(Observable); + +var AjaxSubscriber = function(_super_1) { + __extends(AjaxSubscriber, _super_1); + function AjaxSubscriber(destination, request) { + var _this = _super_1.call(this, destination) || this; + _this.request = request; + _this.done = false; + var headers = request.headers = request.headers || {}; + if (!request.crossDomain && !_this.getHeader(headers, "X-Requested-With")) headers["X-Requested-With"] = "XMLHttpRequest"; + var contentTypeHeader = _this.getHeader(headers, "Content-Type"); + if (!contentTypeHeader && "undefined" !== typeof request.body && !isFormData(request.body)) headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"; + request.body = _this.serializeBody(request.body, _this.getHeader(request.headers, "Content-Type")); + _this.send(); + return _this; + } + AjaxSubscriber.prototype.next = function(e) { + this.done = true; + var destination = this.destination; + var result; + try { + result = new AjaxResponse(e, this.xhr, this.request); + } catch (err) { + return destination.error(err); + } + destination.next(result); + }; + AjaxSubscriber.prototype.send = function() { + var _a = this, request = _a.request, _b = _a.request, user = _b.user, method = _b.method, url = _b.url, async = _b.async, password = _b.password, headers = _b.headers, body = _b.body; + try { + var xhr = this.xhr = request.createXHR(); + this.setupEvents(xhr, request); + if (user) xhr.open(method, url, async, user, password); else xhr.open(method, url, async); + if (async) { + xhr.timeout = request.timeout; + xhr.responseType = request.responseType; + } + if ("withCredentials" in xhr) xhr.withCredentials = !!request.withCredentials; + this.setHeaders(xhr, headers); + if (body) xhr.send(body); else xhr.send(); + } catch (err) { + this.error(err); + } + }; + AjaxSubscriber.prototype.serializeBody = function(body, contentType) { + if (!body || "string" === typeof body) return body; else if (isFormData(body)) return body; + if (contentType) { + var splitIndex = contentType.indexOf(";"); + if (-1 !== splitIndex) contentType = contentType.substring(0, splitIndex); + } + switch (contentType) { + case "application/x-www-form-urlencoded": + return Object.keys(body).map(function(key) { + return encodeURIComponent(key) + "=" + encodeURIComponent(body[key]); + }).join("&"); + + case "application/json": + return JSON.stringify(body); + + default: + return body; + } + }; + AjaxSubscriber.prototype.setHeaders = function(xhr, headers) { + for (var key in headers) if (headers.hasOwnProperty(key)) xhr.setRequestHeader(key, headers[key]); + }; + AjaxSubscriber.prototype.getHeader = function(headers, headerName) { + for (var key in headers) if (key.toLowerCase() === headerName.toLowerCase()) return headers[key]; + return; + }; + AjaxSubscriber.prototype.setupEvents = function(xhr, request) { + var _this = this; + var progressSubscriber = request.progressSubscriber; + xhr.ontimeout = function(e) { + var _a; + null === (_a = null === progressSubscriber || void 0 === progressSubscriber ? void 0 : progressSubscriber.error) || void 0 === _a ? void 0 : _a.call(progressSubscriber, e); + var error; + try { + error = new AjaxTimeoutError(xhr, request); + } catch (err) { + error = err; + } + _this.error(error); + }; + if (progressSubscriber) xhr.upload.onprogress = function(e) { + var _a; + null === (_a = progressSubscriber.next) || void 0 === _a ? void 0 : _a.call(progressSubscriber, e); + }; + xhr.onerror = function(e) { + var _a; + null === (_a = null === progressSubscriber || void 0 === progressSubscriber ? void 0 : progressSubscriber.error) || void 0 === _a ? void 0 : _a.call(progressSubscriber, e); + _this.error(new AjaxError("ajax error", xhr, request)); + }; + xhr.onload = function(e) { + var _a, _b; + if (xhr.status < 400) { + null === (_a = null === progressSubscriber || void 0 === progressSubscriber ? void 0 : progressSubscriber.complete) || void 0 === _a ? void 0 : _a.call(progressSubscriber); + _this.next(e); + _this.complete(); + } else { + null === (_b = null === progressSubscriber || void 0 === progressSubscriber ? void 0 : progressSubscriber.error) || void 0 === _b ? void 0 : _b.call(progressSubscriber, e); + var error = void 0; + try { + error = new AjaxError("ajax error " + xhr.status, xhr, request); + } catch (err) { + error = err; + } + _this.error(error); + } + }; + }; + AjaxSubscriber.prototype.unsubscribe = function() { + var _a = this, done = _a.done, xhr = _a.xhr; + if (!done && xhr && 4 !== xhr.readyState && "function" === typeof xhr.abort) xhr.abort(); + _super_1.prototype.unsubscribe.call(this); + }; + return AjaxSubscriber; +}(Subscriber); + +var AjaxResponse = function() { + function AjaxResponse(originalEvent, xhr, request) { + this.originalEvent = originalEvent; + this.xhr = xhr; + this.request = request; + this.status = xhr.status; + this.responseType = xhr.responseType || request.responseType; + this.response = getXHRResponse(xhr); + } + return AjaxResponse; +}(); + +var AjaxError = createErrorClass(function(_super) { + return function AjaxError(message, xhr, request) { + _super(this); + this.message = message; + this.xhr = xhr; + this.request = request; + this.status = xhr.status; + this.responseType = xhr.responseType; + var response; + try { + response = getXHRResponse(xhr); + } catch (err) { + response = xhr.responseText; + } + this.response = response; + return this; + }; +}); + +function getXHRResponse(xhr) { + switch (xhr.responseType) { + case "json": + if ("response" in xhr) return xhr.response; else { + var ieXHR = xhr; + return JSON.parse(ieXHR.responseText); + } + + case "document": + return xhr.responseXML; + + case "text": + default: + if ("response" in xhr) return xhr.response; else { + var ieXHR = xhr; + return ieXHR.responseText; + } + } +} + +var AjaxTimeoutErrorImpl = function() { + function AjaxTimeoutErrorImpl(xhr, request) { + AjaxError.call(this, "ajax timeout", xhr, request); + this.name = "AjaxTimeoutError"; + return this; + } + AjaxTimeoutErrorImpl.prototype = Object.create(AjaxError.prototype); + return AjaxTimeoutErrorImpl; +}(); + +var AjaxTimeoutError = AjaxTimeoutErrorImpl; From c85734bfed4718c1957385118ca35868c446998d Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Tue, 1 Sep 2020 21:18:39 -0500 Subject: [PATCH 4/6] chore: address comments --- src/internal/observable/dom/AjaxObservable.ts | 1 - src/internal/operators/timeout.ts | 1 - src/internal/util/ArgumentOutOfRangeError.ts | 1 - src/internal/util/EmptyError.ts | 1 - src/internal/util/NotFoundError.ts | 1 - src/internal/util/ObjectUnsubscribedError.ts | 1 - src/internal/util/SequenceError.ts | 1 - src/internal/util/UnsubscriptionError.ts | 3 +-- 8 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/internal/observable/dom/AjaxObservable.ts b/src/internal/observable/dom/AjaxObservable.ts index 01d8fe966c..bbd550b773 100644 --- a/src/internal/observable/dom/AjaxObservable.ts +++ b/src/internal/observable/dom/AjaxObservable.ts @@ -348,7 +348,6 @@ export const AjaxError: AjaxErrorCtor = createErrorClass( response = xhr.responseText; } this.response = response; - return this; } ); diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index 7d3b0b429a..9563345d2c 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -92,7 +92,6 @@ export const TimeoutError: TimeoutErrorCtor = createErrorClass( this.message = 'Timeout has occurred'; this.name = 'TimeoutError'; this.info = info; - return this; } ); diff --git a/src/internal/util/ArgumentOutOfRangeError.ts b/src/internal/util/ArgumentOutOfRangeError.ts index 55927c58c3..3356de654b 100644 --- a/src/internal/util/ArgumentOutOfRangeError.ts +++ b/src/internal/util/ArgumentOutOfRangeError.ts @@ -22,6 +22,5 @@ export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = createErrorC function ArgumentOutOfRangeError(this: any) { _super(this); this.message = 'argument out of range'; - return this; } ); diff --git a/src/internal/util/EmptyError.ts b/src/internal/util/EmptyError.ts index 0f37a4a900..7a6e9aa0d4 100644 --- a/src/internal/util/EmptyError.ts +++ b/src/internal/util/EmptyError.ts @@ -20,5 +20,4 @@ export interface EmptyErrorCtor { export const EmptyError: EmptyErrorCtor = createErrorClass((_super) => function EmptyError(this: any) { _super(this); this.message = 'no elements in sequence'; - return this; }); \ No newline at end of file diff --git a/src/internal/util/NotFoundError.ts b/src/internal/util/NotFoundError.ts index 6be839c3eb..c1a575a6f7 100644 --- a/src/internal/util/NotFoundError.ts +++ b/src/internal/util/NotFoundError.ts @@ -20,6 +20,5 @@ export const NotFoundError: NotFoundErrorCtor = createErrorClass( function NotFoundError(this: any, message: string) { _super(this); this.message = message; - return this; } ); diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index 4b63dcda97..36b990ed2c 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -21,6 +21,5 @@ export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorC function ObjectUnsubscribedError(this: any) { _super(this); this.message = 'object unsubscribed'; - return this; } ); diff --git a/src/internal/util/SequenceError.ts b/src/internal/util/SequenceError.ts index 671c05786c..54bcba0bd7 100644 --- a/src/internal/util/SequenceError.ts +++ b/src/internal/util/SequenceError.ts @@ -20,6 +20,5 @@ export const SequenceError: SequenceErrorCtor = createErrorClass( function SequenceError(this: any, message: string) { _super(this); this.message = message; - return this; } ); diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 455bf73813..68eca5832e 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -22,7 +22,6 @@ export const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass( ${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\n ')}` : ''; this.name = 'UnsubscriptionError'; - (this as any).errors = errors; - return this; + this.errors = errors; } ); From ac7aa52ec7eff0cd3b8bae8e80a85a4e20c97eb0 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Wed, 2 Sep 2020 10:13:33 -0500 Subject: [PATCH 5/6] chore: update side-effects golden files again --- integration/side-effects/snapshots/esm5/ajax.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration/side-effects/snapshots/esm5/ajax.js b/integration/side-effects/snapshots/esm5/ajax.js index 65387f8ecd..afc61c9940 100644 --- a/integration/side-effects/snapshots/esm5/ajax.js +++ b/integration/side-effects/snapshots/esm5/ajax.js @@ -68,7 +68,6 @@ var UnsubscriptionError = createErrorClass(function(_super) { }).join("\n ") : ""; this.name = "UnsubscriptionError"; this.errors = errors; - return this; }; }); @@ -633,7 +632,6 @@ var AjaxError = createErrorClass(function(_super) { response = xhr.responseText; } this.response = response; - return this; }; }); From daf8bb8758bb75993709d2db8428f1bda8ddbec6 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 3 Sep 2020 08:05:57 -0500 Subject: [PATCH 6/6] chore: I hate ts-api-guardian sometimes --- api_guard/dist/types/index.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api_guard/dist/types/index.d.ts b/api_guard/dist/types/index.d.ts index e649c59f1a..11bf8a6625 100644 --- a/api_guard/dist/types/index.d.ts +++ b/api_guard/dist/types/index.d.ts @@ -130,6 +130,7 @@ export declare function combineLatest, O2 extend export declare function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(sources: [O1, O2, O3, O4, O5]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; export declare function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(sources: [O1, O2, O3, O4, O5, O6]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; export declare function combineLatest>(sources: O[]): Observable[]>; +export declare function combineLatest[]>(sources: O): Observable>; export declare function combineLatest>(v1: O1, scheduler?: SchedulerLike): Observable<[ObservedValueOf]>; export declare function combineLatest, O2 extends ObservableInput>(v1: O1, v2: O2, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf]>; export declare function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput>(v1: O1, v2: O2, v3: O3, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf]>; @@ -405,7 +406,7 @@ export declare type ObservedValueOf = O extends ObservableInput ? T export declare type ObservedValuesFromArray = ObservedValueUnionFromArray; -export declare type ObservedValueTupleFromArray = X extends Array> ? { +export declare type ObservedValueTupleFromArray = X extends readonly ObservableInput[] ? { [K in keyof X]: ObservedValueOf; } : never;