UNPKG

graphql-redis-subscriptions

Version:
172 lines 6.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedisPubSub = void 0; const pubsub_async_iterator_1 = require("./pubsub-async-iterator"); class RedisPubSub { constructor(options = {}) { const { triggerTransform, connection, connectionListener, subscriber, publisher, reviver, serializer, deserializer, messageEventName = 'message', pmessageEventName = 'pmessage', } = options; this.triggerTransform = triggerTransform || (trigger => trigger); if (reviver && deserializer) { throw new Error("Reviver and deserializer can't be used together"); } this.reviver = reviver; this.serializer = serializer; this.deserializer = deserializer; if (subscriber && publisher) { this.redisPublisher = publisher; this.redisSubscriber = subscriber; } else { try { const IORedis = require('ioredis'); this.redisPublisher = new IORedis(connection); this.redisSubscriber = new IORedis(connection); if (connectionListener) { this.redisPublisher .on('connect', connectionListener) .on('error', connectionListener); this.redisSubscriber .on('connect', connectionListener) .on('error', connectionListener); } else { this.redisPublisher.on('error', console.error); this.redisSubscriber.on('error', console.error); } } catch (error) { console.error(`No publisher or subscriber instances were provided and the package 'ioredis' wasn't found. Couldn't create Redis clients.`); } } this.redisSubscriber.on(pmessageEventName, this.onMessage.bind(this)); this.redisSubscriber.on(messageEventName, this.onMessage.bind(this, undefined)); this.subscriptionMap = {}; this.subsRefsMap = new Map(); this.subsPendingRefsMap = new Map(); this.currentSubscriptionId = 0; } async publish(trigger, payload) { if (this.serializer) { await this.redisPublisher.publish(trigger, this.serializer(payload)); } else if (payload instanceof Buffer) { await this.redisPublisher.publish(trigger, payload); } else { await this.redisPublisher.publish(trigger, JSON.stringify(payload)); } } subscribe(trigger, onMessage, options = {}) { const triggerName = this.triggerTransform(trigger, options); const id = this.currentSubscriptionId++; this.subscriptionMap[id] = [triggerName, onMessage]; if (!this.subsRefsMap.has(triggerName)) { this.subsRefsMap.set(triggerName, new Set()); } const refs = this.subsRefsMap.get(triggerName); const pendingRefs = this.subsPendingRefsMap.get(triggerName); if (pendingRefs != null) { pendingRefs.refs.push(id); return pendingRefs.pending.then(() => id); } else if (refs.size > 0) { refs.add(id); return Promise.resolve(id); } else { const pending = new Deferred(); const subsPendingRefsMap = this.subsPendingRefsMap; subsPendingRefsMap.set(triggerName, { refs: [], pending }); const sub = new Promise((resolve, reject) => { const subscribeFn = options['pattern'] ? this.redisSubscriber.psubscribe : this.redisSubscriber.subscribe; subscribeFn.call(this.redisSubscriber, triggerName, err => { if (err) { subsPendingRefsMap.delete(triggerName); reject(err); } else { const pendingRefs = subsPendingRefsMap.get(triggerName); pendingRefs.refs.forEach((id) => refs.add(id)); subsPendingRefsMap.delete(triggerName); refs.add(id); resolve(id); } }); }); sub.then(pending.resolve).catch(pending.reject); return sub; } } unsubscribe(subId) { const [triggerName = null] = this.subscriptionMap[subId] || []; const refs = this.subsRefsMap.get(triggerName); if (!refs) throw new Error(`There is no subscription of id "${subId}"`); if (refs.size === 1) { this.redisSubscriber.unsubscribe(triggerName); this.redisSubscriber.punsubscribe(triggerName); this.subsRefsMap.delete(triggerName); } else { refs.delete(subId); } delete this.subscriptionMap[subId]; } asyncIterator(triggers, options) { return new pubsub_async_iterator_1.PubSubAsyncIterator(this, triggers, options); } asyncIterableIterator(triggers, options) { return new pubsub_async_iterator_1.PubSubAsyncIterator(this, triggers, options); } getSubscriber() { return this.redisSubscriber; } getPublisher() { return this.redisPublisher; } close() { return Promise.all([ this.redisPublisher.quit(), this.redisSubscriber.quit(), ]); } onMessage(pattern, channel, message) { if (typeof channel === 'object') channel = channel.toString('utf8'); const subscribers = this.subsRefsMap.get(pattern || channel); if (!subscribers?.size) return; let parsedMessage; try { if (this.deserializer) { parsedMessage = this.deserializer(Buffer.from(message), { pattern, channel }); } else if (typeof message === 'string') { parsedMessage = JSON.parse(message, this.reviver); } else { parsedMessage = message; } } catch (e) { parsedMessage = message; } subscribers.forEach(subId => { const [, listener] = this.subscriptionMap[subId]; listener(parsedMessage); }); } } exports.RedisPubSub = RedisPubSub; function Deferred() { const p = this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); this.then = p.then.bind(p); this.catch = p.catch.bind(p); if (p.finally) { this.finally = p.finally.bind(p); } } //# sourceMappingURL=redis-pubsub.js.map