UNPKG

@feathersjs/transport-commons

Version:

Shared functionality for websocket providers

121 lines (99 loc) 4.14 kB
import { Application, FeathersService, RealTimeConnection, getServiceOptions } from '@feathersjs/feathers' import { createDebug } from '@feathersjs/commons' import flattenDeep from 'lodash/flattenDeep' import { Channel } from './channel/base' import { CombinedChannel } from './channel/combined' import { channelMixin, publishMixin, keys, PublishMixin, Event, Publisher } from './mixins' import EventEmitter from 'events' const debug = createDebug('@feathersjs/transport-commons/channels') const { CHANNELS } = keys declare module '@feathersjs/feathers/lib/declarations' { interface ServiceAddons<A, S> extends EventEmitter { // eslint-disable-line publish(publisher: Publisher<ServiceGenericType<S>, A, this>): this publish(event: Event, publisher: Publisher<ServiceGenericType<S>, A, this>): this registerPublisher(publisher: Publisher<ServiceGenericType<S>, A, this>): this registerPublisher(event: Event, publisher: Publisher<ServiceGenericType<S>, A, this>): this } // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Application<Services, Settings> { // eslint-disable-line channels: string[] channel(name: string | string[]): Channel channel(...names: string[]): Channel publish<T>(publisher: Publisher<T, this>): this publish<T>(event: Event, publisher: Publisher<T, this>): this registerPublisher<T>(publisher: Publisher<T, this>): this registerPublisher<T>(event: Event, publisher: Publisher<T, this>): this } interface Params { connection?: RealTimeConnection } } export { keys } export function channels() { return (app: Application) => { if (typeof app.channel === 'function' && typeof app.publish === 'function') { return } Object.assign(app, channelMixin(), publishMixin()) Object.defineProperty(app, 'channels', { get() { return Object.keys(this[CHANNELS]) } }) app.mixins.push((service: FeathersService, path: string) => { const { serviceEvents } = getServiceOptions(service) if (typeof service.publish === 'function') { return } Object.assign(service, publishMixin()) serviceEvents.forEach((event: string) => { service.on(event, function (data, hook) { if (!hook) { // Fake hook for custom events hook = { path, service, app, result: data } } debug('Publishing event', event, hook.path) const logError = (error: any) => debug(`Error in '${hook.path} ${event}' publisher`, error) const servicePublishers = (service as unknown as PublishMixin)[keys.PUBLISHERS] const appPublishers = (app as unknown as PublishMixin)[keys.PUBLISHERS] // This will return the first publisher list that is not empty // In the following precedence const publisher = // 1. Service publisher for a specific event servicePublishers[event] || // 2. Service publisher for all events servicePublishers[keys.ALL_EVENTS] || // 3. App publisher for a specific event appPublishers[event] || // 4. App publisher for all events appPublishers[keys.ALL_EVENTS] || // 5. No publisher (() => {}) try { Promise.resolve(publisher(data, hook)) .then((result: any) => { if (!result) { return } const results = Array.isArray(result) ? flattenDeep(result).filter(Boolean) : ([result] as Channel[]) const channel = new CombinedChannel(results) if (channel && channel.length > 0) { app.emit('publish', event, channel, hook, data) } else { debug('No connections to publish to') } }) .catch(logError) } catch (error: any) { logError(error) } }) }) }) } } export { Channel, CombinedChannel, RealTimeConnection }