Skip to main content
Version: 0.1.0

Features Overview

Event Lens organises its capabilities into four areas: Capture, Analyze, Debug, Advanced.

Capture — Catch Events

Event Lens automatically captures the following.

CustomEvent / dispatchEvent

document.dispatchEvent(new CustomEvent('user:logged-in', {
detail: { userId: 123, timestamp: Date.now() }
}));

In Event Lens:

CustomEvent:user:logged-in
Payload: { userId: 123, timestamp: 1714756800000 }
Source: app.js:42
Subscribers: 3 listeners

postMessage

window.postMessage({ type: 'UPDATE', data: { /* ... */ } }, '*');

In Event Lens:

postMessage:UPDATE
Payload: { type: 'UPDATE', data: { ... } }
Origin: https://myapp.com
Correlated API: POST /api/sync (fired 50ms later)

BroadcastChannel

const channel = new BroadcastChannel('user-sync');
channel.postMessage({ action: 'LOGOUT' });

In Event Lens:

BroadcastChannel:user-sync:LOGOUT
Payload: { action: 'LOGOUT' }
Cross-tab: Other tabs receive it too
Delivery: Broadcast (one-to-many)

Event Bus (auto-detect)

Event Lens automatically finds event buses attached to window.

window.eventBus = new EventEmitter();
window.eventBus.emit('checkout:completed', { orderId: 'ORD-123' });

In Event Lens:

PubSub:checkout:completed
Pattern: emit/on (EventEmitter)
Payload: { orderId: 'ORD-123' }
Subscribers: updateUI(), trackAnalytics(), sendNotification()
Delivery: Sync

Supported patterns:

  • emit/on (EventEmitter).
  • emit/addEventListener.
  • dispatch/on.
  • dispatch/subscribe.
  • publish/subscribe.
  • fire/on.
  • trigger/on.
  • send/on.
  • notify/subscribe.

Manual SDK hook

If auto-detect doesn't work for your bus, hook in explicitly:

window.__REACT_EVENT_LENS__ = {
onEvent: function (callback) {
yourCustomBus.on('*', callback);
}
};

Event Lens picks up this hook and connects to it.

fetch / XMLHttpRequest

fetch('/api/cart/items', {
method: 'POST',
body: JSON.stringify({ /* ... */ })
});

In Event Lens:

Network:fetch:POST /api/cart/items
Triggered by: cart:item-added (50ms earlier)
Status: 200 OK
Duration: 145ms
Response: { success: true, itemCount: 5 }

Analyze — Understand Events

Schema drift detection

The shape of each event payload is checked on every fire:

First emission:
{ "userId": 123, "email": "[email protected]" }

Second emission:
{ "userId": 456, "email": "[email protected]", "role": "admin" } <- new key!

Event Lens shows a [DRIFT] badge.

Badges:

  • DRIFT — new keys appeared or a type changed.
  • -KEYS — keys present in earlier payloads are missing.
  • DRIFT -KEYS — both at once.

A changing payload shape can quietly break subscribers, which is why this matters.

Duplicate detection

If the same event fires with the same payload in quick succession:

cart:item-added { "itemId": 123 } First
cart:item-added { "itemId": 123 } DUP
cart:item-added { "itemId": 123 } DUP x3

Badge: DUP x3.

Duplicates often hide buggy handler logic or accidental double-emits.

Performance budget

Define limits for event behavior:

Settings → Budget
Max payload size: 50 KB
Max frequency: 100 events/sec
Max subscriber duration: 100 ms
Max total per minute: 10,000 events

When a rule is violated the event gets an OVER BUDGET badge.

Slow subscriber detection

When a handler takes too long:

user:login
updateUI() 2ms
trackAnalytics() 85ms <- SLOW
syncCache() 3ms
Total: 90ms

Slow handlers can freeze the UI.

Event chains

When events trigger each other, the chain is visualised:

user:click (user clicked)
| 5ms
product:selected (product selected)
| 8ms
api:request (API call)
| 145ms
api:response (response received)
| 2ms
ui:updated (UI refreshed)

Network correlation

Events and API requests share a single timeline:

Timeline (ms):
0 cart:item-added
50 fetch /api/cart (200ms)
100 analytics:track
250 fetch /api/analytics (100ms)

Event Lens automatically figures out how many ms after an event each API call happened.

Debug — Test Events

Event replay

Re-fire an event with its original payload:

Event: cart:item-added { "itemId": 123, "quantity": 1 }
Action: [Replay]
Result: The event fires again on the page; the UI updates.

Confirmation required — guards against accidental triggers.

Payload editor

Replay an event with a modified payload:

Original:
{ "itemId": 123, "quantity": 1 }

Edited:
{ "itemId": 456, "quantity": 5 }

[Replay Edited] (no confirmation — editing is itself the intent)

Useful for testing edge cases and probing subscriber behavior with different inputs.

Breakpoints

Pause the debugger when an event fires:

  1. Right-click an event → Breakpoint.
  2. When the event fires, execution pauses.
  3. Inspect state before subscribers run.
  4. Resume to continue.

Replay queue

Replay multiple events in order:

Event 1: user:login
Event 2: product:viewed
Event 3: cart:item-added
Event 4: checkout:completed

[Play All] replays each in sequence with a 500 ms delay.

For simulating complex user flows and integration tests.

Conditional filters

Find events with a multi-field filter:

Filter:
Event name contains "cart"
Payload.itemId equals "123"
Source contains "app.js"
Warning type is "SLOW"

Only events that match every condition remain visible.

Advanced

Memory leak detection

addEventListener / removeEventListener balance is tracked:

Active listeners (by event type):

resize
Added: 5
Removed: 2
Outstanding: 3 <- potential leak
Last added: app.js:142

scroll
Added: 10
Removed: 10
Outstanding: 0

Thresholds:

  • Listener count > 50 → HIGH severity.
  • Unbounded growth in a 10s window → MEDIUM severity.

Session capture

Record a user flow:

[Start recording]
-> clicked .product-card
-> scrolled 500px
-> filled #email input
-> clicked [Submit]
-> navigated to /order-confirmation
[Stop recording]
-> Export to JSON

What the JSON captures:

  • User flow — clicks, scrolls, inputs, navigation.
  • DOM mutations — node additions and removals.
  • DOM snapshots — page state at intervals.
  • Events — every event with its timeline.

Use cases: bug reproduction (share the JSON), regression tests, validating complex flows.

Delivery tracking

Per-subscriber delivery status for custom event buses:

checkout:completed
Delivery model: ACK / NACK
updateOrderTable() ACK (success)
sendConfirmationEmail() NACK (error: email service down)
updateAnalytics() PENDING (async, not yet resolved)
triggerNotification() ACK

Summary:
Success: 2
Failed: 1
Pending: 1

Tells you at a glance which handlers succeeded, failed, or are still in flight.

Leak report

Detailed report on memory leaks:

HIGH severity: resize listener leak

Current count: 87 listeners (threshold: 50)
Added in last 10s: 25
Removed in last 10s: 0
Growth rate: +25/sec

Outstanding listeners:
addEventListener(...) app.js:142
addEventListener(...) utils.js:89
... (85 more)

Where added:
ResizeObserver cleanup (15 times)
Window resize handler (10 times)

Performance Budgets

A violated budget surfaces an OVER BUDGET badge on the event and a row in the Budget tab:

Budget Rules:
Max payload: 50 KB VIOLATED (78 KB)
Max frequency: 100/sec VIOLATED (120/sec)
Max subscriber duration: 100ms VIOLATED (250ms)
Max total/min: 10,000 OK

Violations:
[x3] event:large-payload (78 KB)
[x12] event:frequent (20/sec)
[x1] event:slow-subscriber (250ms handler)

Clients Tab

Aggregate view: who is listening to what.

By Subscriber:
updateUI() listens to:
cart:updated (45 fires)
user:changed (12 fires)
[Transport: Event Bus]

trackAnalytics() listens to:
button:click (234 fires)
page:scroll (567 fires)
[Transport: DOM]

By Topic:
cart:updated (45 fires)
updateUI() 1ms
persistToLocalStorage() 2ms
triggerNotification() 50ms

Export / Import

Export the session to JSON, import it later:

{
"version": 2,
"exportedAt": "2026-05-08T14:03:21Z",
"url": "https://myapp.com/products",
"events": [],
"networkRequests": [],
"leakReport": [],
"listenerCounts": [],
"breakpoints": ["cart:item-added"],
"budget": {},
"replayHistory": []
}

Use cases: attach a session to a bug report, do offline analysis, commit test cases to a repo.

Theme

Dark (default, Catppuccin Mocha) and Light modes are available.

Settings → Theme
Dark (default)
Light

The choice is persisted in localStorage.

Next