Skip to content

Commit 1a1a837

Browse files
MajorTalclaude
andcommitted
feat(sdk): extend AssetRef with blurhash_data_url + asset_schema (v1.54)
The v1.54 gateway encoder writes two new fields to internal.blobs and @run402/functions v2.7.0 surfaces them on AssetRef. The public SDK was missing them — consumers that rehydrate stored AssetRefs or render the v1.54 placeholder substrate had to fall back to `any` casts. Threaded through all four surfaces: - AssetRef (sdk/src/namespaces/assets.types.ts) — public envelope - ResolvedAssetRef + AssetManifestEntry (deploy.types.ts) — wire shapes - UploadCompleteResponse → buildAssetRef (assets.ts) — single-asset put - ResolvedAssetRef → buildAssetManifestFromPlanEntries (deploy.ts) — apply Omit-when-undefined throughout so pre-v1.54 gateway envelopes stay bytewise-identical to before. Matches the v1.49 pattern (not the v1.50 widen-to-null pattern), since these fields are only present when the v1.54+ encoder ran on an image source. Build clean, 555/555 unit tests pass, 42/42 doc snippets compile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c7f4d34 commit 1a1a837

4 files changed

Lines changed: 56 additions & 0 deletions

File tree

sdk/src/namespaces/assets.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ interface UploadCompleteResponse {
151151
image_info?: Record<string, unknown> | null;
152152
image_exif?: Record<string, unknown> | null;
153153
image_exif_policy?: "keep" | "strip" | null;
154+
// v1.54+ shape-contract fields. Absent for pre-v1.54 gateways and for
155+
// non-image uploads. Threaded through to AssetRef using the omit-when-
156+
// undefined pattern so the envelope stays bytewise-identical on older
157+
// gateway versions.
158+
blurhash_data_url?: string | null;
159+
asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
154160
}
155161

156162
function escapeHtmlAttr(s: string): string {
@@ -318,6 +324,15 @@ function buildAssetRef(
318324
image_exif: imageExifField,
319325
image_exif_policy: imageExifPolicyField,
320326

327+
// v1.54: shape-contract fields. Omit-when-undefined so pre-v1.54
328+
// envelopes stay bytewise-identical (no null injection).
329+
...(resp.blurhash_data_url !== undefined
330+
? { blurhash_data_url: resp.blurhash_data_url }
331+
: {}),
332+
...(resp.asset_schema !== undefined
333+
? { asset_schema: resp.asset_schema }
334+
: {}),
335+
321336
scriptTag(opts) {
322337
// Default `defer: true` — modern best practice. Defer prevents
323338
// render-blocking when placed in <head> and is a no-op when placed
@@ -601,6 +616,13 @@ export class Assets {
601616
...(entry.image_exif_policy !== undefined
602617
? { image_exif_policy: entry.image_exif_policy }
603618
: {}),
619+
// v1.54 shape-contract pass-through.
620+
...(entry.blurhash_data_url !== undefined
621+
? { blurhash_data_url: entry.blurhash_data_url }
622+
: {}),
623+
...(entry.asset_schema !== undefined
624+
? { asset_schema: entry.asset_schema }
625+
: {}),
604626
};
605627
return buildAssetRef(completion, contentType);
606628
}

sdk/src/namespaces/assets.types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,26 @@ export interface AssetRef {
452452
/** Echo of the EXIF policy actually applied to the stored bytes. `null`
453453
* for non-image uploads. */
454454
image_exif_policy: ExifPolicy | null;
455+
456+
// ---- v1.54+ shape-contract fields ----------------------------------------
457+
// Populated atomically at upload time when the v1.54+ gateway encoder runs.
458+
// Surfaced on AssetRef so consumers (`<Run402Image>`, agent code that
459+
// rehydrates stored AssetRefs) can render placeholders + apply
460+
// schema-filtered strict-mode without an extra DB roundtrip.
461+
462+
/** Pre-decoded PNG data URL (~600-1200 bytes typical at 16×16) computed
463+
* once at upload time from the canonical pinned BlurHash decoder.
464+
* `<Run402Image>` emits this as the placeholder `background-image` so
465+
* render is pure-local (no SSR-time decode). `null` when the decoder
466+
* failed on otherwise-valid input (rare). Omitted for non-image and
467+
* pre-v1.54 uploads. */
468+
blurhash_data_url?: string | null;
469+
/** Semver shape-contract stamp recording the highest contract this row's
470+
* fields satisfy. `null` for partial-shape rows (e.g., HEIC sources
471+
* missing `display_jpeg`). Read by `<Run402Image>` strict mode
472+
* (`imageDefaults.strict: { onSchema: ">=v1.49" }`) to skip legacy rows.
473+
* Omitted for pre-v1.54 uploads. */
474+
asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
455475
}
456476

457477
/**

sdk/src/namespaces/deploy.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,6 +3204,10 @@ function buildAssetManifestFromPlanEntries(
32043204
if (ref.image_info !== undefined) e.image_info = ref.image_info;
32053205
if (ref.image_exif !== undefined) e.image_exif = ref.image_exif;
32063206
if (ref.image_exif_policy !== undefined) e.image_exif_policy = ref.image_exif_policy;
3207+
// v1.54 pass-through: shape-contract fields. Pre-v1.54 plan responses
3208+
// omit them; the manifest entry stays bytewise-identical to before.
3209+
if (ref.blurhash_data_url !== undefined) e.blurhash_data_url = ref.blurhash_data_url;
3210+
if (ref.asset_schema !== undefined) e.asset_schema = ref.asset_schema;
32073211
list.push(e);
32083212
byKey[entry.key] = e;
32093213
manifest[entry.key] = e;

sdk/src/namespaces/deploy.types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,11 @@ export interface ResolvedAssetRef {
15281528
image_info?: Record<string, unknown> | null;
15291529
image_exif?: Record<string, unknown> | null;
15301530
image_exif_policy?: "keep" | "strip" | null;
1531+
1532+
// v1.54+ shape-contract fields. Populated atomically at upload time when
1533+
// the gateway encoder runs; absent on pre-v1.54 plan responses.
1534+
blurhash_data_url?: string | null;
1535+
asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
15311536
}
15321537

15331538
export interface AssetSyncPlanBlock {
@@ -2309,6 +2314,11 @@ export interface AssetManifestEntry {
23092314
image_info?: Record<string, unknown> | null;
23102315
image_exif?: Record<string, unknown> | null;
23112316
image_exif_policy?: "keep" | "strip" | null;
2317+
2318+
// v1.54+ shape-contract fields, threaded from `ResolvedAssetRef`. Absent
2319+
// on pre-v1.54 plan responses.
2320+
blurhash_data_url?: string | null;
2321+
asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
23122322
}
23132323

23142324
// ─── Apply / start / low-level options ───────────────────────────────────────

0 commit comments

Comments
 (0)