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

eventx-bridge

v0.2.0

Published

Node.js OSC bridge — subscribes to Supabase Realtime and forwards EventX events to Notch via OSC.

Readme

eventx-bridge

Node.js OSC bridge — subscribes to Supabase Realtime and forwards EventX events to Notch via OSC.

Quick start (no install)

Step 1 — Get your .env

In the EventX dashboard, go to your event → Bridge tab → click Download .env.

Save it to ~/.eventx-bridge/.env (auto-discovered) or keep it anywhere and use --env.

Step 2 — Run

npx eventx-bridge

CLI options

npx eventx-bridge [options]

  --env <path>           Path to .env file
                           Default: ./.env then ~/.eventx-bridge/.env
  --event <id>           Active event ID override (ACTIVE_EVENT_ID)
  --mode <mode>          production | test  (default: production)
  --notch-host <ip>      Override NOTCH_HOST
  --notch-port <port>    Override NOTCH_PORT
  --log-level <level>    debug | info | warn | error
  --version, -v          Show version
  --help, -h             Show this help

Local dev (monorepo)

# test mode — generates fake OSC traffic, no Supabase required
pnpm --filter eventx-bridge dev

# production mode — requires SUPABASE_URL + SUPABASE_SERVICE_KEY in .env
pnpm --filter eventx-bridge dev

Production mode behavior

In production mode the bridge:

  1. Connects to Supabase with service role key (bypasses RLS)
  2. Fetches active sessions (activity_sessions WHERE status='live') on startup and seeds the in-memory session tracker
  3. Subscribes to two Realtime channels:
    • eventx-aggregations — listens for UPDATE on aggregations table; routes to handleAggregationUpdate (OSC diff logic in task 006)
    • eventx-activity-sessions — listens for all changes on activity_sessions table; routes to handleSessionStatusChange which emits /control/start, /control/end OSC messages on status transitions
  4. Reconnects automatically via supabase-js built-in Realtime reconnect. Bridge logs channel status events (SUBSCRIBED, TIMED_OUT, CHANNEL_ERROR).
  5. Retry on error: if a channel hits CHANNEL_ERROR or TIMED_OUT, bridge retries once after 5 s. On second failure it exits (LaunchAgent/PM2 restarts the process).

Required env vars for production

| Variable | Description | |---|---| | SUPABASE_URL | Supabase project URL (e.g. https://xxx.supabase.co) | | SUPABASE_SERVICE_KEY | Service role key — full DB access, never expose to client |

OSC addresses emitted

| Address | Args | Trigger | |---|---|---| | /eventx/{shortId}/control/start | — | activity_sessions.statuslive | | /eventx/{shortId}/control/end | — | activity_sessions.statusended | | /eventx/{shortId}/wordcloud/add | string word, int count | New or increased word in aggregation diff | | /eventx/{shortId}/wordcloud/remove | string word | Word dropped from aggregation or moderated hidden | | /eventx/{shortId}/wordcloud/state | string json | Periodic full snapshot every 2 s (JSON-encoded [word, count][]) |

shortId = first 10 hex chars of SHA-1 of activity_sessions.id.

OSC arg types (per spec section 4.2)

  • /wordcloud/add — two args: s (word) + i (count as integer, Math.floor applied)
  • /wordcloud/remove — one arg: s (word)
  • /wordcloud/state — one arg: s (JSON string of Array<[string, number]>)
  • /control/start, /control/end — no args

node-osc infers OSC types from JS values: strings, number with no decimal → i, float → f.

Environment variables

| Variable | Required | Default | Description | |---|---|---|---| | BRIDGE_MODE | no | production | test or production | | SUPABASE_URL | prod only | — | Supabase project URL | | SUPABASE_SERVICE_KEY | prod only | — | Supabase service role key | | NOTCH_HOST | no | 127.0.0.1 | OSC target host | | NOTCH_PORT | no | 7000 | OSC target port | | BRIDGE_LOG_LEVEL | no | info | pino log level | | BRIDGE_STATE_FILE | no | /tmp/bridge-state.json | crash recovery state file |

Copy .env.example from repo root and fill in your values.

Recovery behavior

The bridge persists its in-memory session state to a local JSON file after every state change (debounced 500 ms). On restart it merges the file with a fresh DB query: the DB is authoritative for session status, the file provides the last known wordcloud snapshot so Notch can be restored immediately.

State file location

Controlled by BRIDGE_STATE_FILE (default /tmp/bridge-state.json). The file is overwritten atomically on every flush.

Restart workflow

  1. Bridge crashes or is stopped with SIGTERM/SIGINT.
  2. On SIGTERM/SIGINT the bridge flushes state synchronously and exits cleanly (process.exit(0)).
    On a hard crash the last debounced flush (≤ 500 ms old) is already on disk.
  3. LaunchAgent/PM2 restarts the process automatically.
  4. initSessionTracker loads live sessions from DB and merges the snapshot file.
  5. The periodic state push (/wordcloud/state every 2 s) re-delivers the last snapshot to Notch — full recovery within 3 s of restart.

Fault tolerance

| Failure | Behavior | |---|---| | Notch unreachable | send() wrapped in try/catch; logs warn, never crashes | | Supabase outage | supabase-js handles reconnect; bridge logs channel status events; no panic | | State file corrupt | loadState() catches parse errors, returns empty Map, logs warn | | Process crash | Last flushed state ≤ 500 ms old; LaunchAgent restarts immediately |

Heartbeat

The bridge sends a heartbeat INSERT to bridge_heartbeats every 10 seconds so the operator dashboard can show connection status.

bridge_id

On startup the bridge generates a persistent bridge_id from {mode}-{hostname}-{pid} (max 64 chars). In test mode the id starts with test-.

Notch reachability

Every OSC send callback updates an in-memory flag. The heartbeat row includes notch_reachable: true if the most recent UDP send did not error, false otherwise (best-effort — UDP has no acknowledgement).

Failure tolerance

| Scenario | Behavior | |---|---| | Supabase outage | Insert fails silently; bridge logs warn and keeps running | | No SUPABASE_URL (test mode without env) | Heartbeat disabled; warn logged on startup | | Bridge stopped | stopHeartbeat() called in SIGTERM/SIGINT handler before exit |

Show control

When ACTIVE_EVENT_ID is set the bridge subscribes to show_control_triggers (Realtime INSERT, filtered to that event). Per trigger row it:

  1. Re-validates osc_address against the zod regex (server-side guard; rejects with warn, no throw).
  2. Coerces OSC args: boolean → 0/1, number stays, everything else String().
  3. Sends the OSC message via the same OscSender used for all other channels.
  4. Appends a tab-separated line to packages/bridge/logs/show_control.log: ISO8601\tevent_id\tbutton_id\tosc_address\targs_json

Missed-trigger behavior

Show control is a fire-and-forget real-time trigger pad. There is no delivery-confirmation column (delivered_at) and no replay queue in v1. If the bridge is offline when a button is pressed, that trigger is lost. The operator sees the result directly on the Notch scene (visual feedback) — no automatic retransmit on reconnect.

This is an intentional v1 trade-off. A retry/delivery queue is deferred to a future spec (PHASE_SHOW_CONTROL_V2_SPEC.md).

Required env var

| Variable | Required | Description | |---|---|---| | ACTIVE_EVENT_ID | yes (for show control) | UUID of the event whose show_control_triggers the bridge should watch. Leave unset to disable show_control channel. |

Sensor Race integration

When an activity_sessions row transitions to status='live' for a sensor_race activity, the bridge:

  1. Instantiates a SensorStreamSession (src/handlers/sensor-stream.ts)
  2. Subscribes to the Supabase Realtime broadcast channel sensor:{sessionId}
  3. On each received sensor broadcast event, calls session.onSample(input, participantId, teamId)
  4. Runs a 30 Hz physics loop: setInterval(() => session.tick(1/30), 33)
  5. Persists aggregated state to aggregations at 1 Hz: setInterval(() => session.persistPeriodic(), 1000)
  6. Sends OSC messages for each physics tick (car positions, winner)

Bridge must run for sensor activities

# Local dev (operator's machine)
pnpm --filter eventx-bridge dev

# Or build + start
pnpm --filter eventx-bridge build && pnpm --filter eventx-bridge start

Requires .env with SUPABASE_URL + SUPABASE_SERVICE_KEY (service key — bridge writes to aggregations table which requires elevated permissions).

When the session ends (status='ended'), bridge stops intervals and unsubscribes from the broadcast channel.

OSC output for sensor_race

| Address | Args | Cadence | |---|---|---| | /eventx/{sessionId}/race/team/{teamId}/x | float | 30 Hz (only on change) | | /eventx/{sessionId}/race/team/{teamId}/y | float | 30 Hz | | /eventx/{sessionId}/race/team/{teamId}/heading | float | 30 Hz | | /eventx/{sessionId}/race/team/{teamId}/speed | float | 30 Hz | | /eventx/{sessionId}/race/team/{teamId}/lap | int | on lap change | | /eventx/{sessionId}/race/winner | string teamId | once, on finish |

OSC sends are no-ops (logged as warn) when Notch is not configured — bridge continues running for HTML projection.

Smoke test

scripts/smoke-show-control.mjs verifies the full show-control chain: dashboard button click → show_control_triggers INSERT → Realtime → bridge → OSC dispatch to mock sink.

Run from repo root:

# With bridge active (requires SUPABASE_URL + SUPABASE_SERVICE_KEY + ACTIVE_EVENT_ID):
SUPABASE_URL=... SUPABASE_SERVICE_KEY=... ACTIVE_EVENT_ID=<uuid> pnpm smoke:show-control

# Without bridge (assertions 2 + latency are skipped; assertions 1/3/4 still run):
pnpm smoke:show-control

The mock OSC sink (scripts/mock-osc-sink.mjs) is spawned automatically and logs all received OSC packets to /tmp/eventx-bridge-mock-osc.log.

Assertions checked:

| # | Assertion | Requires | |---|---|---| | 1 | show_control_triggers DB row has correct osc_address + osc_args | SUPABASE_URL + SUPABASE_ANON_KEY | | 2 | Bridge OSC log contains /test/cue + arg 42 | Bridge running | | 3 | Pulse CSS class applied on clicked button (DOM check, 300ms window) | Always | | 4 | Recent triggers sidebar updates within 2 s (Realtime) | Always | | 5 | Latency p95 < 500 ms over 10 clicks (click → OSC log entry) | Bridge running |

Screenshots are saved to scripts/screenshots/sc-*.png.

Test

pnpm --filter eventx-bridge test
pnpm --filter eventx-bridge typecheck

Verify OSC output (test mode)

In a separate terminal, listen on UDP 7000:

nc -u -l 7000

Then start the bridge in test mode — you should see OSC bytes arriving every 500 ms.