light-sse 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # EventSource2
2
+
3
+ A lightweight Server-Sent Events (SSE) client library built with TypeScript. Unlike the native `EventSource` API, this library supports custom headers, making it ideal for authenticated SSE connections.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Basic Example
15
+
16
+ ```typescript
17
+ import { EventSource2 } from "./eventsource2";
18
+
19
+ const eventSource = await EventSource2.new("https://api.example.com/stream", {
20
+ onmessage: (payload) => {
21
+ console.log("Received:", payload);
22
+ },
23
+ onerror: (error) => {
24
+ console.error("Error:", error);
25
+ },
26
+ });
27
+
28
+ // When done, close the connection
29
+ eventSource.close();
30
+ ```
31
+
32
+ ### With Custom Headers
33
+
34
+ ```typescript
35
+ import { EventSource2 } from "./eventsource2";
36
+
37
+ const eventSource = await EventSource2.new(
38
+ "https://api.example.com/stream",
39
+ {
40
+ onmessage: (payload) => {
41
+ console.log("Received:", payload);
42
+ },
43
+ onerror: (error) => {
44
+ console.error("Error:", error);
45
+ },
46
+ },
47
+ {
48
+ Authorization: "Bearer your-token-here",
49
+ "X-Custom-Header": "custom-value",
50
+ }
51
+ );
52
+
53
+ eventSource.close();
54
+ ```
55
+
56
+ ## API
57
+
58
+ ### `EventSource2.new(url, callbacks, headers?)`
59
+
60
+ Creates a new SSE connection.
61
+
62
+ **Parameters:**
63
+
64
+ | Parameter | Type | Required | Description |
65
+ |-------------|-----------------------------------|----------|------------------------------------------|
66
+ | `url` | `string` | Yes | The SSE endpoint URL |
67
+ | `callbacks` | `{ onmessage, onerror? }` | Yes | Event handlers |
68
+ | `headers` | `HeadersInit` | No | Custom headers for the request |
69
+
70
+ **Callbacks:**
71
+
72
+ - `onmessage(payload: string)`: Called when a message is received. Returns the parsed data content.
73
+ - `onerror(error: any)`: Called when an error occurs (optional).
74
+
75
+ ### `eventSource.close()`
76
+
77
+ Closes the SSE connection and cancels the underlying reader.
78
+
79
+ ## How It Works
80
+
81
+ 1. **Connection**: Uses the Fetch API with `Accept: text/event-stream` header
82
+ 2. **Reading**: Utilizes `ReadableStream` to read chunks from the response body
83
+ 3. **Parsing**: Detects SSE message boundaries (`\n\n` or `\r\n\r\n`) and extracts `data:` fields
84
+ 4. **Decoding**: Uses `TextDecoder` to convert binary chunks to strings
85
+
86
+ ## SSE Format
87
+
88
+ The library expects the standard SSE format:
89
+
90
+ ```
91
+ data: Message 1
92
+
93
+ data: Message 2
94
+
95
+ data: Message 3
96
+ ```
97
+
98
+ Multiple `data:` lines within the same block are joined with newlines.
99
+
100
+ ## Browser Support
101
+
102
+ Requires browsers that support:
103
+ - [Fetch API](https://caniuse.com/fetch)
104
+ - [ReadableStream](https://caniuse.com/streams)
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventSource2 = void 0;
4
+ class EventSource2 {
5
+ reader;
6
+ decoder = new TextDecoder();
7
+ buffer = "";
8
+ onmessage;
9
+ onerror;
10
+ static async new(url, callbacks, headers) {
11
+ const event = new EventSource2();
12
+ await event._init(url, headers);
13
+ event.onmessage = callbacks.onmessage;
14
+ if (callbacks.onerror)
15
+ event.onerror = callbacks.onerror;
16
+ event.startReading();
17
+ return event;
18
+ }
19
+ async _init(url, headers = {}) {
20
+ const response = await fetch(url, {
21
+ method: 'GET',
22
+ headers: {
23
+ ...headers,
24
+ "Accept": "text/event-stream",
25
+ }
26
+ });
27
+ if (!response.ok) {
28
+ throw new Error(`Failed to connect to EventSource: ${response.status} ${response.statusText}`);
29
+ }
30
+ this.reader = response.body?.getReader();
31
+ }
32
+ async startReading() {
33
+ if (!this.reader)
34
+ return;
35
+ while (true) {
36
+ try {
37
+ const { value, done } = await this.reader.read();
38
+ if (done)
39
+ break;
40
+ this.buffer += this.decoder.decode(value, { stream: true });
41
+ let boundary = this.buffer.indexOf('\n\n');
42
+ while (boundary !== -1) {
43
+ const messageBlock = this.buffer.substring(0, boundary).trim();
44
+ this.buffer = this.buffer.substring(boundary + 2);
45
+ if (this.onmessage && messageBlock) {
46
+ const cleanMessage = this.parseSSE(messageBlock);
47
+ if (cleanMessage) {
48
+ this.onmessage(cleanMessage);
49
+ }
50
+ }
51
+ boundary = this.buffer.indexOf('\n\n');
52
+ }
53
+ }
54
+ catch (error) {
55
+ if (error?.name === 'AbortError')
56
+ break;
57
+ if (this.onerror)
58
+ this.onerror(error);
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ parseSSE(block) {
64
+ return block
65
+ .split('\n')
66
+ .filter(line => line.startsWith('data:'))
67
+ .map(line => line.replace(/^data:\s?/, ''))
68
+ .join('\n');
69
+ }
70
+ close() {
71
+ if (this.reader) {
72
+ this.reader.cancel().catch(() => { });
73
+ this.reader = undefined;
74
+ }
75
+ }
76
+ }
77
+ exports.EventSource2 = EventSource2;
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "light-sse",
3
+ "version": "1.0.0",
4
+ "main": "dist/main.js",
5
+ "description": "Tu descripción",
6
+ "author": "Tu nombre",
7
+ "license": "MIT",
8
+ "keywords": ["eventsource", "sse"],
9
+ "files": ["dist/"],
10
+ "scripts": {
11
+ "build": "tsc"
12
+ }
13
+ }