{"_id":"@ws-kit/pubsub","_rev":"7-b79636282a9d7e0d3bbae79571a6703c","name":"@ws-kit/pubsub","dist-tags":{"latest":"0.10.2"},"versions":{"0.0.0":{"name":"@ws-kit/pubsub","version":"0.0.0","keywords":["websocket","pubsub","pub-sub","adapter","plugin","broadcasting","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.0.0","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"71bd5f3ea1c3229dbc99de8b766c91681a0a2335","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.0.0.tgz","fileCount":44,"integrity":"sha512-9a7KEAo1ZLFwLT7lMnHvXzdLmmCqUUiTJn3Ke1UMTJzsPdcI7nBm+5BfIWhEEz60fe7cmCRJI86RqjJbvZC8OQ==","signatures":[{"sig":"MEQCIHUqbixuyvGtERcMKhBF2DhR0US6NTj/oAdNwm3ZiUfdAiAV1vi4bTTov3P3Fve5bMUBConBdB4R7ggKlS0IfQ8p4Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":262952},"main":"./index.js","type":"module","types":"./index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./index.d.ts","default":"./index.js"},"./internal":{"types":"./internal.d.ts","default":"./internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"6d3f6cf7565bad547d6efc388e6840cd54e50b1c","_npmUser":{"name":"kriasoft-bot","email":"kriasoft@gmail.com"},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub plugin for WS-Kit with in-memory adapter","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"workspace:^","@ws-kit/core":"workspace:^","@ws-kit/memory":"workspace:^"},"peerDependencies":{"@ws-kit/core":"^0.0.1"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.0.0_1764541789183_0.3842572383428","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@ws-kit/pubsub","version":"0.8.0","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.8.0","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"6c02b24a03e60a1a90cb97f5a75ac288c4054afb","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.8.0.tgz","fileCount":44,"integrity":"sha512-1+IGirQd2uauVKLQlelAwAqDzDV3ZHKlqLxvcCosRYOgDBkqQSDRs8biQoJwBbk7MrpcrhcPRuSsAwL5JmLe5Q==","signatures":[{"sig":"MEYCIQDQGvTFwtFfOcErdYq9YrZrU5oLGkmFfPo+DxZDhUiFRAIhANtBwzg8J6xTOZw2vh2MwT0auFxxzqhFgKVoWrdvIfq4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":263090},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"b1d18ca9b170c34c784ff4b4d5b03d44e13148b3","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"^0.8.0","@ws-kit/core":"^0.8.0","@ws-kit/memory":"^0.8.0"},"peerDependencies":{"@ws-kit/core":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.8.0_1764547610393_0.1501047197005203","host":"s3://npm-registry-packages-npm-production"}},"0.8.1":{"name":"@ws-kit/pubsub","version":"0.8.1","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.8.1","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"73826469b4e74bbb705ca26b485f1d815177d796","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.8.1.tgz","fileCount":44,"integrity":"sha512-iV922a9iYADmF5cALNNlhiTU3RgCni4TCIg1tNyhl/gakRzE5q5rpsarrEmaYwapf0/dUxpmPChtmR8EM1TYIA==","signatures":[{"sig":"MEYCIQD/PAfmSkLmksZHaDy8p+h08b3ynSGKghcOGmsLQXpQUQIhAKBA223b1ugWW7aYazKCiDo4gCyT/bwVJXlrp1WZkDee","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.8.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":264646},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"7ba20c305112106486d082bd4241806667c98b8d","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"^0.8.1","@ws-kit/core":"^0.8.1","@ws-kit/memory":"^0.8.0"},"peerDependencies":{"@ws-kit/core":"^0.8.1"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.8.1_1764550070478_0.9354042130925104","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@ws-kit/pubsub","version":"0.9.0","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.9.0","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"a82050f60d74e2a4759c473c2557cbab2189ce89","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.9.0.tgz","fileCount":44,"integrity":"sha512-GvymXPFtR9nUyYkC7oP/DfGslMnvkG7Q44doHzEgOudU0ZUCyxjnKX6LBwCxQ8VsLOae1YDIA2RX8O5MaKtCvA==","signatures":[{"sig":"MEUCIQCZuCU7Gt6J4UOlsuVmWTf0RXMlnYzkdETKtgpW87MAjQIgSDrpm2+6lNgL+sZY0RPAEAa10h1lC390PanI/VlhLmc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":265185},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"7f41d1c07b55142e2f575372066b7db0a6cc5362","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"^0.9.0","@ws-kit/core":"^0.9.0","@ws-kit/memory":"^0.9.0"},"peerDependencies":{"@ws-kit/core":"^0.9.0"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.9.0_1764885621373_0.03487013846120934","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"@ws-kit/pubsub","version":"0.10.0","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.10.0","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"07ddcbffd1910484f45ee8fb00eb5c60fafa5280","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.10.0.tgz","fileCount":44,"integrity":"sha512-cHs01KMvyi2EvSEtEZlpcB7TiQFyJiKHEWzV5xc4tqoZuETH4ymHTJ3PlGOW8JNi4JAlq4vAbb7Sa54nr1ak2g==","signatures":[{"sig":"MEUCIExnVe73XuH5QUJsR7Jm0EsHejdUkFMQKeTnxLezriPWAiEAgF3pPoPFHCyTIUmJM8mwwwbRBHVq/AgCVaKdv9jNqBg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":265269},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"b0a32263ddc6a0b9a54084519304f0bbb478538f","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"^0.10.0","@ws-kit/core":"^0.10.0","@ws-kit/memory":"^0.10.0"},"peerDependencies":{"@ws-kit/core":"^0.10.0"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.10.0_1764943200778_0.5463815319063954","host":"s3://npm-registry-packages-npm-production"}},"0.10.1":{"name":"@ws-kit/pubsub","version":"0.10.1","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"author":{"name":"Konstantin Tarkus"},"license":"MIT","_id":"@ws-kit/pubsub@0.10.1","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"dist":{"shasum":"f4bd444cd74e3291ef0bba19b1888fa695d12d90","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.10.1.tgz","fileCount":44,"integrity":"sha512-GSrAnhCgdYEEYoRDGe6jU+iz8cL5Utw3txEknSIyXq9SKniXrMO5zPNbsck0tnscoD3BIGqY34dCx9s1n4v6sw==","signatures":[{"sig":"MEYCIQC6if/eIFV0iP19sXZkWKFWk66BLPW+DytLq7FvaqcF3AIhAOOomc7RejYEUJ91fMGziOqq980k5CzAYE2awVCPAnzP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.10.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":265850},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"funding":{"url":"https://github.com/sponsors/koistya","type":"github"},"gitHead":"ae81d17456776d492cd462c510166ab145176e8a","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"repository":{"url":"git+https://github.com/kriasoft/ws-kit.git","type":"git","directory":"packages/pubsub"},"_npmVersion":"11.6.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","directories":{},"sideEffects":false,"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.3","@ws-kit/zod":"^0.10.1","@ws-kit/core":"^0.10.0","@ws-kit/memory":"^0.10.0"},"peerDependencies":{"@ws-kit/core":"^0.10.0"},"_npmOperationalInternal":{"tmp":"tmp/pubsub_0.10.1_1764946397299_0.349240180024492","host":"s3://npm-registry-packages-npm-production"}},"0.10.2":{"name":"@ws-kit/pubsub","version":"0.10.2","description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","type":"module","sideEffects":false,"license":"MIT","author":{"name":"Konstantin Tarkus"},"homepage":"https://kriasoft.com/ws-kit/","bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"repository":{"type":"git","url":"git+https://github.com/kriasoft/ws-kit.git","directory":"packages/pubsub"},"funding":{"type":"github","url":"https://github.com/sponsors/koistya"},"keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./internal":{"types":"./dist/internal.d.ts","default":"./dist/internal.js"}},"main":"./dist/index.js","types":"./dist/index.d.ts","peerDependencies":{"@ws-kit/core":"^0.10.1"},"devDependencies":{"@types/bun":"^1.3.6","@ws-kit/core":"^0.10.1","@ws-kit/memory":"^0.10.1","@ws-kit/zod":"^0.10.2"},"engines":{"node":">=22"},"publishConfig":{"access":"public"},"gitHead":"aaa14caf31960392a54c7475fbf458811407a7f9","_id":"@ws-kit/pubsub@0.10.2","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-IeItJ2eVEflZo6xP8zLD898sVTwWgVqvEkkExsKW38UYNUjv+tM3WhdKozzWiZwnOZ5eSEKb7xL6y8ESnZSFNQ==","shasum":"7e4834f2f9e9317dc73de09603dce054014bece6","tarball":"https://registry.npmjs.org/@ws-kit/pubsub/-/pubsub-0.10.2.tgz","fileCount":44,"unpackedSize":276221,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ws-kit%2fpubsub@0.10.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH2FUeM08XnaYcPwSEcPAn+/+Xxu+D803WtOyJZso9qpAiB95MJuIHCyx1+JTE+A08kG4zx7CFv3BRTetivxit485w=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a7e27c6f-4377-4b05-a5ab-4b2f9fade4cd"}},"directories":{},"maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pubsub_0.10.2_1768870685208_0.21414471200752794"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-30T22:29:49.110Z","modified":"2026-01-20T00:58:05.684Z","0.0.0":"2025-11-30T22:29:49.414Z","0.8.0":"2025-12-01T00:06:50.629Z","0.8.1":"2025-12-01T00:47:50.677Z","0.9.0":"2025-12-04T22:00:21.543Z","0.10.0":"2025-12-05T14:00:00.948Z","0.10.1":"2025-12-05T14:53:17.471Z","0.10.2":"2026-01-20T00:58:05.358Z"},"bugs":{"url":"https://github.com/kriasoft/ws-kit/issues"},"author":{"name":"Konstantin Tarkus"},"license":"MIT","homepage":"https://kriasoft.com/ws-kit/","keywords":["websocket","pubsub","pub-sub","middleware","policy","topics","authorization","type-safe","typescript"],"repository":{"type":"git","url":"git+https://github.com/kriasoft/ws-kit.git","directory":"packages/pubsub"},"description":"Pub/Sub policy middleware for WS-Kit (topic normalization, authorization) plus shared pub/sub primitives","maintainers":[{"name":"koistya","email":"hello@tarkus.me"},{"name":"kriasoft-bot","email":"kriasoft@gmail.com"}],"readme":"# @ws-kit/pubsub\n\nPolicy enforcement middleware for pub/sub operations (topic normalization, authorization).\n\n**Note:** The core `withPubSub()` plugin has moved to `@ws-kit/plugins`. This package provides optional policy middleware for controlling subscribe/publish/unsubscribe operations.\n\n## Installation\n\n```bash\nbun add @ws-kit/pubsub\n```\n\n## Quick Start\n\n```typescript\nimport { createRouter, message, withZod } from \"@ws-kit/zod\";\nimport { withPubSub } from \"@ws-kit/plugins\";\nimport { memoryPubSub } from \"@ws-kit/memory\";\nimport { usePubSub } from \"@ws-kit/pubsub\";\nimport { z } from \"zod\";\n\ndeclare module \"@ws-kit/core\" {\n  interface ConnectionData {\n    userId?: string;\n  }\n}\n\nconst Notify = message(\"NOTIFY\", { text: z.string() });\n\nconst router = createRouter()\n  .plugin(withZod())\n  .plugin(withPubSub({ adapter: memoryPubSub() }))\n  .use(\n    usePubSub({\n      hooks: {\n        normalizeTopic: (topic) => topic.toLowerCase(),\n        authorize: async (action, topic, ctx) => {\n          if (action === \"subscribe\" && !ctx.data?.userId) {\n            throw new Error(\"Unauthorized\");\n          }\n        },\n      },\n    }),\n  );\n\nrouter.on(Notify, async (ctx) => {\n  await ctx.topics.subscribe(\"room:lobby\");\n  await ctx.publish(\"room:lobby\", Notify, { text: \"Hello\" });\n});\n```\n\n## Architecture\n\n### File Structure\n\n```bash\nsrc/\n├─ core/                   # Core pub/sub primitives (internal)\n│  ├─ topics.ts            # OptimisticTopics, createTopics(): per-connection subscription state\n│  ├─ error.ts             # PubSubError, AbortError, error codes\n│  └─ constants.ts         # DEFAULT_TOPIC_PATTERN, DEFAULT_TOPIC_MAX_LENGTH\n├─ adapters/\n│  └─ memory.ts            # In-memory adapter implementation (use via @ws-kit/memory)\n├─ index.ts                # Public exports (plugin, adapters, middleware, types)\n├─ plugin.ts               # withPubSub() plugin factory\n├─ middleware.ts           # usePubSub() policy enforcement middleware\n├─ types.ts                # Type definitions (Topics, PublishOptions, hooks)\n└─ internal.ts             # Internal re-exports (OptimisticTopics, createTopics, etc. for core)\n```\n\n### Design Principles\n\n- **`core/`** — Internal implementation details not part of public API. Used by `@ws-kit/core` router for managing subscription state.\n- **`adapters/`** — Extensible location for adapter implementations (memory, Redis, Cloudflare, etc.).\n- **Public API** — `plugin.ts`, `middleware.ts`, and user-facing types exported from `index.ts`.\n- **Internal API** — Implementation classes and core primitives exported from `internal.ts` for `@ws-kit/core` integration only.\n\n### Components\n\n#### Plugin: `withPubSub({ adapter })`\n\nThe core `withPubSub()` plugin is now in `@ws-kit/plugins`. It adds pub/sub capability to the router:\n\n```typescript\nimport { withPubSub } from \"@ws-kit/plugins\";\nimport { memoryPubSub } from \"@ws-kit/memory\";\n\nconst router = createRouter().plugin(withPubSub({ adapter: memoryPubSub() }));\n```\n\nProvides:\n\n- `router.publish(topic, schema, payload, opts)` — Broadcast to topic subscribers\n- `ctx.publish(topic, schema, payload, opts)` — Publish from handler\n- `ctx.topics` — Per-connection subscription operations\n\nSee `@ws-kit/plugins` for the complete plugin API.\n\n#### Middleware: `usePubSub(options)`\n\nOptional middleware for topic normalization and authorization.\n\n```typescript\n.use(usePubSub({\n  hooks: {\n    normalizeTopic: (topic, ctx) => {\n      // Apply transformations (e.g., lowercase, validation)\n      return topic.toLowerCase();\n    },\n    authorize: async (action, topic, ctx) => {\n      // Check permissions before subscribe/unsubscribe/publish\n      const allowed = await checkAccess(ctx.data.userId, action, topic);\n      if (!allowed) throw new Error(\"Access denied\");\n    },\n  },\n}))\n```\n\nSupported actions: `\"subscribe\"`, `\"unsubscribe\"`, `\"publish\"`\n\n#### Adapter: In-Memory (from `@ws-kit/memory`)\n\nIn-memory pub/sub adapter using `Map<topic, Set<clientId>>`. Available from memory package for zero-config development:\n\n```typescript\nimport { memoryPubSub } from \"@ws-kit/memory\";\n\nconst adapter = memoryPubSub();\n```\n\nFor production, use distributed adapters like `redisPubSub()` from `@ws-kit/redis` or `cloudflarePubSub()` from `@ws-kit/cloudflare`.\n\nAll adapters implement the same `PubSubAdapter` interface, so swapping is seamless.\n\n## API Reference\n\n### Router Methods\n\n```typescript\n// Publish to a topic (process-wide)\nawait router.publish(topic, schema, payload, options?);\n\n// Query subscriptions\nrouter.subscriptions.list();     // → string[]\nrouter.subscriptions.has(topic); // → boolean\n```\n\n### Context Methods\n\n```typescript\n// Publish to a topic\nawait ctx.publish(topic, schema, payload, options?);\n\n// Subscribe/unsubscribe\nawait ctx.topics.subscribe(topic, options?);\nawait ctx.topics.unsubscribe(topic, options?);\n\n// Batch operations\nawait ctx.topics.subscribeMany(topics, options?);\nawait ctx.topics.unsubscribeMany(topics, options?);\nawait ctx.topics.set(topics, options?);\nawait ctx.topics.update(mutator, options?);\nawait ctx.topics.clear(options?);\n\n// Lifecycle and verification\nctx.topics.localStatus(topic);  // Check settlement status\nawait ctx.topics.settle(topic?, options?);  // Wait for operations to settle\nawait ctx.topics.verify(topic, options?);   // Verify adapter truth\n\n// Query subscriptions (ReadonlySet<string>)\nctx.topics.has(topic);\nctx.topics.size;\nfor (const topic of ctx.topics) { ... }\n```\n\n### Publish Options\n\n```typescript\ninterface PublishOptions {\n  partitionKey?: string; // Routing hint for distributed adapters\n  meta?: Record<string, unknown>; // Custom metadata\n  excludeSelf?: boolean; // Exclude sender (memory/Redis only; Bun returns UNSUPPORTED)\n}\n```\n\n## Features\n\n- **Lightweight**: Minimal in-memory implementation using Map and Set\n- **Type-safe**: Full TypeScript support with capability gating\n- **Plugin-based**: Integrates seamlessly with WS-Kit's plugin system\n- **Functional**: Plain factory function design, consistent with `createRouter()` and `message()`\n- **Flexible**: Optional policy middleware for normalization and authorization\n- **Portable**: Identical semantics across adapters\n\n## License\n\nMIT\n","readmeFilename":"README.md"}