okengine 0.18.4 → 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 (512) 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/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. 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>