@absolutejs/absolute 0.20.0-beta.84 → 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 +39 -7
- package/dist/build.js.map +3 -3
- package/dist/cli/{compile-t73ac1zb.js → compile-d7g2fvqe.js} +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/index.js +39 -7
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +212 -52
- package/dist/mobile/index.js.map +4 -4
- package/dist/mobile/shellUpdate.js +280 -47
- package/dist/src/mobile/updateClient.d.ts +20 -0
- package/package.json +2 -2
|
@@ -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
|
|
190
|
+
for (const chunk of chunks) {
|
|
189
191
|
contents.set(chunk, offset);
|
|
190
192
|
offset += chunk.byteLength;
|
|
191
193
|
}
|
|
@@ -206,49 +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
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
+
])
|
|
230
323
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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;
|
|
252
377
|
};
|
|
253
378
|
const check = async (download = false) => {
|
|
254
379
|
const response = await request(manifestUrl, {
|
|
@@ -285,7 +410,10 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
285
410
|
transfer = await downloadFiles(manifest);
|
|
286
411
|
await options.store.commit(manifest);
|
|
287
412
|
} catch (error) {
|
|
288
|
-
|
|
413
|
+
if (options.store.suspend)
|
|
414
|
+
await options.store.suspend(manifest.releaseId);
|
|
415
|
+
else
|
|
416
|
+
await options.store.abort(manifest.releaseId);
|
|
289
417
|
throw error;
|
|
290
418
|
}
|
|
291
419
|
return { kind: "downloaded", manifest, transfer };
|
|
@@ -299,10 +427,12 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
299
427
|
|
|
300
428
|
// src/mobile/shellUpdate.ts
|
|
301
429
|
var STATE_KEY = "absolute.mobile.update.state.v1";
|
|
430
|
+
var STAGING_KEY = "absolute.mobile.update.staging.v1";
|
|
302
431
|
var INSTALLATION_KEY = "absolute.mobile.update.installation.v1";
|
|
303
432
|
var RESULT_KEY = Symbol.for("absolutejs.mobile.update.result");
|
|
304
433
|
var RESULTS_KEY = Symbol.for("absolutejs.mobile.update.results");
|
|
305
434
|
var ROOT = "NoCloud/ionic_built_snapshots";
|
|
435
|
+
var STAGING_ROOT = "NoCloud/absolute_update_staging";
|
|
306
436
|
var watchdog = registerPlugin("AbsoluteMobileUpdateWatchdog");
|
|
307
437
|
var base64Bytes = (value) => Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
|
|
308
438
|
var arrayBuffer = (value) => new Uint8Array(value).buffer;
|
|
@@ -361,6 +491,25 @@ var readState = async () => {
|
|
|
361
491
|
}
|
|
362
492
|
};
|
|
363
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
|
+
};
|
|
364
513
|
var installationId = async () => {
|
|
365
514
|
const existing = await Preferences.get({ key: INSTALLATION_KEY });
|
|
366
515
|
if (existing.value && /^[a-f0-9-]{36}$/u.test(existing.value))
|
|
@@ -379,6 +528,7 @@ var webView = () => {
|
|
|
379
528
|
};
|
|
380
529
|
var currentServerBasePath = () => new Promise((resolve) => webView().getServerBasePath(resolve));
|
|
381
530
|
var releasePath = (releaseId) => `${ROOT}/${releaseId}`;
|
|
531
|
+
var partialPath = (releaseId, file) => `${STAGING_ROOT}/${releaseId}/${file.path}.part`;
|
|
382
532
|
var releaseNativePath = async (releaseId) => {
|
|
383
533
|
const { uri } = await Filesystem.getUri({
|
|
384
534
|
directory: Directory.Library,
|
|
@@ -396,6 +546,15 @@ var removeRelease = async (releaseId) => {
|
|
|
396
546
|
return;
|
|
397
547
|
});
|
|
398
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
|
+
};
|
|
399
558
|
var filesystemBytes = async (data) => typeof data === "string" ? base64Bytes(data) : new Uint8Array(await data.arrayBuffer());
|
|
400
559
|
var createStore = () => {
|
|
401
560
|
let staging;
|
|
@@ -403,6 +562,10 @@ var createStore = () => {
|
|
|
403
562
|
abort: async (releaseId) => {
|
|
404
563
|
staging = undefined;
|
|
405
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 });
|
|
406
569
|
const state = await readState();
|
|
407
570
|
if (state.readyRelease === releaseId || state.pendingRelease === releaseId)
|
|
408
571
|
await writeState({
|
|
@@ -439,18 +602,59 @@ var createStore = () => {
|
|
|
439
602
|
throw error;
|
|
440
603
|
}
|
|
441
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
|
+
},
|
|
442
630
|
begin: async (manifest) => {
|
|
443
|
-
await
|
|
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
|
+
}
|
|
444
642
|
await Filesystem.mkdir({
|
|
445
643
|
directory: Directory.Library,
|
|
446
644
|
path: releasePath(manifest.releaseId),
|
|
447
645
|
recursive: true
|
|
448
646
|
});
|
|
647
|
+
await Preferences.set({
|
|
648
|
+
key: STAGING_KEY,
|
|
649
|
+
value: JSON.stringify(manifest)
|
|
650
|
+
});
|
|
449
651
|
staging = manifest;
|
|
450
652
|
},
|
|
451
653
|
commit: async (manifest) => {
|
|
452
654
|
if (staging?.releaseId !== manifest.releaseId)
|
|
453
655
|
throw new TypeError("Mobile update staging transaction changed identity.");
|
|
656
|
+
await removeStaging(manifest.releaseId);
|
|
657
|
+
await Preferences.remove({ key: STAGING_KEY });
|
|
454
658
|
const state = await readState();
|
|
455
659
|
await writeState({
|
|
456
660
|
activeRelease: state.activeRelease,
|
|
@@ -459,6 +663,15 @@ var createStore = () => {
|
|
|
459
663
|
});
|
|
460
664
|
staging = undefined;
|
|
461
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
|
+
},
|
|
462
675
|
readReusable: async (file) => {
|
|
463
676
|
const state = await readState();
|
|
464
677
|
if (!state.activeRelease)
|
|
@@ -469,6 +682,19 @@ var createStore = () => {
|
|
|
469
682
|
}).catch(() => null);
|
|
470
683
|
return result ? filesystemBytes(result.data).catch(() => null) : null;
|
|
471
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
|
+
},
|
|
472
698
|
write: async (file, contents) => {
|
|
473
699
|
if (!staging || !staging.files.some((candidate) => candidate.path === file.path))
|
|
474
700
|
throw new TypeError("Mobile update write is outside its staging transaction.");
|
|
@@ -561,18 +787,25 @@ var installAbsoluteMobileShellUpdates = async (manifest) => {
|
|
|
561
787
|
runtimeFingerprint: manifest.nativeRuntime
|
|
562
788
|
},
|
|
563
789
|
store,
|
|
564
|
-
verifier: createVerifier(manifest.updates.publicKeys)
|
|
790
|
+
verifier: createVerifier(manifest.updates.publicKeys),
|
|
791
|
+
onProgress: (progress) => emitUpdateResult(progress)
|
|
565
792
|
});
|
|
566
793
|
const activateDownloaded = async (result) => {
|
|
567
794
|
if (result.kind !== "downloaded")
|
|
568
795
|
return;
|
|
569
796
|
emitUpdateResult({
|
|
797
|
+
avoidedBytes: result.transfer.avoidedBytes,
|
|
798
|
+
completedFiles: result.transfer.completedFiles,
|
|
570
799
|
downloadedBytes: result.transfer.downloadedBytes,
|
|
571
800
|
downloadedFiles: result.transfer.downloadedFiles,
|
|
801
|
+
durationMs: Math.round(result.transfer.durationMs),
|
|
572
802
|
kind: "downloaded",
|
|
573
803
|
releaseId: result.manifest.releaseId,
|
|
804
|
+
resumedBytes: result.transfer.resumedBytes,
|
|
805
|
+
resumedFiles: result.transfer.resumedFiles,
|
|
574
806
|
reusedBytes: result.transfer.reusedBytes,
|
|
575
807
|
reusedFiles: result.transfer.reusedFiles,
|
|
808
|
+
throughputBytesPerSecond: result.transfer.throughputBytesPerSecond,
|
|
576
809
|
totalBytes: result.transfer.totalBytes,
|
|
577
810
|
totalFiles: result.transfer.totalFiles
|
|
578
811
|
});
|
|
@@ -13,8 +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>;
|
|
16
20
|
/** Return a locally cached candidate for this exact path, when available. */
|
|
17
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>;
|
|
18
26
|
write(file: AbsoluteMobileUpdateFile, contents: Uint8Array): Promise<void>;
|
|
19
27
|
};
|
|
20
28
|
export type AbsoluteMobileUpdateVerifier = {
|
|
@@ -23,7 +31,10 @@ export type AbsoluteMobileUpdateVerifier = {
|
|
|
23
31
|
};
|
|
24
32
|
export type AbsoluteMobileUpdateClientOptions = {
|
|
25
33
|
config: AbsoluteMobileUpdateClientConfig;
|
|
34
|
+
/** Maximum parallel asset requests. Network conditions may reduce this. */
|
|
35
|
+
concurrency?: number;
|
|
26
36
|
fetch?: typeof globalThis.fetch;
|
|
37
|
+
onProgress?: (progress: AbsoluteMobileUpdateProgress) => void;
|
|
27
38
|
store: AbsoluteMobileUpdateStore;
|
|
28
39
|
verifier: AbsoluteMobileUpdateVerifier;
|
|
29
40
|
};
|
|
@@ -41,13 +52,22 @@ export type AbsoluteMobileUpdateCheckResult = {
|
|
|
41
52
|
manifest: AbsoluteMobileUpdateManifest;
|
|
42
53
|
};
|
|
43
54
|
export type AbsoluteMobileUpdateTransfer = {
|
|
55
|
+
avoidedBytes: number;
|
|
56
|
+
completedFiles: number;
|
|
44
57
|
downloadedBytes: number;
|
|
45
58
|
downloadedFiles: number;
|
|
59
|
+
durationMs: number;
|
|
60
|
+
resumedBytes: number;
|
|
61
|
+
resumedFiles: number;
|
|
46
62
|
reusedBytes: number;
|
|
47
63
|
reusedFiles: number;
|
|
64
|
+
throughputBytesPerSecond: number;
|
|
48
65
|
totalBytes: number;
|
|
49
66
|
totalFiles: number;
|
|
50
67
|
};
|
|
68
|
+
export type AbsoluteMobileUpdateProgress = AbsoluteMobileUpdateTransfer & {
|
|
69
|
+
kind: 'download-progress';
|
|
70
|
+
};
|
|
51
71
|
export declare const createAbsoluteMobileUpdateClient: (options: AbsoluteMobileUpdateClientOptions) => {
|
|
52
72
|
check: (download?: boolean) => Promise<AbsoluteMobileUpdateCheckResult>;
|
|
53
73
|
activate: (releaseId: string) => Promise<void>;
|
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.
|
|
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.
|
|
526
|
+
"version": "0.20.0-beta.85",
|
|
527
527
|
"workspaces": [
|
|
528
528
|
"tests/fixtures/*",
|
|
529
529
|
"tests/fixtures/_packages/*"
|