xPulse
πŸ‡¬πŸ‡§ EN

@xpulse/event – Component Spec

Status: CONCEPT Β· Created March 2026 Parent: GLOBAL_concept-ecosystem.md


Overview

Event system – the foundation for all @xpulse/* packages. Zero dependencies.

Extends Node's native EventEmitter with optional registration and introspection – so that events in the ecosystem are documented and traceable.


Concept

Events can optionally be registered – this is not required, but a convention. An emit() on an unregistered event works, but does not appear in event.list().

register() β†’ documents an event (optional)
on() β†’ listens to an event
once() β†’ listens once
off() β†’ stops listening
emit() β†’ fires an event

API

Listening

import event from '@xpulse/event';
// Listen permanently
event.on('page:ready', (data) => {
console.log(data.lang);
});
// Listen once
event.once('project:ready', (data) => {
console.log('project loaded:', data.name);
});
// Stop listening
const handler = (data) => { ... };
event.on('page:ready', handler);
event.off('page:ready', handler);

Firing

event.emit('page:ready', { lang: 'de', route: '/privacy/' });

Registering (optional)

// Documents an event – not required before emit()
event.register('page:ready', {
description: 'Page fully rendered, i18n can take over',
emittedBy: '@xpulse/project',
payload: '{ lang: string, route: string }',
});

Introspection

// All registered events
event.list();
// β†’ [
// { name: 'page:ready', description: '...', emittedBy: '...', listeners: 2 },
// { name: 'project:ready', description: '...', emittedBy: '...', listeners: 1 },
// ]
// Active listeners for an event
event.listeners('page:ready');
// β†’ [ [Function: handler1], [Function: handler2] ]
// Listener count
event.listenerCount('page:ready');
// β†’ 2

Event Naming Convention

namespace:event
namespace:area:event
Example Meaning
project:ready project has been loaded
project:reloaded project has been reloaded
page:ready page rendered, ready for i18n
i18n:ready translations applied
http:request incoming request
http:response response sent

Package Structure

@xpulse/event/
index.js ← default export: event object
README.md
package.json

Internals

Based on Node's EventEmitter – no custom event loop, no queues, no persistence. Fired and forgotten. The registration map is a simple Map<name, metadata> on top.

// Internals
import { EventEmitter } from 'events';
const emitter = new EventEmitter();
const registry = new Map(); // name β†’ { description, emittedBy, payload }
// Limit set to 50 β€” many packages legitimately listen to the same events in the ecosystem
emitter.setMaxListeners(50);

Dependencies

None.


Debug Integration

When @xpulse/debug is installed as a devDependency, @xpulse/event automatically provides a DataCollector. @xpulse/debug discovers it via node_modules/@xpulse/event/src/datacollectors/ – no configuration needed.

"optionalDependencies": {
"@xpulse/debug": "^1.0.0"
}

The EventsCollector (name: 'events', icon: '⚑') shows in the Web Profiler:

Strategy: The EventsCollector wraps event.emit directly instead of registering a separate listener for each event. This means events are also captured that were never registered via event.register() (e.g. template:render:after). Excluded are http:response:before (circular payload) and logger:write (too many entries).

Badge in the toolbar: number of events emitted during the request (e.g. 7)


Open Questions

Question Status
event.listAll() also for unregistered (but active) events? TBD
en/spec.md 2026-04-13