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
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,22 @@ See [docs/concepts.md](./docs/concepts.md) and [docs/fixtures-and-schemas.md](./

## Admin/CMS Schema Metadata

Schemas can also drive local admin, CMS, and form-building screens. Configure `schemaOutFile` when an app needs committed field metadata such as field types, labels, defaults, enum values, relation hints, descriptions, and app-specific UI hints.
Schemas can also drive local admin, CMS, custom data viewers, and form-building screens. Use `GET /__jsondb/manifest.json` at runtime when a UI runs beside `jsondb serve`, or configure `viewerManifestOutFile` when app code needs a committed JSON artifact with the same viewer metadata. Browser requests can open `GET /__jsondb/manifest.html`; AI clients can use `GET /__jsondb/manifest.md`; `GET /__jsondb/manifest` lets the `Accept` header choose among registered response formats.

Use `schemaOutFile` when an app only needs the smaller model metadata file without server route links, diagnostics, or viewer capabilities.

```js
import { defineConfig, mergeManifest } from 'jsondb/config';

export default defineConfig({
schemaOutFile: './src/generated/jsondb.schema.json',
viewerManifestOutFile: './src/generated/jsondb.viewer.json',

server: {
viewerLinks: [
{ label: 'App Data Viewer', href: 'http://127.0.0.1:5173/jsondb' },
],
},

schemaManifest: {
customizeResource({ file, defaultManifest }) {
Expand Down Expand Up @@ -238,7 +247,7 @@ export default defineConfig({
});
```

The generated manifest is metadata output; schema defaults and validation still come from the schema contract.
The generated manifest is metadata output; schema defaults and validation still come from the schema contract. Actual records stay on REST or GraphQL routes, so a custom viewer fetches `manifest.json` for fields and route links, then calls the listed resource routes for rows. `server.viewerLinks` exposes custom viewer URLs in root discovery and the shared manifest.

See [docs/generated-files.md](./docs/generated-files.md) and [examples/schema-manifest](./examples/schema-manifest).

Expand Down Expand Up @@ -296,7 +305,7 @@ curl 'http://127.0.0.1:7331/users?select=id,name&offset=0&limit=20'

The viewer at `/__jsondb` lets you inspect resources, import CSV files into the configured fixture folder, view generated schema metadata, read GraphQL SDL/operation references, and try REST requests without writing client code first.

The built-in viewer is intentionally small; apps can use the REST API, GraphQL endpoint, generated schema metadata, or integrations to build their own viewer UI.
The built-in viewer and custom viewer UIs use the same JSON manifest at `/__jsondb/manifest.json`. `/__jsondb/manifest.html` opens a formatted JSON viewer, `/__jsondb/manifest.md` returns an AI-friendly Markdown wrapper, and `/__jsondb/manifest` chooses from registered media types in `Accept`. Apps can use `api.formats` from the manifest to discover supported extensions and build their own viewer UI against REST or GraphQL records.

See [docs/server-and-viewer.md](./docs/server-and-viewer.md).

Expand All @@ -309,6 +318,7 @@ See [docs/server-and-viewer.md](./docs/server-and-viewer.md).
| `.jsondb/types/index.ts` | Normally no | Default generated TypeScript output. |
| `types.commitOutFile` output | Yes, when configured | Use for stable imports before sync runs. |
| `schemaOutFile` output | Yes, when configured | Use for model-driven admin/CMS metadata. |
| `viewerManifestOutFile` output | Yes, when configured | Use for custom data viewers that need metadata plus route links. |
| `examples/*/src/generated/jsondb.types.ts` | Yes, in selected examples | Intentionally committed example type output. |
| `examples/*/src/generated/jsondb.schema.json` | Yes, in selected examples | Intentionally committed example manifest. |

Expand Down
34 changes: 31 additions & 3 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ export default {
},

server: {
apiBase: '/__jsondb',
host: '127.0.0.1',
port: 7331,
maxBodyBytes: 1048576,
Expand Down Expand Up @@ -781,14 +782,24 @@ REST batches are non-transactional by design. Items execute in order, and earlie

Schema-backed writes should validate declared field types before mutating runtime state. Required fields, primitive types, enum values, arrays, nullable fields, datetime strings, flexible objects with intentional additional properties, nested objects, and field constraints (`unique`, `min`, `max`, `minLength`, `maxLength`, `pattern`) should be checked for package API writes, REST writes, GraphQL mutations, `jsondb sync`, and `jsondb schema validate`.

The root route should work as a discovery endpoint. API-style requests to `GET /` should return JSON with resource names plus links for the data viewer, schema endpoint, GraphQL endpoint, and resource routes. Browser-style requests that prefer `text/html` should return a small HTML index with those same links.
The root route should work as a discovery endpoint. API-style requests to `GET /` should return JSON with resource names plus links for the data viewer, schema endpoint, GraphQL endpoint, resource routes, and registered response formats. Browser-style requests that prefer `text/html` should return a small HTML index with those same links.

The local server should also expose a built-in dependency-free viewer:

```txt
GET /__jsondb
GET /__jsondb/manifest
GET /__jsondb/manifest.json
GET /__jsondb/manifest.html
GET /__jsondb/manifest.md
```

`server.apiBase` should default to `/__jsondb` and should configure the
standalone viewer, viewer manifest, schema, batch, import, events, log, and fork route base
without changing root REST resource routes or the standalone GraphQL path.

The viewer manifest should be the shared JSON contract used by the built-in viewer and custom data viewers. `/manifest.json` should return JSON by default. `/manifest.html` should render a formatted JSON viewer with dark mode as the default, dark/light/system controls, copy, and pretty/raw formatting controls. `/manifest.md` should render Markdown with the manifest JSON in a fenced code block for AI clients. `/manifest` should choose among registered media types from `Accept`, and explicit `/manifest.<extension>` routes should use the matching registered response format. The manifest should include API links, capabilities, diagnostics, configured viewer links, response format metadata, collections, documents, field metadata, UI hints, and relation hints. It must not include seed records, source paths, source hashes, runtime state paths, or GraphQL SDL. Custom viewers should use the manifest for UI metadata and fetch actual records from REST or GraphQL.

The viewer should support:

```txt
Expand Down Expand Up @@ -831,7 +842,7 @@ POST /__jsondb/import

The upload should copy the CSV into the configured fixture folder, run sync, reload the in-memory resources, update the URL query parameter to the imported resource, and reload the dashboard view.

While serving, jsondb should watch the configured fixture folder for fixture and schema changes, ignoring `.jsondb/`. On change, reload resources and notify the single-file viewer through `/__jsondb/events` so the dashboard refreshes automatically. If one source file fails to parse or load, report a file-specific diagnostic in the viewer and keep the remaining valid resources available. If the runtime cannot create a file watcher because of environment resource limits such as `EMFILE` or `ENOSPC`, keep the HTTP server running, publish a warning diagnostic, and serve without live reload.
While serving, jsondb should watch the configured fixture folder for fixture and schema changes, ignoring `.jsondb/`. On change, reload resources and notify the single-file viewer through the configured events route, defaulting to `/__jsondb/events`, so the dashboard refreshes automatically. If one source file fails to parse or load, report a file-specific diagnostic in the viewer and keep the remaining valid resources available. If the runtime cannot create a file watcher because of environment resource limits such as `EMFILE` or `ENOSPC`, keep the HTTP server running, publish a warning diagnostic, and serve without live reload.

Vite development should be supported through a dependency-light plugin export:

Expand All @@ -843,7 +854,7 @@ export default {
};
```

The plugin should return a plain Vite-compatible plugin object with `apply: 'serve'`, mount jsondb into the existing Vite dev middleware stack, and avoid bundling Node-only fixture runtime code into production builds. By default, Vite dev routes should be scoped under `/__jsondb` and should not answer root app routes:
The plugin should return a plain Vite-compatible plugin object with `apply: 'serve'`, mount jsondb into the existing Vite dev middleware stack, and avoid bundling Node-only fixture runtime code into production builds. By default, Vite dev routes should be scoped under `/__jsondb` and should not answer root app routes. A plugin-level `apiBase` should win over `server.apiBase`:

```txt
GET /__jsondb
Expand Down Expand Up @@ -1093,6 +1104,23 @@ When `schemaOutFile` is set, `jsondb sync` writes the manifest. The CLI can also
jsondb schema manifest --out ./src/generated/jsondb.schema.json
```

Custom viewer UIs can use the live `GET /__jsondb/manifest.json` route or a committed viewer manifest. Browser users can open `GET /__jsondb/manifest.html`, AI clients can open `GET /__jsondb/manifest.md`, and `GET /__jsondb/manifest` negotiates from registered `Accept` media types:

```js
export default {
viewerManifestOutFile: './src/generated/jsondb.viewer.json',
server: {
viewerLinks: [
{ label: 'App Data Viewer', href: 'http://127.0.0.1:5173/jsondb' },
],
},
};
```

```bash
jsondb viewer manifest --out ./src/generated/jsondb.viewer.json
```

The manifest should have this top-level shape:

```json
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ db/*.json, *.jsonc, *.csv, *.schema.json(c), *.schema.mjs
| HTTP client | `src/client.js` | REST, GraphQL, direct batching, automatic batching, fork support. |
| REST server | `src/server.js`, `src/rest/` | Dependency-free local routes and response shaping. |
| GraphQL | `src/graphql/` | Dependency-free subset parser, executor, and HTTP handler. |
| Viewer | `src/web/` | Built-in UI served at `/__jsondb`. |
| Viewer | `src/web/` | Built-in UI served at `server.apiBase`, defaulting to `/__jsondb`. |
| Vite integration | `src/vite.js`, `src/integrations/` | Optional dev server plugin and virtual client. |
| Hono/SQLite | `src/hono.js`, `src/sqlite.js`, `src/generate/hono.js` | Optional runtime integration and generated starter output. |

Expand Down
36 changes: 34 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ See [jsondb.config.example.mjs](../jsondb.config.example.mjs) for a commented co
| Index intent metadata | Off | `resources.<name>.indexes` |
| Importable generated types | `.jsondb/types/index.ts` | `types.commitOutFile` |
| Importable schema manifest | Off | `schemaOutFile` |
| Importable viewer manifest | Off | `viewerManifestOutFile` |
| REST response formats | `.json`, `.html`, `.md` | `rest.formats` |
| Unknown fields | Warn | `schema.unknownFields` |
| Schema-only mock records | Off | `seed.generateFromSchema` |
| Local latency | `30-100ms` | `mock.delay` |
| Random local failures | Off | `mock.errors` |
| Legacy database shapes | Off | `forks` |
| Host, port, body limit | `127.0.0.1:7331`, 1 MB bodies | `server` |
| Host, port, dev-tool route base, body limit | `127.0.0.1:7331`, `/__jsondb`, 1 MB bodies | `server` |

## Full Example

Expand Down Expand Up @@ -66,6 +68,7 @@ export default defineConfig({
},

schemaOutFile: './src/generated/jsondb.schema.json',
viewerManifestOutFile: './src/generated/jsondb.viewer.json',

schema: {
unknownFields: 'warn',
Expand All @@ -77,9 +80,32 @@ export default defineConfig({
},

server: {
apiBase: '/__jsondb',
host: '127.0.0.1',
port: 7331,
maxBodyBytes: 1048576,
viewerLinks: [
{ label: 'App Data Viewer', href: 'http://127.0.0.1:5173/jsondb' },
],
},

rest: {
formats: {
default: 'json',
md({ resourceName, data }) {
return {
body: `# ${resourceName}\n\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`,
contentType: 'text/markdown; charset=utf-8',
};
},
// yaml: {
// mediaTypes: ['application/yaml', 'text/yaml'],
// contentType: 'application/yaml; charset=utf-8',
// render({ data }) {
// return stringifyYaml(data);
// },
// },
},
},

mock: {
Expand Down Expand Up @@ -191,20 +217,26 @@ Random errors stay off by default. Turn them on when testing retries and error U

## Server Options

Use `server` for a different host, port, or JSON body limit:
Use `server` for a different host, port, dev-tool route base, or JSON body limit:

```js
import { defineConfig } from 'jsondb/config';

export default defineConfig({
server: {
apiBase: '/__jsondb',
host: '127.0.0.1',
port: 7331,
maxBodyBytes: 1048576,
},
});
```

`server.apiBase` scopes the standalone viewer and internal development routes:
viewer, schema, batch, import, live events, runtime log, and fork routes. REST
resources such as `/users` and the standalone GraphQL path stay unchanged unless
you configure those surfaces separately.

## Database Forks

Use forks when part of an app needs an older fixture shape while other pages move to a new shape.
Expand Down
21 changes: 21 additions & 0 deletions docs/generated-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,32 @@ The manifest includes normalized resource and field metadata such as `type`, `re

The manifests at [examples/schema-manifest/src/generated/jsondb.schema.json](../examples/schema-manifest/src/generated/jsondb.schema.json) and [examples/schema-ui/src/generated/jsondb.schema.json](../examples/schema-ui/src/generated/jsondb.schema.json) are intentionally committed.

## Viewer Manifest Output

Use `viewerManifestOutFile` when a custom data viewer needs the same JSON metadata used by the built-in viewer:

```js
import { defineConfig } from 'jsondb/config';

export default defineConfig({
viewerManifestOutFile: './src/generated/jsondb.viewer.json',
});
```

`jsondb sync` writes the viewer manifest when `viewerManifestOutFile` is set. You can also generate it directly:

```bash
npm run db -- viewer manifest --out ./src/generated/jsondb.viewer.json
```

The viewer manifest includes field metadata, UI hints, relation hints, diagnostics, capabilities, configured viewer links, and API links such as `/__jsondb/manifest`, `/__jsondb/manifest.json`, `/__jsondb/manifest.html`, `/__jsondb/manifest.md`, `/__jsondb/batch`, `/graphql`, and each REST resource route. It does not include seed records, source paths, source hashes, runtime state paths, or GraphQL SDL. Fetch actual records from REST or GraphQL.

## Cleanup Rules

- Do not commit `.jsondb/` unless a task explicitly asks for generated runtime state.
- Do commit configured `types.commitOutFile` output when an app needs stable imports in a fresh checkout.
- Do commit configured `schemaOutFile` output when an app needs stable schema metadata at runtime.
- Do commit configured `viewerManifestOutFile` output when a custom viewer needs stable metadata and route links at runtime.
- Smoke commands against examples may create `examples/*/.jsondb/`; remove that generated runtime output before finalizing.

## Related Examples
Expand Down
1 change: 1 addition & 0 deletions docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const legacyUsers = await legacyDb.rest.get('/users');
```

Plugin options include `cwd`, `dbDir`, `stateDir`, `forks`, `apiBase`, `restBasePath`, `graphqlPath`, `rootRoutes`, `clientVirtualModule`, and `clientImport`.
The plugin uses `apiBase` first, then `server.apiBase`, then `/__jsondb` for scoped dev routes.

Set `rootRoutes: true` only when you intentionally want Vite dev to also answer unscoped routes like `/users`. Standalone `jsondb serve` keeps those root REST routes by default.

Expand Down
2 changes: 2 additions & 0 deletions docs/package-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ npm run db -- schema infer users
npm run db -- schema infer users --out db/users.schema.jsonc
npm run db -- schema manifest --out ./src/generated/jsondb.schema.json
npm run db -- schema validate
npm run db -- viewer manifest --out ./src/generated/jsondb.viewer.json
npm run db -- doctor
npm run db -- doctor --json
npm run db -- check --strict
Expand All @@ -32,6 +33,7 @@ Inside npm scripts, `jsondb` resolves to the local dependency binary. Equivalent
jsondb sync
jsondb types
jsondb schema validate
jsondb viewer manifest --out ./src/generated/jsondb.viewer.json
jsondb doctor
jsondb check --strict
jsondb serve
Expand Down
Loading