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

@ontahi/devtools

v1.0.0-alpha.12

Published

Development-only runtime diagnostics and React tooling for Ontahi clients

Readme

@ontahi/devtools

Experimental, development-only diagnostics for Ontahí web clients.

The package currently provides a bounded in-memory diagnostic store, a compositional RuntimeTransport instrument, and an opt-in React panel. The panel leads with application intent, keeps transport families as supporting metadata, and lets each request and response move between a semantic projection, body JSON, and its complete Runtime Protocol envelope. It does not patch fetch, WebSocket, or browser globals, and it does not persist or upload diagnostic data.

Activity Graph Read summaries follow Settings → Authoring language in the list, detail heading, and Visual request (Selection and ordering included). Filtering matches the displayed dialect. This is a pure projection of captured canonical data, not a CodeMirror editor or a conversion of previously rendered text. Changing the preference reprojects existing entries without traffic or payload mutation; Body JSON, Envelope, and copied JSON remain the captured protocol data. Summaries are compact diagnostic labels, not guaranteed executable Console documents: they may include Views, multiple ordering keys, redacted values, or Reference selections outside today's Console grammar. Nullable get is shown as first, not guessed to be exists. Command and Operation labels remain unchanged until those authoring dialects are defined.

import { entity, field } from '@ontahi/core/data-graph';
import { createRuntimeTransportRouter } from '@ontahi/core/runtime/protocol';
import { createOntahiDiagnostics, instrumentRuntimeTransport } from '@ontahi/devtools';
import { OntahiDevtools } from '@ontahi/devtools/react';
import { createFetchRuntimeTransport, createWebSocketRuntimeTransport } from '@ontahi/react/graph';

const TodoItem = entity('TodoItem', {
  id: field.id(),
  completed: field.boolean(),
});
const diagnostics = createOntahiDiagnostics();
const runtimeTransport = createRuntimeTransportRouter({
  transports: {
    http: instrumentRuntimeTransport({
      diagnostics,
      id: 'http',
      kind: 'fetch',
      transport: createFetchRuntimeTransport(),
    }),
    websocket: instrumentRuntimeTransport({
      diagnostics,
      id: 'websocket',
      kind: 'websocket',
      transport: createWebSocketRuntimeTransport(),
    }),
  },
  routing: {
    'graph.read': 'websocket',
    'graph.command': 'websocket',
    operation: 'websocket',
    'durable.operation.observe': 'websocket',
  },
});

<OntahiDevtools
  console={{
    entities: [TodoItem],
    initialDocument: 'TodoItem.where(completed = false).many()',
  }}
  diagnostics={diagnostics}
  runtimeTransport={runtimeTransport}
/>;

The Console supports filtered and unfiltered read terminals. Tag.count() and TodoItem.where(completed = false).count() lower directly to the canonical Graph Read count mode; count requests do not inherit the Console row limit or a row cardinality. Tag.exists() and TodoItem.where(completed = false).exists() return a Boolean in both Visual and JSON. They reuse nullable get with a limit of one and the existing read policy, then project the successful result to presence/absence. Errors remain errors, never false. exists() accepts neither limit nor orderBy, and has no table controls. Activity retains the actual get exchange. Many reads may override the default row limit in source, for example Tag.limit(10).many() or TodoItem.where(completed = false).limit(5).many().

The composer includes TS / Declarative controls. For example, TodoItem where completed = false selects many by default; append order by title descending limit 10, or an explicit terminal such as first, one, count, or exists with the existing modifier restrictions. Both dialects keep the same runtime and policy boundary, Boolean/enum controls, and permission-aware completion. Header sorting and limit changes edit the active dialect and submit through Runtime Transport. Accepting order by/orderBy reopens completion for its permitted Fields. Ordering suggestions load through a metadata-only graph.read request as soon as the draft names a reflected Entity, including incomplete queries. No initial Run is required. Field and direction dropdowns edit the source in both dialects without executing; undo/redo and Escape work like Boolean/enum controls. Loading, denied/unavailable metadata (with retry), and a known empty policy are distinguished. Typing and accepting suggestions never execute data queries.

Switching dialect does not run a query. Undo/redo restores the original source and dialect together; equivalent converted queries keep the current result without a false stale notice. Invalid drafts (including unsupported comments) block switching with an explanation and are never discarded. Empty drafts may switch unchanged. Settings → Authoring language saves the preferred dialect for Ontahí authoring editors on this browser origin, including other tabs. With no saved preference, TS is the default. The Console switch is a local override; it does not change Settings. Visiting Settings or Activity preserves the mounted Console draft, result, and undo history. A preference change converts valid drafts without running; incomplete drafts retain their dialect with a notice.

An explicit console.initialDialect is a host override of the saved preference. If supplied, initialDocument must use that initial dialect (TS when omitted); a saved preference then converts it safely. Explorer Selection predicates already share their syntax across both dialects, and plain-text searches remain plain text. Syntax highlighting uses a dedicated dark palette for the Console, including where, many, other clauses, Fields, and literals.

Query ordering is shared by the source editor and the Visual result table:

Tag.orderBy(name).limit(10).many()
Tag.orderBy(name, desc).many()

Run reflects source ordering in the table header. Clicking a scalar Field header cycles through ascending, descending, and no explicit order: it edits only the ordering source ranges as one undoable transaction and submits a new Graph Read. Sorting happens in the runtime before the limit, never just over visible rows. The first slice supports one ordering Field; first() and one() also accept textual ordering, while count() and exists() do not.

Typing or undoing does not execute. The table and arrow describe the last successful execution. The result uses one compact toolbar: last successful round-trip duration (including transport), editable limit for many reads, and Visual/JSON. It does not repeat the query, success message, or row-count/limit summary above the table. Draft changes, pending reads, and failures leave the snapshot visible with a short toolbar notice; actionable errors remain visible in the result body. Controls are disabled while running or when the draft is invalid, targets another Entity, or is no longer a many read. A valid same-Entity draft is preserved and submitted with the new sort. Headers intersect intrinsically sortable Fields with the receiver's ordering capabilities, discovered independently of data execution. Denied headers remain focusable but inactive, with a tooltip explaining the policy restriction. Missing or malformed capabilities preserve readable results but disable ordering with a refresh explanation. Replacing Runtime Transport or changing its graph.read route automatically refreshes metadata; old results still require a Run on that transport before table edits. An access_denied response also refreshes capabilities. Pass console.identity the same ExecutionIdentity used by the application's Graph provider. Changing its principal or cacheScope immediately hides prior results, cancels pending reads, and refreshes ordering metadata, without losing the draft or undo history. Use cacheScope for tenant/role/policy revisions that do not change the principal. Equivalent identity values do not trigger discovery. This is local cache invalidation, not a credential or a client-supplied permission grant; it is never added to protocol requests. When omitted, identity defaults to anonymous. Hosts must propagate authority changes, including login/logout; unreported cookie or server policy changes cannot be detected automatically. Receiver policy remains authoritative on every request. Within an unchanged identity, rejections are shown without replacing the successful result data. Activity remains an explicit diagnostic history; this invalidation does not erase its captured exchanges. The many-result toolbar's numeric Limit control accepts non-negative safe integers, including zero. Apply or Enter edits only the existing limit literal (or inserts .limit(...)) and runs the current same-Entity draft, preserving its filters and ordering as one undoable source transaction. Typing in the control alone does not execute; Apply appears only while its numeric draft differs from the executed limit, which is also available in the input tooltip. Textual limit changes appear in the control after a successful Run; pending or rejected reads retain the old result and executed limit. Invalid/non-many/other-Entity drafts, pending reads, or a replaced transport disable the control. Server maximum-limit policy remains authoritative; the control does not grant a higher limit. Multi-Field ordering and pagination remain follow-ups. The existing 50-row visual preview cap is reported separately when reached.

orderBy(...) autocomplete uses that same capability snapshot for the matching Entity and transport, even in incomplete drafts. It suggests only permitted scalar Fields. While discovery is pending or unavailable, it offers no ordering Fields; retry loads metadata, not rows. Changing Entity or replacing the transport discards stale suggestions. Other completions and manual source authoring remain schema-based; this assistance does not grant authority or prevent the server from rejecting a manually authored order.

When ordering is the rejected capability, the receiver reports the requested Entity and Field, for example Ordering by TodoItem.completed is not allowed by the Graph Read policy. The Console displays that server message; the protocol body retains access_denied and optional details: { reason: 'ordering_not_allowed', entityName, fieldName }. Other authorization failures remain generic. Textual ordering can still be authored independently of header availability.

createRuntimeTransportRouter(...) owns effective routing, capability validation, inspection, and subscription. Devtools recognizes that configurable Runtime Transport and owns its generic Settings projection; applications do not provide settings UI, React state, or presets. Profiles are derived from the registered transports and their supported capabilities. The host still chooses the initial routing and may subscribe for application policies such as cache invalidation or local persistence. Changing a setting never replays requests or moves an active observation between transports.

instrumentRuntimeTransport(...) preserves and delegates this routing capability when it wraps a configurable transport.

The default Visual detail projects Operation requests to their input and successful responses to their returned value, flattening Entity Refs to their locator identity. Body JSON and Envelope keep the complete Runtime Protocol evidence available when transport-level inspection is needed.

The React surface opens as a full-width bottom drawer at a compact default height. Drag its top handle, or focus the handle and use the arrow keys, to resize it while the application remains visible above.

When the host supplies Console Entity definitions, Devtools adds a Console panel backed by the shared Ontahí Lezer and CodeMirror language packages. The first walking skeleton accepts Entity.where(Selection).many(), nullable Entity.where(Selection).first(), and exact-cardinality Entity.where(Selection).one(), lowers them to the canonical Graph Read body, and sends them through the same configured Runtime Transport as application traffic. Submission is explicit through Run or Mod-Enter; results default to the same semantic visual projection used by Activity, can be switched to JSON, remain in the panel, and the exchange appears in Activity.

Contextual Entity Selections are also available as Book.parts.chapters.many() or declarative Book through parts through chapters many. Completion, finite-value widgets, capability discovery and result ordering use the current destination Entity. The shared language model preserves source factories and hops through dialect switching and table-driven sort/limit edits. Contextual reads negotiate Graph Read v2 support on the active transport before execution; unsupported providers do not receive a downgraded read. Activity renders the expanded relation membership in the chosen dialect, without guessing which named factory produced it.

The Console loads Graph Read capability metadata for all configured base Entities when mounted, without running row/count queries. Variants registered on a base read policy appear automatically as roots (for example Chapter.many() / Chapter many), labeled with their base Entity. No separate variant client export or Console configuration is required. Both dialects share inherited Fields, narrowed enum values and declared by factories; ordering uses the owning base policy, including table-driven source edits. The server imposes classification rather than trusting a client filter. Contextual factories targeting variants also support Book.parts.chapters.many() and declarative Book through parts through chapters many. Completion and table sorting use the final classified destination's base policy, not the starting Book policy. Graph Read v2 independently enforces every source and target classification plus base scopes. Variant-root Views remain unsupported.

The catalog is scoped to the active transport, route and execution identity; switching any of these drops old metadata and ignores late replies. A failed base lookup does not hide successful roots from other bases. Changing the draft between known roots does not issue another metadata lookup. Hosts must keep passing the same console.identity as their transport's execution identity.

Payload capture is disabled by default. Enabling it requires a host-owned redactor:

createOntahiDiagnostics({
  capturePayloads: true,
  redact: value => removeApplicationSecrets(value),
});

The Todo example exercises this integration. Transport connection-state evidence remains a later Plan 148 slice.

Cache: local runtime state

Views are ordered Console (when configured), Activity, Cache, Settings. Activity remains the initial view. Pass the same clientCache used by the application's graph provider/client:

<OntahiDevtools diagnostics={diagnostics} clientCache={graphClient.clientCache} />

Cache is a live, read-only view of canonical entity records, locator aliases, freshness markers, and normalized output skeletons. Instances are grouped by entity type with collapsible groups and counts. Search names, identities or aliases across groups; matching groups expand while searching. Names and titles supplement canonical identities when available. In Data, follow normalized field references to cached instances; the Console entity definitions also identify embedded relationship rows by their declared identity. Missing targets are marked unavailable. Back restores the previous selection, search and detail section, including navigation between outputs and entities. These values come directly from the local cache; Activity payload capture/redaction settings do not transform them. Mount Devtools only in the development contexts where inspecting application data is intended.

Output entries are not hook instances. The current inspector does not track active observers, Operation execution state, historical writers, field-level coverage, or indirect/transitive references. Missing fields are not classified as null or stale. Invalidating an entity may leave an output skeleton with an unresolved reference, visible in its normalized JSON.

Output entries use semantic read titles, View/Selection summaries, and visible identity scopes. Operation query outputs carry explicit source labels; arbitrary custom keys remain generic rather than being guessed to be Operations. Full keys remain available under “Cache key / JSON”. Source labels are descriptive provenance, not a freshness or authority guarantee.

Query observations and entity history

The transport decorator also instruments RuntimeTransport.graph.observe(...). Activity groups its start, incoming snapshots, and termination into one query observation, with transport, status, update count, and a semantic query title when the request was captured. Results default to the visual projection, with JSON available in the detail header. Choose an earlier snapshot or Follow latest; inspecting a snapshot does not pause the application stream. Observations stay lazy and preserve cancellation, consumer closure, protocol errors, and transport errors.

Query requests and snapshot payloads follow the same diagnostics capture/redaction policy as exchanges. With payload capture disabled, status and row/update counts remain visible. The bounded Activity store can evict older snapshots; the detail reports when the selected snapshot is lost. These entries describe actual graph transport streams, not React hook instances or every query that reruns after invalidation.

With clientCache connected, enable Settings → Record entity history to capture a baseline and subsequent entity writes, invalidations, and cache clears. Cache → History shows the timeline, field differences from the previous retained snapshot in that recording segment, and a detached snapshot. Missing fields mean absent from that local snapshot, not server deletion. Repeated writes are retained even when field values are unchanged.

Recording starts disabled and stays in memory: at most 200 entries and approximately 2 MB of serialized UTF-16 payloads, with dropped-entry and capture-error counts. Oversized entries are skipped. Recording continues while the panel is closed; stopping keeps the captured entries, and Clear entity history only clears that history. Reloading, unmounting Devtools, or replacing the cache drops the history; a replacement cache starts with recording disabled.

History captures local cache field values, independently of Activity's payload redactor. It is a debugging record of this client's observed state, not persisted entity versioning or a complete audit. Cache writes do not yet carry observation/exchange IDs, so this release does not infer causal links between an Activity snapshot and a cache write.

Observe from the Console

Use Observe next to Run to subscribe to the current many-query expression. For example, in Todo enter TodoItem.where(completed = false).many() (or the corresponding declarative query), then complete an item in the application: a new snapshot removes it from the Console result. Stop cancels the subscription and keeps the last received snapshot visible.

Observe requires a transport with graph.observe, such as Todo's WebSocket route. The initial slice supports Graph Read v1 many queries; scalar terminals (first, one, count, exists), invalid expressions and contextual v2 selections cannot start an observation. No .observe() Console syntax is introduced. Graph observation frames carry rows rather than read capabilities; the Console continues using its separate capability discovery requests.

The submitted expression stays fixed while observing. Editor and dialect changes remain drafts; Run and the result's sort/limit execution controls wait for Stop. Switching to Activity, Cache or Settings keeps the observation alive. Closing Devtools, unmounting it, replacing its transport or cache, or changing console.identity cancels it. Late responses cannot update results or the cache.

With clientCache connected, received Entity rows are normalized using the host's Entity reflection, including base identities of discovered variants. This lets enabled History record those writes. A row disappearing from a query does not delete its canonical entity. Console observations do not create retained output skeletons, and historical output semantics remain independent. The Console result itself uses the received snapshot; it does not live-denormalize older results through newer cache values.