npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

evtsrc

v1.1.4

Published

- EventSource, written in TypeScript with 2023 syntax, and without any runtime dependencies. - This is actually a side project by the author, discretely experimenting with [SWC](https://swc.rs/).

Downloads

32

Readme

Evtsrc

  • EventSource, written in TypeScript with 2023 syntax, and without any runtime dependencies.
  • This is actually a side project by the author, discretely experimenting with SWC.

Installation

yarn add evtsrc
  • No dependencies, only a few devDependencies.

Usage

1. Browser environment (requires vite, webpack etc.)

import { EvtSrcClient } from "evtsrc";

// same usage as EventSource but with a few added methods:
// 1. Awaits until connection state changes
// get connectionChangePromise(): Promise<void>;
//
// 2. Waits for a new message to arrive
// get messageChangePromise(): Promise<T>;
//
// 3. Returns immediately if connected, otherwise await
// get connectionPromise(): Promise<void>;
//
// 4. Returns any messages(might be stale) arrived, otherwise await
// get messagePromise(): Promise<T>;
//
// All promises may reject due to unfavorable conditions, 
// such as when the server intentionally disconnects an SSE source.
// Please handle these rejections appropriately. ;)

// Create an instance of EvtSrcClient
const client = new EvtSrcClient<string>("https://example.com/stream", {
  eosMarker: "END_OF_STREAM", // match with Server
});

// Wait for the connection to open
await client.connectionPromise;

// Infinite loop to receive messages
while (true) {
  const message = await client.messagePromise;
  console.log("Received message:", message);

  // Check for the eosMarker
  if (message === "END_OF_STREAM") {
    console.log("End of stream reached. Closing connection.");
    break;
  }
}

// Close the connection
client.close();

2. Node.js environment

import { EvtSrcServer } from "evtsrc";

// Inherits native EventEmitter but the main juice happens with those methods:
//
// 1. Ctor receives a few options. `eosMarker` is required!
// constructor(options: {
//     asJson?: boolean;
//     heartbeat?: number;
//     eosMarker: EOS;
// });
//
// emitMessage(chunk: Chunk<T> | EOS): void;
//
// emitComment(comment: string): void;
//
// private processQueue;
//
// get messagePromise(): Promise<string>;
//
// close(): Promise<void>;

// Example with express
import express, { Request, Response } from 'express';
import { EvtSrcServer, Chunk } from './EvtSrcServer'; // Assuming the EvtSrcServer code is in a separate file

const app = express();

app.get('/event-stream', async (req: Request, res: Response) => {
  // Instantiate EvtSrcServer (match eosMarker with Client)
  const evtsrc = new EvtSrcServer<string>({ eosMarker: { data: 'END_OF_STREAM' } }); 

  // Send appropriate headers and flush response
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();

  try {
    // Call yourAwesomeFunction with evtsrc
    yourAwesomeFunction(evtsrc); // branch out your logic here and begin the loop

    // Infinite loop to send messages as they arrive
    while (true) {
      try {
        const message = await evtsrc.messagePromise;
        res.write(message); // Send the formatted message to the response
      } catch (error) {
        if(!error) {
          // evtsrc.close() was called in yourAwesomeFunction
        } 
        break; // Break the loop and safely close the response
      }
    }
  } finally {
    // Close the response and end the connection
    res.end();
  }
});

// Function that demonstrates a long-lasting async call
async function yourAwesomeFunction(evtsrc: EvtSrcServer<string>) {
  try {
    // Simulate a long-running task with progress updates
    for (let i = 1; i <= 10; i++) {
      await delay(1000); // Delay for 1 second (replace with your actual task logic)
      
      // Send progress update as a message
      evtsrc.emitMessage({ data: `Progress: ${i * 10}%` });
    }

    // Task completed
    evtsrc.emitMessage({ data: 'Task completed!', eventName: 'taskComplete' });
  } finally {
    evtsrc.close();
  }
}

Why?

  • Utilizing EventSource in both browser and Node.js server necessitates the use of a callback function, leading to a non-linear data flow.
  • As a Node.js server developer, you might want to avoid bloating your node_modules simply to dispatch a few Server-Sent Events (also known as Server Side Events, SSE, etc.)
    • If not, you'll have to manually implement this specification.
    • There are packages available that facilitate emitting server-side events, but they often require acting as specific middleware or taking control of the response object.
  • This package tries to provide a few helper promises to easily await inside a for loop.

Contribution

  • Feel free to suggest me an idea. Github messages are not the best way to contact me though... (too much noise ratio)

Changelog

1.1.0

- Client
    - Support `event: ` case
    - Support EOS marker
- Server
    - Skip heartbeat with falsy value

1.0.0 : Initial publish


Class: EvtSrcServer

Represents an event source server.

Constructor:

constructor(options: {
  asJson?: boolean;
  heartbeat?: number;
  eosMarker: EOS;
})

Creates an instance of EvtSrcServer.

  • options (required): An object that specifies the server options.
    • asJson (optional): A boolean indicating whether the data should be emitted as JSON. Default is false.
    • heartbeat (optional): The interval (in milliseconds) for sending heartbeat comments to keep the connection alive. Set to 0 to disable. Default is 0.
    • eosMarker (required): An object representing the End of Stream (EOS) marker.

Method: emitMessage

public emitMessage(chunk: Chunk<T> | EOS): void

Emits a message to the queue. The message can either be a chunk of data or an EOS (End of Stream) marker.

  • chunk (required): The message to emit. Must have at least one property.

Method: emitComment

public emitComment(comment: string): void

Emits a comment. This is a convenience method for keeping the connection alive or for debugging purposes.

  • comment (required): The comment to emit.

Method: close

public async close(): Promise<void>

Closes the connection, emits an EOS marker, and emits a 'close' event once all pending Promises have resolved.

Returns:

  • A Promise that resolves when all pending Promises have resolved.

Getter: messagePromise

get messagePromise(): Promise<string>

Returns a Promise that resolves with the next message event data. If the connection is closed, the Promise is rejected immediately.

Returns:

  • A Promise that resolves with the data of the next message event.

Class: EvtSrcClient

Represents an event source client that extends the EventSource class.

Constructor:

constructor(url: string, options: Partial<EventSourceInit> & { eosMarker: EOS_DEFAULT })

Creates an instance of EvtSrcClient.

  • url (required): The URL of the server-side event source.
  • options (required): An object that specifies additional options for the event source connection.
    • eosMarker (required): The End of Stream (EOS) marker.

Method: connectionChangePromise

get connectionChangePromise(): Promise<boolean>

Returns a Promise that resolves when the EventSource connection opens or rejects when an error event occurs.

Returns:

  • A Promise<boolean> that resolves when the EventSource connection opens.

Method: connectionPromise

get connectionPromise(): Promise<boolean>

Returns a Promise that resolves when the EventSource connection is open. If the connection is already open, it returns a resolved Promise. Otherwise, it returns a Promise that resolves when the connection state changes.

Returns:

  • A Promise<boolean> that resolves when the EventSource connection is open.

Method: messageChangePromise

get messageChangePromise(): Promise<T | EOS>

Returns a Promise that resolves when a new message event is received or rejects when an error event occurs. The resolved value is the data of the message event.

Returns:

  • A Promise<T | EOS> that resolves with the data of the next message event.

Method: messagePromise

get messagePromise(): Promise<T | EOS>

Returns a Promise that resolves with the most recent message event data. If no message has been received yet, it returns a Promise that resolves with the data of the next message event.

Returns:

  • A Promise<T | EOS> that resolves with the data of the last or next message event.

Method: messageChangePromiseByEventName

messageChangePromiseByEventName(eventName: string): Promise<T | EOS>

Returns a Promise that resolves with the data of the next message event with the specified event name or rejects when an error event occurs.

  • eventName (required): The name of the event to listen for.

Returns:

  • A Promise<T | EOS> that resolves with the data of the next message event.

Method: close

close(): void

Closes the connection and dispatches a "close" event.