@absolutejs/absolute 0.20.0-beta.60 → 0.20.0-beta.62
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/cli/index.js +548 -95
- package/dist/index.js +11 -3
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +496 -51
- package/dist/mobile/index.js.map +8 -8
- package/dist/mobile/remoteMacAgentEntry.js +195 -195
- package/dist/mobile/shellUpdate.js +94 -7
- package/dist/src/mobile/capacitorBundle.d.ts +1 -0
- package/dist/src/mobile/config.d.ts +1 -0
- package/dist/src/mobile/transport.d.ts +1 -0
- package/dist/src/mobile/updateClient.d.ts +4 -0
- package/dist/src/mobile/updateRuntime.d.ts +1 -1
- package/dist/types/build.d.ts +2 -0
- package/package.json +8 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/mobile/shellUpdate.ts
|
|
2
2
|
import { Directory, Filesystem } from "@capacitor/filesystem";
|
|
3
3
|
import { Preferences } from "@capacitor/preferences";
|
|
4
|
+
import { registerPlugin } from "@capacitor/core";
|
|
4
5
|
|
|
5
6
|
// src/mobile/updateProtocol.ts
|
|
6
7
|
var ABSOLUTE_MOBILE_UPDATE_FORMAT = 1;
|
|
@@ -238,6 +239,8 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
238
239
|
throw new TypeError("Mobile update signature verification failed.");
|
|
239
240
|
if (manifest.releaseId === options.config.currentReleaseId)
|
|
240
241
|
return { kind: "current" };
|
|
242
|
+
if (options.config.blockedReleaseIds?.includes(manifest.releaseId))
|
|
243
|
+
return { kind: "quarantined", releaseId: manifest.releaseId };
|
|
241
244
|
if (!download)
|
|
242
245
|
return { kind: "update-available", manifest };
|
|
243
246
|
await options.store.begin(manifest);
|
|
@@ -260,7 +263,10 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
260
263
|
// src/mobile/shellUpdate.ts
|
|
261
264
|
var STATE_KEY = "absolute.mobile.update.state.v1";
|
|
262
265
|
var INSTALLATION_KEY = "absolute.mobile.update.installation.v1";
|
|
266
|
+
var RESULT_KEY = Symbol.for("absolutejs.mobile.update.result");
|
|
267
|
+
var RESULTS_KEY = Symbol.for("absolutejs.mobile.update.results");
|
|
263
268
|
var ROOT = "NoCloud/ionic_built_snapshots";
|
|
269
|
+
var watchdog = registerPlugin("AbsoluteMobileUpdateWatchdog");
|
|
264
270
|
var base64Bytes = (value) => Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
|
|
265
271
|
var arrayBuffer = (value) => new Uint8Array(value).buffer;
|
|
266
272
|
var bytesBase64 = (value) => {
|
|
@@ -283,10 +289,35 @@ var readState = async () => {
|
|
|
283
289
|
const candidate = Reflect.get(parsed, key);
|
|
284
290
|
return typeof candidate === "string" ? candidate : undefined;
|
|
285
291
|
};
|
|
292
|
+
const number = (key) => {
|
|
293
|
+
const candidate = Reflect.get(parsed, key);
|
|
294
|
+
return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : undefined;
|
|
295
|
+
};
|
|
296
|
+
const recoveryValue = Reflect.get(parsed, "recovery");
|
|
297
|
+
const quarantineValue = Reflect.get(parsed, "quarantinedReleases");
|
|
298
|
+
const quarantinedReleases = Array.isArray(quarantineValue) ? [
|
|
299
|
+
...new Set(quarantineValue.filter((candidateRelease) => typeof candidateRelease === "string" && /^amu_[a-f0-9]{64}$/u.test(candidateRelease)))
|
|
300
|
+
].slice(-8) : [];
|
|
301
|
+
const recovery = typeof recoveryValue === "object" && recoveryValue !== null ? {
|
|
302
|
+
durationMs: Reflect.get(recoveryValue, "durationMs"),
|
|
303
|
+
reason: Reflect.get(recoveryValue, "reason"),
|
|
304
|
+
releaseId: Reflect.get(recoveryValue, "releaseId")
|
|
305
|
+
} : undefined;
|
|
306
|
+
let validRecovery;
|
|
307
|
+
if (recovery && typeof recovery.durationMs === "number" && Number.isFinite(recovery.durationMs) && recovery.durationMs >= 0 && (recovery.reason === "boot-interrupted" || recovery.reason === "boot-timeout") && typeof recovery.releaseId === "string")
|
|
308
|
+
validRecovery = {
|
|
309
|
+
durationMs: recovery.durationMs,
|
|
310
|
+
reason: recovery.reason,
|
|
311
|
+
releaseId: recovery.releaseId
|
|
312
|
+
};
|
|
286
313
|
return {
|
|
287
314
|
...text("activeRelease") ? { activeRelease: text("activeRelease") } : {},
|
|
288
315
|
...text("pendingRelease") ? { pendingRelease: text("pendingRelease") } : {},
|
|
289
|
-
...
|
|
316
|
+
...number("pendingStartedAt") === undefined ? {} : { pendingStartedAt: number("pendingStartedAt") },
|
|
317
|
+
...text("previousPath") ? { previousPath: text("previousPath") } : {},
|
|
318
|
+
...quarantinedReleases.length > 0 ? { quarantinedReleases } : {},
|
|
319
|
+
...text("readyRelease") ? { readyRelease: text("readyRelease") } : {},
|
|
320
|
+
...validRecovery ? { recovery: validRecovery } : {}
|
|
290
321
|
};
|
|
291
322
|
} catch {
|
|
292
323
|
return {};
|
|
@@ -336,18 +367,39 @@ var createStore = () => {
|
|
|
336
367
|
await removeRelease(releaseId);
|
|
337
368
|
const state = await readState();
|
|
338
369
|
if (state.readyRelease === releaseId || state.pendingRelease === releaseId)
|
|
339
|
-
await writeState({
|
|
370
|
+
await writeState({
|
|
371
|
+
activeRelease: state.activeRelease,
|
|
372
|
+
quarantinedReleases: state.quarantinedReleases,
|
|
373
|
+
recovery: state.recovery
|
|
374
|
+
});
|
|
340
375
|
},
|
|
341
376
|
activate: async (releaseId) => {
|
|
342
377
|
const state = await readState();
|
|
343
378
|
if (state.readyRelease !== releaseId)
|
|
344
379
|
throw new TypeError("Mobile update is not committed and ready to activate.");
|
|
345
380
|
const path = await releaseNativePath(releaseId);
|
|
381
|
+
const previousPath = await currentServerBasePath();
|
|
346
382
|
await writeState({
|
|
347
383
|
activeRelease: state.activeRelease,
|
|
348
|
-
pendingRelease: releaseId
|
|
384
|
+
pendingRelease: releaseId,
|
|
385
|
+
pendingStartedAt: Date.now(),
|
|
386
|
+
previousPath,
|
|
387
|
+
quarantinedReleases: state.quarantinedReleases
|
|
349
388
|
});
|
|
350
|
-
|
|
389
|
+
try {
|
|
390
|
+
await watchdog.arm({ releaseId });
|
|
391
|
+
webView().setServerBasePath(path);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
await watchdog.confirm({ releaseId }).catch(() => {
|
|
394
|
+
return;
|
|
395
|
+
});
|
|
396
|
+
await removeRelease(releaseId);
|
|
397
|
+
await writeState({
|
|
398
|
+
activeRelease: state.activeRelease,
|
|
399
|
+
quarantinedReleases: state.quarantinedReleases
|
|
400
|
+
});
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
351
403
|
},
|
|
352
404
|
begin: async (manifest) => {
|
|
353
405
|
await removeRelease(manifest.releaseId);
|
|
@@ -364,6 +416,7 @@ var createStore = () => {
|
|
|
364
416
|
const state = await readState();
|
|
365
417
|
await writeState({
|
|
366
418
|
activeRelease: state.activeRelease,
|
|
419
|
+
quarantinedReleases: state.quarantinedReleases,
|
|
367
420
|
readyRelease: manifest.releaseId
|
|
368
421
|
});
|
|
369
422
|
staging = undefined;
|
|
@@ -397,7 +450,16 @@ var createVerifier = (publicKeys) => ({
|
|
|
397
450
|
}
|
|
398
451
|
}
|
|
399
452
|
});
|
|
400
|
-
var emitUpdateResult = (detail) =>
|
|
453
|
+
var emitUpdateResult = (detail) => {
|
|
454
|
+
Reflect.set(globalThis, RESULT_KEY, detail);
|
|
455
|
+
const existing = Reflect.get(globalThis, RESULTS_KEY);
|
|
456
|
+
const results = Array.isArray(existing) ? existing : [];
|
|
457
|
+
results.push(detail);
|
|
458
|
+
if (results.length > 8)
|
|
459
|
+
results.splice(0, results.length - 8);
|
|
460
|
+
Reflect.set(globalThis, RESULTS_KEY, results);
|
|
461
|
+
dispatchEvent(new CustomEvent("absolute:mobile-update", { detail }));
|
|
462
|
+
};
|
|
401
463
|
var removePriorRelease = async (prior, active) => {
|
|
402
464
|
if (prior && prior !== active)
|
|
403
465
|
await removeRelease(prior);
|
|
@@ -416,18 +478,34 @@ var reconcilePendingRelease = async (store, state) => {
|
|
|
416
478
|
webView().persistServerBasePath();
|
|
417
479
|
const next = { activeRelease: state.pendingRelease };
|
|
418
480
|
await writeState(next);
|
|
481
|
+
await watchdog.confirm({ releaseId: state.pendingRelease });
|
|
419
482
|
await removePriorRelease(state.activeRelease, next.activeRelease);
|
|
420
483
|
emitUpdateResult({ kind: "activated", releaseId: next.activeRelease });
|
|
421
484
|
return next;
|
|
422
485
|
};
|
|
486
|
+
var consumeNativeRecovery = async (state) => {
|
|
487
|
+
if (!state.recovery)
|
|
488
|
+
return state;
|
|
489
|
+
const { recovery, ...next } = state;
|
|
490
|
+
emitUpdateResult({
|
|
491
|
+
durationMs: Math.round(recovery.durationMs),
|
|
492
|
+
kind: "rolled-back",
|
|
493
|
+
reason: recovery.reason,
|
|
494
|
+
releaseId: recovery.releaseId
|
|
495
|
+
});
|
|
496
|
+
await writeState(next);
|
|
497
|
+
return next;
|
|
498
|
+
};
|
|
423
499
|
var installAbsoluteMobileShellUpdates = async (manifest) => {
|
|
424
500
|
if (!manifest.updates)
|
|
425
501
|
return;
|
|
426
502
|
const store = createStore();
|
|
427
|
-
const
|
|
503
|
+
const recovered = await consumeNativeRecovery(await readState());
|
|
504
|
+
const state = await reconcilePendingRelease(store, recovered);
|
|
428
505
|
const client = createAbsoluteMobileUpdateClient({
|
|
429
506
|
config: {
|
|
430
507
|
appId: manifest.appId,
|
|
508
|
+
blockedReleaseIds: state.quarantinedReleases ?? [],
|
|
431
509
|
channel: manifest.updates.channel,
|
|
432
510
|
currentReleaseId: state.activeRelease ?? `embedded:${manifest.appBuild}`,
|
|
433
511
|
installationId: await installationId(),
|
|
@@ -446,7 +524,16 @@ var installAbsoluteMobileShellUpdates = async (manifest) => {
|
|
|
446
524
|
});
|
|
447
525
|
await client.activate(result.manifest.releaseId);
|
|
448
526
|
};
|
|
449
|
-
client.download().then(
|
|
527
|
+
client.download().then((result) => {
|
|
528
|
+
if (result.kind === "quarantined") {
|
|
529
|
+
emitUpdateResult({
|
|
530
|
+
kind: "quarantined",
|
|
531
|
+
releaseId: result.releaseId
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
return activateDownloaded(result);
|
|
536
|
+
}).catch((error) => {
|
|
450
537
|
console.error("[Absolute Mobile] Update failed:", error);
|
|
451
538
|
emitUpdateResult({ kind: "failed" });
|
|
452
539
|
});
|
|
@@ -24,6 +24,7 @@ export declare const materializeAbsoluteCapacitorWebBundle: (options: AbsoluteCa
|
|
|
24
24
|
storageSchema: SyncLocalStoreSchemaBundle;
|
|
25
25
|
} | undefined;
|
|
26
26
|
updates?: {
|
|
27
|
+
bootTimeoutMs: number;
|
|
27
28
|
channel: string;
|
|
28
29
|
manifestUrl: string;
|
|
29
30
|
publicKeys: Record<string, string>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AbsoluteMobileUpdateFile, type AbsoluteMobileUpdateManifest } from './updateProtocol';
|
|
2
2
|
export type AbsoluteMobileUpdateClientConfig = {
|
|
3
3
|
appId: string;
|
|
4
|
+
blockedReleaseIds?: readonly string[];
|
|
4
5
|
channel: string;
|
|
5
6
|
currentReleaseId: string;
|
|
6
7
|
installationId: string;
|
|
@@ -29,6 +30,9 @@ export type AbsoluteMobileUpdateCheckResult = {
|
|
|
29
30
|
} | {
|
|
30
31
|
kind: 'downloaded';
|
|
31
32
|
manifest: AbsoluteMobileUpdateManifest;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'quarantined';
|
|
35
|
+
releaseId: string;
|
|
32
36
|
} | {
|
|
33
37
|
kind: 'update-available';
|
|
34
38
|
manifest: AbsoluteMobileUpdateManifest;
|
|
@@ -2,7 +2,7 @@ import type { AbsoluteDeviceCapabilityPlan } from './deviceCapabilities';
|
|
|
2
2
|
import type { NormalizedAbsoluteMobileConfig } from './config';
|
|
3
3
|
import type { AbsoluteMobileAuthManifest } from './nativeAuth';
|
|
4
4
|
import type { SyncLocalStoreSchemaBundle } from '@absolutejs/sync/client';
|
|
5
|
-
export declare const ABSOLUTE_MOBILE_SHELL_ABI:
|
|
5
|
+
export declare const ABSOLUTE_MOBILE_SHELL_ABI: 2;
|
|
6
6
|
export declare const ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT: 1;
|
|
7
7
|
export type AbsoluteMobileUpdateRuntimeDescriptor = {
|
|
8
8
|
appId: string;
|
package/dist/types/build.d.ts
CHANGED
|
@@ -57,6 +57,8 @@ type MobileSharedConfig = {
|
|
|
57
57
|
};
|
|
58
58
|
/** Signed over-the-air web-bundle updates. Native capability changes still require a store build. */
|
|
59
59
|
updates?: {
|
|
60
|
+
/** Maximum time an activated bundle may take to render its first page before native rollback. Defaults to 20 seconds. */
|
|
61
|
+
bootTimeoutMs?: number;
|
|
60
62
|
/** Release channel. Defaults to `production`. */
|
|
61
63
|
channel?: string;
|
|
62
64
|
/** Exact signed-manifest URL. Defaults to the AbsoluteJS production origin. */
|
package/package.json
CHANGED
|
@@ -293,12 +293,12 @@
|
|
|
293
293
|
"main": "./dist/index.js",
|
|
294
294
|
"name": "@absolutejs/absolute",
|
|
295
295
|
"optionalDependencies": {
|
|
296
|
-
"@absolutejs/native-darwin-arm64": "0.20.0-beta.
|
|
297
|
-
"@absolutejs/native-darwin-x64": "0.20.0-beta.
|
|
298
|
-
"@absolutejs/native-linux-arm64": "0.20.0-beta.
|
|
299
|
-
"@absolutejs/native-linux-x64": "0.20.0-beta.
|
|
300
|
-
"@absolutejs/native-windows-arm64": "0.20.0-beta.
|
|
301
|
-
"@absolutejs/native-windows-x64": "0.20.0-beta.
|
|
296
|
+
"@absolutejs/native-darwin-arm64": "0.20.0-beta.62",
|
|
297
|
+
"@absolutejs/native-darwin-x64": "0.20.0-beta.62",
|
|
298
|
+
"@absolutejs/native-linux-arm64": "0.20.0-beta.62",
|
|
299
|
+
"@absolutejs/native-linux-x64": "0.20.0-beta.62",
|
|
300
|
+
"@absolutejs/native-windows-arm64": "0.20.0-beta.62",
|
|
301
|
+
"@absolutejs/native-windows-x64": "0.20.0-beta.62"
|
|
302
302
|
},
|
|
303
303
|
"overrides": {
|
|
304
304
|
"@sinclair/typebox": "0.34.52",
|
|
@@ -460,6 +460,7 @@
|
|
|
460
460
|
"test:native:android:bundle": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts",
|
|
461
461
|
"test:native:android:hmr": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-hmr-conformance.test.ts",
|
|
462
462
|
"test:native:android:lifecycle": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-lifecycle-conformance.test.ts",
|
|
463
|
+
"test:native:android:updates": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts -t 'signed web updates'",
|
|
463
464
|
"test:native:android:upgrade": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts -t 'installed APK upgrade'",
|
|
464
465
|
"test:native:ios": "bun run test:native:ios:lifecycle && bun run test:native:ios:bundle",
|
|
465
466
|
"test:native:ios:bundle": "ABSOLUTE_TEST_NATIVE_IOS=1 bun test tests/native/ios-embedded-bundle-conformance.test.ts",
|
|
@@ -514,7 +515,7 @@
|
|
|
514
515
|
]
|
|
515
516
|
}
|
|
516
517
|
},
|
|
517
|
-
"version": "0.20.0-beta.
|
|
518
|
+
"version": "0.20.0-beta.62",
|
|
518
519
|
"workspaces": [
|
|
519
520
|
"tests/fixtures/*",
|
|
520
521
|
"tests/fixtures/_packages/*"
|