@ricardojbd/ngrx-signals-broadcast-sync
Version:
Enables seamless synchronization of state between browser contexts
95 lines • 16.7 kB
JavaScript
import { signalStoreFeature, withMethods, withHooks, getState, patchState } from '@ngrx/signals';
import { effect, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';
import isEqual from 'lodash.isequal';
const NOOP = () => null;
const BroadcastSyncStub = {
getBroadcastChannel: NOOP,
broadcastState: NOOP,
_requestBroadcastState: NOOP,
_patchStateFromBroadcast: NOOP
};
/**
* Adds broadcast synchronization capabilities to a Signal Store.
*
* This function enables state synchronization between multiple clients using the `BroadcastChannel` API.
* It can be invoked either with a broadcast channel name or with a configuration object (`Options`).
* It allows clients to request, send, and patch the state across multiple contexts.
*
* @template Input The type of the store feature result, extending `SignalStoreFeatureResult`.
* @param channelOrOptions Either the name of the broadcast channel or a configuration object.
* If a string is provided, it is treated as the broadcast channel name.
* If an `Options` object is provided, it allows further configuration of the broadcast behavior.
* @returns A Signal Store feature that adds methods for broadcast synchronization.
*/
export function withBroadcastSync(channelOrOptions) {
const { channel, requestState = false, select = (state) => state, messageEventInterceptor = (event) => event.data, broadcastStateInterceptor = (state) => state, onMessageError = () => null } = typeof channelOrOptions === 'string' ? { channel: channelOrOptions } : channelOrOptions;
const runGuard = (platformId) => isPlatformServer(platformId) || typeof window?.BroadcastChannel === 'undefined';
let stateChannel;
let lastSyncedState = null;
return signalStoreFeature(withMethods((store, platformId = inject(PLATFORM_ID)) => {
if (runGuard(platformId)) {
console.warn(`'withBroadcastSync' provides non-functional implementation due to server-side execution`);
return BroadcastSyncStub;
}
return {
getBroadcastChannel() {
return stateChannel;
},
broadcastState() {
let state = select(getState(store));
if (isEqual(state, lastSyncedState)) {
return;
}
state = broadcastStateInterceptor(state);
if (stateChannel != null) {
lastSyncedState = state; // Avoid broadcast duplicated state
stateChannel.postMessage({ type: 'UPDATE_STATE', state });
}
},
_requestBroadcastState() {
lastSyncedState = select(getState(store)); // Avoid broadcast initial state
stateChannel?.postMessage({ type: 'REQUEST_STATE' });
},
_patchStateFromBroadcast(state) {
lastSyncedState = state; // Avoid broadcast this state change
patchState(store, state);
}
};
}), withHooks({
onInit(store, platformId = inject(PLATFORM_ID)) {
if (runGuard(platformId)) {
return;
}
stateChannel = new BroadcastChannel(channel);
if (requestState) {
store._requestBroadcastState();
}
stateChannel.onmessage = (event) => {
const message = messageEventInterceptor(event);
switch (message.type) {
case 'REQUEST_STATE': {
lastSyncedState = null; // Force broadcast state
store.broadcastState();
break;
}
case 'UPDATE_STATE': {
store._patchStateFromBroadcast(message.state);
break;
}
default: {
console.warn(`Unknown message type "${message}" in the broadcast channel "${channel}"`);
}
}
};
stateChannel.onmessageerror = (event) => onMessageError(event);
effect(() => {
store.broadcastState();
});
},
onDestroy() {
stateChannel?.close();
}
}));
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1icm9hZGNhc3Qtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2xpYnMvbmdyeC1zaWduYWxzLWJyb2FkY2FzdC1zeW5jL3NyYy9saWIvd2l0aC1icm9hZGNhc3Qtc3luYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsa0JBQWtCLEVBR2xCLFdBQVcsRUFDWCxTQUFTLEVBQ1QsUUFBUSxFQUNSLFVBQVUsRUFDWCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDbkQsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFHckMsTUFBTSxJQUFJLEdBQUcsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDO0FBRXhCLE1BQU0saUJBQWlCLEdBQTJEO0lBQ2hGLG1CQUFtQixFQUFFLElBQUk7SUFDekIsY0FBYyxFQUFFLElBQUk7SUFDcEIsc0JBQXNCLEVBQUUsSUFBSTtJQUM1Qix3QkFBd0IsRUFBRSxJQUFJO0NBQy9CLENBQUM7QUE4QkY7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixnQkFBa0Q7SUFFbEQsTUFBTSxFQUNKLE9BQU8sRUFDUCxZQUFZLEdBQUcsS0FBSyxFQUNwQixNQUFNLEdBQUcsQ0FBQyxLQUFxQixFQUFFLEVBQUUsQ0FBQyxLQUFLLEVBQ3pDLHVCQUF1QixHQUFHLENBQUMsS0FBNEMsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksRUFDdEYseUJBQXlCLEdBQUcsQ0FBQyxLQUFxQixFQUFFLEVBQUUsQ0FBQyxLQUFLLEVBQzVELGNBQWMsR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJLEVBQzVCLEdBQUcsT0FBTyxnQkFBZ0IsS0FBSyxRQUFRLENBQUMsQ0FB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LElBQUksQ0FBQyx5QkFBeUIsT0FBTywrQkFBK0IsT0FBTyxHQUFHLENBQUMsQ0FBQztvQkFDMUYsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQyxDQUFDO1lBRUYsWUFBWSxDQUFDLGNBQWMsR0FBRyxDQUFDLEtBQW1CLEVBQUUsRUFBRSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUU3RSxNQUFNLENBQUMsR0FBRyxFQUFFO2dCQUNWLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN6QixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxTQUFTO1lBQ1AsWUFBWSxFQUFFLEtBQUssRUFBRSxDQUFDO1FBQ3hCLENBQUM7S0FDRixDQUFDLENBQ0gsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBzaWduYWxTdG9yZUZlYXR1cmUsXG4gIFNpZ25hbFN0b3JlRmVhdHVyZSxcbiAgU2lnbmFsU3RvcmVGZWF0dXJlUmVzdWx0LFxuICB3aXRoTWV0aG9kcyxcbiAgd2l0aEhvb2tzLFxuICBnZXRTdGF0ZSxcbiAgcGF0Y2hTdGF0ZVxufSBmcm9tICdAbmdyeC9zaWduYWxzJztcbmltcG9ydCB7IGVmZmVjdCwgaW5qZWN0LCBQTEFURk9STV9JRCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgaXNQbGF0Zm9ybVNlcnZlciB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgaXNFcXVhbCBmcm9tICdsb2Rhc2guaXNlcXVhbCc7XG5pbXBvcnQgeyBNZXNzYWdlLCBPcHRpb25zLCBCcm9hZGNhc3RTeW5jRmVhdHVyZVJlc3VsdCB9IGZyb20gJy4vbW9kZWxzJztcblxuY29uc3QgTk9PUCA9ICgpID0+IG51bGw7XG5cbmNvbnN0IEJyb2FkY2FzdFN5bmNTdHViOiBQaWNrPEJyb2FkY2FzdFN5bmNGZWF0dXJlUmVzdWx0LCAnbWV0aG9kcyc+WydtZXRob2RzJ10gPSB7XG4gIGdldEJyb2FkY2FzdENoYW5uZWw6IE5PT1AsXG4gIGJyb2FkY2FzdFN0YXRlOiBOT09QLFxuICBfcmVxdWVzdEJyb2FkY2FzdFN0YXRlOiBOT09QLFxuICBfcGF0Y2hTdGF0ZUZyb21Ccm9hZGNhc3Q6IE5PT1Bcbn07XG5cbi8qKlxuICogQWRkcyBicm9hZGNhc3Qgc3luY2hyb25pemF0aW9uIGNhcGFiaWxpdGllcyB0byBhIFNpZ25hbCBTdG9yZS5cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGVuYWJsZXMgc3RhdGUgc3luY2hyb25pemF0aW9uIGJldHdlZW4gbXVsdGlwbGUgY2xpZW50cyB1c2luZyB0aGUgYEJyb2FkY2FzdENoYW5uZWxgIEFQSS5cbiAqIEl0IGFsbG93cyBjbGllbnRzIHRvIHJlcXVlc3QsIHNlbmQsIGFuZCBwYXRjaCB0aGUgc3RhdGUgYWNyb3NzIG11bHRpcGxlIGNvbnRleHRzLlxuICpcbiAqIEB0ZW1wbGF0ZSBJbnB1dCBUaGUgdHlwZSBvZiB0aGUgc3RvcmUgZmVhdHVyZSByZXN1bHQsIGV4dGVuZGluZyBgU2lnbmFsU3RvcmVGZWF0dXJlUmVzdWx0YC5cbiAqIEBwYXJhbSBjaGFubmVsIFRoZSBuYW1lIG9mIHRoZSBicm9hZGNhc3QgY2hhbm5lbCB0byB1c2UgZm9yIHN5bmNocm9uaXppbmcgc3RhdGUgYWNyb3NzIGNsaWVudHMuXG4gKiBAcmV0dXJucyBBIFNpZ25hbCBTdG9yZSBmZWF0dXJlIHdpdGggYnJvYWRjYXN0IGNhcGFiaWxpdGllcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhCcm9hZGNhc3RTeW5jPElucHV0IGV4dGVuZHMgU2lnbmFsU3RvcmVGZWF0dXJlUmVzdWx0PihcbiAgY2hhbm5lbDogc3RyaW5nXG4pOiBTaWduYWxTdG9yZUZlYXR1cmU8SW5wdXQsIEJyb2FkY2FzdFN5bmNGZWF0dXJlUmVzdWx0PjtcblxuLyoqXG4gKiBBZGRzIGJyb2FkY2FzdCBzeW5jaHJvbml6YXRpb24gY2FwYWJpbGl0aWVzIHRvIGEgU2lnbmFsIFN0b3JlLlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gZW5hYmxlcyBzdGF0ZSBzeW5jaHJvbml6YXRpb24gYmV0d2VlbiBtdWx0aXBsZSBjbGllbnRzIHVzaW5nIHRoZSBgQnJvYWRjYXN0Q2hhbm5lbGAgQVBJLlxuICogSXQgYWxsb3dzIGNsaWVudHMgdG8gcmVxdWVzdCwgc2VuZCwgYW5kIHBhdGNoIHRoZSBzdGF0ZSBhY3Jvc3MgbXVsdGlwbGUgY29udGV4dHMuXG4gKlxuICogQHRlbXBsYXRlIElucHV0IFRoZSB0eXBlIG9mIHRoZSBzdG9yZSBmZWF0dXJlIHJlc3VsdCwgZXh0ZW5kaW5nIGBTaWduYWxTdG9yZUZlYXR1cmVSZXN1bHRgLlxuICogQHBhcmFtIG9wdGlvbnMgQSBjb25maWd1cmF0aW9uIG9iamVjdCBzcGVjaWZ5aW5nIHRoZSBicm9hZGNhc3QgY2hhbm5lbCBhbmQgYWRkaXRpb25hbCBiZWhhdmlvciBmb3Igc3RhdGUgc3luY2hyb25pemF0aW9uLlxuICogQHJldHVybnMgQSBTaWduYWwgU3RvcmUgZmVhdHVyZSB3aXRoIGN1c3RvbWl6YWJsZSBicm9hZGNhc3QgY2FwYWJpbGl0aWVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aEJyb2FkY2FzdFN5bmM8SW5wdXQgZXh0ZW5kcyBTaWduYWxTdG9yZUZlYXR1cmVSZXN1bHQ+KFxuICBvcHRpb25zOiBPcHRpb25zPElucHV0WydzdGF0ZSddPlxuKTogU2lnbmFsU3RvcmVGZWF0dXJlPElucHV0LCBCcm9hZGNhc3RTeW5jRmVhdHVyZVJlc3VsdD47XG5cbi8qKlxuICogQWRkcyBicm9hZGNhc3Qgc3luY2hyb25pemF0aW9uIGNhcGFiaWxpdGllcyB0byBhIFNpZ25hbCBTdG9yZS5cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGVuYWJsZXMgc3RhdGUgc3luY2hyb25pemF0aW9uIGJldHdlZW4gbXVsdGlwbGUgY2xpZW50cyB1c2luZyB0aGUgYEJyb2FkY2FzdENoYW5uZWxgIEFQSS5cbiAqIEl0IGNhbiBiZSBpbnZva2VkIGVpdGhlciB3aXRoIGEgYnJvYWRjYXN0IGNoYW5uZWwgbmFtZSBvciB3aXRoIGEgY29uZmlndXJhdGlvbiBvYmplY3QgKGBPcHRpb25zYCkuXG4gKiBJdCBhbGxvd3MgY2xpZW50cyB0byByZXF1ZXN0LCBzZW5kLCBhbmQgcGF0Y2ggdGhlIHN0YXRlIGFjcm9zcyBtdWx0aXBsZSBjb250ZXh0cy5cbiAqXG4gKiBAdGVtcGxhdGUgSW5wdXQgVGhlIHR5cGUgb2YgdGhlIHN0b3JlIGZlYXR1cmUgcmVzdWx0LCBleHRlbmRpbmcgYFNpZ25hbFN0b3JlRmVhdHVyZVJlc3VsdGAuXG4gKiBAcGFyYW0gY2hhbm5lbE9yT3B0aW9ucyBFaXRoZXIgdGhlIG5hbWUgb2YgdGhlIGJyb2FkY2FzdCBjaGFubmVsIG9yIGEgY29uZmlndXJhdGlvbiBvYmplY3QuXG4gKiBJZiBhIHN0cmluZyBpcyBwcm92aWRlZCwgaXQgaXMgdHJlYXRlZCBhcyB0aGUgYnJvYWRjYXN0IGNoYW5uZWwgbmFtZS5cbiAqIElmIGFuIGBPcHRpb25zYCBvYmplY3QgaXMgcHJvdmlkZWQsIGl0IGFsbG93cyBmdXJ0aGVyIGNvbmZpZ3VyYXRpb24gb2YgdGhlIGJyb2FkY2FzdCBiZWhhdmlvci5cbiAqIEByZXR1cm5zIEEgU2lnbmFsIFN0b3JlIGZlYXR1cmUgdGhhdCBhZGRzIG1ldGhvZHMgZm9yIGJyb2FkY2FzdCBzeW5jaHJvbml6YXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoQnJvYWRjYXN0U3luYzxJbnB1dCBleHRlbmRzIFNpZ25hbFN0b3JlRmVhdHVyZVJlc3VsdD4oXG4gIGNoYW5uZWxPck9wdGlvbnM6IE9wdGlvbnM8SW5wdXRbJ3N0YXRlJ10+IHwgc3RyaW5nXG4pOiBTaWduYWxTdG9yZUZlYXR1cmU8SW5wdXQsIEJyb2FkY2FzdFN5bmNGZWF0dXJlUmVzdWx0PiB7XG4gIGNvbnN0IHtcbiAgICBjaGFubmVsLFxuICAgIHJlcXVlc3RTdGF0ZSA9IGZhbHNlLFxuICAgIHNlbGVjdCA9IChzdGF0ZTogSW5wdXRbJ3N0YXRlJ10pID0+IHN0YXRlLFxuICAgIG1lc3NhZ2VFdmVudEludGVyY2VwdG9yID0gKGV2ZW50OiBNZXNzYWdlRXZlbnQ8TWVzc2FnZTxJbnB1dFsnc3RhdGUnXT4+KSA9PiBldmVudC5kYXRhLFxuICAgIGJyb2FkY2FzdFN0YXRlSW50ZXJjZXB0b3IgPSAoc3RhdGU6IElucHV0WydzdGF0ZSddKSA9PiBzdGF0ZSxcbiAgICBvbk1lc3NhZ2VFcnJvciA9ICgpID0+IG51bGxcbiAgfSA9IHR5cGVvZiBjaGFubmVsT3JPcHRpb25zID09PSAnc3RyaW5nJyA/IHsgY2hhbm5lbDogY2hhbm5lbE9yT3B0aW9ucyB9IDogY2hhbm5lbE9yT3B0aW9ucztcblxuICBjb25zdCBydW5HdWFyZCA9IChwbGF0Zm9ybUlkOiBvYmplY3QpID0+XG4gICAgaXNQbGF0Zm9ybVNlcnZlcihwbGF0Zm9ybUlkKSB8fCB0eXBlb2Ygd2luZG93Py5Ccm9hZGNhc3RDaGFubmVsID09PSAndW5kZWZpbmVkJztcbiAgbGV0IHN0YXRlQ2hhbm5lbDogQnJvYWRjYXN0Q2hhbm5lbCB8IG51bGw7XG4gIGxldCBsYXN0U3luY2VkU3RhdGU6IFBhcnRpYWw8SW5wdXRbJ3N0YXRlJ10+IHwgbnVsbCA9IG51bGw7XG5cbiAgcmV0dXJuIHNpZ25hbFN0b3JlRmVhdHVyZShcbiAgICB3aXRoTWV0aG9kcygoc3RvcmUsIHBsYXRmb3JtSWQgPSBpbmplY3QoUExBVEZPUk1fSUQpKSA9PiB7XG4gICAgICBpZiAocnVuR3VhcmQocGxhdGZvcm1JZCkpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGAnd2l0aEJyb2FkY2FzdFN5bmMnIHByb3ZpZGVzIG5vbi1mdW5jdGlvbmFsIGltcGxlbWVudGF0aW9uIGR1ZSB0byBzZXJ2ZXItc2lkZSBleGVjdXRpb25gKTtcbiAgICAgICAgcmV0dXJuIEJyb2FkY2FzdFN5bmNTdHViO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBnZXRCcm9hZGNhc3RDaGFubmVsKCk6IEJyb2FkY2FzdENoYW5uZWwgfCBudWxsIHtcbiAgICAgICAgICByZXR1cm4gc3RhdGVDaGFubmVsO1xuICAgICAgICB9LFxuXG4gICAgICAgIGJyb2FkY2FzdFN0YXRlKCk6IHZvaWQge1xuICAgICAgICAgIGxldCBzdGF0ZSA9IHNlbGVjdChnZXRTdGF0ZShzdG9yZSkpO1xuXG4gICAgICAgICAgaWYgKGlzRXF1YWwoc3RhdGUsIGxhc3RTeW5jZWRTdGF0ZSkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzdGF0ZSA9IGJyb2FkY2FzdFN0YXRlSW50ZXJjZXB0b3Ioc3RhdGUpO1xuXG4gICAgICAgICAgaWYgKHN0YXRlQ2hhbm5lbCAhPSBudWxsKSB7XG4gICAgICAgICAgICBsYXN0U3luY2VkU3RhdGUgPSBzdGF0ZTsgLy8gQXZvaWQgYnJvYWRjYXN0IGR1cGxpY2F0ZWQgc3RhdGVcbiAgICAgICAgICAgIHN0YXRlQ2hhbm5lbC5wb3N0TWVzc2FnZSh7IHR5cGU6ICdVUERBVEVfU1RBVEUnLCBzdGF0ZSB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG5cbiAgICAgICAgX3JlcXVlc3RCcm9hZGNhc3RTdGF0ZSgpOiB2b2lkIHtcbiAgICAgICAgICBsYXN0U3luY2VkU3RhdGUgPSBzZWxlY3QoZ2V0U3RhdGUoc3RvcmUpKTsgLy8gQXZvaWQgYnJvYWRjYXN0IGluaXRpYWwgc3RhdGVcbiAgICAgICAgICBzdGF0ZUNoYW5uZWw/LnBvc3RNZXNzYWdlKHsgdHlwZTogJ1JFUVVFU1RfU1RBVEUnIH0pO1xuICAgICAgICB9LFxuXG4gICAgICAgIF9wYXRjaFN0YXRlRnJvbUJyb2FkY2FzdChzdGF0ZTogSW5wdXRbJ3N0YXRlJ10pIHtcbiAgICAgICAgICBsYXN0U3luY2VkU3RhdGUgPSBzdGF0ZTsgLy8gQXZvaWQgYnJvYWRjYXN0IHRoaXMgc3RhdGUgY2hhbmdlXG4gICAgICAgICAgcGF0Y2hTdGF0ZShzdG9yZSwgc3RhdGUpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgIH0pLFxuICAgIHdpdGhIb29rcyh7XG4gICAgICBvbkluaXQoc3RvcmUsIHBsYXRmb3JtSWQgPSBpbmplY3QoUExBVEZPUk1fSUQpKSB7XG4gICAgICAgIGlmIChydW5HdWFyZChwbGF0Zm9ybUlkKSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHN0YXRlQ2hhbm5lbCA9IG5ldyBCcm9hZGNhc3RDaGFubmVsKGNoYW5uZWwpO1xuXG4gICAgICAgIGlmIChyZXF1ZXN0U3RhdGUpIHtcbiAgICAgICAgICBzdG9yZS5fcmVxdWVzdEJyb2FkY2FzdFN0YXRlKCk7XG4gICAgICAgIH1cblxuICAgICAgICBzdGF0ZUNoYW5uZWwub25tZXNzYWdlID0gKGV2ZW50OiBNZXNzYWdlRXZlbnQ8TWVzc2FnZTxJbnB1dFsnc3RhdGUnXT4+KSA9PiB7XG4gICAgICAgICAgY29uc3QgbWVzc2FnZSA9IG1lc3NhZ2VFdmVudEludGVyY2VwdG9yKGV2ZW50KTtcblxuICAgICAgICAgIHN3aXRjaCAobWVzc2FnZS50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdSRVFVRVNUX1NUQVRFJzoge1xuICAgICAgICAgICAgICBsYXN0U3luY2VkU3RhdGUgPSBudWxsOyAvLyBGb3JjZSBicm9hZGNhc3Qgc3RhdGVcbiAgICAgICAgICAgICAgc3RvcmUuYnJvYWRjYXN0U3RhdGUoKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlICdVUERBVEVfU1RBVEUnOiB7XG4gICAgICAgICAgICAgIHN0b3JlLl9wYXRjaFN0YXRlRnJvbUJyb2FkY2FzdChtZXNzYWdlLnN0YXRlKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgIGNvbnNvbGUud2FybihgVW5rbm93biBtZXNzYWdlIHR5cGUgXCIke21lc3NhZ2V9XCIgaW4gdGhlIGJyb2FkY2FzdCBjaGFubmVsIFwiJHtjaGFubmVsfVwiYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHN0YXRlQ2hhbm5lbC5vbm1lc3NhZ2VlcnJvciA9IChldmVudDogTWVzc2FnZUV2ZW50KSA9PiBvbk1lc3NhZ2VFcnJvcihldmVudCk7XG5cbiAgICAgICAgZWZmZWN0KCgpID0+IHtcbiAgICAgICAgICBzdG9yZS5icm9hZGNhc3RTdGF0ZSgpO1xuICAgICAgICB9KTtcbiAgICAgIH0sXG5cbiAgICAgIG9uRGVzdHJveSgpIHtcbiAgICAgICAgc3RhdGVDaGFubmVsPy5jbG9zZSgpO1xuICAgICAgfVxuICAgIH0pXG4gICk7XG59XG4iXX0=