|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +--- |
| 4 | + |
| 5 | +# Data Inspector |
| 6 | + |
| 7 | +An interactive query workbench for live server-side objects, built as a **Vue** SPA. Plugins and hosts register **data sources**; you compose [jora](https://discoveryjs.github.io/jora/) queries against them — executed in the process that owns the objects — and explore normalized results in a struct view with type badges, a data-shape panel, filters, and saved queries. |
| 8 | + |
| 9 | +Package: `@devframes/plugin-data-inspector` · framework: **Vue + Vite** |
| 10 | + |
| 11 | +## What it does |
| 12 | + |
| 13 | +- **Query workbench** — a CodeMirror jora editor with syntax highlighting and server-computed autocomplete; queries auto-run as you type, with a client-side syntax gate so malformed input never hits the wire. Source, query, and filters persist in the URL, so any workbench state is shareable. |
| 14 | +- **Result viewer** — results normalize to strict JSON (circulars become `$ref` markers; Maps, Sets, class instances, functions, and Dates get type badges) with per-query stats: jora / normalize / rpc timings, payload size, node count. The value-actions popup copies paths and turns any key into a query. |
| 15 | +- **Data shape panel** — a one-level type skeleton of the active source, independent of the query; click a property to query it. |
| 16 | +- **Filters** — exclude functions, `_`-prefixed, or `$`-prefixed properties from results and skeleton alike. |
| 17 | +- **Saved queries** — recipes (`query` + optional title/description + the filters they were authored with), id-keyed, in two scopes: **workspace** (committable, shared with the team) and **project** (per-checkout). |
| 18 | + |
| 19 | +A built-in example source demonstrates all of it on first run; disable it with `createDataInspectorDevframe({ exampleSource: false })` once your own sources are registered. |
| 20 | + |
| 21 | +## Providing data sources |
| 22 | + |
| 23 | +The registry is **process-global**: register from anywhere in the process — plugin setup, host hooks, application code — before or after the inspector mounts. |
| 24 | + |
| 25 | +```ts |
| 26 | +import { registerDataSource } from '@devframes/plugin-data-inspector/registry' |
| 27 | + |
| 28 | +registerDataSource({ |
| 29 | + id: 'my-plugin:store', // namespace with your plugin id |
| 30 | + title: 'My plugin store', |
| 31 | + description: 'The live state store', |
| 32 | + icon: 'i-ph:database-duotone', |
| 33 | + data: () => store, |
| 34 | + queries: [ |
| 35 | + { title: 'Active sessions', query: 'sessions.mapEntries().value.[active]' }, |
| 36 | + { title: 'Config (data only)', query: 'config', excludeFunctions: true }, |
| 37 | + ], |
| 38 | +}) |
| 39 | +``` |
| 40 | + |
| 41 | +The contract: |
| 42 | + |
| 43 | +```ts |
| 44 | +interface DataSourceEntry { |
| 45 | + id: string |
| 46 | + title: string |
| 47 | + description?: string |
| 48 | + icon?: string |
| 49 | + /** A plain value, or a factory returning one (sync or async). */ |
| 50 | + data: unknown | (() => unknown | Promise<unknown>) |
| 51 | + /** The resolved value never changes: resolve once and memoize. */ |
| 52 | + static?: boolean |
| 53 | + /** Suggested queries, shown read-only next to saved ones. */ |
| 54 | + queries?: Query[] |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +Live objects passed to `data` stay live — every query reads their current state. `registerDataSource` returns an unregister function, and connected workbenches refresh their source list on every change. |
| 59 | + |
| 60 | +Integrations that prefer zero package dependency consume the same registry through the typed [context service](../guide/devframe-definition#cross-plugin-services): |
| 61 | + |
| 62 | +```ts |
| 63 | +ctx.services.whenAvailable('devframes:plugin:data-inspector:sources', (sources) => { |
| 64 | + sources.register({ id: 'my-plugin:store', title: 'My store', data: () => store }) |
| 65 | +}) |
| 66 | +``` |
| 67 | + |
| 68 | +> [!WARNING] |
| 69 | +> Queries are eval-grade access to registered objects: jora can invoke any function reachable as an own property and fires own getters. Register live objects with that in mind, and keep inspector endpoints on loopback. |
| 70 | +
|
| 71 | +## Mount into a Vite host |
| 72 | + |
| 73 | +```ts |
| 74 | +import { registerDataSource } from '@devframes/plugin-data-inspector/registry' |
| 75 | +// vite.config.ts |
| 76 | +import { dataInspectorVitePlugin } from '@devframes/plugin-data-inspector/vite' |
| 77 | +import { defineConfig } from 'vite' |
| 78 | + |
| 79 | +export default defineConfig({ |
| 80 | + plugins: [ |
| 81 | + dataInspectorVitePlugin(), |
| 82 | + { |
| 83 | + name: 'my-app:data-sources', |
| 84 | + configureServer(server) { |
| 85 | + registerDataSource({ |
| 86 | + id: 'vite:server', |
| 87 | + title: 'Vite Dev Server', |
| 88 | + data: () => server, |
| 89 | + queries: [{ title: 'Plugin names', query: 'config.plugins.name' }], |
| 90 | + }) |
| 91 | + }, |
| 92 | + }, |
| 93 | + ], |
| 94 | +}) |
| 95 | +``` |
| 96 | + |
| 97 | +Hub hosts mount the default export like any devframe definition. |
| 98 | + |
| 99 | +## Standalone CLI |
| 100 | + |
| 101 | +```sh |
| 102 | +devframe-data-inspector # the example source |
| 103 | +devframe-data-inspector stats.json log.jsonl # one static source per data file |
| 104 | +devframe-data-inspector build stats.json # self-contained static export |
| 105 | +devframe-data-inspector attach # attach to a process running the agent |
| 106 | +``` |
| 107 | + |
| 108 | +`.json` files parse whole; `.jsonl` / `.ndjson` parse as an array of records. `build` writes a static site embedding the dataset — the same query engine runs client-side there, so saved recipes keep working. |
| 109 | + |
| 110 | +## Attach to another Node process |
| 111 | + |
| 112 | +The target process opts in by starting the agent: |
| 113 | + |
| 114 | +```ts |
| 115 | +import { exposeDataInspector } from '@devframes/plugin-data-inspector/agent' |
| 116 | + |
| 117 | +await exposeDataInspector() |
| 118 | +``` |
| 119 | + |
| 120 | +or with zero code changes: |
| 121 | + |
| 122 | +```sh |
| 123 | +DEVFRAME_DATA_INSPECTOR=1 node --import @devframes/plugin-data-inspector/agent server.js |
| 124 | +``` |
| 125 | + |
| 126 | +The agent binds `127.0.0.1`, requires devframe's trust handshake with a per-run pre-shared token, and advertises its endpoint in `node_modules/.data-inspector/agent.json` — `devframe-data-inspector attach` consumes it automatically (or pass `ws://…` and `--token` explicitly). Queries execute inside the target process, where the live objects are. Treat the endpoint like a debugger port. |
| 127 | + |
| 128 | +## RPC surface |
| 129 | + |
| 130 | +All functions are namespaced `devframes:plugin:data-inspector:*`: |
| 131 | + |
| 132 | +| Function | Type | Returns | |
| 133 | +|----------|------|---------| |
| 134 | +| `sources` | `query` | Every registered source (meta and suggested queries). | |
| 135 | +| `query` | `query` | Runs a jora query against a source; normalized result with stats. | |
| 136 | +| `skeleton` | `query` | The type skeleton of a source, honoring the filter options. | |
| 137 | +| `suggest` | `query` | Autocomplete candidates from jora's stat mode at a cursor position. | |
| 138 | +| `saved:list` / `saved:save` / `saved:delete` | `query` / `action` | Saved-query recipes in the `workspace` and `project` scopes. | |
0 commit comments