diff --git a/.changeset/quiet-streams-remain.md b/.changeset/quiet-streams-remain.md new file mode 100644 index 0000000..1b113a1 --- /dev/null +++ b/.changeset/quiet-streams-remain.md @@ -0,0 +1,5 @@ +--- +'eventsource': patch +--- + +Copy URL objects during construction so later caller mutations do not change the exposed URL or reconnection target. diff --git a/src/EventSource.ts b/src/EventSource.ts index 75ed50f..4f6eac6 100644 --- a/src/EventSource.ts +++ b/src/EventSource.ts @@ -297,7 +297,7 @@ class EventSourceImpl extends EventTarget implements EventSource { try { if (url instanceof URL) { - this.#url = url + this.#url = new URL(url) } else if (typeof url === 'string') { this.#url = new URL(url, getBaseURL()) } else { diff --git a/test/client.test.ts b/test/client.test.ts index 18895e4..4c1c309 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -51,6 +51,45 @@ const bufferedEventTypes = [ {name: 'notice', eventType: 'notice'}, ] +test('snapshots the caller-owned URL during construction', () => { + const input = new URL(`${serverUrl}/counter?original=yes`) + const original = input.href + const es = new OurEventSource(input, esInit) + try { + input.pathname = '/changed' + input.search = '?changed=yes' + input.hash = '#changed' + expect(es.url).toBe(original) + } finally { + es.close() + } +}) + +test('reconnects to the original URL after the caller mutates its URL object', async () => { + const input = new URL(`${serverUrl}/counter?original=yes`) + const original = input.href + const requested: string[] = [] + const onMessage = getCallCounter({name: 'messages after reconnect'}) + const es = new OurEventSource(input, { + fetch(url, init) { + requested.push(url.toString()) + return request(url, init) + }, + }) + es.addEventListener('open', () => { + input.search = '?changed=yes' + }) + es.addEventListener('counter', onMessage.listener) + try { + await onMessage.waitForCallCount(4) + expect(requested.length).toBeGreaterThanOrEqual(2) + expect(requested.every((url) => url === original)).toBe(true) + expect(es.url).toBe(original) + } finally { + es.close() + } +}) + test.each(bufferedEventTypes)( 'stops dispatching buffered $name events when a listener closes the connection', async ({eventType}) => {