← Val TownCONTENT HISTORY

Update to Val Town

Snapshot Sep 30, 2026 · 22:50 UTC · version 3.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "blob-storage",
  "description": "Use when a val needs simple key/value persistence — JSON documents, cached responses, uploaded files, or binary assets. Covers the std/blob API, listing and deleting keys, account-global or val scoping, and storage limits.",
  "included_files": [],
  "skill_md_contents": "---\nname: blob-storage\ndescription: Use when a val needs simple key/value persistence — JSON documents, cached responses, uploaded files, or binary assets. Covers the std/blob API, listing and deleting keys, account-global or val scoping, and storage limits.\n---\n\n# Blob Storage\n\nVal Town provides built-in key/value blob storage via the `std/blob` module. Reach for it whenever a val needs to persist simple values — JSON documents, cached API responses, uploaded files, or binary assets — keyed by a string. For relational or structured data you query with SQL, prefer `std/sqlite` instead.\n\n## Scoping: account-global or per-val depending on import\n\nThere are two exports of the blob utility: `global.ts`, which is scoped to the user account, and `main.ts`, which is scoped to the val itself. Prefer the `main.ts` interface and val scoping for new vals.\n\nHere is the scoped import:\n\n```ts\n/**\n * Importing from `main.ts` provides an interface to val-scoped blobs.\n */\nimport { blob } from \"https://esm.town/v/std/blob/main.ts\";\n```\n\nHere are the global imports:\n\n```ts\n/**\n * Importing from `global.ts` provides a blob interface that is scoped\n * to your account.\n */\nimport { blob } from \"https://esm.town/v/std/blob/global.ts\";\n/**\n * This entrypoint is also available as `v/std/blob`. This is common\n * in older vals.\n */\nimport { blob } from \"https://esm.town/v/std/blob\";\n```\n\nThe scoped & global `blob` interfaces have the same methods.\n\nScoped & global blobs are stored separately: you cannot access global blobs with the scoped interface or vice versa.\n\n## Basic usage (JSON)\n\n```ts\nimport { blob } from \"https://esm.town/v/std/blob/main.ts\";\n\nawait blob.setJSON(\"config\", { theme: \"dark\", count: 0 });\n\nconst config = await blob.getJSON(\"config\");\n// config = { theme: \"dark\", count: 0 }, or undefined if the key doesn't exist\n```\n\n`getJSON` returns `undefined` when the key is missing, so guard before using the result:\n\n```ts\nconst config = (await blob.getJSON(\"config\")) ?? { theme: \"light\", count: 0 };\n```\n\n## Raw and binary data\n\nUse `set`/`get` for strings, binary, or any `BodyInit`. `get` returns a standard `Response`, so use its body helpers (`.text()`, `.json()`, `.arrayBuffer()`, `.blob()`):\n\n```ts\nawait blob.set(\"logo.png\", imageBytes); // string | BodyInit (Blob, ArrayBuffer, ReadableStream, …)\n\nconst res = await blob.get(\"logo.png\");\nconst bytes = await res.arrayBuffer();\n```\n\nUnlike `getJSON`, `get` **throws** `ValTownBlobNotFoundError` if the key doesn't exist — wrap it in `try/catch` when the key may be absent.\n\n## Listing, deleting, copying\n\n```ts\nconst entries = await blob.list(\"user_\"); // optional key prefix filter\n// entries = [{ key, size, lastModified }, …]\n\nfor (const { key } of entries) {\n  await blob.delete(key);\n}\n\nawait blob.copy(\"config\", \"config.bak\"); // duplicate under a new key\nawait blob.move(\"draft\", \"published\");   // rename / relocate\n```\n\n`list(prefix?)` returns an array of `{ key: string; size: number; lastModified: string }` — objects, not bare key strings.\n\n\n## Limits\n\n- **Key length:** up to 512 characters.\n- **Total storage:** 10 MB on the free plan, 1 GB on Pro — shared across all blobs in the account.\n- Store large or structured datasets in `std/sqlite` rather than as one giant blob.\n\n## Reading/writing blobs via tools\n\nWhen using the `storeBlob`, `readBlob`, `listBlobs`, or `deleteBlob` tools against a val owned by an organization (not your personal account), pass the org handle as the `org` parameter so the call hits that organization's blob storage. Example: `{ key: \"myapp:config\", org: \"some-org\" }`. This only matters for the tool calls — code inside the val reads and writes its owning account's storage automatically. Note `storeBlob` accepts UTF-8 text up to 100 KB; write larger or binary blobs from code with `blob.set`.\n\n## Rules\n\n- Treat keys as a flat namespace. Use prefixes (`feature:subkey`) for organization and to scope `list`.\n- `getJSON` returns `undefined` for missing keys; `get` throws `ValTownBlobNotFoundError`. Handle the absent case accordingly.\n- Don't store secrets in blobs — use environment variables for credentials.\n\n## Reference\n\nFull API docs: https://docs.val.town/std/blob/\n"
}

SHA-256: c84bdf59b1b77b05e0fe0719d623276bafcb666c4ca78ebc79e730087f6d5ef8