@absolutejs/absolute 0.20.0-beta.83 → 0.20.0-beta.85
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/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +104 -17
- package/dist/build.js.map +3 -3
- package/dist/cli/{compile-t73ac1zb.js → compile-d7g2fvqe.js} +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/{mobile-9dx39bd3.js → mobile-pdck6a35.js} +17 -3
- package/dist/index.js +104 -17
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +294 -48
- package/dist/mobile/index.js.map +5 -5
- package/dist/mobile/shellUpdate.js +302 -31
- package/dist/src/mobile/updateClient.d.ts +31 -0
- package/dist/src/mobile/updatePublisher.d.ts +4 -0
- package/package.json +2 -2
package/dist/mobile/index.js
CHANGED
|
@@ -20137,6 +20137,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20137
20137
|
const releaseRoot = (manifest) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;
|
|
20138
20138
|
const manifestKey = (manifest) => `${releaseRoot(manifest)}/update.json`;
|
|
20139
20139
|
const fileKey = (manifest, file) => `${releaseRoot(manifest)}/files/${file.path}`;
|
|
20140
|
+
const contentBlobKey = (appId, sha2563) => `${root(appId)}/blobs/${sha2563}`;
|
|
20140
20141
|
const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
|
|
20141
20142
|
const channelKey = (appId, channel) => {
|
|
20142
20143
|
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
@@ -20283,9 +20284,19 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20283
20284
|
const releasePrefix = `${appRoot}releases/`;
|
|
20284
20285
|
const channelPrefix = `${appRoot}channels/`;
|
|
20285
20286
|
const markerPrefix = `${appRoot}gc/`;
|
|
20287
|
+
const blobPrefix = `${appRoot}blobs/`;
|
|
20286
20288
|
const objectKeysByRelease = new Map;
|
|
20287
20289
|
const objectBytesByRelease = new Map;
|
|
20290
|
+
const objectKeysByDigest = new Map;
|
|
20291
|
+
const objectBytesByDigest = new Map;
|
|
20288
20292
|
for (const blobObject of objects) {
|
|
20293
|
+
if (blobObject.key.startsWith(blobPrefix)) {
|
|
20294
|
+
const sha2563 = blobObject.key.slice(blobPrefix.length);
|
|
20295
|
+
if (HASH.test(sha2563)) {
|
|
20296
|
+
objectKeysByDigest.set(sha2563, blobObject.key);
|
|
20297
|
+
objectBytesByDigest.set(sha2563, blobObject.size);
|
|
20298
|
+
}
|
|
20299
|
+
}
|
|
20289
20300
|
if (!blobObject.key.startsWith(releasePrefix))
|
|
20290
20301
|
continue;
|
|
20291
20302
|
const suffix = blobObject.key.slice(releasePrefix.length);
|
|
@@ -20364,20 +20375,28 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20364
20375
|
releaseId: manifest.releaseId
|
|
20365
20376
|
};
|
|
20366
20377
|
}).toSorted((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
20378
|
+
const protectedReleaseIds = new Set(releases.filter((release) => release.protectedBy.length > 0).map((release) => release.releaseId));
|
|
20379
|
+
const protectedDigests = new Set(manifests.filter((manifest) => protectedReleaseIds.has(manifest.releaseId)).flatMap((manifest) => manifest.files.map((file) => file.sha256)));
|
|
20380
|
+
const reclaimableContentBytes = [...objectBytesByDigest].filter(([sha2563]) => !protectedDigests.has(sha2563)).reduce((total, [, bytes]) => total + bytes, 0);
|
|
20381
|
+
const contentBlobBytes = [...objectBytesByDigest.values()].reduce((total, bytes) => total + bytes, 0);
|
|
20367
20382
|
const releaseBytes = releases.reduce((total, release) => total + release.bytes, 0);
|
|
20368
20383
|
const totalBytes = objects.reduce((total, object22) => total + object22.size, 0);
|
|
20369
20384
|
return {
|
|
20370
20385
|
objectKeysByRelease,
|
|
20386
|
+
objectKeysByDigest,
|
|
20371
20387
|
report: {
|
|
20372
20388
|
appId: input.appId,
|
|
20373
20389
|
channelCount: channels.length,
|
|
20374
|
-
|
|
20390
|
+
contentBlobBytes,
|
|
20391
|
+
contentBlobCount: objectKeysByDigest.size,
|
|
20392
|
+
reclaimableBytes: releases.filter((release) => release.protectedBy.length === 0).reduce((total, release) => total + release.bytes, 0) + reclaimableContentBytes,
|
|
20393
|
+
reclaimableContentBytes,
|
|
20375
20394
|
releaseBytes,
|
|
20376
20395
|
releaseCount: releases.length,
|
|
20377
20396
|
releases,
|
|
20378
20397
|
totalBytes,
|
|
20379
20398
|
totalObjectCount: objects.length,
|
|
20380
|
-
untrackedBytes: totalBytes - releaseBytes
|
|
20399
|
+
untrackedBytes: totalBytes - releaseBytes - contentBlobBytes
|
|
20381
20400
|
}
|
|
20382
20401
|
};
|
|
20383
20402
|
};
|
|
@@ -20389,7 +20408,8 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20389
20408
|
marked: [],
|
|
20390
20409
|
reclaimedBytes: 0,
|
|
20391
20410
|
restored: [],
|
|
20392
|
-
swept: []
|
|
20411
|
+
swept: [],
|
|
20412
|
+
sweptContentBlobs: []
|
|
20393
20413
|
};
|
|
20394
20414
|
if (input.apply !== true)
|
|
20395
20415
|
return result;
|
|
@@ -20447,6 +20467,21 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20447
20467
|
result.reclaimedBytes += candidate.bytes;
|
|
20448
20468
|
result.swept.push(release.releaseId);
|
|
20449
20469
|
}
|
|
20470
|
+
const afterReleaseSweep = await inventory(input);
|
|
20471
|
+
const referencedDigests = new Set;
|
|
20472
|
+
for (const release of afterReleaseSweep.report.releases) {
|
|
20473
|
+
const manifest = await readManifest(input.appId, release.releaseId);
|
|
20474
|
+
for (const file of manifest?.manifest.files ?? [])
|
|
20475
|
+
referencedDigests.add(file.sha256);
|
|
20476
|
+
}
|
|
20477
|
+
for (const [sha2563, key] of afterReleaseSweep.objectKeysByDigest) {
|
|
20478
|
+
if (referencedDigests.has(sha2563))
|
|
20479
|
+
continue;
|
|
20480
|
+
const head = await options.store.head(key);
|
|
20481
|
+
await remove(key);
|
|
20482
|
+
result.reclaimedBytes += head?.size ?? 0;
|
|
20483
|
+
result.sweptContentBlobs.push(sha2563);
|
|
20484
|
+
}
|
|
20450
20485
|
return result;
|
|
20451
20486
|
};
|
|
20452
20487
|
return {
|
|
@@ -20463,8 +20498,16 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20463
20498
|
throw new MobileUpdateRegistryError("Local mobile update manifest changed");
|
|
20464
20499
|
const existing = await readManifest(manifest.appId, manifest.releaseId);
|
|
20465
20500
|
let reused = existing !== null;
|
|
20501
|
+
let storedBytes = 0;
|
|
20502
|
+
let storedFiles = 0;
|
|
20503
|
+
let reusedBytes = 0;
|
|
20504
|
+
let reusedFiles = 0;
|
|
20466
20505
|
if (existing && JSON.stringify(existing.manifest) !== JSON.stringify(manifest))
|
|
20467
20506
|
throw new MobileUpdateRegistryError("Published mobile update is immutable");
|
|
20507
|
+
if (existing) {
|
|
20508
|
+
reusedBytes = manifest.files.reduce((total, file) => total + file.bytes, 0);
|
|
20509
|
+
reusedFiles = manifest.files.length;
|
|
20510
|
+
}
|
|
20468
20511
|
if (!existing) {
|
|
20469
20512
|
for (const file of manifest.files) {
|
|
20470
20513
|
const local = path.join(localRoot, "files", file.path);
|
|
@@ -20473,18 +20516,24 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20473
20516
|
throw new MobileUpdateRegistryError(`Mobile update file ${file.path} size changed`);
|
|
20474
20517
|
if (await fileDigest(Bun.file(local)) !== file.sha256)
|
|
20475
20518
|
throw new MobileUpdateRegistryError(`Mobile update file ${file.path} integrity failed`);
|
|
20476
|
-
const key =
|
|
20519
|
+
const key = contentBlobKey(manifest.appId, file.sha256);
|
|
20477
20520
|
const stored = await options.store.head(key);
|
|
20478
20521
|
if (!stored) {
|
|
20479
20522
|
await options.store.put(key, Bun.file(local).stream(), {
|
|
20480
20523
|
cacheControl: "public, max-age=31536000, immutable",
|
|
20481
20524
|
contentType: "application/octet-stream",
|
|
20482
20525
|
maxBytes: file.bytes,
|
|
20483
|
-
metadata: {
|
|
20526
|
+
metadata: { sha256: file.sha256 },
|
|
20484
20527
|
signal: input.signal
|
|
20485
20528
|
});
|
|
20486
|
-
|
|
20487
|
-
|
|
20529
|
+
storedBytes += file.bytes;
|
|
20530
|
+
storedFiles += 1;
|
|
20531
|
+
} else if (stored.size !== file.bytes || stored.metadata?.sha256 !== file.sha256)
|
|
20532
|
+
throw new MobileUpdateRegistryError("Stored mobile update content identity changed");
|
|
20533
|
+
else {
|
|
20534
|
+
reusedBytes += file.bytes;
|
|
20535
|
+
reusedFiles += 1;
|
|
20536
|
+
}
|
|
20488
20537
|
}
|
|
20489
20538
|
const bytes = json(manifest);
|
|
20490
20539
|
await options.store.put(manifestKey(manifest), bytes, {
|
|
@@ -20508,8 +20557,12 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20508
20557
|
return {
|
|
20509
20558
|
appId: manifest.appId,
|
|
20510
20559
|
channel: manifest.channel,
|
|
20560
|
+
storedBytes,
|
|
20561
|
+
storedFiles,
|
|
20511
20562
|
releaseId: manifest.releaseId,
|
|
20512
20563
|
reused,
|
|
20564
|
+
reusedBytes,
|
|
20565
|
+
reusedFiles,
|
|
20513
20566
|
rollout: input.rollout,
|
|
20514
20567
|
stage: "published"
|
|
20515
20568
|
};
|
|
@@ -20558,14 +20611,16 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20558
20611
|
const file = release.manifest.files.find((candidate) => candidate.path === requested);
|
|
20559
20612
|
if (!file)
|
|
20560
20613
|
return null;
|
|
20561
|
-
const
|
|
20614
|
+
const legacyKey = fileKey(release.manifest, file);
|
|
20615
|
+
const legacyHead = await options.store.head(legacyKey);
|
|
20616
|
+
const key = legacyHead ? legacyKey : contentBlobKey(release.manifest.appId, file.sha256);
|
|
20562
20617
|
const [bytes, head] = await Promise.all([
|
|
20563
20618
|
options.store.get(key),
|
|
20564
|
-
options.store.head(key)
|
|
20619
|
+
legacyHead ? Promise.resolve(legacyHead) : options.store.head(key)
|
|
20565
20620
|
]);
|
|
20566
20621
|
if (!bytes || !head)
|
|
20567
20622
|
return null;
|
|
20568
|
-
if (bytes.byteLength !== file.bytes || head.size !== file.bytes || head.metadata?.sha256 !== file.sha256 || (head.metadata?.releaseid ?? head.metadata?.releaseId) !== release.manifest.releaseId || digest(bytes) !== file.sha256)
|
|
20623
|
+
if (bytes.byteLength !== file.bytes || head.size !== file.bytes || head.metadata?.sha256 !== file.sha256 || legacyHead && (head.metadata?.releaseid ?? head.metadata?.releaseId) !== release.manifest.releaseId || digest(bytes) !== file.sha256)
|
|
20569
20624
|
throw new MobileUpdateRegistryError("Stored mobile update file integrity failed");
|
|
20570
20625
|
return { bytes, file };
|
|
20571
20626
|
}
|
|
@@ -20705,14 +20760,18 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20705
20760
|
const expoCodeSigning = resolveExpoCodeSigning(options.expoCodeSigning);
|
|
20706
20761
|
return async (request) => {
|
|
20707
20762
|
const origin = request.headers.get("origin");
|
|
20708
|
-
const cors = origin && allowedOrigins.has(origin) ? {
|
|
20763
|
+
const cors = origin && allowedOrigins.has(origin) ? {
|
|
20764
|
+
"access-control-allow-origin": origin,
|
|
20765
|
+
"access-control-expose-headers": "content-range,etag",
|
|
20766
|
+
vary: "Origin"
|
|
20767
|
+
} : {};
|
|
20709
20768
|
if (request.method === "OPTIONS") {
|
|
20710
20769
|
if (!origin || !allowedOrigins.has(origin))
|
|
20711
20770
|
return new Response(null, { status: 403 });
|
|
20712
20771
|
return new Response(null, {
|
|
20713
20772
|
headers: {
|
|
20714
20773
|
...cors,
|
|
20715
|
-
"access-control-allow-headers": "x-absolute-mobile-app,x-absolute-mobile-channel,x-absolute-mobile-installation,x-absolute-mobile-release,x-absolute-mobile-runtime",
|
|
20774
|
+
"access-control-allow-headers": "if-range,range,x-absolute-mobile-app,x-absolute-mobile-channel,x-absolute-mobile-installation,x-absolute-mobile-release,x-absolute-mobile-runtime",
|
|
20716
20775
|
"access-control-allow-methods": "GET,OPTIONS",
|
|
20717
20776
|
"access-control-max-age": "600"
|
|
20718
20777
|
},
|
|
@@ -20840,14 +20899,42 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20840
20899
|
});
|
|
20841
20900
|
if (!file)
|
|
20842
20901
|
return new Response(null, { status: 404 });
|
|
20843
|
-
|
|
20902
|
+
const etag = `"${file.file.sha256}"`;
|
|
20903
|
+
const range = request.headers.get("range");
|
|
20904
|
+
const useRange = range !== null && (!request.headers.has("if-range") || request.headers.get("if-range") === etag);
|
|
20905
|
+
let contents = file.bytes;
|
|
20906
|
+
let status = 200;
|
|
20907
|
+
let contentRange;
|
|
20908
|
+
if (useRange) {
|
|
20909
|
+
const parsed = /^bytes=(\d+)-(\d*)$/.exec(range);
|
|
20910
|
+
const start = parsed?.[1] === undefined ? NaN : Number(parsed[1]);
|
|
20911
|
+
const requestedEnd = parsed?.[2] === undefined || parsed[2] === "" ? file.bytes.byteLength - 1 : Number(parsed[2]);
|
|
20912
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd) || start < 0 || start >= file.bytes.byteLength || requestedEnd < start)
|
|
20913
|
+
return new Response(null, {
|
|
20914
|
+
headers: {
|
|
20915
|
+
...cors,
|
|
20916
|
+
"accept-ranges": "bytes",
|
|
20917
|
+
"content-range": `bytes */${file.bytes.byteLength}`,
|
|
20918
|
+
etag
|
|
20919
|
+
},
|
|
20920
|
+
status: 416
|
|
20921
|
+
});
|
|
20922
|
+
const end = Math.min(requestedEnd, file.bytes.byteLength - 1);
|
|
20923
|
+
contents = file.bytes.slice(start, end + 1);
|
|
20924
|
+
status = 206;
|
|
20925
|
+
contentRange = `bytes ${start}-${end}/${file.bytes.byteLength}`;
|
|
20926
|
+
}
|
|
20927
|
+
return new Response(new Blob([new Uint8Array(contents).buffer]), {
|
|
20844
20928
|
headers: {
|
|
20845
20929
|
...cors,
|
|
20930
|
+
"accept-ranges": "bytes",
|
|
20846
20931
|
"cache-control": "public, max-age=31536000, immutable",
|
|
20847
|
-
"content-length": String(
|
|
20932
|
+
"content-length": String(contents.byteLength),
|
|
20933
|
+
...contentRange ? { "content-range": contentRange } : {},
|
|
20848
20934
|
"content-type": expoContentType(file.file.path.includes(".") ? file.file.path.slice(file.file.path.lastIndexOf(".") + 1) : undefined, false),
|
|
20849
|
-
etag
|
|
20850
|
-
}
|
|
20935
|
+
etag
|
|
20936
|
+
},
|
|
20937
|
+
status
|
|
20851
20938
|
});
|
|
20852
20939
|
};
|
|
20853
20940
|
};
|
|
@@ -38650,17 +38737,18 @@ var fileUrl = (manifestUrl, releaseId, path) => {
|
|
|
38650
38737
|
throw new TypeError("Mobile update asset escaped its signed release origin.");
|
|
38651
38738
|
return result;
|
|
38652
38739
|
};
|
|
38653
|
-
var readChunks = async (reader, maximum2, chunks = [], received = 0) => {
|
|
38740
|
+
var readChunks = async (reader, maximum2, onChunk, chunks = [], received = 0) => {
|
|
38654
38741
|
const result = await reader.read();
|
|
38655
38742
|
if (result.done)
|
|
38656
38743
|
return { chunks, received };
|
|
38657
38744
|
const total = received + result.value.byteLength;
|
|
38658
38745
|
if (total > maximum2)
|
|
38659
38746
|
throw new TypeError("Mobile update response exceeds its signed size.");
|
|
38747
|
+
await onChunk?.(result.value, received);
|
|
38660
38748
|
chunks.push(result.value);
|
|
38661
|
-
return readChunks(reader, maximum2, chunks, total);
|
|
38749
|
+
return readChunks(reader, maximum2, onChunk, chunks, total);
|
|
38662
38750
|
};
|
|
38663
|
-
var readBounded = async (response, maximum2) => {
|
|
38751
|
+
var readBounded = async (response, maximum2, onChunk) => {
|
|
38664
38752
|
const declared = Number(response.headers.get("content-length"));
|
|
38665
38753
|
if (Number.isFinite(declared) && declared > maximum2)
|
|
38666
38754
|
throw new TypeError("Mobile update response exceeds its signed size.");
|
|
@@ -38668,8 +38756,9 @@ var readBounded = async (response, maximum2) => {
|
|
|
38668
38756
|
return new Uint8Array;
|
|
38669
38757
|
const reader = response.body.getReader();
|
|
38670
38758
|
let result;
|
|
38759
|
+
const chunks = [];
|
|
38671
38760
|
try {
|
|
38672
|
-
result = await readChunks(reader, maximum2);
|
|
38761
|
+
result = await readChunks(reader, maximum2, onChunk, chunks);
|
|
38673
38762
|
} catch (error) {
|
|
38674
38763
|
await reader.cancel().catch(() => {
|
|
38675
38764
|
return;
|
|
@@ -38678,7 +38767,7 @@ var readBounded = async (response, maximum2) => {
|
|
|
38678
38767
|
}
|
|
38679
38768
|
const contents = new Uint8Array(result.received);
|
|
38680
38769
|
let offset = 0;
|
|
38681
|
-
for (const chunk of
|
|
38770
|
+
for (const chunk of chunks) {
|
|
38682
38771
|
contents.set(chunk, offset);
|
|
38683
38772
|
offset += chunk.byteLength;
|
|
38684
38773
|
}
|
|
@@ -38699,29 +38788,172 @@ var requireCompatible = (manifest, config) => {
|
|
|
38699
38788
|
if (manifest.runtimeFingerprint !== config.runtimeFingerprint)
|
|
38700
38789
|
throw new TypeError("Mobile update requires a different native runtime.");
|
|
38701
38790
|
};
|
|
38791
|
+
var networkConcurrency = (requested) => {
|
|
38792
|
+
const bounded = Math.max(1, Math.min(6, Math.floor(requested ?? 3)));
|
|
38793
|
+
const navigatorValue = Reflect.get(globalThis, "navigator");
|
|
38794
|
+
const connection = typeof navigatorValue === "object" && navigatorValue !== null ? Reflect.get(navigatorValue, "connection") : undefined;
|
|
38795
|
+
if (typeof connection !== "object" || connection === null)
|
|
38796
|
+
return bounded;
|
|
38797
|
+
if (Reflect.get(connection, "saveData") === true)
|
|
38798
|
+
return 1;
|
|
38799
|
+
const effectiveType = Reflect.get(connection, "effectiveType");
|
|
38800
|
+
if (effectiveType === "slow-2g" || effectiveType === "2g")
|
|
38801
|
+
return 1;
|
|
38802
|
+
if (effectiveType === "3g")
|
|
38803
|
+
return Math.min(2, bounded);
|
|
38804
|
+
return bounded;
|
|
38805
|
+
};
|
|
38806
|
+
var combine = (prefix, suffix) => {
|
|
38807
|
+
const result = new Uint8Array(prefix.byteLength + suffix.byteLength);
|
|
38808
|
+
result.set(prefix);
|
|
38809
|
+
result.set(suffix, prefix.byteLength);
|
|
38810
|
+
return result;
|
|
38811
|
+
};
|
|
38812
|
+
var validContentRange = (value, start, total) => value === `bytes ${start}-${total - 1}/${total}`;
|
|
38813
|
+
var combineSignals = (signals) => {
|
|
38814
|
+
const nativeAny = Reflect.get(AbortSignal, "any");
|
|
38815
|
+
if (typeof nativeAny === "function")
|
|
38816
|
+
return Reflect.apply(nativeAny, AbortSignal, [signals]);
|
|
38817
|
+
const controller = new AbortController;
|
|
38818
|
+
const abort = () => controller.abort();
|
|
38819
|
+
if (signals.some((signal) => signal.aborted))
|
|
38820
|
+
abort();
|
|
38821
|
+
else
|
|
38822
|
+
signals.forEach((signal) => signal.addEventListener("abort", abort, { once: true }));
|
|
38823
|
+
return controller.signal;
|
|
38824
|
+
};
|
|
38702
38825
|
var createAbsoluteMobileUpdateClient = (options) => {
|
|
38703
38826
|
const manifestUrl = exactManifestUrl(options.config.manifestUrl);
|
|
38704
38827
|
const request = options.fetch ?? globalThis.fetch;
|
|
38705
|
-
const downloadFiles = async (manifest
|
|
38706
|
-
const
|
|
38707
|
-
|
|
38708
|
-
|
|
38709
|
-
|
|
38710
|
-
|
|
38711
|
-
|
|
38712
|
-
|
|
38713
|
-
|
|
38714
|
-
|
|
38715
|
-
|
|
38716
|
-
|
|
38717
|
-
|
|
38718
|
-
|
|
38719
|
-
|
|
38720
|
-
|
|
38721
|
-
|
|
38722
|
-
|
|
38723
|
-
|
|
38724
|
-
|
|
38828
|
+
const downloadFiles = async (manifest) => {
|
|
38829
|
+
const startedAt = performance.now();
|
|
38830
|
+
const transfer = {
|
|
38831
|
+
avoidedBytes: 0,
|
|
38832
|
+
completedFiles: 0,
|
|
38833
|
+
downloadedBytes: 0,
|
|
38834
|
+
downloadedFiles: 0,
|
|
38835
|
+
durationMs: 0,
|
|
38836
|
+
resumedBytes: 0,
|
|
38837
|
+
resumedFiles: 0,
|
|
38838
|
+
reusedBytes: 0,
|
|
38839
|
+
reusedFiles: 0,
|
|
38840
|
+
throughputBytesPerSecond: 0,
|
|
38841
|
+
totalBytes: manifest.files.reduce((total, file) => total + file.bytes, 0),
|
|
38842
|
+
totalFiles: manifest.files.length
|
|
38843
|
+
};
|
|
38844
|
+
const updateTiming = () => {
|
|
38845
|
+
transfer.durationMs = Math.max(0, performance.now() - startedAt);
|
|
38846
|
+
transfer.avoidedBytes = transfer.reusedBytes + transfer.resumedBytes;
|
|
38847
|
+
transfer.throughputBytesPerSecond = transfer.durationMs > 0 ? Math.round(transfer.downloadedBytes * 1000 / transfer.durationMs) : transfer.downloadedBytes;
|
|
38848
|
+
};
|
|
38849
|
+
const progress = () => {
|
|
38850
|
+
updateTiming();
|
|
38851
|
+
try {
|
|
38852
|
+
options.onProgress?.({
|
|
38853
|
+
...transfer,
|
|
38854
|
+
kind: "download-progress"
|
|
38855
|
+
});
|
|
38856
|
+
} catch {}
|
|
38857
|
+
};
|
|
38858
|
+
const controller = new AbortController;
|
|
38859
|
+
let next = 0;
|
|
38860
|
+
let firstError;
|
|
38861
|
+
const downloadFile = async (file) => {
|
|
38862
|
+
const staged = await options.store.readStaged?.(file);
|
|
38863
|
+
if (staged?.byteLength === file.bytes && await options.verifier.digest(staged) === file.sha256) {
|
|
38864
|
+
transfer.resumedBytes += staged.byteLength;
|
|
38865
|
+
transfer.resumedFiles += 1;
|
|
38866
|
+
transfer.completedFiles += 1;
|
|
38867
|
+
progress();
|
|
38868
|
+
return;
|
|
38869
|
+
}
|
|
38870
|
+
const reusable = await options.store.readReusable?.(file);
|
|
38871
|
+
if (reusable?.byteLength === file.bytes && await options.verifier.digest(reusable) === file.sha256) {
|
|
38872
|
+
await options.store.write(file, reusable);
|
|
38873
|
+
transfer.reusedBytes += reusable.byteLength;
|
|
38874
|
+
transfer.reusedFiles += 1;
|
|
38875
|
+
transfer.completedFiles += 1;
|
|
38876
|
+
progress();
|
|
38877
|
+
return;
|
|
38878
|
+
}
|
|
38879
|
+
const candidate = await options.store.readPartial?.(file);
|
|
38880
|
+
if (candidate?.byteLength === file.bytes && await options.verifier.digest(candidate) === file.sha256) {
|
|
38881
|
+
await options.store.write(file, candidate);
|
|
38882
|
+
transfer.resumedBytes += candidate.byteLength;
|
|
38883
|
+
transfer.resumedFiles += 1;
|
|
38884
|
+
transfer.completedFiles += 1;
|
|
38885
|
+
progress();
|
|
38886
|
+
return;
|
|
38887
|
+
}
|
|
38888
|
+
const partial = candidate && candidate.byteLength > 0 && candidate.byteLength < file.bytes ? candidate : new Uint8Array;
|
|
38889
|
+
const headers = new Headers;
|
|
38890
|
+
if (partial.byteLength > 0) {
|
|
38891
|
+
headers.set("if-range", `"${file.sha256}"`);
|
|
38892
|
+
headers.set("range", `bytes=${partial.byteLength}-`);
|
|
38893
|
+
}
|
|
38894
|
+
const asset2 = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
|
|
38895
|
+
cache: "no-store",
|
|
38896
|
+
credentials: "omit",
|
|
38897
|
+
headers,
|
|
38898
|
+
redirect: "error",
|
|
38899
|
+
signal: combineSignals([
|
|
38900
|
+
controller.signal,
|
|
38901
|
+
AbortSignal.timeout(30000)
|
|
38902
|
+
])
|
|
38903
|
+
});
|
|
38904
|
+
if (!asset2.ok)
|
|
38905
|
+
throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset2.status}.`);
|
|
38906
|
+
const ranged = asset2.status === 206;
|
|
38907
|
+
if (ranged && (partial.byteLength === 0 || !validContentRange(asset2.headers.get("content-range"), partial.byteLength, file.bytes)))
|
|
38908
|
+
throw new TypeError(`Mobile update asset ${file.path} returned an invalid byte range.`);
|
|
38909
|
+
const prefix = ranged ? partial : new Uint8Array;
|
|
38910
|
+
if (ranged) {
|
|
38911
|
+
transfer.resumedBytes += partial.byteLength;
|
|
38912
|
+
transfer.resumedFiles += 1;
|
|
38913
|
+
}
|
|
38914
|
+
const downloaded = await readBounded(asset2, file.bytes - prefix.byteLength, options.store.appendPartial ? async (chunk, offset) => {
|
|
38915
|
+
await options.store.appendPartial?.(file, chunk, prefix.byteLength + offset);
|
|
38916
|
+
transfer.downloadedBytes += chunk.byteLength;
|
|
38917
|
+
progress();
|
|
38918
|
+
} : undefined);
|
|
38919
|
+
if (!options.store.appendPartial) {
|
|
38920
|
+
transfer.downloadedBytes += downloaded.byteLength;
|
|
38921
|
+
progress();
|
|
38922
|
+
}
|
|
38923
|
+
if (transfer.downloadedBytes > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
38924
|
+
throw new TypeError("Mobile update exceeds the maximum transfer size.");
|
|
38925
|
+
const contents = combine(prefix, downloaded);
|
|
38926
|
+
if (contents.byteLength !== file.bytes)
|
|
38927
|
+
throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
|
|
38928
|
+
if (await options.verifier.digest(contents) !== file.sha256)
|
|
38929
|
+
throw new TypeError(`Mobile update asset ${file.path} failed integrity verification.`);
|
|
38930
|
+
await options.store.write(file, contents);
|
|
38931
|
+
transfer.downloadedFiles += 1;
|
|
38932
|
+
transfer.completedFiles += 1;
|
|
38933
|
+
progress();
|
|
38934
|
+
};
|
|
38935
|
+
const worker = async () => {
|
|
38936
|
+
if (firstError)
|
|
38937
|
+
return;
|
|
38938
|
+
const index = next++;
|
|
38939
|
+
const file = manifest.files[index];
|
|
38940
|
+
if (!file)
|
|
38941
|
+
return;
|
|
38942
|
+
try {
|
|
38943
|
+
await downloadFile(file);
|
|
38944
|
+
} catch (error) {
|
|
38945
|
+
firstError ??= error;
|
|
38946
|
+
controller.abort();
|
|
38947
|
+
}
|
|
38948
|
+
await worker();
|
|
38949
|
+
};
|
|
38950
|
+
await Promise.all(Array.from({
|
|
38951
|
+
length: Math.min(networkConcurrency(options.concurrency), manifest.files.length)
|
|
38952
|
+
}, () => worker()));
|
|
38953
|
+
if (firstError)
|
|
38954
|
+
throw firstError;
|
|
38955
|
+
updateTiming();
|
|
38956
|
+
return transfer;
|
|
38725
38957
|
};
|
|
38726
38958
|
const check = async (download = false) => {
|
|
38727
38959
|
const response = await request(manifestUrl, {
|
|
@@ -38753,14 +38985,18 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
38753
38985
|
if (!download)
|
|
38754
38986
|
return { kind: "update-available", manifest };
|
|
38755
38987
|
await options.store.begin(manifest);
|
|
38988
|
+
let transfer;
|
|
38756
38989
|
try {
|
|
38757
|
-
await downloadFiles(manifest);
|
|
38990
|
+
transfer = await downloadFiles(manifest);
|
|
38758
38991
|
await options.store.commit(manifest);
|
|
38759
38992
|
} catch (error) {
|
|
38760
|
-
|
|
38993
|
+
if (options.store.suspend)
|
|
38994
|
+
await options.store.suspend(manifest.releaseId);
|
|
38995
|
+
else
|
|
38996
|
+
await options.store.abort(manifest.releaseId);
|
|
38761
38997
|
throw error;
|
|
38762
38998
|
}
|
|
38763
|
-
return { kind: "downloaded", manifest };
|
|
38999
|
+
return { kind: "downloaded", manifest, transfer };
|
|
38764
39000
|
};
|
|
38765
39001
|
return {
|
|
38766
39002
|
check,
|
|
@@ -38888,6 +39124,7 @@ var lifecycleMethod = (publisher, name) => {
|
|
|
38888
39124
|
throw new TypeError(`Mobile update registry does not support ${name}. Re-run \`absolute mobile update provision --force\` after upgrading @absolutejs/deploy.`);
|
|
38889
39125
|
return method;
|
|
38890
39126
|
};
|
|
39127
|
+
var validOptionalCounters = (values) => values.every((value) => value === undefined) || values.every((value) => Number.isSafeInteger(value) && (value ?? -1) >= 0);
|
|
38891
39128
|
var validateStorageIdentity = (result, appId) => {
|
|
38892
39129
|
if (!object5(result) || result.appId !== appId || !Array.isArray(result.releases) || ![
|
|
38893
39130
|
result.channelCount,
|
|
@@ -38897,7 +39134,11 @@ var validateStorageIdentity = (result, appId) => {
|
|
|
38897
39134
|
result.totalBytes,
|
|
38898
39135
|
result.totalObjectCount,
|
|
38899
39136
|
result.untrackedBytes
|
|
38900
|
-
].every((value) => Number.isSafeInteger(value) && value >= 0)
|
|
39137
|
+
].every((value) => Number.isSafeInteger(value) && value >= 0) || !validOptionalCounters([
|
|
39138
|
+
result.contentBlobBytes,
|
|
39139
|
+
result.contentBlobCount,
|
|
39140
|
+
result.reclaimableContentBytes
|
|
39141
|
+
]))
|
|
38901
39142
|
throw new TypeError("Mobile update registry returned an invalid storage report.");
|
|
38902
39143
|
return result;
|
|
38903
39144
|
};
|
|
@@ -38917,7 +39158,7 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
38917
39158
|
...options.signal ? { signal: options.signal } : {}
|
|
38918
39159
|
});
|
|
38919
39160
|
validateStorageIdentity(result, options.appId);
|
|
38920
|
-
if (!Array.isArray(result.marked) || !Array.isArray(result.restored) || !Array.isArray(result.swept) || typeof result.dryRun !== "boolean" || !Number.isSafeInteger(result.reclaimedBytes) || result.reclaimedBytes < 0)
|
|
39161
|
+
if (!Array.isArray(result.marked) || !Array.isArray(result.restored) || !Array.isArray(result.swept) || result.sweptContentBlobs !== undefined && !Array.isArray(result.sweptContentBlobs) || typeof result.dryRun !== "boolean" || !Number.isSafeInteger(result.reclaimedBytes) || result.reclaimedBytes < 0)
|
|
38921
39162
|
throw new TypeError("Mobile update registry returned an invalid collection report.");
|
|
38922
39163
|
return result;
|
|
38923
39164
|
};
|
|
@@ -38963,7 +39204,12 @@ var publishAbsoluteMobileUpdate = async (options) => {
|
|
|
38963
39204
|
rollout: options.rollout,
|
|
38964
39205
|
signal: options.signal
|
|
38965
39206
|
});
|
|
38966
|
-
if (result.appId !== manifest.appId || result.channel !== manifest.channel || result.releaseId !== manifest.releaseId || result.rollout !== options.rollout || result.stage !== "published" || typeof result.reused !== "boolean"
|
|
39207
|
+
if (result.appId !== manifest.appId || result.channel !== manifest.channel || result.releaseId !== manifest.releaseId || result.rollout !== options.rollout || result.stage !== "published" || typeof result.reused !== "boolean" || !validOptionalCounters([
|
|
39208
|
+
result.storedBytes,
|
|
39209
|
+
result.storedFiles,
|
|
39210
|
+
result.reusedBytes,
|
|
39211
|
+
result.reusedFiles
|
|
39212
|
+
]))
|
|
38967
39213
|
throw new TypeError("Mobile update registry returned a different publication identity.");
|
|
38968
39214
|
return result;
|
|
38969
39215
|
};
|
|
@@ -39247,5 +39493,5 @@ export {
|
|
|
39247
39493
|
writeAbsoluteMobileUpdateRegistry
|
|
39248
39494
|
};
|
|
39249
39495
|
|
|
39250
|
-
//# debugId=
|
|
39496
|
+
//# debugId=B713A6D1CA49D10364756E2164756E21
|
|
39251
39497
|
//# sourceMappingURL=index.js.map
|