UNPKG

@aws-amplify/core

Version:

Core category of aws-amplify

139 lines (136 loc) • 4.72 kB
import { ConsoleLogger } from '../Logger/ConsoleLogger.mjs'; import { NO_HUBCALLBACK_PROVIDED_EXCEPTION } from '../constants.mjs'; import { AmplifyError } from '../errors/AmplifyError.mjs'; import '../types/errors.mjs'; import '../errors/errorHelpers.mjs'; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 const AMPLIFY_SYMBOL = (typeof Symbol !== 'undefined' ? Symbol('amplify_default') : '@@amplify_default'); const logger = new ConsoleLogger('Hub'); class HubClass { constructor(name) { this.listeners = new Map(); this.protectedChannels = [ 'core', 'auth', 'api', 'analytics', 'interactions', 'pubsub', 'storage', 'ui', 'xr', ]; this.name = name; } /** * Used internally to remove a Hub listener. * * @remarks * This private method is for internal use only. Instead of calling Hub.remove, call the result of Hub.listen. */ _remove(channel, listener) { const holder = this.listeners.get(channel); if (!holder) { logger.warn(`No listeners for ${channel}`); return; } this.listeners.set(channel, [ ...holder.filter(({ callback }) => callback !== listener), ]); } dispatch(channel, payload, source, ampSymbol, crossTab) { if (typeof channel === 'string' && this.protectedChannels.indexOf(channel) > -1) { const hasAccess = ampSymbol === AMPLIFY_SYMBOL; if (!hasAccess) { logger.warn(`WARNING: ${channel} is protected and dispatching on it can have unintended consequences`); } } const capsule = { channel, crossTab, payload: { ...payload }, source, patternInfo: [], }; try { this._toListeners(capsule); } catch (e) { logger.error(e); } } listen(channel, callback, options = { listenerName: 'noname', }) { let cb; const o = typeof options === 'string' ? { listenerName: options, enableCrossTabEvents: false } : { listenerName: 'noname', enableCrossTabEvents: false, ...options }; if (typeof callback !== 'function') { throw new AmplifyError({ name: NO_HUBCALLBACK_PROVIDED_EXCEPTION, message: 'No callback supplied to Hub', }); } else { // Needs to be casted as a more generic type cb = callback; } let holder = this.listeners.get(channel); if (!holder) { holder = []; this.listeners.set(channel, holder); } holder.push({ name: o.listenerName, crossTab: o.enableCrossTabEvents, callback: cb, }); return () => { this._remove(channel, cb); }; } _toListeners(capsule) { const { channel, payload, crossTab } = capsule; const holder = this.listeners.get(channel); if (holder) { const eligibleListeners = holder.filter(listener => { // Cross-tab dispatch: only cross-tab listeners on the 'auth' // channel are eligible (cross-tab events are auth-only). if (crossTab) { return !!listener.crossTab && channel === 'auth'; } // Same-tab dispatch: every listener is eligible — cross-tab // listeners form a superset and therefore ALSO receive same-tab // events, while plain listeners receive same-tab events only. return true; }); eligibleListeners.forEach(listener => { logger.debug(`Dispatching to ${channel} with `, payload); try { listener.callback(capsule); } catch (e) { logger.error(e); } }); } } } /* We export a __default__ instance of HubClass to use it as a pseudo Singleton for the main messaging bus, however you can still create your own instance of HubClass() for a separate "private bus" of events. */ const Hub = new HubClass('__default__'); /** * @internal * * Internal hub used for core Amplify functionality. Not intended for use outside of Amplify. * */ const HubInternal = new HubClass('internal-hub'); export { AMPLIFY_SYMBOL, Hub, HubClass, HubInternal }; //# sourceMappingURL=index.mjs.map