@absolutejs/absolute 0.20.0-beta.83 → 0.20.0-beta.84

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.
@@ -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
- reclaimableBytes: releases.filter((release) => release.protectedBy.length === 0).reduce((total, release) => total + release.bytes, 0),
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 = fileKey(manifest, file);
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: { releaseid: manifest.releaseId, sha256: file.sha256 },
20526
+ metadata: { sha256: file.sha256 },
20484
20527
  signal: input.signal
20485
20528
  });
20486
- } else if (stored.size !== file.bytes || stored.metadata?.sha256 !== file.sha256 || (stored.metadata?.releaseid ?? stored.metadata?.releaseId) !== manifest.releaseId)
20487
- throw new MobileUpdateRegistryError("Stored mobile update file identity changed");
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 key = fileKey(release.manifest, file);
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
  }
@@ -38702,10 +38757,26 @@ var requireCompatible = (manifest, config) => {
38702
38757
  var createAbsoluteMobileUpdateClient = (options) => {
38703
38758
  const manifestUrl = exactManifestUrl(options.config.manifestUrl);
38704
38759
  const request = options.fetch ?? globalThis.fetch;
38705
- const downloadFiles = async (manifest, index = 0, received = 0) => {
38760
+ const downloadFiles = async (manifest, index = 0, transfer = {
38761
+ downloadedBytes: 0,
38762
+ downloadedFiles: 0,
38763
+ reusedBytes: 0,
38764
+ reusedFiles: 0,
38765
+ totalBytes: manifest.files.reduce((total, file) => total + file.bytes, 0),
38766
+ totalFiles: manifest.files.length
38767
+ }) => {
38706
38768
  const file = manifest.files[index];
38707
38769
  if (!file)
38708
- return received;
38770
+ return transfer;
38771
+ const reusable = await options.store.readReusable?.(file);
38772
+ if (reusable && reusable.byteLength === file.bytes && await options.verifier.digest(reusable) === file.sha256) {
38773
+ await options.store.write(file, reusable);
38774
+ return downloadFiles(manifest, index + 1, {
38775
+ ...transfer,
38776
+ reusedBytes: transfer.reusedBytes + reusable.byteLength,
38777
+ reusedFiles: transfer.reusedFiles + 1
38778
+ });
38779
+ }
38709
38780
  const asset2 = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
38710
38781
  cache: "no-store",
38711
38782
  credentials: "omit",
@@ -38715,13 +38786,17 @@ var createAbsoluteMobileUpdateClient = (options) => {
38715
38786
  if (!asset2.ok)
38716
38787
  throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset2.status}.`);
38717
38788
  const contents = await readBounded(asset2, file.bytes);
38718
- const total = received + contents.byteLength;
38719
- if (contents.byteLength !== file.bytes || total > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
38789
+ const downloadedBytes = transfer.downloadedBytes + contents.byteLength;
38790
+ if (contents.byteLength !== file.bytes || downloadedBytes > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
38720
38791
  throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
38721
38792
  if (await options.verifier.digest(contents) !== file.sha256)
38722
38793
  throw new TypeError(`Mobile update asset ${file.path} failed integrity verification.`);
38723
38794
  await options.store.write(file, contents);
38724
- return downloadFiles(manifest, index + 1, total);
38795
+ return downloadFiles(manifest, index + 1, {
38796
+ ...transfer,
38797
+ downloadedBytes,
38798
+ downloadedFiles: transfer.downloadedFiles + 1
38799
+ });
38725
38800
  };
38726
38801
  const check = async (download = false) => {
38727
38802
  const response = await request(manifestUrl, {
@@ -38753,14 +38828,15 @@ var createAbsoluteMobileUpdateClient = (options) => {
38753
38828
  if (!download)
38754
38829
  return { kind: "update-available", manifest };
38755
38830
  await options.store.begin(manifest);
38831
+ let transfer;
38756
38832
  try {
38757
- await downloadFiles(manifest);
38833
+ transfer = await downloadFiles(manifest);
38758
38834
  await options.store.commit(manifest);
38759
38835
  } catch (error) {
38760
38836
  await options.store.abort(manifest.releaseId);
38761
38837
  throw error;
38762
38838
  }
38763
- return { kind: "downloaded", manifest };
38839
+ return { kind: "downloaded", manifest, transfer };
38764
38840
  };
38765
38841
  return {
38766
38842
  check,
@@ -38888,6 +38964,7 @@ var lifecycleMethod = (publisher, name) => {
38888
38964
  throw new TypeError(`Mobile update registry does not support ${name}. Re-run \`absolute mobile update provision --force\` after upgrading @absolutejs/deploy.`);
38889
38965
  return method;
38890
38966
  };
38967
+ var validOptionalCounters = (values) => values.every((value) => value === undefined) || values.every((value) => Number.isSafeInteger(value) && (value ?? -1) >= 0);
38891
38968
  var validateStorageIdentity = (result, appId) => {
38892
38969
  if (!object5(result) || result.appId !== appId || !Array.isArray(result.releases) || ![
38893
38970
  result.channelCount,
@@ -38897,7 +38974,11 @@ var validateStorageIdentity = (result, appId) => {
38897
38974
  result.totalBytes,
38898
38975
  result.totalObjectCount,
38899
38976
  result.untrackedBytes
38900
- ].every((value) => Number.isSafeInteger(value) && value >= 0))
38977
+ ].every((value) => Number.isSafeInteger(value) && value >= 0) || !validOptionalCounters([
38978
+ result.contentBlobBytes,
38979
+ result.contentBlobCount,
38980
+ result.reclaimableContentBytes
38981
+ ]))
38901
38982
  throw new TypeError("Mobile update registry returned an invalid storage report.");
38902
38983
  return result;
38903
38984
  };
@@ -38917,7 +38998,7 @@ var pruneAbsoluteMobileUpdates = async (options) => {
38917
38998
  ...options.signal ? { signal: options.signal } : {}
38918
38999
  });
38919
39000
  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)
39001
+ 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
39002
  throw new TypeError("Mobile update registry returned an invalid collection report.");
38922
39003
  return result;
38923
39004
  };
@@ -38963,7 +39044,12 @@ var publishAbsoluteMobileUpdate = async (options) => {
38963
39044
  rollout: options.rollout,
38964
39045
  signal: options.signal
38965
39046
  });
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")
39047
+ 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([
39048
+ result.storedBytes,
39049
+ result.storedFiles,
39050
+ result.reusedBytes,
39051
+ result.reusedFiles
39052
+ ]))
38967
39053
  throw new TypeError("Mobile update registry returned a different publication identity.");
38968
39054
  return result;
38969
39055
  };
@@ -39247,5 +39333,5 @@ export {
39247
39333
  writeAbsoluteMobileUpdateRegistry
39248
39334
  };
39249
39335
 
39250
- //# debugId=FB53026A710DD13464756E2164756E21
39336
+ //# debugId=A051836D5390197D64756E2164756E21
39251
39337
  //# sourceMappingURL=index.js.map