{"_id":"@adamthedeveloper/pretzel-sdk","_rev":"8-ee70ac6fcf66f8c02311578d854121d2","name":"@adamthedeveloper/pretzel-sdk","dist-tags":{"latest":"0.1.10"},"versions":{"0.1.2":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.2","_id":"@adamthedeveloper/pretzel-sdk@0.1.2","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"000df3b593e1fb35528eaa277af5f9ac69ad0bb0","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.2.tgz","fileCount":6,"integrity":"sha512-Q8Wl9f7ghKm4siSDQwYkNUF2/hof47ZfSV1dl/w/x4pnhvSQVEjXRA1QDon2CxtA8fABYGiD0L63Q74BUAuTcQ==","signatures":[{"sig":"MEUCICp22AUCzjy/1eMUObba3V8S3fyQ2F/Df6l+lc0F/lkvAiEA0iibou1CnxkusuLAlV+/xz3DbF76SNpHPNc+CsKAE8c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21956},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"41e2e3afe14bd45c90875e258d9b13d06135e111","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"TypeScript SDK for Pretzel presence services.","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.2_1777446841363_0.6067823278871807","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.3","_id":"@adamthedeveloper/pretzel-sdk@0.1.3","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"b127daa56886bdc39b265c58d6afd80e4edb6c03","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.3.tgz","fileCount":6,"integrity":"sha512-QQYyB7hZ6naBb9hCsk4929aHGHQbwKrd0RKx1hqHg+RTXPPgDUXex41Bg7qXdXrtD2QKlFRGuKI6MN/TJ05OFg==","signatures":[{"sig":"MEUCIQCE5/Albn8c0A2r19Y+C9BQLS6XmO+vhFhWmsReB2FwZwIgPajhv9TiZnBPrgJqR7/CAtneKdi/xmLzE/QnsBbJoWA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27851},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"a268f1c76bf9e96ee5a9744112a73cd1af5ba7e2","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.3_1777450010385_0.5132306942841112","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.4","keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"_id":"@adamthedeveloper/pretzel-sdk@0.1.4","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"f686c2127ee4a1d7453347b600bfc4a1b2891cbd","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.4.tgz","fileCount":6,"integrity":"sha512-TMw0eiIHjibQKYc23wpLx9oAwxoWyeJ3QLLRUt4z6ux2KsFcNq3wmw++vWfDiPGTMqt8Mbo/UOoRrKrqQtWHsw==","signatures":[{"sig":"MEYCIQCm5fQm83z2GzXwLv4dGBb96KOgW6vuGnuI9xs0Mdpm3AIhAPw5v13zyJ9H1oYXeb93xUw1iWwc2b2iNUnucfFk/uGq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27978},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"a268f1c76bf9e96ee5a9744112a73cd1af5ba7e2","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.4_1777450125691_0.8652053255742964","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.5","keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"_id":"@adamthedeveloper/pretzel-sdk@0.1.5","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"fa26290700e964402371174a20c9d78799cb59ac","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.5.tgz","fileCount":6,"integrity":"sha512-EJKyevOqMgw8bEIZoG9uUem61pjn/O2wyqpxhN9oPqBzQ3MxFmMqvJV2Ak//q+jNzbCI0jeY/xi18XHD0Hr2JA==","signatures":[{"sig":"MEUCIHmn4fJ+EFa35/ULo0bnTu0GlxQZvByVuCmYJprBrO+oAiEAgu16jK5IzFSKJ6FA3gTLPhVnpgNTOQKaB2THao0ZdDw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26984},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"fa9b6b9d939611042916cad79c9a58fbdf86deed","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.5_1777455490443_0.8154142775161859","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.7","keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"_id":"@adamthedeveloper/pretzel-sdk@0.1.7","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"3774e9e833e1d11bf3cf524dde0a33f38222140e","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.7.tgz","fileCount":6,"integrity":"sha512-Zw1JOANIx6NZsjrfdKQWAu8xMdzx/0u1ocmd1mxnJA8kNJ/Bc5xiaU0D5mkqN4I2zDhucaxnz6WB2fL60lzbiw==","signatures":[{"sig":"MEUCIHwBgLBVIRPOQ6rjpO2s77lBotuDEEPpvVeV3K40rPtZAiEA5i4/j2YLU+sDYiXK9BRqYb2iEE49P8P9sjXOuH5Ez0c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26984},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"30e79b1fbb328a3fd55f607a5af92a096fe1a327","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.7_1777457606955_0.8021989886956717","host":"s3://npm-registry-packages-npm-production"}},"0.1.8":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.8","keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"_id":"@adamthedeveloper/pretzel-sdk@0.1.8","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"333911f0593ae3c1263e11fa78bf8bd9e6be234f","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.8.tgz","fileCount":6,"integrity":"sha512-EJDEb6aPnuTDzw9YwmkxMFXcfJVZcmtvMnUcUucAb09j1CFGmwa6g2QgulI3LtIHjjcwMcZ1NucvqbB/e1z6tw==","signatures":[{"sig":"MEYCIQDbIcgeE98sX8FepRZ/HkYGrt7PgQ1NL3whskU7xjprcQIhAIoj6d7rvmWga9l+a7ekhxia2ww4hIXUAZEaRpzZOAn1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26984},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"7824fa11cb99b709752ff9ac93ad90c2b85f6268","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.8_1777458650353_0.7106317078533042","host":"s3://npm-registry-packages-npm-production"}},"0.1.9":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.9","keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"_id":"@adamthedeveloper/pretzel-sdk@0.1.9","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"dist":{"shasum":"e22f558e1d51f1471ffb6c533819112d5205a9f8","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.9.tgz","fileCount":6,"integrity":"sha512-ozTTAyWeHQd4uzz3W+N4yIn3su6VZgWQkzgTLBfDMGQhItC9EW5aRdIZ4S3jWinTJZn/atVyZiw8YU3I6bKt4w==","signatures":[{"sig":"MEQCICiArP/lMFUrSbxM7pL4BUtkiK6UjnyC7obwoTEsGWaXAiBsfHMfrBdfMTX5y9wVbOcRe62mbRKw/3Oa5Qt22pcOyA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27247},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"5d11a83ea20bee93a1349598cc8fb799b6a8dda6","private":false,"scripts":{"lint":"tsc -p tsconfig.json --noEmit","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"_npmVersion":"10.8.2","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","directories":{},"_nodeVersion":"20.19.2","dependencies":{"socket.io-client":"^4.8.1","@adamthedeveloper/pretzel-types":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","@types/node":"^22.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretzel-sdk_0.1.9_1777459458218_0.8759078565437064","host":"s3://npm-registry-packages-npm-production"}},"0.1.10":{"name":"@adamthedeveloper/pretzel-sdk","version":"0.1.10","private":false,"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"publishConfig":{"access":"public"},"scripts":{"build":"tsup","lint":"tsc -p tsconfig.json --noEmit","typecheck":"tsc -p tsconfig.json --noEmit"},"dependencies":{"@adamthedeveloper/pretzel-types":"0.1.2","socket.io-client":"^4.8.1"},"devDependencies":{"@types/node":"^22.15.0","tsup":"^8.5.0"},"_id":"@adamthedeveloper/pretzel-sdk@0.1.10","gitHead":"5d11a83ea20bee93a1349598cc8fb799b6a8dda6","description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","_nodeVersion":"20.19.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-PS+JtYuQVrdsMkMSTcg62eYgWrvuvSgsyHKUtxY7jUEdGb+7FAwELhpWzCPcnfeOGobCSA/XZTnS6chw8z5Hyg==","shasum":"156d94cb19a227ad88d7b06d276cf5786002f310","tarball":"https://registry.npmjs.org/@adamthedeveloper/pretzel-sdk/-/pretzel-sdk-0.1.10.tgz","fileCount":6,"unpackedSize":28072,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCsWk+tA0BBJXq5iQ7zvp2m0J3p1wju1fnnWf8vZg98igIhAP9yiUhUjc+56QXpXPdHVPjNBK1uG1u/zs3UsZqjlEoj"}]},"_npmUser":{"name":"adamthedeveloper","email":"adaminiature@gmail.com"},"directories":{},"maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pretzel-sdk_0.1.10_1777459757283_0.09134656814709419"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T07:14:01.289Z","modified":"2026-04-29T10:49:17.549Z","0.1.2":"2026-04-29T07:14:01.511Z","0.1.3":"2026-04-29T08:06:50.519Z","0.1.4":"2026-04-29T08:08:45.857Z","0.1.5":"2026-04-29T09:38:10.619Z","0.1.7":"2026-04-29T10:13:27.381Z","0.1.8":"2026-04-29T10:30:50.491Z","0.1.9":"2026-04-29T10:44:18.356Z","0.1.10":"2026-04-29T10:49:17.439Z"},"keywords":["presence","realtime","websocket","socket.io","typing-indicator","last-seen","online-status","typescript-sdk","multi-tenant","pretzel"],"description":"This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)","maintainers":[{"name":"adamthedeveloper","email":"adaminiature@gmail.com"}],"readme":"# Pretzel SDK Consumer Guide\n\nThis document covers all currently published consumer features for:\n- `@adamthedeveloper/pretzel-sdk` (`0.1.2`)\n- `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)\n\n## Install\n\n```bash\nnpm install @adamthedeveloper/pretzel-sdk @adamthedeveloper/pretzel-sdk-react\n```\n\n`@adamthedeveloper/pretzel-sdk-react` requires peer dependencies:\n- `react` `^19.0.0`\n- `react-dom` `^19.0.0`\n\n## Package Features\n\n### `@adamthedeveloper/pretzel-sdk`\n\n- WebSocket connect/disconnect lifecycle\n- Automatic heartbeat (`ping`) every 15 seconds\n- Automatic reconnection with exponential backoff\n  - initial delay: `500ms`\n  - max delay: `10,000ms`\n- Per-user event subscriptions with explicit unsubscribe\n- Presence read APIs:\n  - `getPresence(userId)`\n  - `getPresenceBatch(userIds)`\n- Typing APIs:\n  - `startTyping(userId)`\n  - `stopTyping(userId)`\n- Event dispatch support for:\n  - `user_online`\n  - `user_offline`\n  - `typing_start`\n  - `typing_stop`\n- Tenant-safe request headers derived from `apiKey`\n\n### `@adamthedeveloper/pretzel-sdk-react`\n\n- `PresenceProvider` for sharing one `PresenceClient` instance\n- `usePresenceClient()` for direct client access in components\n- `usePresence(userId)` for single-user reactive presence state\n- `usePresenceBatch(userIds)` for multi-user reactive presence map\n- `useTyping(userId)` for reactive typing status\n- Built-in stale-event protection using event timestamps for:\n  - `usePresence`\n  - `useTyping`\n- Automatic subscribe/unsubscribe lifecycle inside hooks\n\n## Core Types\n\n```ts\ntype PresenceStatus = \"online\" | \"offline\";\n\ntype UserPresence = {\n  userId: string;\n  status: PresenceStatus;\n  last_seen: string | null;\n};\n\ntype PresenceEvent = {\n  type: \"user_online\" | \"user_offline\" | \"typing_start\" | \"typing_stop\";\n  userId: string;\n  tenantId: string;\n  timestamp: string;\n};\n```\n\n## SDK API Reference\n\n### Create client\n\n```ts\nimport { PresenceClient } from \"@adamthedeveloper/pretzel-sdk\";\n\nconst client = new PresenceClient();\n```\n\n### `connect(options)`\n\n```ts\nclient.connect({\n  url: \"ws://localhost:3001\",\n  httpUrl: \"http://localhost:3000\",\n  apiKey: \"<keyId>.<secret>\",\n  userId: \"user-123\",\n});\n```\n\n- `url`: WebSocket base URL\n- `httpUrl` (optional): HTTP base URL for presence reads; if omitted, derived from `url`\n- `apiKey`: issued presented API key in `<keyId>.<secret>` format\n- `userId`: current connected user\n\n### `disconnect()`\n\nStops heartbeat and reconnection attempts, removes listeners, and disconnects the socket.\n\n### `subscribeToUser(userId, callback)`\n\n```ts\nconst unsubscribe = client.subscribeToUser(\"user-456\", (event) => {\n  console.log(event.type, event.timestamp);\n});\n```\n\n- Each call creates an independent subscription\n- Always call `unsubscribe()` during cleanup to avoid leaks\n\n### `getPresence(userId)`\n\n```ts\nconst presence = await client.getPresence(\"user-456\");\n```\n\nReturns:\n\n```ts\n{\n  userId: \"user-456\",\n  status: \"online\" | \"offline\",\n  last_seen: string | null\n}\n```\n\n### `getPresenceBatch(userIds)`\n\n```ts\nconst presenceList = await client.getPresenceBatch([\"user-1\", \"user-2\"]);\n```\n\nReturns `UserPresence[]`.\n\n### `startTyping(userId)` / `stopTyping(userId)`\n\n```ts\nclient.startTyping(\"user-456\");\nclient.stopTyping(\"user-456\");\n```\n\n## React API Reference\n\n### `PresenceProvider`\n\n```tsx\nimport { PresenceClient } from \"@adamthedeveloper/pretzel-sdk\";\nimport { PresenceProvider } from \"@adamthedeveloper/pretzel-sdk-react\";\n\nconst client = new PresenceClient();\n\nclient.connect({\n  url: \"ws://localhost:3001\",\n  httpUrl: \"http://localhost:3000\",\n  apiKey: \"<keyId>.<secret>\",\n  userId: \"user-123\",\n});\n\nexport function AppRoot() {\n  return <PresenceProvider client={client}>{/* app */}</PresenceProvider>;\n}\n```\n\n### `usePresenceClient()`\n\nReturns the `PresenceClient` from context.  \nThrows if used outside `PresenceProvider`.\n\n### `usePresence(userId)`\n\n```tsx\nconst presence = usePresence(\"user-456\");\n// { status: \"online\" | \"offline\", last_seen: string | null }\n```\n\nBehavior:\n- Fetches initial state via `getPresence`\n- Subscribes to real-time presence events\n- Ignores out-of-order stale events using timestamps\n\n### `usePresenceBatch(userIds)`\n\n```tsx\nconst presenceByUserId = usePresenceBatch([\"user-1\", \"user-2\"]);\n// Record<string, { status: \"online\" | \"offline\"; last_seen: string | null }>\n```\n\nBehavior:\n- Fetches initial batch via `getPresenceBatch`\n- Creates one live subscription per user ID\n- Returns `{}` for an empty input list\n\n### `useTyping(userId)`\n\n```tsx\nconst { isTyping } = useTyping(\"user-456\");\n```\n\nBehavior:\n- Listens to `typing_start` / `typing_stop`\n- Ignores stale events using timestamps\n- Resets to `isTyping: false` on cleanup\n\n## End-to-End Usage Example\n\n```tsx\nimport { useEffect } from \"react\";\nimport { PresenceClient } from \"@adamthedeveloper/pretzel-sdk\";\nimport {\n  PresenceProvider,\n  usePresence,\n  useTyping,\n  usePresenceClient,\n} from \"@adamthedeveloper/pretzel-sdk-react\";\n\nconst client = new PresenceClient();\n\nclient.connect({\n  url: \"ws://localhost:3001\",\n  httpUrl: \"http://localhost:3000\",\n  apiKey: \"<keyId>.<secret>\",\n  userId: \"viewer-user\",\n});\n\nfunction UserPresenceCard({ userId }: { userId: string }) {\n  const presence = usePresence(userId);\n  const { isTyping } = useTyping(userId);\n  const sdkClient = usePresenceClient();\n\n  useEffect(() => {\n    sdkClient.startTyping(userId);\n    return () => {\n      sdkClient.stopTyping(userId);\n    };\n  }, [sdkClient, userId]);\n\n  return (\n    <div>\n      <div>{userId}</div>\n      <div>Status: {presence.status}</div>\n      <div>Last seen: {presence.last_seen ?? \"never\"}</div>\n      <div>Typing: {isTyping ? \"yes\" : \"no\"}</div>\n    </div>\n  );\n}\n\nexport function App() {\n  return (\n    <PresenceProvider client={client}>\n      <UserPresenceCard userId=\"target-user\" />\n    </PresenceProvider>\n  );\n}\n```\n\n## Error and Input Notes\n\n- `connect()` must be called before HTTP presence reads\n- `apiKey` must be `<keyId>.<secret>`\n- `userId` must be a non-empty string\n- `getPresenceBatch(userIds)` requires at least one user ID\n- Non-2xx presence HTTP responses throw an error\n- `usePresenceClient()` throws if no `PresenceProvider` is present\n\n## Service Expectations\n\n- WS service should be reachable at your configured `url`\n- Query API uses `httpUrl` when provided\n- If `httpUrl` is omitted, Query API is inferred from WS URL host:\n  - `ws://host` -> `http://host`\n  - `wss://host` -> `https://host`\n- Presence read requests (`getPresence`, `getPresenceBatch`) are sent to that resolved HTTP base URL\n","readmeFilename":"README.md"}