@run402/sdk 2.2.0 → 2.3.1
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.
- package/README.md +70 -0
- package/dist/namespaces/assets.d.ts.map +1 -1
- package/dist/namespaces/assets.js +129 -2
- package/dist/namespaces/assets.js.map +1 -1
- package/dist/namespaces/assets.types.d.ts +116 -0
- package/dist/namespaces/assets.types.d.ts.map +1 -1
- package/dist/namespaces/deploy.d.ts +5 -0
- package/dist/namespaces/deploy.d.ts.map +1 -1
- package/dist/namespaces/deploy.js +165 -27
- package/dist/namespaces/deploy.js.map +1 -1
- package/dist/namespaces/deploy.types.d.ts +44 -0
- package/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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,16 +404,79 @@ 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({
|
|
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).
|
|
393
419
|
// Release-only applies leave `result.assets` undefined.
|
|
394
420
|
if (plan.asset_entries && plan.asset_entries.length > 0) {
|
|
395
421
|
result.assets = buildAssetManifestFromPlanEntries(plan.asset_entries);
|
|
422
|
+
// v1.49 image-variant follow-up: variants are generated AT COMMIT TIME
|
|
423
|
+
// (parent gateway change Section 5 — `prepareStagedAssetVariants` runs
|
|
424
|
+
// before the activation txn opens). The plan that funded `result.assets`
|
|
425
|
+
// was built BEFORE commit, so its `asset_entries[].asset_ref` doesn't
|
|
426
|
+
// include the variant fields (the gateway only threads `image_data`
|
|
427
|
+
// through `buildAssetRefForPlan` for SHAs that ALREADY have variants
|
|
428
|
+
// in `internal.blob_image_variants`).
|
|
429
|
+
//
|
|
430
|
+
// For image puts, do a dry-run re-plan with the same spec. Bytes are
|
|
431
|
+
// now in CAS (the commit just landed) AND variant rows exist in the
|
|
432
|
+
// DB. The new plan response surfaces the variants. Dry-run keeps it
|
|
433
|
+
// cheap: no new plan_id or operation_id rows are created.
|
|
434
|
+
//
|
|
435
|
+
// Best-effort: a re-plan failure shouldn't fail the apply. The bytes
|
|
436
|
+
// are committed, the release is live, and the variant fields are
|
|
437
|
+
// strictly additive — leaving them empty when the recheck errors is
|
|
438
|
+
// worse than failing the apply.
|
|
439
|
+
const hasImagePut = spec.assets?.put?.some((entry) => {
|
|
440
|
+
const ct = ("content_type" in entry && typeof entry.content_type === "string")
|
|
441
|
+
? entry.content_type
|
|
442
|
+
: "";
|
|
443
|
+
return ct.startsWith("image/");
|
|
444
|
+
});
|
|
445
|
+
if (hasImagePut) {
|
|
446
|
+
try {
|
|
447
|
+
const { plan: recheck } = await planInternal(client, spec, undefined, true);
|
|
448
|
+
if (recheck.asset_entries && recheck.asset_entries.length > 0) {
|
|
449
|
+
for (const recheckEntry of recheck.asset_entries) {
|
|
450
|
+
const existing = result.assets.byKey[recheckEntry.key];
|
|
451
|
+
if (!existing)
|
|
452
|
+
continue;
|
|
453
|
+
const ref = recheckEntry.asset_ref;
|
|
454
|
+
if (ref.width_px !== undefined)
|
|
455
|
+
existing.width_px = ref.width_px;
|
|
456
|
+
if (ref.height_px !== undefined)
|
|
457
|
+
existing.height_px = ref.height_px;
|
|
458
|
+
if (ref.blurhash !== undefined)
|
|
459
|
+
existing.blurhash = ref.blurhash;
|
|
460
|
+
if (ref.variant_spec_version !== undefined) {
|
|
461
|
+
existing.variant_spec_version = ref.variant_spec_version;
|
|
462
|
+
}
|
|
463
|
+
if (ref.display_url !== undefined)
|
|
464
|
+
existing.display_url = ref.display_url;
|
|
465
|
+
if (ref.display_immutable_url !== undefined) {
|
|
466
|
+
existing.display_immutable_url = ref.display_immutable_url;
|
|
467
|
+
}
|
|
468
|
+
if (ref.variants !== undefined)
|
|
469
|
+
existing.variants = ref.variants;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
// Best-effort: leave variant fields unpopulated rather than
|
|
475
|
+
// failing a successful apply. Consumers can re-call
|
|
476
|
+
// `r.assets.put` with the same bytes (dedup) to trigger the
|
|
477
|
+
// plan-time surfacing if variants are missing.
|
|
478
|
+
}
|
|
479
|
+
}
|
|
396
480
|
}
|
|
397
481
|
return result;
|
|
398
482
|
}
|
|
@@ -873,6 +957,7 @@ async function uploadMissing(client, projectId, presence, byteReaders, emit) {
|
|
|
873
957
|
label: reader?.label ?? p.sha256,
|
|
874
958
|
sha256: p.sha256,
|
|
875
959
|
reason: "present",
|
|
960
|
+
...(reader?.slice ? { slice_kind: reader.slice } : {}),
|
|
876
961
|
});
|
|
877
962
|
}
|
|
878
963
|
// Filter to refs the gateway reported as missing for this project.
|
|
@@ -925,6 +1010,7 @@ async function uploadMissing(client, projectId, presence, byteReaders, emit) {
|
|
|
925
1010
|
sha256: session.sha256,
|
|
926
1011
|
done,
|
|
927
1012
|
total,
|
|
1013
|
+
...(reader.slice ? { slice_kind: reader.slice } : {}),
|
|
928
1014
|
});
|
|
929
1015
|
}
|
|
930
1016
|
// Plan-level finalize — marks the plan committed in the deploy_plans
|
|
@@ -1040,7 +1126,7 @@ async function putToS3(fetchFn, url, body, checksumBase64, partNumber) {
|
|
|
1040
1126
|
}
|
|
1041
1127
|
return res.headers.get("etag");
|
|
1042
1128
|
}
|
|
1043
|
-
async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
|
|
1129
|
+
async function pollUntilReady(client, commit, diff, warnings, emit, projectId, sliceKinds = []) {
|
|
1044
1130
|
if (commit.status === "failed") {
|
|
1045
1131
|
throw translateGatewayError(commit.error, "commit", null, commit.operation_id);
|
|
1046
1132
|
}
|
|
@@ -1054,7 +1140,12 @@ async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
|
|
|
1054
1140
|
context: "committing deploy",
|
|
1055
1141
|
});
|
|
1056
1142
|
}
|
|
1057
|
-
emit({
|
|
1143
|
+
emit({
|
|
1144
|
+
type: "ready",
|
|
1145
|
+
releaseId: commit.release_id,
|
|
1146
|
+
urls: commit.urls,
|
|
1147
|
+
...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
|
|
1148
|
+
});
|
|
1058
1149
|
return {
|
|
1059
1150
|
release_id: commit.release_id,
|
|
1060
1151
|
operation_id: commit.operation_id,
|
|
@@ -1065,9 +1156,16 @@ async function pollUntilReady(client, commit, diff, warnings, emit, projectId) {
|
|
|
1065
1156
|
}
|
|
1066
1157
|
const opHeaders = projectId ? await apikeyHeaders(client, projectId) : {};
|
|
1067
1158
|
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) {
|
|
1159
|
+
return await pollSnapshotUntilReady(client, initialSnapshot, diff, warnings, emit, projectId, sliceKinds);
|
|
1160
|
+
}
|
|
1161
|
+
async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, projectId, sliceKinds = []) {
|
|
1162
|
+
// Helper to spread slice_kinds onto every commit.phase / ready emit so
|
|
1163
|
+
// agents grouping per-slice telemetry don't need to track the apply's
|
|
1164
|
+
// spec separately. The low-level commit/upload helpers that pass no
|
|
1165
|
+
// sliceKinds get an empty array → field is omitted from events.
|
|
1166
|
+
const withSliceKinds = (ev) => sliceKinds.length > 0
|
|
1167
|
+
? { ...ev, slice_kinds: sliceKinds }
|
|
1168
|
+
: ev;
|
|
1071
1169
|
let snapshot = initial;
|
|
1072
1170
|
const opHeaders = projectId ? await apikeyHeaders(client, projectId) : {};
|
|
1073
1171
|
let lastPhaseEmitted = null;
|
|
@@ -1107,7 +1205,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
|
|
|
1107
1205
|
return;
|
|
1108
1206
|
if (nextPhase !== undefined && prev.phase === nextPhase)
|
|
1109
1207
|
return;
|
|
1110
|
-
emit({ type: "commit.phase", phase: prev.phase, status: closeStatus });
|
|
1208
|
+
emit(withSliceKinds({ type: "commit.phase", phase: prev.phase, status: closeStatus }));
|
|
1111
1209
|
};
|
|
1112
1210
|
while (true) {
|
|
1113
1211
|
if (lastPhaseEmitted !== snapshot.status) {
|
|
@@ -1115,7 +1213,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
|
|
|
1115
1213
|
if (ev) {
|
|
1116
1214
|
if (ev.type === "commit.phase")
|
|
1117
1215
|
closePreviousPhase(ev.phase);
|
|
1118
|
-
emit(ev);
|
|
1216
|
+
emit(withSliceKinds(ev));
|
|
1119
1217
|
lastPhaseEmitted = snapshot.status;
|
|
1120
1218
|
}
|
|
1121
1219
|
// If `ev` is null (status not in the phase map, e.g. "ready"), leave
|
|
@@ -1133,7 +1231,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
|
|
|
1133
1231
|
});
|
|
1134
1232
|
}
|
|
1135
1233
|
closePreviousPhase();
|
|
1136
|
-
emit({ type: "ready", releaseId: snapshot.release_id, urls: snapshot.urls });
|
|
1234
|
+
emit(withSliceKinds({ type: "ready", releaseId: snapshot.release_id, urls: snapshot.urls }));
|
|
1137
1235
|
return {
|
|
1138
1236
|
release_id: snapshot.release_id,
|
|
1139
1237
|
operation_id: snapshot.operation_id,
|
|
@@ -1215,12 +1313,18 @@ async function startInternal(client, spec, opts) {
|
|
|
1215
1313
|
reason: plan.payment_required.reason,
|
|
1216
1314
|
});
|
|
1217
1315
|
}
|
|
1316
|
+
const sliceKinds = deriveSliceKinds(spec);
|
|
1218
1317
|
const resultPromise = (async () => {
|
|
1219
1318
|
await uploadMissing(client, spec.project, plan.missing_content, byteReaders, emit);
|
|
1220
|
-
emit({
|
|
1319
|
+
emit({
|
|
1320
|
+
type: "commit.phase",
|
|
1321
|
+
phase: "validate",
|
|
1322
|
+
status: "started",
|
|
1323
|
+
...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
|
|
1324
|
+
});
|
|
1221
1325
|
const { planId } = requirePersistedPlan(plan, "starting deploy");
|
|
1222
1326
|
const commit = await commitInternal(client, planId, opts.idempotencyKey);
|
|
1223
|
-
return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project);
|
|
1327
|
+
return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
|
|
1224
1328
|
})();
|
|
1225
1329
|
// Avoid an unhandled-rejection at construction time. Consumers must call
|
|
1226
1330
|
// .result() to actually observe the error.
|
|
@@ -2033,7 +2137,13 @@ function invalidSecretSpec(message, resource) {
|
|
|
2033
2137
|
}
|
|
2034
2138
|
async function normalizeReleaseSpec(client, spec) {
|
|
2035
2139
|
const byteReaders = new Map();
|
|
2036
|
-
|
|
2140
|
+
// Slice-tagged `remember`. Each slice category creates its own remember
|
|
2141
|
+
// closure so the registered reader carries `reader.slice = "release" |
|
|
2142
|
+
// "asset"`. On cross-kind dedup (same SHA from both a release-bound
|
|
2143
|
+
// slice and the asset slice) the value escalates to `"mixed"`. This
|
|
2144
|
+
// value surfaces on `content.upload.*` events so agents can group
|
|
2145
|
+
// upload telemetry by slice kind.
|
|
2146
|
+
const makeRemember = (slice) => (resolved) => {
|
|
2037
2147
|
// Propagate the final content-type onto the deferred reader so the CAS
|
|
2038
2148
|
// upload session can declare it correctly. Callers may set
|
|
2039
2149
|
// ref.contentType *after* resolveContent returns (e.g. normalizeFileSet
|
|
@@ -2042,18 +2152,25 @@ async function normalizeReleaseSpec(client, spec) {
|
|
|
2042
2152
|
resolved.reader.contentType = resolved.ref.contentType;
|
|
2043
2153
|
}
|
|
2044
2154
|
if (!byteReaders.has(resolved.ref.sha256)) {
|
|
2155
|
+
resolved.reader.slice = slice;
|
|
2045
2156
|
byteReaders.set(resolved.ref.sha256, resolved.reader);
|
|
2046
2157
|
}
|
|
2047
2158
|
else {
|
|
2048
2159
|
// Already remembered — but if the existing reader has no contentType
|
|
2049
|
-
// and we just learned it, fill it in.
|
|
2160
|
+
// and we just learned it, fill it in. Also escalate slice tag when
|
|
2161
|
+
// the second registration comes from a different kind.
|
|
2050
2162
|
const existing = byteReaders.get(resolved.ref.sha256);
|
|
2051
2163
|
if (resolved.ref.contentType && !existing.contentType) {
|
|
2052
2164
|
existing.contentType = resolved.ref.contentType;
|
|
2053
2165
|
}
|
|
2166
|
+
if (existing.slice && existing.slice !== slice && existing.slice !== "mixed") {
|
|
2167
|
+
existing.slice = "mixed";
|
|
2168
|
+
}
|
|
2054
2169
|
}
|
|
2055
2170
|
return resolved.ref;
|
|
2056
2171
|
};
|
|
2172
|
+
const rememberRelease = makeRemember("release");
|
|
2173
|
+
const rememberAsset = makeRemember("asset");
|
|
2057
2174
|
const normalized = { project: spec.project };
|
|
2058
2175
|
if (spec.base)
|
|
2059
2176
|
normalized.base = spec.base;
|
|
@@ -2074,19 +2191,19 @@ async function normalizeReleaseSpec(client, spec) {
|
|
|
2074
2191
|
db.zero_downtime = spec.database.zero_downtime;
|
|
2075
2192
|
}
|
|
2076
2193
|
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,
|
|
2194
|
+
db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, rememberRelease)));
|
|
2078
2195
|
}
|
|
2079
2196
|
normalized.database = db;
|
|
2080
2197
|
}
|
|
2081
2198
|
if (spec.functions) {
|
|
2082
2199
|
const fns = {};
|
|
2083
2200
|
if (spec.functions.replace) {
|
|
2084
|
-
fns.replace = await normalizeFunctionMap(spec.functions.replace,
|
|
2201
|
+
fns.replace = await normalizeFunctionMap(spec.functions.replace, rememberRelease);
|
|
2085
2202
|
}
|
|
2086
2203
|
if (spec.functions.patch) {
|
|
2087
2204
|
fns.patch = {};
|
|
2088
2205
|
if (spec.functions.patch.set) {
|
|
2089
|
-
fns.patch.set = await normalizeFunctionMap(spec.functions.patch.set,
|
|
2206
|
+
fns.patch.set = await normalizeFunctionMap(spec.functions.patch.set, rememberRelease);
|
|
2090
2207
|
}
|
|
2091
2208
|
if (spec.functions.patch.delete)
|
|
2092
2209
|
fns.patch.delete = spec.functions.patch.delete;
|
|
@@ -2096,7 +2213,7 @@ async function normalizeReleaseSpec(client, spec) {
|
|
|
2096
2213
|
if (spec.site) {
|
|
2097
2214
|
const publicPaths = "public_paths" in spec.site ? spec.site.public_paths : undefined;
|
|
2098
2215
|
if ("replace" in spec.site && spec.site.replace) {
|
|
2099
|
-
const map = await normalizeFileSet(spec.site.replace,
|
|
2216
|
+
const map = await normalizeFileSet(spec.site.replace, rememberRelease);
|
|
2100
2217
|
normalized.site = {
|
|
2101
2218
|
replace: map,
|
|
2102
2219
|
...(publicPaths ? { public_paths: publicPaths } : {}),
|
|
@@ -2105,7 +2222,7 @@ async function normalizeReleaseSpec(client, spec) {
|
|
|
2105
2222
|
else if ("patch" in spec.site && spec.site.patch) {
|
|
2106
2223
|
const patch = {};
|
|
2107
2224
|
if (spec.site.patch.put) {
|
|
2108
|
-
patch.put = await normalizeFileSet(spec.site.patch.put,
|
|
2225
|
+
patch.put = await normalizeFileSet(spec.site.patch.put, rememberRelease);
|
|
2109
2226
|
}
|
|
2110
2227
|
if (spec.site.patch.delete)
|
|
2111
2228
|
patch.delete = spec.site.patch.delete;
|
|
@@ -2123,7 +2240,7 @@ async function normalizeReleaseSpec(client, spec) {
|
|
|
2123
2240
|
// register a byte-reader; emit the wire-shaped `AssetPutEntry[]`.
|
|
2124
2241
|
// Cross-kind SHA dedup is automatic via the shared `byteReaders` map.
|
|
2125
2242
|
if (spec.assets) {
|
|
2126
|
-
normalized.assets = await normalizeAssetSlice(spec.assets,
|
|
2243
|
+
normalized.assets = await normalizeAssetSlice(spec.assets, rememberAsset);
|
|
2127
2244
|
}
|
|
2128
2245
|
return { normalized, byteReaders };
|
|
2129
2246
|
}
|
|
@@ -2186,20 +2303,41 @@ function buildAssetManifestFromPlanEntries(entries) {
|
|
|
2186
2303
|
let bytesUploaded = 0;
|
|
2187
2304
|
let bytesReused = 0;
|
|
2188
2305
|
for (const entry of entries) {
|
|
2306
|
+
const ref = entry.asset_ref;
|
|
2189
2307
|
const e = {
|
|
2190
2308
|
key: entry.key,
|
|
2191
2309
|
sha256: entry.sha256,
|
|
2192
2310
|
size_bytes: entry.size_bytes,
|
|
2193
2311
|
content_type: entry.content_type,
|
|
2194
2312
|
visibility: entry.visibility,
|
|
2195
|
-
url:
|
|
2196
|
-
immutable_url:
|
|
2197
|
-
cdn_url:
|
|
2198
|
-
cdn_immutable_url:
|
|
2199
|
-
sri:
|
|
2200
|
-
etag:
|
|
2201
|
-
content_digest:
|
|
2313
|
+
url: ref.url,
|
|
2314
|
+
immutable_url: ref.immutable_url,
|
|
2315
|
+
cdn_url: ref.cdn_url,
|
|
2316
|
+
cdn_immutable_url: ref.cdn_immutable_url,
|
|
2317
|
+
sri: ref.sri,
|
|
2318
|
+
etag: ref.etag,
|
|
2319
|
+
content_digest: ref.content_digest,
|
|
2202
2320
|
};
|
|
2321
|
+
// v1.49+ image-variant pass-through. Only emitted when the gateway
|
|
2322
|
+
// returned them (image MIMEs ≥320×320; HEIC/HEIF sources also include
|
|
2323
|
+
// `display_jpeg`). Pre-v1.49 plan responses omit these fields entirely
|
|
2324
|
+
// and the manifest entry stays bytewise-identical to before.
|
|
2325
|
+
if (ref.width_px !== undefined)
|
|
2326
|
+
e.width_px = ref.width_px;
|
|
2327
|
+
if (ref.height_px !== undefined)
|
|
2328
|
+
e.height_px = ref.height_px;
|
|
2329
|
+
if (ref.blurhash !== undefined)
|
|
2330
|
+
e.blurhash = ref.blurhash;
|
|
2331
|
+
if (ref.variant_spec_version !== undefined) {
|
|
2332
|
+
e.variant_spec_version = ref.variant_spec_version;
|
|
2333
|
+
}
|
|
2334
|
+
if (ref.display_url !== undefined)
|
|
2335
|
+
e.display_url = ref.display_url;
|
|
2336
|
+
if (ref.display_immutable_url !== undefined) {
|
|
2337
|
+
e.display_immutable_url = ref.display_immutable_url;
|
|
2338
|
+
}
|
|
2339
|
+
if (ref.variants !== undefined)
|
|
2340
|
+
e.variants = ref.variants;
|
|
2203
2341
|
list.push(e);
|
|
2204
2342
|
byKey[entry.key] = e;
|
|
2205
2343
|
manifest[entry.key] = e;
|