Skip to content

Repository files navigation

Orbi-Tation

A framework-agnostic debugging utility that records DOM mutations as typed, serializable events through an explicit, side-effect-free package API.

Zero Runtime Dependencies TypeScript

Features

  • Attribute, child-list, and character-data observation
  • Serializable events without native DOM nodes or MutationRecord objects
  • Strict TypeScript types and declaration maps
  • Side-effect-free ESM and CommonJS package entries
  • Console presentation and visual highlighting
  • Bounded history, deduplication, subscriptions, and explicit lifecycle control
  • Reproducible Chromium performance benchmarks with machine-readable results
  • Zero runtime dependencies

Install

pnpm add --save-dev orbi-tation

Importing the package does not start an observer or change the page.

import { createTracker } from "orbi-tation";

const tracker = createTracker();
const unsubscribe = tracker.subscribe((event) => {
  console.log(event);
});

tracker.start();

// Later:
unsubscribe();
tracker.stop();

To scope tracking to one subtree, pass a root node or a selector. Selectors are resolved when start() runs so the target can be created after the tracker instance:

const tracker = createTracker({ root: "#app" });
tracker.start();

Development-only integration

Installing the package as a devDependency does not by itself keep it out of a production browser bundle. Guard a lazy import with a compile-time development constant so the production build can remove the import and tracker code:

if (import.meta.env.DEV) {
  void Promise.all([import("orbi-tation"), import("orbi-tation/panel")]).then(
    ([{ createTracker }, { createPanel }]) => {
      const tracker = createTracker();
      const panel = createPanel(tracker);

      panel.mount();
      tracker.start();
    },
  );
}

import.meta.env.DEV is the Vite-style spelling. Other bundlers should replace an equivalent compile-time constant with false in production. Keep the guard directly around the dynamic import so dead-code elimination can remove the entire chunk; a runtime-only setting cannot provide that guarantee.

The package does not inspect the host application's environment. If production code explicitly imports and starts a tracker, it will run. This explicit-opt-in behavior keeps the core bundler-neutral and avoids unreliable environment detection; production exclusion therefore remains the host build's responsibility. Every public entry is side-effect-free, no entry auto-starts, and no browser global is exposed as an alternate initialization path.

Console and highlight presentation

The optional panel entry owns presentation behavior so the core remains free of UI side effects.

import { createTracker } from "orbi-tation";
import { createPanel } from "orbi-tation/panel";

const tracker = createTracker();
const panel = createPanel(tracker, {
  highlightColor: "#ff0000",
  highlightDuration: 3000,
});

panel.mount();
tracker.start();

// Cleanup:
tracker.stop();
panel.unmount();

Core API

createTracker(options?)

Options:

Option Default Description
root document.body DOM node or selector observed after start()
maxEvents 100 Positive integer event-history limit
dedupeWindowMs 50 Non-negative duplicate suppression window
onError console.error Receives normalized record and listener failures

The returned tracker provides:

  • start() — begin observing; repeated calls are safe.
  • stop() — disconnect the observer; repeated calls are safe.
  • clear() — clear history and deduplication state without stopping.
  • getEvents() — return a readonly snapshot of normalized events.
  • subscribe(listener) — receive events and return an idempotent unsubscribe function.

The root may be an Element, Document, or ShadowRoot/DocumentFragment. Only the configured root and its descendants are observed. Changing roots requires creating a new tracker instance; stop()/start() preserves the root configured at creation time.

Use root for coarse subtree scoping. Fine-grained include/exclude selector filters and automatic discovery of open Shadow DOM roots are separate planned capabilities; for now, pass an open ShadowRoot explicitly when that is the subtree you want to inspect.

Invalid configuration fails synchronously. Starting without an available root, with a selector that matches nothing, with an unsupported root node, or without MutationObserver throws a TrackerError with a stable error code.

Event model

TrackerMutationEvent is a discriminated union of:

  • TrackerAttributeEvent
  • TrackerChildListEvent
  • TrackerCharacterDataEvent

Every event has a monotonic sequence, ISO timestamp, type, and serializable target description. Child nodes are represented by compact summaries. Events do not expose live DOM nodes or native mutation records.

tracker.subscribe((event) => {
  if (event.type === "attributes") {
    console.log(event.attributeName, event.oldValue, event.newValue);
  }
});

See docs/ARCHITECTURE.md for the complete public contract and module boundaries.

Package outputs

pnpm build creates:

  • dist/index.js and dist/index.cjs — side-effect-free core entry
  • dist/panel.js and dist/panel.cjs — optional presentation entry
  • .d.ts, .d.cts, declaration maps, and JavaScript source maps
  • src/ — TypeScript sources referenced by declaration maps

Package contents and both ESM and CommonJS imports are verified from the packed tarball by pnpm test:package.

Development

Installing dependencies also installs the repository's Lefthook-managed Git hooks. Pre-commit hooks check staged files with Prettier and ESLint and prevent commits on protected branches. Pre-push hooks run type-checking and unit tests. CI repeats these checks as the authoritative merge gate.

pnpm format
pnpm format:check
pnpm typecheck
pnpm test
pnpm test:browser:install
pnpm test:browser
pnpm test:package
pnpm test:production
pnpm --silent benchmark

pnpm verify runs strict type-checking, unit tests, builds, package packing, ESM/CommonJS smoke imports, the focused Chromium integration suite, and a production tree-shaking fixture that verifies guarded imports are removed.

Performance

pnpm --silent benchmark measures low-volume latency, burst processing, large-subtree updates, bounded long-running sessions, retained heap and event payloads, and current panel-presentation overhead in headless Chromium. Results are environment-specific and are not enforced as brittle shared-CI thresholds. See docs/PERFORMANCE.md for the baseline environment, workloads, initial budgets, and interpretation rules.

Limitations

  • Observation defaults to document.body; pass root for another DOM node or selector.
  • Closed Shadow DOM and iframe contents are not observed automatically. Open ShadowRoot instances can be observed explicitly; automatic discovery is tracked separately.
  • Selectors describe the target at mutation-processing time and may become stale after later DOM changes.
  • Large mutation volumes still have runtime cost despite bounded history and deduplication; measure representative application workloads against the documented benchmark rather than assuming negligible overhead.

Browser compatibility

The package targets the latest two releases of Chrome, Firefox, Safari, and Edge. Internet Explorer is not supported.

Roadmap

See the product roadmap for milestones and recommended implementation order.

License

This project is released under the MIT License.

About

A Chrome DevTools snippet that tracks DOM mutations, logs them cleanly in the console, and visually highlights changed elements in real time.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages