Перейти к содержимому

ONREZA Functions

ONREZA Functions — beta runtime для быстрых Bun-native handlers, которые не требуют постоянного server process. Используйте их для коротких атомарных flow: функция экспортирует fetch(request, ctx), получает явный ctx и возвращает стандартный Response.

ONREZA Functions не являются слоем deployment manifest. nrz deploy публикует function entry-файлы отдельным DB-native payload alongside обычного SOURCE_BUNDLE_V1, а платформа создаёт immutable source snapshot, revision и environment release.

Целевая модель:

  • функция — это live-ресурс уровня проекта, адресуемый стабильным триггером (route или расписание), а не build-артефакт с unique URL;
  • каждая публикация (UI, CLI, deploy) создаёт immutable source snapshot из одного self-contained entry file на функцию и новую revision;
  • snapshot хранится как bounded DB source snapshot, а не как S3/archive bundle;
  • активация переключает active environment release: app artifact pointer, function-key → revision map и edge ruleset меняются как одно состояние;
  • production, preview и каждое из ваших окружений имеют независимый указатель: изменения функций на preview-ветке не трогают production;
  • rollback — это обратное переключение указателя на прошлый release.

Function revision не является deployment release. Публикация функции не создаёт новый unique deployment URL, не засоряет release history приложения и не мутирует live-деплой. Редактируете вы функцию, а не деплой.

В beta deploy-origin publish и standalone function publish используют один invariant: live code не меняется in place, каждая публикация создаёт новую immutable revision, а live-состояние выбирается per-окружение через release.

Используйте ONREZA Functions для:

  • API handlers и webhook endpoints
  • route pipeline steps: auth, redirects, geo-routing, A/B, security headers вокруг Static/Compute или terminal-функции
  • лёгких proxy/adapter routes
  • request-time JSON transforms
  • маленьких backend-for-frontend endpoints

Не используйте ONREZA Functions для dependencies, static assets, generated bundles, sourcemaps, WebSocket, долгоживущего HTTP server, SSR-фреймворков (Next.js, Remix, Astro SSR), native modules или больших фоновых jobs. Для этого используйте Compute или Static hosting.

Entry файл должен быть self-contained ESM module. Основной публичный путь beta — HTTP fetch. В начале файла объявите статический config: CLI/server извлекают его через AST на publish, не исполняя пользовательский код. Для HTTP route wiring используйте onreza.rules.toml, а не config.triggers.

CLI ищет функции в проекте по брендовому суффиксу *.nrz-fn.ts, *.nrz-fn.tsx, *.nrz-fn.js, *.nrz-fn.jsx или *.nrz-fn.mjs, пропуская vendor/build/cache директории. Имя функции по умолчанию выводится из имени файла до суффикса: src/api/hello.nrz-fn.ts становится hello. Если в config указан name, он имеет приоритет. Для namespace используйте явное имя, например api-hello, в config.name или в имени файла. Одинаковые effective names отклоняются publish-инвариантом платформы.

В v1 одна функция публикуется как один entry-файл. Relative imports в другие файлы проекта, shared helper files, assets и npm dependencies не входят в публичный contract: скопируйте helper-код в entry-файл или используйте Compute, если нужен полноценный dependency graph.

В open beta ONREZA Functions не публикуют local modules, node_modules или npm dependencies. Это не означает, что shared code не появится в Functions: будущая модель должна быть version-pinned library/snippet surface, который разворачивается платформой на publish в проверяемый source snapshot.

Такой shared code будет частью Functions, если он остаётся маленьким, версионированным и проходит те же policy/size checks до активации. Полноценные npm dependencies, generated bundles, assets, WASM/native addons, WebSocket, свой server lifecycle, subprocess/FFI/raw sockets и большие dependency graphs останутся Compute или Static workloads.

export const config = {
name: "hello",
} as const;
export default {
async fetch(request, ctx) {
const url = new URL(request.url);
await ctx.log.info("hello", {
path: url.pathname,
invocationId: ctx.invocation.id,
});
return Response.json({
ok: true,
path: url.pathname,
workspace: ctx.invocation.workspaceId,
});
},
};

HTTP-привязка функций объявляется не в export const config, а в onreza.rules.toml через pipeline action. Для path conditions используйте структурированные exact, prefix или glob.

ctx содержит:

ПолеОписание
ctx.envEnvironment bindings текущего окружения. Функция на preview видит preview-переменные, на production — production. Используйте вместо process.env/Bun.env.
ctx.kvKey-value хранилище окружения: get, put/setttl), incr, delete, list. get возвращает Uint8Array | null; строковые значения декодируйте через TextDecoder.
ctx.invocationid, workspaceId, deploymentId, releaseId, functionName, revisionId и тип события. releaseId равен null для test-invoke без активного environment release.
ctx.logStructured function logs: debug, info, warn, error.
ctx.localsОбъект для передачи данных между шагами одного route pipeline.
ctx.waitUntil(promise)Дождаться короткого side effect после формирования response.

Pipeline выполняет Functions вокруг Static/Compute или другой terminal-функции. Порядок и cache boundary задаются в onreza.rules.toml, а каждый шаг реализует соответствующий handler: request, response, observe или fetch.

[[rules]]
id = "dashboard-auth"
condition.path = { type = "prefix", value = "/dashboard" }
action = { type = "pipeline", steps = [
{ use = "require-session", mode = "request", failure = "closed" },
{ handle = "@app" },
{ use = "add-user-header", mode = "response", failure = "open" },
] }
export default {
async request(request, ctx) {
const session = request.headers.get("cookie")?.match(/session=([^;]+)/)?.[1];
if (!session) {
return Response.redirect(new URL("/login", request.url), 307);
}
const userBytes = await ctx.kv.get(`session:${session}`);
const user = userBytes ? new TextDecoder().decode(userBytes) : null;
if (!user) {
return Response.redirect(new URL("/login", request.url), 307);
}
ctx.locals.user = user;
return request;
},
async response(response, ctx) {
const user = typeof ctx.locals.user === "string" ? ctx.locals.user : "anonymous";
response.headers.set("x-user", user);
return response;
},
};

Если request step возвращает Response, pipeline короткозамыкает downstream. Если возвращает Request, edge продолжает выполнение с обновлённым request. Для каждого function step задаётся поведение при сбое/паузе через failure:

  • closed (по умолчанию для request) — запрос блокируется. Безопасный дефолт: auth-гейт не должен молча пропускать трафик.
  • open (по умолчанию для response/observe) — step пропускается, запрос идёт в downstream. Подходит для некритичной логики (аналитика, заголовки, A/B), но не для авторизации.

cache_position = "before" выполняет step до cache boundary, а "after" — после. Для auth-гейтов используйте failure = "closed" и before-cache позицию.

ctx.kv — хранилище уровня окружения. Сессия, записанная route pipeline, читается другими функциями и приложением (Compute) в том же окружении. Между окружениями данные изолированы: preview не видит production. KV-операции тарифицируются отдельно от вызовов и ограничены лимитом плана.

const encoder = new TextEncoder();
const decoder = new TextDecoder();
await ctx.kv.set("user:42", JSON.stringify(profile), { ttl: 3600 });
await ctx.kv.put("avatar:42", encoder.encode("binary-safe value"), { ttl: 3600 });
const raw = await ctx.kv.get("user:42");
const storedProfile = raw ? JSON.parse(decoder.decode(raw)) : null;
const page = await ctx.kv.list({ prefix: "user:", limit: 100 });
await ctx.kv.delete("user:42");
// Атомарный счётчик — для rate limiting
const count = await ctx.kv.incr(`rate:${ip}`, { ttl: 60 });
if (count > 100) return new Response("Too Many Requests", { status: 429 });

Гарантии (важно для auth и лимитов):

  • read-your-writes в пределах региона — запись сразу видна на чтении в том же регионе;
  • между регионами — eventual с небольшим окном распространения. Отзыв сессии не мгновенный глобально → для безопасности дополняйте коротким ttl токена;
  • incr атомарен в пределах региона; глобальные rate-лимиты считайте приблизительными per-region;
  • переполнение namespace — запись падает с ошибкой, чужие ключи не вытесняются (никаких внезапных разлогинов). Истечение — только по ttl.

Если нужна строгая глобальная консистентность — это не KV, используйте внешнюю БД.

В onreza.toml нет секции для ONREZA Functions. Суффикс entry-файла не настраивается намеренно, чтобы сканер искал только branded files, а не обходил проект как generic TypeScript indexer. Конфигурация функции живёт рядом с handler в самом entry-файле:

export const config = {
name: "api-webhook",
} as const;

nrz deploy, будущий nrz functions publish и UI publish используют одну модель: CLI/server парсят top-of-file config через AST, нормализуют declaration, собирают один bounded self-contained source file на функцию и отправляют его в function publish API. nrz deploy активирует функции только вместе с успешным app release; standalone publish создаёт functions-only release поверх текущего active app artifact pointer.

Wire schema для publish payload генерируется из shared contract: /schemas/onreza-functions-publish-payload-v1.schema.json. Source contract beta фиксирует maxFilesPerFunction = 1, maxUserImportDepth = 0 и 128 KB на entry source file.

onreza.rules.toml используется для static edge rules и HTTP route pipeline: redirects, rewrites, headers, cache, firewall и привязки Functions к маршрутам. Если publish payload не содержит edgeRules, платформа сохраняет текущий active ruleset окружения; чтобы очистить ruleset, нужно опубликовать явный пустой rules = [].

Эффективные runtime limits берутся из плана и platform policy. Значения выше hard cap плана не применяются.

ONREZA Functions используют Bun runtime, но публичный контракт остаётся allowlist-first: разрешены только API, которые подходят короткому handler flow.

  • Web APIs для handlers: fetch, Request, Response, Headers, URL, URLSearchParams, streams и web crypto.
  • Safe Node utility imports с префиксом node:: node:crypto, node:buffer, node:path, node:stream, node:url, node:util, node:zlib, node:events, node:timers и близкие pure utility modules.
  • Bun utility APIs для hashing, compression, parsing, text/ANSI helpers, Bun.password, Bun.semver, stream conversion helpers и похожих CPU/local операций из runtime allowlist.
  • Bun.file для /tmp и platform-hydrated function source. Это не asset API: файлы проекта вне entry source не публикуются вместе с функцией.
  • Bun.write для записи только во временную директорию /tmp.
  • Публичный outbound HTTP через обычный fetch.

Beta sandbox блокирует ambient capabilities и host-level APIs, которые не подходят короткому атомарному flow или должны идти через ctx:

  • CommonJS и require
  • package imports и Node built-ins вне allowlist: fs, net, tls, http, https, dns, child_process, worker_threads, process и похожие modules
  • user imports из других файлов проекта, package imports, npm dependencies, node_modules, generated bundles, sourcemaps, assets, WASM и native addons
  • Bun.env, Bun.SQL, Bun.postgres, Bun.redis, Bun.s3, Bun.secrets
  • Bun.spawn, Bun.serve, Bun.connect, Bun.listen, raw sockets, FFI, cron
  • Bun.fetch: используйте обычный fetch, чтобы запросы проходили через runtime guards
  • запись в source snapshot directory
  • direct imports/linking между функциями
  • nested workers и process control
  • private network egress: private IP ranges, host-local addresses, metadata endpoints и SMTP

Если функция использует запрещённый API, runtime возвращает policy error; в deployment detail он показывается как customer-safe ошибка вместе с function logs.

LimitHobbyProEnterprise
Concurrent invocations432256
Memory per worker256 MB1 GB4 GB
CPU per invocation250 ms1000 ms4000 ms
Wall timeout10 s60 s300 s
Pending queue162561024
Warm retention (макс.)5 s30 s300 s
Response body4 MB10 MB50 MB
Route pipeline steps3816
KV operations / min1 00010 000100 000
KV value size64 KB256 KB1 MB
Source entry files / function111
Source entry file size128 KB128 KBSupport-approved

Function logs отправляются в обычный deployment log pipeline и доступны на странице деплоя в блоке ONREZA Functions. Каждая запись сохраняет function identity, revision/layer context в beta, invocation id, runtime и уровень лога.

Runtime errors в beta мапятся в короткие customer-safe категории:

Runtime codeЧто означает
FUNCTION_BACKPRESSUREОчередь invocation заполнена или concurrency cap достигнут.
FUNCTION_TIMEOUTInvocation превысил wall timeout.
FUNCTION_POLICY_VIOLATIONИспользован запрещённый API/import.
FUNCTION_HANDLER_ERRORHandler выбросил ошибку или вернул не Response.
FUNCTION_DISABLEDФункция приостановлена (kill switch или авто-защита: error-loop, рекурсия, превышение объёма).
PLATFORM_ERRORRuntime path не смог завершить invocation.

Функции дешёвые и self-serve, поэтому платформа защищает вас от runaway-расходов per-функция и per-окружение:

  • если функция стабильно ошибается, зацикливает вызовы (например, дёргает собственный route) или превышает безопасный объём — срабатывает защита: если сбой связан с недавней публикацией, платформа сперва откатывается на прошлую рабочую revision; иначе функция приостанавливается в этом окружении, перестаёт тарифицироваться, и вы получаете уведомление;
  • приостановка одной функции на preview никогда не затрагивает production — указатели независимы;
  • приостановленная функция отвечает FUNCTION_DISABLED; восстановление — автоматически по cooldown или вручную;
  • вы можете в любой момент сами отключить функцию или отдельную route pipeline привязку.

Перед активацией можно прогнать функцию test-запросом в реальном sandbox, не переключая live-трафик — правьте логику (особенно auth) без риска на проде.

  • Public beta обслуживает HTTP fetch handlers. Queue и scheduled event shapes зарезервированы в runtime protocol, но не являются стабильным customer API.
  • Relative/local helper imports не являются публичным v1 API. Runtime hydrator оставляет seam для будущих version-pinned publish-time snippets/libraries, но customer-authored source сейчас self-contained.
  • Standalone function publish и UI source editor ещё не являются публичным customer API. Целевой API уже должен использовать bounded DB source snapshots, AST-extracted config и environment release activation.
  • Route pipeline (request/response/observe вокруг @app или terminal-функции) — часть продуктовой модели Functions и замена прежним edge middleware. Сейчас стабильный публичный handler — HTTP fetch.
  • Streaming responses в v1 буферизуются и ограничены response body cap.
  • ctx.kv — core binding модели Functions (основной способ хранить state для auth/session/RBAC) и выкатывается в рамках беты. ctx.queue, ctx.sql и ctx.bucket пока зарезервированы и не подключены.
  • Billing в beta использует один public overage meter — BFU-hour. Runtime лимиты остаются hard caps, а per-function counters используются для diagnostics и COGS attribution.

В репозитории есть минимальный пример:

  • examples/onreza-functions/hello-world