Repository: denoland/std (TypeScript)

Issue:
[http/server_sent_event] Uncaught TypeError if created without optional EventInit
**Describe the bug**

For my application, I have a ServerSentEvent that doesn't have any data. I'm sending a reload event to the browser to tell it to reload with the following line of code, where target is a ServerSentEventTarget. This worked previously with Oak before ServerSentEvent was moved to std/http.

```ts
target.dispatchEvent(new ServerSentEvent("reload"));
```

When this line is reached, I end up getting the following uncaught exception.

```
error: Uncaught TypeError: Cannot read properties of undefined (reading 'split')
    const data = `data: ${this.#data.split("\n").join("\ndata: ")}\n`;
                                     ^
    at Event.toString (https://deno.land/std@0.185.0/http/server_sent_event.ts:159:38)
    at String (<anonymous>)
    at ServerSentEventStreamTarget.dispatchEvent (https://deno.land/std@0.185.0/http/server_sent_event.ts:376:18)
```

There are 2 possible fixes for this issue. I'm not too familiar with how it worked before so I'm not sure which is best.

1. In the constructor, the following lines are suppose to ensure `this.#data` is set to a string value. However, if data is undefined, JSON.stringify will also return undefined instead of a string. Adding a fallback to an empty string would ensure it's always a string like it is suppose to be.

```ts
this.#data = typeof data === "string"
  ? data
  : JSON.stringify(data, replacer as (string | number)[], space);
```

2. The toString function currently assumes this.#data is always a string. It could be changed to only add data to the string returned if data is set to a non empty string value. If the response requires the data key even if it's empty, I think the first fix would be best instead of changing this function.

```
override toString(): string {
  const data = `data: ${this.#data.split("\n").join("\ndata: ")}\n`;
  return `${this.#type === "__message" ? "" : `event: ${this.#type}\n`}${
      this.#id ? `id: ${String(this.#id)}\n` : ""
    }${data}\n`;
}
```

**Steps to Reproduce**

Take the example and create the ServerSentEvent without the optional eventInit argument.

**Expected behavior**

No uncaught type error. The browser should receive the event even if it has no data.

**Environment**

- OS: Ubuntu 20.04
- deno version: 1.33.1
- std version: 0.185.0



Discussion:

Patch:
diff --git a/http/server_sent_event.ts b/http/server_sent_event.ts
index 1e61e2421cc3..606d47a95b17 100644
--- a/http/server_sent_event.ts
+++ b/http/server_sent_event.ts
@@ -132,7 +132,9 @@ export class ServerSentEvent extends Event {
     try {
       this.#data = typeof data === "string"
         ? data
-        : JSON.stringify(data, replacer as (string | number)[], space);
+        : data !== undefined
+        ? JSON.stringify(data, replacer as (string | number)[], space)
+        : "";
     } catch (e) {
       assert(e instanceof Error);
       throw new TypeError(


Test patch:
diff --git a/http/server_sent_event_test.ts b/http/server_sent_event_test.ts
index 343ec155ae66..28062a28fb1f 100644
--- a/http/server_sent_event_test.ts
+++ b/http/server_sent_event_test.ts
@@ -68,6 +68,16 @@ Deno.test({
   },
 });
 
+Deno.test({
+  name: "ServerSentEvent - without eventInit",
+  fn() {
+    const evt = new ServerSentEvent("reload");
+    assertEquals(evt.type, "reload");
+    assertEquals(evt.data, "");
+    assertEquals(String(evt), `event: reload\ndata: \n\n`);
+  },
+});
+
 Deno.test({
   name: "ServerSentEventStreamTarget - construction",
   async fn() {