@absolutejs/absolute 0.20.0-beta.82 → 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.
@@ -209,10 +209,26 @@ var requireCompatible = (manifest, config) => {
209
209
  var createAbsoluteMobileUpdateClient = (options) => {
210
210
  const manifestUrl = exactManifestUrl(options.config.manifestUrl);
211
211
  const request = options.fetch ?? globalThis.fetch;
212
- const downloadFiles = async (manifest, index = 0, received = 0) => {
212
+ const downloadFiles = async (manifest, index = 0, transfer = {
213
+ downloadedBytes: 0,
214
+ downloadedFiles: 0,
215
+ reusedBytes: 0,
216
+ reusedFiles: 0,
217
+ totalBytes: manifest.files.reduce((total, file) => total + file.bytes, 0),
218
+ totalFiles: manifest.files.length
219
+ }) => {
213
220
  const file = manifest.files[index];
214
221
  if (!file)
215
- return received;
222
+ return transfer;
223
+ const reusable = await options.store.readReusable?.(file);
224
+ if (reusable && reusable.byteLength === file.bytes && await options.verifier.digest(reusable) === file.sha256) {
225
+ await options.store.write(file, reusable);
226
+ return downloadFiles(manifest, index + 1, {
227
+ ...transfer,
228
+ reusedBytes: transfer.reusedBytes + reusable.byteLength,
229
+ reusedFiles: transfer.reusedFiles + 1
230
+ });
231
+ }
216
232
  const asset = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
217
233
  cache: "no-store",
218
234
  credentials: "omit",
@@ -222,13 +238,17 @@ var createAbsoluteMobileUpdateClient = (options) => {
222
238
  if (!asset.ok)
223
239
  throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset.status}.`);
224
240
  const contents = await readBounded(asset, file.bytes);
225
- const total = received + contents.byteLength;
226
- if (contents.byteLength !== file.bytes || total > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
241
+ const downloadedBytes = transfer.downloadedBytes + contents.byteLength;
242
+ if (contents.byteLength !== file.bytes || downloadedBytes > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
227
243
  throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
228
244
  if (await options.verifier.digest(contents) !== file.sha256)
229
245
  throw new TypeError(`Mobile update asset ${file.path} failed integrity verification.`);
230
246
  await options.store.write(file, contents);
231
- return downloadFiles(manifest, index + 1, total);
247
+ return downloadFiles(manifest, index + 1, {
248
+ ...transfer,
249
+ downloadedBytes,
250
+ downloadedFiles: transfer.downloadedFiles + 1
251
+ });
232
252
  };
233
253
  const check = async (download = false) => {
234
254
  const response = await request(manifestUrl, {
@@ -260,14 +280,15 @@ var createAbsoluteMobileUpdateClient = (options) => {
260
280
  if (!download)
261
281
  return { kind: "update-available", manifest };
262
282
  await options.store.begin(manifest);
283
+ let transfer;
263
284
  try {
264
- await downloadFiles(manifest);
285
+ transfer = await downloadFiles(manifest);
265
286
  await options.store.commit(manifest);
266
287
  } catch (error) {
267
288
  await options.store.abort(manifest.releaseId);
268
289
  throw error;
269
290
  }
270
- return { kind: "downloaded", manifest };
291
+ return { kind: "downloaded", manifest, transfer };
271
292
  };
272
293
  return {
273
294
  check,
@@ -375,6 +396,7 @@ var removeRelease = async (releaseId) => {
375
396
  return;
376
397
  });
377
398
  };
399
+ var filesystemBytes = async (data) => typeof data === "string" ? base64Bytes(data) : new Uint8Array(await data.arrayBuffer());
378
400
  var createStore = () => {
379
401
  let staging;
380
402
  return {
@@ -437,6 +459,16 @@ var createStore = () => {
437
459
  });
438
460
  staging = undefined;
439
461
  },
462
+ readReusable: async (file) => {
463
+ const state = await readState();
464
+ if (!state.activeRelease)
465
+ return null;
466
+ const result = await Filesystem.readFile({
467
+ directory: Directory.Library,
468
+ path: `${releasePath(state.activeRelease)}/${file.path}`
469
+ }).catch(() => null);
470
+ return result ? filesystemBytes(result.data).catch(() => null) : null;
471
+ },
440
472
  write: async (file, contents) => {
441
473
  if (!staging || !staging.files.some((candidate) => candidate.path === file.path))
442
474
  throw new TypeError("Mobile update write is outside its staging transaction.");
@@ -535,8 +567,14 @@ var installAbsoluteMobileShellUpdates = async (manifest) => {
535
567
  if (result.kind !== "downloaded")
536
568
  return;
537
569
  emitUpdateResult({
570
+ downloadedBytes: result.transfer.downloadedBytes,
571
+ downloadedFiles: result.transfer.downloadedFiles,
538
572
  kind: "downloaded",
539
- releaseId: result.manifest.releaseId
573
+ releaseId: result.manifest.releaseId,
574
+ reusedBytes: result.transfer.reusedBytes,
575
+ reusedFiles: result.transfer.reusedFiles,
576
+ totalBytes: result.transfer.totalBytes,
577
+ totalFiles: result.transfer.totalFiles
540
578
  });
541
579
  await client.activate(result.manifest.releaseId);
542
580
  };
@@ -13,6 +13,8 @@ export type AbsoluteMobileUpdateStore = {
13
13
  activate(releaseId: string): Promise<void>;
14
14
  begin(manifest: AbsoluteMobileUpdateManifest): Promise<void>;
15
15
  commit(manifest: AbsoluteMobileUpdateManifest): Promise<void>;
16
+ /** Return a locally cached candidate for this exact path, when available. */
17
+ readReusable?(file: AbsoluteMobileUpdateFile): Promise<Uint8Array | null>;
16
18
  write(file: AbsoluteMobileUpdateFile, contents: Uint8Array): Promise<void>;
17
19
  };
18
20
  export type AbsoluteMobileUpdateVerifier = {
@@ -30,6 +32,7 @@ export type AbsoluteMobileUpdateCheckResult = {
30
32
  } | {
31
33
  kind: 'downloaded';
32
34
  manifest: AbsoluteMobileUpdateManifest;
35
+ transfer: AbsoluteMobileUpdateTransfer;
33
36
  } | {
34
37
  kind: 'quarantined';
35
38
  releaseId: string;
@@ -37,6 +40,14 @@ export type AbsoluteMobileUpdateCheckResult = {
37
40
  kind: 'update-available';
38
41
  manifest: AbsoluteMobileUpdateManifest;
39
42
  };
43
+ export type AbsoluteMobileUpdateTransfer = {
44
+ downloadedBytes: number;
45
+ downloadedFiles: number;
46
+ reusedBytes: number;
47
+ reusedFiles: number;
48
+ totalBytes: number;
49
+ totalFiles: number;
50
+ };
40
51
  export declare const createAbsoluteMobileUpdateClient: (options: AbsoluteMobileUpdateClientOptions) => {
41
52
  check: (download?: boolean) => Promise<AbsoluteMobileUpdateCheckResult>;
42
53
  activate: (releaseId: string) => Promise<void>;
@@ -1,9 +1,14 @@
1
+ import type { MobileUpdatePruneOptions, MobileUpdatePruneResult, MobileUpdateRetentionOptions, MobileUpdateStorageReport } from '@absolutejs/deploy/mobile-update';
1
2
  import { readAbsoluteMobileUpdate } from './updateSigning';
2
3
  export type AbsoluteMobileUpdatePublication = {
3
4
  appId: string;
4
5
  channel: string;
6
+ storedBytes?: number;
7
+ storedFiles?: number;
5
8
  releaseId: string;
6
9
  reused: boolean;
10
+ reusedBytes?: number;
11
+ reusedFiles?: number;
7
12
  rollout: number;
8
13
  stage: 'published';
9
14
  };
@@ -21,6 +26,8 @@ export type AbsoluteMobileUpdateRollback = {
21
26
  stage: 'rolled-back';
22
27
  };
23
28
  export type AbsoluteMobileUpdatePublisher = {
29
+ inspectUpdateStorage?: (options: MobileUpdateRetentionOptions) => Promise<MobileUpdateStorageReport>;
30
+ pruneUpdates?: (options: MobileUpdatePruneOptions) => Promise<MobileUpdatePruneResult>;
24
31
  publishUpdate(options: {
25
32
  manifest: Awaited<ReturnType<typeof readAbsoluteMobileUpdate>>;
26
33
  releaseDirectory: string;
@@ -41,6 +48,22 @@ export type AbsoluteMobileUpdatePublisher = {
41
48
  signal?: AbortSignal;
42
49
  }): Promise<AbsoluteMobileUpdateRollback>;
43
50
  };
51
+ export declare const inspectAbsoluteMobileUpdateStorage: (options: {
52
+ appId: string;
53
+ minAgeMs?: number;
54
+ publisher: AbsoluteMobileUpdatePublisher;
55
+ retainRecent?: number;
56
+ signal?: AbortSignal;
57
+ }) => Promise<MobileUpdateStorageReport>;
58
+ export declare const pruneAbsoluteMobileUpdates: (options: {
59
+ appId: string;
60
+ apply?: boolean;
61
+ gracePeriodMs?: number;
62
+ minAgeMs?: number;
63
+ publisher: AbsoluteMobileUpdatePublisher;
64
+ retainRecent?: number;
65
+ signal?: AbortSignal;
66
+ }) => Promise<MobileUpdatePruneResult>;
44
67
  export declare const loadAbsoluteMobileUpdatePublisher: (projectRoot: string, requestedModulePath: string) => Promise<AbsoluteMobileUpdatePublisher>;
45
68
  export declare const promoteAbsoluteMobileUpdate: (options: {
46
69
  appId: string;
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@absolutejs/auth": ">=0.75.0 <0.77.0",
11
11
  "@absolutejs/beacon": ">=0.7.0-beta.4 <0.8.0",
12
- "@absolutejs/deploy": "0.25.6",
12
+ "@absolutejs/deploy": "0.25.8",
13
13
  "@absolutejs/devices": "0.7.0",
14
14
  "@absolutejs/devices-capacitor": "0.8.0",
15
15
  "@absolutejs/devices-expo": "0.0.2",
@@ -523,7 +523,7 @@
523
523
  ]
524
524
  }
525
525
  },
526
- "version": "0.20.0-beta.82",
526
+ "version": "0.20.0-beta.84",
527
527
  "workspaces": [
528
528
  "tests/fixtures/*",
529
529
  "tests/fixtures/_packages/*"