@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.
Files changed (260) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +344 -0
  3. package/banner.jpg +0 -0
  4. package/bin/okdb.js +2 -0
  5. package/docs/OKDB-ARCHITECTURE.md +1036 -0
  6. package/docs/auth-and-sync.md +558 -0
  7. package/docs/auth-permissions.md +143 -0
  8. package/docs/change-log.md +169 -0
  9. package/docs/data-model.md +174 -0
  10. package/docs/embeddings.md +338 -0
  11. package/docs/files.md +131 -0
  12. package/docs/fts.md +139 -0
  13. package/docs/functions.md +514 -0
  14. package/docs/getting-started.md +233 -0
  15. package/docs/http-api.md +705 -0
  16. package/docs/index.md +115 -0
  17. package/docs/indexes.md +207 -0
  18. package/docs/logging.md +116 -0
  19. package/docs/manifest.json +38 -0
  20. package/docs/migration.md +154 -0
  21. package/docs/pipelines.md +598 -0
  22. package/docs/plugins.md +144 -0
  23. package/docs/querying.md +204 -0
  24. package/docs/queue.md +637 -0
  25. package/docs/sync.md +156 -0
  26. package/docs/transactions.md +140 -0
  27. package/docs/ttl.md +375 -0
  28. package/okdb.js +1 -0
  29. package/package.json +26 -0
  30. package/public/admin-app.js +1 -0
  31. package/public/android-chrome-192x192.png +0 -0
  32. package/public/android-chrome-512x512.png +0 -0
  33. package/public/apple-touch-icon.png +0 -0
  34. package/public/components/admin-route-host.ok.js +1 -0
  35. package/public/components/env-picker.ok.js +1 -0
  36. package/public/components/field-path-input.ok.js +1 -0
  37. package/public/components/field.html +6 -0
  38. package/public/components/geo-map.ok.js +1 -0
  39. package/public/components/ok-cron-input.ok.js +1 -0
  40. package/public/components/ok-dropzone.ok.js +1 -0
  41. package/public/components/ok-js-input.ok.js +1 -0
  42. package/public/components/ok-json-input.ok.js +1 -0
  43. package/public/components/ok-text-monaco-input.ok.js +1 -0
  44. package/public/components/theme-toggle.ok.js +1 -0
  45. package/public/favicon-16x16.png +0 -0
  46. package/public/favicon-32x32.png +0 -0
  47. package/public/favicon.ico +0 -0
  48. package/public/images/logo.png +0 -0
  49. package/public/images/logo.svg +333 -0
  50. package/public/index.html +6 -0
  51. package/public/layouts/_default.ok.html +1 -0
  52. package/public/layouts/okdb-cube.ok.html +1 -0
  53. package/public/login-app.js +1 -0
  54. package/public/login.html +1 -0
  55. package/public/modules/ok.js +1 -0
  56. package/public/sections/api/index.ok.html +1 -0
  57. package/public/sections/api/parts/api-endpoint.ok.js +1 -0
  58. package/public/sections/auth/index.ok.html +1 -0
  59. package/public/sections/auth/modals/create-token-modal.ok.js +1 -0
  60. package/public/sections/auth/modals/edit-token-modal.ok.js +1 -0
  61. package/public/sections/auth/modals/perm-info-popover.ok.js +1 -0
  62. package/public/sections/auth/parts/token-detail.ok.js +1 -0
  63. package/public/sections/auth/parts/token-list.ok.js +1 -0
  64. package/public/sections/db/index.ok.html +1 -0
  65. package/public/sections/db/modals/add-item-modal.ok.js +1 -0
  66. package/public/sections/db/modals/create-env-modal.ok.js +1 -0
  67. package/public/sections/db/modals/create-fts-modal.ok.js +1 -0
  68. package/public/sections/db/modals/create-index-modal.ok.js +1 -0
  69. package/public/sections/db/modals/create-pipeline-modal.ok.js +1 -0
  70. package/public/sections/db/modals/demo-env-modal.ok.js +1 -0
  71. package/public/sections/db/modals/remove-env-modal.ok.js +1 -0
  72. package/public/sections/db/modals/schema-edit-modal.ok.js +1 -0
  73. package/public/sections/db/parts/db-overview.ok.js +1 -0
  74. package/public/sections/db/parts/db-pipelines-panel.ok.js +1 -0
  75. package/public/sections/db/parts/edit-row.ok.js +1 -0
  76. package/public/sections/db/parts/tm-timeline.ok.js +1 -0
  77. package/public/sections/db/parts/type-changelog.ok.js +1 -0
  78. package/public/sections/db/parts/type-list.ok.js +1 -0
  79. package/public/sections/db/parts/type-pipelines-tab.ok.js +1 -0
  80. package/public/sections/db/parts/type-schema-tab.ok.js +1 -0
  81. package/public/sections/db/parts/type-viewer.ok.js +1 -0
  82. package/public/sections/db/parts/type-violations-tab.ok.js +1 -0
  83. package/public/sections/docs/index.ok.html +1 -0
  84. package/public/sections/embeddings/index.ok.html +1 -0
  85. package/public/sections/embeddings/modals/register-model-modal.ok.js +1 -0
  86. package/public/sections/embeddings/parts/embed-create-panel.ok.js +1 -0
  87. package/public/sections/embeddings/parts/embed-embedder-panel.ok.js +1 -0
  88. package/public/sections/embeddings/parts/embed-engine-declaration-editor.ok.js +1 -0
  89. package/public/sections/embeddings/parts/embed-engine-list.ok.js +1 -0
  90. package/public/sections/embeddings/parts/embed-indexer-panel.ok.js +1 -0
  91. package/public/sections/embeddings/parts/embed-lifecycle-bar.ok.js +1 -0
  92. package/public/sections/embeddings/parts/embed-model-catalog.ok.js +1 -0
  93. package/public/sections/embeddings/parts/embed-overview.ok.js +1 -0
  94. package/public/sections/embeddings/parts/embed-search-panel.ok.js +1 -0
  95. package/public/sections/embeddings/parts/embed-worker-panel.ok.js +1 -0
  96. package/public/sections/embeddings/parts/embeddings-engine-panel.ok.js +1 -0
  97. package/public/sections/embeddings/parts/engines-overview.ok.js +1 -0
  98. package/public/sections/embeddings/parts/pipeline-create-panel.ok.js +1 -0
  99. package/public/sections/engines/engine-panel-shared-style.js +1 -0
  100. package/public/sections/engines/engine-ui-utils.js +1 -0
  101. package/public/sections/engines/index.ok.html +1 -0
  102. package/public/sections/engines/modals/engine-modal.ok.js +1 -0
  103. package/public/sections/engines/parts/engine-declaration-editor.ok.js +1 -0
  104. package/public/sections/engines/parts/engine-lifecycle-actions.ok.js +1 -0
  105. package/public/sections/engines/parts/engine-panel-shell.ok.js +1 -0
  106. package/public/sections/engines/parts/engine-resource-card.ok.js +1 -0
  107. package/public/sections/engines/parts/engine-row-item.ok.js +1 -0
  108. package/public/sections/engines/parts/generic-engine-panel.ok.js +1 -0
  109. package/public/sections/files/index.ok.html +1 -0
  110. package/public/sections/files/parts/files-blob-status.ok.js +1 -0
  111. package/public/sections/files/parts/files-collection-list.ok.js +1 -0
  112. package/public/sections/files/parts/files-collection-panel.ok.js +1 -0
  113. package/public/sections/files/parts/files-file-list.ok.js +1 -0
  114. package/public/sections/files/parts/files-overview.ok.js +1 -0
  115. package/public/sections/files/parts/files-path-browser.ok.js +1 -0
  116. package/public/sections/files/parts/files-upload-panel.ok.js +1 -0
  117. package/public/sections/functions/index.ok.html +1 -0
  118. package/public/sections/functions/modals/function-modal.ok.js +1 -0
  119. package/public/sections/functions/parts/function-detail.ok.js +1 -0
  120. package/public/sections/functions/parts/function-logs.ok.js +1 -0
  121. package/public/sections/functions/parts/function-runner.ok.js +1 -0
  122. package/public/sections/functions/parts/functions-list.ok.js +1 -0
  123. package/public/sections/license/index.ok.html +1 -0
  124. package/public/sections/license/parts/license-panel.ok.js +1 -0
  125. package/public/sections/processors/index.ok.html +1 -0
  126. package/public/sections/queue/index.ok.html +1 -0
  127. package/public/sections/queue/modals/create-bucket-modal.ok.js +1 -0
  128. package/public/sections/queue/modals/edit-bucket-modal.ok.js +1 -0
  129. package/public/sections/queue/modals/edit-job-modal.ok.js +1 -0
  130. package/public/sections/queue/modals/enqueue-job-modal.ok.js +1 -0
  131. package/public/sections/queue/modals/job-type-modal.ok.js +1 -0
  132. package/public/sections/queue/parts/ok-job-form.ok.js +1 -0
  133. package/public/sections/queue/parts/queue-buckets.ok.js +1 -0
  134. package/public/sections/queue/parts/queue-job-types.ok.js +1 -0
  135. package/public/sections/queue/parts/queue-jobs.ok.js +1 -0
  136. package/public/sections/queue/parts/queue-types.ok.js +1 -0
  137. package/public/sections/queue/parts/queue-workers.ok.js +1 -0
  138. package/public/sections/sync/index.ok.html +5 -0
  139. package/public/sections/sync/parts/sync-node-detail.ok.js +1 -0
  140. package/public/sections/sync/parts/sync-retention.ok.js +1 -0
  141. package/public/sections/sync/parts/sync-topology.ok.js +1 -0
  142. package/public/sections/system/index.ok.html +1 -0
  143. package/public/sections/system/modals/data-ops-modal.ok.js +1 -0
  144. package/public/sections/system/parts/system-api-panel.ok.js +1 -0
  145. package/public/sections/system/parts/system-auth-panel.ok.js +1 -0
  146. package/public/sections/system/parts/system-log-history.ok.js +1 -0
  147. package/public/sections/system/parts/system-log.ok.js +1 -0
  148. package/public/sections/system/parts/system-status.ok.js +1 -0
  149. package/public/sections/system/parts/system-sync-panel.ok.js +1 -0
  150. package/public/sections/views/index.ok.html +1 -0
  151. package/public/sections/views/modals/create-view-modal.ok.js +1 -0
  152. package/public/sections/views/parts/view-detail.ok.js +1 -0
  153. package/public/sections/views/parts/views-list.ok.js +1 -0
  154. package/public/sections/views/parts/views-overview.ok.js +1 -0
  155. package/public/setup-app.js +1 -0
  156. package/public/setup.html +1 -0
  157. package/public/site.webmanifest +1 -0
  158. package/public/stores/auth-store.js +1 -0
  159. package/public/stores/events-store.js +1 -0
  160. package/public/stores/functions-store.js +1 -0
  161. package/public/stores/okdb-store.js +1 -0
  162. package/public/stores/theme-store.js +1 -0
  163. package/public/styles.css +1 -0
  164. package/public/test-cron-input.html +10 -0
  165. package/public/test-json-input.html +101 -0
  166. package/public/util/permissions.js +1 -0
  167. package/public/util/sift-schema.js +1 -0
  168. package/public/vendor/okjs/ok.esm.min.js +2 -0
  169. package/public/vendor/okjs/toolkit/behavior/async.ok.js +1 -0
  170. package/public/vendor/okjs/toolkit/components/ok-chart.ok.js +1 -0
  171. package/public/vendor/okjs/toolkit/components/ok-doc-viewer.ok.js +1 -0
  172. package/public/vendor/okjs/toolkit/components/ok-marked.ok.js +1 -0
  173. package/public/vendor/okjs/toolkit/components/ok-monaco.ok.js +1 -0
  174. package/public/vendor/okjs/toolkit/core/ok-layer-service.ok.js +1 -0
  175. package/public/vendor/okjs/toolkit/core/ok-layer.ok.js +1 -0
  176. package/public/vendor/okjs/toolkit/core/ok-overlay.ok.js +1 -0
  177. package/public/vendor/okjs/toolkit/core/ok-sandbox.ok.js +1 -0
  178. package/public/vendor/okjs/toolkit/core/transition.ok.js +1 -0
  179. package/public/vendor/okjs/toolkit/core/virtualized.ok.js +1 -0
  180. package/public/vendor/okjs/toolkit/devtools/ok-console-service.ok.js +1 -0
  181. package/public/vendor/okjs/toolkit/devtools/ok-console.ok.js +1 -0
  182. package/public/vendor/okjs/toolkit/devtools/ok-fps-monitor-service.ok.js +1 -0
  183. package/public/vendor/okjs/toolkit/devtools/ok-fps-monitor.ok.js +1 -0
  184. package/public/vendor/okjs/toolkit/devtools/ok-inspector-service.ok.js +1 -0
  185. package/public/vendor/okjs/toolkit/devtools/ok-inspector.ok.js +1 -0
  186. package/public/vendor/okjs/toolkit/devtools/ok-js-tree.ok.js +1 -0
  187. package/public/vendor/okjs/toolkit/devtools/ok-timeline-service.ok.js +1 -0
  188. package/public/vendor/okjs/toolkit/devtools/ok-timeline.ok.js +1 -0
  189. package/public/vendor/okjs/toolkit/docs/doc-viewer.ok.js +1 -0
  190. package/public/vendor/okjs/toolkit/editor/ok-live-component.ok.js +1 -0
  191. package/public/vendor/okjs/toolkit/editor/ok-live-example.ok.js +1 -0
  192. package/public/vendor/okjs/toolkit/editor/ok-live-shell.ok.js +1 -0
  193. package/public/vendor/okjs/toolkit/elements/ok-dropdown.ok.js +1 -0
  194. package/public/vendor/okjs/toolkit/elements/ok-icon.ok.js +1 -0
  195. package/public/vendor/okjs/toolkit/elements/ok-select.ok.js +1 -0
  196. package/public/vendor/okjs/toolkit/elements/ok-table.ok.js +1 -0
  197. package/public/vendor/okjs/toolkit/errors/ok-crash-boundary.ok.js +1 -0
  198. package/public/vendor/okjs/toolkit/errors/ok-crash-info.ok.js +1 -0
  199. package/public/vendor/okjs/toolkit/i18n/i18n-editor-examples.ok.js +1 -0
  200. package/public/vendor/okjs/toolkit/i18n/i18n-editor.ok.js +1 -0
  201. package/public/vendor/okjs/toolkit/i18n/i18n-key-editor-tips.ok.js +1 -0
  202. package/public/vendor/okjs/toolkit/i18n/i18n-key-editor.ok.js +1 -0
  203. package/public/vendor/okjs/toolkit/i18n/i18n-service.ok.js +1 -0
  204. package/public/vendor/okjs/toolkit/i18n/ok-i18n.js +1 -0
  205. package/public/vendor/okjs/toolkit/interaction/ok-contextmenu.ok.js +1 -0
  206. package/public/vendor/okjs/toolkit/interaction/ok-dismiss-service.ok.js +1 -0
  207. package/public/vendor/okjs/toolkit/interaction/ok-draggable.ok.js +1 -0
  208. package/public/vendor/okjs/toolkit/interaction/ok-flyover-panel.ok.js +1 -0
  209. package/public/vendor/okjs/toolkit/interaction/ok-flyover.ok.js +1 -0
  210. package/public/vendor/okjs/toolkit/interaction/ok-highlight-service.ok.js +1 -0
  211. package/public/vendor/okjs/toolkit/interaction/ok-highlight.ok.js +1 -0
  212. package/public/vendor/okjs/toolkit/interaction/ok-hotkeys-service.ok.js +1 -0
  213. package/public/vendor/okjs/toolkit/interaction/ok-picker-service.ok.js +1 -0
  214. package/public/vendor/okjs/toolkit/interaction/ok-popout-service.ok.js +1 -0
  215. package/public/vendor/okjs/toolkit/interaction/ok-popout.ok.js +1 -0
  216. package/public/vendor/okjs/toolkit/interaction/ok-portal-service.ok.js +1 -0
  217. package/public/vendor/okjs/toolkit/interaction/ok-submenu.ok.js +1 -0
  218. package/public/vendor/okjs/toolkit/layout/ok-resizer.ok.js +1 -0
  219. package/public/vendor/okjs/toolkit/manifest.js +1 -0
  220. package/public/vendor/okjs/toolkit/modals/ok-modal-service.ok.js +1 -0
  221. package/public/vendor/okjs/toolkit/modals/ok-modal.ok.js +1 -0
  222. package/public/vendor/okjs/toolkit/modals/ok-toast-service.ok.js +1 -0
  223. package/public/vendor/okjs/toolkit/modals/ok-window.ok.js +1 -0
  224. package/public/vendor/okjs/toolkit/navigation/hash-router.ok.js +1 -0
  225. package/public/vendor/okjs/toolkit/navigation/history-router.ok.js +1 -0
  226. package/public/vendor/okjs/toolkit/navigation/router.ok.js +1 -0
  227. package/public/vendor/okjs/toolkit/sync/reactive-sync-service.ok.js +1 -0
  228. package/public/vendor/okjs/toolkit/sync/reactive-sync.js +1 -0
  229. package/public/vendor/okjs/toolkit/theming/blocks/ok-design-button.css.js +1 -0
  230. package/public/vendor/okjs/toolkit/theming/blocks/ok-design-input.css.js +1 -0
  231. package/public/vendor/okjs/toolkit/theming/ok-design-style.js +1 -0
  232. package/public/vendor/okjs/toolkit/theming/ok-design-vars.js +1 -0
  233. package/public/vendor/okjs/toolkit/theming/ok-design.ok.js +1 -0
  234. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-input.ok.js +1 -0
  235. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-service.ok.js +1 -0
  236. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-style.js +1 -0
  237. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-utils.js +1 -0
  238. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-variable.ok.js +1 -0
  239. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor-vm.js +1 -0
  240. package/public/vendor/okjs/toolkit/theming/ok-theme-editor/ok-theme-editor.ok.js +1 -0
  241. package/public/vendor/okjs/toolkit/theming/ok-theme.ok.js +1 -0
  242. package/public/view-models/type-query-vm.js +1 -0
  243. package/types/environment.d.ts +130 -0
  244. package/types/errors.d.ts +98 -0
  245. package/types/features/admin.d.ts +4 -0
  246. package/types/features/api.d.ts +18 -0
  247. package/types/features/auth.d.ts +31 -0
  248. package/types/features/embeddings.d.ts +39 -0
  249. package/types/features/engines.d.ts +23 -0
  250. package/types/features/files.d.ts +29 -0
  251. package/types/features/fts.d.ts +25 -0
  252. package/types/features/functions.d.ts +23 -0
  253. package/types/features/mcp.d.ts +17 -0
  254. package/types/features/pipelines.d.ts +19 -0
  255. package/types/features/queue.d.ts +189 -0
  256. package/types/features/sync.d.ts +34 -0
  257. package/types/features/time-machine.d.ts +32 -0
  258. package/types/features/views.d.ts +16 -0
  259. package/types/index.d.ts +153 -0
  260. package/types/options.d.ts +202 -0
@@ -0,0 +1,169 @@
1
+ # Change Log & Events
2
+
3
+ Every write in OKDB — put, remove, registerType, registerIndex — increments a monotonic integer **clock** and appends an entry to the change log. This is the backbone of sync, the processor system, and reactive change handling.
4
+
5
+ ---
6
+
7
+ ## The clock
8
+
9
+ A per-environment monotonic integer. Starts at 0, increments by 1 on every write transaction.
10
+
11
+ ```javascript
12
+ // Current clock value
13
+ const clock = okdb.getClock();
14
+
15
+ // Per-type clock (highest clock seen for that type)
16
+ const usersClock = okdb.getClock('users');
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Reading the change log
22
+
23
+ ```javascript
24
+ // Get changes between clock 100 and 200 (both inclusive)
25
+ const changes = okdb.getChanges(null, 100, 200);
26
+ for (const change of changes) {
27
+ console.log(change);
28
+ // {
29
+ // clock: 142,
30
+ // id: 'item:users@alice',
31
+ // type: 'users',
32
+ // key: 'alice',
33
+ // action: 'put', // 'put' | 'remove' | 'type_register' | 'type_drop' | 'index_register' | 'index_drop'
34
+ // timestamp: 1709900000000, // HLC value (wall-clock ms + logical counter)
35
+ // origin: 'node-uuid',
36
+ // txnId: 'txn-uuid',
37
+ // }
38
+ }
39
+
40
+ // Changes for a specific type only
41
+ const userChanges = okdb.getChanges('users', 0, okdb.getClock());
42
+ ```
43
+
44
+ :::note Deduplication
45
+ The change log keeps only the **most recent** entry per primary key. If you write to `users/alice` three times, only the last write appears in the log. This is intentional — `getChanges` returns "what is the current state delta", not a full history.
46
+ :::
47
+
48
+ ---
49
+
50
+ ## Events
51
+
52
+ OKDB emits events on `okdb.events` (a standard Node.js `EventEmitter`) after each transaction commits.
53
+
54
+ ### Data events
55
+
56
+ ```javascript
57
+ okdb.events.on('item:create', ({ type, key, value, clock, origin }) => {
58
+ console.log(`Created ${type}/${key}`);
59
+ });
60
+
61
+ okdb.events.on('item:update', ({ type, key, value, oldValue, clock }) => {
62
+ console.log(`Updated ${type}/${key}`);
63
+ });
64
+
65
+ okdb.events.on('item:remove', ({ type, key, oldValue, clock }) => {
66
+ console.log(`Removed ${type}/${key}`);
67
+ });
68
+ ```
69
+
70
+ ### System events
71
+
72
+ ```javascript
73
+ okdb.events.on('system:clock_change', ({ clock, type }) => { ... });
74
+ okdb.events.on('system:clock_change@users', ({ clock }) => { ... }); // type-specific
75
+ okdb.events.on('system:ready', (okdb) => { ... });
76
+ okdb.events.on('system:stopped', () => { ... });
77
+ okdb.events.on('env:opened', ({ name }) => { ... });
78
+ ```
79
+
80
+ Events fire **after** the LMDB transaction is durably committed. Listeners never see speculative data.
81
+
82
+ :::warning Backpressure
83
+ Events are emitted synchronously via `EventEmitter`. A slow listener blocks subsequent event delivery. For heavy processing, use the **Processor** system instead.
84
+ :::
85
+
86
+ ---
87
+
88
+ ## Processor
89
+
90
+ The Processor is OKDB's reactive change handler — a composable, configurable, inline-or-queue mechanism for reacting to type mutations.
91
+
92
+ Think of it as event listeners with superpowers: they can filter by origin, run inline or async, and are managed as named, stoppable handlers.
93
+
94
+ ### Registering a handler
95
+
96
+ ```javascript
97
+ const stop = okdb.env('default').processor.register('orders', {
98
+ name: 'orders:fulfillment', // human-readable name
99
+ mode: 'inline', // 'inline' = synchronous, post-commit
100
+ originMode: 'all', // 'all' | 'local' | 'remote'
101
+
102
+ handler(changes) {
103
+ for (const ch of changes) {
104
+ if (ch.action !== 'put') continue;
105
+ if (ch.newValue.status === 'paid') {
106
+ scheduleShipment(ch.key, ch.newValue);
107
+ }
108
+ }
109
+ },
110
+ });
111
+
112
+ // Stop the handler
113
+ stop();
114
+ ```
115
+
116
+ ### Change object shape
117
+
118
+ ```javascript
119
+ {
120
+ type: 'orders',
121
+ key: 'o42',
122
+ action: 'put', // 'put' | 'remove'
123
+ oldValue: { ... }, // previous value (null if new)
124
+ newValue: { ... }, // new value (null if removed)
125
+ clock: 182,
126
+ origin: 'node-uuid',
127
+ txnId: 'txn-uuid',
128
+ }
129
+ ```
130
+
131
+ ### originMode
132
+
133
+ | Value | Fires when |
134
+ | ---------- | ------------------------------------ |
135
+ | `'all'` | Any write, local or synced |
136
+ | `'local'` | Only writes originating on this node |
137
+ | `'remote'` | Only writes arriving via sync |
138
+
139
+ This is useful for reacting differently to local vs. replicated changes — e.g., sending a notification only when your own node creates a record, not when it receives one from a peer.
140
+
141
+ ### Processor on a custom env
142
+
143
+ Each environment has its own processor:
144
+
145
+ ```javascript
146
+ const env = okdb.env('analytics');
147
+ const stop = env.processor.register('events', {
148
+ mode: 'inline',
149
+ originMode: 'all',
150
+ name: 'analytics:aggregate',
151
+ handler(changes) {
152
+ /* ... */
153
+ },
154
+ });
155
+ ```
156
+
157
+ ---
158
+
159
+ ## HLC timestamps
160
+
161
+ The `timestamp` field on change objects is a **Hybrid Logical Clock** value: a 64-bit integer encoding both wall-clock milliseconds and a logical counter. HLC values are monotonically increasing even when system clocks skew backwards, which makes them safe for LWW conflict resolution in sync.
162
+
163
+ ```javascript
164
+ // Decode an HLC value
165
+ const { OKDBHlc } = require('okdb/okdb-hlc');
166
+ const { wallMs, logical } = OKDBHlc.decode(change.timestamp);
167
+ ```
168
+
169
+ You don't need to manage HLC values manually — OKDB handles them transparently.
@@ -0,0 +1,174 @@
1
+ # Data Model
2
+
3
+ ## Types
4
+
5
+ A **type** is a named collection of records — similar to a table in SQL or a collection in MongoDB.
6
+
7
+ ```javascript
8
+ await okdb.registerType('articles');
9
+ ```
10
+
11
+ Each type gets its own set of LMDB sub-databases: one for data, one for each secondary index, and one for FTS index metadata. All sub-databases live inside the same LMDB environment (directory) and participate in the same transactions.
12
+
13
+ ### Type operations
14
+
15
+ ```javascript
16
+ // Register (throws if already exists)
17
+ await okdb.registerType('articles');
18
+
19
+ // Idempotent register + index setup
20
+ await okdb.ensureType('articles', {
21
+ indexes: [['author'], ['author', 'publishedAt'], { fields: ['location'], type: 'geo' }],
22
+ });
23
+
24
+ // Check existence
25
+ okdb.hasType('articles'); // → boolean
26
+
27
+ // Drop (deletes all records, indexes, and sub-databases)
28
+ await okdb.dropType('articles');
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Records
34
+
35
+ A **record** is a key-value pair inside a type.
36
+
37
+ - **Key**: a non-empty string (your choice — UUID, slug, user ID, etc.)
38
+ - **Value**: any JSON-serialisable value
39
+
40
+ ```javascript
41
+ await okdb.put('articles', 'how-lmdb-works', {
42
+ title: 'How LMDB works',
43
+ author: 'alice',
44
+ body: '...',
45
+ publishedAt: Date.now(),
46
+ tags: ['database', 'internals'],
47
+ });
48
+ ```
49
+
50
+ Values can also be direct JSON scalars or arrays (`"hello"`, `42`, `true`, `null`, `[]`). Those values are stored and read back normally. However, object-oriented features such as `patch()` and observed field schema only apply to plain object records.
51
+
52
+ Values are stored with LZ4 compression by default. The raw bytes are LMDB's [ordered-binary](https://github.com/DoctorEvidence/ordered-binary) codec, which preserves type ordering for secondary indexes.
53
+
54
+ ### Versions
55
+
56
+ Every record implicitly has a **version** — an integer that monotonically increases with each write. OKDB uses LMDB's `useVersions` feature for optimistic concurrency.
57
+
58
+ ```javascript
59
+ const entry = okdb.getEntry('articles', 'how-lmdb-works');
60
+ // entry = { value: {...}, version: 3 }
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Environments
66
+
67
+ An **environment** is an isolated LMDB directory. OKDB opens several by default and you can create more.
68
+
69
+ ### Built-in environments
70
+
71
+ | Name | Purpose | Synced |
72
+ | ------------------- | --------------------------- | ------ |
73
+ | `~system` | Node identity, env registry | yes |
74
+ | `default` | Your types and data | yes |
75
+ | `~<env>:emb:<type>` | Per-type embedding vectors | yes |
76
+
77
+ :::note
78
+ `queue` and `files` live inside each environment — each env has its own queue and file store.
79
+ :::
80
+
81
+ ### Creating custom environments
82
+
83
+ ```javascript
84
+ // Create a new named environment
85
+ const analyticsEnv = await okdb.createEnvironment('analytics', {
86
+ sync: true, // include in multi-node sync (default: true)
87
+ });
88
+
89
+ // It has the same API as okdb itself
90
+ await analyticsEnv.registerType('events');
91
+ await analyticsEnv.put('events', 'e1', { action: 'click', ts: Date.now() });
92
+ const evt = analyticsEnv.get('events', 'e1');
93
+ ```
94
+
95
+ Accessing an environment:
96
+
97
+ ```javascript
98
+ okdb.env('default'); // the default env
99
+ okdb.env('analytics'); // custom env by name
100
+ okdb.default; // shorthand for okdb.env('default')
101
+ ```
102
+
103
+ ### When to use multiple environments
104
+
105
+ - Separate high-churn data from user data (e.g. analytics events vs. user records)
106
+ - Independent encryption keys per dataset
107
+ - Selective sync (set `sync: false` for data that should stay local)
108
+ - Physical isolation for compaction or backup
109
+
110
+ ---
111
+
112
+ ## Storage layout
113
+
114
+ OKDB creates this structure on disk:
115
+
116
+ ```
117
+ mydb/
118
+ ~system/ ← node identity + env registry
119
+ default/ ← your types and records
120
+ ~<env>:emb:<type>/← per-type vector store
121
+ ~fts/ ← FTS indexes (one sub-dir per index)
122
+ blobs/ ← file attachment blobs (SHA-256 content-addressable)
123
+ ```
124
+
125
+ Inside each LMDB environment, sub-databases are named by convention:
126
+
127
+ ```
128
+ "clock" → global monotonic integer
129
+ "types" → type metadata (field schema, FTS config)
130
+ "changes" → changeId → latest clock
131
+ "clocks" → clock → change object (the event log)
132
+
133
+ per type T:
134
+ "type:T" → lifecycle records
135
+ "type:T:data" → key → value (useVersions=true)
136
+ "type:T:index" → index metadata
137
+ "type:T:index:I" → dupSort ordered-binary index entries
138
+ "type:T:clockToChange" → clock → changeId
139
+ "type:T:keyToChange" → key → changeId
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Field schema
145
+
146
+ OKDB observes field types as you write data and maintains a coarse schema (`string | number | boolean | null | array | object`) per field. You can inspect it:
147
+
148
+ ```javascript
149
+ const desc = await okdb.meta.describeType('articles');
150
+ // {
151
+ // count: 42,
152
+ // fields: { title: 'string', publishedAt: 'number', tags: 'array', ... },
153
+ // indexes: ['author', 'author~publishedAt'],
154
+ // ftsIndexes: []
155
+ // }
156
+ ```
157
+
158
+ Schema is **observed-only** — no validation on write. If you need strict validation, do it at the application layer before calling `put`.
159
+
160
+ ---
161
+
162
+ ## Durability modes
163
+
164
+ Set at construction time via the `durability` option:
165
+
166
+ | Mode | Behaviour | Use when |
167
+ | ---------- | -------------------------------------------------------- | ------------------------------ |
168
+ | `strict` | Full fsync on every write | Mission-critical, money |
169
+ | `balanced` | `overlappingSync` — background sync, still safe on crash | Default; most use cases |
170
+ | `fast` | `noSync` — OS controls flush timing | Dev, caches, re-generable data |
171
+
172
+ ```javascript
173
+ const okdb = new OKDB('./db', { durability: 'fast' });
174
+ ```
@@ -0,0 +1,338 @@
1
+ # Embeddings
2
+
3
+ OKDB has a built-in vector embedding pipeline. Point it at a type, tell it which field to embed and which ML model to use, and it handles the rest: watching the change log, calling the embedder, storing vectors, and serving nearest-neighbour queries.
4
+
5
+ ---
6
+
7
+ ## Concepts
8
+
9
+ | Component | Role |
10
+ | ----------------- | ----------------------------------------------------- |
11
+ | **Embedder** | Connection to an ML model (Ollama, OpenAI, or custom) |
12
+ | **Indexer** | Watches a type's change log, embeds changed docs |
13
+ | **Vector store** | Persists `Float32Array` vectors in LMDB |
14
+ | **Search engine** | In-memory HNSW graph for nearest-neighbour queries |
15
+
16
+ These are wired together as **named engines** — persistent service instances whose config lives in OKDB and survive restarts.
17
+
18
+ Embeddings is now also a **built-in pipeline family** on top of the generic `~pipelines` feature:
19
+
20
+ - `okdb.embeddings.createPipeline(...)` still provides the specialized one-call setup
21
+ - the created topology is also persisted as an env-local generic pipeline record
22
+ - the generic record stores explicit ordered member roles (`embedder`, `indexer`, optional `worker`, `search`)
23
+ - embeddings HTTP/admin views read those generic records first and only fall back to legacy engine grouping for older pipelines
24
+ - starter templates now exist too: `GET /api/pipelines/templates` exposes recommended embeddings pipeline blueprints such as `embeddings-inline-fake`, `embeddings-inline-ollama`, `embeddings-queue-fake`, and `embeddings-queue-ollama`
25
+ - MCP clients can discover and expand those blueprints through `okdb_pipeline` actions `template_list`, `template_get`, `template_preview`, and `template_create`
26
+
27
+ ---
28
+
29
+ ## Quick start with `createPipeline`
30
+
31
+ The one-call setup that provisions all four components:
32
+
33
+ ```javascript
34
+ await okdb.embeddings.createPipeline('articles', {
35
+ source_type: 'articles', // the OKDB type to watch
36
+ field: 'body', // which field to embed (null = stringify whole record)
37
+ dims: 1024, // embedding dimensions (must match model)
38
+ embedder: {
39
+ type: 'ollama',
40
+ model: 'mxbai-embed-large',
41
+ url: 'http://localhost:11434',
42
+ },
43
+ });
44
+ ```
45
+
46
+ After this call, any `put` to `articles` will automatically trigger embedding. Vectors are stored in the `~default:emb:articles` environment.
47
+
48
+ The specialized embeddings facade now also writes a generic `~pipelines` record in the source environment. By default:
49
+
50
+ - the generic pipeline record name is the pipeline `storage_key`
51
+ - `meta.label` preserves the user-facing embeddings pipeline label
52
+ - `meta.family === 'embeddings'` marks the record as embeddings-backed
53
+
54
+ So a call like:
55
+
56
+ ```javascript
57
+ await okdb.embeddings.createPipeline('articles-body-pipeline', {
58
+ source_env: 'default',
59
+ source_type: 'articles',
60
+ storage_key: 'articles-body',
61
+ field: 'body',
62
+ dims: 768,
63
+ embedder: { type: 'ollama', model: 'mxbai-embed-large' },
64
+ });
65
+ ```
66
+
67
+ creates engines plus a generic pipeline record named `articles-body` in `default/~pipelines`.
68
+
69
+ This `storage_key` behavior matters when you use the new template registry too:
70
+
71
+ - the starter input usually asks for a human-facing `name`
72
+ - embeddings template plans default `storage_key` to that same `name`
73
+ - if you override `storage_key`, the persisted generic pipeline record is stored under that value instead
74
+ - `meta.label` still keeps the original display label so admin/MCP clients can show a friendly name
75
+
76
+ ---
77
+
78
+ ## Templates and discovery
79
+
80
+ Embeddings templates are presets for the same underlying `okdb.embeddings.createPipeline(...)` flow.
81
+
82
+ You can discover them over HTTP:
83
+
84
+ ```text
85
+ GET /api/pipelines/templates
86
+ GET /api/pipelines/templates/:template
87
+ POST /api/pipelines/templates/:template/preview
88
+ POST /api/env/:env/pipelines/templates/:template/create
89
+ ```
90
+
91
+ Built-in recommended embeddings pipeline starters currently include:
92
+
93
+ - `embeddings-inline-fake`
94
+ - `embeddings-inline-ollama`
95
+ - `embeddings-queue-fake`
96
+ - `embeddings-queue-ollama`
97
+
98
+ The preview route expands one of those starters into the exact `embeddings-create` plan it would execute, including the derived `storage_key`, `mode`, search `algorithm`, and provider config.
99
+
100
+ `POST /api/env/:env/pipelines/templates/:template/create` then executes that plan by calling the specialized embeddings facade and returning the resulting generic pipeline record.
101
+
102
+ If you want to build the pieces separately, the shared engine template registry also exposes direct embeddings engine starters through the engine API:
103
+
104
+ ```text
105
+ GET /api/engines/types
106
+ GET /api/engines/templates
107
+ GET /api/engines/templates/:template
108
+ POST /api/engines/templates/:template/preview
109
+ POST /api/env/:env/engines/templates/:template/create
110
+ ```
111
+
112
+ That shared registry includes starter definitions such as:
113
+
114
+ - `embedder-fake`, `embedder-ollama`, `embedder-openai`
115
+ - `indexer-inline`, `indexer-queue`
116
+ - `vector-search-flat`, `vector-search-hnsw`
117
+ - `embed-worker-basic`
118
+
119
+ So the usual choice is:
120
+
121
+ - use pipeline templates when you want the full recommended embeddings topology in one step
122
+ - use engine templates when you want to compose or manage the embeddings engines individually
123
+
124
+ ### MCP discovery and creation
125
+
126
+ Embeddings-related MCP behavior is now split across three grouped tools:
127
+
128
+ - `okdb_pipeline` — use `template_list`, `template_get`, `template_preview`, and `template_create` for full embeddings pipeline starters
129
+ - `okdb_engine` — use `types`, `templates`, `template_get`, `template_preview`, and `template_create` for direct engine discovery and creation
130
+ - `okdb_embeddings` — use `providers`, `models`, `models_by_provider`, `algorithms`, `probe_ollama_models`, and `embedder_models` to inspect provider schemas and model catalogs before choosing a template or creating engines manually
131
+
132
+ This makes it possible for MCP clients to both discover recommendations and actually execute them, rather than relying on out-of-band docs.
133
+
134
+ ---
135
+
136
+ ## Built-in embedder types
137
+
138
+ ### Ollama (local)
139
+
140
+ ```javascript
141
+ const embedder = {
142
+ type: 'ollama',
143
+ model: 'mxbai-embed-large', // or 'nomic-embed-text', etc.
144
+ url: 'http://localhost:11434',
145
+ };
146
+ ```
147
+
148
+ Requires [Ollama](https://ollama.ai) running locally:
149
+
150
+ ```bash
151
+ ollama pull mxbai-embed-large
152
+ ```
153
+
154
+ ### OpenAI (and compatible APIs)
155
+
156
+ ```javascript
157
+ const embedder = {
158
+ type: 'openai',
159
+ model: 'text-embedding-3-small',
160
+ api_key: process.env.OPENAI_API_KEY,
161
+ // base_url: 'https://api.openai.com/v1' (default)
162
+ };
163
+ ```
164
+
165
+ ### Custom / fake
166
+
167
+ For development and testing without a real ML service:
168
+
169
+ ```javascript
170
+ okdb.embeddings.registerEmbedderFactory('my-embedder', (config) => ({
171
+ async embed(input) {
172
+ // Return a Float32Array of `config.dims` dimensions
173
+ return new Float32Array(config.dims).fill(0.5);
174
+ },
175
+ async health() {
176
+ return { ok: true };
177
+ },
178
+ }));
179
+
180
+ // Then use it in a pipeline:
181
+ await okdb.embeddings.createPipeline('articles', {
182
+ source_type: 'articles',
183
+ dims: 128,
184
+ embedder: { type: 'my-embedder' },
185
+ });
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Semantic search
191
+
192
+ `createPipeline` returns a pipeline object. Use `pipeline.api.search()` to run nearest-neighbour queries:
193
+
194
+ ```javascript
195
+ const pipeline = await okdb.embeddings.createPipeline('articles', {
196
+ source_type: 'articles',
197
+ field: 'body',
198
+ dims: 1024,
199
+ embedder: { type: 'ollama', model: 'mxbai-embed-large' },
200
+ });
201
+
202
+ // Text query — the pipeline embeds it then finds nearest neighbours
203
+ const results = await pipeline.api.search('database performance tuning', { limit: 5 });
204
+ for (const { key, score } of results) {
205
+ console.log(key, score.toFixed(4));
206
+ }
207
+
208
+ // Float32Array query — skip the embed step
209
+ const vec = new Float32Array(1024).fill(0.1);
210
+ const results2 = await pipeline.api.search(vec, { limit: 10, threshold: 0.7 });
211
+ ```
212
+
213
+ ### Search options
214
+
215
+ | Option | Default | Description |
216
+ | ----------- | ------- | ------------------------------ |
217
+ | `limit` | 10 | Maximum results |
218
+ | `threshold` | 0 | Minimum similarity score (0–1) |
219
+
220
+ ---
221
+
222
+ ## Pipeline modes
223
+
224
+ ### Inline (default)
225
+
226
+ The indexer calls the embedder synchronously in the change-log processor. Simple, no extra infrastructure:
227
+
228
+ ```javascript
229
+ await okdb.embeddings.createPipeline('articles', {
230
+ source_type: 'articles',
231
+ field: 'body',
232
+ dims: 1024,
233
+ mode: 'inline', // default
234
+ embedder: { type: 'ollama', model: 'mxbai-embed-large' },
235
+ });
236
+ ```
237
+
238
+ ### Queue mode
239
+
240
+ For high-throughput or external embedder services, the indexer enqueues jobs and a worker pool consumes them concurrently:
241
+
242
+ ```javascript
243
+ await okdb.embeddings.createPipeline('articles', {
244
+ source_type: 'articles',
245
+ field: 'body',
246
+ dims: 1024,
247
+ mode: 'queue',
248
+ embedder: { type: 'openai', model: 'text-embedding-3-small', api_key: '...' },
249
+ worker: {
250
+ concurrency: 4,
251
+ pollInterval: 500,
252
+ ttl: 30_000,
253
+ },
254
+ });
255
+ ```
256
+
257
+ Queue-mode embeddings records keep the same explicit membership order in the generic pipeline registry, with the worker inserted between the indexer and search members.
258
+
259
+ ---
260
+
261
+ ## Waiting for indexing to complete
262
+
263
+ ```javascript
264
+ // For a pipeline named 'articles' in the default env, the scoped key is 'default:articles'
265
+ const indexer = okdb.embeddings.indexer('default:articles');
266
+ const stats = await indexer.stats();
267
+ // { total, pending, embedded, failed }
268
+
269
+ // Pipeline api.stats() wraps all four components
270
+ const { api } = await okdb.embeddings.createPipeline('articles', { ... });
271
+ const allStats = await api.stats();
272
+ // { embedder: {...}, indexer: {...}, worker: {...}, search: {...} }
273
+
274
+ // Wait for the indexer to finish processing all documents
275
+ function waitForIdle(pipelineKey, timeoutMs = 120_000) {
276
+ return new Promise((resolve, reject) => {
277
+ const timer = setTimeout(() => reject(new Error('timeout')), timeoutMs);
278
+ const check = async () => {
279
+ const api = okdb.embeddings.indexer(pipelineKey);
280
+ if (!api) return;
281
+ const { pending } = await api.stats().catch(() => ({ pending: 1 }));
282
+ if (pending === 0) { clearTimeout(timer); resolve(); }
283
+ };
284
+ okdb.events.on(`embeddings:indexer_flushed@indexer@${pipelineKey}`, check);
285
+ check();
286
+ });
287
+ }
288
+
289
+ await waitForIdle('default:articles');
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Engines
295
+
296
+ Pipelines are built on top of the **engines** feature — a generic lifecycle manager for named, persistent services. Each component (embedder, indexer, search, worker) is an engine with a type, name, config, and status stored in `~engines`.
297
+
298
+ They survive restarts: on `okdb.open()`, all previously-created engines are restored and their `start` methods called.
299
+
300
+ ```javascript
301
+ // List all running engines
302
+ const engines = okdb.engines.list();
303
+ // [{ name, type, status, config, meta }, ...]
304
+
305
+ // Get a specific engine
306
+ const embedder = okdb.engines.getEngine('embedder', 'articles');
307
+ ```
308
+
309
+ ### Shared embedder lifecycle behavior
310
+
311
+ Embeddings pipelines usually own the env-scoped indexer/search/worker engines, but the embedder is often shared across pipelines and environments.
312
+
313
+ To preserve that behavior when delegating lifecycle operations through generic pipelines, embeddings-backed pipeline records mark the embedder role as lifecycle-skipped.
314
+
315
+ That means specialized or generic pipeline `start` / `stop` operations:
316
+
317
+ - manage env-owned `indexer`, `search`, and optional `worker` members
318
+ - do **not** stop or restart the shared embedder just because one pipeline is paused
319
+ - still preserve the shared embedder on pipeline delete unless the embedder is explicitly managed elsewhere
320
+
321
+ ---
322
+
323
+ ## Storage layout
324
+
325
+ ```
326
+ ~default:emb:articles/
327
+ vec:<storage_key> ← Float32Array vectors in LMDB
328
+ doc_status:<storage_key> ← per-document embedding status
329
+
330
+ ~system/
331
+ ~emb:models ← model catalog (shared)
332
+ ```
333
+
334
+ The vector store persists raw `Float32Array` bytes (not JSON), preserving full floating-point precision. The HNSW graph is rebuilt from these stored vectors on each restart.
335
+
336
+ :::note
337
+ Vector data (the raw Float32Array values stored in `vec:` types) **is replicated** via the normal change log — embedding is expensive, so every node receives the computed vectors automatically. The in-memory HNSW search graph is rebuilt locally from these synced vectors on each restart.
338
+ :::