@rg-dev/stdlib
Advanced tools
+122
-0
@@ -190,2 +190,4 @@ var __create = Object.create; | ||
| __export(node_env_exports, { | ||
| SSEClient: () => SSEClient, | ||
| SSEResponse: () => SSEResponse, | ||
| checkCommandExistsOrThrow: () => checkCommandExistsOrThrow, | ||
@@ -204,2 +206,122 @@ checkIfDirExistsOrThrow: () => checkIfDirExistsOrThrow, | ||
| var import_command_exists = __toESM(require_command_exists2(), 1); | ||
| // src/SSEResponse.ts | ||
| var SSEResponse = class { | ||
| res; | ||
| isOpen = true; | ||
| constructor(res) { | ||
| this.res = res; | ||
| res.setHeader("Content-Type", "text/event-stream"); | ||
| res.setHeader("Cache-Control", "no-cache"); | ||
| res.setHeader("Connection", "keep-alive"); | ||
| } | ||
| flushHeaders() { | ||
| var _a; | ||
| (_a = this.res) == null ? void 0 : _a.flushHeaders(); | ||
| } | ||
| /** Send JSON to client, optionally with a custom event type */ | ||
| emit(data, event) { | ||
| if (!this.isOpen) | ||
| return; | ||
| try { | ||
| let payload = ""; | ||
| if (event) { | ||
| payload += `event: ${event} | ||
| `; | ||
| } | ||
| payload += `data: ${JSON.stringify(data)} | ||
| `; | ||
| if (this.res.writable) { | ||
| this.res.write(payload); | ||
| } | ||
| } catch (e) { | ||
| console.log("SSE Write error", e); | ||
| } | ||
| } | ||
| /** Close the SSE connection */ | ||
| close() { | ||
| if (!this.isOpen) { | ||
| return; | ||
| } | ||
| this.isOpen = false; | ||
| if (!this.res.writableEnded) { | ||
| this.res.end(); | ||
| } | ||
| } | ||
| }; | ||
| // src/SSEClient.ts | ||
| var import_events = require("events"); | ||
| var SSEClient = class extends import_events.EventEmitter { | ||
| url; | ||
| payload; | ||
| headers; | ||
| controller; | ||
| constructor(options) { | ||
| super(); | ||
| this.url = options.url; | ||
| this.payload = options.payload || {}; | ||
| this.headers = { | ||
| "Content-Type": "application/json", | ||
| Accept: "*/*", | ||
| ...options.headers | ||
| }; | ||
| this.controller = new AbortController(); | ||
| } | ||
| async connect() { | ||
| try { | ||
| const res = await fetch(this.url, { | ||
| method: "POST", | ||
| headers: this.headers, | ||
| body: JSON.stringify(this.payload), | ||
| signal: this.controller.signal | ||
| }); | ||
| if (!res.ok) { | ||
| const data = await res.text(); | ||
| this.emit("error", new Error(`HTTP error: ${res.status} ${data}`)); | ||
| return; | ||
| } | ||
| const reader = res.body.getReader(); | ||
| const decoder = new TextDecoder("utf-8"); | ||
| let buffer = ""; | ||
| let eventName = "message"; | ||
| while (true) { | ||
| const { value, done } = await reader.read(); | ||
| if (done) | ||
| break; | ||
| buffer += decoder.decode(value, { stream: true }); | ||
| let lines = buffer.split("\n"); | ||
| buffer = lines.pop(); | ||
| for (let line of lines) { | ||
| line = line.trim(); | ||
| if (!line) | ||
| continue; | ||
| if (line.startsWith("event:")) { | ||
| eventName = line.slice(6).trim(); | ||
| } else if (line.startsWith("data:")) { | ||
| let data = line.slice(5).trim(); | ||
| try { | ||
| data = JSON.parse(data); | ||
| } catch { | ||
| } | ||
| this.emit(eventName, data); | ||
| eventName = "message"; | ||
| } | ||
| } | ||
| } | ||
| this.emit("end"); | ||
| } catch (err) { | ||
| if (err.name !== "AbortError") { | ||
| this.emit("error", err); | ||
| } | ||
| } | ||
| } | ||
| close() { | ||
| this.controller.abort(); | ||
| this.emit("close"); | ||
| } | ||
| }; | ||
| // src/node-env.ts | ||
| function isWindows() { | ||
@@ -206,0 +328,0 @@ return import_os.default.platform() === "win32"; |
+30
-1
@@ -0,1 +1,30 @@ | ||
| import { Response } from 'express'; | ||
| import { EventEmitter } from 'events'; | ||
| declare class SSEResponse<Events extends string = 'message'> { | ||
| private res; | ||
| private isOpen; | ||
| constructor(res: Response); | ||
| flushHeaders(): void; | ||
| /** Send JSON to client, optionally with a custom event type */ | ||
| emit(data: unknown, event?: Events): void; | ||
| /** Close the SSE connection */ | ||
| close(): void; | ||
| } | ||
| interface SSEClientOptions { | ||
| url: string; | ||
| payload?: Record<string, any>; | ||
| headers?: Record<string, string>; | ||
| } | ||
| declare class SSEClient extends EventEmitter { | ||
| private url; | ||
| private payload; | ||
| private headers; | ||
| private controller; | ||
| constructor(options: SSEClientOptions); | ||
| connect(): Promise<void>; | ||
| close(): void; | ||
| } | ||
| declare function isWindows(): boolean; | ||
@@ -9,2 +38,2 @@ declare function chmodPlusX(filePath: string): void; | ||
| export { checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty }; | ||
| export { SSEClient, SSEResponse, checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty }; |
+30
-1
@@ -0,1 +1,30 @@ | ||
| import { Response } from 'express'; | ||
| import { EventEmitter } from 'events'; | ||
| declare class SSEResponse<Events extends string = 'message'> { | ||
| private res; | ||
| private isOpen; | ||
| constructor(res: Response); | ||
| flushHeaders(): void; | ||
| /** Send JSON to client, optionally with a custom event type */ | ||
| emit(data: unknown, event?: Events): void; | ||
| /** Close the SSE connection */ | ||
| close(): void; | ||
| } | ||
| interface SSEClientOptions { | ||
| url: string; | ||
| payload?: Record<string, any>; | ||
| headers?: Record<string, string>; | ||
| } | ||
| declare class SSEClient extends EventEmitter { | ||
| private url; | ||
| private payload; | ||
| private headers; | ||
| private controller; | ||
| constructor(options: SSEClientOptions); | ||
| connect(): Promise<void>; | ||
| close(): void; | ||
| } | ||
| declare function isWindows(): boolean; | ||
@@ -9,2 +38,2 @@ declare function chmodPlusX(filePath: string): void; | ||
| export { checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty }; | ||
| export { SSEClient, SSEResponse, checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty }; |
+122
-0
@@ -194,2 +194,122 @@ var __create = Object.create; | ||
| import path from "path"; | ||
| // src/SSEResponse.ts | ||
| var SSEResponse = class { | ||
| res; | ||
| isOpen = true; | ||
| constructor(res) { | ||
| this.res = res; | ||
| res.setHeader("Content-Type", "text/event-stream"); | ||
| res.setHeader("Cache-Control", "no-cache"); | ||
| res.setHeader("Connection", "keep-alive"); | ||
| } | ||
| flushHeaders() { | ||
| var _a; | ||
| (_a = this.res) == null ? void 0 : _a.flushHeaders(); | ||
| } | ||
| /** Send JSON to client, optionally with a custom event type */ | ||
| emit(data, event) { | ||
| if (!this.isOpen) | ||
| return; | ||
| try { | ||
| let payload = ""; | ||
| if (event) { | ||
| payload += `event: ${event} | ||
| `; | ||
| } | ||
| payload += `data: ${JSON.stringify(data)} | ||
| `; | ||
| if (this.res.writable) { | ||
| this.res.write(payload); | ||
| } | ||
| } catch (e) { | ||
| console.log("SSE Write error", e); | ||
| } | ||
| } | ||
| /** Close the SSE connection */ | ||
| close() { | ||
| if (!this.isOpen) { | ||
| return; | ||
| } | ||
| this.isOpen = false; | ||
| if (!this.res.writableEnded) { | ||
| this.res.end(); | ||
| } | ||
| } | ||
| }; | ||
| // src/SSEClient.ts | ||
| import { EventEmitter } from "events"; | ||
| var SSEClient = class extends EventEmitter { | ||
| url; | ||
| payload; | ||
| headers; | ||
| controller; | ||
| constructor(options) { | ||
| super(); | ||
| this.url = options.url; | ||
| this.payload = options.payload || {}; | ||
| this.headers = { | ||
| "Content-Type": "application/json", | ||
| Accept: "*/*", | ||
| ...options.headers | ||
| }; | ||
| this.controller = new AbortController(); | ||
| } | ||
| async connect() { | ||
| try { | ||
| const res = await fetch(this.url, { | ||
| method: "POST", | ||
| headers: this.headers, | ||
| body: JSON.stringify(this.payload), | ||
| signal: this.controller.signal | ||
| }); | ||
| if (!res.ok) { | ||
| const data = await res.text(); | ||
| this.emit("error", new Error(`HTTP error: ${res.status} ${data}`)); | ||
| return; | ||
| } | ||
| const reader = res.body.getReader(); | ||
| const decoder = new TextDecoder("utf-8"); | ||
| let buffer = ""; | ||
| let eventName = "message"; | ||
| while (true) { | ||
| const { value, done } = await reader.read(); | ||
| if (done) | ||
| break; | ||
| buffer += decoder.decode(value, { stream: true }); | ||
| let lines = buffer.split("\n"); | ||
| buffer = lines.pop(); | ||
| for (let line of lines) { | ||
| line = line.trim(); | ||
| if (!line) | ||
| continue; | ||
| if (line.startsWith("event:")) { | ||
| eventName = line.slice(6).trim(); | ||
| } else if (line.startsWith("data:")) { | ||
| let data = line.slice(5).trim(); | ||
| try { | ||
| data = JSON.parse(data); | ||
| } catch { | ||
| } | ||
| this.emit(eventName, data); | ||
| eventName = "message"; | ||
| } | ||
| } | ||
| } | ||
| this.emit("end"); | ||
| } catch (err) { | ||
| if (err.name !== "AbortError") { | ||
| this.emit("error", err); | ||
| } | ||
| } | ||
| } | ||
| close() { | ||
| this.controller.abort(); | ||
| this.emit("close"); | ||
| } | ||
| }; | ||
| // src/node-env.ts | ||
| function isWindows() { | ||
@@ -260,2 +380,4 @@ return os.platform() === "win32"; | ||
| export { | ||
| SSEClient, | ||
| SSEResponse, | ||
| checkCommandExistsOrThrow, | ||
@@ -262,0 +384,0 @@ checkIfDirExistsOrThrow, |
+2
-4
| { | ||
| "name": "@rg-dev/stdlib", | ||
| "version": "1.0.21", | ||
| "version": "1.0.22", | ||
| "description": "", | ||
@@ -14,5 +14,2 @@ "scripts": { | ||
| ], | ||
| "typesVersions": { | ||
@@ -62,2 +59,3 @@ "*": { | ||
| "command-exists": "^1.2.9", | ||
| "express": "^5.1.0", | ||
| "node-fetch": "^3.3.2", | ||
@@ -64,0 +62,0 @@ "tsup": "^8.0.1", |
536842
1.41%13446
1.99%9
12.5%12
20%