A web-based database inspector and generator for Hyper .hyper files. Browse schemas, preview data with server-side sorting and infinite scroll, view column statistics with distribution graphs and Fourier analysis, run ad-hoc SQL, review query history (user and internal Hyper queries) with optional Hyper query statistics from hyperd logs, and generate new databases with configurable data distributions.
The UI is a React SPA (Vite + Tailwind). The API is Express + TypeScript, backed by hyperdb-api-node with a small per-database connection pool so concurrent browser requests do not block each other.
- Node.js >= 21
- Rust toolchain (stable) — required to build
hyperdb-api-nodefrom the workspace when usingfile:../../ hyperdb-api-nodebuilt — from thehyperdb-api-node/directory:- Release:
npm run build(faster runtime) - Debug:
npm run build:debug(faster compile while iterating on Rust)
- Release:
hyperd— set theHYPERD_PATHenvironment variable to thehyperdexecutable (same as the rest of this repo)
cd hyperdb-api-node/examples/hyper-explorer
npm install
HYPERD_PATH=/path/to/hyperd npm run devWindows (PowerShell):
cd hyperdb-api-node\examples\hyper-explorer
npm install
$env:HYPERD_PATH = "C:\path\to\hyperd.exe"
npm run devOpen http://localhost:5173 in your browser.
The dev script runs two processes (via concurrently):
| Process | Port | Role |
|---|---|---|
| Vite | 5173 | React app + HMR; proxies /api/* to Express |
| Express | 3000 | REST API, HyperProcess, connection pool |
Override the API port with PORT (default 3000). The Vite proxy in vite.config.ts targets http://localhost:3000 — if you change PORT, update the proxy target to match.
| Script | Description |
|---|---|
npm run dev |
Express (tsx watch) + Vite together (recommended) |
npm run dev:server |
API only on PORT / 3000 |
npm run dev:client |
Vite only on 5173 (expects API already running) |
npm run build |
Production build of the React app → dist/ |
npm start |
Run server/index.ts with tsx (API only; does not serve dist/) |
For a static deployment, run npm run build, host dist/ behind any static file server, and point /api at the Express process (same proxy idea as Vite).
%%{init: {'theme': 'base', 'themeVariables': { 'lineColor': '#93c5fd', 'primaryColor': '#1e293b', 'primaryTextColor': '#e2e8f0', 'primaryBorderColor': '#475569', 'secondaryColor': '#172554', 'tertiaryColor': '#1e293b', 'edgeLabelBackground': '#0f172a', 'clusterBkg': '#0f172a', 'clusterBorder': '#475569' }}}%%
graph TB
subgraph Browser["Browser (localhost:5173)"]
React["React SPA<br/>Vite + TailwindCSS"]
FC["Fourier client<br/>(client-side DFT)"]
React --> FC
end
subgraph Vite["Vite dev server :5173"]
HMR["HMR + static assets"]
Proxy["/api/* proxy"]
end
subgraph Express["Express API :3000"]
Routes["Route handlers"]
Pool["ConnectionPool<br/>(per-db, max 5 idle)"]
Profiler["profiler.ts<br/>(stats / histogram SQL)"]
Generator["generator.ts<br/>(CREATE / INSERT SQL)"]
FFT["fft.ts<br/>(histogram FFT magnitudes)"]
Routes --> Pool
Routes --> Profiler
Routes --> Generator
Routes --> FFT
end
subgraph HyperProcess["HyperProcess (hyperd)"]
Engine["Hyper SQL engine"]
DB1[("database.hyper")]
DB2[("generated.hyper")]
Log["hyperd.log (optional)<br/>query stats"]
Engine --> DB1
Engine --> DB2
Engine -.-> Log
end
React -->|"fetch /api/*"| Vite
Vite -->|"HMR"| React
Proxy -->|"proxy"| Express
Pool -->|"hyperdb-api-node<br/>Connection, Catalog"| HyperProcess
style Browser fill:#1e293b,stroke:#475569,color:#e2e8f0
style Express fill:#1e293b,stroke:#475569,color:#e2e8f0
style HyperProcess fill:#172554,stroke:#60a5fa,color:#93c5fd
style Vite fill:#1e293b,stroke:#475569,color:#e2e8f0
- Single
HyperProcessis created when the server starts (server/index.ts). All routes share it. ConnectionPool(server/routes.ts) keeps up to five idle connections per database path, reuses them across requests, and assigns each connection a stable numeric ID (shown in Query History as Conn).- When
HyperProcessexposes alogPath, new pooled connections callenableQueryStatswith that path so Hyper can attach engine metrics to queries. Those metrics are returned on tracked queries and surfaced in the UI viaQueryStatsTooltip(hover the duration in Query History or after running SQL in the editor). - Responses that execute SQL may include
_queries: an array of internal{ sql, rowCount, durationMs, source, connectionId, queryStats? }. The frontendapi.tsforwards these tosetOnInternalQueries, which fills Query History alongside user runs.
If hyperd.log is unavailable, stats collection is skipped gracefully; the app still works with wall-clock durationMs only.
%%{init: {'theme': 'base', 'themeVariables': { 'lineColor': '#93c5fd', 'signalColor': '#93c5fd', 'actorLineColor': '#93c5fd', 'actorBorder': '#60a5fa', 'actorBkg': '#1e293b', 'actorTextColor': '#e2e8f0', 'loopTextColor': '#cbd5e1', 'noteBkgColor': '#1e3a5f', 'noteTextColor': '#e2e8f0', 'noteBorderColor': '#60a5fa', 'activationBkgColor': '#334155', 'activationBorderColor': '#60a5fa', 'sequenceNumberColor': '#93c5fd' }}}%%
sequenceDiagram
participant U as User
participant R as React app
participant E as Express API
participant P as ConnectionPool
participant H as HyperProcess
U->>R: Enter path / browse / drag-drop
R->>E: POST /api/open {path}
E->>P: acquire(path)
P->>H: Connection.connect(...)
P-->>E: connection + connectionId
E->>H: Catalog.getSchemaNames()
H-->>E: ["public", ...]
loop Each schema
E->>H: Catalog.getTableNames(schema)
H-->>E: ["table1", "table2", ...]
loop Each table
E->>H: querySchema("SELECT * FROM schema.table LIMIT 0")
H-->>E: [{name, typeName}, ...]
end
end
E->>P: release(path, conn)
E-->>R: {database, schemas, _queries}
R->>R: Render SchemaTree sidebar
R-->>U: Schema tree displayed
%%{init: {'theme': 'base', 'themeVariables': { 'lineColor': '#93c5fd', 'signalColor': '#93c5fd', 'actorLineColor': '#93c5fd', 'actorBorder': '#60a5fa', 'actorBkg': '#1e293b', 'actorTextColor': '#e2e8f0', 'loopTextColor': '#cbd5e1', 'noteBkgColor': '#1e3a5f', 'noteTextColor': '#e2e8f0', 'noteBorderColor': '#60a5fa', 'activationBkgColor': '#334155', 'activationBorderColor': '#60a5fa', 'sequenceNumberColor': '#93c5fd' }}}%%
sequenceDiagram
participant U as User
participant R as React app
participant FC as Fourier client
participant E as Express API
participant H as HyperProcess
U->>R: Click column in sidebar
R->>E: GET /api/column-detail/... ?db=...
E->>H: querySchema (column type)
E->>H: executeQuery (detail stats: min, max, mean, median, percentiles...)
E->>H: executeQuery (200-bucket histogram via WIDTH_BUCKET)
E->>E: FFT magnitude of histogram counts (Cooley-Tukey)
E->>H: executeQuery (top 10 values)
E-->>R: {detail: {stats, histogram, fft, topValues}, _queries}
R->>FC: computeFourierCoefficients(histogram counts)
FC-->>R: {a0, allTerms[]} (harmonics precomputed)
R->>FC: findMinTermsForFit(coefs, signal, 0.99)
FC-->>R: optimalTerms (e.g. 7)
R->>R: Set slider to optimalTerms
R->>FC: reconstructWithTerms(coefs, N, numTerms)
FC-->>R: {reconstructed[], terms[]}
R->>FC: computeR²(original, reconstructed)
FC-->>R: 0.9923
R-->>U: Distribution chart + Fourier overlay + equation + FFT spectrum
U->>R: Drag slider to 15 terms
R->>FC: reconstructWithTerms(coefs, N, 15)
FC-->>R: Updated reconstruction (no server call)
R-->>U: Charts + equation update live
%%{init: {'theme': 'base', 'themeVariables': { 'lineColor': '#93c5fd', 'signalColor': '#93c5fd', 'actorLineColor': '#93c5fd', 'actorBorder': '#60a5fa', 'actorBkg': '#1e293b', 'actorTextColor': '#e2e8f0', 'loopTextColor': '#cbd5e1', 'noteBkgColor': '#1e3a5f', 'noteTextColor': '#e2e8f0', 'noteBorderColor': '#60a5fa', 'activationBkgColor': '#334155', 'activationBorderColor': '#60a5fa', 'sequenceNumberColor': '#93c5fd' }}}%%
sequenceDiagram
participant U as User
participant R as React app
participant E as Express API
participant H as HyperProcess
U->>R: Click "Generate DB", configure tables/columns
R->>R: Save spec to localStorage
U->>R: Click "Generate Database"
R->>E: POST /api/generate {dbPath, tables: [...]}
loop Each table
E->>E: buildCreateTableSql(tableSpec)
E->>H: executeCommand(CREATE TABLE ...)
E->>E: buildInsertSql(tableSpec)
E->>H: executeCommand(INSERT INTO...SELECT FROM generate_series)
Note over H: Hyper generates rows using random() and math functions
E->>H: executeQuery(SELECT COUNT(*))
end
E-->>R: {dbPath, results: [{table, rowCount, durationMs}], _queries}
R-->>U: Success + "Open in Explorer"
U->>R: Open generated file
R->>E: POST /api/open {path}
- Generate DB — full-screen generator (toggles with Back to Explorer).
- Query History — full-screen log; button shows
(N)when entries exist. - Query Editor — full-screen SQL workspace; disabled until a database is open (unlike the per-table SQL Editor tab).
- Current path — truncated path shown in the header when a DB is open (explorer mode).
- Schema tree — collapsible schemas → tables → columns (types and nullable metadata from
LIMIT 0schema queries). - Tabs — Preview, Column Stats, dynamic tab named after the selected column (column detail), SQL Editor (for the selected table).
- Data preview — infinite-scroll grid (200-row pages), read-ahead near 50% scroll, server-side sorting (click header: asc → desc → clear); issues
ORDER BYon Hyper. - Column stats — per-column cards (table-level view).
- Column detail — sidebar column click; deep analysis for numerics (histogram, Fourier, FFT spectrum, top values, box plot), text, boolean, dates.
- SQL editor (tab) — same editor component as full-screen mode: ⌘/Ctrl+Enter to run, auto SELECT vs command, timing with optional query stats tooltip, results grid or command summary, schema-aware example query chips when the editor is empty.
- Query history — chronological table: source badge (user vs internal
sourcestring), Conn#id, truncated SQL (click → modal with sql-formatter PostgreSQL layout + syntax highlighting + Copy formatted), wall-clock duration (hover → Hyper metrics when available), timestamp. - File browser — directories and
.hyperfiles; size and last modified; remembers last directory in localStorage; modal with pinned..parent row. - Drag and drop — drop
.hyperfiles or plain-text paths onto the page; full-screen overlay while dragging.
| Data type | Statistics |
|---|---|
| All types | Row count, null count/%, distinct count, cardinality % |
| Numeric (INT, DOUBLE, …) | Min, max, mean, median, std dev, CV%, variance, sum, p10/p25/p75/p90 |
| Text | Min/max/avg length, top values |
| Boolean | True/false counts and percentages |
| Date / timestamp | Min, max |
- Value distribution — smooth area chart (200 buckets, monotone cubic spline).
- Fourier series overlay — reconstructed curve over the histogram.
- Interactive slider — number of Fourier terms (1 … max harmonics).
- Auto fit — slider initializes to the smallest term count with R² ≥ 99%.
- Live R² — color-coded fit quality.
- Fourier equation — expanded series with coefficients.
- Coefficient table — n, aₙ, bₙ, amplitude + mini bars.
- Percentile box plot — min / p10 / p25 / median / p75 / p90 / max.
- FFT spectrum — frequency magnitudes from server-side FFT of bucket counts.
- Top values — horizontal bar chart with percentages.
- Configurable tables — names, row counts, add/remove.
- Column editor — SQL type, distribution, nullable %, parameters.
- Eight SQL types — INT, BIGINT, SMALLINT, DOUBLE PRECISION, TEXT, BOOLEAN, DATE, TIMESTAMP.
- Eleven distributions — sequential, uniform, normal, lognormal, exponential, bimodal, categorical (Zipf), uuid, words, bernoulli, uniform_range.
- Inline parameters — mean, stddev, min, max, lambda, categories, etc.
- localStorage — generator spec persistence.
- Fast inserts —
INSERT … SELECT … FROM generate_series()with Hyperrandom()(on the order of millions of rows in a few seconds on typical hardware).
hyper-explorer/
├── server/ # Express backend (TypeScript, tsx)
│ ├── index.ts # Express, CORS, JSON, HyperProcess, pool, PORT, shutdown
│ ├── routes.ts # REST handlers, ConnectionPool, tracked connections, _queries
│ ├── profiler.ts # SQL for column stats, histograms, detail
│ ├── generator.ts # CREATE/INSERT generation from spec
│ └── fft.ts # Radix-2 Cooley-Tukey FFT (histogram spectrum)
│
├── src/ # React frontend
│ ├── main.tsx
│ ├── App.tsx # Layout, tabs, pool-driven loading, global drag-drop
│ ├── api.ts # fetch helpers, _queries fan-out, types (incl. QueryStats)
│ ├── fourierClient.ts # Client DFT, reconstruction, R², term selection
│ └── components/
│ ├── FileOpener.tsx
│ ├── SchemaTree.tsx
│ ├── DataGrid.tsx
│ ├── ColumnStats.tsx
│ ├── ColumnDetailView.tsx
│ ├── SqlEditor.tsx
│ ├── QueryHistory.tsx
│ ├── QueryStatsTooltip.tsx
│ └── GenerateDatabase.tsx
│
├── package.json # hyperdb-api-node: file:../../
├── vite.config.ts # dev server + /api → :3000
├── tsconfig.json
├── tailwind.config.js
├── postcss.config.js
└── index.html
All data routes (except browse/open/close/generate-meta) require the db query parameter or body field set to the absolute path of the open .hyper file.
| Method | Path | Description |
|---|---|---|
GET |
/api/browse?dir= |
List directories and .hyper files (optional dir, default home); items include size, lastModified |
POST |
/api/open |
Body { path } — schema tree; may include _queries |
POST |
/api/close |
Body { path } — close pooled connections for that path |
GET |
/api/preview/:schema/:table?db=&limit=&offset=&sortColumn=&sortDir= |
Paginated rows + totalRowCount; limit capped at 1000; optional sortColumn / sortDir (asc/desc); _queries |
GET |
/api/stats/:schema/:table?db= |
Per-column statistics; _queries |
GET |
/api/column-detail/:schema/:table/:column?db= |
Detail payload + _queries |
POST |
/api/query |
Body { db, sql } — SELECT → rows + columns; otherwise command; returns durationMs, optional queryStats, _queries |
GET |
/api/generate-meta |
Supported types and distributions |
POST |
/api/generate |
Body { dbPath, tables: [...] } — create DB; _queries |
- Backend — Express 4, TypeScript,
tsx(watch in dev),hyperdb-api-node(N-API bindings to the Rust Hyper client). - Frontend — React 18, Vite 7, TailwindCSS 3,
sql-formatter(PostgreSQL dialect). - Math — Radix-2 FFT on the server for spectrum data; client-side DFT / Fourier reconstruction / monotone cubic splines (Fritsch–Carlson).
- Concurrency —
ConnectionPool+ tracked wrappers attachqueryStatswhen the log path is known.
The example is self-contained aside from:
hyperdb-api-node—file:../../inpackage.json(swap for a published package if you ship it).hyperd— pointed to byHYPERD_PATH.
npm run buildmay print a Vite warning about large JS chunks; it is informational and does not fail the build.lucide-reactis listed as a dev dependency for optional icon work; the current UI uses inline SVG where needed.