diff --git a/.yarn/patches/@types-connect-redis-npm-0.0.18-4fd2b614d3 b/.yarn/patches/@types-connect-redis-npm-0.0.18-4fd2b614d3 new file mode 100644 index 0000000000..5e1e902d3d --- /dev/null +++ b/.yarn/patches/@types-connect-redis-npm-0.0.18-4fd2b614d3 @@ -0,0 +1,13 @@ +diff --git a/index.d.ts b/index.d.ts +index 413b15edf95c12b1b176279c82b3a9c3f06ade20..0158f6f7a95935acbb67aeea00cb94c241475565 100755 +--- a/index.d.ts ++++ b/index.d.ts +@@ -19,7 +19,7 @@ declare module 'connect-redis' { + function s(options: (options?: session.SessionOptions) => express.RequestHandler): s.RedisStore; + + namespace s { +- type Client = redis.RedisClient | ioRedis.Redis | ioRedis.Cluster; ++ type Client = redis.RedisClientType | ioRedis.Redis | ioRedis.Cluster; + interface RedisStore extends session.Store { + new (options: RedisStoreOptions): RedisStore; + client: Client; diff --git a/examples/angular-example/package.json b/examples/angular-example/package.json index 35f4b3bcd6..bd5f225c7c 100644 --- a/examples/angular-example/package.json +++ b/examples/angular-example/package.json @@ -45,7 +45,7 @@ "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.0", "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jsdoc": "^37.0.0", + "eslint-plugin-jsdoc": "^38.0.0", "eslint-plugin-prefer-arrow": "^1.2.3", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", diff --git a/package.json b/package.json index 64d22666bd..b88254d37a 100644 --- a/package.json +++ b/package.json @@ -77,13 +77,13 @@ "eslint-plugin-compat": "^4.0.0", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jest": "^25.0.0", - "eslint-plugin-jsdoc": "^37.0.0", + "eslint-plugin-jest": "^26.0.0", + "eslint-plugin-jsdoc": "^38.0.0", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-markdown": "^2.2.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prefer-import": "^0.0.1", - "eslint-plugin-promise": "^5.1.1", + "eslint-plugin-promise": "^6.0.0", "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", "events.once": "^2.0.2", @@ -198,7 +198,7 @@ ] }, "resolutions": { - "@types/redis": "2", + "@types/connect-redis@0.0.18": "patch:@types/connect-redis@npm:0.0.18#.yarn/patches/@types-connect-redis-npm-0.0.18-4fd2b614d3", "@types/eslint@^7.2.13": "^8.2.0", "@types/react": "^17", "@types/webpack-dev-server": "^4", diff --git a/packages/@uppy/companion/package.json b/packages/@uppy/companion/package.json index a746fb7750..7da91a641d 100644 --- a/packages/@uppy/companion/package.json +++ b/packages/@uppy/companion/package.json @@ -34,7 +34,7 @@ "body-parser": "1.19.0", "chalk": "2.4.2", "common-tags": "1.8.0", - "connect-redis": "4.0.3", + "connect-redis": "6.1.3", "cookie-parser": "1.4.6", "cors": "^2.8.5", "escape-goat": "3.0.0", @@ -55,11 +55,10 @@ "moment-timezone": "^0.5.31", "morgan": "1.10.0", "ms": "2.1.2", - "node-redis-pubsub": "^5.0.0", "node-schedule": "1.3.2", "prom-client": "12.0.0", "purest": "3.1.0", - "redis": "3.1.1", + "redis": "4.1.0", "request": "2.88.2", "semver": "6.3.0", "serialize-error": "^2.1.0", @@ -71,7 +70,7 @@ }, "devDependencies": { "@types/compression": "1.7.0", - "@types/connect-redis": "0.0.17", + "@types/connect-redis": "0.0.18", "@types/cookie-parser": "1.4.2", "@types/cors": "2.8.6", "@types/eslint": "^8.2.0", diff --git a/packages/@uppy/companion/src/server/emitter/redis-emitter.js b/packages/@uppy/companion/src/server/emitter/redis-emitter.js index 58e02a322c..549d6ed3f7 100644 --- a/packages/@uppy/companion/src/server/emitter/redis-emitter.js +++ b/packages/@uppy/companion/src/server/emitter/redis-emitter.js @@ -1,4 +1,4 @@ -const NRP = require('node-redis-pubsub') +const redis = require('redis') /** * This module simulates the builtin events.EventEmitter but with the use of redis. @@ -6,10 +6,50 @@ const NRP = require('node-redis-pubsub') * to be distributed across. */ module.exports = (redisUrl, redisPubSubScope) => { - const nrp = new NRP({ url: redisUrl, scope: redisPubSubScope }) + const prefix = redisPubSubScope ? `${redisPubSubScope}:` : '' + const publisher = redis.createClient({ url: redisUrl }) + let subscriber + + const connectedPromise = publisher.connect().then(() => { + subscriber = publisher.duplicate() + return subscriber.connect() + }) + + const errorHandlers = new Set() + function errorHandler (err) { + if (errorHandlers.size === 0) { + Promise.reject(err) // trigger unhandled rejection if there are no error handlers. + } + for (const handler of errorHandlers) { + handler(err) + } + } + let runWhenConnected = (fn) => { + connectedPromise.then(fn).catch(errorHandler) + } + connectedPromise.then(() => { + runWhenConnected = fn => fn().catch(errorHandler) + }, (err) => { + runWhenConnected = () => errorHandler(err) + }) function on (eventName, handler) { - nrp.on(eventName, handler) + function pMessageHandler (message, _channel, pattern) { + if (prefix + eventName === pattern) { + let jsonMsg = message + try { + jsonMsg = JSON.parse(message) + } catch (ex) { + if (typeof errorHandler === 'function') { + return errorHandler(`Invalid JSON received! Channel: ${eventName} Message: ${message}`) + } + } + return handler(jsonMsg, _channel) + } + return undefined + } + + runWhenConnected(() => subscriber.pSubscribe(prefix + eventName, pMessageHandler)) } /** @@ -19,10 +59,11 @@ module.exports = (redisUrl, redisPubSubScope) => { * @param {Function} handler the handler of the event */ function once (eventName, handler) { - const off = nrp.on(eventName, (message) => { + const actualHandler = (message) => { handler(message) - off() - }) + removeListener(eventName, actualHandler) + } + on(eventName, actualHandler) } /** @@ -32,18 +73,17 @@ module.exports = (redisUrl, redisPubSubScope) => { * @param {object} message the message to pass along with the event */ function emit (eventName, message) { - return nrp.emit(eventName, message || {}) + runWhenConnected(() => publisher.publish(prefix + eventName, message)) } /** * Remove an event listener * * @param {string} eventName name of the event - * @param {Function} handler the handler of the event to remove + * @param {any} handler the handler of the event to remove */ function removeListener (eventName, handler) { - nrp.receiver.removeListener(eventName, handler) - nrp.receiver.punsubscribe(`${nrp.prefix}${eventName}`) + runWhenConnected(() => subscriber.pUnsubscribe(`${prefix}${eventName}`, handler)) } /** @@ -52,8 +92,7 @@ module.exports = (redisUrl, redisPubSubScope) => { * @param {string} eventName name of the event */ function removeAllListeners (eventName) { - nrp.receiver.removeAllListeners(eventName) - nrp.receiver.punsubscribe(`${nrp.prefix}${eventName}`) + runWhenConnected(() => subscriber.pUnsubscribe(`${prefix}${eventName}`)) } return { diff --git a/packages/@uppy/companion/src/server/redis.js b/packages/@uppy/companion/src/server/redis.js index 4e2bf6c11d..a5ffed19a7 100644 --- a/packages/@uppy/companion/src/server/redis.js +++ b/packages/@uppy/companion/src/server/redis.js @@ -11,7 +11,8 @@ let redisClient */ function createClient (opts) { if (!redisClient) { - redisClient = redis.createClient(opts) + // TODO: rewrite to non-legacy mode once connect-redis supports it + redisClient = redis.createClient({ ...opts, legacyMode: true }) } return redisClient