run402-mcp 2.2.0 → 2.3.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.
@@ -137,6 +137,27 @@ export interface BlobCdnEnvelope {
137
137
  * Tag emitters require the SHA-256 (only computed when uploaded with
138
138
  * `immutable: true`). On non-immutable uploads, calling them throws.
139
139
  */
140
+ /**
141
+ * One image variant generated by the gateway (v1.49+) on `assets.put` for
142
+ * image MIMEs. Three sizes are produced (`thumb` 320w, `medium` 800w,
143
+ * `large` 1920w) plus a full-resolution `display_jpeg` for HEIC/HEIF
144
+ * sources. WebP-only in v1; AVIF is deferred (see `imgTagWithSrcSet`'s
145
+ * JSDoc for the `<picture>` type-precedence footgun).
146
+ */
147
+ export interface AssetVariant {
148
+ /** Mutable URL of the variant (preferred-host form). */
149
+ url: string;
150
+ /** Content-addressed CDN URL of the variant. Recommended for HTML/CSS. */
151
+ cdn_url: string;
152
+ /** Pixel width of the variant in display orientation. */
153
+ width_px: number;
154
+ /** Pixel height of the variant in display orientation. */
155
+ height_px: number;
156
+ /** Encoded format of the variant. */
157
+ format: "webp" | "jpeg";
158
+ /** Hex SHA-256 of the variant bytes. */
159
+ sha256: string;
160
+ }
140
161
  export interface AssetRef {
141
162
  key: string;
142
163
  size_bytes: number;
@@ -176,6 +197,42 @@ export interface AssetRef {
176
197
  /** CloudFront invalidation envelope. For immutable uploads `cdn.ready ===
177
198
  * true` and no further action is needed. */
178
199
  cdn: BlobCdnEnvelope;
200
+ /** Display-oriented width (post-EXIF rotation) of the source image, in
201
+ * pixels. Present only for image MIMEs against a v1.49+ gateway. */
202
+ width_px?: number;
203
+ /** Display-oriented height (post-EXIF rotation) of the source image, in
204
+ * pixels. Present only for image MIMEs against a v1.49+ gateway. */
205
+ height_px?: number;
206
+ /** Blurhash string suitable for a low-quality image placeholder (LQIP).
207
+ * Present only for image MIMEs against a v1.49+ gateway. */
208
+ blurhash?: string;
209
+ /** Variant spec version. v1 in the v1.49 gateway release. Future
210
+ * spec-version bumps produce different variant bytes at different URLs. */
211
+ variant_spec_version?: string;
212
+ /** Browser-renderable URL for the source. For jpeg/png/webp/avif this
213
+ * equals `cdn_url`. For HEIC/HEIF this points to a generated JPEG
214
+ * display variant so apps render correctly without HEIC-aware code. */
215
+ display_url?: string;
216
+ /** Immutable (content-addressed) form of `display_url`. */
217
+ display_immutable_url?: string;
218
+ /** Generated variant set for image MIMEs ≥320×320. Sub-320 images skip
219
+ * the WebP set (the source IS the thumbnail at that size). `display_jpeg`
220
+ * is present only for HEIC/HEIF sources (full-resolution JPEG transcode). */
221
+ variants?: {
222
+ thumb?: AssetVariant;
223
+ medium?: AssetVariant;
224
+ large?: AssetVariant;
225
+ display_jpeg?: AssetVariant;
226
+ };
227
+ /** Convenience: `variants.thumb.cdn_url` if a thumb variant exists,
228
+ * else `display_url` (acts as the thumbnail for sub-320 images). **Undefined
229
+ * for non-image AssetRefs** — TypeScript narrows accordingly; see also
230
+ * `imgTag()`. */
231
+ thumbUrl?: string;
232
+ /** Convenience: `display_url` falling back to `cdn_url` for images.
233
+ * **Undefined for non-image AssetRefs** — TypeScript narrows accordingly;
234
+ * see also `imgTag()`. */
235
+ displayUrl?: string;
179
236
  /**
180
237
  * Returns a ready-to-paste `<script>` tag with the content-addressed
181
238
  * URL + Subresource Integrity + `crossorigin`. The browser will refuse
@@ -234,8 +291,67 @@ export interface AssetRef {
234
291
  * @example
235
292
  * asset.imgTag("Company logo")
236
293
  * // → <img src="https://pr-abc.run402.com/_blob/logo-a1b2c3d4.png" alt="Company logo" loading="lazy" decoding="async">
294
+ *
295
+ * **v1.49+ HEIC handling:** when the source is HEIC/HEIF, the gateway sets
296
+ * `display_url` to a generated JPEG transcode of the source and `cdn_url`
297
+ * to the original HEIC bytes (which browsers can't render). `imgTag()`
298
+ * defaults `<img src>` to `display_url ?? cdn_url`, so HEIC uploads render
299
+ * correctly without HEIC-specific code in the caller. When the ref carries
300
+ * `width_px` and `height_px`, the emitter also adds `width`/`height`
301
+ * attributes to eliminate Cumulative Layout Shift. Non-image refs omit
302
+ * those attributes silently — `imgTag()` never throws on absence.
237
303
  */
238
304
  imgTag(alt?: string): string;
305
+ /**
306
+ * Returns a `<picture>` element with a responsive WebP `<source>` (three
307
+ * sizes: 320w / 800w / 1920w) and the gateway's `display_url` as the
308
+ * `<img>` fallback. Designed for hero images and any layout where the
309
+ * browser should pick the right resolution per viewport.
310
+ *
311
+ * **Both throw conditions fail loud — no silent fallbacks:**
312
+ *
313
+ * - **`opts.sizes` is required.** Without `sizes`, browsers conservatively
314
+ * download the largest candidate in `srcset` (defeating the variant
315
+ * set). The helper throws `LocalError` with a message that names the
316
+ * issue and gives a copy-pasteable example.
317
+ * - **`variants` must be present.** This helper assumes the gateway
318
+ * generated the three WebP variants (image source ≥320×320, encoded
319
+ * successfully). On non-image refs, sub-320 images, or older gateway
320
+ * responses without variants, the helper throws `LocalError` and tells
321
+ * the caller to use `imgTag()` instead. Silent fallback would render a
322
+ * broken layout (no srcset, no responsive benefit) with no diagnostic.
323
+ *
324
+ * **AVIF footgun — why no `<source type="image/avif">`:** `<picture>`
325
+ * browsers select sources by `type` precedence, not best size. A single
326
+ * AVIF source at 1920w would be picked for thumbnails by AVIF-capable
327
+ * browsers, defeating the variant set. AVIF support, if it returns, must
328
+ * land at all three sizes simultaneously OR via a separate `imgTagHero()`
329
+ * helper that opts in explicitly for above-the-fold heroes.
330
+ *
331
+ * @example
332
+ * ref.imgTagWithSrcSet({
333
+ * alt: "Hero",
334
+ * sizes: "(max-width: 800px) 100vw, 1920px",
335
+ * });
336
+ * // → <picture>
337
+ * // <source type="image/webp"
338
+ * // srcset="<thumb-cdn-url> 320w, <medium-cdn-url> 800w, <large-cdn-url> 1920w"
339
+ * // sizes="(max-width: 800px) 100vw, 1920px">
340
+ * // <img src="<display_url>"
341
+ * // alt="Hero"
342
+ * // width="4032" height="3024"
343
+ * // loading="lazy"
344
+ * // decoding="async">
345
+ * // </picture>
346
+ */
347
+ imgTagWithSrcSet(opts: {
348
+ alt?: string;
349
+ /** REQUIRED. Browser `sizes` attribute (e.g. `"100vw"` or
350
+ * `"(max-width: 800px) 100vw, 1920px"`). Throws when missing/empty. */
351
+ sizes: string;
352
+ /** Default `"lazy"`. Pass `"eager"` for above-the-fold heroes. */
353
+ loading?: "lazy" | "eager";
354
+ }): string;
239
355
  }
240
356
  /**
241
357
  * Return type of `client.blobs.put`. v1.45 widens this to AssetRef; the
@@ -1 +1 @@
1
- {"version":3,"file":"assets.types.d.ts","sourceRoot":"","sources":["../../src/namespaces/assets.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"assets.types.d.ts","sourceRoot":"","sources":["../../src/namespaces/assets.types.ts"],"names":[],"mappings":"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"}
@@ -145,5 +145,10 @@ export interface ByteReader {
145
145
  (): Promise<Uint8Array>;
146
146
  label?: string;
147
147
  contentType?: string;
148
+ /** Which spec slice category registered this byte reader. Set by the
149
+ * slice-tagged `remember` in `normalizeReleaseSpec` and surfaces on
150
+ * `content.upload.*` events so callers can group telemetry by slice.
151
+ * Cross-kind CAS dedup escalates the value to `"mixed"`. */
152
+ slice?: "release" | "asset" | "mixed";
148
153
  }
149
154
  //# sourceMappingURL=deploy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAiB3C,OAAO,KAAK,EACV,YAAY,EACZ,sBAAsB,EAQtB,WAAW,EACX,oBAAoB,EAEpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EAYrB,iBAAiB,EAGjB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,2BAA2B,EAC3B,uBAAuB,EACvB,WAAW,EACX,oBAAoB,EACpB,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAiE3B,qBAAa,MAAM;IACL,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C;;;;OAIG;IACG,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IA4C9E;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAI3E;;;;OAIG;IACG,IAAI,CACR,IAAI,EAAE,WAAW,EACjB,IAAI,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GACvD,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;KAAE,CAAC;IAIxE;;;;;OAKG;IACG,MAAM,CACV,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;KACxC,GACA,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;OAKG;IACG,MAAM,CACV,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QACJ,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;QACvC,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;KACb,GACL,OAAO,CAAC,YAAY,CAAC;IAMxB;;;;;;;;;OASG;IACG,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GACtE,OAAO,CAAC,YAAY,CAAC;IAqBxB;;;;OAIG;IACG,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAC9B,OAAO,CAAC,iBAAiB,CAAC;IAmB7B;;;;;;OAMG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAC/B,OAAO,CAAC,kBAAkB,CAAC;IA6B9B;;;;;;;;OAQG;IACG,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,oBAAoB,CAAC;IAmBhC;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2B9E;;;;OAIG;IACG,gBAAgB,CACpB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,sBAAsB,CAAC;IAqBlC;;;;OAIG;IACG,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA+BnE;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAW1E;AAs1CD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;iEAG6D;IAC7D,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;CACvC"}
@@ -362,8 +362,29 @@ function requireNonEmptyStringQueryOption(value, label, context) {
362
362
  return value;
363
363
  }
364
364
  // ─── Internal pipeline ───────────────────────────────────────────────────────
365
+ /**
366
+ * Compute the sorted set of slice kinds the spec carried. Surfaces on
367
+ * `commit.phase` and `ready` events so agents can group per-phase
368
+ * telemetry by slice category. `assets` slice → `"asset"`; any of
369
+ * `database` / `functions` / `site` → `"release"`. Order is stable
370
+ * (release before asset).
371
+ */
372
+ function deriveSliceKinds(spec) {
373
+ // Guard against non-object spec — the validate phase throws below
374
+ // (INVALID_SPEC), but this is called before validation in applyOnce so
375
+ // we must not blow up first.
376
+ if (!spec || typeof spec !== "object")
377
+ return [];
378
+ const set = new Set();
379
+ if (spec.database || spec.functions || spec.site)
380
+ set.add("release");
381
+ if (spec.assets)
382
+ set.add("asset");
383
+ return [...set].sort((a, b) => (a === "release" ? -1 : 1));
384
+ }
365
385
  async function applyOnce(client, spec, opts, emit) {
366
386
  const allowWarningCodes = normalizeAllowWarningCodes(opts.allowWarningCodes);
387
+ const sliceKinds = deriveSliceKinds(spec);
367
388
  emit({ type: "plan.started" });
368
389
  const { plan, byteReaders } = await planInternal(client, spec, opts.idempotencyKey);
369
390
  emit({ type: "plan.diff", diff: plan.diff });
@@ -383,10 +404,15 @@ async function applyOnce(client, spec, opts, emit) {
383
404
  // event and resolve before we hit upload.
384
405
  }
385
406
  await uploadMissing(client, spec.project, plan.missing_content, byteReaders, emit);
386
- emit({ type: "commit.phase", phase: "validate", status: "started" });
407
+ emit({
408
+ type: "commit.phase",
409
+ phase: "validate",
410
+ status: "started",
411
+ ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
412
+ });
387
413
  const { planId } = requirePersistedPlan(plan, "applying deploy");
388
414
  const commit = await commitInternal(client, planId, opts.idempotencyKey);
389
- const result = await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project);
415
+ const result = await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
390
416
  // v1.48 unified-apply: thread the plan response's `asset_entries[]` back
391
417
  // into DeployResult.assets so callers reading `result.assets.byKey[key]`
392
418
  // get the gateway-authoritative `AssetRef` envelope (URLs, SRI, etag).
@@ -873,6 +899,7 @@ async function uploadMissing(client, projectId, presence, byteReaders, emit) {
873
899
  label: reader?.label ?? p.sha256,
874
900
  sha256: p.sha256,
875
901
  reason: "present",
902
+ ...(reader?.slice ? { slice_kind: reader.slice } : {}),
876
903
  });
877
904
  }
878
905
  // Filter to refs the gateway reported as missing for this project.
@@ -925,6 +952,7 @@ async function uploadMissing(client, projectId, presence, byteReaders, emit) {
925
952
  sha256: session.sha256,
926
953
  done,
927
954
  total,
955
+ ...(reader.slice ? { slice_kind: reader.slice } : {}),
928
956
  });
929
957
  }
930
958
  // Plan-level finalize — marks the plan committed in the deploy_plans
@@ -1040,7 +1068,7 @@ async function putToS3(fetchFn, url, body, checksumBase64, partNumber) {
1040
1068
  }
1041
1069
  return res.headers.get("etag");
1042
1070
  }
1043
- async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
1071
+ async function pollUntilReady(client, commit, diff, warnings, emit, projectId, sliceKinds = []) {
1044
1072
  if (commit.status === "failed") {
1045
1073
  throw translateGatewayError(commit.error, "commit", null, commit.operation_id);
1046
1074
  }
@@ -1054,7 +1082,12 @@ async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
1054
1082
  context: "committing deploy",
1055
1083
  });
1056
1084
  }
1057
- emit({ type: "ready", releaseId: commit.release_id, urls: commit.urls });
1085
+ emit({
1086
+ type: "ready",
1087
+ releaseId: commit.release_id,
1088
+ urls: commit.urls,
1089
+ ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
1090
+ });
1058
1091
  return {
1059
1092
  release_id: commit.release_id,
1060
1093
  operation_id: commit.operation_id,
@@ -1065,9 +1098,16 @@ async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
1065
1098
  }
1066
1099
  const opHeaders = projectId ? await apikeyHeaders(client, projectId) : {};
1067
1100
  const initialSnapshot = await client.request(`/apply/v1/operations/${encodeURIComponent(commit.operation_id)}`, { headers: opHeaders, context: "fetching deploy operation" });
1068
- return await pollSnapshotUntilReady(client, initialSnapshot, diff, warnings, emit, projectId);
1069
- }
1070
- async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, projectId) {
1101
+ return await pollSnapshotUntilReady(client, initialSnapshot, diff, warnings, emit, projectId, sliceKinds);
1102
+ }
1103
+ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, projectId, sliceKinds = []) {
1104
+ // Helper to spread slice_kinds onto every commit.phase / ready emit so
1105
+ // agents grouping per-slice telemetry don't need to track the apply's
1106
+ // spec separately. The low-level commit/upload helpers that pass no
1107
+ // sliceKinds get an empty array → field is omitted from events.
1108
+ const withSliceKinds = (ev) => sliceKinds.length > 0
1109
+ ? { ...ev, slice_kinds: sliceKinds }
1110
+ : ev;
1071
1111
  let snapshot = initial;
1072
1112
  const opHeaders = projectId ? await apikeyHeaders(client, projectId) : {};
1073
1113
  let lastPhaseEmitted = null;
@@ -1107,7 +1147,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
1107
1147
  return;
1108
1148
  if (nextPhase !== undefined && prev.phase === nextPhase)
1109
1149
  return;
1110
- emit({ type: "commit.phase", phase: prev.phase, status: closeStatus });
1150
+ emit(withSliceKinds({ type: "commit.phase", phase: prev.phase, status: closeStatus }));
1111
1151
  };
1112
1152
  while (true) {
1113
1153
  if (lastPhaseEmitted !== snapshot.status) {
@@ -1115,7 +1155,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
1115
1155
  if (ev) {
1116
1156
  if (ev.type === "commit.phase")
1117
1157
  closePreviousPhase(ev.phase);
1118
- emit(ev);
1158
+ emit(withSliceKinds(ev));
1119
1159
  lastPhaseEmitted = snapshot.status;
1120
1160
  }
1121
1161
  // If `ev` is null (status not in the phase map, e.g. "ready"), leave
@@ -1133,7 +1173,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
1133
1173
  });
1134
1174
  }
1135
1175
  closePreviousPhase();
1136
- emit({ type: "ready", releaseId: snapshot.release_id, urls: snapshot.urls });
1176
+ emit(withSliceKinds({ type: "ready", releaseId: snapshot.release_id, urls: snapshot.urls }));
1137
1177
  return {
1138
1178
  release_id: snapshot.release_id,
1139
1179
  operation_id: snapshot.operation_id,
@@ -1215,12 +1255,18 @@ async function startInternal(client, spec, opts) {
1215
1255
  reason: plan.payment_required.reason,
1216
1256
  });
1217
1257
  }
1258
+ const sliceKinds = deriveSliceKinds(spec);
1218
1259
  const resultPromise = (async () => {
1219
1260
  await uploadMissing(client, spec.project, plan.missing_content, byteReaders, emit);
1220
- emit({ type: "commit.phase", phase: "validate", status: "started" });
1261
+ emit({
1262
+ type: "commit.phase",
1263
+ phase: "validate",
1264
+ status: "started",
1265
+ ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
1266
+ });
1221
1267
  const { planId } = requirePersistedPlan(plan, "starting deploy");
1222
1268
  const commit = await commitInternal(client, planId, opts.idempotencyKey);
1223
- return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project);
1269
+ return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
1224
1270
  })();
1225
1271
  // Avoid an unhandled-rejection at construction time. Consumers must call
1226
1272
  // .result() to actually observe the error.
@@ -2033,7 +2079,13 @@ function invalidSecretSpec(message, resource) {
2033
2079
  }
2034
2080
  async function normalizeReleaseSpec(client, spec) {
2035
2081
  const byteReaders = new Map();
2036
- const remember = (resolved) => {
2082
+ // Slice-tagged `remember`. Each slice category creates its own remember
2083
+ // closure so the registered reader carries `reader.slice = "release" |
2084
+ // "asset"`. On cross-kind dedup (same SHA from both a release-bound
2085
+ // slice and the asset slice) the value escalates to `"mixed"`. This
2086
+ // value surfaces on `content.upload.*` events so agents can group
2087
+ // upload telemetry by slice kind.
2088
+ const makeRemember = (slice) => (resolved) => {
2037
2089
  // Propagate the final content-type onto the deferred reader so the CAS
2038
2090
  // upload session can declare it correctly. Callers may set
2039
2091
  // ref.contentType *after* resolveContent returns (e.g. normalizeFileSet
@@ -2042,18 +2094,25 @@ async function normalizeReleaseSpec(client, spec) {
2042
2094
  resolved.reader.contentType = resolved.ref.contentType;
2043
2095
  }
2044
2096
  if (!byteReaders.has(resolved.ref.sha256)) {
2097
+ resolved.reader.slice = slice;
2045
2098
  byteReaders.set(resolved.ref.sha256, resolved.reader);
2046
2099
  }
2047
2100
  else {
2048
2101
  // Already remembered — but if the existing reader has no contentType
2049
- // and we just learned it, fill it in.
2102
+ // and we just learned it, fill it in. Also escalate slice tag when
2103
+ // the second registration comes from a different kind.
2050
2104
  const existing = byteReaders.get(resolved.ref.sha256);
2051
2105
  if (resolved.ref.contentType && !existing.contentType) {
2052
2106
  existing.contentType = resolved.ref.contentType;
2053
2107
  }
2108
+ if (existing.slice && existing.slice !== slice && existing.slice !== "mixed") {
2109
+ existing.slice = "mixed";
2110
+ }
2054
2111
  }
2055
2112
  return resolved.ref;
2056
2113
  };
2114
+ const rememberRelease = makeRemember("release");
2115
+ const rememberAsset = makeRemember("asset");
2057
2116
  const normalized = { project: spec.project };
2058
2117
  if (spec.base)
2059
2118
  normalized.base = spec.base;
@@ -2074,19 +2133,19 @@ async function normalizeReleaseSpec(client, spec) {
2074
2133
  db.zero_downtime = spec.database.zero_downtime;
2075
2134
  }
2076
2135
  if (spec.database.migrations && spec.database.migrations.length > 0) {
2077
- db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, remember)));
2136
+ db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, rememberRelease)));
2078
2137
  }
2079
2138
  normalized.database = db;
2080
2139
  }
2081
2140
  if (spec.functions) {
2082
2141
  const fns = {};
2083
2142
  if (spec.functions.replace) {
2084
- fns.replace = await normalizeFunctionMap(spec.functions.replace, remember);
2143
+ fns.replace = await normalizeFunctionMap(spec.functions.replace, rememberRelease);
2085
2144
  }
2086
2145
  if (spec.functions.patch) {
2087
2146
  fns.patch = {};
2088
2147
  if (spec.functions.patch.set) {
2089
- fns.patch.set = await normalizeFunctionMap(spec.functions.patch.set, remember);
2148
+ fns.patch.set = await normalizeFunctionMap(spec.functions.patch.set, rememberRelease);
2090
2149
  }
2091
2150
  if (spec.functions.patch.delete)
2092
2151
  fns.patch.delete = spec.functions.patch.delete;
@@ -2096,7 +2155,7 @@ async function normalizeReleaseSpec(client, spec) {
2096
2155
  if (spec.site) {
2097
2156
  const publicPaths = "public_paths" in spec.site ? spec.site.public_paths : undefined;
2098
2157
  if ("replace" in spec.site && spec.site.replace) {
2099
- const map = await normalizeFileSet(spec.site.replace, remember);
2158
+ const map = await normalizeFileSet(spec.site.replace, rememberRelease);
2100
2159
  normalized.site = {
2101
2160
  replace: map,
2102
2161
  ...(publicPaths ? { public_paths: publicPaths } : {}),
@@ -2105,7 +2164,7 @@ async function normalizeReleaseSpec(client, spec) {
2105
2164
  else if ("patch" in spec.site && spec.site.patch) {
2106
2165
  const patch = {};
2107
2166
  if (spec.site.patch.put) {
2108
- patch.put = await normalizeFileSet(spec.site.patch.put, remember);
2167
+ patch.put = await normalizeFileSet(spec.site.patch.put, rememberRelease);
2109
2168
  }
2110
2169
  if (spec.site.patch.delete)
2111
2170
  patch.delete = spec.site.patch.delete;
@@ -2123,7 +2182,7 @@ async function normalizeReleaseSpec(client, spec) {
2123
2182
  // register a byte-reader; emit the wire-shaped `AssetPutEntry[]`.
2124
2183
  // Cross-kind SHA dedup is automatic via the shared `byteReaders` map.
2125
2184
  if (spec.assets) {
2126
- normalized.assets = await normalizeAssetSlice(spec.assets, remember);
2185
+ normalized.assets = await normalizeAssetSlice(spec.assets, rememberAsset);
2127
2186
  }
2128
2187
  return { normalized, byteReaders };
2129
2188
  }
@@ -2186,20 +2245,41 @@ function buildAssetManifestFromPlanEntries(entries) {
2186
2245
  let bytesUploaded = 0;
2187
2246
  let bytesReused = 0;
2188
2247
  for (const entry of entries) {
2248
+ const ref = entry.asset_ref;
2189
2249
  const e = {
2190
2250
  key: entry.key,
2191
2251
  sha256: entry.sha256,
2192
2252
  size_bytes: entry.size_bytes,
2193
2253
  content_type: entry.content_type,
2194
2254
  visibility: entry.visibility,
2195
- url: entry.asset_ref.url,
2196
- immutable_url: entry.asset_ref.immutable_url,
2197
- cdn_url: entry.asset_ref.cdn_url,
2198
- cdn_immutable_url: entry.asset_ref.cdn_immutable_url,
2199
- sri: entry.asset_ref.sri,
2200
- etag: entry.asset_ref.etag,
2201
- content_digest: entry.asset_ref.content_digest,
2255
+ url: ref.url,
2256
+ immutable_url: ref.immutable_url,
2257
+ cdn_url: ref.cdn_url,
2258
+ cdn_immutable_url: ref.cdn_immutable_url,
2259
+ sri: ref.sri,
2260
+ etag: ref.etag,
2261
+ content_digest: ref.content_digest,
2202
2262
  };
2263
+ // v1.49+ image-variant pass-through. Only emitted when the gateway
2264
+ // returned them (image MIMEs ≥320×320; HEIC/HEIF sources also include
2265
+ // `display_jpeg`). Pre-v1.49 plan responses omit these fields entirely
2266
+ // and the manifest entry stays bytewise-identical to before.
2267
+ if (ref.width_px !== undefined)
2268
+ e.width_px = ref.width_px;
2269
+ if (ref.height_px !== undefined)
2270
+ e.height_px = ref.height_px;
2271
+ if (ref.blurhash !== undefined)
2272
+ e.blurhash = ref.blurhash;
2273
+ if (ref.variant_spec_version !== undefined) {
2274
+ e.variant_spec_version = ref.variant_spec_version;
2275
+ }
2276
+ if (ref.display_url !== undefined)
2277
+ e.display_url = ref.display_url;
2278
+ if (ref.display_immutable_url !== undefined) {
2279
+ e.display_immutable_url = ref.display_immutable_url;
2280
+ }
2281
+ if (ref.variants !== undefined)
2282
+ e.variants = ref.variants;
2203
2283
  list.push(e);
2204
2284
  byKey[entry.key] = e;
2205
2285
  manifest[entry.key] = e;