@angular/core
Version:
Angular - the core framework
97 lines • 21 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { assertInInjectionContext, assertNotInReactiveContext, computed, DestroyRef, inject, signal, ɵRuntimeError, } from '@angular/core';
/**
* Get the current value of an `Observable` as a reactive `Signal`.
*
* `toSignal` returns a `Signal` which provides synchronous reactive access to values produced
* by the given `Observable`, by subscribing to that `Observable`. The returned `Signal` will always
* have the most recent value emitted by the subscription, and will throw an error if the
* `Observable` errors.
*
* With `requireSync` set to `true`, `toSignal` will assert that the `Observable` produces a value
* immediately upon subscription. No `initialValue` is needed in this case, and the returned signal
* does not include an `undefined` type.
*
* By default, the subscription will be automatically cleaned up when the current [injection
* context](guide/di/dependency-injection-context) is destroyed. For example, when `toSignal` is
* called during the construction of a component, the subscription will be cleaned up when the
* component is destroyed. If an injection context is not available, an explicit `Injector` can be
* passed instead.
*
* If the subscription should persist until the `Observable` itself completes, the `manualCleanup`
* option can be specified instead, which disables the automatic subscription teardown. No injection
* context is needed in this configuration as well.
*
* @developerPreview
*/
export function toSignal(source, options) {
ngDevMode &&
assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' +
'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.');
const requiresCleanup = !options?.manualCleanup;
requiresCleanup && !options?.injector && assertInInjectionContext(toSignal);
const cleanupRef = requiresCleanup
? options?.injector?.get(DestroyRef) ?? inject(DestroyRef)
: null;
const equal = makeToSignalEqual(options?.equal);
// Note: T is the Observable value type, and U is the initial value type. They don't have to be
// the same - the returned signal gives values of type `T`.
let state;
if (options?.requireSync) {
// Initially the signal is in a `NoValue` state.
state = signal({ kind: 0 /* StateKind.NoValue */ }, { equal });
}
else {
// If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.
state = signal({ kind: 1 /* StateKind.Value */, value: options?.initialValue }, { equal });
}
// Note: This code cannot run inside a reactive context (see assertion above). If we'd support
// this, we would subscribe to the observable outside of the current reactive context, avoiding
// that side-effect signal reads/writes are attribute to the current consumer. The current
// consumer only needs to be notified when the `state` signal changes through the observable
// subscription. Additional context (related to async pipe):
// https://github.com/angular/angular/pull/50522.
const sub = source.subscribe({
next: (value) => state.set({ kind: 1 /* StateKind.Value */, value }),
error: (error) => {
if (options?.rejectErrors) {
// Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes
// the error to end up as an uncaught exception.
throw error;
}
state.set({ kind: 2 /* StateKind.Error */, error });
},
// Completion of the Observable is meaningless to the signal. Signals don't have a concept of
// "complete".
});
if (ngDevMode && options?.requireSync && state().kind === 0 /* StateKind.NoValue */) {
throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
}
// Unsubscribe when the current context is destroyed, if requested.
cleanupRef?.onDestroy(sub.unsubscribe.bind(sub));
// The actual returned signal is a `computed` of the `State` signal, which maps the various states
// to either values or errors.
return computed(() => {
const current = state();
switch (current.kind) {
case 1 /* StateKind.Value */:
return current.value;
case 2 /* StateKind.Error */:
throw current.error;
case 0 /* StateKind.NoValue */:
// This shouldn't really happen because the error is thrown on creation.
// TODO(alxhub): use a RuntimeError when we finalize the error semantics
throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
}
}, { equal: options?.equal });
}
function makeToSignalEqual(userEquality = Object.is) {
return (a, b) => a.kind === 1 /* StateKind.Value */ && b.kind === 1 /* StateKind.Value */ && userEquality(a.value, b.value);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9fc2lnbmFsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29yZS9yeGpzLWludGVyb3Avc3JjL3RvX3NpZ25hbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBQ0wsd0JBQXdCLEVBQ3hCLDBCQUEwQixFQUMxQixRQUFRLEVBQ1IsVUFBVSxFQUNWLE1BQU0sRUFFTixNQUFNLEVBR04sYUFBYSxHQUVkLE1BQU0sZUFBZSxDQUFDO0FBd0Z2Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxNQUFNLFVBQVUsUUFBUSxDQUN0QixNQUF1QyxFQUN2QyxPQUFxRDtJQUVyRCxTQUFTO1FBQ1AsMEJBQTBCLENBQ3hCLFFBQVEsRUFDUiwyREFBMkQ7WUFDekQsb0dBQW9HLENBQ3ZHLENBQUM7SUFFSixNQUFNLGVBQWUsR0FBRyxDQUFDLE9BQU8sRUFBRSxhQUFhLENBQUM7SUFDaEQsZUFBZSxJQUFJLENBQUMsT0FBTyxFQUFFLFFBQVEsSUFBSSx3QkFBd0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM1RSxNQUFNLFVBQVUsR0FBRyxlQUFlO1FBQ2hDLENBQUMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDO1FBQzFELENBQUMsQ0FBQyxJQUFJLENBQUM7SUFFVCxNQUFNLEtBQUssR0FBRyxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFaEQsK0ZBQStGO0lBQy9GLDJEQUEyRDtJQUMzRCxJQUFJLEtBQW1DLENBQUM7SUFDeEMsSUFBSSxPQUFPLEVBQUUsV0FBVyxFQUFFLENBQUM7UUFDekIsZ0RBQWdEO1FBQ2hELEtBQUssR0FBRyxNQUFNLENBQUMsRUFBQyxJQUFJLDJCQUFtQixFQUFDLEVBQUUsRUFBQyxLQUFLLEVBQUMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7U0FBTSxDQUFDO1FBQ04sMkZBQTJGO1FBQzNGLEtBQUssR0FBRyxNQUFNLENBQ1osRUFBQyxJQUFJLHlCQUFpQixFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsWUFBaUIsRUFBQyxFQUMxRCxFQUFDLEtBQUssRUFBQyxDQUNSLENBQUM7SUFDSixDQUFDO0lBRUQsOEZBQThGO0lBQzlGLCtGQUErRjtJQUMvRiwwRkFBMEY7SUFDMUYsNEZBQTRGO0lBQzVGLDREQUE0RDtJQUM1RCxpREFBaUQ7SUFDakQsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUMzQixJQUFJLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBQyxJQUFJLHlCQUFpQixFQUFFLEtBQUssRUFBQyxDQUFDO1FBQzFELEtBQUssRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQ2YsSUFBSSxPQUFPLEVBQUUsWUFBWSxFQUFFLENBQUM7Z0JBQzFCLDJGQUEyRjtnQkFDM0YsZ0RBQWdEO2dCQUNoRCxNQUFNLEtBQUssQ0FBQztZQUNkLENBQUM7WUFDRCxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUMsSUFBSSx5QkFBaUIsRUFBRSxLQUFLLEVBQUMsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFDRCw2RkFBNkY7UUFDN0YsY0FBYztLQUNmLENBQUMsQ0FBQztJQUVILElBQUksU0FBUyxJQUFJLE9BQU8sRUFBRSxXQUFXLElBQUksS0FBSyxFQUFFLENBQUMsSUFBSSw4QkFBc0IsRUFBRSxDQUFDO1FBQzVFLE1BQU0sSUFBSSxhQUFhLDZEQUVyQixxRkFBcUYsQ0FDdEYsQ0FBQztJQUNKLENBQUM7SUFFRCxtRUFBbUU7SUFDbkUsVUFBVSxFQUFFLFNBQVMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBRWpELGtHQUFrRztJQUNsRyw4QkFBOEI7SUFDOUIsT0FBTyxRQUFRLENBQ2IsR0FBRyxFQUFFO1FBQ0gsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUFFLENBQUM7UUFDeEIsUUFBUSxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDckI7Z0JBQ0UsT0FBTyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQ3ZCO2dCQUNFLE1BQU0sT0FBTyxDQUFDLEtBQUssQ0FBQztZQUN0QjtnQkFDRSx3RUFBd0U7Z0JBQ3hFLHdFQUF3RTtnQkFDeEUsTUFBTSxJQUFJLGFBQWEsNkRBRXJCLHFGQUFxRixDQUN0RixDQUFDO1FBQ04sQ0FBQztJQUNILENBQUMsRUFDRCxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFDLENBQ3hCLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxpQkFBaUIsQ0FDeEIsZUFBbUMsTUFBTSxDQUFDLEVBQUU7SUFFNUMsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUNkLENBQUMsQ0FBQyxJQUFJLDRCQUFvQixJQUFJLENBQUMsQ0FBQyxJQUFJLDRCQUFvQixJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUMvRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7XG4gIGFzc2VydEluSW5qZWN0aW9uQ29udGV4dCxcbiAgYXNzZXJ0Tm90SW5SZWFjdGl2ZUNvbnRleHQsXG4gIGNvbXB1dGVkLFxuICBEZXN0cm95UmVmLFxuICBpbmplY3QsXG4gIEluamVjdG9yLFxuICBzaWduYWwsXG4gIFNpZ25hbCxcbiAgV3JpdGFibGVTaWduYWwsXG4gIMm1UnVudGltZUVycm9yLFxuICDJtVJ1bnRpbWVFcnJvckNvZGUsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtWYWx1ZUVxdWFsaXR5Rm59IGZyb20gJ0Bhbmd1bGFyL2NvcmUvcHJpbWl0aXZlcy9zaWduYWxzJztcbmltcG9ydCB7T2JzZXJ2YWJsZSwgU3Vic2NyaWJhYmxlfSBmcm9tICdyeGpzJztcblxuLyoqXG4gKiBPcHRpb25zIGZvciBgdG9TaWduYWxgLlxuICpcbiAqIEBwdWJsaWNBcGlcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUb1NpZ25hbE9wdGlvbnM8VD4ge1xuICAvKipcbiAgICogSW5pdGlhbCB2YWx1ZSBmb3IgdGhlIHNpZ25hbCBwcm9kdWNlZCBieSBgdG9TaWduYWxgLlxuICAgKlxuICAgKiBUaGlzIHdpbGwgYmUgdGhlIHZhbHVlIG9mIHRoZSBzaWduYWwgdW50aWwgdGhlIG9ic2VydmFibGUgZW1pdHMgaXRzIGZpcnN0IHZhbHVlLlxuICAgKi9cbiAgaW5pdGlhbFZhbHVlPzogdW5rbm93bjtcblxuICAvKipcbiAgICogV2hldGhlciB0byByZXF1aXJlIHRoYXQgdGhlIG9ic2VydmFibGUgZW1pdHMgc3luY2hyb25vdXNseSB3aGVuIGB0b1NpZ25hbGAgc3Vic2NyaWJlcy5cbiAgICpcbiAgICogSWYgdGhpcyBpcyBgdHJ1ZWAsIGB0b1NpZ25hbGAgd2lsbCBhc3NlcnQgdGhhdCB0aGUgb2JzZXJ2YWJsZSBwcm9kdWNlcyBhIHZhbHVlIGltbWVkaWF0ZWx5IHVwb25cbiAgICogc3Vic2NyaXB0aW9uLiBTZXR0aW5nIHRoaXMgb3B0aW9uIHJlbW92ZXMgdGhlIG5lZWQgdG8gZWl0aGVyIGRlYWwgd2l0aCBgdW5kZWZpbmVkYCBpbiB0aGVcbiAgICogc2lnbmFsIHR5cGUgb3IgcHJvdmlkZSBhbiBgaW5pdGlhbFZhbHVlYCwgYXQgdGhlIGNvc3Qgb2YgYSBydW50aW1lIGVycm9yIGlmIHRoaXMgcmVxdWlyZW1lbnQgaXNcbiAgICogbm90IG1ldC5cbiAgICovXG4gIHJlcXVpcmVTeW5jPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogYEluamVjdG9yYCB3aGljaCB3aWxsIHByb3ZpZGUgdGhlIGBEZXN0cm95UmVmYCB1c2VkIHRvIGNsZWFuIHVwIHRoZSBPYnNlcnZhYmxlIHN1YnNjcmlwdGlvbi5cbiAgICpcbiAgICogSWYgdGhpcyBpcyBub3QgcHJvdmlkZWQsIGEgYERlc3Ryb3lSZWZgIHdpbGwgYmUgcmV0cmlldmVkIGZyb20gdGhlIGN1cnJlbnQgW2luamVjdGlvblxuICAgKiBjb250ZXh0XShndWlkZS9kaS9kZXBlbmRlbmN5LWluamVjdGlvbi1jb250ZXh0KSwgdW5sZXNzIG1hbnVhbCBjbGVhbnVwIGlzIHJlcXVlc3RlZC5cbiAgICovXG4gIGluamVjdG9yPzogSW5qZWN0b3I7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIHN1YnNjcmlwdGlvbiBzaG91bGQgYmUgYXV0b21hdGljYWxseSBjbGVhbmVkIHVwICh2aWEgYERlc3Ryb3lSZWZgKSB3aGVuXG4gICAqIGB0b1NpZ25hbGAncyBjcmVhdGlvbiBjb250ZXh0IGlzIGRlc3Ryb3llZC5cbiAgICpcbiAgICogSWYgbWFudWFsIGNsZWFudXAgaXMgZW5hYmxlZCwgdGhlbiBgRGVzdHJveVJlZmAgaXMgbm90IHVzZWQsIGFuZCB0aGUgc3Vic2NyaXB0aW9uIHdpbGwgcGVyc2lzdFxuICAgKiB1bnRpbCB0aGUgYE9ic2VydmFibGVgIGl0c2VsZiBjb21wbGV0ZXMuXG4gICAqL1xuICBtYW51YWxDbGVhbnVwPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogV2hldGhlciBgdG9TaWduYWxgIHNob3VsZCB0aHJvdyBlcnJvcnMgZnJvbSB0aGUgT2JzZXJ2YWJsZSBlcnJvciBjaGFubmVsIGJhY2sgdG8gUnhKUywgd2hlcmVcbiAgICogdGhleSdsbCBiZSBwcm9jZXNzZWQgYXMgdW5jYXVnaHQgZXhjZXB0aW9ucy5cbiAgICpcbiAgICogSW4gcHJhY3RpY2UsIHRoaXMgbWVhbnMgdGhhdCB0aGUgc2lnbmFsIHJldHVybmVkIGJ5IGB0b1NpZ25hbGAgd2lsbCBrZWVwIHJldHVybmluZyB0aGUgbGFzdFxuICAgKiBnb29kIHZhbHVlIGZvcmV2ZXIsIGFzIE9ic2VydmFibGVzIHdoaWNoIGVycm9yIHByb2R1Y2Ugbm8gZnVydGhlciB2YWx1ZXMuIFRoaXMgb3B0aW9uIGVtdWxhdGVzXG4gICAqIHRoZSBiZWhhdmlvciBvZiB0aGUgYGFzeW5jYCBwaXBlLlxuICAgKi9cbiAgcmVqZWN0RXJyb3JzPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQSBjb21wYXJpc29uIGZ1bmN0aW9uIHdoaWNoIGRlZmluZXMgZXF1YWxpdHkgZm9yIHZhbHVlcyBlbWl0dGVkIGJ5IHRoZSBvYnNlcnZhYmxlLlxuICAgKlxuICAgKiBFcXVhbGl0eSBjb21wYXJpc29ucyBhcmUgZXhlY3V0ZWQgYWdhaW5zdCB0aGUgaW5pdGlhbCB2YWx1ZSBpZiBvbmUgaXMgcHJvdmlkZWQuXG4gICAqL1xuICBlcXVhbD86IFZhbHVlRXF1YWxpdHlGbjxUPjtcbn1cblxuLy8gQmFzZSBjYXNlOiBubyBvcHRpb25zIC0+IGB1bmRlZmluZWRgIGluIHRoZSByZXN1bHQgdHlwZS5cbmV4cG9ydCBmdW5jdGlvbiB0b1NpZ25hbDxUPihzb3VyY2U6IE9ic2VydmFibGU8VD4gfCBTdWJzY3JpYmFibGU8VD4pOiBTaWduYWw8VCB8IHVuZGVmaW5lZD47XG4vLyBPcHRpb25zIHdpdGggYHVuZGVmaW5lZGAgaW5pdGlhbCB2YWx1ZSBhbmQgbm8gYHJlcXVpcmVkU3luY2AgLT4gYHVuZGVmaW5lZGAuXG5leHBvcnQgZnVuY3Rpb24gdG9TaWduYWw8VD4oXG4gIHNvdXJjZTogT2JzZXJ2YWJsZTxUPiB8IFN1YnNjcmliYWJsZTxUPixcbiAgb3B0aW9uczogTm9JbmZlcjxUb1NpZ25hbE9wdGlvbnM8VCB8IHVuZGVmaW5lZD4+ICYge1xuICAgIGluaXRpYWxWYWx1ZT86IHVuZGVmaW5lZDtcbiAgICByZXF1aXJlU3luYz86IGZhbHNlO1xuICB9LFxuKTogU2lnbmFsPFQgfCB1bmRlZmluZWQ+O1xuLy8gT3B0aW9ucyB3aXRoIGBudWxsYCBpbml0aWFsIHZhbHVlIC0+IGBudWxsYC5cbmV4cG9ydCBmdW5jdGlvbiB0b1NpZ25hbDxUPihcbiAgc291cmNlOiBPYnNlcnZhYmxlPFQ+IHwgU3Vic2NyaWJhYmxlPFQ+LFxuICBvcHRpb25zOiBOb0luZmVyPFRvU2lnbmFsT3B0aW9uczxUIHwgbnVsbD4+ICYge2luaXRpYWxWYWx1ZT86IG51bGw7IHJlcXVpcmVTeW5jPzogZmFsc2V9LFxuKTogU2lnbmFsPFQgfCBudWxsPjtcbi8vIE9wdGlvbnMgd2l0aCBgdW5kZWZpbmVkYCBpbml0aWFsIHZhbHVlIGFuZCBgcmVxdWlyZWRTeW5jYCAtPiBzdHJpY3QgcmVzdWx0IHR5cGUuXG5leHBvcnQgZnVuY3Rpb24gdG9TaWduYWw8VD4oXG4gIHNvdXJjZTogT2JzZXJ2YWJsZTxUPiB8IFN1YnNjcmliYWJsZTxUPixcbiAgb3B0aW9uczogTm9JbmZlcjxUb1NpZ25hbE9wdGlvbnM8VD4+ICYge2luaXRpYWxWYWx1ZT86IHVuZGVmaW5lZDsgcmVxdWlyZVN5bmM6IHRydWV9LFxuKTogU2lnbmFsPFQ+O1xuLy8gT3B0aW9ucyB3aXRoIGEgbW9yZSBzcGVjaWZpYyBpbml0aWFsIHZhbHVlIHR5cGUuXG5leHBvcnQgZnVuY3Rpb24gdG9TaWduYWw8VCwgY29uc3QgVSBleHRlbmRzIFQ+KFxuICBzb3VyY2U6IE9ic2VydmFibGU8VD4gfCBTdWJzY3JpYmFibGU8VD4sXG4gIG9wdGlvbnM6IE5vSW5mZXI8VG9TaWduYWxPcHRpb25zPFQgfCBVPj4gJiB7aW5pdGlhbFZhbHVlOiBVOyByZXF1aXJlU3luYz86IGZhbHNlfSxcbik6IFNpZ25hbDxUIHwgVT47XG5cbi8qKlxuICogR2V0IHRoZSBjdXJyZW50IHZhbHVlIG9mIGFuIGBPYnNlcnZhYmxlYCBhcyBhIHJlYWN0aXZlIGBTaWduYWxgLlxuICpcbiAqIGB0b1NpZ25hbGAgcmV0dXJucyBhIGBTaWduYWxgIHdoaWNoIHByb3ZpZGVzIHN5bmNocm9ub3VzIHJlYWN0aXZlIGFjY2VzcyB0byB2YWx1ZXMgcHJvZHVjZWRcbiAqIGJ5IHRoZSBnaXZlbiBgT2JzZXJ2YWJsZWAsIGJ5IHN1YnNjcmliaW5nIHRvIHRoYXQgYE9ic2VydmFibGVgLiBUaGUgcmV0dXJuZWQgYFNpZ25hbGAgd2lsbCBhbHdheXNcbiAqIGhhdmUgdGhlIG1vc3QgcmVjZW50IHZhbHVlIGVtaXR0ZWQgYnkgdGhlIHN1YnNjcmlwdGlvbiwgYW5kIHdpbGwgdGhyb3cgYW4gZXJyb3IgaWYgdGhlXG4gKiBgT2JzZXJ2YWJsZWAgZXJyb3JzLlxuICpcbiAqIFdpdGggYHJlcXVpcmVTeW5jYCBzZXQgdG8gYHRydWVgLCBgdG9TaWduYWxgIHdpbGwgYXNzZXJ0IHRoYXQgdGhlIGBPYnNlcnZhYmxlYCBwcm9kdWNlcyBhIHZhbHVlXG4gKiBpbW1lZGlhdGVseSB1cG9uIHN1YnNjcmlwdGlvbi4gTm8gYGluaXRpYWxWYWx1ZWAgaXMgbmVlZGVkIGluIHRoaXMgY2FzZSwgYW5kIHRoZSByZXR1cm5lZCBzaWduYWxcbiAqIGRvZXMgbm90IGluY2x1ZGUgYW4gYHVuZGVmaW5lZGAgdHlwZS5cbiAqXG4gKiBCeSBkZWZhdWx0LCB0aGUgc3Vic2NyaXB0aW9uIHdpbGwgYmUgYXV0b21hdGljYWxseSBjbGVhbmVkIHVwIHdoZW4gdGhlIGN1cnJlbnQgW2luamVjdGlvblxuICogY29udGV4dF0oZ3VpZGUvZGkvZGVwZW5kZW5jeS1pbmplY3Rpb24tY29udGV4dCkgaXMgZGVzdHJveWVkLiBGb3IgZXhhbXBsZSwgd2hlbiBgdG9TaWduYWxgIGlzXG4gKiBjYWxsZWQgZHVyaW5nIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSBjb21wb25lbnQsIHRoZSBzdWJzY3JpcHRpb24gd2lsbCBiZSBjbGVhbmVkIHVwIHdoZW4gdGhlXG4gKiBjb21wb25lbnQgaXMgZGVzdHJveWVkLiBJZiBhbiBpbmplY3Rpb24gY29udGV4dCBpcyBub3QgYXZhaWxhYmxlLCBhbiBleHBsaWNpdCBgSW5qZWN0b3JgIGNhbiBiZVxuICogcGFzc2VkIGluc3RlYWQuXG4gKlxuICogSWYgdGhlIHN1YnNjcmlwdGlvbiBzaG91bGQgcGVyc2lzdCB1bnRpbCB0aGUgYE9ic2VydmFibGVgIGl0c2VsZiBjb21wbGV0ZXMsIHRoZSBgbWFudWFsQ2xlYW51cGBcbiAqIG9wdGlvbiBjYW4gYmUgc3BlY2lmaWVkIGluc3RlYWQsIHdoaWNoIGRpc2FibGVzIHRoZSBhdXRvbWF0aWMgc3Vic2NyaXB0aW9uIHRlYXJkb3duLiBObyBpbmplY3Rpb25cbiAqIGNvbnRleHQgaXMgbmVlZGVkIGluIHRoaXMgY29uZmlndXJhdGlvbiBhcyB3ZWxsLlxuICpcbiAqIEBkZXZlbG9wZXJQcmV2aWV3XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b1NpZ25hbDxULCBVID0gdW5kZWZpbmVkPihcbiAgc291cmNlOiBPYnNlcnZhYmxlPFQ+IHwgU3Vic2NyaWJhYmxlPFQ+LFxuICBvcHRpb25zPzogVG9TaWduYWxPcHRpb25zPFQgfCBVPiAmIHtpbml0aWFsVmFsdWU/OiBVfSxcbik6IFNpZ25hbDxUIHwgVT4ge1xuICBuZ0Rldk1vZGUgJiZcbiAgICBhc3NlcnROb3RJblJlYWN0aXZlQ29udGV4dChcbiAgICAgIHRvU2lnbmFsLFxuICAgICAgJ0ludm9raW5nIGB0b1NpZ25hbGAgY2F1c2VzIG5ldyBzdWJzY3JpcHRpb25zIGV2ZXJ5IHRpbWUuICcgK1xuICAgICAgICAnQ29uc2lkZXIgbW92aW5nIGB0b1NpZ25hbGAgb3V0c2lkZSBvZiB0aGUgcmVhY3RpdmUgY29udGV4dCBhbmQgcmVhZCB0aGUgc2lnbmFsIHZhbHVlIHdoZXJlIG5lZWRlZC4nLFxuICAgICk7XG5cbiAgY29uc3QgcmVxdWlyZXNDbGVhbnVwID0gIW9wdGlvbnM/Lm1hbnVhbENsZWFudXA7XG4gIHJlcXVpcmVzQ2xlYW51cCAmJiAhb3B0aW9ucz8uaW5qZWN0b3IgJiYgYXNzZXJ0SW5JbmplY3Rpb25Db250ZXh0KHRvU2lnbmFsKTtcbiAgY29uc3QgY2xlYW51cFJlZiA9IHJlcXVpcmVzQ2xlYW51cFxuICAgID8gb3B0aW9ucz8uaW5qZWN0b3I/LmdldChEZXN0cm95UmVmKSA/PyBpbmplY3QoRGVzdHJveVJlZilcbiAgICA6IG51bGw7XG5cbiAgY29uc3QgZXF1YWwgPSBtYWtlVG9TaWduYWxFcXVhbChvcHRpb25zPy5lcXVhbCk7XG5cbiAgLy8gTm90ZTogVCBpcyB0aGUgT2JzZXJ2YWJsZSB2YWx1ZSB0eXBlLCBhbmQgVSBpcyB0aGUgaW5pdGlhbCB2YWx1ZSB0eXBlLiBUaGV5IGRvbid0IGhhdmUgdG8gYmVcbiAgLy8gdGhlIHNhbWUgLSB0aGUgcmV0dXJuZWQgc2lnbmFsIGdpdmVzIHZhbHVlcyBvZiB0eXBlIGBUYC5cbiAgbGV0IHN0YXRlOiBXcml0YWJsZVNpZ25hbDxTdGF0ZTxUIHwgVT4+O1xuICBpZiAob3B0aW9ucz8ucmVxdWlyZVN5bmMpIHtcbiAgICAvLyBJbml0aWFsbHkgdGhlIHNpZ25hbCBpcyBpbiBhIGBOb1ZhbHVlYCBzdGF0ZS5cbiAgICBzdGF0ZSA9IHNpZ25hbCh7a2luZDogU3RhdGVLaW5kLk5vVmFsdWV9LCB7ZXF1YWx9KTtcbiAgfSBlbHNlIHtcbiAgICAvLyBJZiBhbiBpbml0aWFsIHZhbHVlIHdhcyBwYXNzZWQsIHVzZSBpdC4gT3RoZXJ3aXNlLCB1c2UgYHVuZGVmaW5lZGAgYXMgdGhlIGluaXRpYWwgdmFsdWUuXG4gICAgc3RhdGUgPSBzaWduYWw8U3RhdGU8VCB8IFU+PihcbiAgICAgIHtraW5kOiBTdGF0ZUtpbmQuVmFsdWUsIHZhbHVlOiBvcHRpb25zPy5pbml0aWFsVmFsdWUgYXMgVX0sXG4gICAgICB7ZXF1YWx9LFxuICAgICk7XG4gIH1cblxuICAvLyBOb3RlOiBUaGlzIGNvZGUgY2Fubm90IHJ1biBpbnNpZGUgYSByZWFjdGl2ZSBjb250ZXh0IChzZWUgYXNzZXJ0aW9uIGFib3ZlKS4gSWYgd2UnZCBzdXBwb3J0XG4gIC8vIHRoaXMsIHdlIHdvdWxkIHN1YnNjcmliZSB0byB0aGUgb2JzZXJ2YWJsZSBvdXRzaWRlIG9mIHRoZSBjdXJyZW50IHJlYWN0aXZlIGNvbnRleHQsIGF2b2lkaW5nXG4gIC8vIHRoYXQgc2lkZS1lZmZlY3Qgc2lnbmFsIHJlYWRzL3dyaXRlcyBhcmUgYXR0cmlidXRlIHRvIHRoZSBjdXJyZW50IGNvbnN1bWVyLiBUaGUgY3VycmVudFxuICAvLyBjb25zdW1lciBvbmx5IG5lZWRzIHRvIGJlIG5vdGlmaWVkIHdoZW4gdGhlIGBzdGF0ZWAgc2lnbmFsIGNoYW5nZXMgdGhyb3VnaCB0aGUgb2JzZXJ2YWJsZVxuICAvLyBzdWJzY3JpcHRpb24uIEFkZGl0aW9uYWwgY29udGV4dCAocmVsYXRlZCB0byBhc3luYyBwaXBlKTpcbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2FuZ3VsYXIvYW5ndWxhci9wdWxsLzUwNTIyLlxuICBjb25zdCBzdWIgPSBzb3VyY2Uuc3Vic2NyaWJlKHtcbiAgICBuZXh0OiAodmFsdWUpID0+IHN0YXRlLnNldCh7a2luZDogU3RhdGVLaW5kLlZhbHVlLCB2YWx1ZX0pLFxuICAgIGVycm9yOiAoZXJyb3IpID0+IHtcbiAgICAgIGlmIChvcHRpb25zPy5yZWplY3RFcnJvcnMpIHtcbiAgICAgICAgLy8gS2ljayB0aGUgZXJyb3IgYmFjayB0byBSeEpTLiBJdCB3aWxsIGJlIGNhdWdodCBhbmQgcmV0aHJvd24gaW4gYSBtYWNyb3Rhc2ssIHdoaWNoIGNhdXNlc1xuICAgICAgICAvLyB0aGUgZXJyb3IgdG8gZW5kIHVwIGFzIGFuIHVuY2F1Z2h0IGV4Y2VwdGlvbi5cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICBzdGF0ZS5zZXQoe2tpbmQ6IFN0YXRlS2luZC5FcnJvciwgZXJyb3J9KTtcbiAgICB9LFxuICAgIC8vIENvbXBsZXRpb24gb2YgdGhlIE9ic2VydmFibGUgaXMgbWVhbmluZ2xlc3MgdG8gdGhlIHNpZ25hbC4gU2lnbmFscyBkb24ndCBoYXZlIGEgY29uY2VwdCBvZlxuICAgIC8vIFwiY29tcGxldGVcIi5cbiAgfSk7XG5cbiAgaWYgKG5nRGV2TW9kZSAmJiBvcHRpb25zPy5yZXF1aXJlU3luYyAmJiBzdGF0ZSgpLmtpbmQgPT09IFN0YXRlS2luZC5Ob1ZhbHVlKSB7XG4gICAgdGhyb3cgbmV3IMm1UnVudGltZUVycm9yKFxuICAgICAgybVSdW50aW1lRXJyb3JDb2RlLlJFUVVJUkVfU1lOQ19XSVRIT1VUX1NZTkNfRU1JVCxcbiAgICAgICdgdG9TaWduYWwoKWAgY2FsbGVkIHdpdGggYHJlcXVpcmVTeW5jYCBidXQgYE9ic2VydmFibGVgIGRpZCBub3QgZW1pdCBzeW5jaHJvbm91c2x5LicsXG4gICAgKTtcbiAgfVxuXG4gIC8vIFVuc3Vic2NyaWJlIHdoZW4gdGhlIGN1cnJlbnQgY29udGV4dCBpcyBkZXN0cm95ZWQsIGlmIHJlcXVlc3RlZC5cbiAgY2xlYW51cFJlZj8ub25EZXN0cm95KHN1Yi51bnN1YnNjcmliZS5iaW5kKHN1YikpO1xuXG4gIC8vIFRoZSBhY3R1YWwgcmV0dXJuZWQgc2lnbmFsIGlzIGEgYGNvbXB1dGVkYCBvZiB0aGUgYFN0YXRlYCBzaWduYWwsIHdoaWNoIG1hcHMgdGhlIHZhcmlvdXMgc3RhdGVzXG4gIC8vIHRvIGVpdGhlciB2YWx1ZXMgb3IgZXJyb3JzLlxuICByZXR1cm4gY29tcHV0ZWQoXG4gICAgKCkgPT4ge1xuICAgICAgY29uc3QgY3VycmVudCA9IHN0YXRlKCk7XG4gICAgICBzd2l0Y2ggKGN1cnJlbnQua2luZCkge1xuICAgICAgICBjYXNlIFN0YXRlS2luZC5WYWx1ZTpcbiAgICAgICAgICByZXR1cm4gY3VycmVudC52YWx1ZTtcbiAgICAgICAgY2FzZSBTdGF0ZUtpbmQuRXJyb3I6XG4gICAgICAgICAgdGhyb3cgY3VycmVudC5lcnJvcjtcbiAgICAgICAgY2FzZSBTdGF0ZUtpbmQuTm9WYWx1ZTpcbiAgICAgICAgICAvLyBUaGlzIHNob3VsZG4ndCByZWFsbHkgaGFwcGVuIGJlY2F1c2UgdGhlIGVycm9yIGlzIHRocm93biBvbiBjcmVhdGlvbi5cbiAgICAgICAgICAvLyBUT0RPKGFseGh1Yik6IHVzZSBhIFJ1bnRpbWVFcnJvciB3aGVuIHdlIGZpbmFsaXplIHRoZSBlcnJvciBzZW1hbnRpY3NcbiAgICAgICAgICB0aHJvdyBuZXcgybVSdW50aW1lRXJyb3IoXG4gICAgICAgICAgICDJtVJ1bnRpbWVFcnJvckNvZGUuUkVRVUlSRV9TWU5DX1dJVEhPVVRfU1lOQ19FTUlULFxuICAgICAgICAgICAgJ2B0b1NpZ25hbCgpYCBjYWxsZWQgd2l0aCBgcmVxdWlyZVN5bmNgIGJ1dCBgT2JzZXJ2YWJsZWAgZGlkIG5vdCBlbWl0IHN5bmNocm9ub3VzbHkuJyxcbiAgICAgICAgICApO1xuICAgICAgfVxuICAgIH0sXG4gICAge2VxdWFsOiBvcHRpb25zPy5lcXVhbH0sXG4gICk7XG59XG5cbmZ1bmN0aW9uIG1ha2VUb1NpZ25hbEVxdWFsPFQ+KFxuICB1c2VyRXF1YWxpdHk6IFZhbHVlRXF1YWxpdHlGbjxUPiA9IE9iamVjdC5pcyxcbik6IFZhbHVlRXF1YWxpdHlGbjxTdGF0ZTxUPj4ge1xuICByZXR1cm4gKGEsIGIpID0+XG4gICAgYS5raW5kID09PSBTdGF0ZUtpbmQuVmFsdWUgJiYgYi5raW5kID09PSBTdGF0ZUtpbmQuVmFsdWUgJiYgdXNlckVxdWFsaXR5KGEudmFsdWUsIGIudmFsdWUpO1xufVxuXG5jb25zdCBlbnVtIFN0YXRlS2luZCB7XG4gIE5vVmFsdWUsXG4gIFZhbHVlLFxuICBFcnJvcixcbn1cblxuaW50ZXJmYWNlIE5vVmFsdWVTdGF0ZSB7XG4gIGtpbmQ6IFN0YXRlS2luZC5Ob1ZhbHVlO1xufVxuXG5pbnRlcmZhY2UgVmFsdWVTdGF0ZTxUPiB7XG4gIGtpbmQ6IFN0YXRlS2luZC5WYWx1ZTtcbiAgdmFsdWU6IFQ7XG59XG5cbmludGVyZmFjZSBFcnJvclN0YXRlIHtcbiAga2luZDogU3RhdGVLaW5kLkVycm9yO1xuICBlcnJvcjogdW5rbm93bjtcbn1cblxudHlwZSBTdGF0ZTxUPiA9IE5vVmFsdWVTdGF0ZSB8IFZhbHVlU3RhdGU8VD4gfCBFcnJvclN0YXRlO1xuIl19