Data Inspector

A Vue query workbench for live server-side objects: register data sources, then compose jora queries in the owning process.

A Vue query workbench for live server-side objects: register data sources, then compose jora queries in the owning process.

Package: @devframes/plugin-data-inspector · Vue + Vite

Query workbench with results and data shape panel

Query data with advanced Jora syntax

What it does

  • Query workbench — a CodeMirror jora editor with server-computed autocomplete; queries auto-run as you type, state in the URL hash.
  • Auto rerun — optional poller (auto rerun every N seconds).
  • Result viewer — normalizes to strict JSON (circulars → $ref; Maps, Sets, class instances, functions, Dates get type badges) plus per-query stats.
  • Expansion, shape & filters — deep nodes fetch lazily via load deeper; a shape panel shows a one-level skeleton; filters drop functions and _/$ properties.
  • Saved queries — recipes (query + title/description + filters) in the workspace scope (committable) and the project scope (per-checkout).

A built-in example source registers by default; opt out with exampleSource: false (--no-example; DEVFRAME_DATA_INSPECTOR_EXAMPLE=0 when injected).

Providing data sources

The registry is process-global — register anywhere, before or after mount.

import { registerDataSource } from '@devframes/plugin-data-inspector/registry'

registerDataSource({
  id: 'my-plugin:store', // namespace with your devframe id
  title: 'My plugin store',
  description: 'The live state store',
  icon: 'i-ph:database-duotone',
  data: () => store,
  queries: [
    { title: 'Active sessions', query: 'sessions.mapEntries().value.[active]' },
    { title: 'Config (data only)', query: 'config', excludeFunctions: true },
  ],
})

data is a plain value or sync/async factory; static: true resolves it once; queries show read-only beside saved ones; registerDataSource returns an unregister callback. Zero-dependency devframes register through the typed context service ctx.services.whenAvailable('devframes:plugin:data-inspector:sources', …).

Queries are eval-grade: jora invokes any function reachable as an own property and fires own getters. Register live objects accordingly, and keep endpoints on loopback.

Deep linking

Workbench state lives in the URL hash (#source=<id>&query=<jora>, filters); the handshake token rides the query string (?devframe_auth_token=), scrubbed on read.

In a hub, another dock jumps to a source via dock activationrpc.call('hub:docks:activate', { dockId: 'devframes:plugin:data-inspector', params: { sourceId } }) (waits for registration).

Standalone

pnpx @devframes/plugin-data-inspector                      # the example source
pnpx @devframes/plugin-data-inspector stats.json log.jsonl # one static source per data file
pnpx @devframes/plugin-data-inspector build stats.json     # self-contained static export
pnpx @devframes/plugin-data-inspector attach               # attach to a process running the inject endpoint

.json parses whole; .jsonl / .ndjson as a record array. build embeds the dataset in a static site.

Mount into a Vite host

For Vite DevTools:

// vite.config.ts
import createDataInspectorDevframe from '@devframes/plugin-data-inspector'
import { createPluginFromDevframe } from '@vitejs/devtools-kit/node'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    createPluginFromDevframe(createDataInspectorDevframe()),
  ],
})

Register sources with registerDataSource, or mount via devframeVite(createDataInspectorDevframe()) from @devframes/vite/single.

Programmatic

import { createDataInspectorDevframe } from '@devframes/plugin-data-inspector'

export default createDataInspectorDevframe({
  exampleSource: false,
})

Attach to another Node process

The target starts the inject endpoint:

import { exposeDataInspector } from '@devframes/plugin-data-inspector/inject'

await exposeDataInspector({
  sources: [{ id: 'app:store', title: 'App store', data: () => store }],
})

sources pre-registers entries; call it empty to expose whatever's registered. Or code-free:

DEVFRAME_DATA_INSPECTOR=1 node --import @devframes/plugin-data-inspector/inject server.js

This auto-registers a globalThis source:

// somewhere in the running process
globalThis.store = store
globalThis.cache = cache

It reads globalThis at query time, so later assignments appear next run (opt out: DEVFRAME_DATA_INSPECTOR_GLOBAL=0).

The inject endpoint binds 127.0.0.1, requires the trust handshake with a per-run token, and advertises itself in node_modules/.data-inspector/discovery.json, which attach reads (or pass ws://… + --token).

RPC

All devframes:plugin:data-inspector:*:

FunctionTypeReturns
sourcesqueryEvery registered source (meta, suggested queries).
queryqueryRuns a jora query; normalized result with stats.
queryPathqueryDepth-limited subtree slice (lazy expansion).
skeletonqueryA source's type skeleton, honoring filters.
suggestqueryAutocomplete candidates at the cursor.
saved:list / saved:save / saved:deletequery / actionSaved-query recipes (workspace, project).

Source

plugins/data-inspector