{"_id":"@13bytes/redis-pubsub","_rev":"3-45e2ffb0cbbefd34f39beb86b7ebcad6","name":"@13bytes/redis-pubsub","dist-tags":{"latest":"4.1.5"},"versions":{"4.1.3":{"name":"@13bytes/redis-pubsub","version":"4.1.3","keywords":["ioredis","redis","pubsub","graphql"],"author":{"name":"PabloSzx","email":"pablosaez1995@gmail.com"},"license":"MIT","_id":"@13bytes/redis-pubsub@4.1.3","maintainers":[{"name":"13bytes","email":"coding@13bytes.de"}],"homepage":"https://github.com/13bytes/redis-pubsub#readme","bugs":{"url":"https://github.com/13bytes/redis-pubsub/issues"},"dist":{"shasum":"600325d80195ea598e725c3cbdfeaf2d0e08adbb","tarball":"https://registry.npmjs.org/@13bytes/redis-pubsub/-/redis-pubsub-4.1.3.tgz","fileCount":9,"integrity":"sha512-BnAASO3nTta6STRtAKVy5teyn7DGODfPJLcDejguq6TgzvYyzB6h1v+4nYdwl5kRVq+o0rhUbAqlgh0f1Owa7g==","signatures":[{"sig":"MEYCIQD2Fu1p4wSahexgGRZof2ZkJ/E+zR4If/wK0NarCIpT7QIhAIhSIJk4kf2xtJR6Ivdl2u0hwiNWHklNzFMgyTMi0fTY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42666},"main":"dist/index.js","_from":"file:13bytes-redis-pubsub-4.1.3.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","import":"./dist/*.mjs","require":"./dist/*.js"}},"scripts":{"test":"c8 --check-coverage --lines=100 --statements=100 --branches=97 --functions=95 --include=src --exclude=src/promise.ts ava","release":"changeset publish","test:watch":"ava --watch","test:watch:coverage":"bob-watch -w src test package.json -c \"pnpm test\""},"_npmUser":{"name":"13bytes","email":"coding@13bytes.de"},"_resolved":"C:\\Users\\louis\\AppData\\Local\\Temp\\0c55e38fee7c3c31e4a6ddd86958cdf1\\13bytes-redis-pubsub-4.1.3.tgz","_integrity":"sha512-BnAASO3nTta6STRtAKVy5teyn7DGODfPJLcDejguq6TgzvYyzB6h1v+4nYdwl5kRVq+o0rhUbAqlgh0f1Owa7g==","repository":{"url":"git+https://github.com/13bytes/redis-pubsub.git","type":"git"},"_npmVersion":"10.8.3","description":"Full type-safe Redis PubSub with Zod","directories":{},"_nodeVersion":"22.4.1","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","ava":"^5.3.1","zod":"^3.21.4","pino":"^9.6.0","bob-ts":"^4.1.1","dotenv":"^16.3.1","bob-tsm":"^1.1.2","esbuild":"^0.25.0","fastify":"^4.19.2","graphql":"^16.7.1","ioredis":"^5.3.2","prettier":"^3.0.0","bob-watch":"^0.1.2","superjson":"^1.12.4","graphql-ez":"^0.16.0","graphql-ws":"^5.14.0","typescript":"^5.1.6","@types/node":"^20.4.2","@pothos/core":"^3.30.0","concurrently":"^8.2.0","@changesets/cli":"^2.26.2","wait-for-expect":"^3.0.2","@graphql-ez/client":"^0.6.1","@graphql-ez/fastify":"^0.12.0","@graphql-ez/fastify-testing":"^0.4.0","@graphql-ez/plugin-websockets":"^0.11.2"},"peerDependencies":{"zod":"^3.17.3","ioredis":"^5.0.5","superjson":"^1.12.4"},"peerDependenciesMeta":{"zod":{"optional":true},"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/redis-pubsub_4.1.3_1747591242600_0.6640065839692313","host":"s3://npm-registry-packages-npm-production"}},"4.1.4":{"name":"@13bytes/redis-pubsub","version":"4.1.4","keywords":["ioredis","redis","pubsub","graphql"],"author":{"name":"PabloSzx","email":"pablosaez1995@gmail.com"},"license":"MIT","_id":"@13bytes/redis-pubsub@4.1.4","maintainers":[{"name":"13bytes","email":"coding@13bytes.de"}],"homepage":"https://github.com/13bytes/redis-pubsub#readme","bugs":{"url":"https://github.com/13bytes/redis-pubsub/issues"},"dist":{"shasum":"6712214c734936cf67831adb4df5d4017f573178","tarball":"https://registry.npmjs.org/@13bytes/redis-pubsub/-/redis-pubsub-4.1.4.tgz","fileCount":9,"integrity":"sha512-QgGf5EjrTpFVgUN8r36pE+xHLZuzVCe5qMu6R+3i6iPXbalWo2G5oStim4lgEQ1kilnSbChocThzz7trHJXcWw==","signatures":[{"sig":"MEUCIEjmS5FY/rilnMDHnVcLkmfECpXvy4CTxUKaCi1EJDAeAiEA+HGzxB34j+6csrUs5U9HHdDh3zB8prd5lwkPJxevOZw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42665},"main":"dist/index.js","_from":"file:13bytes-redis-pubsub-4.1.4.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","import":"./dist/*.mjs","require":"./dist/*.js"}},"scripts":{"test":"c8 --check-coverage --lines=100 --statements=100 --branches=97 --functions=95 --include=src --exclude=src/promise.ts ava","release":"changeset publish","test:watch":"ava --watch","test:watch:coverage":"bob-watch -w src test package.json -c \"pnpm test\""},"_npmUser":{"name":"13bytes","email":"coding@13bytes.de"},"_resolved":"C:\\Users\\louis\\AppData\\Local\\Temp\\1bbbc533b02edd66df65a7a791645a2e\\13bytes-redis-pubsub-4.1.4.tgz","_integrity":"sha512-QgGf5EjrTpFVgUN8r36pE+xHLZuzVCe5qMu6R+3i6iPXbalWo2G5oStim4lgEQ1kilnSbChocThzz7trHJXcWw==","repository":{"url":"git+https://github.com/13bytes/redis-pubsub.git","type":"git"},"_npmVersion":"10.8.3","description":"Full type-safe Redis PubSub with Zod","directories":{},"_nodeVersion":"22.15.1","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","ava":"^6.3.0","zod":"^3.24.4","pino":"^9.6.0","bob-ts":"^4.1.1","dotenv":"^16.5.0","bob-tsm":"^1.1.2","esbuild":"^0.25.4","fastify":"^5.3.3","graphql":"^16.11.0","ioredis":"^5.6.1","prettier":"^3.5.3","bob-watch":"^0.1.2","superjson":"^2.2.2","graphql-ez":"^0.16.1","graphql-ws":"^6.0.5","typescript":"^5.8.3","@types/node":"^22.15.18","@pothos/core":"^4.6.2","concurrently":"^9.1.2","@changesets/cli":"^2.29.4","wait-for-expect":"^3.0.2","@graphql-ez/client":"^0.6.2","@graphql-ez/fastify":"^0.12.1","@graphql-ez/fastify-testing":"^0.4.1","@graphql-ez/plugin-websockets":"^0.11.3"},"peerDependencies":{"zod":"^3.17.3","ioredis":"^5.0.5","superjson":"^1.12.4"},"peerDependenciesMeta":{"zod":{"optional":true},"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/redis-pubsub_4.1.4_1747595220024_0.12595563809734078","host":"s3://npm-registry-packages-npm-production"}},"4.1.5":{"name":"@13bytes/redis-pubsub","version":"4.1.5","description":"Full type-safe Redis PubSub with Zod","keywords":["ioredis","redis","pubsub","graphql"],"repository":{"type":"git","url":"git+https://github.com/13bytes/redis-pubsub.git"},"license":"MIT","author":{"name":"PabloSzx","email":"pablosaez1995@gmail.com"},"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js","import":"./dist/index.mjs"},"./*":{"types":"./dist/*.d.ts","require":"./dist/*.js","import":"./dist/*.mjs"}},"main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","devDependencies":{"@changesets/cli":"^2.29.4","@graphql-ez/client":"^0.6.2","@graphql-ez/fastify":"^0.12.1","@graphql-ez/fastify-testing":"^0.4.1","@graphql-ez/plugin-websockets":"^0.11.3","@pothos/core":"^4.6.2","@types/node":"^22.15.18","ava":"^6.3.0","bob-ts":"^4.1.1","bob-tsm":"^1.1.2","bob-watch":"^0.1.2","c8":"^10.1.3","concurrently":"^9.1.2","dotenv":"^16.5.0","esbuild":"^0.25.4","fastify":"^5.3.3","graphql":"^16.11.0","graphql-ez":"^0.16.1","graphql-ws":"^6.0.5","ioredis":"^5.6.1","pino":"^9.6.0","prettier":"^3.5.3","superjson":"^2.2.2","typescript":"^5.8.3","wait-for-expect":"^3.0.2","zod":"^3.24.4"},"peerDependencies":{"ioredis":"^5.0.1","superjson":"^2.0.1","zod":"^3.0.1"},"peerDependenciesMeta":{"ioredis":{"optional":true},"zod":{"optional":true}},"scripts":{"release":"changeset publish","test":"c8 --check-coverage --lines=100 --statements=100 --branches=97 --functions=95 --include=src --exclude=src/promise.ts ava","test:watch":"ava --watch","test:watch:coverage":"bob-watch -w src test package.json -c \"pnpm test\""},"_id":"@13bytes/redis-pubsub@4.1.5","bugs":{"url":"https://github.com/13bytes/redis-pubsub/issues"},"homepage":"https://github.com/13bytes/redis-pubsub#readme","_integrity":"sha512-qyLkfStIvfCGARRcsm5b+92QAlnmo0dB4ndiljHXWchcYaCAzMY6Fepu6fwJ7wh1MUyJy6yB3LHm9QVpStIRVw==","_resolved":"C:\\Users\\louis\\AppData\\Local\\Temp\\16bb8a5315379fd1f13fac9da1fa396c\\13bytes-redis-pubsub-4.1.5.tgz","_from":"file:13bytes-redis-pubsub-4.1.5.tgz","_nodeVersion":"22.15.1","_npmVersion":"10.8.3","dist":{"integrity":"sha512-qyLkfStIvfCGARRcsm5b+92QAlnmo0dB4ndiljHXWchcYaCAzMY6Fepu6fwJ7wh1MUyJy6yB3LHm9QVpStIRVw==","shasum":"62f16d43ffcd8b933b79072f93ba1a1e6118cf28","tarball":"https://registry.npmjs.org/@13bytes/redis-pubsub/-/redis-pubsub-4.1.5.tgz","fileCount":9,"unpackedSize":42663,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDULmuzpNsM57UkzkNiXiO3BerTEe6hQ6T6hbtjmsXcoAIgf6NpSBVhDWCOz82k06xeA6gtQng9XAAcR1tS/dCuyYA="}]},"_npmUser":{"name":"13bytes","email":"coding@13bytes.de"},"directories":{},"maintainers":[{"name":"13bytes","email":"coding@13bytes.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/redis-pubsub_4.1.5_1747596865369_0.061734180601314526"},"_hasShrinkwrap":false}},"time":{"created":"2025-05-18T18:00:42.468Z","modified":"2025-05-18T19:34:25.792Z","4.1.3":"2025-05-18T18:00:42.832Z","4.1.4":"2025-05-18T19:07:00.234Z","4.1.5":"2025-05-18T19:34:25.628Z"},"bugs":{"url":"https://github.com/13bytes/redis-pubsub/issues"},"author":{"name":"PabloSzx","email":"pablosaez1995@gmail.com"},"license":"MIT","homepage":"https://github.com/13bytes/redis-pubsub#readme","keywords":["ioredis","redis","pubsub","graphql"],"repository":{"type":"git","url":"git+https://github.com/13bytes/redis-pubsub.git"},"description":"Full type-safe Redis PubSub with Zod","maintainers":[{"name":"13bytes","email":"coding@13bytes.de"}],"readme":"# redis-pubsub\n\nFull type-safe Redis PubSub system with async iterators\n\n## Features\n\n- [x] Type-safety with [Zod](https://github.com/colinhacks/zod)\n- [x] Out-of-the-box support for `Date`/`Map`/`Set`/`BigInt` serialization with [superjson](https://github.com/blitz-js/superjson)\n- [x] Full usage of Async Iterators\n- [x] Support for [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) / [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)\n- [x] Support for type-safe filtering using [Type Guards / Type predicates](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)\n- [x] Support for **Entity** + **Identifier** pattern subscriptions\n- [x] Support for Async [Zod Refines](https://zod.dev/?id=refine) and Async [Zod Transforms](https://zod.dev/?id=transform)\n- [x] [GraphQL](https://graphql.org/) API ready\n\n## Install\n\n```sh\npnpm add @soundxyz/redis-pubsub\n```\n\n```sh\nnpm install @soundxyz/redis-pubsub\n```\n\n```sh\nyarn add @soundxyz/redis-pubsub\n```\n\n> Peer dependencies\n\n```sh\npnpm add zod ioredis\n```\n\n```sh\nnpm install zod ioredis\n```\n\n```sh\nyarn add zod ioredis\n```\n\n## Usage\n\n### Create a Redis PubSub instance:\n\n```ts\nimport Redis from \"ioredis\";\nimport { z } from \"zod\";\n\nimport { RedisPubSub } from \"@soundxyz/redis-pubsub\";\n\nconst { createChannel } = RedisPubSub({\n  publisher: new Redis({\n    port: 6379,\n  }),\n  subscriber: new Redis({\n    port: 6379,\n  }),\n});\n```\n\n### Create a channel with any `Zod` schema and a unique `\"name\"` to be used as main trigger.\n\n```ts\nconst schema = z.object({\n  id: z.string(),\n  name: z.string(),\n});\n\nconst userChannel = createChannel({\n  name: \"User\",\n  schema,\n});\n\nconst nonLazyUserChannel = createChannel({\n  name: \"User\",\n  schema,\n  // By default the channels are lazily connected with redis\n  isLazy: false,\n});\n```\n\n### Subscribe and publish to the channel\n\n```ts\n// Using async iterators / async generators to subscribe\n(async () => {\n  for await (const user of userChannel.subscribe()) {\n    console.log(\"User\", {\n      id: user.id,\n      name: user.name,\n    });\n  }\n})();\n\n// You can explicitly wait until the channel is sucessfully connected with Redis\nawait userChannel.isReady();\n\n// Publish data into the channel\nawait userChannel.publish(\n  {\n    value: {\n      id: \"1\",\n      name: \"John\",\n    },\n  },\n  // You can also publish more than a single value\n  {\n    value: {\n      id: \"2\",\n      name: \"Peter\",\n    },\n  }\n);\n```\n\n### Filter based on the data\n\n```ts\n(async () => {\n  for await (const user of userChannel.subscribe({\n    filter(value) {\n      return value.id === \"1\";\n    },\n  })) {\n    console.log(\"User 1\", {\n      id: user.id,\n      name: user.name,\n    });\n  }\n})();\n\n// You can also use type predicates / type guards\n(async () => {\n  for await (const user of userChannel.subscribe({\n    filter(value): value is { id: \"1\"; name: string } {\n      return value.id === \"1\";\n    },\n  })) {\n    // typeof user.id == \"1\"\n    console.log(\"User 1\", {\n      id: user.id,\n      name: user.name,\n    });\n  }\n})();\n```\n\n### Use custom identifiers\n\nIt will create a separate redis channel for every identifier, concatenating `\"name\"` and `\"identifier\"`, for example, with `\"name\"`=`\"User\"` and `\"identifier\"` = `1`, the channel trigger name will be `\"User1\"`\n\n```ts\n(async () => {\n  for await (const user of userChannel.subscribe({\n    // number or string\n    identifier: 1,\n  })) {\n    console.log(\"User with identifier=1\", {\n      id: user.id,\n      name: user.name,\n    });\n  }\n})();\n\nawait userChannel.isReady({\n  // number or string\n  identifier: 1,\n});\n\nawait userChannel.publish({\n  value: {\n    id: \"1\",\n    name: \"John\",\n  },\n  identifier: 1,\n});\n```\n\n### Separate input from output\n\nYou can levarage [Zod Transforms](https://zod.dev/?id=transform) to be able to separate input types from the output types, and receive any custom class or output on your subscriptions.\n\n```ts\nclass CustomClass {\n  constructor(public name: string) {}\n}\n\nconst inputSchema = z.string();\nconst outputSchema = z.string().transform((input) => new CustomClass(input));\n\nconst channel = pubSub.createChannel({\n  name: \"separate-type\",\n  inputSchema,\n  outputSchema,\n});\n\nconst subscription = (async () => {\n  for await (const data of channel.subscribe()) {\n    return data;\n  }\n})();\n\nawait channel.isReady();\n\nawait channel.publish({\n  value: \"test\",\n});\n\nconst result = await subscription;\n\n// true\nconsole.log(result instanceof CustomClass);\n\n// true\nconsole.log(result.name === \"test\");\n```\n\n### Use AbortController / AbortSignal\n\nIf `isLazy` is **not** disabled, the last subscription to a channel will be automatically unsubscribed from **Redis**.\n\n```ts\nconst abortController = new AbortController();\nconst abortedSubscription = (() => {\n  for await (const data of userChannel.subscribe({\n    abortSignal: abortController.signal,\n  })) {\n    console.log({ data });\n  }\n})();\n\n// ...\n\nfirstSubscribeAbortController.abort();\n\nawait abortedSubscription;\n```\n\n### Unsubscribe specific identifiers\n\n```ts\nawait userChannel.unsubscribe(\n  {\n    identifier: 1,\n  },\n  // You can specify more than a single identifer at once\n  {\n    identifier: 2,\n  }\n);\n```\n\n### Unsubscribe an entire channel\n\n```ts\nawait userChannel.unsubscribeAll();\n```\n\n### Close the PubSub instance\n\n```ts\nconst pubSub = RedisPubSub({\n  publisher: new Redis({\n    port: 6379,\n  }),\n  subscriber: new Redis({\n    port: 6379,\n  }),\n});\n\n// ...\nawait pubSub.close();\n```\n","readmeFilename":"README.md"}