@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
package/docs/queue.md ADDED
@@ -0,0 +1,637 @@
1
+ # Queue
2
+
3
+ OKDB has a built-in durable job queue.
4
+
5
+ Queue data lives in the **current environment**:
6
+
7
+ - `okdb.queue` uses the **default** environment
8
+ - `okdb.env('myenv').queue` uses that **custom** environment
9
+
10
+ Jobs survive restart, are stored as normal OKDB records, and can be listed, edited, retried, and processed by workers.
11
+
12
+ No Redis, no RabbitMQ. Just OKDB.
13
+
14
+ ---
15
+
16
+ ## Quick start
17
+
18
+ ```javascript
19
+ const OKDB = require('./okdb');
20
+
21
+ async function main() {
22
+ const okdb = new OKDB('./db');
23
+ await okdb.open();
24
+
25
+ const worker = okdb.queue.worker(
26
+ 'send-email',
27
+ async (payload, ctx) => {
28
+ await ctx.markProgress('sending email');
29
+
30
+ await sendEmail(payload.to, payload.subject, payload.orderId);
31
+
32
+ await ctx.okdb.put('email_log', payload.orderId, {
33
+ to: payload.to,
34
+ sentAt: Date.now(),
35
+ jobId: ctx.jobId,
36
+ });
37
+ },
38
+ {
39
+ concurrency: 4,
40
+ pollInterval: 100,
41
+ ttl: 30_000,
42
+ },
43
+ );
44
+
45
+ const jobId = await okdb.queue.enqueue(
46
+ 'send-email',
47
+ {
48
+ to: 'alice@example.com',
49
+ subject: 'Your order shipped',
50
+ orderId: 'o42',
51
+ },
52
+ {
53
+ max_tries: 3,
54
+ retry_delay: 1000,
55
+ backoff_multiplier: 2,
56
+ },
57
+ );
58
+
59
+ console.log('enqueued', jobId);
60
+
61
+ // later
62
+ await worker.stop();
63
+ await okdb.close();
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Environments
70
+
71
+ Queue data is **not** stored in a special dedicated `queue/` environment.
72
+
73
+ Instead:
74
+
75
+ - `okdb.queue.enqueue(...)` stores queue records in `default`
76
+ - `customEnv.queue.enqueue(...)` stores queue records in `customEnv`
77
+
78
+ Example:
79
+
80
+ ```javascript
81
+ const okdb = new OKDB('./db');
82
+ await okdb.open();
83
+
84
+ const jobsEnv = await okdb.createEnvironment('jobs');
85
+
86
+ await okdb.queue.enqueue('default-task', { a: 1 });
87
+ await jobsEnv.queue.enqueue('custom-task', { b: 2 });
88
+ ```
89
+
90
+ This is useful when you want separate queues per environment.
91
+
92
+ ---
93
+
94
+ ## Enqueuing jobs
95
+
96
+ ```javascript
97
+ const id = await okdb.queue.enqueue('process-image', {
98
+ fileId: 'f1',
99
+ userId: 'u1',
100
+ });
101
+
102
+ console.log(id); // UUID string
103
+ ```
104
+
105
+ ### Signature
106
+
107
+ ```javascript
108
+ await okdb.queue.enqueue(type, payload, options);
109
+ ```
110
+
111
+ - `type` — non-empty job type string
112
+ - `payload` — any serialisable value; stored separately from job metadata
113
+ - `options` — scheduling / retry / routing metadata
114
+
115
+ ### Enqueue options
116
+
117
+ ```javascript
118
+ await okdb.queue.enqueue(
119
+ 'process-image',
120
+ { fileId: 'f1' },
121
+ {
122
+ when: Date.now() + 5000, // schedule in the future
123
+ priority: 10,
124
+ tags: ['images', 'urgent'],
125
+ bucket: 'gpu-workers',
126
+ bucket_tokens: 1,
127
+ max_tries: 3, // null = unlimited
128
+ retry_delay: 2000,
129
+ backoff_multiplier: 2.0,
130
+ cron: '0 * * * *',
131
+ },
132
+ );
133
+ ```
134
+
135
+ Notes:
136
+
137
+ - `priority` is an integer
138
+ - `tags` can be a string or array of strings
139
+ - `bucket` and `bucket_tokens` integrate with token buckets
140
+ - `max_tries: 1` means “try once, then fail permanently”
141
+ - `max_tries: null` means unlimited retries
142
+
143
+ ---
144
+
145
+ ## Processing jobs with `worker(...)`
146
+
147
+ Use a normal in-process worker like this:
148
+
149
+ ```javascript
150
+ const worker = okdb.queue.worker(
151
+ 'send-email',
152
+ async (payload, ctx) => {
153
+ await ctx.markProgress('validating');
154
+
155
+ if (!payload.to) throw new Error('missing recipient');
156
+
157
+ await ctx.markProgress('sending');
158
+ await sendEmail(payload.to, payload.subject);
159
+
160
+ await ctx.okdb.put('email_log', payload.orderId, {
161
+ to: payload.to,
162
+ sentAt: Date.now(),
163
+ jobId: ctx.jobId,
164
+ });
165
+ },
166
+ {
167
+ concurrency: 2,
168
+ pollInterval: 250,
169
+ ttl: 30_000,
170
+ onPermanentFail: async (job, err) => {
171
+ console.error('permanently failed', job.id, err.message);
172
+ },
173
+ },
174
+ );
175
+ ```
176
+
177
+ ### Handler signature
178
+
179
+ The handler receives:
180
+
181
+ ```javascript
182
+ async (payload, ctx) => { ... }
183
+ ```
184
+
185
+ Where `ctx` contains:
186
+
187
+ - `ctx.okdb` — the root `OKDB` instance
188
+ - `ctx.jobId` — queue job id
189
+ - `ctx.heartbeat()` — extend the claim TTL for long-running jobs
190
+ - `ctx.markProgress(message)` — update the job's `progress` field
191
+
192
+ These local worker helpers now map to the same public queue API that remote workers can use:
193
+
194
+ - `okdb.queue.claim(type, options)`
195
+ - `okdb.queue.markJobHeartbeat(jobId, claimId, ttl?)`
196
+ - `okdb.queue.markJobProgress(jobId, claimId, message)`
197
+ - `okdb.queue.markJobComplete(jobId, claimId, result?)`
198
+ - `okdb.queue.markJobFail(jobId, claimId, error, code?)`
199
+
200
+ ### Important note
201
+
202
+ `worker(...)` runs in the **same Node process** unless you explicitly use `spawn(...)` or a separate worker process yourself.
203
+
204
+ That means:
205
+
206
+ - great for I/O-heavy jobs
207
+ - not isolation from CPU-heavy work
208
+
209
+ ---
210
+
211
+ ## Worker options
212
+
213
+ ### `pollInterval`
214
+
215
+ How long to sleep when the queue is empty.
216
+
217
+ ```javascript
218
+ {
219
+ pollInterval: 100;
220
+ }
221
+ ```
222
+
223
+ ### `concurrency`
224
+
225
+ How many jobs to process in parallel **inside the same process**.
226
+
227
+ ```javascript
228
+ {
229
+ concurrency: 4;
230
+ }
231
+ ```
232
+
233
+ This is implemented as multiple independent claim/handle lanes.
234
+
235
+ ### `ttl`
236
+
237
+ Claim timeout in ms.
238
+
239
+ ```javascript
240
+ {
241
+ ttl: 30_000;
242
+ }
243
+ ```
244
+
245
+ If your worker dies or stops heartbeating, expired running jobs are re-queued by reconciliation.
246
+
247
+ ### `onPermanentFail`
248
+
249
+ Called after a job permanently fails.
250
+
251
+ ---
252
+
253
+ ## Remote / external workers
254
+
255
+ If your workers run outside the embedded Node.js process, use the public queue lifecycle methods directly or call the equivalent HTTP endpoints.
256
+
257
+ ### Public queue methods
258
+
259
+ ```javascript
260
+ const claimed = await okdb.queue.claim('send-email', { ttl: 30_000 });
261
+ if (!claimed) return;
262
+
263
+ await okdb.queue.markJobProgress(claimed.id, claimed.claim_id, 'sending');
264
+
265
+ try {
266
+ await sendEmail(claimed.payload.to, claimed.payload.subject);
267
+ await okdb.queue.markJobComplete(claimed.id, claimed.claim_id, { delivered: true });
268
+ } catch (err) {
269
+ await okdb.queue.markJobFail(claimed.id, claimed.claim_id, err, 'send_failed');
270
+ }
271
+ ```
272
+
273
+ ### Claim ownership contract
274
+
275
+ Remote lifecycle operations require the active `claim_id` returned by `claim(...)`.
276
+
277
+ - only the current claim holder may heartbeat, write progress, complete, or fail the job
278
+ - stale workers are rejected once the claim changes or expires
279
+ - this prevents duplicate workers from mutating the same running job
280
+
281
+ ### HTTP endpoints for remote workers
282
+
283
+ The queue HTTP API now exposes the full remote-worker lifecycle:
284
+
285
+ - `POST /api/queue/claim/:type`
286
+ - `POST /api/queue/job/:id/heartbeat`
287
+ - `POST /api/queue/job/:id/progress`
288
+ - `POST /api/queue/job/:id/complete`
289
+ - `POST /api/queue/job/:id/fail`
290
+
291
+ Example:
292
+
293
+ ```text
294
+ // claim
295
+ POST /api/queue/claim/send-email
296
+ { "options": { "ttl": 30000 } }
297
+
298
+ // progress
299
+ POST /api/queue/job/<id>/progress
300
+ { "claim_id": "<claim>", "progress": "sending" }
301
+
302
+ // complete
303
+ POST /api/queue/job/<id>/complete
304
+ { "claim_id": "<claim>", "result": { "delivered": true } }
305
+
306
+ // fail
307
+ POST /api/queue/job/<id>/fail
308
+ { "claim_id": "<claim>", "code": "send_failed", "message": "smtp timeout" }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Spawned workers
314
+
315
+ If you want queue handling in a **separate Node process**, use `spawn(...)`:
316
+
317
+ ```javascript
318
+ const spawned = await okdb.queue.spawn('send-email', './workers/email.js', {
319
+ concurrency: 4,
320
+ pollInterval: 100,
321
+ });
322
+
323
+ // later
324
+ await spawned.stop();
325
+ ```
326
+
327
+ The module must export a function:
328
+
329
+ ```javascript
330
+ module.exports = async function (payload, ctx) {
331
+ await ctx.okdb.put('email_log', payload.orderId, {
332
+ sentAt: Date.now(),
333
+ jobId: ctx.jobId,
334
+ });
335
+ };
336
+ ```
337
+
338
+ The child process opens the same OKDB path and receives the same `ctx.okdb` semantics as a normal worker.
339
+
340
+ ---
341
+
342
+ ## Job lifecycle
343
+
344
+ ```text
345
+ pending → running → done
346
+ └→ pending (retry)
347
+ └→ failed
348
+ ```
349
+
350
+ In more detail:
351
+
352
+ - `pending` — ready to be claimed
353
+ - `running` — claimed by a worker and currently in-flight
354
+ - `done` — completed successfully
355
+ - `failed` — permanently failed after all tries are exhausted
356
+
357
+ When a running job's claim expires, reconciliation moves it back to `pending`.
358
+
359
+ ---
360
+
361
+ ## Heartbeats and reconciliation
362
+
363
+ For long-running jobs, call `ctx.heartbeat()` periodically:
364
+
365
+ ```javascript
366
+ okdb.queue.worker(
367
+ 'import',
368
+ async (payload, ctx) => {
369
+ for (let i = 0; i < 10; i++) {
370
+ await processChunk(i);
371
+ await ctx.heartbeat();
372
+ await ctx.markProgress(`chunk ${i + 1}/10`);
373
+ }
374
+ },
375
+ { ttl: 5000 },
376
+ );
377
+ ```
378
+
379
+ You can also manually trigger reconciliation:
380
+
381
+ ```javascript
382
+ await okdb.queue._reconcile();
383
+ ```
384
+
385
+ The admin UI also exposes reconcile actions.
386
+
387
+ ---
388
+
389
+ ## Listing jobs
390
+
391
+ ```javascript
392
+ const { items, cursor } = await okdb.queue.list({
393
+ type: 'send-email',
394
+ status: 'pending',
395
+ sort: 'when', // 'when' | 'priority'
396
+ direction: 'asc',
397
+ limit: 50,
398
+ cursor: null,
399
+ });
400
+ ```
401
+
402
+ Supported filters/options:
403
+
404
+ - `type`
405
+ - `status`
406
+ - `bucket`
407
+ - `tag`
408
+ - `sort`
409
+ - `direction`
410
+ - `limit`
411
+ - `cursor`
412
+
413
+ ---
414
+
415
+ ## Getting and updating one job
416
+
417
+ ### Get a job
418
+
419
+ ```javascript
420
+ const job = await okdb.queue.getJob(jobId);
421
+
422
+ console.log(job.status);
423
+ console.log(job.payload);
424
+ console.log(job.progress);
425
+ ```
426
+
427
+ `getJob()` returns job metadata **plus payload**.
428
+
429
+ ### Update a job
430
+
431
+ ```javascript
432
+ await okdb.queue.updateJob(jobId, {
433
+ payload: { fileId: 'f1', force: true },
434
+ priority: 9,
435
+ tags: ['edited', 'manual'],
436
+ progress: 'manually adjusted',
437
+ status_message: 'edited from admin ui',
438
+ retry_delay: 500,
439
+ });
440
+ ```
441
+
442
+ Editable fields currently include:
443
+
444
+ - `type`
445
+ - `payload`
446
+ - `when`
447
+ - `priority`
448
+ - `tags`
449
+ - `bucket`
450
+ - `bucket_tokens`
451
+ - `cron`
452
+ - `max_tries`
453
+ - `retry_delay`
454
+ - `backoff_multiplier`
455
+ - `progress`
456
+ - `status_message`
457
+
458
+ ---
459
+
460
+ ## Retrying, removing, and clearing jobs
461
+
462
+ ### Retry one job
463
+
464
+ ```javascript
465
+ await okdb.queue.retryJob(jobId);
466
+ ```
467
+
468
+ This resets the job back to `pending` and resets `tries` to `0`.
469
+
470
+ ### Remove one job
471
+
472
+ ```javascript
473
+ await okdb.queue.removeJob(jobId);
474
+ ```
475
+
476
+ This deletes both the job metadata and its payload.
477
+
478
+ ### Bulk cleanup helpers
479
+
480
+ ```javascript
481
+ await okdb.queue.clearDone();
482
+ await okdb.queue.clearFailed();
483
+ await okdb.queue.clearStuck();
484
+ await okdb.queue.retryFailed();
485
+ ```
486
+
487
+ Each method also accepts optional `type` and `limit` parameters.
488
+
489
+ ---
490
+
491
+ ## Buckets (token-bucket rate limiting)
492
+
493
+ Create a bucket:
494
+
495
+ ```javascript
496
+ await okdb.queue.addBucket('gpu-workers', {
497
+ capacity: 100,
498
+ tokens: 100,
499
+ refill_amount: 10,
500
+ refill_every: 1000,
501
+ });
502
+ ```
503
+
504
+ Update a bucket:
505
+
506
+ ```javascript
507
+ await okdb.queue.updateBucket('gpu-workers', {
508
+ tokens: 50,
509
+ refill_amount: 20,
510
+ });
511
+ ```
512
+
513
+ Remove a bucket:
514
+
515
+ ```javascript
516
+ await okdb.queue.removeBucket('gpu-workers');
517
+ ```
518
+
519
+ Manually claim tokens:
520
+
521
+ ```javascript
522
+ const ok = await okdb.queue.tryClaimTokens('gpu-workers', 2);
523
+ ```
524
+
525
+ Jobs can reference a bucket via `enqueue(..., { bucket, bucket_tokens })`.
526
+
527
+ ---
528
+
529
+ ## Cron support
530
+
531
+ If you use `cron`, you must configure `queue.cron_next`:
532
+
533
+ ```javascript
534
+ const Croner = require('croner');
535
+
536
+ const okdb = new OKDB('./db', {
537
+ queue: {
538
+ cron_next: (expr, fromMs) => {
539
+ return new Croner(expr, { currentDate: new Date(fromMs) }).nextRun().getTime();
540
+ },
541
+ },
542
+ });
543
+ ```
544
+
545
+ Then enqueue recurring work:
546
+
547
+ ```javascript
548
+ await okdb.queue.enqueue(
549
+ 'daily-report',
550
+ { reportType: 'summary' },
551
+ {
552
+ cron: '0 8 * * *',
553
+ },
554
+ );
555
+ ```
556
+
557
+ ---
558
+
559
+ ## Events
560
+
561
+ Queue lifecycle events are emitted on **`okdb.events`**, not on `okdb.queue.events`.
562
+
563
+ Current low-frequency emitted events include:
564
+
565
+ - `queue:enqueued`
566
+ - `queue:done`
567
+ - `queue:failed`
568
+ - `queue:retry`
569
+ - `queue:removed`
570
+ - `queue:reconciled`
571
+
572
+ Example:
573
+
574
+ ```javascript
575
+ okdb.events.on('queue:done', ({ id, type }) => {
576
+ console.log('done', id, type);
577
+ });
578
+
579
+ okdb.events.on('queue:failed', ({ id, type, status_code }) => {
580
+ console.log('failed', id, type, status_code);
581
+ });
582
+ ```
583
+
584
+ ### About filtering by type or key
585
+
586
+ Today, filtering by job type or job id is best done in your handler:
587
+
588
+ ```javascript
589
+ okdb.events.on('queue:done', (ev) => {
590
+ if (ev.type !== 'send-email') return;
591
+ console.log('email job done', ev.id);
592
+ });
593
+ ```
594
+
595
+ If a richer queue-specific event subscription API is added later, it will likely build on top of these base events.
596
+
597
+ ---
598
+
599
+ ## Sync behaviour
600
+
601
+ Queue data lives in the current environment, so queue sync follows that environment's sync settings.
602
+
603
+ That means:
604
+
605
+ - a job enqueued in a synced env can replicate to peers
606
+ - a worker on another node/process can consume from the same synced data
607
+ - retry/failure state also follows the same environment data
608
+
609
+ If you want queue isolation, use a separate environment with the sync behavior you want.
610
+
611
+ ---
612
+
613
+ ## Queue-worker engine
614
+
615
+ For persistent, self-managing workers that use stored functions, see **queue-worker engines** in [Pipelines](./pipelines.md).
616
+
617
+ The queue-worker engine is a pipeline-compatible engine type that:
618
+
619
+ - claims jobs from the queue automatically
620
+ - invokes a stored function for each claimed payload
621
+ - handles completion/failure using normal queue semantics
622
+
623
+ Worker `concurrency` controls job throughput. The function runner pool scales automatically to support it — no manual pool sizing is needed.
624
+
625
+ ---
626
+
627
+ ## Admin UI
628
+
629
+ The admin queue UI supports:
630
+
631
+ - filtering by status and type
632
+ - retry / cancel / delete
633
+ - bulk clear / bulk retry actions
634
+ - viewing progress
635
+ - editing payload and queue settings for a job
636
+
637
+ Expanded rows show key metadata, and the edit dialog allows modifying payload and most queue settings without editing raw `~queue_jobs` records directly.