Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/FEATURE_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ an interactive page (open it in a browser), and
[the test and fix plan](plans/2026-10-25-feature-and-dialect-test-plan.md) sets
the order every feature and dialect is tested in.

Measured on `main` at `100fb679` (2026-10-26): 17 server feature modules,
155 HTTP routes (pinned by `packages/server/src/api/http-contract.test.ts`),
14 frontend features, 16 database engines, 28 metadata tables (31 migrations),
56 e2e specs, ~600 unit-test files. Re-measure with the commands in
Measured on `main` at `b12bcd5607` (2026-10-31): 34 features, 18 server
feature modules, 158 HTTP routes (pinned by
`packages/server/src/api/http-contract.test.ts`), 14 frontend features,
16 database engines, 28 metadata tables (31 migrations), 57 e2e specs in 35
groups, ~550 unit-test files. Re-measure with the commands in
[Keeping this page true](#keeping-this-page-true).

- [System](#system)
Expand All @@ -37,7 +38,7 @@ flowchart LR
end
subgraph server["Fox server — packages/server (Fastify)"]
PLAT["platform<br/>identity · RBAC · workspaces<br/>connections · guards"]
FEAT["17 feature modules<br/>153 routes"]
FEAT["18 feature modules<br/>158 routes"]
end
WFS["Workflow server :8081<br/>apps/workflow-server<br/>+ packages/workflow-engine"]
META[("Metadata store<br/>SQLite · 28 tables")]
Expand Down Expand Up @@ -625,7 +626,9 @@ tables in the schema diagram.

The interactive page, [feature-map.html](feature-map.html), is generated. Edit
its inventory in `docs/feature-map/data.js` (its layout in `template.html`, its
script in `app.js`; the diagrams come from this page), then rebuild it:
script in `app.js`; the diagrams come from this page), then rebuild it. Never
edit the page itself: `feature-map-build.test.ts` fails while it differs from
the build.

```bash
npm run docs:feature-map
Expand Down
52 changes: 30 additions & 22 deletions docs/feature-map.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,17 +238,17 @@
<div class="wrap">
<div class="titlerow">
<h1>Fox <span>Feature Map</span></h1>
<div class="stamp">Measured on <code>main</code> · <code>100fb679</code> · 2026-10-26 · mirrors <code>docs/FEATURE_MAP.md</code></div>
<div class="stamp">Measured on <code>main</code> · <code>b12bcd5607</code> · 2026-10-31 · mirrors <code>docs/FEATURE_MAP.md</code></div>
</div>
<ul class="stats" aria-label="Inventory">
<li><b>33</b> features</li>
<li><b>155</b> HTTP routes</li>
<li><b>17</b> server modules</li>
<li><b>34</b> features</li>
<li><b>158</b> HTTP routes</li>
<li><b>18</b> server modules</li>
<li><b>14</b> web features</li>
<li><b>16</b> engines</li>
<li><b>28</b> metadata tables</li>
<li><b>56</b> e2e specs</li>
<li><b>~600</b> unit-test files</li>
<li><b>57</b> e2e specs</li>
<li><b>~550</b> unit-test files</li>
</ul>
<nav class="tabs" role="tablist" aria-label="Views">
<button role="tab" id="tab-plan" data-tab="plan" aria-selected="true">Plan</button>
Expand Down Expand Up @@ -307,7 +307,7 @@ <h2>System</h2>
end
subgraph server["Fox server — packages/server (Fastify)"]
PLAT["platform&lt;br/&gt;identity · RBAC · workspaces&lt;br/&gt;connections · guards"]
FEAT["17 feature modules&lt;br/&gt;153 routes"]
FEAT["18 feature modules&lt;br/&gt;158 routes"]
end
WFS["Workflow server :8081&lt;br/&gt;apps/workflow-server&lt;br/&gt;+ packages/workflow-engine"]
META[("Metadata store&lt;br/&gt;SQLite · 28 tables")]
Expand All @@ -322,7 +322,8 @@ <h2>System</h2>
WFS -- "internal: resolve a granted connection" --&gt; FEAT
WFS --&gt; ENG</pre></div>
<div class="diagram"><pre class="mermaid">flowchart LR
R[Request] --&gt; O["Origin policy&lt;br/&gt;+ rate limit"] --&gt; S["Session guard&lt;br/&gt;(auth.guard)"] --&gt; W["Workspace scope&lt;br/&gt;(resolveWorkspace)"]
R[Request] --&gt; O["Origin policy&lt;br/&gt;+ rate limit"] --&gt; S["Session guard&lt;br/&gt;(auth.guard)"] --&gt; RO["app.write or GET / reads"]
RO --&gt; W["Workspace scope&lt;br/&gt;(resolveWorkspace)"]
W --&gt; P["Permission guard&lt;br/&gt;requirePermissions()"] --&gt; H["Feature route&lt;br/&gt;→ service"]
H --&gt; C["Connection resolver&lt;br/&gt;(decrypts the saved connection)"] --&gt; D["Driver&lt;br/&gt;packages/db"]
H --&gt; M[("Metadata store")]</pre><p>Every request passes origin, session, workspace and permission checks before a feature runs.</p></div>
Expand Down Expand Up @@ -383,13 +384,15 @@ <h2>Workflows</h2>
<div class="two">
<div class="diagram"><h3>Data compare → data migrate</h3><pre class="mermaid">flowchart LR
A[Same SELECT on ≥2 destinations] --&gt; B[Side by side + Compare data]
B --&gt; C[align rows by key columns]
C --&gt; D[add / edit / delete per target&lt;br/&gt;Sync column picks rows]
B --&gt; W["Compare 2,000 rows&lt;br/&gt;both sides in key order"]
B --&gt; C[or align the pages already loaded]
W --&gt; D[add / edit / delete per target&lt;br/&gt;Sync column picks rows]
C --&gt; D
D --&gt; E[Preview SQL]
E --&gt; F{Safe mode}
F -- confirm --&gt; G[POST /api/data-migrations/start]
G --&gt; H[backup snapshot of affected rows]
H --&gt; I[POST /api/data-migrate/execute&lt;br/&gt;Transaction only where it can roll back&lt;br/&gt;stop or continue on error]
H --&gt; I["POST /api/data-migrate/execute&lt;br/&gt;≤ 2,000 ops, one shared cap&lt;br/&gt;Transaction only where it can roll back"]
I --&gt; J[POST /api/data-migrations/:id/finish&lt;br/&gt;data_migrate_runs]
J --&gt; K[History: restore from snapshot]</pre></div>
<div class="diagram"><h3>Snapshots — capture, compare, revert</h3><pre class="mermaid">flowchart LR
Expand Down Expand Up @@ -650,7 +653,7 @@ <h2>Coverage</h2>
</div>
<div class="section">
<h3>End-to-end suites</h3>
<p class="lede">56 Playwright specs in 37 groups, run with <code>npm run test:e2e:all</code>. Dialect suites need their Docker container and a fresh seed.</p>
<p class="lede">57 Playwright specs in 35 groups, run with <code>npm run test:e2e:all</code>. Dialect suites need their Docker container and a fresh seed.</p>
<div class="areas" id="e2e-groups"></div>
</div>
</section>
Expand All @@ -663,7 +666,7 @@ <h3>End-to-end suites</h3>

<script>
// Reference data: the inventory of what Fox Schema ships, measured from the
// codebase on main 100fb679 (2026-10-26). Mirrors docs/FEATURE_MAP.md.
// codebase on main b12bcd5607 (2026-10-31). Mirrors docs/FEATURE_MAP.md.
// Work items are not here: they live in the page's shared store.
const AREAS = [
{ id: 'platform', name: 'Platform' },
Expand Down Expand Up @@ -744,10 +747,10 @@ <h3>End-to-end suites</h3>
code: 'web ResultsPanel, DataGrid', perms: 'editor.datagrid.insert, update, delete',
tables: '—', tests: 'e2e sql-editor-result-edit, -copy-export' },
{ id: 'editor.datacompare', area: 'editor', name: 'Data compare & migrate',
subs: ['Compare toggle and Source', 'Compare 2,000 rows in key order', 'In-memory filters and pages', 'Diff cells', 'Key columns', 'Add, edit, delete', 'Sync column', 'Apply up to 2,000 ops (one shared cap)', 'Transaction where it can roll back, stop or continue, backup snapshot', 'History', 'Preview SQL', 'Multi-target'],
subs: ['Compare toggle and Source', 'Diff cells', 'Key columns', 'Add, edit, delete', 'Sync column', 'Apply: Transaction where it can roll back, stop or continue, backup snapshot', 'History', 'Preview SQL', 'Multi-target'],
ui: 'Editor, side by side', api: '/api/data-migrate/execute, /api/data-migrations (5)',
code: 'server data-migrate · web DataMigrateBar, useCompareWindow, compareView.ts', perms: 'editor.dml',
tables: 'data_migrate_runs', tests: 'server 4 · e2e sql-editor-compare-multi-target, sql-editor-compare-window' },
code: 'server data-migrate · web DataMigrateBar', perms: 'editor.dml',
tables: 'data_migrate_runs', tests: 'server 4 · e2e sql-editor-compare-multi-target' },
{ id: 'editor.peek', area: 'editor', name: 'Data peek',
subs: ['Cmd/Ctrl-click to open', 'Stacked panels', 'WHERE (one row per AND), ORDER BY, limit', 'Paging', 'Row editor: add, edit, clone, delete, generators', 'Insight tab', 'FK drill and referenced-by', 'Date picker'],
ui: 'Editor', api: 'with /api/sql/execute',
Expand Down Expand Up @@ -791,6 +794,11 @@ <h3>End-to-end suites</h3>
subs: ['Database Access modal'],
ui: 'Utils', api: '/api/schema/db-access', code: 'server access (db-access)', perms: 'utility.access, editor.grant',
tables: '—', tests: 'e2e database-access-dialects' },
{ id: 'utilities.trace', area: 'utilities', name: 'SQL Tracer',
subs: ['Live: the engine\'s own sessions, about once a second', 'Fox activity: statements run from Fox on a saved connection', 'Secrets masked on the server', 'Stops at 15 min or 10,000 statements, on Stop, or on leaving', 'Open a statement in the editor'],
ui: 'Utils → SQL Tracer', api: '/api/sql-trace (3)',
code: 'server sql-trace, platform/sql-activity · sql utilities/sql-trace · web SqlTracerPanel', perms: 'utility.trace',
tables: '—', tests: 'sql 1 · server 3 · web 1 · e2e utilities-sql-tracer' },

{ id: 'access.accounts', area: 'access', name: 'Database accounts',
subs: ['Create', 'Rename', 'Drop', 'Passwords (per-engine DDL)'],
Expand Down Expand Up @@ -855,22 +863,22 @@ <h3>End-to-end suites</h3>
],
};

// Routes per feature, from the 155 pinned in http-contract.test.ts.
// Routes per feature, from the 158 pinned in http-contract.test.ts.
const ROUTES = [
['platform.auth', 22], ['platform.workspaces', 20], ['platform.admin', 19], ['schema.git', 15],
['platform.auth', 22], ['platform.workspaces', 21], ['platform.admin', 20], ['schema.git', 15],
['schema.snapshots', 11], ['editor.files', 11], ['platform.secrets', 9], ['platform.connections', 8],
['workflow.engine', 7], ['platform.system', 6], ['editor.datacompare', 6], ['schema.applies', 5],
['utilities + access', 5], ['schema.compare', 4], ['editor.run + code', 3], ['utilities.backup', 2],
['utilities + access', 5], ['schema.compare', 4], ['editor.run + code', 3], ['utilities.trace', 3], ['utilities.backup', 2],
];

// Unit-test files per package.
const TESTS = [
['apps/web', 179], ['packages/server', 104], ['packages/workflow-engine', 87], ['packages/sql', 86],
['apps/cli', 14], ['packages/db', 26], ['apps/workflow-server', 19], ['packages/shared', 12],
['apps/web', 180], ['packages/server', 107], ['packages/workflow-engine', 87], ['packages/sql', 87],
['apps/cli', 25], ['packages/db', 26], ['apps/workflow-server', 19], ['packages/shared', 13],
['packages/workflow-contract', 1], ['packages/ui-shared', 1],
];

const E2E_GROUPS = ['PostgreSQL', 'MySQL', 'MariaDB', 'SQL Server', 'Oracle', 'DB2', 'SQLite', 'Azure SQL', 'ClickHouse', 'Redshift', 'CockroachDB', 'YugabyteDB', 'TiDB', 'DuckDB', 'Smoke', 'Sign-in', 'One admin or several', 'Demo role', 'Workspaces', 'SQL Editor (16 specs)', 'Compare 2,000 rows', 'Redis editor', 'Send feedback', 'Browse', 'History', 'Revert', 'Revert edges', 'Force-migrate', 'Access SQLite', 'Workflow', 'Utilities', 'DB Access', 'Access all', 'Migrations in Git', 'Execute gate', 'Backup & Restore', 'Backup commands'];
const E2E_GROUPS = ['PostgreSQL', 'MySQL', 'MariaDB', 'SQL Server', 'Oracle', 'DB2', 'SQLite', 'Azure SQL', 'ClickHouse', 'Redshift', 'CockroachDB', 'YugabyteDB', 'TiDB', 'DuckDB', 'Smoke', 'Sign-in', 'One admin or several', 'Workspaces', 'Demo role', 'SQL Editor (19 specs)', 'Send feedback', 'Browse', 'History', 'Revert', 'Revert edges', 'Force-migrate', 'Access SQLite', 'Workflow', 'Utilities', 'DB Access', 'Access all', 'Migrations in Git', 'Execute gate', 'Backup & Restore', 'Backup commands'];

// Metadata table → owning feature, column count (schema.ts, 31 migrations).
const TABLES = [
Expand Down
23 changes: 14 additions & 9 deletions docs/feature-map/data.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Reference data: the inventory of what Fox Schema ships, measured from the
// codebase on main 91d4165c09 (2026-10-25). Mirrors docs/FEATURE_MAP.md.
// codebase on main b12bcd5607 (2026-10-31). Mirrors docs/FEATURE_MAP.md.
// Work items are not here: they live in the page's shared store.
const AREAS = [
{ id: 'platform', name: 'Platform' },
Expand Down Expand Up @@ -28,7 +28,7 @@ const FEATURES = [
code: 'server workspaces, platform/workspaces · web workspaces', perms: 'workspace.members, workspace.settings, workspace.create',
tables: 'workspaces, workspace_members, workspace_invites, workspace_scripts', tests: 'server 5 · web 3 · e2e workspaces, workspaces-sharing-limits' },
{ id: 'platform.connections', area: 'platform', name: 'Connections & drivers',
subs: ['Credential manager', 'Connection form: password, Windows, LDAP', 'SSL and pool', 'SQLite / DuckDB file picker', 'Schema list', 'Connection test', 'Driver check and Install driver', 'Cloud provider credentials'],
subs: ['Credential manager', 'Connection form: password, Windows, LDAP, No password (Redis / MongoDB)', 'SSL and pool', 'SQLite / DuckDB file picker', 'Schema list', 'Connection test', 'Driver check and Install driver', 'Cloud provider credentials'],
ui: 'Creds (rail), connection modal', api: '/api/connections (4), /api/connection/test, /api/db/test, /api/driver (2)',
code: 'server connections, platform/connections, internal/driver-install · web connections', perms: 'workspace role; admin.drivers to install',
tables: 'connections, cloud_provider_credentials', tests: 'server 1 · web 1' },
Expand Down Expand Up @@ -62,7 +62,7 @@ const FEATURES = [
subs: ['Capture', 'Databases', 'Version timeline', 'Graph', 'Version compare', 'Change chart', 'Object inspector', 'Revert: plan → run', 'Force-migrate: plan → run', 'Rename a version'],
ui: 'Snapshots (rail)', api: '/api/lokee (11)',
code: 'server history · web lokee-weave · sql lokee-weave', perms: 'compare.history, schema.migrate.force',
tables: 'lokee_databases, lokee_versions, lokee_version_objects, lokee_objects, lokee_latest_objects, lokee_shapes', tests: 'sql 15 · server 3 · web 14 · e2e schema-history, schema-revert, schema-version-revert-edges' },
tables: 'lokee_databases, lokee_versions, lokee_version_objects, lokee_objects, lokee_latest_objects, lokee_shapes', tests: 'sql 15 · server 3 · web 14 · e2e schema-history, schema-revert, schema-version-revert-edges, schema-force-migrate' },

{ id: 'editor.run', area: 'editor', name: 'Editor & run',
subs: ['Tabs, stored per workspace', 'Destinations', 'Run rail: In [n], ▶, include, Safe-mode marks, copy with values', 'Selection and caret run', 'Safe mode confirm', 'One transaction', 'Per-dialect write classification', 'Format SQL+JS', 'Clause-aware completions', 'Column picker', 'Shortcuts'],
Expand Down Expand Up @@ -127,6 +127,11 @@ const FEATURES = [
subs: ['Database Access modal'],
ui: 'Utils', api: '/api/schema/db-access', code: 'server access (db-access)', perms: 'utility.access, editor.grant',
tables: '—', tests: 'e2e database-access-dialects' },
{ id: 'utilities.trace', area: 'utilities', name: 'SQL Tracer',
subs: ['Live: the engine\'s own sessions, about once a second', 'Fox activity: statements run from Fox on a saved connection', 'Secrets masked on the server', 'Stops at 15 min or 10,000 statements, on Stop, or on leaving', 'Open a statement in the editor'],
ui: 'Utils → SQL Tracer', api: '/api/sql-trace (3)',
code: 'server sql-trace, platform/sql-activity · sql utilities/sql-trace · web SqlTracerPanel', perms: 'utility.trace',
tables: '—', tests: 'sql 1 · server 3 · web 1 · e2e utilities-sql-tracer' },

{ id: 'access.accounts', area: 'access', name: 'Database accounts',
subs: ['Create', 'Rename', 'Drop', 'Passwords (per-engine DDL)'],
Expand Down Expand Up @@ -191,22 +196,22 @@ const ENGINES = {
],
};

// Routes per feature, from the 153 pinned in http-contract.test.ts.
// Routes per feature, from the 158 pinned in http-contract.test.ts.
const ROUTES = [
['platform.auth', 22], ['platform.workspaces', 20], ['platform.admin', 19], ['schema.git', 15],
['platform.auth', 22], ['platform.workspaces', 21], ['platform.admin', 20], ['schema.git', 15],
['schema.snapshots', 11], ['editor.files', 11], ['platform.secrets', 9], ['platform.connections', 8],
['workflow.engine', 7], ['platform.system', 6], ['editor.datacompare', 6], ['schema.applies', 5],
['utilities + access', 5], ['schema.compare', 4], ['editor.run + code', 3], ['utilities.backup', 2],
['utilities + access', 5], ['schema.compare', 4], ['editor.run + code', 3], ['utilities.trace', 3], ['utilities.backup', 2],
];

// Unit-test files per package.
const TESTS = [
['apps/web', 175], ['packages/server', 100], ['packages/workflow-engine', 87], ['packages/sql', 86],
['apps/cli', 25], ['packages/db', 24], ['apps/workflow-server', 19], ['packages/shared', 12],
['apps/web', 180], ['packages/server', 107], ['packages/workflow-engine', 87], ['packages/sql', 87],
['apps/cli', 25], ['packages/db', 26], ['apps/workflow-server', 19], ['packages/shared', 13],
['packages/workflow-contract', 1], ['packages/ui-shared', 1],
];

const E2E_GROUPS = ['PostgreSQL', 'MySQL', 'MariaDB', 'SQL Server', 'Oracle', 'DB2', 'SQLite', 'Azure SQL', 'ClickHouse', 'Redshift', 'CockroachDB', 'YugabyteDB', 'TiDB', 'DuckDB', 'Smoke', 'Sign-in', 'One admin or several', 'Workspaces', 'SQL Editor (14 specs)', 'Send feedback', 'Browse', 'History', 'Revert', 'Revert edges', 'Access SQLite', 'Workflow', 'Utilities', 'DB Access', 'Access all', 'Migrations in Git', 'Execute gate', 'Backup & Restore', 'Backup commands'];
const E2E_GROUPS = ['PostgreSQL', 'MySQL', 'MariaDB', 'SQL Server', 'Oracle', 'DB2', 'SQLite', 'Azure SQL', 'ClickHouse', 'Redshift', 'CockroachDB', 'YugabyteDB', 'TiDB', 'DuckDB', 'Smoke', 'Sign-in', 'One admin or several', 'Workspaces', 'Demo role', 'SQL Editor (19 specs)', 'Send feedback', 'Browse', 'History', 'Revert', 'Revert edges', 'Force-migrate', 'Access SQLite', 'Workflow', 'Utilities', 'DB Access', 'Access all', 'Migrations in Git', 'Execute gate', 'Backup & Restore', 'Backup commands'];

// Metadata table → owning feature, column count (schema.ts, 31 migrations).
const TABLES = [
Expand Down
14 changes: 7 additions & 7 deletions docs/feature-map/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -227,17 +227,17 @@
<div class="wrap">
<div class="titlerow">
<h1>Fox <span>Feature Map</span></h1>
<div class="stamp">Measured on <code>main</code> · <code>f9d1e138de</code> · 2026-10-25 · mirrors <code>docs/FEATURE_MAP.md</code></div>
<div class="stamp">Measured on <code>main</code> · <code>b12bcd5607</code> · 2026-10-31 · mirrors <code>docs/FEATURE_MAP.md</code></div>
</div>
<ul class="stats" aria-label="Inventory">
<li><b>33</b> features</li>
<li><b>153</b> HTTP routes</li>
<li><b>17</b> server modules</li>
<li><b>34</b> features</li>
<li><b>158</b> HTTP routes</li>
<li><b>18</b> server modules</li>
<li><b>14</b> web features</li>
<li><b>16</b> engines</li>
<li><b>28</b> metadata tables</li>
<li><b>49</b> e2e specs</li>
<li><b>~530</b> unit-test files</li>
<li><b>57</b> e2e specs</li>
<li><b>~550</b> unit-test files</li>
</ul>
<nav class="tabs" role="tablist" aria-label="Views">
<button role="tab" id="tab-plan" data-tab="plan" aria-selected="true">Plan</button>
Expand Down Expand Up @@ -355,7 +355,7 @@ <h2>Coverage</h2>
</div>
<div class="section">
<h3>End-to-end suites</h3>
<p class="lede">49 Playwright specs in 33 groups, run with <code>npm run test:e2e:all</code>. Dialect suites need their Docker container and a fresh seed.</p>
<p class="lede">57 Playwright specs in 35 groups, run with <code>npm run test:e2e:all</code>. Dialect suites need their Docker container and a fresh seed.</p>
<div class="areas" id="e2e-groups"></div>
</div>
</section>
Expand Down
Loading
Loading