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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rotomeca/event

v5.0.0

Published

Add event system

Readme

@rotomeca/event

npm version CI License: ISC Node.js >= 18 Docs

Système d'événements typé à la C# pour Node.js et le navigateur. Inspiré du pattern event de C# et de la chaîne de middleware ASP.NET Core, @rotomeca/event fournit une API cohérente, sûre et observable pour gérer les callbacks dans vos projets TypeScript.


Installation

# pnpm
pnpm add @rotomeca/event

# npm
npm install @rotomeca/event

# yarn
yarn add @rotomeca/event

Compatibilité

| Environnement | Support | | ----------------- | ---------------------------------------- | | Node.js | ≥ 18 | | ESM | ✅ | | CommonJS | ✅ | | Bundle navigateur | ✅ (dist/bundle/rotomeca-event.min.js) | | TypeScript | ✅ (types inclus) |


Concepts clés

Le package expose deux familles de gestionnaires d'événements et un décorateur :

| Classe | Comportement lors de invoke | | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | | EventHandler / EventDelegate | Chaque callback est appelé indépendamment — résultats séparés | | CircularEventHandler / CircularEventDelegate | Chaque callback reçoit et enrichit le résultat du précédent (pipeline) | | @event / @circularEvent | Décorateur d'accesseur automatique pour déclarer des événements comme propriétés de classe |


EventHandler — événements standard

Inspiré de EventHandler<TEventArgs> en C#. Les callbacks sont appelés dans leur ordre d'enregistrement, indépendamment les uns des autres.

import { EventHandler, EventDelegate } from '@rotomeca/event';

// Avec EventHandler (deux génériques : TArgs + T)
const onClick = new EventHandler<[MouseEvent], (e: MouseEvent) => void>();

// Avec EventDelegate (un seul générique : le type du callback)
const onSubmit = new EventDelegate<(data: FormData) => string>();

Ajouter et retirer des callbacks

// push — clé générée automatiquement
const key = onClick.push(e => console.log(e.clientX));

// add — clé explicite, chaînable
onClick
	.add('logger', e => console.log(e.clientX))
	.add('tracker', e => analytics.track(e));

// remove — retourne le callback supprimé
const ancien = onClick.remove('logger');

// has — vérification
if (onClick.has('tracker')) {
	onClick.remove('tracker');
}

// clear — supprime tout, retourne this
onClick.clear();

Déclencher l'événement

invoke retourne un type discriminé à trois cas :

const result = onClick.invoke(new MouseEvent('click'));

switch (result.type) {
	case 'empty':
		// Aucun callback enregistré
		break;
	case 'single':
		console.log(result.value); // ReturnType<T>
		break;
	case 'multiple':
		console.log(result.values); // Record<string, ReturnType<T>>
		break;
}

Inspecter l'état

onClick.count(); // → uint — nombre de callbacks
onClick.haveEvents(); // → boolean
onClick.keys; // → string[] — clés dans l'ordre d'enregistrement
onClick.getInvocationList(); // → T[]     — callbacks dans l'ordre d'enregistrement

Arguments par défaut

Chaque callback peut avoir des arguments par défaut, passés avant ceux fournis à invoke :

const onLog = new EventHandler<[string, string]>();

// 'prefix' sera toujours passé en premier
onLog.add(
	'handler',
	(prefix, msg) => console.log(`[${prefix}] ${msg}`),
	'INFO',
);

onLog.invoke('Bonjour'); // → [INFO] Bonjour

CircularEventHandler — pipeline de transformation

Inspiré du pattern Middleware d'ASP.NET Core. Chaque callback reçoit le record courant, le transforme et retourne un Partial<TRecord> qui est mergé dans le record avant d'être transmis au suivant.

import { CircularEventHandler, CircularEventDelegate } from '@rotomeca/event';

// Avec CircularEventDelegate (un seul générique : le type du record)
const pipeline = new CircularEventDelegate<{ value: number; label: string }>();

pipeline.add('double', ({ value }) => ({ value: value * 2 }));
pipeline.add('label', ({ value }) => ({ label: `result=${value}` }));

const result = pipeline.invoke({ value: 5, label: '' });
// result.type  → 'record'
// result.value → { value: 10, label: 'result=10' }

Résultat discriminé

switch (result.type) {
	case 'empty':
		// Aucun callback enregistré
		break;
	case 'record':
		console.log(result.value); // TRecord final
		break;
	case 'other':
		// La valeur initiale n'était pas un plain object
		console.warn('Valeur inattendue :', result.originalValue);
		console.log(result.value);
		break;
}

@event — décorateur d'accesseur

Déclare un EventHandler comme propriété gérée d'une classe. Garantit une instanciation unique (singleton par propriété), empêche l'écrasement accidentel via le setter et supporte le chargement lazy ou eager.

Prérequis : nécessite "experimentalDecorators": false et les décorateurs ECMAScript Stage 3 (accessor keyword).

import { Listener, NoInitListener, EventHandler } from '@rotomeca/event';

class Button {
	// Lazy (par défaut) — instancié au premier accès
	@event(evt => evt.add('default', () => console.log('clicked')))
	accessor onClick: EventHandler<[], () => void>;

	// Eager — instancié à la construction de l'objet
	@event(NoInitListener, { lazy: false })
	accessor onDestroy: EventHandler<[], () => void>;

	// Circulaire — instancie un CircularEventHandler
	@circularEvent(NoInitListener, { circular: true })
	accessor onTransform: CircularEventHandler<[{ value: number }]>;
}

const btn = new Button();
btn.onClick.push(() => console.log('custom handler'));
btn.onClick.invoke(); // → 'clicked', puis 'custom handler'

NoInitListener est un placeholder sémantique à utiliser quand vous n'avez pas de logique d'initialisation mais devez passer des options.


Événements d'observation

Chaque gestionnaire expose trois méta-événements, initialisés de façon lazy (aucun coût si personne n'y souscrit) :

const event = new EventHandler<[string]>();

// Déclenché à chaque ajout via add() ou push()
event.onHandlerAdded.push((key, cb) => {
	console.log(`Handler "${key}" ajouté`);
});

// Déclenché à chaque suppression via remove()
event.onHandlerRemoved.push((key, cb) => {
	console.log(`Handler "${key}" retiré`);
});

// Déclenché une seule fois lors d'un clear() — reçoit la liste complète
event.onHandlerCleared.push(callbacks => {
	console.log(`${callbacks.length} handler(s) supprimé(s)`);
});

event.add('a', s => s.toUpperCase()); // → 'Handler "a" ajouté'
event.clear(); // → '1 handler(s) supprimé(s)'

Référence des exports publics

// Classes
import {
	EventHandler,
	EventDelegate,
	CircularEventHandler,
	CircularEventDelegate,
	EventData,
} from '@rotomeca/event';

// Décorateur
import { Listener, NoInitListener } from '@rotomeca/event';

// Types
import type {
	IEventHandler,
	IEventData,
	EventCallResult,
	CircularEventCallResult,
	HandlerAddedCallback,
	HandlerRemovedCallback,
	HandlerClearedCallback,
	CallbackInitializer,
	ListenerOptions,
} from '@rotomeca/event';

// Alias rétrocompatibles (dépréciés)
import { JsEvent, JsCircularEvent } from '@rotomeca/event';

Ecosystème Rotomeca

Ce package fait partie d'un écosystème cohérent :

| Package | Description | | ------------------------------------------------------------------ | ----------------------------------------- | | @rotomeca/utils | Fonctions pures, types brandés et helpers | | @rotomeca/rop | Gestion d'erreurs typée (Result<T, E>) | | @rotomeca/event | Ce package | | @rotomeca/jsenumerable | LINQ lazy en TypeScript |


Contribuer

git clone https://github.com/Rotomeca/node-event.git
cd node-event
pnpm install
pnpm test

Les contributions sont les bienvenues via Pull Request sur la branche dev.


Licence

ISC © Rotomeca