{"id":34976252,"url":"https://github.com/matthewwid/better-sse","last_synced_at":"2025-12-29T12:00:02.129Z","repository":{"id":37782640,"uuid":"323901641","full_name":"MatthewWid/better-sse","owner":"MatthewWid","description":"⬆ Dead simple, dependency-less, spec-compliant server-sent events implementation written in TypeScript","archived":false,"fork":false,"pushed_at":"2025-11-03T10:51:05.000Z","size":1434,"stargazers_count":763,"open_issues_count":9,"forks_count":24,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-11-03T12:16:17.287Z","etag":null,"topics":["bun","deno","event-driven","javascript","messaging","node","nodejs","real-time","realtime","server-sent-events","sse","typescript","web-standards"],"latest_commit_sha":null,"homepage":"https://matthewwid.github.io/better-sse/","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/MatthewWid.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-12-23T12:53:11.000Z","updated_at":"2025-11-03T10:50:48.000Z","dependencies_parsed_at":"2023-09-27T13:15:56.469Z","dependency_job_id":"f5c55287-a816-4dcc-8848-e50a57056668","html_url":"https://github.com/MatthewWid/better-sse","commit_stats":{"total_commits":243,"total_committers":2,"mean_commits":121.5,"dds":0.04526748971193417,"last_synced_commit":"a76a59aeadeed477dfbd610e2dc729dca308781c"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/MatthewWid/better-sse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewWid%2Fbetter-sse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewWid%2Fbetter-sse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewWid%2Fbetter-sse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewWid%2Fbetter-sse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MatthewWid","download_url":"https://codeload.github.com/MatthewWid/better-sse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewWid%2Fbetter-sse/sbom","scorecard":{"id":91412,"data":{"date":"2025-08-11","repo":{"name":"github.com/MatthewWid/better-sse","commit":"ad684b62a5bb4d7bd2cc3c6e5641f3a3f9472416"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 6 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":1,"reason":"Found 1/10 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/docs.yaml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/docs.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/docs.yaml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/docs.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/docs.yaml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/docs.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yaml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/lint.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yaml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/lint.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/lint.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/test.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yaml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/test.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/MatthewWid/better-sse/test.yaml/master?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/docs.yaml:6","Warn: no topLevel permission defined: .github/workflows/lint.yaml:1","Warn: no topLevel permission defined: .github/workflows/test.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-cq8c-xv66-36gw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-15T07:59:30.013Z","repository_id":37782640,"created_at":"2025-08-15T07:59:30.014Z","updated_at":"2025-08-15T07:59:30.014Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28115764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-29T02:00:07.021Z","response_time":58,"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":["bun","deno","event-driven","javascript","messaging","node","nodejs","real-time","realtime","server-sent-events","sse","typescript","web-standards"],"created_at":"2025-12-27T00:11:37.498Z","updated_at":"2025-12-29T12:00:02.123Z","avatar_url":"https://github.com/MatthewWid.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Better SSE\n\n\u003cp\u003e\n    \u003ca href=\"https://www.npmjs.com/package/better-sse\"\u003e\n        \u003cimg src=\"https://img.shields.io/npm/v/better-sse?color=blue\u0026style=flat-square\" alt=\"npm\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://jsr.io/@mwid/better-sse\"\u003e\n        \u003cimg src=\"https://jsr.io/badges/@mwid/better-sse\" alt=\"jsr\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/MatthewWid/better-sse/blob/master/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/npm/l/better-sse?color=green\u0026style=flat-square\" alt=\"MIT license\" /\u003e\n    \u003c/a\u003e\n\t\u003cimg src=\"https://img.shields.io/npm/dt/better-sse?color=grey\u0026style=flat-square\" alt=\"Downloads\" /\u003e\n\t\u003ca href=\"https://github.com/MatthewWid/better-sse\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/stars/MatthewWid/better-sse?style=social\" alt=\"GitHub stars\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\nA dead simple, dependency-less, spec-compliant server-sent events implementation written in TypeScript.\n\nThis package aims to be the easiest to use, most compliant and most streamlined solution to server-sent events that is framework-agnostic and feature-rich.\n\nPlease consider starring the project [on GitHub ⭐](https://github.com/MatthewWid/better-sse).\n\n## Why use Server-sent Events?\n\nServer-sent events (SSE) is a standardised protocol that allows web-servers to push data to clients without the need for alternative mechanisms such as pinging, long-polling or [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API).\n\nUsing SSE can allow for significant savings in bandwidth and battery life on portable devices and will work with your existing infrastructure as it operates directly over the HTTP protocol without the need for the connection upgrade that WebSockets require.\n\nCompared to WebSockets it has comparable performance and bandwidth usage, especially over HTTP/2, and natively includes event ID generation and automatic reconnection when clients are disconnected.\n\nRead the [Getting Started](https://matthewwid.github.io/better-sse/guides/getting-started/) guide for more.\n\n* [Comparison: Server-sent Events vs WebSockets vs Polling](https://medium.com/dailyjs/a-comparison-between-websockets-server-sent-events-and-polling-7a27c98cb1e3)\n* [WHATWG standards section for server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html)\n* [MDN guide to server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)\n* [Can I use... Server-sent events](https://caniuse.com/eventsource)\n\n## Highlights\n\n* Compatible with all popular frameworks and runtimes ([Express](https://nodejs.org/api/http.html), [Hono](https://hono.dev/), [Fastify](https://fastify.dev/), [Nest](https://nestjs.com/), [Next.js](https://nextjs.org/), [Bun](https://bun.sh/docs/api/http), [Deno](https://docs.deno.com/runtime/fundamentals/http_server/), [etc.](https://matthewwid.github.io/better-sse/reference/recipes/))\n* Fully written in TypeScript (+ ships with types directly).\n* [Thoroughly tested](./src/Session.test.ts) (+ 100% code coverage!).\n* [Comprehensively documented](https://matthewwid.github.io/better-sse) with guides and API documentation.\n* [Channels](https://matthewwid.github.io/better-sse/guides/channels) allow you to broadcast events to many clients at once.\n* [Event buffers](https://matthewwid.github.io/better-sse/guides/batching/) allow you to batch events for increased performance and lower bandwidth usage.\n* Configurable reconnection time, message serialization and data sanitization (with good defaults).\n* Trust or ignore the client-given last event ID.\n* Automatically send keep-alive pings to keep connections open.\n* Add or override the response status code and headers.\n* Pipe [streams](https://nodejs.org/api/stream.html#stream_readable_streams) and [iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) directly from the server to the client as a series of events.\n* Support for popular EventSource polyfills [`event-source-polyfill`](https://www.npmjs.com/package/event-source-polyfill) and [`eventsource-polyfill`](https://www.npmjs.com/package/eventsource-polyfill).\n\n[See a comparison with other SSE libraries in the documentation.](https://matthewwid.github.io/better-sse/reference/comparison)\n\n# Installation\n\nBetter SSE is published as a package on [npm](https://www.npmjs.com/package/better-sse) and the [JSR](https://jsr.io/@mwid/better-sse). You can install it with any package manager:\n\n```sh\nnpm install better-sse\n```\n\n```sh\nyarn add better-sse\n```\n\n```sh\npnpm add better-sse\n```\n\n```sh\nbun add better-sse\n```\n\n```sh\ndeno install jsr:@mwid/better-sse\n```\n\n_Better SSE ships with types built in. No need to install from DefinitelyTyped for TypeScript users!_\n\n# Usage\n\nThe examples below show usage with [Express](http://expressjs.com/) and [Hono](https://hono.dev/), but Better SSE works with any web-server framework that uses the Node [HTTP module](https://nodejs.org/api/http.html) or the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).\n\nSee the [Recipes](https://matthewwid.github.io/better-sse/reference/recipes/) section of the documentation for use with other frameworks and libraries.\n\n---\n\nUse [sessions](https://matthewwid.github.io/better-sse/guides/getting-started/#create-a-session) to push events to clients:\n\n```typescript\n// Server - Express\nimport { createSession } from \"better-sse\"\n\napp.get(\"/sse\", async (req, res) =\u003e {\n\tconst session = await createSession(req, res)\n\tsession.push(\"Hello world!\", \"message\")\n})\n```\n\n```typescript\n// Server - Hono\nimport { createResponse } from \"better-sse\"\n\napp.get(\"/sse\", (c) =\u003e\n    createResponse(c.req.raw, (session) =\u003e {\n        session.push(\"Hello world!\", \"message\")\n    })\n)\n```\n\n```typescript\n// Client\nconst eventSource = new EventSource(\"/sse\")\n\neventSource.addEventListener(\"message\", ({ data }) =\u003e {\n\tconst contents = JSON.parse(data)\n\tconsole.log(contents) // Hello world!\n})\n```\n\nUse [channels](https://matthewwid.github.io/better-sse/guides/channels/#create-a-channel) to send events to many clients at once:\n\n```typescript\nimport { createSession, createChannel } from \"better-sse\"\n\nconst channel = createChannel()\n\napp.get(\"/sse\", async (req, res) =\u003e {\n\tconst session = await createSession(req, res)\n\n\tchannel.register(session)\n\n\tchannel.broadcast(\"A user has joined.\", \"join-notification\")\n})\n```\n\nUse [batching](https://matthewwid.github.io/better-sse/guides/batching/) to send multiple events at once for improved performance and lower bandwidth usage:\n\n```typescript\nawait session.batch(async (buffer) =\u003e {\n    await buffer.iterate([\"My\", \"huge\", \"event\", \"list\"])\n})\n```\n\nLoop over sync and async [iterables](https://matthewwid.github.io/better-sse/reference/api/#sessioniterate-iterable-iterable--asynciterable-options-object--promisevoid) and send each value as an event:\n\n```typescript\nconst iterable = [1, 2, 3]\n\nawait session.iterate(iterable)\n```\n\nPipe [readable stream](https://matthewwid.github.io/better-sse/reference/api/#sessionstream-stream-readable-options-object--promiseboolean) data to the client as a stream of events:\n\n```typescript\nconst stream = Readable.from([1, 2, 3])\n\nawait session.stream(stream)\n```\n\n---\n\nCheck the [API documentation](https://matthewwid.github.io/better-sse/reference/api) and [live examples](./examples) for information on getting more fine-tuned control over your data such as managing event IDs, data serialization, event filtering, dispatch controls and more!\n\n# Documentation\n\nSee [the documentation website](https://matthewwid.github.io/better-sse/) for guides, usage examples, compatibility information and an API reference.\n\n# Contributing\n\nThis library is always open to contributions whether it be code, bug reports, documentation or anything else.\n\nPlease submit suggestions, bugs and issues to the [GitHub issues page](https://github.com/MatthewWid/better-sse/issues).\n\nFor code or documentation changes [submit a pull request on GitHub](https://github.com/MatthewWid/better-sse/pulls).\n\n## Local Development\n\nInstall [Node](https://nodejs.org/en) (with [n](https://github.com/tj/n)):\n\n```bash\ncurl -L https://git.io/n-install | bash\nn auto\n```\n\nInstall dependencies (with [pnpm](https://pnpm.io/)):\n\n```bash\nnpm i -g pnpm\npnpm i\n```\n\nRun tests (with [Vitest](https://vitest.dev/)):\n\n```bash\npnpm t\n```\n\nLint and format (with [Biome](https://biomejs.dev/)):\n\n```bash\npnpm lint\npnpm format\n```\n\nBundle for distribution (with [tsup](https://tsup.egoist.dev/)):\n\n```bash\npnpm build\n```\n\n## Documentation\n\nThe documentation is built with [Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/). Its source is located in the `docs` directory.\n\nInstall dependencies:\n\n```bash\ncd docs\npnpm i\n```\n\nRun development server:\n\n```bash\npnpm dev\n```\n\nBuild for distribution:\n\n```bash\npnpm build\n```\n\n# License\n\nThis project is licensed under the [MIT license](https://opensource.org/license/mit/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewwid%2Fbetter-sse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthewwid%2Fbetter-sse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewwid%2Fbetter-sse/lists"}