Coder Social home page Coder Social logo

emit-typed-server-sent-events's Introduction

emit-typed-server-sent-events

This library contains helpers to convert events from a node.js event emitter to a event stream for server-sent events and handle them on the client side with type safety (via @microsoft/fetch-event-source).

Installation

Choose the one for your package manager.

npm install 'github:fehnomenal/emit-typed-server-sent-events#semver:v2.0.0'
yarn install 'github:fehnomenal/emit-typed-server-sent-events#semver:v2.0.0'
pnpm install 'github:fehnomenal/emit-typed-server-sent-events#semver:v2.0.0'
bun add 'github:fehnomenal/emit-typed-server-sent-events#semver:v2.0.0'

Server side

  1. Create an event emitter (optionally with typed events which I recommend).
  2. Configure an event streamer that filters events and optionally maps the data to send. The filter functions can optionally access a context argument.
  3. Respond to requests.
import { defineMapFor } from 'emit-typed-server-sent-events';
import EventEmitter from 'node:events';

// 1. Create an event emitter (optionally with typed events which I recommend).
const jobEmitter = new EventEmitter<{
  start: [id: string];
  progress: [id: string, current: number, total: number];
  finish: [id: string];
}>();

// 2. Configure an event streamer.
const jobStreamer = defineMapFor(jobEmitter)
  // You have to specify which events to handle.
  .switchEvents({
    // We do not care about the `start` event and omit it here.

    // We want to throttle the rate of `progress` events.
    progress(id, current, total) {
      const percent = current / total;
      if (percent % 5 === 0) {
        // Needs either a const assertion or a specific return type. Otherwise the type at the
        // frontend would be (string | number)[].
        return [id, percent] as const;
      }

      // Skip this event.
      return false;
    },

    // Pass the data of the `finish` event unaltered.
    finish: true,
  });

// 3. Respond to requests. This is a sveltekit server endpoint but the library should work
// everywhere you can return a web response.
export const GET = (event) => {
  // Optionally check authentication.

  return jobStreamer.streamEvents();
};

// Now emit events into your emitter to pass them to listening clients.
jobEmitter.emit('start', 'job-1');
jobEmitter.emit('progress', 'job-1', 1, 100);
jobEmitter.emit('finish', 'job-1');

// Export for the frontend.
export type { jobStreamer };

You can also call defineMapFor(..).withContext<{ ... }>() to get access to a context value inside the event forwarding functions. In this example from a chat application only message events shall be streamed that belong to the current chat window:

const messageEmitter = new EventEmitter<{
  msg: [chatId: string, message: string];
}>();

const messageStreamer = defineMapFor(messageEmitter)
  .withContext<{ chatId: string }>()
  .switchEvents({
    msg(chatId, _message, ctx) {
      return chatId === ctx.chatId;
    },
  });

export const GET = (event) => {
  return messageStreamer.streamEvents({ chatId: event.params.chat_id });
};

export type { messageStreamer };

Client side

Call listenToEvents with the url to your endpoint, a map of event handlers and optionally config that is passed to @microsoft/fetch-event-source. The event handler arguments are typed from the sse emitter.

import { listenToEvents } from 'emit-typed-server-sent-events';
import type { jobStreamer } from './server.js';

const sse = listenToEvents<typeof jobStreamer>(
  '/endpoint/url',
  {
    progress(id, percent) {
      if (percent === 0) {
        console.log('started job', id);
      } else {
        console.log('job progress', id, percent.toLocaleString(undefined, { style: 'percent' }));
      }
    },
    finish(id) {
      console.log('finished job', id);
    },
  },
  {
    // @microsoft/fetch-event-source config
    // signal: ...
  },
);

// You can abort the event source. This is only available if you didn't pass a `signal` to the `listenToEvents` call.
sse.abort();

// You can await the closing of the connection.
await sse.promise;

Development and publishing

> bun i
> # work work work
> git add ...
> bun changeset
> git commit
> bun version
> git add -i
> git commit -m "release ..."
> bun run build
> npm2git c
> git push
> git push --tags

emit-typed-server-sent-events's People

Contributors

fehnomenal avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.