workerd / eventsource
AGENTS.md9.1k starsChanged 8 months agoApache-2.0View on GitHub
An informal specification of EventSource as implemented in workerd, derived from, and kept in lockstep with, the test suite in this directory. The tests are the normative artifact. Both the C++ implementation (src/workerd/api/eventsource.{h,c++}) and the TypeScript implementation (src/per_isolate/eventsource/eventsource.ts, behind the TS_EVENT_SOURCE autogate) are covered. The TypeScript class subclasses EventTarget through the seam in src/per_isolate/events/event-target-internals.ts and does its I/O through RequestIo (src/workerd/api/request-io.{h,c++}), so dispatch, fetch and request binding are shared; the divergences are in class shape, in event stream parsing, in…
Trending
More talk lately.about the whole repository
Who's talking about cloudflare/workerd
- A Hacker News story“OS Workerd, Cloudflare's JavaScript/WASM Runtime”Hacker News · Sep 27, 2022 · 27 points
- withastro/astro“Cloudflare adapter returning [object Object]”GitHub · Oct 10, 2025 · 18 reactions
- supabase/supabase-js“The 'cache' field on 'RequestInitializerDict' is not implemented - Cloudflare Workers”GitHub · Mar 28, 2024 · 16 reactions
- NixOS/nixpkgs“Package request: workerd”GitHub · Nov 12, 2024 · 13 reactions
- nodejs/node“src: improve buffer.transcode performance”GitHub · Aug 1, 2024 · 13 reactions
The biggest 5 of 39 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 39
Talked about on GitHub, Bluesky, Hacker News and Stack Overflow (39 times).
On GitHub · 32
- Oct 10, 2025Cloudflare adapter returning [object Object] — withastro/astro18 reactions · 15 comments
- Mar 28, 2024The 'cache' field on 'RequestInitializerDict' is not implemented - Cloudflare Workers — supabase/supabase-js16 reactions · 24 comments
- Nov 12, 2024Package request: workerd — NixOS/nixpkgs13 reactions · 10 comments
- Aug 1, 2024src: improve buffer.transcode performance — nodejs/node13 reactions · 26 comments
- Feb 19, 2024Add Curve25519 — w3c/webcrypto9 reactions · 16 comments
27 more on GitHub
- Nov 3, 2025Optimize TextEncoder::encode — nodejs/performance7 reactions · 33 comments
- Oct 23, 2023🐛 BUG: pages dev output is unnecessarily verbose — cloudflare/workers-sdk7 reactions · 7 comments
- Nov 24, 2025util: improve textencoder encodeInto performance — nodejs/node6 reactions · 8 comments
- Aug 8, 2024Fix fetch adapter in cloudflare workers — axios/axios6 reactions · 1 comment
- May 25, 2024Partially fix #3160 by properly handling resultsFormat query — cloudflare/workers-sdk6 reactions · 12 comments
- Jan 23, 2026src: improve textEncoder encode performance with simdutf — nodejs/node5 reactions · 8 comments
- Jan 19, 2024Implement Vitest testing environment for Miniflare 3 — cloudflare/workers-sdksays it tested it5 reactions · 7 comments
- Nov 28, 2024src,lib: remove the cost of encoding parameter serialization — nodejs/node4 reactions · 3 comments
- Aug 8, 2026deps: bump the npm group in /kanarek-companion with 2 updates — trvny/trvny3 reactions · 4 comments
- Aug 8, 2026deps: bump the npm group in /weather-feed with 3 updates — trvny/trvny3 reactions · 4 comments
- Aug 8, 2026deps: bump the npm group in /mcp/status-mcp with 2 updates — trvny/trvny3 reactions · 4 comments
- Jan 24, 2026build(deps-dev): bump @cloudflare/workers-types from 4.20260123.0 to 4.20260124.0 — magicappdev/magicappdev3 reactions · 5 comments
- Nov 1, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20261029.1 — paglaai/PaglaROUTER2 reactions · 2 comments
- Oct 25, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20261022.1 — paglaai/PaglaROUTER2 reactions · 3 comments
- Oct 18, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20261015.1 — paglaai/PaglaROUTER2 reactions · 3 comments
- Oct 11, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20261008.1 — paglaai/PaglaROUTER2 reactions · 4 comments
- Oct 4, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20261001.1 — paglaai/PaglaROUTER2 reactions · 4 comments
- Sep 27, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20260924.1 — paglaai/PaglaROUTER2 reactions · 4 comments
- Sep 20, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20260917.1 — paglaai/PaglaROUTER2 reactions · 3 comments
- Sep 16, 2026[wrangler] Add Analytics SQL binding support — cloudflare/workers-sdk2 reactions · 4 comments
- Sep 13, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260821.1 to 5.20260910.1 — paglaai/PaglaROUTER2 reactions · 4 comments
- Aug 23, 2026chore(deps-dev): bump @cloudflare/workers-types from 5.20260801.1 to 5.20260821.1 — paglaai/PaglaROUTER2 reactions · 3 comments
- Aug 18, 2026Use GPT-5.6 Terra and Sol for Bonk — cloudflare/vinext2 reactions · 24 comments
- Apr 21, 2026build(deps-dev): Bump @cloudflare/workers-types from 4.20260411.1 to 4.20260421.1 in /workers/auth-api — Trancendos/infinity-adminOS2 reactions · 4 comments
- Apr 14, 2026build(deps-dev): Bump @cloudflare/workers-types from 4.20260411.1 to 4.20260414.1 in /workers/ai-api — Trancendos/infinity-adminOS2 reactions · 4 comments
- Apr 14, 2026build(deps-dev): Bump @cloudflare/workers-types from 4.20260411.1 to 4.20260414.1 in /workers/files-api — Trancendos/infinity-adminOS2 reactions · 4 comments
- Jan 24, 2026build(deps-dev): bump @cloudflare/workers-types from 4.20260123.0 to 4.20260124.0 — magicappdev/templates2 reactions · 2 comments
On Bluesky · 4
- Mar 24, 2026workerd : le runtime JavaScript/Wasm open source de Cloudflare Workers — @news.humancoders.com4 likes and reposts
- Aug 22, 2026Current status: trying to build github.com/cloudflare/w... on various sandbox providers — @jeremymorrell.dev1 likes and repost
- Mar 24, 2026workerd : le runtime JavaScript/Wasm qui propulse Cloudflare Workers est maintenant open source. Basé sur les standards web, il permet… — @humancoders.com1 likes and repost
- Nov 5, 2024Technically workers are open source github.com/cloudflare/w... — @userops.bsky.social1 likes and repost
On Hacker News · 2
- Sep 27, 2022OS Workerd, Cloudflare's JavaScript/WASM Runtime27 points · 2 comments
- Oct 5, 2023Cloudflare workers is adopting Ada URL parser2 points
On Stack Overflow · 1
- Oct 1, 2024How to user KV in cloudflare workers?1 vote
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository79 other files it gives its agents.
- AGENTS.mdAGENTS.md
- build/AGENTS.mdAGENTS.md
- src/bench/streams/AGENTS.mdAGENTS.md
- src/bench/streams/body/AGENTS.mdAGENTS.md
- src/bench/streams/compression/AGENTS.mdAGENTS.md
- src/bench/streams/digest/AGENTS.mdAGENTS.md
- src/bench/streams/encoding/AGENTS.mdAGENTS.md
- src/bench/streams/identity/AGENTS.mdAGENTS.md
- src/bench/streams/piping/AGENTS.mdAGENTS.md
- src/bench/streams/readable/AGENTS.mdAGENTS.md
- src/bench/streams/readable-byte/AGENTS.mdAGENTS.md
- src/bench/streams/strategies/AGENTS.mdAGENTS.md
- src/bench/streams/transform/AGENTS.mdAGENTS.md
- src/bench/streams/writable/AGENTS.mdAGENTS.md
- src/cloudflare/AGENTS.mdAGENTS.md
- src/node/AGENTS.mdAGENTS.md
- src/per_isolate/AGENTS.mdAGENTS.md
- src/per_isolate/crypto/AGENTS.mdAGENTS.md
- src/per_isolate/events/AGENTS.mdAGENTS.md
- src/per_isolate/webfs/AGENTS.mdAGENTS.md
- src/per_isolate/webstreams/AGENTS.mdAGENTS.md
- src/pyodide/AGENTS.mdAGENTS.md
- src/rust/AGENTS.mdAGENTS.md
- src/rust/cxx/AGENTS.mdAGENTS.md
- src/tests/events/AGENTS.mdAGENTS.md
- src/tests/events/messagechannel/AGENTS.mdAGENTS.md
- src/tests/node/AGENTS.mdAGENTS.md
- src/tests/node/http-client/AGENTS.mdAGENTS.md
- src/tests/node/http-server/AGENTS.mdAGENTS.md
- src/tests/node/net/AGENTS.mdAGENTS.md
- src/tests/node/stream/AGENTS.mdAGENTS.md
- src/tests/streams/AGENTS.mdAGENTS.md
- src/tests/streams/cache/AGENTS.mdAGENTS.md
- src/tests/streams/compression/AGENTS.mdAGENTS.md
- src/tests/streams/digest/AGENTS.mdAGENTS.md
- src/tests/streams/encoding/AGENTS.mdAGENTS.md
- src/tests/streams/formdata/AGENTS.mdAGENTS.md
- src/tests/streams/htmlrewriter/AGENTS.mdAGENTS.md
- src/tests/streams/identity/AGENTS.mdAGENTS.md
- src/tests/streams/inspect/AGENTS.mdAGENTS.md
- src/tests/streams/iocontext/AGENTS.mdAGENTS.md
- src/tests/streams/piping/AGENTS.mdAGENTS.md
- src/tests/streams/r2-patterns/AGENTS.mdAGENTS.md
- src/tests/streams/readable/AGENTS.mdAGENTS.md
- src/tests/streams/readable-byte/AGENTS.mdAGENTS.md
- src/tests/streams/scaling/AGENTS.mdAGENTS.md
- src/tests/streams/sockets/AGENTS.mdAGENTS.md
- src/tests/streams/strategies/AGENTS.mdAGENTS.md
- src/tests/streams/transform/AGENTS.mdAGENTS.md
- src/tests/streams/writable/AGENTS.mdAGENTS.md
- src/workerd/api/AGENTS.mdAGENTS.md
- src/workerd/api/crypto/AGENTS.mdAGENTS.md
- src/workerd/api/node/AGENTS.mdAGENTS.md
- src/workerd/api/streams/AGENTS.mdAGENTS.md
- src/workerd/io/AGENTS.mdAGENTS.md
- src/workerd/jsg/AGENTS.mdAGENTS.md
- src/workerd/server/AGENTS.mdAGENTS.md
- src/workerd/util/AGENTS.mdAGENTS.md
- src/wpt/fetch/AGENTS.mdAGENTS.md
- types/AGENTS.mdAGENTS.md
- The first 60 shown.
The file
We split this AGENTS.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 54
Every member is brand-checked and throws JSG's illegal-invocation TypeError for any other receiver.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
EventSource
An informal specification of EventSource as implemented in workerd,
derived from, and kept in lockstep with, the test suite in this directory.
The tests are the normative artifact. Both the C++ implementation
(src/workerd/api/eventsource.{h,c++}) and the TypeScript implementation
(src/per_isolate/eventsource/eventsource.ts, behind the
TS_EVENT_SOURCE autogate) are covered. The TypeScript class subclasses
EventTarget through the seam in
src/per_isolate/events/event-target-internals.ts and does its I/O through
RequestIo (src/workerd/api/request-io.{h,c++}), so dispatch, fetch
and request binding are shared; the divergences are in
class shape, in event stream parsing, in the Last-Event-ID header, and in
how reads are driven.
Interface
[Exposed=Worker]
interface EventSource : EventTarget {
constructor(USVString url, optional EventSourceInit eventSourceInitDict = {});
static EventSource from(ReadableStream stream); // Workers extension
readonly attribute USVString url;
readonly attribute boolean withCredentials; // always false
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
readonly attribute unsigned short readyState;
attribute EventHandler onopen;
attribute EventHandler onmessage;
attribute EventHandler onerror;
undefined close();
};
dictionary EventSourceInit {
boolean withCredentials = false; // a truthy value throws NotSupportedError
Fetcher fetcher; // Workers extension
};- EventSource is always a global, with no compatibility flag.
- An EventSource can only be created within a request:
NotSupportedError, from the constructor and from() alike. - The URL must be absolute (
SyntaxError); it is normalized as the URL parser does. Both arguments are converted, init's members in order, before anything is checked. EventSource.from()takes a ReadableStream that is neither locked nor disturbed, needs no receiver, and returns an EventSource that is already OPEN, withurl''.[Symbol.dispose]is a function of its own that calls close().- Every member is brand-checked and throws JSG's illegal-invocation
TypeError for any other receiver. Members are enumerable, like every JSG
member; names, lengths and constants' configurability follow
spec_compliant_property_attributes.
Core semantics
- Request: a GET through the init's
fetcher, else the global fetch(), withAccept: text/event-stream,Cache-Control: no-cache, andLast-Event-IDon a reconnection that has one (whitespace trimmed in the TypeScript implementation, #16). - Response: a status other than 200, or a content type other than
text/event-stream(parameters allowed), fails the EventSource for good with anAbortError. A redirect movesurland the messages'origin. A response without a body is retried once, as an end of the stream; a second in a row fails the EventSource. A rejected fetch() closes it without reconnecting. - Reconnection: the end of a connected EventSource's stream fires an
'error' (
The server disconnected.) in CONNECTING and reconnects after the reconnection time, 2 seconds unless aretryfield set it, clamped to 1 to 10 seconds. The end of an EventSource.from() stream closes it. - Parsing follows the HTML standard in the TypeScript implementation; the C++ parser departs from it in the cases of ledger #4 to #9. The stream is decoded as UTF-8 with replacement. A string chunk from a from() stream is read as its UTF-8 bytes, any buffer or view as its bytes; anything else fails the EventSource with the TypeError the streams implementation's byte reads throw.
- Events: a trusted OpenEvent (whose class is not a global), trusted
MessageEvents carrying
lastEventIdand the URL's origin (null for a from() source), and trusted ErrorEvents whoseerrorsays why. None bubbles or is cancelable. - Event handlers: onopen, onmessage and onerror are HTML event handler
attributes, positioned at first assignment and kept across reassignment;
any other
on<type>property is called as EventTarget calls it. - close() is idempotent and fires nothing. Callbacks resumed by another request do nothing, and close() from another request throws.
- Listener exceptions under
spec_compliant_dispatch_exceptions: a throwing 'open' or 'message' listener's exception is reported, the remaining listeners run, then the EventSource errors out with it and the rest of the messages are dropped. A throwing 'error' listener's exception is only reported. Without the flag the first throwing 'message' listener ends the dispatch, unreported, and errors the EventSource out. - Lifetime: a pending read retains its EventSource; once the stream has ended, nothing does.
- Prototype pollution: the TypeScript implementation reaches built-ins
only through primordials. The C++ implementation calls no built-in
either, except that reading the TypeScript streams, the native pump
resolves a promise with another, which looks up
Promise.prototype.then;pollution.jsleaves that unpatched for thecpp-ts-streamscell.
Compatibility flags
| Flag (enable date) | Selects | Unflagged behavior tested by |
set_tostring_tag (2024-09-26) | [object EventSource] branding | legacyToStringTag |
spec_compliant_dispatch_exceptions (2026-09-15) | reporting listener exceptions | legacyThrowingMessageListener |
spec_compliant_property_attributes (dateless) | accessor names; lengths; non-configurable constants; the global as an own property | the main cells (the -opt-in cells enable it) |
immutable_api_prototypes (dateless) | C++ constructor's prototype non-writable | the main cells (the -opt-in cells enable it) |
typescript_implemented_streams (experimental) | the streams an EventSource reads | the main cells (the -ts-streams cells enable it) |
The main cells pin the two dated flags on; the legacy cells pin them off
by their disable names, so every cell keeps its @all-compat-flags
variant. The main cells also pin nodejs_compat (node:assert,
node:util), enable_weak_ref and rpc (for gc.js), and
streams_enable_constructors.
Divergence ledger (C++ vs TypeScript)
| # | Area | C++ | TypeScript | Pinned in |
| 1 | Constructor static inheritance | getPrototypeOf(EventSource) === Function.prototype | === EventTarget | prototypeChains |
| 2 | Constructor's prototype | writable unless immutable_api_prototypes | never writable | constructorAttributes |
| 3 | Calling the constructor without new | JSG's TypeError | the language's TypeError | constructorRequiresNew |
| 4 | Empty event field | an event of type '' | a 'message' | emptyEventField |
| 5 | BOM split across chunks | not stripped | stripped | bomSplitAcrossChunks |
| 6 | id field containing U+0000 | taken | ignored | idWithNullIsIgnored |
| 7 | data field with an empty value | dispatches nothing | dispatches empty data | formatFieldData |
| 8 | CRLF split across chunks | ends two lines | ends one | crlfSplitAcrossChunks |
| 9 | Last event ID across a reconnection | the last id field parsed | the last dispatched event's | formatDataBeforeFinalEmptyLine |
| 10 | A listener closes the EventSource | the rest of the messages read with that one are dispatched | dropped | closeFromAListener |
| 11 | close() of a from() source | goes on reading the stream | cancels it | closeCancelsAFromStream |
| 12 | retry value | anything KJ parses as a uint32_t; larger values ignored | ASCII digits only; any length, clamped | not pinned: observable only through reconnection timing |
| 13 | Message dispatch | from a task per write | from the reaction to each read | not pinned: no cheap observable. Both are held by a Durable Object's input gate: a native body's read resumes through IoContext::run(), and a JavaScript stream's chunks are enqueued under the input lock |
| 14 | A from() source closed while a read is pending | retained until the read settles | collectable (its read is cancelled, #11) | not pinned: the C++ side is a leak |
| 15 | Collection after the last event | on the next major GC | one major GC later: an event wrapped before dispatch keeps its target alive until it is swept | not pinned: gc.js allows three collections |
| 16 | Last-Event-ID with leading or trailing whitespace | sent as parsed | trimmed, as Headers trims values | lastEventIdWhitespace |
The C++ implementation also logs a from() stream's non-byte chunk error twice; the TypeScript one, once. Not in the ledger, as only the logs see it.
Assertion catalogue
| Module | Asserts |
api-surface.js | static and prototype members, attributes, names and lengths; key order; prototype chains (#1); constructor attributes (#2); runtime class marks; toStringTag branding; dispose member; no own instance properties; the global |
inspect.js | util.inspect() of an EventSource and of EventSource.prototype |
construction.js | new required (#3); URL conversion and errors; absolute URLs; init, withCredentials and fetcher checks; conversion order; initial state; module scope refused |
illegal-invocation.js | every member brand-checked against every non-EventSource receiver |
from.js | argument checks; locked and disturbed streams; no receiver; synchronous open; reading to the end; chunk kinds; string chunks; non-byte chunks |
parser.js | data joining; ignored lines; leading space; line endings; unterminated events; BOMs; UTF-8; named events; last event ID; #4 to #8; WPT format cases |
request.js | request headers; a rejected fetch() closes |
response.js | status; content type; redirects; responses without a body |
reconnection.js | end of stream reconnects; Last-Event-ID; #16; #9; retry |
event-shapes.js | OpenEvent, MessageEvent and ErrorEvent shapes |
handlers.js | default null; positional ordering; on<type> for a named event |
close.js | idempotent and silent; dispose, which ignores a patched close(); closing from a listener (#10); cancelling a from() stream (#11) |
listener-exceptions.js | throwing 'message', 'open' and 'error' listeners |
context.js | cross-request callbacks and close(), with and without handle_cross_request_promise_resolution; close during reconnection; same-request delivery |
gc.js | pending reads retain, ended streams release, for native and JavaScript sources and in a Durable Object |
pollution.js | from() and connected delivery under patched built-ins |
legacy-shape.js | [object Object] |
legacy-listener-exceptions.js | the first throwing listener ends the dispatch, unreported |
server.js | the event streams connected EventSources fetch, through subrequest |
context-worker.js | context.js's fixture services |
which-impl.js | the cell's implementation and the flags the suite branches on |
helpers.js | descriptor attribute strings; streams; event waiting and recording |
Web Platform Tests
src/wpt runs the eventsource WPTs, but most need a base URL to resolve
relative URLs, which workerd lacks, or browser features (workers, CORS,
credentials). The format cases are adapted here instead, through
EventSource.from() or server.js: format-bom-2,
format-data-before-final-empty-line, format-field-data,
format-field-event-empty, format-field-parsing,
format-leading-space, format-newlines and format-null-character.
Tests that live elsewhere (by design)
src/workerd/api/eventsource-test.c++: a unit test of the C++ implementation's native pump, removed with it.eventSourceLegacyErrorinsrc/workerd/api/tests/legacy-dispatch-exceptions-test.js, which pins the legacy dispatch policy across several APIs at once.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

