Examples

Small patterns.
Clear ownership.

Each example isolates one practical behavior. Combine them only where the application boundary calls for it.

Load once, with a bundler or without one.

A bundler resolves the bare import normally. For a non-bundled app, serve the installed files over HTTP(S) and put this map before the first module script:

<script type="importmap">
{"imports":{"event-pubsub":"./node_modules/event-pubsub/index.js","strong-type":"./node_modules/strong-type/index.js"}}
</script>
<script type="module">import EventPubSub from 'event-pubsub';</script>

If the app uses a strict CSP, authorize the inline import-map and module scripts with an allowed nonce or hash.

Typed event

const events = new EventPubSub();

events.on('invoice.paid', ({ id }) => {
    console.log(`Paid: ${id}`);
});

events.emit('invoice.paid', { id: 42 });

One-shot readiness

events.once('app.ready', (context) => {
    hydrate(context);
});

events.emit('app.ready', context);
events.emit('app.ready', context); // no second call

Wildcard audit

events.on('*', (type, ...payload) => {
    audit.write({ type, payload });
});

events.emit('user.created', user);

Explicit cleanup

function render(state) {
    view.update(state);
}

events.on('state.changed', render);
events.off('state.changed', render);

Extending

Give a domain object its own event surface.

class Counter extends EventPubSub {
    value = 0;

    increment() {
        this.value += 1;
        return this.emit('change', this.value);
    }
}

const counter = new Counter();
counter.on('change', console.log).increment();
Prefer specific hubs over one global bus.

Separate lifecycle boundaries reduce accidental coupling and make reset() meaningful. Multiple instances are fully isolated.