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

tagged-event-proxy

v0.4.1

Published

Encapsulates tagged event data in a well-defined schema

Downloads

10

Readme

tagged-event-proxy

Build Status Coverage Status David npm

$ npm install tagged-event-proxy

At some point an app needs to generate events. With this module you can emit events with a consistent json schema to consume them easily using a flexible function signature that can adapt to your needs. With the ability to add metadata to the event, we ensure that the event is always fully-descriptive.

A logging system may be implemented on top of this utility, like Hapi event logs. Tagged events are far more expressive, standard and useful than simple log lines.

import { EventEmitter } from 'events';
import { createEventProxy, Event, interfaces } from 'tagged-event-proxy';

const emitter = new EventEmitter();
const newEvent = createEventProxy({
  emitter,
  name: 'log',
  tags: ['test'],
  data: {
    foo: 'bar'
  }
});

emitter.on('log',
  (event: Event, tags: interfaces.StringKeyedObject<boolean>) => {
  console.log(event);
  /*
  { tags: [ 'test', 'info' ],
  message: 'hello world',
  data: { id: 1 },
  timestamp: 2017-04-24T13:11:17.089Z }
  */
  
  console.log(tags);
  /*
  { test: true, info: true }
  */
});

newEvent(['info'], 'hello world');

The reason why the event proxy does not inherit from an EventEmitter is to ease the event forwarding to a single EventEmitter, you decide where you want to attach the event proxy. Typically you have multiple event origins, for instance server events and request-specific events. Maybe you want to send metrics to a backend or you want to track any kind of business event. By decoupling the emission of the events you can create a single EventEmitter that will receive all kind of events, you can centralize all your application events.

createEventProxy(options: CreateEventProxyOptions): EventProxy

Options:

  • emitter - EventEmitter | EventEmitter[] EventEmitter or array of EventEmitter instances that will receive events with name <name> and error.
  • name string Name of the event.
  • tags string[] (optional) Default tags to include with the event.
  • data object (optional) Default data to include with the event. The data must be a string-keyed object, eg. { foo: 'bar' }.
interface CreateEventProxyOptions {
  emitter: EventEmitter | EventEmitter[];
  name: string;
  tags?: string[];
  data?: StringKeyedObject<any>;
}

Returns a function that is able to emit events. Child event emitters can be created that will inherit and merge the default tags and data of the parent.

interface EventProxyChildOptions {
  tags?: string[];
  data?: StringKeyedObject<any>;
}

type EventError = Error | EventTags;
type EventTags = string[] | EventData;
type EventData = Error | StringKeyedObject<any> |
  (() => Promise<StringKeyedObject<any>>) | EventMessage;
type EventMessage = string | EventTimestamp;
type EventTimestamp = Date;

interface EventProxy {
  (tags: EventTags, data?: EventData, message?: EventMessage,
    timestamp?: EventTimestamp): void;
  tags: string[];
  data: StringKeyedObject<any>;
  child: (options: EventProxyChildOptions) => EventProxy;
}

There are a lot of ways to emit an event.

const emitter = new EventEmitter();
const newEvent = createEventProxy({
  emitter,
  name: 'log',
  tags: ['tag1'],
  data: {
    foo: 'bar'
  }
});

newEvent(['tag2', 'tag3'], { bar: 'foo' }, 'message', new Date());
newEvent(['tag2', 'tag3'], 'message');
newEvent('message');
newEvent(['tag2', 'tag3'], { bar: 'foo' });
newEvent({ bar: 'foo' }, 'message');
newEvent();

The data parameter can be also a sync/async function, useful to provide a value by using a closure. Errors thrown from inside the function are catched and emitted as error.

newEvent(() => ({ a: 'b'}));
newEvent(async () => ({ a: 'b'}));

Tags and data are accessible from outside just in case they need to be changed.

console.log(newEvent.tags); // ['tag1']
console.log(newEvent.data); // { foo: 'bar' }

normalizeEvent(error?: EventError, tags?: EventTags, data?: EventData, message?: EventMessage, timestamp?: EventTimestamp) : Promise<Event>

The function that normalizes the event calls. Takes the same parameters as the event proxy function.

Childs

Event proxies can be also create more event proxies that reuse tags and data:

const emitter = new EventEmitter();

const event1 = createEventProxy({
  emitter,
  name: 'log',
  tags: ['tag1'],
  data: {
    n: 1
  }
});
event1();
/*
{ tags: [ 'tag1' ],
  data: { n: 1 },
  message: '',
  timestamp: 2017-04-24T15:56:33.912Z }
*/

const event2 = event1.child({
  tags: ['tag2']
});
event2();
/*
{ tags: [ 'tag1', 'tag2' ],
  data: { n: 1 },
  message: '',
  timestamp: 2017-04-24T15:56:33.912Z }
*/

const event3 = event2.child({
  tags: ['tag3'],
  data: {
    n: 3
  }
});
event3();
/*
{ tags: [ 'tag1', 'tag2', 'tag3' ],
  data: { n: 3 },
  message: '',
  timestamp: 2017-04-24T15:56:33.913Z }
*/

Errors

The first parameter of the event proxy function is an error. This allows you to easily track errors without caring about the other parameters because all of them are optional. A new error field is added to the event with this error.

const emitter = new EventEmitter();
const newEvent = createEventProxy({
  emitter,
  name: 'log',
  tags: ['tag1']
});

try {
  throw new Error('whoops!');
} catch (err) {
  newEvent(err);
  /*
  { tags: [ 'tag1' ],
  data: {},
  message: '',
  timestamp: 2017-04-25T12:51:37.043Z,
  error:
   Error: whoops!
       at Object.<anonymous> ...
       ... }
  */
}

As you may know, any kind of data can be thrown using the throw keyword, this means that any potential unexpected type of parameter can be passed to the function. This situation is also controlled and any unexpected type is treated as an Error.

try {
  throw 123;
} catch (err) {
  newEvent(err);
  /*
  { tags: [ 'tag1' ],
  data: {},
  message: '',
  timestamp: 2017-04-25T12:51:37.043Z,
  error:
   Error: unexpected argument: 123
       at Object.<anonymous> ...
       ... }
  */
}