|
| 1 | +# prototype-data-inspector |
| 2 | + |
| 3 | +> **PROTOTYPE — throwaway code.** This is not an example and not a plugin. |
| 4 | +> It exists to answer one question and is kept on its spike branch as a |
| 5 | +> primary source. Do not merge, extend, or depend on it. |
| 6 | +
|
| 7 | +## The question |
| 8 | + |
| 9 | +Can a **data-inspector plugin** work on devframe? Architecture under test: |
| 10 | +other plugins/hosts register live server objects into a registry; the UI |
| 11 | +composes jora queries that execute **server-side against the live object**; |
| 12 | +normalized results return over devframe RPC and render in a **themed |
| 13 | +discovery `struct` view** inside a **Vue + `@antfu/design`** workbench, with |
| 14 | +saved queries persisted per user or per project. |
| 15 | + |
| 16 | +## Verdict: YES — validated end to end |
| 17 | + |
| 18 | +Stage 1 (`pnpm --filter prototype-data-inspector spike`): **13/13 checks** |
| 19 | +against a real programmatic `ViteDevServer` (jora over live objects, Map/Set |
| 20 | +bridge methods, normalizer, stat-mode suggestions, function-invocation hazard). |
| 21 | + |
| 22 | +Stage 2 (`pnpm --filter prototype-data-inspector dev`): the full workbench |
| 23 | +against the Vite server serving the page itself — left editor / right viewer |
| 24 | +split panes, debounced auto-run, client-side syntax gate, non-destructive |
| 25 | +errors, per-query stats (jora / normalize / rpc timings, payload size, node |
| 26 | +count), remote autocomplete, saved queries in both scopes, synced dark mode. |
| 27 | + |
| 28 | +## The pieces (what the real plugin reuses) |
| 29 | + |
| 30 | +- **`src/registry.ts`** — data sources as |
| 31 | + `{ id, title, description?, getData(): any, static?: boolean }`, registered |
| 32 | + dynamically against the shared `DevframeNodeContext` |
| 33 | + (`WeakMap` idiom from `plugins/git`). `static: true` memoizes `getData()`; |
| 34 | + `registerDataSource` returns an unregister fn and fires change listeners. |
| 35 | +- **`src/normalize.ts`** — the core asset: live graph -> strict JSON |
| 36 | + (circular -> `$ref`, Map/Set/class/function/BigInt/Error tagging, |
| 37 | + depth/entry/prop/string caps) with stats. |
| 38 | +- **`src/query-engine.ts`** — jora with duck-typed `fromMap()` / |
| 39 | + `mapEntries()` / `fromSet()` / `ownKeys()` / `typeOf()` bridges, plus |
| 40 | + flattened stat-mode suggestions. |
| 41 | +- **`src/saved-queries.ts`** — id-keyed saved queries via devframe |
| 42 | + `createStorage` (debounced atomic JSON): |
| 43 | + `user` -> `node_modules/.data-inspector/queries.json` (per checkout), |
| 44 | + `project` -> `.devframe/data-inspector/queries.json` (committable, shared). |
| 45 | + Saving an id into one scope removes its twin from the other. |
| 46 | +- **`src/rpc-functions.ts`** — `data-inspector:{sources,query,suggest}` + |
| 47 | + `data-inspector:saved:{list,save,delete}`, all `jsonSerializable`. |
| 48 | +- **`src/spa/`** — Vue 3 + `@antfu/design` workbench: canonical uno preset |
| 49 | + block (mirrors `plugins/inspect`), design components used directly |
| 50 | + (`LayoutToolbar`, `LayoutSplitPane`, `FormSelect`, `FormTextInput`, |
| 51 | + `ActionButton`/`ActionIconButton`/`ActionDarkToggle`, |
| 52 | + `DisplayBadge`/`DisplayBytes`/`DisplayDuration`), discovery `ViewModel` |
| 53 | + bound to a container ref with the default page redefined as a struct view. |
| 54 | + |
| 55 | +## Findings (things the real plugin must know) |
| 56 | + |
| 57 | +1. **Vite 8 compat surfaces**: `server.moduleGraph.idToModuleMap` is |
| 58 | + Map-*shaped* but not a `Map` (duck-type it), and compat `ModuleNode`s are |
| 59 | + getter facades whose own keys are `_moduleGraph`/`_clientModule`/`_ssrModule` |
| 60 | + only — query `environments.<name>.moduleGraph` for real own fields. |
| 61 | +2. **discovery under Vite** needs `define: { global: 'globalThis' }` and its |
| 62 | + CSS injected into the shadow root via `?inline` import. Render results |
| 63 | + through the model (`page.define('default', ...)` + `setData`) — rendering |
| 64 | + into `dom.pageContent` manually races discovery's own page cycle. |
| 65 | +3. **Theming**: `host.colorScheme.set()` + `--discovery-*` custom props |
| 66 | + (bridged to design tokens through CSS vars that flip with `.dark`). |
| 67 | +4. **jora syntax check runs client-side** (`jora.syntax.parse`, ~20 KB gzip): |
| 68 | + parse errors carry `details.loc.range`, so end-of-input errors show as a |
| 69 | + soft "keep typing" state and malformed queries never hit the wire. |
| 70 | +5. **Suggestions** arrive nested per stat entry (flatten server-side); |
| 71 | + prefix-filtering is the client's job. |
| 72 | +6. **`DisplayBadge` with an explicit CSS color** sets fg and bg to the same |
| 73 | + value — pass a hue number (theme-aware tint) or use `variant="solid"`. |
| 74 | +7. **`LayoutSplitPane`** needs `splitpanes` (+ `Pane`) as a direct dep — it's |
| 75 | + an `@antfu/design` peer, not a bundled dep. |
| 76 | +8. **HAZARD (by design of live-query mode)**: a jora query can invoke any |
| 77 | + function reachable as an own property of the live object and fires own |
| 78 | + getters; no CPU/timeout limits. Present it as eval-grade access to the dev |
| 79 | + server (localhost-only), and pin `jora >= 1.0.0-beta.16`. |
| 80 | +9. **Versions pinned deliberately**: `jora@1.0.0-beta.16`, |
| 81 | + `@discoveryjs/discovery@1.0.0-beta.99` — both beta with breaking renames |
| 82 | + between betas. |
| 83 | + |
| 84 | +## Deviations from repo conventions (prototype-only) |
| 85 | + |
| 86 | +- `jora` + `@discoveryjs/discovery` pinned in the *default* catalog (pnpm |
| 87 | + auto-promoted them); a real plugin would place them in a named catalog |
| 88 | +- plain textarea editor; production should embed discovery's `QueryEditor` |
| 89 | + (CodeMirror 5 + jora mode) with the async-suggestion re-trigger pattern |
| 90 | +- `.vue` internals aren't typechecked (`tsc --noEmit` + shims, same as the |
| 91 | + existing plugins) |
| 92 | + |
| 93 | +## Run it |
| 94 | + |
| 95 | +```sh |
| 96 | +pnpm --filter prototype-data-inspector spike # stage 1: node-only proof, prints 13 checks |
| 97 | +pnpm --filter prototype-data-inspector dev # stage 2: http://localhost:5173/ |
| 98 | +``` |
0 commit comments