@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.
@@ -157,17 +157,18 @@ var fileUrl = (manifestUrl, releaseId, path) => {
157
157
  throw new TypeError("Mobile update asset escaped its signed release origin.");
158
158
  return result;
159
159
  };
160
- var readChunks = async (reader, maximum, chunks = [], received = 0) => {
160
+ var readChunks = async (reader, maximum, onChunk, chunks = [], received = 0) => {
161
161
  const result = await reader.read();
162
162
  if (result.done)
163
163
  return { chunks, received };
164
164
  const total = received + result.value.byteLength;
165
165
  if (total > maximum)
166
166
  throw new TypeError("Mobile update response exceeds its signed size.");
167
+ await onChunk?.(result.value, received);
167
168
  chunks.push(result.value);
168
- return readChunks(reader, maximum, chunks, total);
169
+ return readChunks(reader, maximum, onChunk, chunks, total);
169
170
  };
170
- var readBounded = async (response, maximum) => {
171
+ var readBounded = async (response, maximum, onChunk) => {
171
172
  const declared = Number(response.headers.get("content-length"));
172
173
  if (Number.isFinite(declared) && declared > maximum)
173
174
  throw new TypeError("Mobile update response exceeds its signed size.");
@@ -175,8 +176,9 @@ var readBounded = async (response, maximum) => {
175
176
  return new Uint8Array;
176
177
  const reader = response.body.getReader();
177
178
  let result;
179
+ const chunks = [];
178
180
  try {
179
- result = await readChunks(reader, maximum);
181
+ result = await readChunks(reader, maximum, onChunk, chunks);
180
182
  } catch (error) {
181
183
  await reader.cancel().catch(() => {
182
184
  return;
@@ -185,7 +187,7 @@ var readBounded = async (response, maximum) => {
185
187
  }
186
188
  const contents = new Uint8Array(result.received);
187
189
  let offset = 0;
188
- for (const chunk of result.chunks) {
190
+ for (const chunk of chunks) {
189
191
  contents.set(chunk, offset);
190
192
  offset += chunk.byteLength;
191
193
  }
@@ -206,29 +208,172 @@ var requireCompatible = (manifest, config) => {
206
208
  if (manifest.runtimeFingerprint !== config.runtimeFingerprint)
207
209
  throw new TypeError("Mobile update requires a different native runtime.");
208
210
  };
211
+ var networkConcurrency = (requested) => {
212
+ const bounded = Math.max(1, Math.min(6, Math.floor(requested ?? 3)));
213
+ const navigatorValue = Reflect.get(globalThis, "navigator");
214
+ const connection = typeof navigatorValue === "object" && navigatorValue !== null ? Reflect.get(navigatorValue, "connection") : undefined;
215
+ if (typeof connection !== "object" || connection === null)
216
+ return bounded;
217
+ if (Reflect.get(connection, "saveData") === true)
218
+ return 1;
219
+ const effectiveType = Reflect.get(connection, "effectiveType");
220
+ if (effectiveType === "slow-2g" || effectiveType === "2g")
221
+ return 1;
222
+ if (effectiveType === "3g")
223
+ return Math.min(2, bounded);
224
+ return bounded;
225
+ };
226
+ var combine = (prefix, suffix) => {
227
+ const result = new Uint8Array(prefix.byteLength + suffix.byteLength);
228
+ result.set(prefix);
229
+ result.set(suffix, prefix.byteLength);
230
+ return result;
231
+ };
232
+ var validContentRange = (value, start, total) => value === `bytes ${start}-${total - 1}/${total}`;
233
+ var combineSignals = (signals) => {
234
+ const nativeAny = Reflect.get(AbortSignal, "any");
235
+ if (typeof nativeAny === "function")
236
+ return Reflect.apply(nativeAny, AbortSignal, [signals]);
237
+ const controller = new AbortController;
238
+ const abort = () => controller.abort();
239
+ if (signals.some((signal) => signal.aborted))
240
+ abort();
241
+ else
242
+ signals.forEach((signal) => signal.addEventListener("abort", abort, { once: true }));
243
+ return controller.signal;
244
+ };
209
245
  var createAbsoluteMobileUpdateClient = (options) => {
210
246
  const manifestUrl = exactManifestUrl(options.config.manifestUrl);
211
247
  const request = options.fetch ?? globalThis.fetch;
212
- const downloadFiles = async (manifest, index = 0, received = 0) => {
213
- const file = manifest.files[index];
214
- if (!file)
215
- return received;
216
- const asset = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
217
- cache: "no-store",
218
- credentials: "omit",
219
- redirect: "error",
220
- signal: AbortSignal.timeout(30000)
221
- });
222
- if (!asset.ok)
223
- throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset.status}.`);
224
- 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)
227
- throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
228
- if (await options.verifier.digest(contents) !== file.sha256)
229
- throw new TypeError(`Mobile update asset ${file.path} failed integrity verification.`);
230
- await options.store.write(file, contents);
231
- return downloadFiles(manifest, index + 1, total);
248
+ const downloadFiles = async (manifest) => {
249
+ const startedAt = performance.now();
250
+ const transfer = {
251
+ avoidedBytes: 0,
252
+ completedFiles: 0,
253
+ downloadedBytes: 0,
254
+ downloadedFiles: 0,
255
+ durationMs: 0,
256
+ resumedBytes: 0,
257
+ resumedFiles: 0,
258
+ reusedBytes: 0,
259
+ reusedFiles: 0,
260
+ throughputBytesPerSecond: 0,
261
+ totalBytes: manifest.files.reduce((total, file) => total + file.bytes, 0),
262
+ totalFiles: manifest.files.length
263
+ };
264
+ const updateTiming = () => {
265
+ transfer.durationMs = Math.max(0, performance.now() - startedAt);
266
+ transfer.avoidedBytes = transfer.reusedBytes + transfer.resumedBytes;
267
+ transfer.throughputBytesPerSecond = transfer.durationMs > 0 ? Math.round(transfer.downloadedBytes * 1000 / transfer.durationMs) : transfer.downloadedBytes;
268
+ };
269
+ const progress = () => {
270
+ updateTiming();
271
+ try {
272
+ options.onProgress?.({
273
+ ...transfer,
274
+ kind: "download-progress"
275
+ });
276
+ } catch {}
277
+ };
278
+ const controller = new AbortController;
279
+ let next = 0;
280
+ let firstError;
281
+ const downloadFile = async (file) => {
282
+ const staged = await options.store.readStaged?.(file);
283
+ if (staged?.byteLength === file.bytes && await options.verifier.digest(staged) === file.sha256) {
284
+ transfer.resumedBytes += staged.byteLength;
285
+ transfer.resumedFiles += 1;
286
+ transfer.completedFiles += 1;
287
+ progress();
288
+ return;
289
+ }
290
+ const reusable = await options.store.readReusable?.(file);
291
+ if (reusable?.byteLength === file.bytes && await options.verifier.digest(reusable) === file.sha256) {
292
+ await options.store.write(file, reusable);
293
+ transfer.reusedBytes += reusable.byteLength;
294
+ transfer.reusedFiles += 1;
295
+ transfer.completedFiles += 1;
296
+ progress();
297
+ return;
298
+ }
299
+ const candidate = await options.store.readPartial?.(file);
300
+ if (candidate?.byteLength === file.bytes && await options.verifier.digest(candidate) === file.sha256) {
301
+ await options.store.write(file, candidate);
302
+ transfer.resumedBytes += candidate.byteLength;
303
+ transfer.resumedFiles += 1;
304
+ transfer.completedFiles += 1;
305
+ progress();
306
+ return;
307
+ }
308
+ const partial = candidate && candidate.byteLength > 0 && candidate.byteLength < file.bytes ? candidate : new Uint8Array;
309
+ const headers = new Headers;
310
+ if (partial.byteLength > 0) {
311
+ headers.set("if-range", `"${file.sha256}"`);
312
+ headers.set("range", `bytes=${partial.byteLength}-`);
313
+ }
314
+ const asset = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
315
+ cache: "no-store",
316
+ credentials: "omit",
317
+ headers,
318
+ redirect: "error",
319
+ signal: combineSignals([
320
+ controller.signal,
321
+ AbortSignal.timeout(30000)
322
+ ])
323
+ });
324
+ if (!asset.ok)
325
+ throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset.status}.`);
326
+ const ranged = asset.status === 206;
327
+ if (ranged && (partial.byteLength === 0 || !validContentRange(asset.headers.get("content-range"), partial.byteLength, file.bytes)))
328
+ throw new TypeError(`Mobile update asset ${file.path} returned an invalid byte range.`);
329
+ const prefix = ranged ? partial : new Uint8Array;
330
+ if (ranged) {
331
+ transfer.resumedBytes += partial.byteLength;
332
+ transfer.resumedFiles += 1;
333
+ }
334
+ const downloaded = await readBounded(asset, file.bytes - prefix.byteLength, options.store.appendPartial ? async (chunk, offset) => {
335
+ await options.store.appendPartial?.(file, chunk, prefix.byteLength + offset);
336
+ transfer.downloadedBytes += chunk.byteLength;
337
+ progress();
338
+ } : undefined);
339
+ if (!options.store.appendPartial) {
340
+ transfer.downloadedBytes += downloaded.byteLength;
341
+ progress();
342
+ }
343
+ if (transfer.downloadedBytes > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
344
+ throw new TypeError("Mobile update exceeds the maximum transfer size.");
345
+ const contents = combine(prefix, downloaded);
346
+ if (contents.byteLength !== file.bytes)
347
+ throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
348
+ if (await options.verifier.digest(contents) !== file.sha256)
349
+ throw new TypeError(`Mobile update asset ${file.path} failed integrity verification.`);
350
+ await options.store.write(file, contents);
351
+ transfer.downloadedFiles += 1;
352
+ transfer.completedFiles += 1;
353
+ progress();
354
+ };
355
+ const worker = async () => {
356
+ if (firstError)
357
+ return;
358
+ const index = next++;
359
+ const file = manifest.files[index];
360
+ if (!file)
361
+ return;
362
+ try {
363
+ await downloadFile(file);
364
+ } catch (error) {
365
+ firstError ??= error;
366
+ controller.abort();
367
+ }
368
+ await worker();
369
+ };
370
+ await Promise.all(Array.from({
371
+ length: Math.min(networkConcurrency(options.concurrency), manifest.files.length)
372
+ }, () => worker()));
373
+ if (firstError)
374
+ throw firstError;
375
+ updateTiming();
376
+ return transfer;
232
377
  };
233
378
  const check = async (download = false) => {
234
379
  const response = await request(manifestUrl, {
@@ -260,14 +405,18 @@ var createAbsoluteMobileUpdateClient = (options) => {
260
405
  if (!download)
261
406
  return { kind: "update-available", manifest };
262
407
  await options.store.begin(manifest);
408
+ let transfer;
263
409
  try {
264
- await downloadFiles(manifest);
410
+ transfer = await downloadFiles(manifest);
265
411
  await options.store.commit(manifest);
266
412
  } catch (error) {
267
- await options.store.abort(manifest.releaseId);
413
+ if (options.store.suspend)
414
+ await options.store.suspend(manifest.releaseId);
415
+ else
416
+ await options.store.abort(manifest.releaseId);
268
417
  throw error;
269
418
  }
270
- return { kind: "downloaded", manifest };
419
+ return { kind: "downloaded", manifest, transfer };
271
420
  };
272
421
  return {
273
422
  check,
@@ -278,10 +427,12 @@ var createAbsoluteMobileUpdateClient = (options) => {
278
427
 
279
428
  // src/mobile/shellUpdate.ts
280
429
  var STATE_KEY = "absolute.mobile.update.state.v1";
430
+ var STAGING_KEY = "absolute.mobile.update.staging.v1";
281
431
  var INSTALLATION_KEY = "absolute.mobile.update.installation.v1";
282
432
  var RESULT_KEY = Symbol.for("absolutejs.mobile.update.result");
283
433
  var RESULTS_KEY = Symbol.for("absolutejs.mobile.update.results");
284
434
  var ROOT = "NoCloud/ionic_built_snapshots";
435
+ var STAGING_ROOT = "NoCloud/absolute_update_staging";
285
436
  var watchdog = registerPlugin("AbsoluteMobileUpdateWatchdog");
286
437
  var base64Bytes = (value) => Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
287
438
  var arrayBuffer = (value) => new Uint8Array(value).buffer;
@@ -340,6 +491,25 @@ var readState = async () => {
340
491
  }
341
492
  };
342
493
  var writeState = (state) => Preferences.set({ key: STATE_KEY, value: JSON.stringify(state) });
494
+ var clearStagingRoot = () => Filesystem.rmdir({
495
+ directory: Directory.Library,
496
+ path: STAGING_ROOT,
497
+ recursive: true
498
+ }).catch(() => {
499
+ return;
500
+ });
501
+ var readStaging = async () => {
502
+ const { value } = await Preferences.get({ key: STAGING_KEY });
503
+ if (!value)
504
+ return;
505
+ try {
506
+ return parseAbsoluteMobileUpdateManifest(JSON.parse(value));
507
+ } catch {
508
+ await Preferences.remove({ key: STAGING_KEY });
509
+ await clearStagingRoot();
510
+ return;
511
+ }
512
+ };
343
513
  var installationId = async () => {
344
514
  const existing = await Preferences.get({ key: INSTALLATION_KEY });
345
515
  if (existing.value && /^[a-f0-9-]{36}$/u.test(existing.value))
@@ -358,6 +528,7 @@ var webView = () => {
358
528
  };
359
529
  var currentServerBasePath = () => new Promise((resolve) => webView().getServerBasePath(resolve));
360
530
  var releasePath = (releaseId) => `${ROOT}/${releaseId}`;
531
+ var partialPath = (releaseId, file) => `${STAGING_ROOT}/${releaseId}/${file.path}.part`;
361
532
  var releaseNativePath = async (releaseId) => {
362
533
  const { uri } = await Filesystem.getUri({
363
534
  directory: Directory.Library,
@@ -375,12 +546,26 @@ var removeRelease = async (releaseId) => {
375
546
  return;
376
547
  });
377
548
  };
549
+ var removeStaging = async (releaseId) => {
550
+ await Filesystem.rmdir({
551
+ directory: Directory.Library,
552
+ path: `${STAGING_ROOT}/${releaseId}`,
553
+ recursive: true
554
+ }).catch(() => {
555
+ return;
556
+ });
557
+ };
558
+ var filesystemBytes = async (data) => typeof data === "string" ? base64Bytes(data) : new Uint8Array(await data.arrayBuffer());
378
559
  var createStore = () => {
379
560
  let staging;
380
561
  return {
381
562
  abort: async (releaseId) => {
382
563
  staging = undefined;
383
564
  await removeRelease(releaseId);
565
+ await removeStaging(releaseId);
566
+ const persisted = await readStaging();
567
+ if (persisted?.releaseId === releaseId)
568
+ await Preferences.remove({ key: STAGING_KEY });
384
569
  const state = await readState();
385
570
  if (state.readyRelease === releaseId || state.pendingRelease === releaseId)
386
571
  await writeState({
@@ -417,18 +602,59 @@ var createStore = () => {
417
602
  throw error;
418
603
  }
419
604
  },
605
+ appendPartial: async (file, contents, offset) => {
606
+ if (!staging || !staging.files.some((candidate) => candidate.path === file.path))
607
+ throw new TypeError("Mobile update partial write is outside its staging transaction.");
608
+ const path = partialPath(staging.releaseId, file);
609
+ if (offset === 0) {
610
+ await Filesystem.writeFile({
611
+ data: bytesBase64(contents),
612
+ directory: Directory.Library,
613
+ path,
614
+ recursive: true
615
+ });
616
+ return;
617
+ }
618
+ const stat = await Filesystem.stat({
619
+ directory: Directory.Library,
620
+ path
621
+ });
622
+ if (stat.size !== offset)
623
+ throw new TypeError("Mobile update partial checkpoint changed unexpectedly.");
624
+ await Filesystem.appendFile({
625
+ data: bytesBase64(contents),
626
+ directory: Directory.Library,
627
+ path
628
+ });
629
+ },
420
630
  begin: async (manifest) => {
421
- await removeRelease(manifest.releaseId);
631
+ const persisted = await readStaging();
632
+ const resume = persisted?.releaseId === manifest.releaseId && persisted.signature.keyId === manifest.signature.keyId && persisted.signature.value === manifest.signature.value;
633
+ if (persisted && !resume)
634
+ await Promise.all([
635
+ removeRelease(persisted.releaseId),
636
+ removeStaging(persisted.releaseId)
637
+ ]);
638
+ if (!resume) {
639
+ await removeRelease(manifest.releaseId);
640
+ await removeStaging(manifest.releaseId);
641
+ }
422
642
  await Filesystem.mkdir({
423
643
  directory: Directory.Library,
424
644
  path: releasePath(manifest.releaseId),
425
645
  recursive: true
426
646
  });
647
+ await Preferences.set({
648
+ key: STAGING_KEY,
649
+ value: JSON.stringify(manifest)
650
+ });
427
651
  staging = manifest;
428
652
  },
429
653
  commit: async (manifest) => {
430
654
  if (staging?.releaseId !== manifest.releaseId)
431
655
  throw new TypeError("Mobile update staging transaction changed identity.");
656
+ await removeStaging(manifest.releaseId);
657
+ await Preferences.remove({ key: STAGING_KEY });
432
658
  const state = await readState();
433
659
  await writeState({
434
660
  activeRelease: state.activeRelease,
@@ -437,6 +663,38 @@ var createStore = () => {
437
663
  });
438
664
  staging = undefined;
439
665
  },
666
+ readPartial: async (file) => {
667
+ if (!staging)
668
+ return null;
669
+ const result = await Filesystem.readFile({
670
+ directory: Directory.Library,
671
+ path: partialPath(staging.releaseId, file)
672
+ }).catch(() => null);
673
+ return result ? filesystemBytes(result.data).catch(() => null) : null;
674
+ },
675
+ readReusable: async (file) => {
676
+ const state = await readState();
677
+ if (!state.activeRelease)
678
+ return null;
679
+ const result = await Filesystem.readFile({
680
+ directory: Directory.Library,
681
+ path: `${releasePath(state.activeRelease)}/${file.path}`
682
+ }).catch(() => null);
683
+ return result ? filesystemBytes(result.data).catch(() => null) : null;
684
+ },
685
+ readStaged: async (file) => {
686
+ if (!staging)
687
+ return null;
688
+ const result = await Filesystem.readFile({
689
+ directory: Directory.Library,
690
+ path: `${releasePath(staging.releaseId)}/${file.path}`
691
+ }).catch(() => null);
692
+ return result ? filesystemBytes(result.data).catch(() => null) : null;
693
+ },
694
+ suspend: async (releaseId) => {
695
+ if (staging?.releaseId === releaseId)
696
+ staging = undefined;
697
+ },
440
698
  write: async (file, contents) => {
441
699
  if (!staging || !staging.files.some((candidate) => candidate.path === file.path))
442
700
  throw new TypeError("Mobile update write is outside its staging transaction.");
@@ -529,14 +787,27 @@ var installAbsoluteMobileShellUpdates = async (manifest) => {
529
787
  runtimeFingerprint: manifest.nativeRuntime
530
788
  },
531
789
  store,
532
- verifier: createVerifier(manifest.updates.publicKeys)
790
+ verifier: createVerifier(manifest.updates.publicKeys),
791
+ onProgress: (progress) => emitUpdateResult(progress)
533
792
  });
534
793
  const activateDownloaded = async (result) => {
535
794
  if (result.kind !== "downloaded")
536
795
  return;
537
796
  emitUpdateResult({
797
+ avoidedBytes: result.transfer.avoidedBytes,
798
+ completedFiles: result.transfer.completedFiles,
799
+ downloadedBytes: result.transfer.downloadedBytes,
800
+ downloadedFiles: result.transfer.downloadedFiles,
801
+ durationMs: Math.round(result.transfer.durationMs),
538
802
  kind: "downloaded",
539
- releaseId: result.manifest.releaseId
803
+ releaseId: result.manifest.releaseId,
804
+ resumedBytes: result.transfer.resumedBytes,
805
+ resumedFiles: result.transfer.resumedFiles,
806
+ reusedBytes: result.transfer.reusedBytes,
807
+ reusedFiles: result.transfer.reusedFiles,
808
+ throughputBytesPerSecond: result.transfer.throughputBytesPerSecond,
809
+ totalBytes: result.transfer.totalBytes,
810
+ totalFiles: result.transfer.totalFiles
540
811
  });
541
812
  await client.activate(result.manifest.releaseId);
542
813
  };
@@ -13,6 +13,16 @@ 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
+ /** Append a response chunk to persistent staging at the expected offset. */
17
+ appendPartial?(file: AbsoluteMobileUpdateFile, contents: Uint8Array, offset: number): Promise<void>;
18
+ /** Return an unverified, incomplete download from persistent staging. */
19
+ readPartial?(file: AbsoluteMobileUpdateFile): Promise<Uint8Array | null>;
20
+ /** Return a locally cached candidate for this exact path, when available. */
21
+ readReusable?(file: AbsoluteMobileUpdateFile): Promise<Uint8Array | null>;
22
+ /** Return an unverified completed file from a prior staging attempt. */
23
+ readStaged?(file: AbsoluteMobileUpdateFile): Promise<Uint8Array | null>;
24
+ /** End this attempt without deleting persistent staging. */
25
+ suspend?(releaseId: string): Promise<void>;
16
26
  write(file: AbsoluteMobileUpdateFile, contents: Uint8Array): Promise<void>;
17
27
  };
18
28
  export type AbsoluteMobileUpdateVerifier = {
@@ -21,7 +31,10 @@ export type AbsoluteMobileUpdateVerifier = {
21
31
  };
22
32
  export type AbsoluteMobileUpdateClientOptions = {
23
33
  config: AbsoluteMobileUpdateClientConfig;
34
+ /** Maximum parallel asset requests. Network conditions may reduce this. */
35
+ concurrency?: number;
24
36
  fetch?: typeof globalThis.fetch;
37
+ onProgress?: (progress: AbsoluteMobileUpdateProgress) => void;
25
38
  store: AbsoluteMobileUpdateStore;
26
39
  verifier: AbsoluteMobileUpdateVerifier;
27
40
  };
@@ -30,6 +43,7 @@ export type AbsoluteMobileUpdateCheckResult = {
30
43
  } | {
31
44
  kind: 'downloaded';
32
45
  manifest: AbsoluteMobileUpdateManifest;
46
+ transfer: AbsoluteMobileUpdateTransfer;
33
47
  } | {
34
48
  kind: 'quarantined';
35
49
  releaseId: string;
@@ -37,6 +51,23 @@ export type AbsoluteMobileUpdateCheckResult = {
37
51
  kind: 'update-available';
38
52
  manifest: AbsoluteMobileUpdateManifest;
39
53
  };
54
+ export type AbsoluteMobileUpdateTransfer = {
55
+ avoidedBytes: number;
56
+ completedFiles: number;
57
+ downloadedBytes: number;
58
+ downloadedFiles: number;
59
+ durationMs: number;
60
+ resumedBytes: number;
61
+ resumedFiles: number;
62
+ reusedBytes: number;
63
+ reusedFiles: number;
64
+ throughputBytesPerSecond: number;
65
+ totalBytes: number;
66
+ totalFiles: number;
67
+ };
68
+ export type AbsoluteMobileUpdateProgress = AbsoluteMobileUpdateTransfer & {
69
+ kind: 'download-progress';
70
+ };
40
71
  export declare const createAbsoluteMobileUpdateClient: (options: AbsoluteMobileUpdateClientOptions) => {
41
72
  check: (download?: boolean) => Promise<AbsoluteMobileUpdateCheckResult>;
42
73
  activate: (releaseId: string) => Promise<void>;
@@ -3,8 +3,12 @@ import { readAbsoluteMobileUpdate } from './updateSigning';
3
3
  export type AbsoluteMobileUpdatePublication = {
4
4
  appId: string;
5
5
  channel: string;
6
+ storedBytes?: number;
7
+ storedFiles?: number;
6
8
  releaseId: string;
7
9
  reused: boolean;
10
+ reusedBytes?: number;
11
+ reusedFiles?: number;
8
12
  rollout: number;
9
13
  stage: 'published';
10
14
  };
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.7",
12
+ "@absolutejs/deploy": "0.25.9",
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.83",
526
+ "version": "0.20.0-beta.85",
527
527
  "workspaces": [
528
528
  "tests/fixtures/*",
529
529
  "tests/fixtures/_packages/*"