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.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/index.mdx +25 -6
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/ai/try-it.mdx +58 -0
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +209 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +232 -0
- package/site/content/docs/elements/ai/index.mdx +341 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +256 -0
- package/site/content/docs/elements/channel/email.mdx +266 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +300 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +445 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +372 -0
- package/site/content/docs/elements/index.mdx +52 -11
- package/site/content/docs/elements/signal/broadcast.mdx +517 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +590 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +596 -0
- package/site/content/docs/elements/store/files.mdx +659 -0
- package/site/content/docs/elements/store/index.mdx +310 -0
- package/site/content/docs/elements/store/kv.mdx +629 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +946 -0
- package/site/content/docs/elements/store/sql.mdx +937 -0
- package/site/content/docs/elements/vault/config.mdx +256 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +273 -0
- package/site/content/docs/elements/vault/secrets.mdx +285 -0
- package/site/content/docs/index.mdx +78 -30
- package/site/content/docs/meta.json +11 -8
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +137 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-anatomy.mdx +132 -0
- package/site/content/docs/understand/the-model.mdx +32 -0
- package/site/content/docs/understand/the-problem.mdx +74 -0
- package/site/content/docs/understand/the-vocabulary.mdx +26 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +49 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/competitor-mention-removal.test.ts +1 -1
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +258 -44
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
- package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +179 -0
- package/src/elements/store/search-runtime.ts +300 -0
- package/src/elements/store/search.test.ts +144 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +50 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +1 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -132
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -162
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- 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>
|