@rotomeca/event - v5.0.0
    Preparing search index...

    @rotomeca/event - v5.0.0

    @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.


    # pnpm
    pnpm add @rotomeca/event

    # npm
    npm install @rotomeca/event

    # yarn
    yarn add @rotomeca/event

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

    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

    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>();
    // 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();

    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;
    }
    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

    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

    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' }
    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;
    }

    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.


    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)'

    // 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';

    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

    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.


    ISC © Rotomeca