okengine 0.18.5 → 0.19.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 (510) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,659 @@
1
+ ---
2
+ title: "Files"
3
+ description: "Object storage buckets — put/get, putImage variants, LQIP placeholders, and chainable Bun.Image transforms."
4
+ icon: "Files"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ The Files facet is a typed object bucket for uploads, exports, and media. Declare with `store.files(name)`, then call `fx.store(decl)`.
9
+
10
+ For developers storing blobs on okengine — put bytes, optionally derive image variants, serve keys from Flows.
11
+
12
+ <Callout title="The one rule">
13
+ Pass the **declaration** into `fx.store(uploads)`. There is no `fx.store.files` namespace, no
14
+ `presignPut`, and no `transform(…)` helper — use `put` / `get` / `putImage` / `image(…)`.
15
+ </Callout>
16
+
17
+ ## Smallest Example
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Declare a bucket
23
+
24
+ ```typescript title="src/core.ts"
25
+ import { store } from "okengine";
26
+
27
+ export const uploads = store.files("uploads");
28
+ ```
29
+
30
+ </Step>
31
+
32
+ <Step>
33
+ ### Put and get in a Flow
34
+
35
+ ```typescript title="src/flows/uploads/create.ts"
36
+ import { on, flow, http } from "okengine";
37
+ import { z } from "zod";
38
+ import { uploads } from "@/core";
39
+
40
+ export const create = on(
41
+ http.post(),
42
+ flow({
43
+ in: z.object({
44
+ name: z.string().min(1),
45
+ bytes: z.string(), // base64 for the demo
46
+ }),
47
+ out: z.object({ key: z.string() }),
48
+ do: async ({ name, bytes }, fx) => {
49
+ const key = `uploads/${fx.id()}-${name}`;
50
+ const data = Uint8Array.from(atob(bytes), (c) => c.charCodeAt(0));
51
+ await fx.store(uploads).put(key, data);
52
+ return { key };
53
+ },
54
+ }),
55
+ );
56
+ ```
57
+
58
+ </Step>
59
+
60
+ <Step>
61
+ ### Call the endpoint
62
+
63
+ ```bash
64
+ curl -X POST http://localhost:6530/uploads \
65
+ -H "content-type: application/json" \
66
+ -d '{"name":"note.txt","bytes":"aGVsbG8="}'
67
+ ```
68
+
69
+ Response:
70
+
71
+ ```json
72
+ {
73
+ "data": { "key": "uploads/…-note.txt" },
74
+ "error": null
75
+ }
76
+ ```
77
+
78
+ </Step>
79
+
80
+ </Steps>
81
+
82
+ <Callout title="Effects are inferred">
83
+ Every `fx.store(uploads)` touch stamps `reads` / `writes` as `files:uploads` on the Flow. That
84
+ powers the Manifest, Console, and least privilege — no hand-written effect lists.
85
+ </Callout>
86
+
87
+ ## Progressive Patterns
88
+
89
+ From a bare put to list/delete, image variants, and the chainable pipeline:
90
+
91
+ <Tabs items={["Put / get", "List / delete", "putImage", "Pipeline"]}>
92
+
93
+ <Tab value="Put / get">
94
+
95
+ `put` accepts `Uint8Array` or UTF-8 `string`. `get` returns bytes or `null`:
96
+
97
+ ```typescript title="src/flows/exports/create.ts"
98
+ import { on, flow, http } from "okengine";
99
+ import { uploads } from "@/core";
100
+
101
+ export const create = on(
102
+ http.post(),
103
+ flow({
104
+ do: async (_, fx) => {
105
+ const key = `exports/${fx.id()}.csv`;
106
+ await fx.store(uploads).put(key, "id,title\n1,Ship\n");
107
+ const bytes = await fx.store(uploads).get(key);
108
+ return { key, bytes: bytes?.byteLength ?? 0 };
109
+ },
110
+ }),
111
+ );
112
+ ```
113
+
114
+ </Tab>
115
+
116
+ <Tab value="List / delete">
117
+
118
+ Prefix `list` for browsing; `delete` returns whether an object was removed:
119
+
120
+ ```typescript title="src/flows/avatars/route.ts"
121
+ import { on, flow, http } from "okengine";
122
+ import { z } from "zod";
123
+ import { uploads } from "@/core";
124
+
125
+ export const cleanup = on(
126
+ http.delete(),
127
+ flow({
128
+ in: z.object({ prefix: z.string().default("avatars/") }),
129
+ do: async ({ prefix }, fx) => {
130
+ const keys = await fx.store(uploads).list(prefix);
131
+ let removed = 0;
132
+ for (const key of keys) {
133
+ if (await fx.store(uploads).delete(key)) removed += 1;
134
+ }
135
+ return { listed: keys.length, removed };
136
+ },
137
+ }),
138
+ );
139
+ ```
140
+
141
+ Prefer stable, printable-ASCII object keys. Avoid `..` and leading `/` — the
142
+ `fs` driver rejects those with `Invalid object key: …`.
143
+
144
+ </Tab>
145
+
146
+ <Tab value="putImage">
147
+
148
+ Write the original plus named derivatives and an optional ThumbHash LQIP data
149
+ URL on the **result** (not a stored object):
150
+
151
+ ```typescript title="src/flows/photos/create.ts"
152
+ import { on, flow, http } from "okengine";
153
+ import { z } from "zod";
154
+ import { uploads } from "@/core";
155
+
156
+ export const create = on(
157
+ http.post(),
158
+ flow({
159
+ in: z.object({ bytes: z.string() }),
160
+ do: async ({ bytes }, fx) => {
161
+ const key = `photos/${fx.id()}.jpg`;
162
+ const data = Uint8Array.from(atob(bytes), (c) => c.charCodeAt(0));
163
+ const result = await fx.store(uploads).putImage(key, data, {
164
+ placeholder: true,
165
+ variants: {
166
+ thumb: {
167
+ resize: [320],
168
+ webp: { quality: 80 },
169
+ },
170
+ },
171
+ });
172
+ return {
173
+ key: result.key,
174
+ thumb: result.variants.thumb,
175
+ placeholder: result.placeholder,
176
+ width: result.meta.width,
177
+ height: result.meta.height,
178
+ };
179
+ },
180
+ }),
181
+ );
182
+ ```
183
+
184
+ Variant key shape: `photos/x.jpg` + `thumb` + `webp` → `photos/x.thumb.webp`.
185
+
186
+ </Tab>
187
+
188
+ <Tab value="Pipeline">
189
+
190
+ Chain Bun.Image ops, then `put` / `bytes` / `blob` / `placeholder` / `metadata`:
191
+
192
+ ```typescript title="src/flows/photos/card.ts"
193
+ import { on, flow, http } from "okengine";
194
+ import { z } from "zod";
195
+ import { uploads } from "@/core";
196
+
197
+ export const card = on(
198
+ http.post(),
199
+ flow({
200
+ in: z.object({ sourceKey: z.string() }),
201
+ do: async ({ sourceKey }, fx) => {
202
+ const outKey = sourceKey.replace(/\.[^.]+$/, ".card.webp");
203
+ await fx
204
+ .store(uploads)
205
+ .image(sourceKey)
206
+ .resize(400, 400, { fit: "inside" })
207
+ .webp({ quality: 80 })
208
+ .put(outKey);
209
+ return { outKey };
210
+ },
211
+ }),
212
+ );
213
+ ```
214
+
215
+ `image(source)` accepts an object key **or** raw bytes. Resize `fit` is
216
+ `"fill"` \| `"inside"` (Bun.Image — not CSS `cover`).
217
+
218
+ </Tab>
219
+
220
+ </Tabs>
221
+
222
+ ## Method Reference
223
+
224
+ `fx.store(filesDecl)`:
225
+
226
+ | Method | Signature | Meaning |
227
+ | ---------- | ---------------------------- | ----------------------------------- |
228
+ | `put` | `put(key, data)` | Store bytes or UTF-8 string |
229
+ | `get` | `get(key)` | Read bytes, or `null` if missing |
230
+ | `delete` | `delete(key)` | Remove; returns whether deleted |
231
+ | `list` | `list(prefix?)` | List object keys (optional prefix) |
232
+ | `image` | `image(source, options?)` | Chainable Bun.Image pipeline |
233
+ | `putImage` | `putImage(key, data, opts?)` | Original + variants + optional LQIP |
234
+
235
+ Handle also exposes `ref` (`files:name`) and `driverId` (`memory` · `fs` · `s3`).
236
+
237
+ There is **no** public `presignPut`, `presignGet`, `copy`, `head`, or `transform`
238
+ alias on the fx handle.
239
+
240
+ ## Object Keys
241
+
242
+ Keys are opaque strings the driver stores as-is. Pick a stable prefix + unique
243
+ suffix; keep SQL rows pointing at the key, not the bytes.
244
+
245
+ **Explicit key** — build the path in the Flow:
246
+
247
+ ```typescript
248
+ const key = `avatars/${fx.auth.userId}/${fx.id()}.png`;
249
+ await fx.store(uploads).put(key, data);
250
+ ```
251
+
252
+ **Content hash** — when the same bytes should collapse to one object, hash
253
+ yourself (SHA-256 hex) and put under that digest. The facet does not auto-
254
+ dedupe on `put`.
255
+
256
+ **Driver guards** — the `fs` driver throws on path escape:
257
+
258
+ ```text
259
+ Invalid object key: ../secret
260
+ ```
261
+
262
+ | Rule | Why |
263
+ | ---------------------- | --------------------------------------------------------- |
264
+ | Prefer printable ASCII | S3-compatible signed URLs are fragile with non-ASCII keys |
265
+ | No leading `/` | Absolute paths are rejected on `fs` |
266
+ | No `..` segments | Path escape is rejected on `fs` |
267
+ | Stable extensions | MIME / Console kind inference uses the suffix |
268
+
269
+ ## Blob Operations
270
+
271
+ Each verb binds through `fx.store(decl)` inside `flow()`:
272
+
273
+ <Tabs items={["put", "get", "delete", "list"]}>
274
+
275
+ <Tab value="put">
276
+
277
+ Write bytes or a UTF-8 string. Overwrites an existing key:
278
+
279
+ ```typescript title="src/flows/notes/[id]/attachment/create.ts"
280
+ import { on, flow, http } from "okengine";
281
+ import { z } from "zod";
282
+ import { uploads } from "@/core";
283
+
284
+ export const create = on(
285
+ http.post(),
286
+ flow({
287
+ in: z.object({
288
+ id: z.string(),
289
+ name: z.string().min(1),
290
+ bytes: z.string(),
291
+ }),
292
+ do: async ({ id, name, bytes }, fx) => {
293
+ const key = `notes/${id}/${name}`;
294
+ const data = Uint8Array.from(atob(bytes), (c) => c.charCodeAt(0));
295
+ await fx.store(uploads).put(key, data);
296
+ return { key };
297
+ },
298
+ }),
299
+ );
300
+ ```
301
+
302
+ </Tab>
303
+
304
+ <Tab value="get">
305
+
306
+ Read bytes, or `null` when the key is missing — treat `null` as not found:
307
+
308
+ ```typescript title="src/flows/notes/[id]/attachment/get.ts"
309
+ import { on, flow, http } from "okengine";
310
+ import { z } from "zod";
311
+ import { uploads } from "@/core";
312
+
313
+ export const get = on(
314
+ http.get(),
315
+ flow({
316
+ in: z.object({ id: z.string(), name: z.string() }),
317
+ errors: { NotFound: z.object({ key: z.string() }) },
318
+ do: async ({ id, name }, fx) => {
319
+ const key = `notes/${id}/${name}`;
320
+ const bytes = await fx.store(uploads).get(key);
321
+ if (!bytes) return fx.fail("NotFound", { key });
322
+ return { key, size: bytes.byteLength };
323
+ },
324
+ }),
325
+ );
326
+ ```
327
+
328
+ </Tab>
329
+
330
+ <Tab value="delete">
331
+
332
+ Remove one key. Returns `true` when an object was deleted, `false` if missing:
333
+
334
+ ```typescript title="src/flows/notes/[id]/attachment/remove.ts"
335
+ import { on, flow, http } from "okengine";
336
+ import { z } from "zod";
337
+ import { uploads } from "@/core";
338
+
339
+ export const remove = on(
340
+ http.delete(),
341
+ flow({
342
+ in: z.object({ id: z.string(), name: z.string() }),
343
+ do: async ({ id, name }, fx) => {
344
+ const key = `notes/${id}/${name}`;
345
+ const removed = await fx.store(uploads).delete(key);
346
+ return { key, removed };
347
+ },
348
+ }),
349
+ );
350
+ ```
351
+
352
+ </Tab>
353
+
354
+ <Tab value="list">
355
+
356
+ List keys under an optional prefix. Use for Console browsing and admin Flows —
357
+ not as a relational index:
358
+
359
+ ```typescript title="src/flows/notes/[id]/attachments.ts"
360
+ import { on, flow, http } from "okengine";
361
+ import { z } from "zod";
362
+ import { uploads } from "@/core";
363
+
364
+ export const attachments = on(
365
+ http.get(),
366
+ flow({
367
+ in: z.object({ id: z.string() }),
368
+ do: async ({ id }, fx) => {
369
+ const keys = await fx.store(uploads).list(`notes/${id}/`);
370
+ return { keys };
371
+ },
372
+ }),
373
+ );
374
+ ```
375
+
376
+ </Tab>
377
+
378
+ </Tabs>
379
+
380
+ ## putImage
381
+
382
+ <Callout title="Detailed section">
383
+ If you only need the original + one thumb, jump to Progressive Patterns → putImage. This section
384
+ covers options, variant key naming, and decode guards.
385
+ </Callout>
386
+
387
+ `putImage(key, data, opts?)` writes the **original** at `key`, then optional
388
+ named variants beside it, then optionally returns a ThumbHash LQIP **data URL**
389
+ on the result (the placeholder is not stored as an object).
390
+
391
+ ```typescript
392
+ const result = await fx.store(uploads).putImage(key, data, {
393
+ placeholder: true,
394
+ variants: {
395
+ thumb: { resize: [320], webp: { quality: 80 } },
396
+ card: { resize: [800, 600, { fit: "inside" }], jpeg: { quality: 85 } },
397
+ },
398
+ maxPixels: 4096 * 4096,
399
+ autoOrient: true,
400
+ });
401
+ ```
402
+
403
+ <Accordions>
404
+
405
+ <Accordion title="putImage Options">
406
+
407
+ | Option | Type | Default | Meaning |
408
+ | ------------- | ---------------------------------- | ------------- | ------------------------------------- |
409
+ | `variants` | `Record<string, ImageVariantSpec>` | omitted | Named derivatives beside the original |
410
+ | `placeholder` | `boolean` | omitted | ThumbHash **data URL** on the result |
411
+ | `maxPixels` | `number` \| `false` | `4096 * 4096` | Decode ceiling; `false` disables |
412
+ | `autoOrient` | `boolean` | `true` | Respect EXIF orientation |
413
+
414
+ </Accordion>
415
+
416
+ <Accordion title="ImageVariantSpec">
417
+ Each variant may set geometry and **exactly one** encode target:
418
+
419
+ | Field | Meaning |
420
+ | ----------------------------------------- | ----------------------------------------------------------- |
421
+ | `resize` | `[width]`, `[width, height]`, or `[width, height, options]` |
422
+ | `rotate` · `flip` · `flop` | Geometry |
423
+ | `modulate` | `{ brightness?, saturation? }` |
424
+ | `jpeg` / `png` / `webp` / `heic` / `avif` | Encode — set **one** only |
425
+
426
+ Resize options: `filter`, `fit` (`"fill"` \| `"inside"`), `withoutEnlargement`.
427
+
428
+ If no encode field is set, the variant keeps the source format when it is
429
+ `jpeg` / `png` / `webp` / `heic` / `avif`; decode-only sources (e.g. gif)
430
+ default to `webp`.
431
+
432
+ Setting two encode fields throws:
433
+
434
+ ```text
435
+ files image variant: set only one of jpeg/png/webp/heic/avif (got jpeg, webp)
436
+ ```
437
+
438
+ </Accordion>
439
+
440
+ <Accordion title="Variant key naming">
441
+ Keys are derived from the original stem + variant name + encode extension
442
+ (`jpeg` → `jpg`):
443
+
444
+ | Original | Variant | Format | Stored key |
445
+ | ------------------- | ------- | ------ | --------------------- |
446
+ | `photos/x.jpg` | `thumb` | `webp` | `photos/x.thumb.webp` |
447
+ | `photos/x.png` | `card` | `jpeg` | `photos/x.card.jpg` |
448
+ | `a/b/hero` (no ext) | `sm` | `png` | `a/b/hero.sm.png` |
449
+
450
+ **Consequence:** `get(result.key)` is the original; read derivatives from
451
+ `result.variants.thumb` (and friends), not by guessing.
452
+
453
+ </Accordion>
454
+
455
+ <Accordion title="PutImageResult">
456
+
457
+ | Field | Type | Meaning |
458
+ | ------------- | --------------------------- | --------------------------------- |
459
+ | `key` | `string` | Original object key |
460
+ | `meta` | `{ width, height, format }` | Header metadata of the original |
461
+ | `variants` | `Record<string, string>` | Variant name → object key |
462
+ | `placeholder` | `string?` | ThumbHash data URL when requested |
463
+
464
+ </Accordion>
465
+
466
+ </Accordions>
467
+
468
+ ## Image Pipeline
469
+
470
+ <Callout title="Detailed section">
471
+ Prefer `putImage` when you want original + named variants in one call. Use `image(…)` when you
472
+ need a one-off transform, metadata probe, or custom out key.
473
+ </Callout>
474
+
475
+ <StoreFilesVariants />
476
+
477
+ Chain on `fx.store(uploads).image(source, options?)`:
478
+
479
+ | Step | Meaning |
480
+ | ---------------------------------------------- | -------------------------------- |
481
+ | `.resize(w, h?, opts?)` | Resize (Bun fit options) |
482
+ | `.rotate(degrees)` · `.flip()` · `.flop()` | Geometry |
483
+ | `.modulate({ brightness?, saturation? })` | Color |
484
+ | `.jpeg` / `.png` / `.webp` / `.heic` / `.avif` | Encode |
485
+ | `.metadata()` | Width / height / format (header) |
486
+ | `.bytes()` · `.blob()` | Materialize |
487
+ | `.placeholder()` | ThumbHash data URL |
488
+ | `.put(outKey)` | Write the transformed bytes |
489
+
490
+ Decode guards (`maxPixels`, `autoOrient`) pass as the second argument to
491
+ `image(…)`, same defaults as `putImage`.
492
+
493
+ <Accordions>
494
+
495
+ <Accordion title="Source resolution">
496
+ `image(source)` accepts an object key **or** raw `Uint8Array`.
497
+
498
+ | Source | Behavior |
499
+ | ------------ | ----------------------------------------------------------------- |
500
+ | `string` key | Loads via `get`; missing → `files image: object not found: {key}` |
501
+ | `Uint8Array` | Transforms in memory (no prior put required) |
502
+
503
+ ```typescript
504
+ const webp = await fx
505
+ .store(uploads)
506
+ .image(rawBytes, { maxPixels: 2048 * 2048 })
507
+ .resize(800)
508
+ .webp({ quality: 80 })
509
+ .bytes();
510
+ ```
511
+
512
+ </Accordion>
513
+
514
+ <Accordion title="Encode & platform codecs">
515
+ Call one encode step before materializing. HEIC / AVIF may fall back to WebP
516
+ when the platform throws `ERR_IMAGE_FORMAT_UNSUPPORTED` (quality preserved
517
+ when set).
518
+
519
+ ```typescript
520
+ await fx
521
+ .store(uploads)
522
+ .image("photos/hero.jpg")
523
+ .resize(1200, undefined, { fit: "inside", withoutEnlargement: true })
524
+ .avif({ quality: 70 })
525
+ .put("photos/hero.avif");
526
+ ```
527
+
528
+ </Accordion>
529
+
530
+ <Accordion title="Decode guards">
531
+
532
+ | Option | Default | Meaning |
533
+ | ------------ | --------------------- | ------------------------------------------------ |
534
+ | `maxPixels` | `4096 * 4096` (16 MP) | Reject oversized width×height before pixel alloc |
535
+ | `autoOrient` | `true` | Apply JPEG EXIF Orientation first |
536
+
537
+ Exceeding the ceiling surfaces Bun’s `ERR_IMAGE_TOO_MANY_PIXELS`. Pass a higher
538
+ ceiling, or `maxPixels: false` only when you trust the source.
539
+
540
+ </Accordion>
541
+
542
+ </Accordions>
543
+
544
+ ## Declare Options
545
+
546
+ | Option | Type | Default | Meaning |
547
+ | ------------- | -------- | ------- | ------------------------ |
548
+ | `description` | `string` | omitted | Console / Manifest label |
549
+
550
+ ```typescript
551
+ export const uploads = store.files("uploads", {
552
+ description: "User uploads and derived image variants",
553
+ });
554
+ ```
555
+
556
+ ## Drivers
557
+
558
+ | Driver | Runs as | Best for |
559
+ | -------- | --------------------------------- | ------------------------ |
560
+ | `s3` | S3-compatible (RustFS in Compose) | Dev + prod default |
561
+ | `fs` | Local filesystem root | Single-node laptop paths |
562
+ | `memory` | Process map | Test default |
563
+
564
+ Defaults: `s3` / `memory` / `s3` (dev / test / prod). Image pins (e.g.
565
+ RustFS) live under `images.store.files` — the driver id stays `s3`.
566
+
567
+ ```typescript title="oke.config.ts"
568
+ import { defineConfig } from "okengine/config";
569
+
570
+ export default defineConfig({
571
+ drivers: {
572
+ // omit store.files to use defaults — pin only overrides
573
+ // store: { files: { dev: "fs", test: "memory", prod: "s3" } },
574
+ },
575
+ images: {
576
+ store: {
577
+ files: "rustfs/rustfs:1.0.0-rc.5",
578
+ },
579
+ },
580
+ });
581
+ ```
582
+
583
+ For `s3`, Compose / env typically supply `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`,
584
+ `S3_SECRET_ACCESS_KEY`, and optional `S3_REGION` / `S3_SESSION_TOKEN`. Without
585
+ `S3_ENDPOINT`, CreateBucket is skipped so real AWS is not auto-provisioned.
586
+
587
+ ## Troubleshooting
588
+
589
+ <Accordions>
590
+
591
+ <Accordion title="No files driver configured">
592
+ Boot needs `drivers.store.files` (or `DRIVER_DEFAULTS`). For `s3`, set the bucket / endpoint env
593
+ your Compose stack expects (`S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`).
594
+ </Accordion>
595
+
596
+ <Accordion title="Unknown store ref for files">
597
+ Cause: `Unknown store ref: …`. Import the module that calls `store.files(…)` before Flows run
598
+ (starters load `@/core`).
599
+ </Accordion>
600
+
601
+ <Accordion title="Invalid object key">
602
+ Cause: `Invalid object key: …` on the `fs` driver when the key starts with `/` or contains `..`.
603
+ Use relative, non-escaping keys.
604
+ </Accordion>
605
+
606
+ <Accordion title="ERR_IMAGE_TOO_MANY_PIXELS">
607
+ Decode exceeded `maxPixels` (default `4096 * 4096`). Pass a higher ceiling or `maxPixels: false`
608
+ only when you trust the source.
609
+ </Accordion>
610
+
611
+ <Accordion title="files image: object not found">
612
+ Cause: `files image: object not found: {key}`. `image(key)` loads via `get` first — put the
613
+ original, or pass raw `Uint8Array` as the source.
614
+ </Accordion>
615
+
616
+ <Accordion title="files image variant: set only one of jpeg/png/…">
617
+ Each `ImageVariantSpec` may set at most one encode field. Pick `webp` **or** `jpeg`, not both.
618
+ </Accordion>
619
+
620
+ <Accordion title="get returns null after putImage variant">
621
+ Read the **variant key** from `result.variants.thumb`, not the original key. Naming is `{stem}.
622
+ {variant}.{ext}` beside the original.
623
+ </Accordion>
624
+
625
+ <Accordion title="I expected presignPut / transform">
626
+ Those helpers are not on the fx handle. Upload with `put` / `putImage`, and transform with
627
+ `image(…).resize(…).webp(…).put(…)` (or `putImage` variants).
628
+ </Accordion>
629
+
630
+ </Accordions>
631
+
632
+ ## Learn more
633
+
634
+ - [Store](/docs/elements/store) — four facets; driver defaults
635
+ - [SQL](/docs/elements/store/sql) — relational data beside blob keys
636
+ - [HTTP](/docs/elements/flow/http) — routes that accept uploads
637
+ - [Vault](/docs/elements/vault) — credentials for S3 when you configure bindings
638
+ - [fx](/docs/reference/fx) — `fx.store(decl)`
639
+ - [Configuration](/docs/reference/configuration) — `drivers.store.files`
640
+
641
+ ## Next
642
+
643
+ <Cards>
644
+ <Card
645
+ title="Search"
646
+ description="BM25 ± LSH on SQL columns, plus store.index."
647
+ href="/docs/elements/store/search"
648
+ />
649
+ <Card
650
+ title="KV"
651
+ description="Namespaced get/set with duration TTL."
652
+ href="/docs/elements/store/kv"
653
+ />
654
+ <Card
655
+ title="Store Overview"
656
+ description="SQL · KV · files · index — one handle."
657
+ href="/docs/elements/store"
658
+ />
659
+ </Cards>