@rg-dev/stdlib 1.0.21 → 1.0.22

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/lib/node-env.cjs CHANGED
@@ -187,6 +187,8 @@ var require_command_exists2 = __commonJS({
187
187
  // src/node-env.ts
188
188
  var node_env_exports = {};
189
189
  __export(node_env_exports, {
190
+ SSEClient: () => SSEClient,
191
+ SSEResponse: () => SSEResponse,
190
192
  checkCommandExistsOrThrow: () => checkCommandExistsOrThrow,
191
193
  checkIfDirExistsOrThrow: () => checkIfDirExistsOrThrow,
192
194
  checkIfFileExistsOrThrow: () => checkIfFileExistsOrThrow,
@@ -200,6 +202,126 @@ var fs = __toESM(require("fs-extra"), 1);
200
202
  var import_os = __toESM(require("os"), 1);
201
203
  var import_path = __toESM(require("path"), 1);
202
204
  var import_command_exists = __toESM(require_command_exists2(), 1);
205
+
206
+ // src/SSEResponse.ts
207
+ var SSEResponse = class {
208
+ res;
209
+ isOpen = true;
210
+ constructor(res) {
211
+ this.res = res;
212
+ res.setHeader("Content-Type", "text/event-stream");
213
+ res.setHeader("Cache-Control", "no-cache");
214
+ res.setHeader("Connection", "keep-alive");
215
+ }
216
+ flushHeaders() {
217
+ var _a;
218
+ (_a = this.res) == null ? void 0 : _a.flushHeaders();
219
+ }
220
+ /** Send JSON to client, optionally with a custom event type */
221
+ emit(data, event) {
222
+ if (!this.isOpen)
223
+ return;
224
+ try {
225
+ let payload = "";
226
+ if (event) {
227
+ payload += `event: ${event}
228
+ `;
229
+ }
230
+ payload += `data: ${JSON.stringify(data)}
231
+
232
+ `;
233
+ if (this.res.writable) {
234
+ this.res.write(payload);
235
+ }
236
+ } catch (e) {
237
+ console.log("SSE Write error", e);
238
+ }
239
+ }
240
+ /** Close the SSE connection */
241
+ close() {
242
+ if (!this.isOpen) {
243
+ return;
244
+ }
245
+ this.isOpen = false;
246
+ if (!this.res.writableEnded) {
247
+ this.res.end();
248
+ }
249
+ }
250
+ };
251
+
252
+ // src/SSEClient.ts
253
+ var import_events = require("events");
254
+ var SSEClient = class extends import_events.EventEmitter {
255
+ url;
256
+ payload;
257
+ headers;
258
+ controller;
259
+ constructor(options) {
260
+ super();
261
+ this.url = options.url;
262
+ this.payload = options.payload || {};
263
+ this.headers = {
264
+ "Content-Type": "application/json",
265
+ Accept: "*/*",
266
+ ...options.headers
267
+ };
268
+ this.controller = new AbortController();
269
+ }
270
+ async connect() {
271
+ try {
272
+ const res = await fetch(this.url, {
273
+ method: "POST",
274
+ headers: this.headers,
275
+ body: JSON.stringify(this.payload),
276
+ signal: this.controller.signal
277
+ });
278
+ if (!res.ok) {
279
+ const data = await res.text();
280
+ this.emit("error", new Error(`HTTP error: ${res.status} ${data}`));
281
+ return;
282
+ }
283
+ const reader = res.body.getReader();
284
+ const decoder = new TextDecoder("utf-8");
285
+ let buffer = "";
286
+ let eventName = "message";
287
+ while (true) {
288
+ const { value, done } = await reader.read();
289
+ if (done)
290
+ break;
291
+ buffer += decoder.decode(value, { stream: true });
292
+ let lines = buffer.split("\n");
293
+ buffer = lines.pop();
294
+ for (let line of lines) {
295
+ line = line.trim();
296
+ if (!line)
297
+ continue;
298
+ if (line.startsWith("event:")) {
299
+ eventName = line.slice(6).trim();
300
+ } else if (line.startsWith("data:")) {
301
+ let data = line.slice(5).trim();
302
+ try {
303
+ data = JSON.parse(data);
304
+ } catch {
305
+ }
306
+ this.emit(eventName, data);
307
+ eventName = "message";
308
+ }
309
+ }
310
+ }
311
+ this.emit("end");
312
+ } catch (err) {
313
+ if (err.name !== "AbortError") {
314
+ this.emit("error", err);
315
+ }
316
+ }
317
+ }
318
+ close() {
319
+ this.controller.abort();
320
+ this.emit("close");
321
+ }
322
+ };
323
+
324
+ // src/node-env.ts
203
325
  function isWindows() {
204
326
  return import_os.default.platform() === "win32";
205
327
  }
@@ -1,3 +1,32 @@
1
+ import { Response } from 'express';
2
+ import { EventEmitter } from 'events';
3
+
4
+ declare class SSEResponse<Events extends string = 'message'> {
5
+ private res;
6
+ private isOpen;
7
+ constructor(res: Response);
8
+ flushHeaders(): void;
9
+ /** Send JSON to client, optionally with a custom event type */
10
+ emit(data: unknown, event?: Events): void;
11
+ /** Close the SSE connection */
12
+ close(): void;
13
+ }
14
+
15
+ interface SSEClientOptions {
16
+ url: string;
17
+ payload?: Record<string, any>;
18
+ headers?: Record<string, string>;
19
+ }
20
+ declare class SSEClient extends EventEmitter {
21
+ private url;
22
+ private payload;
23
+ private headers;
24
+ private controller;
25
+ constructor(options: SSEClientOptions);
26
+ connect(): Promise<void>;
27
+ close(): void;
28
+ }
29
+
1
30
  declare function isWindows(): boolean;
2
31
  declare function chmodPlusX(filePath: string): void;
3
32
  declare function checkIfDirExistsOrThrow(path: string): Promise<void>;
@@ -6,4 +35,4 @@ declare function throwIfDirNotEmpty(dir_path: string): Promise<void>;
6
35
  declare function checkCommandExistsOrThrow(cmd: string): Promise<void>;
7
36
  declare function checkIfFileExistsOrThrow(the_path: string): Promise<void>;
8
37
 
9
- export { checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty };
38
+ export { SSEClient, SSEResponse, checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty };
package/lib/node-env.d.ts CHANGED
@@ -1,3 +1,32 @@
1
+ import { Response } from 'express';
2
+ import { EventEmitter } from 'events';
3
+
4
+ declare class SSEResponse<Events extends string = 'message'> {
5
+ private res;
6
+ private isOpen;
7
+ constructor(res: Response);
8
+ flushHeaders(): void;
9
+ /** Send JSON to client, optionally with a custom event type */
10
+ emit(data: unknown, event?: Events): void;
11
+ /** Close the SSE connection */
12
+ close(): void;
13
+ }
14
+
15
+ interface SSEClientOptions {
16
+ url: string;
17
+ payload?: Record<string, any>;
18
+ headers?: Record<string, string>;
19
+ }
20
+ declare class SSEClient extends EventEmitter {
21
+ private url;
22
+ private payload;
23
+ private headers;
24
+ private controller;
25
+ constructor(options: SSEClientOptions);
26
+ connect(): Promise<void>;
27
+ close(): void;
28
+ }
29
+
1
30
  declare function isWindows(): boolean;
2
31
  declare function chmodPlusX(filePath: string): void;
3
32
  declare function checkIfDirExistsOrThrow(path: string): Promise<void>;
@@ -6,4 +35,4 @@ declare function throwIfDirNotEmpty(dir_path: string): Promise<void>;
6
35
  declare function checkCommandExistsOrThrow(cmd: string): Promise<void>;
7
36
  declare function checkIfFileExistsOrThrow(the_path: string): Promise<void>;
8
37
 
9
- export { checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty };
38
+ export { SSEClient, SSEResponse, checkCommandExistsOrThrow, checkIfDirExistsOrThrow, checkIfFileExistsOrThrow, chmodPlusX, createTempDir, isWindows, throwIfDirNotEmpty };
package/lib/node-env.js CHANGED
@@ -191,6 +191,126 @@ var import_command_exists = __toESM(require_command_exists2(), 1);
191
191
  import * as fs from "fs-extra";
192
192
  import os from "os";
193
193
  import path from "path";
194
+
195
+ // src/SSEResponse.ts
196
+ var SSEResponse = class {
197
+ res;
198
+ isOpen = true;
199
+ constructor(res) {
200
+ this.res = res;
201
+ res.setHeader("Content-Type", "text/event-stream");
202
+ res.setHeader("Cache-Control", "no-cache");
203
+ res.setHeader("Connection", "keep-alive");
204
+ }
205
+ flushHeaders() {
206
+ var _a;
207
+ (_a = this.res) == null ? void 0 : _a.flushHeaders();
208
+ }
209
+ /** Send JSON to client, optionally with a custom event type */
210
+ emit(data, event) {
211
+ if (!this.isOpen)
212
+ return;
213
+ try {
214
+ let payload = "";
215
+ if (event) {
216
+ payload += `event: ${event}
217
+ `;
218
+ }
219
+ payload += `data: ${JSON.stringify(data)}
220
+
221
+ `;
222
+ if (this.res.writable) {
223
+ this.res.write(payload);
224
+ }
225
+ } catch (e) {
226
+ console.log("SSE Write error", e);
227
+ }
228
+ }
229
+ /** Close the SSE connection */
230
+ close() {
231
+ if (!this.isOpen) {
232
+ return;
233
+ }
234
+ this.isOpen = false;
235
+ if (!this.res.writableEnded) {
236
+ this.res.end();
237
+ }
238
+ }
239
+ };
240
+
241
+ // src/SSEClient.ts
242
+ import { EventEmitter } from "events";
243
+ var SSEClient = class extends EventEmitter {
244
+ url;
245
+ payload;
246
+ headers;
247
+ controller;
248
+ constructor(options) {
249
+ super();
250
+ this.url = options.url;
251
+ this.payload = options.payload || {};
252
+ this.headers = {
253
+ "Content-Type": "application/json",
254
+ Accept: "*/*",
255
+ ...options.headers
256
+ };
257
+ this.controller = new AbortController();
258
+ }
259
+ async connect() {
260
+ try {
261
+ const res = await fetch(this.url, {
262
+ method: "POST",
263
+ headers: this.headers,
264
+ body: JSON.stringify(this.payload),
265
+ signal: this.controller.signal
266
+ });
267
+ if (!res.ok) {
268
+ const data = await res.text();
269
+ this.emit("error", new Error(`HTTP error: ${res.status} ${data}`));
270
+ return;
271
+ }
272
+ const reader = res.body.getReader();
273
+ const decoder = new TextDecoder("utf-8");
274
+ let buffer = "";
275
+ let eventName = "message";
276
+ while (true) {
277
+ const { value, done } = await reader.read();
278
+ if (done)
279
+ break;
280
+ buffer += decoder.decode(value, { stream: true });
281
+ let lines = buffer.split("\n");
282
+ buffer = lines.pop();
283
+ for (let line of lines) {
284
+ line = line.trim();
285
+ if (!line)
286
+ continue;
287
+ if (line.startsWith("event:")) {
288
+ eventName = line.slice(6).trim();
289
+ } else if (line.startsWith("data:")) {
290
+ let data = line.slice(5).trim();
291
+ try {
292
+ data = JSON.parse(data);
293
+ } catch {
294
+ }
295
+ this.emit(eventName, data);
296
+ eventName = "message";
297
+ }
298
+ }
299
+ }
300
+ this.emit("end");
301
+ } catch (err) {
302
+ if (err.name !== "AbortError") {
303
+ this.emit("error", err);
304
+ }
305
+ }
306
+ }
307
+ close() {
308
+ this.controller.abort();
309
+ this.emit("close");
310
+ }
311
+ };
312
+
313
+ // src/node-env.ts
194
314
  function isWindows() {
195
315
  return os.platform() === "win32";
196
316
  }
@@ -256,6 +376,8 @@ function removeQuotes(str) {
256
376
  }
257
377
  }
258
378
  export {
379
+ SSEClient,
380
+ SSEResponse,
259
381
  checkCommandExistsOrThrow,
260
382
  checkIfDirExistsOrThrow,
261
383
  checkIfFileExistsOrThrow,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rg-dev/stdlib",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -11,9 +11,6 @@
11
11
  "files": [
12
12
  "lib"
13
13
  ],
14
-
15
-
16
-
17
14
  "typesVersions": {
18
15
  "*": {
19
16
  "lib/common-env": [
@@ -58,6 +55,7 @@
58
55
  "@types/node": "^18.19.3",
59
56
  "builtin-modules": "^3.3.0",
60
57
  "command-exists": "^1.2.9",
58
+ "express": "^5.1.0",
61
59
  "node-fetch": "^3.3.2",
62
60
  "tsup": "^8.0.1",
63
61
  "typescript": "^4.9.5"