shared-persistent-connection 0.1.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-zh.md ADDED
@@ -0,0 +1,11 @@
1
+ # 共享持久连接
2
+ [中文](./README-zh.md) | [English](./README.md)
3
+
4
+ 解决浏览器多标签页使用同一长连接SSE,占用多个TCP连接(HTTP 1.1)的问题
5
+
6
+ 需要浏览器支持
7
+ - [navigator.locks](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/locks)
8
+ - [BroadcastChannel](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel)
9
+
10
+ 特色
11
+ - SSE支持多种请求(GET,POST等),自定义头部,错误重试策略
package/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # shared-persistent-connection
2
+ [中文](./README-zh.md) | [English](./README.md)
3
+
4
+ Using the same connection (SSE, WebSocket) for multiple browser tabs solves the problem of multiple connections occupying multiple TCP connections
5
+
6
+ Features
7
+ - [x] SSE
8
+ - [x] WebSocket
@@ -0,0 +1,305 @@
1
+ 'use strict';
2
+
3
+ async function getBytes(stream, onChunk) {
4
+ const reader = stream.getReader();
5
+ let result;
6
+ while (!(result = await reader.read()).done) {
7
+ onChunk(result.value);
8
+ }
9
+ }
10
+ function getLines(onLine) {
11
+ let buffer;
12
+ let position;
13
+ let fieldLength;
14
+ let discardTrailingNewline = false;
15
+ return function onChunk(arr) {
16
+ if (buffer === undefined) {
17
+ buffer = arr;
18
+ position = 0;
19
+ fieldLength = -1;
20
+ }
21
+ else {
22
+ buffer = concat(buffer, arr);
23
+ }
24
+ const bufLength = buffer.length;
25
+ let lineStart = 0;
26
+ while (position < bufLength) {
27
+ if (discardTrailingNewline) {
28
+ if (buffer[position] === 10) {
29
+ lineStart = ++position;
30
+ }
31
+ discardTrailingNewline = false;
32
+ }
33
+ let lineEnd = -1;
34
+ for (; position < bufLength && lineEnd === -1; ++position) {
35
+ switch (buffer[position]) {
36
+ case 58:
37
+ if (fieldLength === -1) {
38
+ fieldLength = position - lineStart;
39
+ }
40
+ break;
41
+ case 13:
42
+ discardTrailingNewline = true;
43
+ case 10:
44
+ lineEnd = position;
45
+ break;
46
+ }
47
+ }
48
+ if (lineEnd === -1) {
49
+ break;
50
+ }
51
+ onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
52
+ lineStart = position;
53
+ fieldLength = -1;
54
+ }
55
+ if (lineStart === bufLength) {
56
+ buffer = undefined;
57
+ }
58
+ else if (lineStart !== 0) {
59
+ buffer = buffer.subarray(lineStart);
60
+ position -= lineStart;
61
+ }
62
+ };
63
+ }
64
+ function getMessages(onId, onRetry, onMessage) {
65
+ let message = newMessage();
66
+ const decoder = new TextDecoder();
67
+ return function onLine(line, fieldLength) {
68
+ if (line.length === 0) {
69
+ onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
70
+ message = newMessage();
71
+ }
72
+ else if (fieldLength > 0) {
73
+ const field = decoder.decode(line.subarray(0, fieldLength));
74
+ const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
75
+ const value = decoder.decode(line.subarray(valueOffset));
76
+ switch (field) {
77
+ case 'data':
78
+ message.data = message.data
79
+ ? message.data + '\n' + value
80
+ : value;
81
+ break;
82
+ case 'event':
83
+ message.event = value;
84
+ break;
85
+ case 'id':
86
+ onId(message.id = value);
87
+ break;
88
+ case 'retry':
89
+ const retry = parseInt(value, 10);
90
+ if (!isNaN(retry)) {
91
+ onRetry(message.retry = retry);
92
+ }
93
+ break;
94
+ }
95
+ }
96
+ };
97
+ }
98
+ function concat(a, b) {
99
+ const res = new Uint8Array(a.length + b.length);
100
+ res.set(a);
101
+ res.set(b, a.length);
102
+ return res;
103
+ }
104
+ function newMessage() {
105
+ return {
106
+ data: '',
107
+ event: '',
108
+ id: '',
109
+ retry: undefined,
110
+ };
111
+ }
112
+
113
+ var __rest = (undefined && undefined.__rest) || function (s, e) {
114
+ var t = {};
115
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
116
+ t[p] = s[p];
117
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
118
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
119
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
120
+ t[p[i]] = s[p[i]];
121
+ }
122
+ return t;
123
+ };
124
+ const EventStreamContentType = 'text/event-stream';
125
+ const DefaultRetryInterval = 1000;
126
+ const LastEventId = 'last-event-id';
127
+ function fetchEventSource(input, _a) {
128
+ var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
129
+ return new Promise((resolve, reject) => {
130
+ const headers = Object.assign({}, inputHeaders);
131
+ if (!headers.accept) {
132
+ headers.accept = EventStreamContentType;
133
+ }
134
+ let curRequestController;
135
+ function onVisibilityChange() {
136
+ curRequestController.abort();
137
+ if (!document.hidden) {
138
+ create();
139
+ }
140
+ }
141
+ if (!openWhenHidden) {
142
+ document.addEventListener('visibilitychange', onVisibilityChange);
143
+ }
144
+ let retryInterval = DefaultRetryInterval;
145
+ let retryTimer = 0;
146
+ function dispose() {
147
+ document.removeEventListener('visibilitychange', onVisibilityChange);
148
+ window.clearTimeout(retryTimer);
149
+ curRequestController.abort();
150
+ }
151
+ inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
152
+ dispose();
153
+ resolve();
154
+ });
155
+ const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
156
+ const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
157
+ async function create() {
158
+ var _a;
159
+ curRequestController = new AbortController();
160
+ try {
161
+ const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
162
+ await onopen(response);
163
+ await getBytes(response.body, getLines(getMessages(id => {
164
+ if (id) {
165
+ headers[LastEventId] = id;
166
+ }
167
+ else {
168
+ delete headers[LastEventId];
169
+ }
170
+ }, retry => {
171
+ retryInterval = retry;
172
+ }, onmessage)));
173
+ onclose === null || onclose === void 0 ? void 0 : onclose();
174
+ dispose();
175
+ resolve();
176
+ }
177
+ catch (err) {
178
+ if (!curRequestController.signal.aborted) {
179
+ try {
180
+ const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
181
+ window.clearTimeout(retryTimer);
182
+ retryTimer = window.setTimeout(create, interval);
183
+ }
184
+ catch (innerErr) {
185
+ dispose();
186
+ reject(innerErr);
187
+ }
188
+ }
189
+ }
190
+ }
191
+ create();
192
+ });
193
+ }
194
+ function defaultOnOpen(response) {
195
+ const contentType = response.headers.get('content-type');
196
+ if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
197
+ throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
198
+ }
199
+ }
200
+
201
+ function getSelfKey(key, type) {
202
+ return `shared-persistent-connection:${key}:${type}`;
203
+ }
204
+ var ConnectionState;
205
+ (function (ConnectionState) {
206
+ ConnectionState["Closed"] = "closed";
207
+ ConnectionState["Connecting"] = "connecting";
208
+ ConnectionState["Open"] = "open";
209
+ })(ConnectionState || (ConnectionState = {}));
210
+ const instances = new Map();
211
+ class SharedPersistentConnection {
212
+ url;
213
+ config;
214
+ isLeader = false;
215
+ releaseLock = null;
216
+ channel;
217
+ state;
218
+ abortController = null;
219
+ constructor(url, config = {}) {
220
+ this.url = url;
221
+ this.config = config;
222
+ if (!navigator.locks || !window.BroadcastChannel) {
223
+ throw new Error('SharedPersistentConnection is not supported in this browser');
224
+ }
225
+ if (instances.has(url)) {
226
+ console.warn(`[SharedPersistentConnection] Duplicate connection for url "${url}".
227
+ Only one instance per page is allowed.
228
+ Drop the old instance.
229
+ `);
230
+ instances.get(url).close();
231
+ }
232
+ instances.set(url, this);
233
+ this.channel = new BroadcastChannel(getSelfKey(this.url, 'channel'));
234
+ this.channel.onmessage = (ev) => {
235
+ this.config.onmessage?.(ev.data);
236
+ };
237
+ this.state = ConnectionState.Connecting;
238
+ this.attemptToBeLeader();
239
+ }
240
+ abortConnection() {
241
+ this.abortController && this.abortController.abort();
242
+ }
243
+ launch() {
244
+ if (!this.isLeader || this.isClosed()) {
245
+ return;
246
+ }
247
+ this.abortConnection();
248
+ this.abortController = new AbortController();
249
+ try {
250
+ fetchEventSource(this.url, {
251
+ ...this.config,
252
+ signal: this.abortController.signal,
253
+ onmessage: (event) => {
254
+ this.config.onmessage?.(event);
255
+ this.channel.postMessage(event);
256
+ },
257
+ onopen: (response) => {
258
+ this.state = ConnectionState.Open;
259
+ if (this.config.onopen) {
260
+ return this.config.onopen?.(response);
261
+ }
262
+ return Promise.resolve();
263
+ },
264
+ onclose: () => {
265
+ this.config.onclose?.();
266
+ this.close();
267
+ },
268
+ onerror: (err) => {
269
+ this.config.onerror?.(err);
270
+ }
271
+ });
272
+ }
273
+ catch (e) {
274
+ this.config.onerror?.(e);
275
+ }
276
+ }
277
+ isClosed() {
278
+ return this.state === ConnectionState.Closed;
279
+ }
280
+ attemptToBeLeader() {
281
+ const lockKey = getSelfKey(this.url, 'lock');
282
+ navigator.locks.request(lockKey, async () => {
283
+ if (this.isClosed()) {
284
+ return;
285
+ }
286
+ this.isLeader = true;
287
+ this.launch();
288
+ await new Promise(resolve => {
289
+ this.releaseLock = resolve;
290
+ });
291
+ this.isLeader = false;
292
+ });
293
+ }
294
+ close() {
295
+ if (this.isClosed()) {
296
+ return;
297
+ }
298
+ this.state = ConnectionState.Closed;
299
+ this.releaseLock?.();
300
+ this.abortConnection();
301
+ this.channel.close();
302
+ }
303
+ }
304
+
305
+ module.exports = SharedPersistentConnection;
@@ -0,0 +1,20 @@
1
+ import { type FetchEventSourceInit } from "@microsoft/fetch-event-source";
2
+ type FetchEventSourceInitConfig = FetchEventSourceInit & {
3
+ retryWhenError?: boolean;
4
+ };
5
+ export default class SharedPersistentConnection {
6
+ readonly url: string;
7
+ readonly config: FetchEventSourceInitConfig;
8
+ private isLeader;
9
+ private releaseLock;
10
+ private channel;
11
+ private state;
12
+ private abortController;
13
+ constructor(url: string, config?: FetchEventSourceInitConfig);
14
+ private abortConnection;
15
+ private launch;
16
+ private isClosed;
17
+ private attemptToBeLeader;
18
+ close(): void;
19
+ }
20
+ export {};
@@ -0,0 +1,303 @@
1
+ async function getBytes(stream, onChunk) {
2
+ const reader = stream.getReader();
3
+ let result;
4
+ while (!(result = await reader.read()).done) {
5
+ onChunk(result.value);
6
+ }
7
+ }
8
+ function getLines(onLine) {
9
+ let buffer;
10
+ let position;
11
+ let fieldLength;
12
+ let discardTrailingNewline = false;
13
+ return function onChunk(arr) {
14
+ if (buffer === undefined) {
15
+ buffer = arr;
16
+ position = 0;
17
+ fieldLength = -1;
18
+ }
19
+ else {
20
+ buffer = concat(buffer, arr);
21
+ }
22
+ const bufLength = buffer.length;
23
+ let lineStart = 0;
24
+ while (position < bufLength) {
25
+ if (discardTrailingNewline) {
26
+ if (buffer[position] === 10) {
27
+ lineStart = ++position;
28
+ }
29
+ discardTrailingNewline = false;
30
+ }
31
+ let lineEnd = -1;
32
+ for (; position < bufLength && lineEnd === -1; ++position) {
33
+ switch (buffer[position]) {
34
+ case 58:
35
+ if (fieldLength === -1) {
36
+ fieldLength = position - lineStart;
37
+ }
38
+ break;
39
+ case 13:
40
+ discardTrailingNewline = true;
41
+ case 10:
42
+ lineEnd = position;
43
+ break;
44
+ }
45
+ }
46
+ if (lineEnd === -1) {
47
+ break;
48
+ }
49
+ onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
50
+ lineStart = position;
51
+ fieldLength = -1;
52
+ }
53
+ if (lineStart === bufLength) {
54
+ buffer = undefined;
55
+ }
56
+ else if (lineStart !== 0) {
57
+ buffer = buffer.subarray(lineStart);
58
+ position -= lineStart;
59
+ }
60
+ };
61
+ }
62
+ function getMessages(onId, onRetry, onMessage) {
63
+ let message = newMessage();
64
+ const decoder = new TextDecoder();
65
+ return function onLine(line, fieldLength) {
66
+ if (line.length === 0) {
67
+ onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
68
+ message = newMessage();
69
+ }
70
+ else if (fieldLength > 0) {
71
+ const field = decoder.decode(line.subarray(0, fieldLength));
72
+ const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
73
+ const value = decoder.decode(line.subarray(valueOffset));
74
+ switch (field) {
75
+ case 'data':
76
+ message.data = message.data
77
+ ? message.data + '\n' + value
78
+ : value;
79
+ break;
80
+ case 'event':
81
+ message.event = value;
82
+ break;
83
+ case 'id':
84
+ onId(message.id = value);
85
+ break;
86
+ case 'retry':
87
+ const retry = parseInt(value, 10);
88
+ if (!isNaN(retry)) {
89
+ onRetry(message.retry = retry);
90
+ }
91
+ break;
92
+ }
93
+ }
94
+ };
95
+ }
96
+ function concat(a, b) {
97
+ const res = new Uint8Array(a.length + b.length);
98
+ res.set(a);
99
+ res.set(b, a.length);
100
+ return res;
101
+ }
102
+ function newMessage() {
103
+ return {
104
+ data: '',
105
+ event: '',
106
+ id: '',
107
+ retry: undefined,
108
+ };
109
+ }
110
+
111
+ var __rest = (undefined && undefined.__rest) || function (s, e) {
112
+ var t = {};
113
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
114
+ t[p] = s[p];
115
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
116
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
117
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
118
+ t[p[i]] = s[p[i]];
119
+ }
120
+ return t;
121
+ };
122
+ const EventStreamContentType = 'text/event-stream';
123
+ const DefaultRetryInterval = 1000;
124
+ const LastEventId = 'last-event-id';
125
+ function fetchEventSource(input, _a) {
126
+ var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
127
+ return new Promise((resolve, reject) => {
128
+ const headers = Object.assign({}, inputHeaders);
129
+ if (!headers.accept) {
130
+ headers.accept = EventStreamContentType;
131
+ }
132
+ let curRequestController;
133
+ function onVisibilityChange() {
134
+ curRequestController.abort();
135
+ if (!document.hidden) {
136
+ create();
137
+ }
138
+ }
139
+ if (!openWhenHidden) {
140
+ document.addEventListener('visibilitychange', onVisibilityChange);
141
+ }
142
+ let retryInterval = DefaultRetryInterval;
143
+ let retryTimer = 0;
144
+ function dispose() {
145
+ document.removeEventListener('visibilitychange', onVisibilityChange);
146
+ window.clearTimeout(retryTimer);
147
+ curRequestController.abort();
148
+ }
149
+ inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
150
+ dispose();
151
+ resolve();
152
+ });
153
+ const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
154
+ const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
155
+ async function create() {
156
+ var _a;
157
+ curRequestController = new AbortController();
158
+ try {
159
+ const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
160
+ await onopen(response);
161
+ await getBytes(response.body, getLines(getMessages(id => {
162
+ if (id) {
163
+ headers[LastEventId] = id;
164
+ }
165
+ else {
166
+ delete headers[LastEventId];
167
+ }
168
+ }, retry => {
169
+ retryInterval = retry;
170
+ }, onmessage)));
171
+ onclose === null || onclose === void 0 ? void 0 : onclose();
172
+ dispose();
173
+ resolve();
174
+ }
175
+ catch (err) {
176
+ if (!curRequestController.signal.aborted) {
177
+ try {
178
+ const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
179
+ window.clearTimeout(retryTimer);
180
+ retryTimer = window.setTimeout(create, interval);
181
+ }
182
+ catch (innerErr) {
183
+ dispose();
184
+ reject(innerErr);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ create();
190
+ });
191
+ }
192
+ function defaultOnOpen(response) {
193
+ const contentType = response.headers.get('content-type');
194
+ if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
195
+ throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
196
+ }
197
+ }
198
+
199
+ function getSelfKey(key, type) {
200
+ return `shared-persistent-connection:${key}:${type}`;
201
+ }
202
+ var ConnectionState;
203
+ (function (ConnectionState) {
204
+ ConnectionState["Closed"] = "closed";
205
+ ConnectionState["Connecting"] = "connecting";
206
+ ConnectionState["Open"] = "open";
207
+ })(ConnectionState || (ConnectionState = {}));
208
+ const instances = new Map();
209
+ class SharedPersistentConnection {
210
+ url;
211
+ config;
212
+ isLeader = false;
213
+ releaseLock = null;
214
+ channel;
215
+ state;
216
+ abortController = null;
217
+ constructor(url, config = {}) {
218
+ this.url = url;
219
+ this.config = config;
220
+ if (!navigator.locks || !window.BroadcastChannel) {
221
+ throw new Error('SharedPersistentConnection is not supported in this browser');
222
+ }
223
+ if (instances.has(url)) {
224
+ console.warn(`[SharedPersistentConnection] Duplicate connection for url "${url}".
225
+ Only one instance per page is allowed.
226
+ Drop the old instance.
227
+ `);
228
+ instances.get(url).close();
229
+ }
230
+ instances.set(url, this);
231
+ this.channel = new BroadcastChannel(getSelfKey(this.url, 'channel'));
232
+ this.channel.onmessage = (ev) => {
233
+ this.config.onmessage?.(ev.data);
234
+ };
235
+ this.state = ConnectionState.Connecting;
236
+ this.attemptToBeLeader();
237
+ }
238
+ abortConnection() {
239
+ this.abortController && this.abortController.abort();
240
+ }
241
+ launch() {
242
+ if (!this.isLeader || this.isClosed()) {
243
+ return;
244
+ }
245
+ this.abortConnection();
246
+ this.abortController = new AbortController();
247
+ try {
248
+ fetchEventSource(this.url, {
249
+ ...this.config,
250
+ signal: this.abortController.signal,
251
+ onmessage: (event) => {
252
+ this.config.onmessage?.(event);
253
+ this.channel.postMessage(event);
254
+ },
255
+ onopen: (response) => {
256
+ this.state = ConnectionState.Open;
257
+ if (this.config.onopen) {
258
+ return this.config.onopen?.(response);
259
+ }
260
+ return Promise.resolve();
261
+ },
262
+ onclose: () => {
263
+ this.config.onclose?.();
264
+ this.close();
265
+ },
266
+ onerror: (err) => {
267
+ this.config.onerror?.(err);
268
+ }
269
+ });
270
+ }
271
+ catch (e) {
272
+ this.config.onerror?.(e);
273
+ }
274
+ }
275
+ isClosed() {
276
+ return this.state === ConnectionState.Closed;
277
+ }
278
+ attemptToBeLeader() {
279
+ const lockKey = getSelfKey(this.url, 'lock');
280
+ navigator.locks.request(lockKey, async () => {
281
+ if (this.isClosed()) {
282
+ return;
283
+ }
284
+ this.isLeader = true;
285
+ this.launch();
286
+ await new Promise(resolve => {
287
+ this.releaseLock = resolve;
288
+ });
289
+ this.isLeader = false;
290
+ });
291
+ }
292
+ close() {
293
+ if (this.isClosed()) {
294
+ return;
295
+ }
296
+ this.state = ConnectionState.Closed;
297
+ this.releaseLock?.();
298
+ this.abortConnection();
299
+ this.channel.close();
300
+ }
301
+ }
302
+
303
+ export { SharedPersistentConnection as default };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "shared-persistent-connection",
3
+ "author": "Monkey-D-sj",
4
+ "license": "ISC",
5
+ "version": "0.1.0",
6
+ "main": "./dist/index.js",
7
+ "description": "multi browser tabs use one sse connection",
8
+ "repository": "https://github.com/Monkey-D-sj/shared-persistent-connection",
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "rimraf dist && rollup -c rollup.config.ts",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "share persistent connection",
16
+ "sse"
17
+ ],
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "README-zh.md"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "import": "./dist/index.esm.js",
27
+ "require": "./dist/index.cjs.js",
28
+ "types": "./dist/index.d.ts"
29
+ }
30
+ },
31
+ "packageManager": "pnpm@10.28.0",
32
+ "devDependencies": {
33
+ "@rollup/plugin-node-resolve": "^16.0.3",
34
+ "@rollup/plugin-typescript": "^12.3.0",
35
+ "rimraf": "^6.1.2",
36
+ "rollup": "^4.55.1",
37
+ "tslib": "^2.8.1",
38
+ "typescript": "^5.9.3"
39
+ },
40
+ "dependencies": {
41
+ "@microsoft/fetch-event-source": "^2.0.1"
42
+ },
43
+ "volta": {
44
+ "node": "25.3.0"
45
+ }
46
+ }