Skip to content

Commit 973b9a9

Browse files
MajorTalclaude
andcommitted
docs(in-function): document @run402/functions v2.7 helpers + v1.54 AssetRef + <Run402Image>
Two additions to the in-function runtime documentation: 1. v2.7+ helpers - getRun402Context(req): zero-dep reader for the x-run402-* request context (requestId, projectId, releaseId, host, locale, defaultLocale). Same shape as Astro.locals.run402, accepts Request / Headers / plain object. Replaces hand-rolled per-field request.headers.get('x-run402-...') across non-Astro functions. - assets.fromRef(raw): re-hydrate a stored AssetRef (JSONB column read from your DB) back into the typed AssetRef shape with camelCase aliases + variant map. Pure-local, no network. Recommended persistence pattern stores the full AssetRef as JSONB so variant SHAs + immutable URLs survive the round-trip. 2. v1.54 AssetRef shape-contract fields (paired with v2.12.0 SDK changes) - blurhash_data_url: pre-decoded ~600-byte PNG data URL. Embed directly as background-image - zero render-time decode, no client-side blurhash library. - asset_schema: semver shape-contract stamp ("v1.49" | "v1.50" | "v1.54" | null for partial-shape rows). Strict-mode consumers use it to skip legacy rows without per-field existence branching. - Pointer to <Run402Image> in @run402/astro@1.0+ as the consumer reference impl. cli/llms-cli.txt + SKILL.md updated. 120/120 SKILL tests + 32/32 sync tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent db3f9b3 commit 973b9a9

2 files changed

Lines changed: 11 additions & 2 deletions

File tree

SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ export default async (req: Request) => {
335335
- **`ai.generateImage({ prompt, aspect? })`** — live image generation from deployed functions, billed/rate-limited against the project billing account through `RUN402_SERVICE_KEY`. Aspects: `square`, `landscape`, `portrait`; result: `{ image, content_type, aspect }`. For public routed functions, authenticate/rate-limit app users before calling it.
336336
- **`assets.put(key, source, opts?)`** — upload runtime bytes through the same CAS-backed apply substrate as deploy-time assets. `source` is a string, `Uint8Array`, or `{ content | bytes }`; returns an SDK-compatible `AssetRef`. v1.50 `opts` accept `metadata` (flat bag, ≤4 KB, leaves `string | number | boolean | string[]`) and `exifPolicy` (`"keep"` | `"strip"`); the returned `AssetRef` includes `image_format`, `image_info`, `image_exif`, and `image_exif_policy` for image MIMEs.
337337
- **`getUserId(req)` / `getRole(req)`** (v1.51+, `@run402/functions` 2.5+) — typed reads of the `x-run402-user-id` / `x-run402-user-role` headers the gateway injects when a `FunctionSpec.requireAuth` / `requireRole` gate passed. Both return `string | null`. Use these inside a gated function instead of re-decoding the JWT — the gate already verified the caller and resolved the application role. `getRole(req)` is non-null only when `requireRole` ran (the value is guaranteed to be in `requireRole.allowed`). The JWT `role` from `getUser(req)` is the system role (`anon`/`authenticated`/…), NOT the app role — don't conflate them.
338+
- **`getRun402Context(req)`** (v1.52+, `@run402/functions` 2.7+) — zero-dependency reader for the full per-request context the gateway populates as `x-run402-*` headers. Returns `{ requestId, projectId, releaseId, host, locale, defaultLocale }` (all `string | null`). Use this in non-Astro functions (plain webhook handlers, auth endpoints) instead of hand-rolling `request.headers.get('x-run402-...')` per field — the helper papers over `Request`/`Headers`/plain-object header shapes and any future gateway header renames. Same return shape as `Astro.locals.run402`, so Astro and plain-function code share one mental model. The helper never throws; missing headers come back as `null`.
339+
- **`assets.fromRef(raw)`** (`@run402/functions` 2.7+) — re-hydrate a stored AssetRef (e.g., a JSONB column read from your DB) back into the typed `AssetRef` shape with camelCase aliases + variant map. Pure-local; no network. The recommended persistence pattern is to store the full `AssetRef` returned by `r.assets.put` as JSONB so the variant SHAs + immutable URLs the gateway computed at upload time survive the round-trip (these can't be re-derived from `(source_sha, key)` alone). Tolerant of partial inputs: pre-v1.49 blobs come back without `variants` / `width_px` rather than synthesizing them. Throws only on null/undefined or non-object input.
338340

339341
Fluent surface on both `db(req).from(t)` and `adminDb().from(t)`:
340342
- Reads: `.select()`, `.eq()`, `.neq()`, `.gt()`, `.lt()`, `.gte()`, `.lte()`, `.like()`, `.ilike()`, `.in()`, `.order()`, `.limit()`, `.offset()`
@@ -456,7 +458,7 @@ Reference: [`astro/README.md`](./astro/README.md) (top section), [`cli/llms-cli.
456458

457459
### Blob storage (content-addressed CDN)
458460

459-
- **`assets_put`** — upload (any size, up to 5 TiB). Returns an `AssetRef` with `cdn_url`, `sri`, `etag`, `cache_kind`. v1.50: accepts `metadata` (flat bag with `string | number | boolean | string[]` leaves, ≤4 KB) and `exif_policy` (`"keep"` | `"strip"`); response includes `image_format`, `image_info`, `image_exif`, and `image_exif_policy` for image MIMEs. Bad shapes throw `INVALID_ASSET_METADATA` / `INVALID_EXIF_POLICY` before the HTTP call.
461+
- **`assets_put`** — upload (any size, up to 5 TiB). Returns an `AssetRef` with `cdn_url`, `sri`, `etag`, `cache_kind`. v1.50: accepts `metadata` (flat bag with `string | number | boolean | string[]` leaves, ≤4 KB) and `exif_policy` (`"keep"` | `"strip"`); response includes `image_format`, `image_info`, `image_exif`, and `image_exif_policy` for image MIMEs. Bad shapes throw `INVALID_ASSET_METADATA` / `INVALID_EXIF_POLICY` before the HTTP call. v1.54: image uploads also return `blurhash_data_url` (pre-decoded ~600-byte PNG data URL — embed as `background-image` for the placeholder, no client-side decoder) and `asset_schema` (semver shape-contract stamp: `"v1.49"` | `"v1.50"` | `"v1.54"` | `null` for partial-shape rows). When persisting an AssetRef for later render, store the full object as JSONB — the variant SHAs and immutable URLs can't be re-derived from `(source_sha, key)` alone. For Astro consumers, **`<Run402Image>` from `@run402/astro@1.0+`** consumes all of the above directly with zero render-time decode and optional strict-mode schema filtering.
460462
- **`assets_get`** — download to a local file (no context-window bloat).
461463
- **`assets_ls`** — keyset-paginated list with prefix filter. v1.50: accepts `sort` (`key:asc` default, `createdAt:asc`, `createdAt:desc`) and `filter` (keys: `uploaded_by`, `tag`, `format`, `is_image`, `min_width`/`max_width`/`min_height`/`max_height`). Cursor is sort-pinned — cross-sort reuse returns `INVALID_CURSOR_FOR_SORT`.
462464
- **`assets_rm`** — delete.

cli/llms-cli.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,10 @@ Built-in helper: `import { db, adminDb, email, ai, assets, getUser } from '@run4
791791
- `email.send(opts)` — send email from the project's mailbox (see email section below)
792792
- `ai.generateImage({ prompt, aspect? })` — live image generation from deployed functions using project billing authority, not local allowance/x402 signing. Aspects: `square`, `landscape`, `portrait`; result: `{ image, content_type, aspect }`. Add app auth/rate limits before calling it from public routed functions.
793793
- `assets.put(key, source, opts?)` — upload bytes to the project's blob store from inside a deployed function. Uses the same CAS substrate as deploy-time assets. `source` is a string, `Uint8Array`, or `{ content | bytes }` object. Options: `contentType`, `visibility` (`"public"` | `"private"`, default `"public"`), `immutable` (default `true`). Returns an `AssetRef` with `url`, `immutableUrl`, `cdnUrl`, `sha256`, `size_bytes`, etc. (camelCase aliases included). Use for user-uploaded content, generated images, runtime-produced files.
794+
- `assets.fromRef(raw)` (v2.7+) — re-hydrate a stored AssetRef (JSONB column read from your DB) into the typed `AssetRef` shape with camelCase aliases + variant map. Pure-local, no network — safe in SSR render paths. The recommended persistence pattern is to store the full `AssetRef` returned by `r.assets.put` as a JSONB column so variant SHAs + immutable URLs survive the round-trip (they can't be re-derived from `(source_sha, key)` alone). Tolerant of partial inputs: pre-v1.49 blobs come back without `variants` / `width_px` rather than synthesizing them. Throws only on null/undefined or non-object input.
795+
- `getRun402Context(req)` (v2.7+) — zero-dependency reader for the full per-request context the gateway populates as `x-run402-*` headers. Returns `{ requestId, projectId, releaseId, host, locale, defaultLocale }` (all `string | null`). Use in non-Astro functions (plain webhook handlers, auth endpoints) instead of hand-rolling `req.headers.get('x-run402-...')` per field — the helper papers over `Request` / `Headers` / plain-object header shapes and any future gateway header renames. Same return shape as `Astro.locals.run402`. Never throws; missing headers come back as `null`.
794796

795-
**TypeScript types**: `npm install @run402/functions` to get full autocomplete for `db(req)`, `adminDb()`, `getUser()`, `email.send()`, `ai.translate()`, `ai.generateImage()`, and `assets.put()`. Works in any Node.js/TypeScript project (Astro, Next.js, plain TS). For static site generation, use `adminDb().from()` at build time with `RUN402_SERVICE_KEY` + `RUN402_PROJECT_ID` in your `.env`.
797+
**TypeScript types**: `npm install @run402/functions` to get full autocomplete for `db(req)`, `adminDb()`, `getUser()`, `getUserId()`, `getRole()`, `getRun402Context()`, `email.send()`, `ai.translate()`, `ai.generateImage()`, `assets.put()`, and `assets.fromRef()`. Works in any Node.js/TypeScript project (Astro, Next.js, plain TS). For static site generation, use `adminDb().from()` at build time with `RUN402_SERVICE_KEY` + `RUN402_PROJECT_ID` in your `.env`.
796798

797799
#### db(req).from(table) — caller-context, RLS applies
798800

@@ -1017,6 +1019,11 @@ Metadata + EXIF policy + intrinsics fields (v1.50+ gateway, `@run402/sdk@2.4.0+`
10171019
- `image_exif_policy` — echo of the EXIF policy actually applied: `"keep"` (default) or `"strip"`. `null` for non-image rows.
10181020
- These fields ride alongside the v1.49 `width_px` / `height_px` / `blurhash` / `variants` additions — same FLAT shape, NOT wrapped under `image: {}`.
10191021

1022+
Shape-contract fields (v1.54+ gateway, `@run402/sdk@2.12.0+`, populated atomically with variants at upload time):
1023+
- `blurhash_data_url` — pre-decoded PNG data URL (~600-1200 bytes typical at 16×16) computed once at upload from the canonical pinned BlurHash decoder. Embed directly as `background-image` for the placeholder — zero render-time decode, no client-side blurhash library needed. `null` only when the decoder failed on otherwise-valid input (rare). Absent for pre-v1.54 uploads.
1024+
- `asset_schema` — semver shape-contract stamp recording the highest contract this row's fields satisfy (`"v1.49"` | `"v1.50"` | `"v1.54"` | `null`). `null` marks partial-shape rows (e.g., HEIC sources missing `display_jpeg`). Strict-mode consumers read this to skip legacy rows without per-field existence branching. Absent for pre-v1.54 uploads.
1025+
- The v1.54 substrate enables the **`<Run402Image>` component** in `@run402/astro@1.0+`: pre-decoded placeholder + WebP-ladder responsive `<picture>` + width/height for zero-CLS, with optional `imageDefaults.strict: { onSchema: ">=v1.49" }` to hard-fail on legacy rows during the rollout that follows every schema migration. Astro + React entries; byte-identical HTML output across both.
1026+
10201027
Typed errors a caller can branch on (`catch (e) { if (e.code === "...") }`):
10211028
- `INVALID_ASSET_METADATA` (HTTP 400 or `LocalError` pre-network)
10221029
- `INVALID_EXIF_POLICY` (HTTP 400 or `LocalError` pre-network)

0 commit comments

Comments
 (0)