{"id":34766433,"url":"https://github.com/nktkas/rews","last_synced_at":"2026-04-02T02:20:43.627Z","repository":{"id":322228938,"uuid":"1088706696","full_name":"nktkas/rews","owner":"nktkas","description":"Drop-in WebSocket replacement with automatic reconnection.","archived":false,"fork":false,"pushed_at":"2026-02-22T01:15:59.000Z","size":102,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-22T09:20:04.509Z","etag":null,"topics":["reconnect","retry","websocket","ws"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nktkas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-03T10:49:35.000Z","updated_at":"2026-02-22T01:24:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nktkas/rews","commit_stats":null,"previous_names":["nktkas/rews"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/nktkas/rews","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkas%2Frews","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkas%2Frews/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkas%2Frews/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkas%2Frews/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nktkas","download_url":"https://codeload.github.com/nktkas/rews/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkas%2Frews/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["reconnect","retry","websocket","ws"],"created_at":"2025-12-25T07:34:51.840Z","updated_at":"2026-04-02T02:20:43.618Z","avatar_url":"https://github.com/nktkas.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @nktkas/rews\n\n[![npm](https://img.shields.io/npm/v/@nktkas/rews)](https://www.npmjs.com/package/@nktkas/rews)\n[![JSR](https://jsr.io/badges/@nktkas/rews)](https://jsr.io/@nktkas/rews)\n[![bundlejs](https://img.shields.io/bundlejs/size/@nktkas/rews)](https://bundlejs.com/?q=@nktkas/rews)\n\nDrop-in [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) replacement with automatic\nreconnection.\n\n---\n\n**Without rews** — manual reconnection, listener re-attachment, message queuing:\n\n```ts\nlet ws: WebSocket;\nlet attempts = 0;\nconst queue: string[] = [];\nconst onMessage = (e: MessageEvent) =\u003e console.log(e.data);\n\nfunction connect() {\n  ws = new WebSocket(\"wss://example.com\");\n  ws.addEventListener(\"message\", onMessage);\n  ws.onopen = () =\u003e {\n    attempts = 0;\n    while (queue.length) ws.send(queue.shift()!);\n  };\n  ws.onclose = () =\u003e {\n    if (attempts++ \u003c 3) setTimeout(connect, 1000);\n  };\n}\n\nfunction send(data: string) {\n  ws.readyState === WebSocket.OPEN ? ws.send(data) : queue.push(data);\n}\n\nconnect();\nsend(\"hello\");\n```\n\n**With rews:**\n\n```ts\nimport { ReconnectingWebSocket } from \"@nktkas/rews\";\n\nconst ws = new ReconnectingWebSocket(\"wss://example.com\");\nws.addEventListener(\"message\", (e) =\u003e console.log(e.data));\nws.send(\"hello\");\n```\n\n## How It Works\n\n```mermaid\nsequenceDiagram\n    participant App\n    participant rews\n    participant Server\n\n    Server--\u003e\u003erews: open  \n    rews--\u003e\u003eApp: open event\n\n    App-\u003e\u003erews: send(\"hello\")\n    rews-\u003e\u003eServer: \"hello\"\n    Server--\u003e\u003erews: \"world\"\n    rews--\u003e\u003eApp: message event\n\n    Server--xrews: connection lost\n    rews--\u003e\u003eApp: close event\n    Note over rews: ← standard WebSocket dies here,\u003cbr/\u003eApp must handle reconnection manually\n\n    Note over rews: reconnecting...\n    rews-\u003e\u003eServer: reconnect\n\n    App-\u003e\u003erews: send(\"hello\")\n    Note over rews: buffered\n\n    Server--\u003e\u003erews: open\n    rews--\u003e\u003eApp: open event\n    rews-\u003e\u003eServer: \"hello\" (from buffer)\n    Server--\u003e\u003erews: \"world\"\n    rews--\u003e\u003eApp: message event\n\n    Note over App: App didn't notice the disruption — just a slight delay\n```\n\n## Features\n\n- **Drop-in replacement** — standard `WebSocket` API, swap one line\n- **Auto-reconnection** — configurable retries with exponential backoff\n- **Persistent listeners** — `addEventListener` and `on*` handlers survive reconnections\n- **Dynamic URL \u0026 protocols** — factory functions for per-reconnect resolution\n- **Zero dependencies** — works in Node.js, Deno, Bun, and browsers\n\n## Install\n\n```\nnpm i @nktkas/rews        # npm / pnpm / yarn\ndeno add jsr:@nktkas/rews # Deno\nbun add @nktkas/rews      # Bun\n```\n\n## Usage\n\n```ts\nimport { ReconnectingWebSocket } from \"@nktkas/rews\";\n\nconst ws = new ReconnectingWebSocket(\"wss://example.com\", {\n  maxRetries: 5,\n  reconnectionDelay: (attempt) =\u003e Math.min(2 ** attempt * 200, 30_000),\n});\n\nws.addEventListener(\"message\", (e) =\u003e console.log(e.data));\nws.addEventListener(\"terminate\", (e) =\u003e console.error(e.detail.code));\n\nws.send(\"hello\"); // buffered if not yet connected\n```\n\n## Options\n\n```ts\ninterface ReconnectingWebSocketOptions {\n  /** Custom WebSocket constructor. @default globalThis.WebSocket */\n  WebSocket?: typeof WebSocket;\n  /** Maximum number of reconnection attempts. @default 3 */\n  maxRetries?: number;\n  /** Connection timeout in ms (null to disable). @default 10_000 */\n  connectionTimeout?: number | null;\n  /** Delay before reconnection in ms, or a function of attempt number. @default exponential backoff, max 10s */\n  reconnectionDelay?: number | ((attempt: number) =\u003e number);\n}\n```\n\n## Beyond Standard WebSocket\n\n### Dynamic URL \u0026 Protocols\n\n`url` and `protocols` accept functions, invoked on each reconnection:\n\n```ts\nconst ws = new ReconnectingWebSocket(\n  () =\u003e `wss://example.com?token=${getToken()}`,\n  () =\u003e [\"v2\"],\n);\n```\n\n### Event Lifecycle\n\nStandard `open`, `close`, `error`, and `message` events fire on **every** connection cycle — not just the first one. A\nsingle `ReconnectingWebSocket` instance may emit multiple `open`/`close` pairs over its lifetime as it reconnects.\n\n```ts\nws.addEventListener(\"open\", () =\u003e console.log(\"connected\")); // fires on each (re)connection\nws.addEventListener(\"close\", () =\u003e console.log(\"disconnected\")); // fires on each disconnection\n\n// use { once: true } if you only need the first occurrence\nws.addEventListener(\"open\", () =\u003e init(), { once: true });\n```\n\n### Terminate Event\n\nFires when the connection is permanently closed:\n\n| Code                 | Description                                |\n| -------------------- | ------------------------------------------ |\n| `RECONNECTION_LIMIT` | Max retries exceeded                       |\n| `TERMINATED_BY_USER` | `close()` called                           |\n| `UNKNOWN_ERROR`      | Unhandled error in user-provided functions |\n\n```ts\nws.addEventListener(\"terminate\", (e) =\u003e {\n  e.detail.code; // ReconnectingWebSocketErrorCode\n  e.detail.cause; // original error, if any\n});\n\nws.isTerminated; // boolean\nws.terminationReason; // ReconnectingWebSocketError | undefined\nws.terminationSignal; // AbortSignal\n```\n\n### Closing Behavior\n\n```ts\nws.close(); // permanently close (default)\nws.close(code, reason, false); // close current socket only — reconnection continues\n```\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnktkas%2Frews","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnktkas%2Frews","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnktkas%2Frews/lists"}