@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,514 @@
1
+ # Custom Functions
2
+
3
+ OKDB custom functions let you store and execute JavaScript routines inside the database with a controlled runtime.
4
+
5
+ They are designed for:
6
+
7
+ - reusable env-local business logic
8
+ - cross-env orchestration (via `unsafe: true`)
9
+ - stable HTTP entry points
10
+ - MCP-accessible orchestration routines
11
+
12
+ Custom functions are **stored**, **versioned**, **validated**, **audited**, and executed in **pooled sandbox runners**.
13
+
14
+ ---
15
+
16
+ ## Mental model
17
+
18
+ Functions run **inside OKDB** with direct access to a live database facade.
19
+
20
+ Recommended starting flow:
21
+
22
+ 1. fetch a canonical starter with `okdb_function` `action: "get_template"`
23
+ 2. use the required shape `async (ctx) => { ... }`
24
+ 3. use `ctx.env` for normal env-scoped work
25
+ 4. use `ctx.env.txn(...)` for batched writes such as seeding 1000 records
26
+ 5. only enable `unsafe: true` when you intentionally need privileged cross-env access through `ctx.okdb`
27
+
28
+ For bulk inserts specifically, the `seed` template is the best starting point.
29
+
30
+ That means a function should **perform writes itself**:
31
+
32
+ ```js
33
+ async (ctx) => {
34
+ await ctx.env.put('products', 'p1', { name: 'Widget' });
35
+ return { ok: true };
36
+ };
37
+ ```
38
+
39
+ It should **not** return a description of work for OKDB to execute later.
40
+
41
+ This is **incorrect**:
42
+
43
+ ```js
44
+ (ctx) => ({
45
+ operations: [{ action: 'put', type: 'products', key: 'p1', value: { name: 'Widget' } }],
46
+ });
47
+ ```
48
+
49
+ If you want batched atomic writes, use `txn(...)`:
50
+
51
+ ```js
52
+ async (ctx) => {
53
+ await ctx.env.txn((txn) => {
54
+ txn.create('products', 'p1', { name: 'Widget' });
55
+ txn.create('products', 'p2', { name: 'Gadget' });
56
+ });
57
+ return { inserted: 2 };
58
+ };
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Every function belongs to an environment
64
+
65
+ All functions live in an env (including those created via `db.functions` — those are stored in the `~system` env).
66
+
67
+ Every function receives `ctx.env` pointing to its home environment.
68
+
69
+ For cross-env or privileged operations, mark the function `unsafe: true` to additionally receive `ctx.okdb`.
70
+
71
+ Most function code should stay on `ctx.env`, not `ctx.okdb`.
72
+
73
+ ---
74
+
75
+ ## Script contract
76
+
77
+ Stored scripts must be a single function expression:
78
+
79
+ ```js
80
+ (ctx) => {};
81
+ async (ctx) => {};
82
+ ```
83
+
84
+ Do **not** destructure the argument like this:
85
+
86
+ ```js
87
+ async ({ ctx }) => {};
88
+ ```
89
+
90
+ The runtime passes a single argument named `ctx`, so the canonical shape is always `async (ctx) => { ... }`.
91
+
92
+ The runtime rejects scripts that use unsupported forms or forbidden symbols.
93
+
94
+ ### Forbidden
95
+
96
+ The current runtime rejects references to:
97
+
98
+ - `require`
99
+ - `import`
100
+ - `console`
101
+ - `module`
102
+ - `exports`
103
+ - `process`
104
+ - `child_process`
105
+ - `worker_threads`
106
+
107
+ Use `ctx.log(...)` instead of `console`.
108
+
109
+ ---
110
+
111
+ ## Runtime model
112
+
113
+ Functions execute in **isolated child processes** (runners), not in the main OKDB process.
114
+
115
+ The pool is **dynamic**:
116
+
117
+ - runners spawn on demand when `functions.run()` is called
118
+ - runners stay warm between invocations — no spawn overhead on repeated calls
119
+ - idle runners are reaped automatically — no wasted resources when quiet
120
+ - one function runs at a time per runner
121
+
122
+ **Per-env isolation**: each environment has its own pool with its own runner processes. Functions in different envs never share a runner.
123
+
124
+ **Pool option**:
125
+
126
+ - `maxRunners` — maximum number of concurrent runners (optional, defaults to unlimited)
127
+
128
+ When using queue-worker engines with `concurrency: N`, the pool grows automatically to match — no manual sizing needed.
129
+
130
+ Other behavior:
131
+
132
+ - scripts are validated and compile-checked before storage
133
+ - timeouts kill and replace the runner
134
+ - memory is capped per runner process
135
+ - every run is written to a run ledger
136
+
137
+ This makes the feature suitable for operational entry points without paying process spawn cost on every call.
138
+
139
+ ---
140
+
141
+ ## Available runtime context
142
+
143
+ Every function receives:
144
+
145
+ - `ctx.payload` — caller-supplied input
146
+ - `ctx.info` — run metadata (function name, env, version, scope, run ID)
147
+ - `ctx.signal` — AbortSignal for timeout handling
148
+ - `ctx.log` — structured logger (see [Logging](#logging))
149
+ - `ctx.env` — live facade for the function's environment
150
+ - `ctx.job` — queue job context _(only when triggered by a queue-worker engine)_
151
+ - `ctx.processor` — processor trigger context _(only when triggered by a processor engine)_
152
+ - `ctx.materializer` — materializer trigger context _(only when triggered by a materializer engine)_
153
+
154
+ ### `ctx.job` (queue-worker engines only)
155
+
156
+ When a function is invoked by a queue-worker engine, `ctx.job` provides
157
+ the queue job metadata and helpers:
158
+
159
+ - `ctx.job.id` — the job ID
160
+ - `ctx.job.type` — the job type
161
+ - `ctx.job.tries` — current attempt number
162
+ - `ctx.job.created` — creation timestamp
163
+ - `ctx.job.tags` — job tags (array or null)
164
+ - `ctx.job.priority` — job priority
165
+ - `ctx.job.bucket` — token bucket ID (or null)
166
+ - `ctx.job.cron` — cron expression (or null)
167
+ - `ctx.job.heartbeat()` — keep the job alive (fire-and-forget)
168
+ - `ctx.job.markProgress(message)` — report progress (fire-and-forget)
169
+
170
+ `ctx.job` is `undefined` for functions triggered via HTTP, SDK, or processor engines.
171
+
172
+ ### `ctx.processor` (processor engines only)
173
+
174
+ When a function is invoked by a processor engine, `ctx.payload` is the batch of
175
+ changes (an array of `{ key, value, action, clock }` objects) and `ctx.processor`
176
+ provides the trigger metadata:
177
+
178
+ - `ctx.processor.mode` — `"snapshot"` (initial backfill) or `"live"` (ongoing)
179
+ - `ctx.processor.sourceType` — the type whose change-log is being consumed
180
+ - `ctx.processor.sourceEnv` — the environment the source type lives in
181
+ - `ctx.processor.processor` — the processor registration ID
182
+ - `ctx.processor.engineKey` — the engine key
183
+ - `ctx.processor.cursorKey` — the cursor key used to track progress
184
+
185
+ `ctx.processor` is `undefined` for functions triggered via HTTP, SDK, or queue-worker engines.
186
+
187
+ Example:
188
+
189
+ ```js
190
+ async (ctx) => {
191
+ // ctx.payload = array of changes
192
+ // ctx.processor = trigger metadata
193
+ await ctx.env.ensureType('article_projection');
194
+ for (const change of ctx.payload) {
195
+ if (change.action === 'put') {
196
+ await ctx.env.put('article_projection', change.key, {
197
+ title: change.value.title,
198
+ mode: ctx.processor.mode,
199
+ sourceEnv: ctx.processor.sourceEnv,
200
+ });
201
+ }
202
+ }
203
+ return { processed: ctx.payload.length };
204
+ };
205
+ ```
206
+
207
+ ### `ctx.materializer` (materializer engines only)
208
+
209
+ When a function is invoked by a materializer engine, `ctx.payload` is the batch of
210
+ changes (same shape as processor) and `ctx.materializer` provides the trigger metadata.
211
+ The function must **return an ops array** — the engine applies those ops to the target type.
212
+ The function must **not** write directly via `ctx.env`.
213
+
214
+ - `ctx.materializer.mode` — `"build"` (backfill / replay) or `"live"` (ongoing consumption)
215
+ - `ctx.materializer.sourceType` — the source type being consumed
216
+ - `ctx.materializer.targetType` — the target type being maintained
217
+ - `ctx.materializer.sourceEnv` — the environment the source type lives in
218
+ - `ctx.materializer.targetEnv` — the environment the target type lives in
219
+ - `ctx.materializer.engineKey` — the engine key
220
+ - `ctx.materializer.cursorKey` — the cursor key used to track progress
221
+
222
+ `ctx.materializer` is `undefined` for functions triggered via HTTP, SDK, queue-worker, or processor engines.
223
+
224
+ The returned ops array shape:
225
+
226
+ ```js
227
+ [
228
+ { action: 'put', key: 'some-key', value: { ... } },
229
+ { action: 'remove', key: 'other-key' },
230
+ ]
231
+ ```
232
+
233
+ Example:
234
+
235
+ ```js
236
+ async (ctx) => {
237
+ // ctx.payload = changes[]
238
+ // ctx.materializer = trigger metadata
239
+ const ops = [];
240
+ for (const change of ctx.payload) {
241
+ if (change.action === 'put') {
242
+ const doc = change.value ?? change.newValue;
243
+ if (!doc) continue;
244
+
245
+ ops.push({
246
+ action: 'put',
247
+ key: change.key,
248
+ value: {
249
+ total: doc.total,
250
+ customerId: doc.customerId,
251
+ updatedAt: Date.now(),
252
+ sourceEnv: ctx.materializer.sourceEnv,
253
+ },
254
+ });
255
+ }
256
+ if (change.action === 'remove') {
257
+ ops.push({ action: 'remove', key: change.key });
258
+ }
259
+ }
260
+ return ops;
261
+ };
262
+ ```
263
+
264
+ Change record notes:
265
+
266
+ - `change.value` is the normalized source document for `put` changes and is the preferred field to read.
267
+ - During snapshot bootstrap, `change.clock` is `null` because there is no original changelog clock for the synthetic snapshot row.
268
+ - `change.newValue` / `change.oldValue` may still be present for low-level compatibility, but materializer functions should prefer `change.value`.
269
+
270
+ `ctx.env` exposes live env methods such as:
271
+
272
+ - CRUD: `put`, `update`, `patch`, `create`, `remove`, `get`, `query`, ...
273
+ - schema/index helpers: `registerType`, `ensureType`, `registerIndex`, ...
274
+ - `transaction(options)` — low-level transaction object
275
+ - `txn(workOrOps, options?)` — convenience write-focused transaction helper
276
+ - `queue` — queue feature for this env
277
+ - `files` — files feature for this env
278
+
279
+ Example:
280
+
281
+ ```js
282
+ async (ctx) => {
283
+ ctx.log('counting records', { env: ctx.env.name });
284
+ return {
285
+ env: ctx.env.name,
286
+ count: ctx.env.getCount('events'),
287
+ };
288
+ };
289
+ ```
290
+
291
+ ### `ctx.env.txn(...)`
292
+
293
+ `txn(...)` is a **write-batch helper**, not a full read-your-own-writes transaction view.
294
+
295
+ Preferred callback form:
296
+
297
+ ```js
298
+ await ctx.env.txn((txn) => {
299
+ txn.create('events', 'e1', { kind: 'view' });
300
+ txn.create('events', 'e2', { kind: 'click' });
301
+ });
302
+ ```
303
+
304
+ Also supported: ops arrays
305
+
306
+ ```js
307
+ await ctx.env.txn([
308
+ { action: 'create', type: 'events', key: 'e1', value: { kind: 'view' } },
309
+ ['create', 'events', 'e2', { kind: 'click' }],
310
+ ]);
311
+ ```
312
+
313
+ The `txn` object intentionally exposes **write methods only**:
314
+
315
+ - `put`
316
+ - `update`
317
+ - `patch`
318
+ - `create`
319
+ - `remove`
320
+ - `commit`
321
+ - `rollback`
322
+
323
+ For advanced callers, `txn.read` / `txn.raw` expose the underlying transaction object,
324
+ including committed-state reads such as `get()` and `query()`.
325
+
326
+ Important: queued writes are **not** visible to `txn.read` reads before commit.
327
+ `txn(...)` guarantees atomic commit/rollback of writes, but it does **not** provide
328
+ an in-memory overlay or transactional read-your-own-writes view.
329
+
330
+ ---
331
+
332
+ ## Unsafe functions
333
+
334
+ By default a function can only access its own environment. Mark a function `unsafe: true` to additionally receive `ctx.okdb` — a privileged root facade. The name `unsafe` reflects the consequence: these functions can reach any env.
335
+
336
+ ```js
337
+ await okdb.env('default').functions.create({
338
+ name: 'copySummary',
339
+ unsafe: true,
340
+ source: `async (ctx) => {
341
+ const src = ctx.okdb.env('default');
342
+ const dst = ctx.okdb.env('analytics');
343
+ const count = src.getCount('events');
344
+ await dst.put('summaries', 'latest', { count });
345
+ return { count };
346
+ }`,
347
+ });
348
+ ```
349
+
350
+ `ctx.okdb` exposes root-level `env(name)` and a root-scoped `txn(...)`:
351
+
352
+ ```js
353
+ async (ctx) => {
354
+ await ctx.okdb.txn([
355
+ { action: 'create', type: 'users', key: 'u1', value: { name: 'Ada' } },
356
+ { action: 'create', type: 'users', key: 'u2', value: { name: 'Grace' } },
357
+ ]);
358
+ return { count: ctx.okdb.getCount('users') };
359
+ };
360
+ ```
361
+
362
+ Use `unsafe: true` only when cross-env access or root-level writes are genuinely required.
363
+
364
+ ---
365
+
366
+ ## Logging
367
+
368
+ Use `ctx.log(...)` for structured function logs. All log levels are available:
369
+
370
+ ```js
371
+ (ctx) => {
372
+ ctx.log('starting run', { function: ctx.info.functionName });
373
+ ctx.log.info('doing work');
374
+ ctx.log.warn('slow path');
375
+ ctx.log.error('something broke', { err: err.message });
376
+ return { ok: true };
377
+ };
378
+ ```
379
+
380
+ Every entry emitted from a function is automatically stamped with run identity in the `meta` field:
381
+
382
+ | `meta` field | Value |
383
+ | --------------- | ------------------------------ |
384
+ | `meta.feature` | `'functions'` |
385
+ | `meta.env` | The environment name |
386
+ | `meta.fn` | The function name |
387
+ | `meta.runId` | Unique ID for this invocation |
388
+ | `meta.runnerId` | Worker slot ID (e.g. `'fn-1'`) |
389
+
390
+ The second argument to any log call becomes the `context` field in the log entry — this is the user-provided data visible in the Admin UI function log panel.
391
+
392
+ Function logs are suppressed from the console (they are stored in the run record and displayed in the Admin UI). See [Logging](logging.md) for the full entry shape and consumer APIs.
393
+
394
+ ---
395
+
396
+ ## JavaScript API
397
+
398
+ ```js
399
+ const env = okdb.env('analytics');
400
+
401
+ await env.functions.create({
402
+ name: 'countEvents',
403
+ source: '(ctx) => ({ env: ctx.env.name, count: ctx.env.getCount("events") })',
404
+ });
405
+
406
+ const run = await env.functions.run('countEvents');
407
+ console.log(run.status, run.result.value);
408
+ ```
409
+
410
+ For unsafe cross-env functions:
411
+
412
+ ```js
413
+ await okdb.env('default').functions.create({
414
+ name: 'crossEnvCount',
415
+ unsafe: true,
416
+ source: '(ctx) => ({ analytics: ctx.okdb.env("analytics").getCount("events") })',
417
+ });
418
+
419
+ const run = await okdb.env('default').functions.run('crossEnvCount');
420
+ console.log(run.result.value);
421
+ ```
422
+
423
+ ---
424
+
425
+ ## HTTP API
426
+
427
+ All function routes are env-scoped. The `:env` parameter defaults to `default` when omitted.
428
+
429
+ - `GET /api[/env/:env]/functions`
430
+ - `POST /api[/env/:env]/functions`
431
+ - `GET /api[/env/:env]/functions/:name`
432
+ - `PUT /api[/env/:env]/functions/:name`
433
+ - `DELETE /api[/env/:env]/functions/:name`
434
+ - `POST /api[/env/:env]/functions/:name/run`
435
+ - `POST /api[/env/:env]/functions/preview`
436
+ - `GET /api[/env/:env]/functions/:name/runs`
437
+ - `GET /api[/env/:env]/functions/:name/runs/:runId`
438
+
439
+ ### MCP note
440
+
441
+ Function execution is exposed via the grouped `okdb_function_run` MCP tool.
442
+ Use `action: "run"` for stored functions and `action: "preview"` for unsaved drafts.
443
+
444
+ Example preview call:
445
+
446
+ ```json
447
+ {
448
+ "name": "okdb_function_run",
449
+ "arguments": {
450
+ "action": "preview",
451
+ "env": "default",
452
+ "source": "(ctx) => ({ ok: true, env: ctx.env.name })",
453
+ "payload": { "sample": true }
454
+ }
455
+ }
456
+ ```
457
+
458
+ There is no separate dotted MCP method such as `okdb_function_run.preview`.
459
+
460
+ Example — create an unsafe function and run it:
461
+
462
+ ```http
463
+ POST /api/env/analytics/functions
464
+ Authorization: Bearer <token>
465
+ Content-Type: application/json
466
+
467
+ {
468
+ "name": "syncSummary",
469
+ "unsafe": true,
470
+ "source": "(ctx) => ({ count: ctx.okdb.env('default').getCount('users') })"
471
+ }
472
+ ```
473
+
474
+ ```http
475
+ POST /api/env/analytics/functions/syncSummary/run
476
+ Authorization: Bearer <token>
477
+ Content-Type: application/json
478
+
479
+ { "payload": { "sample": true } }
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Validation errors
485
+
486
+ Common error codes:
487
+
488
+ - `FUNCTION_SCRIPT_REQUIRED`
489
+ - `FUNCTION_SCRIPT_TOO_LARGE`
490
+ - `FUNCTION_SCRIPT_INVALID_SHAPE`
491
+ - `FUNCTION_SCRIPT_FORBIDDEN_SYMBOL`
492
+ - `FUNCTION_SCRIPT_COMPILE_FAILED`
493
+ - `FUNCTION_TIMEOUT`
494
+
495
+ ---
496
+
497
+ ## Current limitations
498
+
499
+ Current implementation intentionally does **not** yet include:
500
+
501
+ - precise per-script CPU quotas
502
+ - arbitrary module imports
503
+ - direct console access
504
+ - transactional read-your-own-writes overlays inside `txn(...)`
505
+ - admin UI for functions
506
+
507
+ ---
508
+
509
+ ## See also
510
+
511
+ - [HTTP API](./http-api.md)
512
+ - [Queue](./queue.md)
513
+ - [Files](./files.md)
514
+ - [Plugins](./plugins.md)