@kedem/okdb 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +57 -0
- package/README.md +344 -0
- package/banner.jpg +0 -0
- package/bin/okdb.js +2 -0
- package/docs/OKDB-ARCHITECTURE.md +1036 -0
- package/docs/auth-and-sync.md +558 -0
- package/docs/auth-permissions.md +143 -0
- package/docs/change-log.md +169 -0
- package/docs/data-model.md +174 -0
- package/docs/embeddings.md +338 -0
- package/docs/files.md +131 -0
- package/docs/fts.md +139 -0
- package/docs/functions.md +514 -0
- package/docs/getting-started.md +233 -0
- package/docs/http-api.md +705 -0
- package/docs/index.md +115 -0
- package/docs/indexes.md +207 -0
- package/docs/logging.md +116 -0
- package/docs/manifest.json +38 -0
- package/docs/migration.md +154 -0
- package/docs/pipelines.md +598 -0
- package/docs/plugins.md +144 -0
- package/docs/querying.md +204 -0
- package/docs/queue.md +637 -0
- package/docs/sync.md +156 -0
- package/docs/transactions.md +140 -0
- package/docs/ttl.md +375 -0
- package/okdb.js +1 -0
- package/package.json +26 -0
- package/public/admin-app.js +1 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/components/admin-route-host.ok.js +1 -0
- package/public/components/env-picker.ok.js +1 -0
- package/public/components/field-path-input.ok.js +1 -0
- package/public/components/field.html +6 -0
- package/public/components/geo-map.ok.js +1 -0
- package/public/components/ok-cron-input.ok.js +1 -0
- package/public/components/ok-dropzone.ok.js +1 -0
- package/public/components/ok-js-input.ok.js +1 -0
- package/public/components/ok-json-input.ok.js +1 -0
- package/public/components/ok-text-monaco-input.ok.js +1 -0
- package/public/components/theme-toggle.ok.js +1 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/logo.png +0 -0
- package/public/images/logo.svg +333 -0
- package/public/index.html +6 -0
- package/public/layouts/_default.ok.html +1 -0
- package/public/layouts/okdb-cube.ok.html +1 -0
- package/public/login-app.js +1 -0
- package/public/login.html +1 -0
- package/public/modules/ok.js +1 -0
- package/public/sections/api/index.ok.html +1 -0
- package/public/sections/api/parts/api-endpoint.ok.js +1 -0
- package/public/sections/auth/index.ok.html +1 -0
- package/public/sections/auth/modals/create-token-modal.ok.js +1 -0
- package/public/sections/auth/modals/edit-token-modal.ok.js +1 -0
- package/public/sections/auth/modals/perm-info-popover.ok.js +1 -0
- package/public/sections/auth/parts/token-detail.ok.js +1 -0
- package/public/sections/auth/parts/token-list.ok.js +1 -0
- package/public/sections/db/index.ok.html +1 -0
- package/public/sections/db/modals/add-item-modal.ok.js +1 -0
- package/public/sections/db/modals/create-env-modal.ok.js +1 -0
- package/public/sections/db/modals/create-fts-modal.ok.js +1 -0
- package/public/sections/db/modals/create-index-modal.ok.js +1 -0
- package/public/sections/db/modals/create-pipeline-modal.ok.js +1 -0
- package/public/sections/db/modals/demo-env-modal.ok.js +1 -0
- package/public/sections/db/modals/remove-env-modal.ok.js +1 -0
- package/public/sections/db/modals/schema-edit-modal.ok.js +1 -0
- package/public/sections/db/parts/db-overview.ok.js +1 -0
- package/public/sections/db/parts/db-pipelines-panel.ok.js +1 -0
- package/public/sections/db/parts/edit-row.ok.js +1 -0
- package/public/sections/db/parts/tm-timeline.ok.js +1 -0
- package/public/sections/db/parts/type-changelog.ok.js +1 -0
- package/public/sections/db/parts/type-list.ok.js +1 -0
- package/public/sections/db/parts/type-pipelines-tab.ok.js +1 -0
- package/public/sections/db/parts/type-schema-tab.ok.js +1 -0
- package/public/sections/db/parts/type-viewer.ok.js +1 -0
- package/public/sections/db/parts/type-violations-tab.ok.js +1 -0
- package/public/sections/docs/index.ok.html +1 -0
- package/public/sections/embeddings/index.ok.html +1 -0
- package/public/sections/embeddings/modals/register-model-modal.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-create-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-embedder-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-engine-declaration-editor.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-engine-list.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-indexer-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-lifecycle-bar.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-model-catalog.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-overview.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-search-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/embed-worker-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/embeddings-engine-panel.ok.js +1 -0
- package/public/sections/embeddings/parts/engines-overview.ok.js +1 -0
- package/public/sections/embeddings/parts/pipeline-create-panel.ok.js +1 -0
- package/public/sections/engines/engine-panel-shared-style.js +1 -0
- package/public/sections/engines/engine-ui-utils.js +1 -0
- package/public/sections/engines/index.ok.html +1 -0
- package/public/sections/engines/modals/engine-modal.ok.js +1 -0
- package/public/sections/engines/parts/engine-declaration-editor.ok.js +1 -0
- package/public/sections/engines/parts/engine-lifecycle-actions.ok.js +1 -0
- package/public/sections/engines/parts/engine-panel-shell.ok.js +1 -0
- package/public/sections/engines/parts/engine-resource-card.ok.js +1 -0
- package/public/sections/engines/parts/engine-row-item.ok.js +1 -0
- package/public/sections/engines/parts/generic-engine-panel.ok.js +1 -0
- package/public/sections/files/index.ok.html +1 -0
- package/public/sections/files/parts/files-blob-status.ok.js +1 -0
- package/public/sections/files/parts/files-collection-list.ok.js +1 -0
- package/public/sections/files/parts/files-collection-panel.ok.js +1 -0
- package/public/sections/files/parts/files-file-list.ok.js +1 -0
- package/public/sections/files/parts/files-overview.ok.js +1 -0
- package/public/sections/files/parts/files-path-browser.ok.js +1 -0
- package/public/sections/files/parts/files-upload-panel.ok.js +1 -0
- package/public/sections/functions/index.ok.html +1 -0
- package/public/sections/functions/modals/function-modal.ok.js +1 -0
- package/public/sections/functions/parts/function-detail.ok.js +1 -0
- package/public/sections/functions/parts/function-logs.ok.js +1 -0
- package/public/sections/functions/parts/function-runner.ok.js +1 -0
- package/public/sections/functions/parts/functions-list.ok.js +1 -0
- package/public/sections/license/index.ok.html +1 -0
- package/public/sections/license/parts/license-panel.ok.js +1 -0
- package/public/sections/processors/index.ok.html +1 -0
- package/public/sections/queue/index.ok.html +1 -0
- package/public/sections/queue/modals/create-bucket-modal.ok.js +1 -0
- package/public/sections/queue/modals/edit-bucket-modal.ok.js +1 -0
- package/public/sections/queue/modals/edit-job-modal.ok.js +1 -0
- package/public/sections/queue/modals/enqueue-job-modal.ok.js +1 -0
- package/public/sections/queue/modals/job-type-modal.ok.js +1 -0
- package/public/sections/queue/parts/ok-job-form.ok.js +1 -0
- package/public/sections/queue/parts/queue-buckets.ok.js +1 -0
- package/public/sections/queue/parts/queue-job-types.ok.js +1 -0
- package/public/sections/queue/parts/queue-jobs.ok.js +1 -0
- package/public/sections/queue/parts/queue-types.ok.js +1 -0
- package/public/sections/queue/parts/queue-workers.ok.js +1 -0
- package/public/sections/sync/index.ok.html +5 -0
- package/public/sections/sync/parts/sync-node-detail.ok.js +1 -0
- package/public/sections/sync/parts/sync-retention.ok.js +1 -0
- package/public/sections/sync/parts/sync-topology.ok.js +1 -0
- package/public/sections/system/index.ok.html +1 -0
- package/public/sections/system/modals/data-ops-modal.ok.js +1 -0
- package/public/sections/system/parts/system-api-panel.ok.js +1 -0
- package/public/sections/system/parts/system-auth-panel.ok.js +1 -0
- package/public/sections/system/parts/system-log-history.ok.js +1 -0
- package/public/sections/system/parts/system-log.ok.js +1 -0
- package/public/sections/system/parts/system-status.ok.js +1 -0
- package/public/sections/system/parts/system-sync-panel.ok.js +1 -0
- package/public/sections/views/index.ok.html +1 -0
- package/public/sections/views/modals/create-view-modal.ok.js +1 -0
- package/public/sections/views/parts/view-detail.ok.js +1 -0
- package/public/sections/views/parts/views-list.ok.js +1 -0
- package/public/sections/views/parts/views-overview.ok.js +1 -0
- package/public/setup-app.js +1 -0
- package/public/setup.html +1 -0
- package/public/site.webmanifest +1 -0
- package/public/stores/auth-store.js +1 -0
- package/public/stores/events-store.js +1 -0
- package/public/stores/functions-store.js +1 -0
- package/public/stores/okdb-store.js +1 -0
- package/public/stores/theme-store.js +1 -0
- package/public/styles.css +1 -0
- package/public/test-cron-input.html +10 -0
- package/public/test-json-input.html +101 -0
- package/public/util/permissions.js +1 -0
- package/public/util/sift-schema.js +1 -0
- package/public/vendor/okjs/ok.esm.min.js +2 -0
- package/public/vendor/okjs/toolkit/behavior/async.ok.js +1 -0
- package/public/vendor/okjs/toolkit/components/ok-chart.ok.js +1 -0
- package/public/vendor/okjs/toolkit/components/ok-doc-viewer.ok.js +1 -0
- package/public/vendor/okjs/toolkit/components/ok-marked.ok.js +1 -0
- package/public/vendor/okjs/toolkit/components/ok-monaco.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/ok-layer-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/ok-layer.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/ok-overlay.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/ok-sandbox.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/transition.ok.js +1 -0
- package/public/vendor/okjs/toolkit/core/virtualized.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-console-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-console.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-fps-monitor-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-fps-monitor.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-inspector-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-inspector.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-js-tree.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-timeline-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/devtools/ok-timeline.ok.js +1 -0
- package/public/vendor/okjs/toolkit/docs/doc-viewer.ok.js +1 -0
- package/public/vendor/okjs/toolkit/editor/ok-live-component.ok.js +1 -0
- package/public/vendor/okjs/toolkit/editor/ok-live-example.ok.js +1 -0
- package/public/vendor/okjs/toolkit/editor/ok-live-shell.ok.js +1 -0
- package/public/vendor/okjs/toolkit/elements/ok-dropdown.ok.js +1 -0
- package/public/vendor/okjs/toolkit/elements/ok-icon.ok.js +1 -0
- package/public/vendor/okjs/toolkit/elements/ok-select.ok.js +1 -0
- package/public/vendor/okjs/toolkit/elements/ok-table.ok.js +1 -0
- package/public/vendor/okjs/toolkit/errors/ok-crash-boundary.ok.js +1 -0
- package/public/vendor/okjs/toolkit/errors/ok-crash-info.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/i18n-editor-examples.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/i18n-editor.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/i18n-key-editor-tips.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/i18n-key-editor.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/i18n-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/i18n/ok-i18n.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-contextmenu.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-dismiss-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-draggable.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-flyover-panel.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-flyover.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-highlight-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-highlight.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-hotkeys-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-picker-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-popout-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-popout.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-portal-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/interaction/ok-submenu.ok.js +1 -0
- package/public/vendor/okjs/toolkit/layout/ok-resizer.ok.js +1 -0
- package/public/vendor/okjs/toolkit/manifest.js +1 -0
- package/public/vendor/okjs/toolkit/modals/ok-modal-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/modals/ok-modal.ok.js +1 -0
- package/public/vendor/okjs/toolkit/modals/ok-toast-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/modals/ok-window.ok.js +1 -0
- package/public/vendor/okjs/toolkit/navigation/hash-router.ok.js +1 -0
- package/public/vendor/okjs/toolkit/navigation/history-router.ok.js +1 -0
- package/public/vendor/okjs/toolkit/navigation/router.ok.js +1 -0
- package/public/vendor/okjs/toolkit/sync/reactive-sync-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/sync/reactive-sync.js +1 -0
- package/public/vendor/okjs/toolkit/theming/blocks/ok-design-button.css.js +1 -0
- package/public/vendor/okjs/toolkit/theming/blocks/ok-design-input.css.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-design-style.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-design-vars.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-design.ok.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-input.ok.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-service.ok.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-style.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-utils.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-variable.ok.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-vm.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor.ok.js +1 -0
- package/public/vendor/okjs/toolkit/theming/ok-theme.ok.js +1 -0
- package/public/view-models/type-query-vm.js +1 -0
- package/types/environment.d.ts +130 -0
- package/types/errors.d.ts +98 -0
- package/types/features/admin.d.ts +4 -0
- package/types/features/api.d.ts +18 -0
- package/types/features/auth.d.ts +31 -0
- package/types/features/embeddings.d.ts +39 -0
- package/types/features/engines.d.ts +23 -0
- package/types/features/files.d.ts +29 -0
- package/types/features/fts.d.ts +25 -0
- package/types/features/functions.d.ts +23 -0
- package/types/features/mcp.d.ts +17 -0
- package/types/features/pipelines.d.ts +19 -0
- package/types/features/queue.d.ts +189 -0
- package/types/features/sync.d.ts +34 -0
- package/types/features/time-machine.d.ts +32 -0
- package/types/features/views.d.ts +16 -0
- package/types/index.d.ts +153 -0
- package/types/options.d.ts +202 -0
package/docs/index.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# OKDB
|
|
2
|
+
|
|
3
|
+
**An embedded, document-centric database for Node.js.** No separate server. No network hop. Just open a path and write data.
|
|
4
|
+
|
|
5
|
+
OKDB wraps [LMDB](https://www.symas.com/lmdb) — one of the fastest key-value stores ever built — and adds typed collections, composite secondary indexes, full-text search, vector embeddings, multi-node sync, a durable job queue, and binary blob storage. All in one process, all ACID.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What it is
|
|
10
|
+
|
|
11
|
+
OKDB is a **structured layer on top of LMDB**. The design decision is simple: LMDB gives you memory-mapped, copy-on-write B+ trees with MVCC and crash-safe writes. OKDB adds everything a real application needs on top of that without changing the embedding model.
|
|
12
|
+
|
|
13
|
+
Think of it as a document store where reads are synchronous (just a pointer dereference into mmap'd memory), writes are atomic, and all the derived structures — indexes, FTS posting lists, vector graphs, changelogs — are maintained automatically inside the same transaction.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Strengths
|
|
18
|
+
|
|
19
|
+
- :zap: **Reads are synchronous** — no async, no round trips, no copies
|
|
20
|
+
- :lock: **ACID transactions** — inherited from LMDB; no partial state ever observable
|
|
21
|
+
- :package: **Self-contained** — everything in one process, one set of directories
|
|
22
|
+
- :refresh-cw: **Multi-node sync** — LWW replication over HTTP, no coordinator
|
|
23
|
+
- :search: **Vector + FTS + Geo** — search primitives built in, not bolted on
|
|
24
|
+
- :mail: **Durable queue** — jobs survive crashes, stored in the same database
|
|
25
|
+
- :folder: **File storage** — SHA-256 content-addressable blobs, integrated with sync
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What it is not
|
|
30
|
+
|
|
31
|
+
- Not a client/server database (no PostgreSQL-style network protocol)
|
|
32
|
+
- Not designed for browser or Bun (Node.js only)
|
|
33
|
+
- Not an audit-log — the change-log is LWW-deduplicated per key, not an immutable history
|
|
34
|
+
- Not horizontally scalable in the traditional sense — LMDB is one writer at a time per process
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Core concepts at a glance
|
|
39
|
+
|
|
40
|
+
| Concept | What it is |
|
|
41
|
+
| --------------- | -------------------------------------------------------------- |
|
|
42
|
+
| **Type** | A named collection of records (like a table/collection) |
|
|
43
|
+
| **Key** | String primary key for each record |
|
|
44
|
+
| **Value** | Any JSON-serialisable object |
|
|
45
|
+
| **Environment** | An isolated LMDB database directory |
|
|
46
|
+
| **Index** | A composite secondary index on one or more fields |
|
|
47
|
+
| **Clock** | A monotonic per-environment integer incremented on every write |
|
|
48
|
+
| **Change log** | An ordered log of all mutations, keyed by clock value |
|
|
49
|
+
| **Processor** | A reactive change handler — like triggers, but composable |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Architecture overview
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Your app code
|
|
57
|
+
│
|
|
58
|
+
▼
|
|
59
|
+
OKDB (orchestrator)
|
|
60
|
+
│
|
|
61
|
+
├── ~system env ← node identity, env registry
|
|
62
|
+
├── default env ← your data, indexes, change log
|
|
63
|
+
├── queue env ← job queue
|
|
64
|
+
├── embeddings env ← vector embedding status
|
|
65
|
+
└── custom envs ← whatever you create
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
All environments are LMDB directories on disk. Each has its own clock, changelog, and HLC-based sync cursor.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quick example
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
const OKDB = require('src/okdb');
|
|
76
|
+
|
|
77
|
+
const okdb = new OKDB('./mydb');
|
|
78
|
+
await okdb.open();
|
|
79
|
+
|
|
80
|
+
await okdb.registerType('notes');
|
|
81
|
+
await okdb.registerIndex('notes', ['author', 'createdAt']);
|
|
82
|
+
|
|
83
|
+
await okdb.put('notes', 'n1', { author: 'alice', body: 'Hello world', createdAt: Date.now() });
|
|
84
|
+
await okdb.put('notes', 'n2', { author: 'bob', body: 'Hey there', createdAt: Date.now() });
|
|
85
|
+
|
|
86
|
+
// Synchronous reads
|
|
87
|
+
const note = okdb.get('notes', 'n1');
|
|
88
|
+
|
|
89
|
+
// Index scan
|
|
90
|
+
for (const { key, value } of okdb.byIndex('notes', ['author'], { prefix: ['alice'] })) {
|
|
91
|
+
console.log(key, value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await okdb.close();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Docs map
|
|
100
|
+
|
|
101
|
+
- [Getting Started](./getting-started.md) — install, open, first writes
|
|
102
|
+
- [Data Model](./data-model.md) — types, keys, environments, storage layout
|
|
103
|
+
- [Querying](./querying.md) — reads, ranges, MongoDB-style filters
|
|
104
|
+
- [Indexes](./indexes.md) — secondary, composite, unique, geo
|
|
105
|
+
- [Transactions](./transactions.md) — batching, versioning, optimistic concurrency
|
|
106
|
+
- [Change Log & Events](./change-log.md) — clock, changelog, processor, events
|
|
107
|
+
- [Sync](./sync.md) — multi-node replication
|
|
108
|
+
- [Full-Text Search](./fts.md) — FTS index, tokenizer, query
|
|
109
|
+
- [Embeddings](./embeddings.md) — vector pipeline, semantic search
|
|
110
|
+
- [Queue](./queue.md) — durable job queue
|
|
111
|
+
- [Files](./files.md) — blob / file storage
|
|
112
|
+
- [Custom Functions](./functions.md) — sandboxed stored JS functions, env/global scope, HTTP execution
|
|
113
|
+
- [HTTP API](./http-api.md) — built-in REST API
|
|
114
|
+
- [Plugins](./plugins.md) — extending OKDB before open
|
|
115
|
+
- [Migration](./migration.md) — export, import, backup, blob integrity
|
package/docs/indexes.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Indexes
|
|
2
|
+
|
|
3
|
+
Secondary indexes let you scan records by field values without touching every record. They are maintained atomically inside every write transaction — there is no window where an index is out of sync with the data.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Registering an index
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// Single field
|
|
11
|
+
await okdb.registerIndex('articles', ['author']);
|
|
12
|
+
|
|
13
|
+
// Composite — multi-field
|
|
14
|
+
await okdb.registerIndex('articles', ['author', 'publishedAt']);
|
|
15
|
+
|
|
16
|
+
// Unique constraint
|
|
17
|
+
await okdb.registerIndex('users', ['email'], { unique: true });
|
|
18
|
+
|
|
19
|
+
// Geospatial
|
|
20
|
+
await okdb.registerIndex('places', ['location'], { type: 'geo', precision: 7 });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### MCP note
|
|
24
|
+
|
|
25
|
+
Index management is exposed through the grouped `okdb_index` MCP tool.
|
|
26
|
+
Use `action: "create"` and send either the canonical `index` string or the convenience `fields` array.
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"name": "okdb_index",
|
|
31
|
+
"arguments": {
|
|
32
|
+
"action": "create",
|
|
33
|
+
"env": "default",
|
|
34
|
+
"type": "places",
|
|
35
|
+
"fields": ["location"],
|
|
36
|
+
"indexType": "geo",
|
|
37
|
+
"precision": 7
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For geo indexes, the indexed field should hold a single object shaped like `{ lat, lon }`.
|
|
43
|
+
That means the correct field path is usually `location`, not `location.lat` + `location.lon` as separate fields.
|
|
44
|
+
|
|
45
|
+
Indexes build **asynchronously** from existing data after registration. Writes during the build are tracked and the index converges to READY. To wait for completion:
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
await okdb.indexReady('articles', ['author', 'publishedAt']);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Check status without waiting:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
okdb.getIndexStatus('articles', ['author']);
|
|
55
|
+
// → 'creating' | 'resetting' | 'ready'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Dropping an index
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
await okdb.dropIndex('articles', ['author', 'publishedAt']);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The index sub-database is deleted. Existing query code that references it will throw `OKDBIndexNotRegisteredError`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Index names
|
|
71
|
+
|
|
72
|
+
Internally, an index is identified by its fields joined with `~`:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
['author'] → "author"
|
|
76
|
+
['author', 'publishedAt'] → "author~publishedAt"
|
|
77
|
+
['status', 'priority', 'when'] → "status~priority~when"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
You rarely need to deal with index names directly — all API methods accept `string[]` and normalise for you.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Composite indexes
|
|
85
|
+
|
|
86
|
+
A composite index orders records by the first field, then the second within ties, and so on. This mirrors SQL's multi-column index semantics.
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
await okdb.registerIndex('orders', ['customerId', 'createdAt']);
|
|
90
|
+
|
|
91
|
+
// All orders by customer 'c42', newest first
|
|
92
|
+
for (const { key, value } of okdb.byIndex('orders', ['customerId', 'createdAt'], {
|
|
93
|
+
prefix: ['c42'],
|
|
94
|
+
reverse: true,
|
|
95
|
+
limit: 50,
|
|
96
|
+
})) { ... }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The trailing `null` sentinel in the LMDB key format ensures that a prefix scan on `['c42']` correctly returns all entries for that customer regardless of their `createdAt` value.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Dot-notation for nested fields
|
|
104
|
+
|
|
105
|
+
Field paths can use dots to reach nested properties:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
await okdb.registerIndex('users', ['address.city']);
|
|
109
|
+
|
|
110
|
+
// Records where address.city === 'Paris'
|
|
111
|
+
for (const { key, value } of okdb.byIndex('users', ['address.city'], { prefix: ['Paris'] })) { ... }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Unique indexes
|
|
117
|
+
|
|
118
|
+
A unique index prevents two records from having the same value for the indexed fields.
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
await okdb.registerIndex('users', ['email'], { unique: true });
|
|
122
|
+
|
|
123
|
+
// This will throw OKDBUniqueConstraintError:
|
|
124
|
+
await okdb.put('users', 'user-2', { email: 'alice@example.com' }); // if 'alice@example.com' already exists
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Sync conflicts
|
|
128
|
+
|
|
129
|
+
When a duplicate arrives via sync (from another node), OKDB doesn't reject it — sync data is never lost. Instead, a **violation** is recorded, and a **winner** is deterministically chosen:
|
|
130
|
+
|
|
131
|
+
> Highest HLC timestamp wins. Ties broken by lexicographically largest `origin` node ID.
|
|
132
|
+
|
|
133
|
+
Violations are stored locally (not synced) and resolve automatically when the losing record is removed or updated.
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
// List all violations for a type
|
|
137
|
+
const violations = okdb.getUniqueViolations('users');
|
|
138
|
+
|
|
139
|
+
// Check if any violations exist for a specific index
|
|
140
|
+
const hasProb = okdb.hasUniqueViolations('users', ['email']);
|
|
141
|
+
|
|
142
|
+
// Get a specific violation
|
|
143
|
+
const v = okdb.getUniqueViolation('users', ['email'], 'alice@example.com');
|
|
144
|
+
// v = { winner, entries, count, detectedAt, ... }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Events emitted:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
okdb.events.on('unique:violation', ({ type, index, indexKey, winner }) => { ... });
|
|
151
|
+
okdb.events.on('unique:violation_resolved', ({ type, index, indexKey }) => { ... });
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
:::note
|
|
155
|
+
Geo indexes cannot be unique. Geohash collisions make uniqueness semantics undefined — the registration will throw.
|
|
156
|
+
:::
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Geo indexes
|
|
161
|
+
|
|
162
|
+
Geospatial indexes use geohash encoding to map `{ lat, lon }` coordinates into a lexicographically ordered string space.
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
await okdb.registerIndex('restaurants', ['coords'], { type: 'geo', precision: 7 });
|
|
166
|
+
|
|
167
|
+
await okdb.put('restaurants', 'r1', { name: 'Le Bistro', coords: { lat: 48.87, lon: 2.31 } });
|
|
168
|
+
|
|
169
|
+
// Radius search
|
|
170
|
+
const results = okdb.geoQuery('restaurants', 'coords', {
|
|
171
|
+
lat: 48.87,
|
|
172
|
+
lon: 2.31,
|
|
173
|
+
radius: 1000, // metres
|
|
174
|
+
});
|
|
175
|
+
for (const { key, value, distance } of results) { ... }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Precision guide:**
|
|
179
|
+
|
|
180
|
+
| Precision | Cell size | Good for |
|
|
181
|
+
| --------- | --------- | ------------- |
|
|
182
|
+
| 5 | ~4.9km | Country-level |
|
|
183
|
+
| 6 | ~1.2km | City-level |
|
|
184
|
+
| 7 | ~153m | Neighbourhood |
|
|
185
|
+
| 8 | ~38m | Street |
|
|
186
|
+
| 9 | ~5m | Building |
|
|
187
|
+
|
|
188
|
+
Higher precision = more storage, slower builds, higher accuracy.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## `ensureType` shorthand
|
|
193
|
+
|
|
194
|
+
The most concise way to set up a type with indexes in one call:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
await okdb.ensureType('products', {
|
|
198
|
+
indexes: [
|
|
199
|
+
['category'],
|
|
200
|
+
['category', 'price'],
|
|
201
|
+
{ fields: ['location'], type: 'geo', precision: 6 },
|
|
202
|
+
{ fields: ['sku'], unique: true },
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
This registers the type if missing, registers each index if missing, and is safe to call on startup every time.
|
package/docs/logging.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Logging
|
|
2
|
+
|
|
3
|
+
OKDB has a built-in structured logger available as `okdb.log`. Logs are emitted as plain objects and routed to any number of attached sinks. A console sink is attached by default.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Entry Shape
|
|
8
|
+
|
|
9
|
+
Every log call produces an entry with this shape:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
{
|
|
13
|
+
level: 'info' | 'warn' | 'error' | 'debug',
|
|
14
|
+
msg: string, // human-readable message
|
|
15
|
+
meta: object, // system identity set by child loggers
|
|
16
|
+
context: any, // user-provided call-site data (second arg)
|
|
17
|
+
ts: number // Date.now()
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`meta` contains machine-readable identity fields (feature, env name, function name, etc). `context` contains whatever you pass as the second argument to a log call — it is for human display only and not filtered on.
|
|
22
|
+
|
|
23
|
+
### Known `meta` Keys
|
|
24
|
+
|
|
25
|
+
| Key | Who sets it | Example values |
|
|
26
|
+
| ---------- | -------------------------- | -------------------------------------------------------------- |
|
|
27
|
+
| `feature` | each feature child logger | `'queue'`, `'auth'`, `'fts'`, `'sync'`, `'functions'`, `'env'` |
|
|
28
|
+
| `env` | env logger, functions pool | `'demo-places'` |
|
|
29
|
+
| `fn` | functions pool (per run) | `'myFunction'` |
|
|
30
|
+
| `runId` | functions pool (per run) | `'abc123'` |
|
|
31
|
+
| `runnerId` | functions pool (per run) | `'fn-1'` |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## `okdb.log` — Basic Usage
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
okdb.log.info('server started', { port: 3000 });
|
|
39
|
+
okdb.log.warn('slow query', { ms: 420, type: 'events' });
|
|
40
|
+
okdb.log.error('unexpected failure', { err: err.message });
|
|
41
|
+
okdb.log.debug('cache miss', { key });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## `okdb.log.child(meta)` — Scoped Loggers
|
|
47
|
+
|
|
48
|
+
`child(extraMeta)` returns a new logger instance that shares the same sinks but stamps every entry with additional `meta` fields. Use it to create feature or run-scoped loggers.
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
// Feature logger — created once, reused for all calls
|
|
52
|
+
const authLog = okdb.log.child({ feature: 'auth' });
|
|
53
|
+
authLog.warn('invalid token');
|
|
54
|
+
// → { level: 'warn', msg: 'invalid token', meta: { feature: 'auth' }, context: undefined, ts }
|
|
55
|
+
|
|
56
|
+
// Per-environment logger
|
|
57
|
+
const envLog = okdb.log.child({ feature: 'env', env: 'demo-places' });
|
|
58
|
+
envLog.info('env opened');
|
|
59
|
+
// → { ..., meta: { feature: 'env', env: 'demo-places' }, ... }
|
|
60
|
+
|
|
61
|
+
// Run-scoped child — created per function invocation
|
|
62
|
+
const runLog = okdb.log.child({ feature: 'functions', env, fn, runId, runnerId });
|
|
63
|
+
runLog.info('function invoked');
|
|
64
|
+
runLog.info('result ready', { value: 42 });
|
|
65
|
+
// → { ..., meta: { feature: 'functions', env, fn, runId, runnerId }, context: { value: 42 }, ts }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`child()` does a shallow merge of meta fields. Child meta overrides parent meta for the same key. The returned logger shares the same sink set — attaching or detaching sinks on the root logger affects all children.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Consuming Logs
|
|
73
|
+
|
|
74
|
+
### `okdb.log.attach(fn)` — Custom Sink
|
|
75
|
+
|
|
76
|
+
Attach a function to receive every log entry:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
okdb.log.attach((entry) => {
|
|
80
|
+
// entry: { level, msg, meta, context, ts }
|
|
81
|
+
myExternalLogger.log(entry);
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
To detach, call the returned function:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const detach = okdb.log.attach(fn);
|
|
89
|
+
detach();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `okdb.events.on('log', fn)` — Event Bus
|
|
93
|
+
|
|
94
|
+
Entries are also emitted on the OKDB event bus:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
okdb.events.on('log', (entry) => {
|
|
98
|
+
console.log(entry.meta?.feature, entry.msg);
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### `okdb.logs` — Ring Buffer
|
|
103
|
+
|
|
104
|
+
A circular buffer of recent log entries is available for inspection:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
okdb.logs; // array of recent entries, newest last
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## `attachConsole()` — Console Output
|
|
113
|
+
|
|
114
|
+
`okdb.log.attachConsole()` adds a human-readable console sink. It is called automatically when OKDB starts in development mode.
|
|
115
|
+
|
|
116
|
+
Function-scoped logs (`meta.feature === 'functions'`) are suppressed from the console because they are stored in function run records and displayed in the Admin UI instead. All other features are printed.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "file": "index.md", "label": "Overview" },
|
|
3
|
+
{ "file": "getting-started.md", "label": "Getting Started" },
|
|
4
|
+
{
|
|
5
|
+
"label": "Core",
|
|
6
|
+
"children": [
|
|
7
|
+
{ "file": "data-model.md", "label": "Data Model" },
|
|
8
|
+
{ "file": "querying.md", "label": "Querying" },
|
|
9
|
+
{ "file": "indexes.md", "label": "Indexes" },
|
|
10
|
+
{ "file": "transactions.md", "label": "Transactions" },
|
|
11
|
+
{ "file": "change-log.md", "label": "Change Log & Events" }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"label": "Features",
|
|
16
|
+
"children": [
|
|
17
|
+
{ "file": "sync.md", "label": "Sync" },
|
|
18
|
+
{ "file": "fts.md", "label": "Full-Text Search" },
|
|
19
|
+
{ "file": "embeddings.md", "label": "Embeddings" },
|
|
20
|
+
{ "file": "pipelines.md", "label": "Pipelines" },
|
|
21
|
+
{ "file": "queue.md", "label": "Queue" },
|
|
22
|
+
{ "file": "files.md", "label": "Files" },
|
|
23
|
+
{ "file": "functions.md", "label": "Custom Functions" },
|
|
24
|
+
{ "file": "ttl.md", "label": "TTL (Time-to-Live)" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"label": "Reference",
|
|
29
|
+
"children": [
|
|
30
|
+
{ "file": "auth-and-sync.md", "label": "Auth & Sync" },
|
|
31
|
+
{ "file": "auth-permissions.md", "label": "Auth Permissions" },
|
|
32
|
+
{ "file": "http-api.md", "label": "HTTP API" },
|
|
33
|
+
{ "file": "plugins.md", "label": "Plugins" },
|
|
34
|
+
{ "file": "logging.md", "label": "Logging" },
|
|
35
|
+
{ "file": "migration.md", "label": "Migration" }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Migration & Export
|
|
2
|
+
|
|
3
|
+
OKDB's migration feature handles export/import of all types and records as JSON, plus blob export/import for file storage, and blob integrity tools.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Export
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const data = await okdb.migrate.export();
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Returns an object:
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
{
|
|
17
|
+
types: {
|
|
18
|
+
users: {
|
|
19
|
+
entries: [
|
|
20
|
+
{ key: 'alice', value: { name: 'Alice', role: 'admin' }, version: 3 },
|
|
21
|
+
{ key: 'bob', value: { name: 'Bob', role: 'member' }, version: 1 },
|
|
22
|
+
],
|
|
23
|
+
indexes: [...], // index definitions
|
|
24
|
+
ftsIndexes: {...}, // FTS index definitions
|
|
25
|
+
schema: {...}, // JSON Schema { definition, enforce } if set
|
|
26
|
+
},
|
|
27
|
+
orders: { entries: [...] },
|
|
28
|
+
},
|
|
29
|
+
meta: {}, // empty unless includeMeta: true
|
|
30
|
+
changes: [], // empty unless includeChangeLog: true
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Indexes** are exported and restored on import. **JSON Schemas** (including foreign key `ref` annotations and `enforce` mode) are preserved across export/import. On import, schemas are applied after all data is loaded so `enforce: true` validation succeeds.
|
|
35
|
+
|
|
36
|
+
### Options
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const data = await okdb.migrate.export({
|
|
40
|
+
includeMeta: false, // include field schema metadata
|
|
41
|
+
includeChangeLog: false, // include the clock/change log entries
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Export to JSON string
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
const json = await okdb.migrate.exportJSON({ pretty: true });
|
|
49
|
+
fs.writeFileSync('./backup.json', json);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Import
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const data = JSON.parse(fs.readFileSync('./backup.json', 'utf8'));
|
|
58
|
+
await okdb.migrate.import(data);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
:::warning
|
|
62
|
+
Import **drops all existing types** before recreating them. This is a destructive operation. Back up your data first.
|
|
63
|
+
:::
|
|
64
|
+
|
|
65
|
+
Import recreates all types, populates records with their exact versions (preserving optimistic-concurrency state), and optionally restores the change log.
|
|
66
|
+
|
|
67
|
+
### Import from JSON string
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
const json = fs.readFileSync('./backup.json', 'utf8');
|
|
71
|
+
await okdb.migrate.importJSON(json);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Blob export / import
|
|
77
|
+
|
|
78
|
+
File blobs (from the [Files](./files.md) feature) are stored separately from the OKDB types. Exporting and importing blobs is a separate step:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Export blobs to a target directory
|
|
82
|
+
const { count, bytes } = await okdb.migrate.exportBlobs('./backup-blobs');
|
|
83
|
+
console.log(`Exported ${count} blobs, ${bytes} bytes`);
|
|
84
|
+
|
|
85
|
+
// Import blobs from a source directory (skips blobs already present)
|
|
86
|
+
const { imported, skipped } = await okdb.migrate.importBlobs('./backup-blobs');
|
|
87
|
+
console.log(`Imported ${imported}, skipped ${skipped} already present`);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The blob directory layout is preserved: `<dir>/<first2>/<rest>` (matching the SHA-256 hex filename structure).
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Blob integrity
|
|
95
|
+
|
|
96
|
+
Validate that blobs on disk match the `~files` metadata records:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const issues = await okdb.migrate.validateBlobIntegrity();
|
|
100
|
+
|
|
101
|
+
if (issues.length === 0) {
|
|
102
|
+
console.log('All blobs intact');
|
|
103
|
+
} else {
|
|
104
|
+
for (const issue of issues) {
|
|
105
|
+
console.warn(issue.type, issue.message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Issue types returned:
|
|
111
|
+
|
|
112
|
+
| Type | Description |
|
|
113
|
+
| -------------- | ------------------------------------------------------------------- |
|
|
114
|
+
| `missing_blob` | A `~files` record references a hash that has no blob on disk |
|
|
115
|
+
| `zero_refs` | A blob's ref-count is zero but a `~files` record still points to it |
|
|
116
|
+
| `orphan_blob` | A blob file on disk has no `~files` record in any environment |
|
|
117
|
+
|
|
118
|
+
### Repair ref-counts
|
|
119
|
+
|
|
120
|
+
If ref-counts get out of sync (e.g. after a crash or manual blob manipulation), rebuild them from scratch:
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
const { rebuilt } = await okdb.migrate.repairBlobRefs();
|
|
124
|
+
console.log(`Rebuilt ref-counts for ${rebuilt} blobs`);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Full backup workflow
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
// 1. Export type data
|
|
133
|
+
const json = await okdb.migrate.exportJSON({ pretty: false });
|
|
134
|
+
fs.writeFileSync('./backup/data.json', json);
|
|
135
|
+
|
|
136
|
+
// 2. Export blobs
|
|
137
|
+
await okdb.migrate.exportBlobs('./backup/blobs');
|
|
138
|
+
|
|
139
|
+
console.log('Backup complete');
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Restore:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
// 1. Import type data
|
|
146
|
+
await okdb.migrate.importJSON(fs.readFileSync('./backup/data.json', 'utf8'));
|
|
147
|
+
|
|
148
|
+
// 2. Import blobs
|
|
149
|
+
await okdb.migrate.importBlobs('./backup/blobs');
|
|
150
|
+
|
|
151
|
+
// 3. Optional: verify integrity
|
|
152
|
+
const issues = await okdb.migrate.validateBlobIntegrity();
|
|
153
|
+
if (issues.length > 0) console.warn(issues);
|
|
154
|
+
```
|