Skip to content

feat: data-inspector plugin (jora live-query workbench), ctx.services, storage scopes#101

Merged
antfu merged 22 commits into
mainfrom
free-points-return
Jul 17, 2026
Merged

feat: data-inspector plugin (jora live-query workbench), ctx.services, storage scopes#101
antfu merged 22 commits into
mainfrom
free-points-return

Conversation

@antfubot

@antfubot antfubot commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Intent

Adds @devframes/plugin-data-inspector — an interactive jora query workbench over live server-side objects — together with the two core primitives its design required. Grew out of the in-PR spike (kept in history; the prototype example is replaced by the real plugin).

Devframe core

  • ctx.services — typed, namespaced cross-plugin service registry (provide / get / whenAvailable, augmentable DevframeServicesRegistry, DF0037 on duplicate providers). whenAvailable absorbs setup-order differences, giving every plugin pair a dependency-free communication channel.
  • Storage scopesgetStorageDir now takes 'workspace' | 'project' | 'global': workspace is the committable .devframe/ dir (team-shared), project the per-checkout node_modules dir (previous 'workspace' semantics), global per-user. All hosts and call sites migrated.

The plugin

  • Sources: process-global registry (globalThis Symbol store) — registerDataSource({ id, title, data: value | (async) factory, static?, queries?, icon? }) works with no context from anywhere (plugins, hosts, CLIs, agents); also exposed as a typed context service; registry changes push to connected UIs.
  • Workbench: Vue + @antfu/design; discovery CodeMirror jora editor with remote stat-mode autocomplete; struct viewer with type badges and clean meta-free rendering; data-shape panel; filter options; per-source drafts; shareable URL state; saved queries in workspace/project scopes.
  • Standalone CLI: devframe-data-inspector stats.json trace.jsonl (one static source per file), attach (connect the UI to another process running the in-process agent — loopback + pre-shared-token auth by default, discovery-file handshake), build (self-contained static export; the isomorphic engine runs queries client-side, so saved recipes stay portable).
  • Agent: @devframes/plugin-data-inspector/agent — explicit import or DEVFRAME_DATA_INSPECTOR=1 node --import … for zero-code-change attach. Documented as eval-grade access; treat like a debugger port.

Verification

716/716 tests (new units for registry, engine incl. static-portability, saved queries, file loaders; core services tests), API snapshots updated, lint/typecheck/build green, CLI file-mode smoke-tested in a browser end to end. The minimal Vite hub example dogfoods the plugin with the live ViteDevServer registered as a source.


Created with the help of an agent.

…very viewer)

Validates the live-query data-viewer architecture end to end:
server-side jora over live registered objects (incl. the ViteDevServer
serving the page), result normalizer, devframe RPC round-trip, remote
stat-mode autocomplete, and a themed discovery struct view in
devframe-style chrome. Stage-1 node spike passes 13/13 checks; findings
and hazards documented in the prototype README.

Throwaway code — lives on this spike branch as a primary source, not
for merging into main.
@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit eb3f089
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a59cf9fb4a937000814bf10
😎 Deploy Preview https://deploy-preview-101--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

… source contract, saved queries

Renames the spike to data-inspector and reworks it along the agreed
direction:

- data sources follow { id, title, description?, getData(), static? }
  with dynamic register/unregister + change listeners; static sources
  memoize getData()
- saved queries (title/description, id-keyed) persisted per scope:
  user -> node_modules/.data-inspector, project -> .devframe/data-inspector
  (committable, shared with the team)
- SPA rebuilt as Vue 3 + @antfu/design on the canonical uno preset:
  split-pane workbench, debounced auto-run, client-side jora syntax gate,
  non-destructive errors (last good result stays), stats bar with query
  time + payload size, remote suggestions, synced dark mode

Full e2e verified in-browser; spike still 13/13. Findings captured in
the prototype README.
@antfubot antfubot changed the title chore: spike data-viewer prototype (jora live-query + discovery viewer) chore: spike data-inspector prototype (jora live-query + discovery viewer) Jul 16, 2026
antfubot and others added 9 commits July 16, 2026 05:30
…ush padding

- empty query now runs $ so every source lands on its entire object
- query settings (ignore functions / _-prefixed / $-prefixed keys),
  persisted client-side and applied to both results and the skeleton
- 'available data' panel: query-independent type skeleton of the active
  source (ancestor-path cycle detection keeps shared refs expanded)
- manual re-run button in the stats bar
- left column sections (editor, skeleton, saved queries) resize via the
  same @antfu/design LayoutSplitPane setup as the outer split
- discovery page padding zeroed via --discovery-page-padding-*
…badges, URL state

- FilterOptions { excludeFunctions, excludeUnderscoreProps, excludeDollarProps }
  replace the old settings names; saved queries become source-agnostic
  recipes { query, title?, description?, ...FilterOptions } with title
  optional (untitled ids derive from a stable query hash)
- data sources can provide suggested queries ({ queries?: Query[] }),
  listed read-only next to saved ones; loading any recipe applies its
  filter options
- save query now opens an OverlayModal dialog (query preview, active
  filters, title/description, scope)
- discovery struct annotations badge exotic values: Function, Map(n),
  Set(n), Date, class names, circular refs
- source, query, and filters persist in the URL (shareable/restorable)
…panel, modal filters

- query drafts persist in localStorage per data source; switching sources
  restores (or resets) the draft, and a query arriving via the URL seeds
  the draft for its source so shared links win
- SkeletonPanel replaced by DataSourceInfoPanel: source title/description
  plus a one-level data shape in a plain code block (no discovery);
  clicking a prop sets it as the query and runs
- the save-query modal always shows all three filter options with their
  on/off state
- display transform strips $class/$type meta before rendering: class
  instances show only real props, function stubs collapse to {}, Map/Set
  wrappers unwrap to their entries/values, Date/RegExp/BigInt/... render
  as bare values; badges carry the type info instead, via WeakMap
  side-tables (object identity for objects, parent+key for primitives)
- discovery's built-in query actions are enabled: the struct value-actions
  popup now offers 'Create a subquery from the path' (pipes the path onto
  the query) and 'Append path to current query' (textual append), both
  emitting up to set the editor and re-run
…nified query list

- executions (auto-run included) no longer clear the suggestion dropdown;
  it closes on accept, Escape, blur, or source switch
- the popover gets an explicit solid surface (white / #1e1e1e) with
  stronger elevation so nothing reads through it in either scheme
- suggested and saved queries render through one unified list (resolves
  the TODO), with per-entry icon, filter markers, scope/suggested badge,
  and delete only for saved entries
Promotes the data-inspector spike to a real plugin and lands the two core
primitives it design-required:

devframe core:
- ctx.services — typed, namespaced cross-plugin service registry
  (provide/get/whenAvailable, augmentable DevframeServicesRegistry,
  DF0037 on duplicate providers); whenAvailable absorbs setup-order
  differences between provider and consumer
- getStorageDir scopes become workspace/project/global: workspace is the
  committable .devframe/ dir (team-shared), project is the per-checkout
  node_modules dir (the old 'workspace'), global stays per-user; all
  hosts and call sites migrated (settings and dock prefs now persist
  under 'project')

@devframes/plugin-data-inspector:
- process-global source registry (globalThis Symbol store): sources are
  { id, title, description?, icon?, data: value | (async) factory,
  static?, queries? }; registerDataSource() needs no context, the same
  store is provided as the 'devframes:plugin:data-inspector:sources'
  context service, and changes broadcast to connected UIs
- isomorphic engine (normalize/skeleton/jora bridges) — the Map/Set
  methods duck-type live collections AND their normalized tags, so
  saved queries stay portable between live and static modes
- Vue + @antfu/design SPA with discovery's CodeMirror jora editor
  (remote stat-mode suggestions), struct viewer with type badges,
  shape panel, filters, per-source drafts, URL state, saved queries
  (workspace/project scopes via the new storage classes)
- standalone CLI: inspect .json/.jsonl files, attach to a process
  running the in-process agent (loopback + pre-shared-token auth by
  default, discovery file handshake), and build self-contained static
  exports that run the engine client-side
- unit tests for registry/engine/saved-queries/file loaders; core
  services tests; API snapshots updated

The prototype example is replaced by the plugin; the minimal Vite hub
dogfoods it with the live ViteDevServer registered as a source.
@antfubot antfubot changed the title chore: spike data-inspector prototype (jora live-query + discovery viewer) feat: data-inspector plugin (jora live-query workbench), ctx.services, storage scopes Jul 16, 2026
antfubot added 5 commits July 17, 2026 02:03
The example hub builds against the plugin's dist; without an explicit
task entry and dependsOn edge, CI built the example before the plugin
existed and rolldown failed to resolve the package.
A combined dts graph let rolldown hoist entry declarations into shared
chunks nondeterministically across runs, flaking the tsnapi snapshots in
CI (some matrix cells emitted inline declarations, others re-exports).
One dts graph per entry has nothing to split: declarations always
inline, verified byte-identical across rebuilds. Snapshots regenerated
against the inline form.
- built-in example source: a small live playground graph (Maps, Sets,
  class instances, circulars, BigInt, a query-time getter) with suggested
  queries; on by default, disabled via exampleSource: false, suppressed
  automatically by the agent and by the CLI when data files are given
- docs/plugins/data-inspector.md: using the plugin, providing data
  sources (registry + context service + contract), CLI, agent attach,
  static export, RPC surface; registered in the plugins sidebar/overview
- the SPA header links to the docs page, and an empty-source state
  points at the providing-data-sources section
…t only

The built-in example source is now always registered unless explicitly
opted out (option exampleSource: false; CLI --no-example; agent env
DEVFRAME_DATA_INSPECTOR_EXAMPLE=0) — the agent and file-serving CLI no
longer suppress it automatically. Beyond the playground branch it now
answers real questions: the devframe context (registered RPC functions,
services, storage dirs), OS info, and live process stats via query-time
getters, with matching suggested queries.
Two context-level primitives for plugin interop:

- ctx.services — a typed, namespaced cross-plugin service registry
  (provide/get/whenAvailable, augmentable DevframeServicesRegistry,
  DF0037 on duplicate providers). Providers ship a types-only
  augmentation so consumers get full typing without a runtime
  dependency, and whenAvailable absorbs setup-order differences.

- getStorageDir scopes become workspace/project/global: 'workspace' is
  the committable <workspaceRoot>/.devframe/ dir (team-shared, new),
  'project' is the per-checkout node_modules dir (the previous
  'workspace' semantics), 'global' stays per-user. All hosts and call
  sites migrated: scoped project settings and the hub's dock/user
  settings persist under 'project'.

Docs: a Cross-Plugin Services guide page, storage-scope reference on
the definition page, the hub host example implements all three scopes,
and a DF0037 error page. API snapshots updated.
antfubot and others added 6 commits July 17, 2026 03:56
…select, polished save/list UI

- pnpm dev now runs a full devframe dev server: the SPA vite config
  mounts dataInspectorVitePlugin({ devMiddleware: true }), so the HMR
  frontend has a live RPC + WS backend (with the example source) instead
  of a backend-less SPA
- rename the /agent subpath export to /inject (dir, exports, tsdown,
  alias, imports, docs); the API is unchanged
- new DataSourceSelect component replaces the generic FormSelect for the
  source picker: renders each source's icon (fallback i-ph:database-duotone)
  and a one-line description in the dropdown
- Save-query dialog uses ActionToggleGroup for the save location
  (Just me / Shared with team) with a contextual hint
- SavedQueriesPanel renders filter markers as pretty pill badges and drops
  the per-row suggested/scope badge
- example source gains a filter-carrying suggested query to exercise it
DataSourceSelect used inline <svg> for the trigger caret and the item
check indicator (copied from FormSelect). Replace both with i-ph:caret-down
and i-ph:check spans rendered through the shared presetIcons mask, matching
the design-system convention of icons everywhere.
App.vue was a 231-line mix of header, data-source, and query UI. Split
the left column into focused components and slim the root to a pure
composition surface (231 -> 86 lines):

- AppHeader.vue — brand, connection status, docs + dark-mode buttons
  (reads the connection/scheme singletons directly, no props)
- DataSourcePanel.vue — source select, details toggle, data-shape panel,
  empty state
- QueryPanel.vue — Jora query header (clear/run), editor, filter settings

The workbench is shared app context, so panels inject it (workbenchKey /
injectWorkbench) rather than receiving it as a prop and mutating it
(which vue/no-mutating-props rightly forbids). Query-composition helpers
(queryProp / applySubquery / appendPath) moved onto the workbench so the
panels call them directly instead of App relaying handlers.
@antfu
antfu merged commit bd2d10f into main Jul 17, 2026
11 of 12 checks passed
@antfu
antfu deleted the free-points-return branch July 17, 2026 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants