{"_id":"@afterrealism/dendri-client","_rev":"6-5ed78393ab5ae94c6a8d427055f1e8a0","name":"@afterrealism/dendri-client","dist-tags":{"latest":"2.6.0"},"versions":{"2.3.7":{"name":"@afterrealism/dendri-client","version":"2.3.7","keywords":["dendri","webrtc","p2p","rtc","signaling"],"license":"MIT","_id":"@afterrealism/dendri-client@2.3.7","maintainers":[{"name":"afterrealism","email":"contact@afterrealism.com"}],"homepage":"https://dendri.dev","bugs":{"url":"https://github.com/afterrealism/dendri-packages/issues"},"dist":{"shasum":"864c6e969ce76775876effbc97ebec985b3a61ce","tarball":"https://registry.npmjs.org/@afterrealism/dendri-client/-/dendri-client-2.3.7.tgz","fileCount":29,"integrity":"sha512-4fjKzuap3MWOPx9idkAHycuWK9WKvBYpk16mM457M8wgJqXrp1L8GFhBaB9w9hRqmOT5koQkXlqDqCvnoCxupw==","signatures":[{"sig":"MEUCIQDwM8R/ZN9IvN6Wg98KwmlLnBS8pCjDUuGq2+I7n0H+uQIgJU/i8oaD8vLnJc8nihUohzyCE/xzqQnoVx/2MlS7MpY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4802851},"main":"./dist/dendri.cjs","type":"module","types":"./dist/dendri.d.ts","module":"./dist/dendri.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/dendri.d.ts","default":"./dist/dendri.js"},"require":{"types":"./dist/dendri.d.cts","default":"./dist/dendri.cjs"}},"./store":{"import":{"types":"./dist/store.d.ts","default":"./dist/store.js"},"require":{"types":"./dist/store.d.cts","default":"./dist/store.cjs"}},"./msgpack":{"import":{"types":"./dist/serializer.msgpack.d.ts","default":"./dist/serializer.msgpack.js"},"require":{"types":"./dist/serializer.msgpack.d.cts","default":"./dist/serializer.msgpack.cjs"}}},"gitHead":"4deaac882a801b2e59f9eb7b46edd17a25447c5e","scripts":{"lint":"biome check src/ tests/","test":"vitest run","bench":"vitest bench","build":"tsup","clean":"rm -rf dist","format":"biome format --write src/ tests/","coverage":"vitest run --coverage","lint:fix":"biome check --write src/ tests/","validate":"publint && attw --pack --profile node16","typecheck":"tsc --noEmit","test:watch":"vitest","build:watch":"tsup --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"afterrealism","email":"contact@afterrealism.com"},"deprecated":"Upgrade to >=2.4.0","repository":{"url":"git+https://github.com/afterrealism/dendri-packages.git","type":"git","directory":"dendri-client"},"_npmVersion":"10.9.4","description":"Dendri client WebRTC P2P signaling library","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"eventemitter3":"^4.0.7","webrtc-adapter":"^9.0.0","@msgpack/msgpack":"^2.8.0","eventsource-parser":"^3.0.6","peerjs-js-binarypack":"^2.1.0"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.30.1","devDependencies":{"tsup":"^8.0.0","jsdom":"^25.0.1","vitest":"^3.0.0","publint":"^0.3.0","fast-check":"^4.6.0","typescript":"^5.7.0","mock-socket":"^9.0.0","@biomejs/biome":"^2.0.0","@vitest/coverage-v8":"^3.0.0","@arethetypeswrong/cli":"^0.17.0"},"_npmOperationalInternal":{"tmp":"tmp/dendri-client_2.3.7_1777706593905_0.5302905413377044","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@afterrealism/dendri-client","version":"2.4.0","keywords":["dendri","webrtc","p2p","rtc","signaling"],"license":"Apache-2.0","_id":"@afterrealism/dendri-client@2.4.0","maintainers":[{"name":"afterrealism","email":"contact@afterrealism.com"}],"homepage":"https://dendri.dev","bugs":{"url":"https://github.com/afterrealism/dendri-packages/issues"},"dist":{"shasum":"538c7d354c80650437a9556a94fa606b0fb5de48","tarball":"https://registry.npmjs.org/@afterrealism/dendri-client/-/dendri-client-2.4.0.tgz","fileCount":29,"integrity":"sha512-KLk/+Q+OBYYV0gw0bYWDL7tRPaXIa9QmomTJ37beedSqJENVSE/6o3+GLJGjcj+8tuP3Iw8WIWhHI6PULxW9Xw==","signatures":[{"sig":"MEUCIQCunDuAYWm8bwyndERkti2zMlKTigwtBky6z0PIVdaENQIgElWDmmRlY0DTWwa3iQCL5zfG+5Y1h7HKp+sUsvHgnZw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4917746},"main":"./dist/dendri.cjs","type":"module","types":"./dist/dendri.d.ts","unpkg":"./dist/dendri.min.global.js","module":"./dist/dendri.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/dendri.d.ts","default":"./dist/dendri.js"},"require":{"types":"./dist/dendri.d.cts","default":"./dist/dendri.cjs"}},"./store":{"import":{"types":"./dist/store.d.ts","default":"./dist/store.js"},"require":{"types":"./dist/store.d.cts","default":"./dist/store.cjs"}},"./msgpack":{"import":{"types":"./dist/serializer.msgpack.d.ts","default":"./dist/serializer.msgpack.js"},"require":{"types":"./dist/serializer.msgpack.d.cts","default":"./dist/serializer.msgpack.cjs"}}},"gitHead":"6f853e0372168d77c3337851537e82b8aae9d6fb","scripts":{"lint":"biome check src/ tests/","test":"vitest run","bench":"vitest bench","build":"tsup","clean":"rm -rf dist","format":"biome format --write src/ tests/","coverage":"vitest run --coverage","lint:fix":"biome check --write src/ tests/","validate":"publint && attw --pack --profile node16","typecheck":"tsc --noEmit","test:watch":"vitest","build:watch":"tsup --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"afterrealism","email":"contact@afterrealism.com"},"jsdelivr":"./dist/dendri.min.global.js","repository":{"url":"git+https://github.com/afterrealism/dendri-packages.git","type":"git","directory":"dendri-client"},"_npmVersion":"10.9.4","description":"Dendri client WebRTC P2P signaling library","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"eventemitter3":"^4.0.7","webrtc-adapter":"^9.0.0","@msgpack/msgpack":"^2.8.0","eventsource-parser":"^3.0.6","peerjs-js-binarypack":"^2.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.30.1","devDependencies":{"tsup":"^8.0.0","jsdom":"^25.0.1","vitest":"^3.0.0","publint":"^0.3.0","fast-check":"^4.6.0","typescript":"^5.7.0","mock-socket":"^9.0.0","@biomejs/biome":"^2.0.0","@vitest/coverage-v8":"^3.0.0","@arethetypeswrong/cli":"^0.17.0"},"_npmOperationalInternal":{"tmp":"tmp/dendri-client_2.4.0_1782989444923_0.5696679359612111","host":"s3://npm-registry-packages-npm-production"}},"2.5.0":{"name":"@afterrealism/dendri-client","version":"2.5.0","keywords":["dendri","webrtc","p2p","rtc","signaling"],"license":"Apache-2.0","_id":"@afterrealism/dendri-client@2.5.0","maintainers":[{"name":"afterrealism","email":"contact@afterrealism.com"}],"homepage":"https://dendri.dev","bugs":{"url":"https://github.com/afterrealism/dendri-packages/issues"},"dist":{"shasum":"07e6ac46f8e4885c3bc94e605fae8a66633de79b","tarball":"https://registry.npmjs.org/@afterrealism/dendri-client/-/dendri-client-2.5.0.tgz","fileCount":29,"integrity":"sha512-/oTv8L/Z9144w7mUioWO7tYSUFzUqO841+wKYRUaDRmse34hqqIhlUtneGfB4Wy0tcTvsMJZUSVntGGfcbXOAg==","signatures":[{"sig":"MEUCIQD/daukWNXQdzIzZgIgoxdanZM0Xj7Kx+AsIk2TQwslXQIgRj1eEoZEQoR8+cEttzn52KHhZ+xYBG8DavOT69QVuI8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4954003},"main":"./dist/dendri.cjs","type":"module","types":"./dist/dendri.d.ts","unpkg":"./dist/dendri.min.global.js","module":"./dist/dendri.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/dendri.d.ts","default":"./dist/dendri.js"},"require":{"types":"./dist/dendri.d.cts","default":"./dist/dendri.cjs"}},"./store":{"import":{"types":"./dist/store.d.ts","default":"./dist/store.js"},"require":{"types":"./dist/store.d.cts","default":"./dist/store.cjs"}},"./msgpack":{"import":{"types":"./dist/serializer.msgpack.d.ts","default":"./dist/serializer.msgpack.js"},"require":{"types":"./dist/serializer.msgpack.d.cts","default":"./dist/serializer.msgpack.cjs"}}},"gitHead":"6f853e0372168d77c3337851537e82b8aae9d6fb","scripts":{"lint":"biome check src/ tests/","test":"vitest run","bench":"vitest bench","build":"tsup","clean":"rm -rf dist","format":"biome format --write src/ tests/","coverage":"vitest run --coverage","lint:fix":"biome check --write src/ tests/","validate":"publint && attw --pack --profile node16","typecheck":"tsc --noEmit","test:watch":"vitest","build:watch":"tsup --watch","format:check":"biome format src/ tests/","prepublishOnly":"npm run build"},"_npmUser":{"name":"afterrealism","email":"contact@afterrealism.com"},"jsdelivr":"./dist/dendri.min.global.js","repository":{"url":"git+https://github.com/afterrealism/dendri-packages.git","type":"git","directory":"dendri-client"},"_npmVersion":"10.9.4","description":"Dendri client WebRTC P2P signaling library","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"eventemitter3":"^4.0.7","webrtc-adapter":"^9.0.0","@msgpack/msgpack":"^2.8.0","eventsource-parser":"^3.0.6","peerjs-js-binarypack":"^2.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.30.1","devDependencies":{"tsup":"^8.0.0","jsdom":"^25.0.1","vitest":"^3.0.0","publint":"^0.3.0","fast-check":"^4.6.0","typescript":"^5.7.0","mock-socket":"^9.0.0","@biomejs/biome":"^2.0.0","@vitest/coverage-v8":"^3.0.0","@arethetypeswrong/cli":"^0.17.0"},"_npmOperationalInternal":{"tmp":"tmp/dendri-client_2.5.0_1782994467185_0.11446788758519766","host":"s3://npm-registry-packages-npm-production"}},"2.6.0":{"name":"@afterrealism/dendri-client","version":"2.6.0","type":"module","packageManager":"pnpm@10.30.1","description":"Dendri client WebRTC P2P signaling library","keywords":["dendri","webrtc","p2p","rtc","signaling"],"homepage":"https://dendri.dev","bugs":{"url":"https://github.com/afterrealism/dendri-packages/issues"},"repository":{"type":"git","url":"git+https://github.com/afterrealism/dendri-packages.git","directory":"dendri-client"},"license":"Apache-2.0","main":"./dist/dendri.cjs","module":"./dist/dendri.js","types":"./dist/dendri.d.ts","exports":{".":{"import":{"types":"./dist/dendri.d.ts","default":"./dist/dendri.js"},"require":{"types":"./dist/dendri.d.cts","default":"./dist/dendri.cjs"}},"./store":{"import":{"types":"./dist/store.d.ts","default":"./dist/store.js"},"require":{"types":"./dist/store.d.cts","default":"./dist/store.cjs"}},"./msgpack":{"import":{"types":"./dist/serializer.msgpack.d.ts","default":"./dist/serializer.msgpack.js"},"require":{"types":"./dist/serializer.msgpack.d.cts","default":"./dist/serializer.msgpack.cjs"}},"./react":{"import":{"types":"./dist/react.d.ts","default":"./dist/react.js"},"require":{"types":"./dist/react.d.cts","default":"./dist/react.cjs"}},"./vue":{"import":{"types":"./dist/vue.d.ts","default":"./dist/vue.js"},"require":{"types":"./dist/vue.d.cts","default":"./dist/vue.cjs"}},"./svelte":{"import":{"types":"./dist/svelte.d.ts","default":"./dist/svelte.js"},"require":{"types":"./dist/svelte.d.cts","default":"./dist/svelte.cjs"}}},"sideEffects":false,"publishConfig":{"access":"public"},"unpkg":"./dist/dendri.min.global.js","jsdelivr":"./dist/dendri.min.global.js","engines":{"node":">=18"},"scripts":{"build":"tsup","build:watch":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","bench":"vitest bench","coverage":"vitest run --coverage","lint":"biome check src/ tests/","lint:fix":"biome check --write src/ tests/","format":"biome format --write src/ tests/","format:check":"biome format src/ tests/","validate":"publint && attw --pack --profile node16","prepublishOnly":"npm run build","clean":"rm -rf dist"},"peerDependencies":{"react":">=18","vue":">=3.3"},"peerDependenciesMeta":{"react":{"optional":true},"vue":{"optional":true}},"dependencies":{"@msgpack/msgpack":"^2.8.0","eventemitter3":"^4.0.7","eventsource-parser":"^3.0.6","peerjs-js-binarypack":"^2.1.0","webrtc-adapter":"^9.0.0"},"devDependencies":{"@arethetypeswrong/cli":"^0.17.0","@biomejs/biome":"^2.0.0","@types/react":"^19.2.17","@vitest/coverage-v8":"^3.0.0","fast-check":"^4.6.0","jsdom":"^25.0.1","mock-socket":"^9.0.0","publint":"^0.3.0","react":"^19.2.7","react-dom":"^19.2.7","tsup":"^8.0.0","typescript":"^5.7.0","vitest":"^3.0.0","vue":"^3.5.39"},"_id":"@afterrealism/dendri-client@2.6.0","gitHead":"6f853e0372168d77c3337851537e82b8aae9d6fb","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-2Mk6OWy2JwTE3g/v7WOdWIjqae5cQNlAsZzlVKX1iydvtzKhTKy9SUB+QkbKU99ZeHtB7J0gkH7HZ/PFBLvtkQ==","shasum":"b093eec4d63ca9b9627da4ad9dadc8bfa7e9f4c8","tarball":"https://registry.npmjs.org/@afterrealism/dendri-client/-/dendri-client-2.6.0.tgz","fileCount":49,"unpackedSize":4972187,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCw+2437USrdoPR4FdS4FUZ48CDKG1q8Os0zzYIru48OAIhANYbTBgNd2egPj3SVYm4MH+hsaEJGbjrUvdAIbDK4jaY"}]},"_npmUser":{"name":"afterrealism","email":"contact@afterrealism.com"},"directories":{},"maintainers":[{"name":"afterrealism","email":"contact@afterrealism.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dendri-client_2.6.0_1782994741145_0.7995331415250422"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-02T07:23:13.775Z","modified":"2026-07-02T12:19:01.435Z","2.3.7":"2026-05-02T07:23:14.078Z","2.4.0":"2026-07-02T10:50:45.138Z","2.5.0":"2026-07-02T12:14:27.379Z","2.6.0":"2026-07-02T12:19:01.334Z"},"bugs":{"url":"https://github.com/afterrealism/dendri-packages/issues"},"license":"Apache-2.0","homepage":"https://dendri.dev","keywords":["dendri","webrtc","p2p","rtc","signaling"],"repository":{"type":"git","url":"git+https://github.com/afterrealism/dendri-packages.git","directory":"dendri-client"},"description":"Dendri client WebRTC P2P signaling library","maintainers":[{"name":"afterrealism","email":"contact@afterrealism.com"}],"readme":"# @afterrealism/dendri-client\n\nFramework-neutral TypeScript client SDK for Dendri WebRTC P2P signaling. Works in the browser and Node.js.\n\n## Install\n\n```bash\nnpm install @afterrealism/dendri-client\n```\n\n## Connect To Your Server\n\nPass the signaling server your app should use as a single URL:\n\n```ts\nimport { Dendri } from \"@afterrealism/dendri-client\";\n\nconst peer = new Dendri({\n\turl: \"wss://signal.example.com\",\n\tapiKey: \"your-api-key\", // only needed for hosted / multi-tenant deployments\n});\n```\n\n`url` is shorthand for `host`/`port`/`secure`/`path` — pass those individually if you\nprefer; explicitly passed fields win over url-derived ones.\n\nYou can also pass an explicit peer id as the first argument: `new Dendri(\"my-peer-id\", { ... })`.\n\nLocal development usually looks like:\n\n```ts\nconst peer = new Dendri({ url: \"ws://localhost:9876\" });\n```\n\n## Quick Start\n\n```ts\n// Connect to another peer\nconst conn = peer.connect(\"other-peer-id\");\n\nconn.on(\"open\", () => {\n\tconn.send(\"hello\");\n});\n\nconn.on(\"data\", (data) => {\n\tconsole.log(\"Received:\", data);\n});\n\n// Receive connections\npeer.on(\"connection\", (conn) => {\n\tconn.on(\"data\", (data) => {\n\t\tconsole.log(\"Received:\", data);\n\t});\n});\n```\n\n## Room Store\n\nUse `createDendriStore()` when integrating with UI frameworks:\n\n```ts\nimport { createDendriStore } from \"@afterrealism/dendri-client\";\n\nconst store = createDendriStore({ url: \"wss://signal.example.com\" });\n\nconst unsubscribe = store.subscribe(() => {\n\tconsole.log(store.connectionState, store.peers);\n});\n\nstore.join(\"my-room\");\n```\n\n## Framework Adapters\n\nReady-made bindings ship as subpath exports (`react` and `vue` are optional peer\ndependencies; the Svelte adapter is dependency-free):\n\n```tsx\n// React — re-renders on store changes (useSyncExternalStore under the hood)\nimport { useDendriStore } from \"@afterrealism/dendri-client/react\";\nconst { connectionState, peers } = useDendriStore(store);\n```\n\n```ts\n// Vue — shallowRef that tracks the store, auto-unsubscribes with the component\nimport { useDendriStore } from \"@afterrealism/dendri-client/vue\";\nconst snapshot = useDendriStore(store); // snapshot.value.connectionState\n```\n\n```svelte\n<!-- Svelte — standard store contract, works with $ auto-subscription (Svelte 4 + 5) -->\n<script>\n\timport { toSvelteStore } from \"@afterrealism/dendri-client/svelte\";\n\tconst snapshot = toSvelteStore(store);\n</script>\n{$snapshot.connectionState}\n```\n\nAny other framework can wrap `store.subscribe` and `store.getSnapshot()` the same way.\n\n## Features\n\n- **P2P Data Channels** - Send data directly between browsers\n- **Media Calls** - Video and audio streaming via WebRTC\n- **Rooms** - Group peers into rooms with automatic peer discovery\n- **Presence** - Track online status and custom metadata across peers\n- **Topics** - Pub/sub messaging with named topics\n- **RPC** - Request/response pattern over data channels\n- **Host Migration** - Automatic leader election when the host disconnects\n- **Hybrid Connections** - Seamless fallback between data channel and relay\n- **Relay Encryption** - End-to-end encryption for relayed messages\n- **Store** - Shared synchronized state across peers\n- **Auto Reconnect** - Exponential backoff with jitter\n- **MsgPack Serializer** - Binary serialization for smaller payloads\n\n## Multi-Tab Reconnection\n\nWhen two browser tabs use the same `peer_id` to connect to the same signaling server:\n\n- **Same peer_id, same token** — Treated as a reconnection. The server replaces the old WebSocket with the new one. Room memberships and queued messages are preserved. The first tab eventually receives a disconnect event via heartbeat timeout.\n- **Same peer_id, different token** — Rejected with an `ID_TAKEN` error. Prevents accidental or malicious ID hijacking.\n\nFor multi-tab apps, use `BroadcastChannel` or `localStorage` to coordinate a single Dendri connection across tabs, or let each tab create its own instance with distinct `peer_id` values and communicate through rooms.\n\n## MsgPack Serializer\n\nFor binary serialization instead of JSON:\n\n```ts\nimport { MsgPackDendri } from \"@afterrealism/dendri-client\";\n\nconst peer = new MsgPackDendri(\"my-peer-id\", {\n\thost: \"signal.example.com\",\n\tsecure: true,\n});\n```\n\n## SSR\n\nImporting the package is safe in SSR code, but creating peers and joining rooms should happen only in browser/client lifecycle code because WebRTC and WebSocket connections are browser/runtime side effects.\n\n## Browser Global Build\n\nThe package ships browser global (IIFE) assets for CDN/script-tag workflows:\n\n```html\n<script src=\"https://unpkg.com/@afterrealism/dendri-client\"></script>\n<script>\n\tconst peer = new dendri.Dendri({ host: \"signal.example.com\", port: 443, secure: true, path: \"/\" });\n</script>\n```\n\n- `dist/dendri.min.global.js` (minified, served by unpkg/jsdelivr by default)\n- `dist/dendri.browser.global.js` (unminified, for debugging)\n\nThe global build exposes `Dendri` and `util` on `window.dendri`. Prefer npm imports for framework apps.\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}