{"_id":"@a-type/dialogue","_rev":"5-302e3f84abc7d1cda24f3f2ef79025e7","name":"@a-type/dialogue","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@a-type/dialogue","version":"1.0.0","keywords":[],"author":"","license":"ISC","_id":"@a-type/dialogue@1.0.0","maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"dist":{"shasum":"80e19709dc4b9ace43ffbce44c4e56f08a8d76fe","tarball":"https://registry.npmjs.org/@a-type/dialogue/-/dialogue-1.0.0.tgz","fileCount":14,"integrity":"sha512-6qq+znjh56e3uYXO766p/w2aDnvJJ0OuWbRfeLwrMC+ie2Lug5lDm1zhQJ0hy4Umhcj85gfOt5OxEWCVk8PjYQ==","signatures":[{"sig":"MEYCIQDdaA+3EzloFuyYR08BV5Mv1yYWXJ2iJz/zQlGdvelcCAIhAJRzMra4hq7+pjrn8J+AliJOEHExxr4top2Rk6cT+bP3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26087},"type":"module","_from":"file:a-type-dialogue-1.0.0.tgz","volta":{"node":"24.14.1"},"engines":{"node":">=24.14.1"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"pnpm run --parallel --filter . \"/test:.*/\"","build":"tsc -p tsconfig.json","test:run":"vitest","test:server":"pnpx kill-port 8298 && node test/server.ts"},"_npmUser":{"name":"a-type","email":"gaforres@gmail.com"},"_resolved":"C:\\Users\\a-typ\\AppData\\Local\\Temp\\09d711e78a0b9daa0e0ad0364ac5503d\\a-type-dialogue-1.0.0.tgz","_integrity":"sha512-6qq+znjh56e3uYXO766p/w2aDnvJJ0OuWbRfeLwrMC+ie2Lug5lDm1zhQJ0hy4Umhcj85gfOt5OxEWCVk8PjYQ==","_npmVersion":"11.11.0","description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.20.0","zod":"^4.3.6","bumpp":"^11.0.1","vitest":"^4.1.2","@types/ws":"^8.18.1","typescript":"^6.0.2","@types/node":"^25.5.0","@vitest/browser-playwright":"^4.1.2"},"_npmOperationalInternal":{"tmp":"tmp/dialogue_1.0.0_1775096333780_0.9372624733234705","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@a-type/dialogue","version":"1.0.1","keywords":[],"author":"","license":"ISC","_id":"@a-type/dialogue@1.0.1","maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"dist":{"shasum":"b592c2446b0859ccd2f8789f6ab42f70401e51b7","tarball":"https://registry.npmjs.org/@a-type/dialogue/-/dialogue-1.0.1.tgz","fileCount":14,"integrity":"sha512-e8JqhLpblTEpnXOsPW+KCs3eK2b7pVaBuKq35eRN+ZgDiELrQbKSzTSVLw/+k89Mqp4o5y5aRUXlw8oaLdSunw==","signatures":[{"sig":"MEQCIAXQd8NrYmwZGuiifRErgN4SAh8fLWYdR0/2jtSBAh4oAiBEpnTYnNjoVUl5GrXwHIsInZJw7EfhdriROr7ZaPvRmw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":30105},"type":"module","_from":"file:a-type-dialogue-1.0.1.tgz","volta":{"node":"24.14.1"},"engines":{"node":">=24.14.1"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"bump":"bumpp","test":"pnpm run --parallel --filter . \"/test:.*/\"","build":"tsc -p tsconfig.json","test:run":"vitest","test:server":"pnpx kill-port 8298 && node test/server.ts"},"_npmUser":{"name":"a-type","email":"gaforres@gmail.com"},"_resolved":"C:\\Users\\a-typ\\AppData\\Local\\Temp\\51fe6463a522dffd5befe85f161fbb42\\a-type-dialogue-1.0.1.tgz","_integrity":"sha512-e8JqhLpblTEpnXOsPW+KCs3eK2b7pVaBuKq35eRN+ZgDiELrQbKSzTSVLw/+k89Mqp4o5y5aRUXlw8oaLdSunw==","_npmVersion":"11.11.0","description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.20.0","zod":"^4.3.6","bumpp":"^11.0.1","vitest":"^4.1.2","@types/ws":"^8.18.1","typescript":"^6.0.2","@types/node":"^25.5.0","@vitest/browser-playwright":"^4.1.2"},"_npmOperationalInternal":{"tmp":"tmp/dialogue_1.0.1_1775098503605_0.7926460914842544","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@a-type/dialogue","version":"1.0.4","keywords":[],"author":"","license":"ISC","_id":"@a-type/dialogue@1.0.4","maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"homepage":"https://github.com/a-type/dialogue#readme","bugs":{"url":"https://github.com/a-type/dialogue/issues"},"dist":{"shasum":"bdb8b32745c71a9b46dc6791b8a0b8cc3642d283","tarball":"https://registry.npmjs.org/@a-type/dialogue/-/dialogue-1.0.4.tgz","fileCount":20,"integrity":"sha512-qE2P7pViCsKwrbsXGksmma7p1DCH3PZ3Z4VsaDWA3Q5ivXXxluZwMb/kOCue181JoALBlUCAtuOQpv8dn6SHdg==","signatures":[{"sig":"MEQCIAeHCSFTvFDeStoWlmKmjnr+NXEjP5Xxd+DNVVZ7snwUAiAEjbPGN8M5qB2eaJtDPEs3GK9HIbxV2JzDVnxoOBCwaQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a-type%2fdialogue@1.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":52760},"type":"module","_from":"file:a-type-dialogue-1.0.4.tgz","volta":{"node":"24.14.1"},"engines":{"node":">=24.14.1"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"bump":"bumpp","test":"vitest","build":"tsc -p tsconfig.json","test:ci":"vitest run"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:aa4932f6-d25c-436d-8fcd-61930d525972"}},"_resolved":"/tmp/8aba4857f81e6f67fcf6a449012bfd34/a-type-dialogue-1.0.4.tgz","_integrity":"sha512-qE2P7pViCsKwrbsXGksmma7p1DCH3PZ3Z4VsaDWA3Q5ivXXxluZwMb/kOCue181JoALBlUCAtuOQpv8dn6SHdg==","repository":{"url":"git+https://github.com/a-type/dialogue.git"},"_npmVersion":"11.12.1","description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.20.0","zod":"^4.3.6","bumpp":"^11.0.1","vitest":"^4.1.2","@types/ws":"^8.18.1","typescript":"^6.0.2","@types/node":"^25.5.0","@vitest/browser-playwright":"^4.1.2"},"_npmOperationalInternal":{"tmp":"tmp/dialogue_1.0.4_1775099675376_0.3367626750179733","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@a-type/dialogue","version":"1.1.0","keywords":[],"author":"","license":"ISC","_id":"@a-type/dialogue@1.1.0","maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"homepage":"https://github.com/a-type/dialogue#readme","bugs":{"url":"https://github.com/a-type/dialogue/issues"},"dist":{"shasum":"5c433f5e3f16eb894936172351ab73d012b65cd2","tarball":"https://registry.npmjs.org/@a-type/dialogue/-/dialogue-1.1.0.tgz","fileCount":23,"integrity":"sha512-uOSuhzucT6rUrYIwGWOtdPzzGZ/lIy5/QEpLrqnjEoWEs+H2blxAG3Nni4dvMg+UtY0vcRYFAZoTxgHl4Pjqjg==","signatures":[{"sig":"MEUCIQDzAjDtoU5hwrA+hJR2L9GC5QtaXtwGHxJBsBONbCeoZwIgBcsGX922OC40t1jSIXWlqZHr60Cr2ZcPxmHlIP4aSgM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a-type%2fdialogue@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":65585},"type":"module","_from":"file:a-type-dialogue-1.1.0.tgz","volta":{"node":"24.14.1"},"engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"bump":"bumpp","test":"vitest","build":"tsc -p tsconfig.json","test:ci":"vitest run"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:aa4932f6-d25c-436d-8fcd-61930d525972"}},"_resolved":"/tmp/4951d0bc5edd1cb57fa3e2df5c1f1a2a/a-type-dialogue-1.1.0.tgz","_integrity":"sha512-uOSuhzucT6rUrYIwGWOtdPzzGZ/lIy5/QEpLrqnjEoWEs+H2blxAG3Nni4dvMg+UtY0vcRYFAZoTxgHl4Pjqjg==","repository":{"url":"git+https://github.com/a-type/dialogue.git"},"_npmVersion":"11.12.1","description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.20.0","zod":"^4.3.6","bumpp":"^11.0.1","vitest":"^4.1.2","@types/ws":"^8.18.1","typescript":"^6.0.2","@types/node":"^25.5.0","@vitest/browser-playwright":"^4.1.2"},"_npmOperationalInternal":{"tmp":"tmp/dialogue_1.1.0_1775145595976_0.728440471465428","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@a-type/dialogue","version":"1.1.1","description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","type":"module","engines":{"node":">=24"},"exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"publishConfig":{"access":"public"},"repository":{"url":"git+https://github.com/a-type/dialogue.git"},"sideEffects":false,"keywords":[],"author":"","license":"ISC","devDependencies":{"@types/node":"^25.5.0","@types/ws":"^8.18.1","@vitest/browser-playwright":"^4.1.2","bumpp":"^11.0.1","typescript":"^6.0.2","vitest":"^4.1.2","ws":"^8.20.0","zod":"^4.3.6"},"volta":{"node":"24.14.1"},"scripts":{"test":"vitest","test:ci":"vitest run","build":"tsc -p tsconfig.json","bump":"bumpp"},"_id":"@a-type/dialogue@1.1.1","bugs":{"url":"https://github.com/a-type/dialogue/issues"},"homepage":"https://github.com/a-type/dialogue#readme","_integrity":"sha512-3K23HM61nZU+Kwpa8GRPMsudpH2vWQ/OADUi+CAk6H6Sb5VKOxDotQ75gzHJbb4U+KblWMYoZBo7E1BFldQCfg==","_resolved":"/tmp/bf382b9d31092482454191359c5eee09/a-type-dialogue-1.1.1.tgz","_from":"file:a-type-dialogue-1.1.1.tgz","_nodeVersion":"24.14.1","_npmVersion":"11.12.1","dist":{"integrity":"sha512-3K23HM61nZU+Kwpa8GRPMsudpH2vWQ/OADUi+CAk6H6Sb5VKOxDotQ75gzHJbb4U+KblWMYoZBo7E1BFldQCfg==","shasum":"b74ba5567e21f54c41a8bae6701870b02448d8e2","tarball":"https://registry.npmjs.org/@a-type/dialogue/-/dialogue-1.1.1.tgz","fileCount":23,"unpackedSize":67241,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a-type%2fdialogue@1.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCbHCqUex8KImywLTJ6yIeNejPD210ZqSaKNeZHKo2E3gIhAP+duCI3kwlcMPUyV3Ho1ww0ufE8L3MuAcMTSZ72GTgb"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:aa4932f6-d25c-436d-8fcd-61930d525972"}},"directories":{},"maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dialogue_1.1.1_1775156859930_0.7857535558336668"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-02T02:18:53.726Z","modified":"2026-04-02T19:07:40.344Z","1.0.0":"2026-04-02T02:18:53.916Z","1.0.1":"2026-04-02T02:55:03.755Z","1.0.4":"2026-04-02T03:14:35.523Z","1.1.0":"2026-04-02T15:59:56.123Z","1.1.1":"2026-04-02T19:07:40.058Z"},"bugs":{"url":"https://github.com/a-type/dialogue/issues"},"license":"ISC","homepage":"https://github.com/a-type/dialogue#readme","keywords":[],"repository":{"url":"git+https://github.com/a-type/dialogue.git"},"description":"A small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.","maintainers":[{"name":"a-type","email":"gaforres@gmail.com"}],"readme":"# Dialogue\n\nA small&mdash;opinionated&mdash;robust&mdash;convenient WebSocket wrapper.\n\n## Features\n\n- Automatic reconnection with exponential backoff\n  - Reconnects on error\n  - Reconnects based on heartbeat to detect broken pipes\n- Buffers outgoing messages until connection is ready\n- Parses and validates incoming and outgoing messages to your schema\n- Easily subscribe to specific message types with typechecking: `on('chat', (chatMessage) => {})`\n- All subscribers return unsubcribers\n- `once()`\n- Send a \"request\" message which awaits a response\n- Preprocess outgoing messages\n\n## Get started\n\nConfigure your Connection with client and server message validators. These validators will automatically apply appropriate types to the Connection's methods.\n\n```ts\nimport { Connection } from '@a-type/dialogue';\nimport { z } from 'zod';\n\n// define validators for your server and client message protocol\n// (you don't have to use Zod - anything that takes in a raw\n// JSON message and returns a typed one)\n\nconst clientMessageSchema = z.union([\n\tz.object({ type: z.literal('ping') }),\n\tz.object({ type: z.literal('chat'), message: z.string() }),\n\tz.object({ type: z.literal('ack-this'), id: z.string() }),\n]);\ntype ClientMessage = z.infer<typeof clientMessageSchema>;\nconst serverMessageSchema = z.union([\n\tz.object({ type: z.literal('pong') }),\n\tz.object({ type: z.literal('new-chat'), message: z.string() }),\n\tz.object({ type: z.literal('ack'), ackedId: z.string() }),\n]);\n\n// setup a Connection\n\nconst connection = new Connection({\n\t// required: which key indicates the message type?\n\tmessageTypeKey: 'type',\n\tparseClientMessage: (m) => clientMessageSchema.parse(m),\n\tparseServerMessage: (m) => serverMessageSchema.parse(m),\n\t// optional. omit to open connection manually\n\topenImmediately: true,\n\t// configure your websocket connection\n\twebsocket: {\n\t\tgetUrl: async () => {\n\t\t\t// you can fetch a token here, or whatever\n\t\t\tconst token = await getToken();\n\t\t\treturn `wss://your.app/socket?token=${token}`;\n\t\t},\n\t},\n\t// optional: setup response handling for request()\n\t// what indicates a message is meant as a response for another?\n\t// remember... delivery isn't guaranteed... request() is just\n\t// a convenience.\n\tgetIsResponse: (clientMsg, serverMsg) => {\n\t\tif (clientMsg.type === 'ack-this' && serverMsg.type === 'ack') {\n\t\t\treturn serverMsg.ackedId === clientMsg.id;\n\t\t}\n\t\treturn false;\n\t},\n\t// optional: apply pre-processing to all outgoing messages, like adding\n\t// a timestamp or generating an ID. you must manually specify a type\n\t// for the parameter, which becomes the type used for validating send()\n\t// and request() automatically.\n\tpreprocessClientMessage: (\n\t\tinput: OmitMessageProperty<ClientMessage, 'id'>,\n\t) => {\n\t\tif (input.type === 'ack-this') {\n\t\t\treturn {\n\t\t\t\t...input,\n\t\t\t\tid: Math.random().toString(),\n\t\t\t};\n\t\t}\n\t\treturn input;\n\t},\n\t// optional: configure heartbeat\n\theartbeat: {\n\t\tgetPing: () => `{\"type\":\"ping\"}`, // this is the default.\n\t\tinterval: 5000,\n\t\tpongTimeout: 2000,\n\t},\n});\n```\n\n## Using a Connection\n\n### Connecting and disconnecting\n\n```ts\nconnection.open();\n\nconnection.close();\n```\n\nIf your `getUrl` configured function throws an error, it will not attempt a reconnect. It's assumed you had a non-retryable failure, like authentication failure. If you have logic like token fetching in `getUrl` you want to retry, you must handle that yourself.\n\n### Sending and subscribing to messages\n\n```ts\n// send a message. parameter is typechecked and runtime validated.\nconnection.send({ type: 'chat', message: 'hello world' });\n\n// subscribe to any incoming message with '*'. \"message\" is typed\n// as any server message. cleanup removes the listener.\nconst cleanup = connection.on('*', (message) => {});\n\n// subscribe to a particular message type. \"message\" is narrowed\n// to that type.\nconnection.on('ack', (message) => {});\n\n// listen for one matching message, then clean up automatically\nconnection.once('new-chat', (message) => {});\n\n// send a message and await a response. \"response\" is decided\n// by the 'getIsResponse' configuration passed to the constructor.\n// by default any incoming message is considered a response.\nconst response = await connection.request(\n\t{\n\t\ttype: 'ack-this',\n\t\tid: 'foo',\n\t},\n\t{\n\t\ttimeout: 5000, // the default. throws if hit.\n\t},\n);\n```\n\n## Advanced\n\nSupply an override for `WebSocket` with `config.websocket.environment.WebSocket`.\n\nConfigure whether the socket should auto-reconnect on clean server close (code 1000) with `config.websocket.reconnectOnServerClose`.\n\nConfigure whether an incoming message is treated as a pong for heartbeats with `config.heartbeat.isPong`.\n\nTurn off default logging with `config.logger: false` or pass it your own object which implements the `ILogger` interface.\n\nWhen using the default logging, show debug messages by setting `DEBUG` to `dialogue` or `true` in `localStorage`.\n\nTo tune the reconnection backoff settings, see options on `config.websocket`: `initialReconnectDelay`, `maxReconnectDelay`, `reconnectDelayFactor`, `maxReconnectAttempts`.\n\nInvalid server messages are ignored by default. To handle them, pass `config.onInvalidServerMessage`. It receives the raw string message, and the error from either JSON.parse or your server message validator.\n","readmeFilename":"README.md"}