@absolutejs/absolute 0.20.0-beta.86 → 0.20.0-beta.88
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 +350 -33
- package/dist/build.js.map +5 -5
- package/dist/cli/{compile-e9cn3xpx.js → compile-5hchbcbd.js} +2 -2
- package/dist/cli/{config-z2kf085h.js → config-vy04mqce.js} +1 -1
- package/dist/cli/{dev-3hweza3s.js → dev-6shm8qsn.js} +1 -1
- package/dist/cli/{index-9rk7v35t.js → index-39j0dhq7.js} +2 -2
- package/dist/cli/{index-x48brywr.js → index-jqdt7841.js} +1 -1
- package/dist/cli/{index-3tsmbyze.js → index-rd2hbttb.js} +45 -0
- package/dist/cli/index.js +5 -5
- package/dist/cli/{mobile-2p3z33t1.js → mobile-bg1jy34n.js} +151 -28
- package/dist/cli/{start-7t186mhg.js → start-7jg0csqy.js} +3 -3
- package/dist/index.js +350 -33
- package/dist/index.js.map +5 -5
- package/dist/mobile/index.js +405 -45
- package/dist/mobile/index.js.map +7 -7
- package/dist/mobile/remoteMacAgentEntry.js +167 -167
- package/dist/mobile/shellAuth.js +79 -1
- package/dist/mobile/shellUpdate.js +50 -11
- package/dist/src/mobile/config.d.ts +9 -0
- package/dist/src/mobile/shellAuth.d.ts +8 -0
- package/dist/src/mobile/updatePublisher.d.ts +67 -1
- package/dist/src/mobile/updateServer.d.ts +18 -0
- package/dist/types/build.d.ts +16 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -10636,7 +10636,7 @@ __export(exports_config, {
|
|
|
10636
10636
|
import { readFileSync as readFileSync11 } from "fs";
|
|
10637
10637
|
import { resolve as resolve12 } from "path";
|
|
10638
10638
|
import { createHash as createHash4, createPublicKey, X509Certificate } from "crypto";
|
|
10639
|
-
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, ENVIRONMENT_NAME_PATTERN, DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000, DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2, DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20, MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000, MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
|
|
10639
|
+
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, ENVIRONMENT_NAME_PATTERN, DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000, DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2, DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20, DEFAULT_UPDATE_ROLLOUT_FAILURE_RATE = 0.05, DEFAULT_UPDATE_ROLLOUT_OBSERVATION_MINUTES = 60, MINUTE_MS, DEFAULT_UPDATE_ROLLOUT_STAGES, MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000, MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
|
|
10640
10640
|
const root = resolve12(projectRoot);
|
|
10641
10641
|
const path = resolve12(root, value);
|
|
10642
10642
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
@@ -10844,6 +10844,42 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
10844
10844
|
throw new TypeError("mobile.updates.server.health.secretEnv must be a valid environment variable name.");
|
|
10845
10845
|
health = { failureRate, minimumReports, secretEnv };
|
|
10846
10846
|
}
|
|
10847
|
+
const configuredRollout = config.updates.server?.rollout;
|
|
10848
|
+
let rollout;
|
|
10849
|
+
if (configuredRollout !== undefined && configuredRollout !== false) {
|
|
10850
|
+
if (!health)
|
|
10851
|
+
throw new TypeError("mobile.updates.server.rollout requires fleet health to be enabled.");
|
|
10852
|
+
if (configuredRollout.automatic !== undefined && typeof configuredRollout.automatic !== "boolean")
|
|
10853
|
+
throw new TypeError("mobile.updates.server.rollout.automatic must be boolean.");
|
|
10854
|
+
const configuredStages = configuredRollout.stages ?? DEFAULT_UPDATE_ROLLOUT_STAGES;
|
|
10855
|
+
const stages = configuredStages.map((stage, index) => {
|
|
10856
|
+
const previousStage = configuredStages[index - 1];
|
|
10857
|
+
const maximumFailureRate = stage.maximumFailureRate ?? DEFAULT_UPDATE_ROLLOUT_FAILURE_RATE;
|
|
10858
|
+
const minimumReports = stage.minimumReports ?? DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS;
|
|
10859
|
+
const observationMinutes = stage.observationMinutes ?? DEFAULT_UPDATE_ROLLOUT_OBSERVATION_MINUTES;
|
|
10860
|
+
const observationMs = observationMinutes * MINUTE_MS;
|
|
10861
|
+
if (!Number.isFinite(stage.rollout) || stage.rollout <= 0 || stage.rollout > 1 || previousStage !== undefined && stage.rollout <= previousStage.rollout)
|
|
10862
|
+
throw new TypeError("mobile.updates.server.rollout stages must be strictly increasing fractions greater than 0 and at most 1.");
|
|
10863
|
+
if (!Number.isFinite(maximumFailureRate) || maximumFailureRate < 0 || maximumFailureRate >= health.failureRate)
|
|
10864
|
+
throw new TypeError("mobile.updates.server.rollout maximumFailureRate must be non-negative and lower than the fleet-health pause rate.");
|
|
10865
|
+
if (!Number.isSafeInteger(minimumReports) || minimumReports < health.minimumReports)
|
|
10866
|
+
throw new TypeError("mobile.updates.server.rollout minimumReports must be an integer at least as large as the fleet-health minimumReports.");
|
|
10867
|
+
if (!Number.isFinite(observationMinutes) || observationMinutes < 0 || !Number.isSafeInteger(observationMs))
|
|
10868
|
+
throw new TypeError("mobile.updates.server.rollout observationMinutes must produce a non-negative whole number of milliseconds.");
|
|
10869
|
+
return {
|
|
10870
|
+
maximumFailureRate,
|
|
10871
|
+
minimumReports,
|
|
10872
|
+
observationMs,
|
|
10873
|
+
rollout: stage.rollout
|
|
10874
|
+
};
|
|
10875
|
+
});
|
|
10876
|
+
if (stages.length === 0 || stages.at(-1)?.rollout !== 1)
|
|
10877
|
+
throw new TypeError("mobile.updates.server.rollout stages must end at rollout 1.");
|
|
10878
|
+
rollout = {
|
|
10879
|
+
automatic: configuredRollout.automatic ?? false,
|
|
10880
|
+
stages
|
|
10881
|
+
};
|
|
10882
|
+
}
|
|
10847
10883
|
if (autoMount) {
|
|
10848
10884
|
const manifest = new URL(updates.manifestUrl);
|
|
10849
10885
|
if (manifest.origin !== productionOrigin)
|
|
@@ -10880,6 +10916,7 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
10880
10916
|
autoMount,
|
|
10881
10917
|
expoCodeSigningKeys,
|
|
10882
10918
|
...health ? { health } : {},
|
|
10919
|
+
...rollout ? { rollout } : {},
|
|
10883
10920
|
registryModule
|
|
10884
10921
|
};
|
|
10885
10922
|
}, validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
|
|
@@ -10973,6 +11010,12 @@ var init_config = __esm(() => {
|
|
|
10973
11010
|
UPDATE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
10974
11011
|
UPDATE_PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/u;
|
|
10975
11012
|
ENVIRONMENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
11013
|
+
MINUTE_MS = 60 * 1000;
|
|
11014
|
+
DEFAULT_UPDATE_ROLLOUT_STAGES = [
|
|
11015
|
+
{ minimumReports: 20, observationMinutes: 60, rollout: 0.05 },
|
|
11016
|
+
{ minimumReports: 100, observationMinutes: 360, rollout: 0.25 },
|
|
11017
|
+
{ minimumReports: 100, observationMinutes: 0, rollout: 1 }
|
|
11018
|
+
];
|
|
10976
11019
|
HOSTNAME_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/;
|
|
10977
11020
|
EXPO_RESERVED_ROUTE_PREFIXES = new Set([
|
|
10978
11021
|
"_expo",
|
|
@@ -12302,6 +12345,7 @@ import {
|
|
|
12302
12345
|
createHmac,
|
|
12303
12346
|
createPrivateKey,
|
|
12304
12347
|
createPublicKey as createPublicKey2,
|
|
12348
|
+
randomUUID,
|
|
12305
12349
|
sign,
|
|
12306
12350
|
timingSafeEqual,
|
|
12307
12351
|
verify,
|
|
@@ -12438,7 +12482,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12438
12482
|
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
12439
12483
|
const appId = text2(value.appId, "channel appId");
|
|
12440
12484
|
const channel = text2(value.channel, "channel");
|
|
12441
|
-
if (!APP_ID.test(appId) || !NAME.test(channel) || !iso(value.promotedAt) || typeof value.rollout !== "number" || value.rollout < 0 || value.rollout > 1 || value.releaseId !== undefined && (typeof value.releaseId !== "string" || !RELEASE.test(value.releaseId)) || value.fallbackReleaseId !== undefined && (typeof value.fallbackReleaseId !== "string" || !RELEASE.test(value.fallbackReleaseId)) || value.activationId !== undefined && (typeof value.activationId !== "string" || !HASH.test(value.activationId)) || value.activatedAt !== undefined && !iso(value.activatedAt) || value.activationId === undefined !== (value.activatedAt === undefined))
|
|
12485
|
+
if (!APP_ID.test(appId) || !NAME.test(channel) || !iso(value.promotedAt) || typeof value.rollout !== "number" || value.rollout < 0 || value.rollout > 1 || value.releaseId !== undefined && (typeof value.releaseId !== "string" || !RELEASE.test(value.releaseId)) || value.fallbackReleaseId !== undefined && (typeof value.fallbackReleaseId !== "string" || !RELEASE.test(value.fallbackReleaseId)) || value.promotionId !== undefined && (typeof value.promotionId !== "string" || !HASH.test(value.promotionId)) || value.activationId !== undefined && (typeof value.activationId !== "string" || !HASH.test(value.activationId)) || value.activatedAt !== undefined && !iso(value.activatedAt) || value.activationId === undefined !== (value.activatedAt === undefined))
|
|
12442
12486
|
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
12443
12487
|
return {
|
|
12444
12488
|
...value.activationId ? { activationId: value.activationId } : {},
|
|
@@ -12447,6 +12491,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12447
12491
|
channel,
|
|
12448
12492
|
...value.fallbackReleaseId ? { fallbackReleaseId: value.fallbackReleaseId } : {},
|
|
12449
12493
|
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
12494
|
+
...value.promotionId ? { promotionId: value.promotionId } : {},
|
|
12450
12495
|
promotedAt: value.promotedAt,
|
|
12451
12496
|
...value.releaseId ? { releaseId: value.releaseId } : {},
|
|
12452
12497
|
rollout: value.rollout
|
|
@@ -12469,7 +12514,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12469
12514
|
return true;
|
|
12470
12515
|
const value = createHash5("sha256").update(`${input.appId}\x00${input.channel}\x00${input.releaseId}\x00${input.installationId}`).digest().readUInt32BE(0);
|
|
12471
12516
|
return value / 4294967296 < input.rollout;
|
|
12472
|
-
}, base64Url = (value) => Buffer.from(value).toString("base64url"), healthPromotionId = (channel) => digest(new TextEncoder().encode(`${channel.appId}\x00${channel.channel}\x00${channel.releaseId ?? "embedded"}\x00${channel.promotedAt}`)), finiteMetric = (value, field) => {
|
|
12517
|
+
}, base64Url = (value) => Buffer.from(value).toString("base64url"), healthPromotionId = (channel) => channel.promotionId ?? digest(new TextEncoder().encode(`${channel.appId}\x00${channel.channel}\x00${channel.releaseId ?? "embedded"}\x00${channel.promotedAt}`)), finiteMetric = (value, field) => {
|
|
12473
12518
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
12474
12519
|
throw new MobileUpdateRegistryError(`Mobile update health ${field} is invalid`);
|
|
12475
12520
|
return value;
|
|
@@ -12490,10 +12535,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12490
12535
|
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
12491
12536
|
const clock = options.clock ?? (() => new Date);
|
|
12492
12537
|
const health = options.health;
|
|
12538
|
+
const rollout = options.rollout;
|
|
12493
12539
|
if (health && health.secret.length < 32)
|
|
12494
12540
|
throw new MobileUpdateRegistryError("Mobile update health secret must contain at least 32 characters");
|
|
12495
12541
|
if (health && !options.store.list)
|
|
12496
12542
|
throw new MobileUpdateRegistryError("Mobile update health requires storage lifecycle listing");
|
|
12543
|
+
if (rollout && (!health || !options.store.list))
|
|
12544
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration requires fleet health and storage lifecycle listing");
|
|
12545
|
+
if (rollout && (rollout.stages.length === 0 || rollout.stages.some((stage, index) => stage.rollout <= 0 || stage.rollout > 1 || !Number.isSafeInteger(stage.minimumReports) || stage.minimumReports < 1 || !Number.isSafeInteger(stage.observationMs) || stage.observationMs < 0 || stage.maximumFailureRate < 0 || stage.maximumFailureRate >= 1 || index > 0 && stage.rollout <= rollout.stages[index - 1].rollout)))
|
|
12546
|
+
throw new MobileUpdateRegistryError("Mobile update rollout stages are invalid");
|
|
12497
12547
|
const minimumReports = health?.autoPause?.minimumReports ?? 20;
|
|
12498
12548
|
const failureThreshold = health?.autoPause?.failureRate ?? 0.2;
|
|
12499
12549
|
if (health && (!Number.isSafeInteger(minimumReports) || minimumReports < 1 || failureThreshold <= 0 || failureThreshold > 1))
|
|
@@ -12506,6 +12556,8 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12506
12556
|
const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
|
|
12507
12557
|
const healthRoot = (appId, promotionId, releaseId) => `${root(appId)}/health/${promotionId}/${releaseId}`;
|
|
12508
12558
|
const pauseKey = (appId, promotionId, releaseId) => `${healthRoot(appId, promotionId, releaseId)}/paused.json`;
|
|
12559
|
+
const rolloutRoot = (appId, promotionId, releaseId) => `${healthRoot(appId, promotionId, releaseId)}/rollout`;
|
|
12560
|
+
const rolloutPlanKey = (appId, promotionId, releaseId) => `${rolloutRoot(appId, promotionId, releaseId)}/plan.json`;
|
|
12509
12561
|
const channelKey = (appId, channel) => {
|
|
12510
12562
|
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
12511
12563
|
throw new MobileUpdateRegistryError("Mobile update channel identity is invalid");
|
|
@@ -12534,6 +12586,81 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12534
12586
|
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
12535
12587
|
return value;
|
|
12536
12588
|
};
|
|
12589
|
+
const listPrefix = async (value) => {
|
|
12590
|
+
const objects = [];
|
|
12591
|
+
const cursors = new Set;
|
|
12592
|
+
let cursor;
|
|
12593
|
+
do {
|
|
12594
|
+
const page = await options.store.list({
|
|
12595
|
+
...cursor ? { cursor } : {},
|
|
12596
|
+
prefix: value
|
|
12597
|
+
});
|
|
12598
|
+
objects.push(...page.objects);
|
|
12599
|
+
if (!page.truncated)
|
|
12600
|
+
break;
|
|
12601
|
+
if (!page.cursor || cursors.has(page.cursor))
|
|
12602
|
+
throw new MobileUpdateRegistryError("Mobile update storage returned an invalid cursor");
|
|
12603
|
+
cursors.add(page.cursor);
|
|
12604
|
+
cursor = page.cursor;
|
|
12605
|
+
} while (true);
|
|
12606
|
+
return objects;
|
|
12607
|
+
};
|
|
12608
|
+
const readVerifiedObject = async (key, label) => {
|
|
12609
|
+
const bytes = await options.store.get(key);
|
|
12610
|
+
if (!bytes)
|
|
12611
|
+
return null;
|
|
12612
|
+
const head = await options.store.head(key);
|
|
12613
|
+
if (!head || head.size !== bytes.byteLength || head.metadata?.sha256 !== digest(bytes))
|
|
12614
|
+
throw new MobileUpdateRegistryError(`Stored mobile update ${label} integrity failed`);
|
|
12615
|
+
return decode(bytes);
|
|
12616
|
+
};
|
|
12617
|
+
const parseRolloutPlan = (value, promotionId, releaseId) => {
|
|
12618
|
+
if (!object3(value) || value.format !== 1 || value.promotionId !== promotionId || value.releaseId !== releaseId || typeof value.automatic !== "boolean" || !iso(value.createdAt) || !Array.isArray(value.stages))
|
|
12619
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
12620
|
+
const stages = value.stages.map((stage) => {
|
|
12621
|
+
if (!object3(stage) || typeof stage.rollout !== "number" || typeof stage.maximumFailureRate !== "number" || !Number.isSafeInteger(stage.minimumReports) || !Number.isSafeInteger(stage.observationMs))
|
|
12622
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
12623
|
+
return {
|
|
12624
|
+
maximumFailureRate: stage.maximumFailureRate,
|
|
12625
|
+
minimumReports: stage.minimumReports,
|
|
12626
|
+
observationMs: stage.observationMs,
|
|
12627
|
+
rollout: stage.rollout
|
|
12628
|
+
};
|
|
12629
|
+
});
|
|
12630
|
+
if (stages.length === 0 || stages.some((stage, index) => stage.rollout <= 0 || stage.rollout > 1 || stage.minimumReports < 1 || stage.observationMs < 0 || stage.maximumFailureRate < 0 || stage.maximumFailureRate >= 1 || index > 0 && stage.rollout <= stages[index - 1].rollout))
|
|
12631
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
12632
|
+
return {
|
|
12633
|
+
automatic: value.automatic,
|
|
12634
|
+
createdAt: value.createdAt,
|
|
12635
|
+
format: 1,
|
|
12636
|
+
promotionId,
|
|
12637
|
+
releaseId,
|
|
12638
|
+
stages
|
|
12639
|
+
};
|
|
12640
|
+
};
|
|
12641
|
+
const initializeRollout = async (channel, signal) => {
|
|
12642
|
+
if (!rollout || !channel.releaseId)
|
|
12643
|
+
return;
|
|
12644
|
+
if (!rollout.stages.some((stage) => stage.rollout === channel.rollout))
|
|
12645
|
+
throw new MobileUpdateRegistryError("Mobile update promotion rollout must match a configured rollout stage");
|
|
12646
|
+
const promotionId = healthPromotionId(channel);
|
|
12647
|
+
const plan = {
|
|
12648
|
+
automatic: rollout.automatic ?? false,
|
|
12649
|
+
createdAt: channel.promotedAt,
|
|
12650
|
+
format: 1,
|
|
12651
|
+
promotionId,
|
|
12652
|
+
releaseId: channel.releaseId,
|
|
12653
|
+
stages: rollout.stages.map((stage) => ({ ...stage }))
|
|
12654
|
+
};
|
|
12655
|
+
const bytes = json(plan);
|
|
12656
|
+
await options.store.put(rolloutPlanKey(channel.appId, promotionId, channel.releaseId), bytes, {
|
|
12657
|
+
cacheControl: "no-store",
|
|
12658
|
+
contentType: "application/json",
|
|
12659
|
+
maxBytes: bytes.byteLength,
|
|
12660
|
+
metadata: { releaseid: channel.releaseId, sha256: digest(bytes) },
|
|
12661
|
+
signal
|
|
12662
|
+
});
|
|
12663
|
+
};
|
|
12537
12664
|
const signHealthToken = (payload) => {
|
|
12538
12665
|
if (!health)
|
|
12539
12666
|
return null;
|
|
@@ -12572,12 +12699,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12572
12699
|
if (await options.store.head(tombstoneKey(appId, releaseId)))
|
|
12573
12700
|
throw new MobileUpdateRegistryError("Mobile update release is marked for collection. Increase retention and apply garbage collection to restore it before promotion");
|
|
12574
12701
|
};
|
|
12575
|
-
const writeChannel = async (input, signal) => {
|
|
12702
|
+
const writeChannel = async (input, signal, beforeWrite) => {
|
|
12703
|
+
const promotedAt = clock().toISOString();
|
|
12576
12704
|
const value = {
|
|
12577
12705
|
...input,
|
|
12578
12706
|
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
12579
|
-
promotedAt
|
|
12707
|
+
promotedAt,
|
|
12708
|
+
promotionId: digest(new TextEncoder().encode(`${input.appId}\x00${input.channel}\x00${input.releaseId ?? "embedded"}\x00${promotedAt}\x00${randomUUID()}`))
|
|
12580
12709
|
};
|
|
12710
|
+
await beforeWrite?.(value);
|
|
12581
12711
|
const bytes = json(value);
|
|
12582
12712
|
await options.store.put(channelKey(value.appId, value.channel), bytes, {
|
|
12583
12713
|
cacheControl: "no-cache",
|
|
@@ -12592,10 +12722,92 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12592
12722
|
});
|
|
12593
12723
|
return value;
|
|
12594
12724
|
};
|
|
12725
|
+
const rolloutContext = async (channel) => {
|
|
12726
|
+
if (!options.store.list || !channel.releaseId)
|
|
12727
|
+
return null;
|
|
12728
|
+
const promotionId = healthPromotionId(channel);
|
|
12729
|
+
const storedPlan = await readVerifiedObject(rolloutPlanKey(channel.appId, promotionId, channel.releaseId), "rollout plan");
|
|
12730
|
+
if (storedPlan === null)
|
|
12731
|
+
return null;
|
|
12732
|
+
const plan = parseRolloutPlan(storedPlan, promotionId, channel.releaseId);
|
|
12733
|
+
const initialStage = plan.stages.findIndex((stage) => stage.rollout === channel.rollout);
|
|
12734
|
+
if (initialStage < 0)
|
|
12735
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout does not match its plan");
|
|
12736
|
+
let currentStage = initialStage;
|
|
12737
|
+
let enteredAt = plan.createdAt;
|
|
12738
|
+
const advances = await listPrefix(`${rolloutRoot(channel.appId, promotionId, channel.releaseId)}/advances/`);
|
|
12739
|
+
for (const item of advances) {
|
|
12740
|
+
const value = await readVerifiedObject(item.key, "rollout advancement");
|
|
12741
|
+
if (!object3(value) || value.format !== 1 || value.promotionId !== promotionId || value.releaseId !== channel.releaseId || !Number.isSafeInteger(value.stage) || Number(value.stage) < initialStage || Number(value.stage) >= plan.stages.length || value.rollout !== plan.stages[Number(value.stage)].rollout || !iso(value.createdAt) || typeof value.failureRate !== "number" || !Number.isSafeInteger(value.terminalReports))
|
|
12742
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout advancement is invalid");
|
|
12743
|
+
if (Number(value.stage) >= currentStage) {
|
|
12744
|
+
currentStage = Number(value.stage);
|
|
12745
|
+
enteredAt = value.createdAt;
|
|
12746
|
+
}
|
|
12747
|
+
}
|
|
12748
|
+
const controls = await listPrefix(`${rolloutRoot(channel.appId, promotionId, channel.releaseId)}/controls/`);
|
|
12749
|
+
const parsedControls = [];
|
|
12750
|
+
for (const item of controls) {
|
|
12751
|
+
const value = await readVerifiedObject(item.key, "rollout control");
|
|
12752
|
+
if (!object3(value) || value.format !== 1 || value.promotionId !== promotionId || value.releaseId !== channel.releaseId || typeof value.id !== "string" || value.action !== "pause" && value.action !== "resume" && value.action !== "cancel" || !iso(value.createdAt) || value.resumedPauseIds !== undefined && (!Array.isArray(value.resumedPauseIds) || value.resumedPauseIds.some((id) => typeof id !== "string")))
|
|
12753
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout control is invalid");
|
|
12754
|
+
parsedControls.push(value);
|
|
12755
|
+
}
|
|
12756
|
+
const cancelled = parsedControls.some(({ action }) => action === "cancel");
|
|
12757
|
+
const resumedPauseIds = new Set(parsedControls.flatMap((control) => control.resumedPauseIds ?? []));
|
|
12758
|
+
const activePauseIds = parsedControls.filter(({ action, id }) => action === "pause" && !resumedPauseIds.has(id)).map(({ id }) => id);
|
|
12759
|
+
const operatorPaused = activePauseIds.length > 0;
|
|
12760
|
+
const fleetPaused = Boolean(await options.store.head(pauseKey(channel.appId, promotionId, channel.releaseId)));
|
|
12761
|
+
return {
|
|
12762
|
+
cancelled,
|
|
12763
|
+
activePauseIds,
|
|
12764
|
+
channel,
|
|
12765
|
+
currentStage,
|
|
12766
|
+
enteredAt,
|
|
12767
|
+
fleetPaused,
|
|
12768
|
+
operatorPaused,
|
|
12769
|
+
plan,
|
|
12770
|
+
promotionId,
|
|
12771
|
+
rollout: plan.stages[currentStage].rollout
|
|
12772
|
+
};
|
|
12773
|
+
};
|
|
12774
|
+
const writeRolloutControl = async (channel, action, signal) => {
|
|
12775
|
+
const context = await rolloutContext(channel);
|
|
12776
|
+
if (!context)
|
|
12777
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
12778
|
+
if (context.cancelled)
|
|
12779
|
+
throw new MobileUpdateRegistryError("Mobile update rollout was already cancelled");
|
|
12780
|
+
if (action === "resume" && context.fleetPaused)
|
|
12781
|
+
throw new MobileUpdateRegistryError("A fleet-health pause requires an explicit re-promotion");
|
|
12782
|
+
const releaseId = channel.releaseId;
|
|
12783
|
+
if (!releaseId)
|
|
12784
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
12785
|
+
const createdAt = clock().toISOString();
|
|
12786
|
+
const id = randomUUID();
|
|
12787
|
+
const event = {
|
|
12788
|
+
action,
|
|
12789
|
+
createdAt,
|
|
12790
|
+
format: 1,
|
|
12791
|
+
id,
|
|
12792
|
+
promotionId: context.promotionId,
|
|
12793
|
+
releaseId,
|
|
12794
|
+
...action === "resume" ? { resumedPauseIds: context.activePauseIds } : {}
|
|
12795
|
+
};
|
|
12796
|
+
const bytes = json(event);
|
|
12797
|
+
await options.store.put(`${rolloutRoot(channel.appId, context.promotionId, releaseId)}/controls/${createdAt}-${id}-${action}.json`, bytes, {
|
|
12798
|
+
cacheControl: "no-store",
|
|
12799
|
+
contentType: "application/json",
|
|
12800
|
+
maxBytes: bytes.byteLength,
|
|
12801
|
+
metadata: { action, sha256: digest(bytes) },
|
|
12802
|
+
signal
|
|
12803
|
+
});
|
|
12804
|
+
};
|
|
12595
12805
|
const promoteUpdate = async (input) => {
|
|
12596
12806
|
input.signal?.throwIfAborted();
|
|
12597
12807
|
if (input.rollout <= 0 || input.rollout > 1)
|
|
12598
12808
|
throw new MobileUpdateRegistryError("Mobile update rollout is invalid");
|
|
12809
|
+
if (rollout && !rollout.stages.some((stage) => stage.rollout === input.rollout))
|
|
12810
|
+
throw new MobileUpdateRegistryError("Mobile update promotion rollout must match a configured rollout stage");
|
|
12599
12811
|
await assertNotMarked(input.appId, input.releaseId);
|
|
12600
12812
|
const release = await readManifest(input.appId, input.releaseId);
|
|
12601
12813
|
if (!release || release.manifest.channel !== input.channel)
|
|
@@ -12607,7 +12819,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12607
12819
|
...existing?.releaseId && existing.releaseId !== input.releaseId ? { fallbackReleaseId: existing.releaseId } : existing?.fallbackReleaseId ? { fallbackReleaseId: existing.fallbackReleaseId } : {},
|
|
12608
12820
|
releaseId: input.releaseId,
|
|
12609
12821
|
rollout: input.rollout
|
|
12610
|
-
}, input.signal);
|
|
12822
|
+
}, input.signal, (channel) => initializeRollout(channel, input.signal));
|
|
12611
12823
|
return {
|
|
12612
12824
|
appId: input.appId,
|
|
12613
12825
|
channel: input.channel,
|
|
@@ -12620,14 +12832,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12620
12832
|
const channel = await readChannel(input.appId, input.channel);
|
|
12621
12833
|
if (!channel?.releaseId)
|
|
12622
12834
|
return { status: "empty" };
|
|
12835
|
+
const rolloutState = await rolloutContext(channel);
|
|
12623
12836
|
let selected = rolloutMember({
|
|
12624
12837
|
appId: input.appId,
|
|
12625
12838
|
channel: input.channel,
|
|
12626
12839
|
installationId: input.installationId,
|
|
12627
12840
|
releaseId: channel.releaseId,
|
|
12628
|
-
rollout: channel.rollout
|
|
12841
|
+
rollout: rolloutState?.rollout ?? channel.rollout
|
|
12629
12842
|
}) ? channel.releaseId : channel.fallbackReleaseId;
|
|
12630
|
-
if (
|
|
12843
|
+
if (selected === channel.releaseId && (rolloutState?.cancelled || rolloutState?.fleetPaused || rolloutState?.operatorPaused || health && await options.store.head(pauseKey(input.appId, healthPromotionId(channel), channel.releaseId))))
|
|
12631
12844
|
selected = channel.fallbackReleaseId;
|
|
12632
12845
|
if (!selected)
|
|
12633
12846
|
return { status: "empty" };
|
|
@@ -12671,23 +12884,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12671
12884
|
if (!channel || !releaseId || channel.releaseId !== releaseId)
|
|
12672
12885
|
return null;
|
|
12673
12886
|
const promotionId = healthPromotionId(channel);
|
|
12674
|
-
const
|
|
12675
|
-
const objects = [];
|
|
12676
|
-
const cursors = new Set;
|
|
12677
|
-
let cursor;
|
|
12678
|
-
do {
|
|
12679
|
-
const page = await options.store.list({
|
|
12680
|
-
...cursor ? { cursor } : {},
|
|
12681
|
-
prefix: prefix2
|
|
12682
|
-
});
|
|
12683
|
-
objects.push(...page.objects);
|
|
12684
|
-
if (!page.truncated)
|
|
12685
|
-
break;
|
|
12686
|
-
if (!page.cursor || cursors.has(page.cursor))
|
|
12687
|
-
throw new MobileUpdateRegistryError("Mobile update health storage returned an invalid cursor");
|
|
12688
|
-
cursors.add(page.cursor);
|
|
12689
|
-
cursor = page.cursor;
|
|
12690
|
-
} while (true);
|
|
12887
|
+
const objects = await listPrefix(`${healthRoot(input.appId, promotionId, releaseId)}/events/`);
|
|
12691
12888
|
const installations = new Set;
|
|
12692
12889
|
const byKind = new Map([...HEALTH_KINDS].map((kind) => [
|
|
12693
12890
|
kind,
|
|
@@ -12726,6 +12923,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12726
12923
|
]);
|
|
12727
12924
|
const terminals = new Set([...byKind.get("activated"), ...failures]);
|
|
12728
12925
|
const failureRate = terminals.size === 0 ? 0 : failures.size / terminals.size;
|
|
12926
|
+
const rolloutState = await rolloutContext(channel);
|
|
12729
12927
|
return {
|
|
12730
12928
|
activated: byKind.get("activated").size,
|
|
12731
12929
|
appId: input.appId,
|
|
@@ -12734,17 +12932,107 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12734
12932
|
downloadFailed: byKind.get("download-failed").size,
|
|
12735
12933
|
failureRate,
|
|
12736
12934
|
failures: failures.size,
|
|
12737
|
-
paused: Boolean(await options.store.head(pauseKey(input.appId, promotionId, releaseId))),
|
|
12935
|
+
paused: Boolean(rolloutState?.cancelled || rolloutState?.fleetPaused || rolloutState?.operatorPaused || await options.store.head(pauseKey(input.appId, promotionId, releaseId))),
|
|
12738
12936
|
promotionId,
|
|
12739
12937
|
quarantined: byKind.get("quarantined").size,
|
|
12740
12938
|
releaseId,
|
|
12741
12939
|
reportedInstallations: installations.size,
|
|
12742
12940
|
rolledBack: byKind.get("rolled-back").size,
|
|
12743
|
-
rollout: channel.rollout,
|
|
12941
|
+
rollout: rolloutState?.rollout ?? channel.rollout,
|
|
12744
12942
|
terminalReports: terminals.size,
|
|
12745
12943
|
transfer
|
|
12746
12944
|
};
|
|
12747
12945
|
};
|
|
12946
|
+
const inspectUpdateRollout = async (input) => {
|
|
12947
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
12948
|
+
if (!channel?.releaseId)
|
|
12949
|
+
return null;
|
|
12950
|
+
const context = await rolloutContext(channel);
|
|
12951
|
+
if (!context)
|
|
12952
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
12953
|
+
const healthReport = await inspectUpdateHealth(input);
|
|
12954
|
+
if (!healthReport)
|
|
12955
|
+
return null;
|
|
12956
|
+
const paused = context.fleetPaused || context.operatorPaused;
|
|
12957
|
+
const complete = context.currentStage === context.plan.stages.length - 1;
|
|
12958
|
+
return {
|
|
12959
|
+
...healthReport,
|
|
12960
|
+
automatic: context.plan.automatic,
|
|
12961
|
+
currentStage: context.currentStage,
|
|
12962
|
+
enteredAt: context.enteredAt,
|
|
12963
|
+
...!complete ? { nextStage: context.plan.stages[context.currentStage + 1] } : {},
|
|
12964
|
+
...context.fleetPaused ? { pausedBy: "fleet-health" } : context.operatorPaused ? { pausedBy: "operator" } : {},
|
|
12965
|
+
status: context.cancelled ? "cancelled" : paused ? "paused" : complete ? "complete" : "active"
|
|
12966
|
+
};
|
|
12967
|
+
};
|
|
12968
|
+
const advanceRollout = async (input, strict) => {
|
|
12969
|
+
input.signal?.throwIfAborted();
|
|
12970
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
12971
|
+
if (!channel?.releaseId)
|
|
12972
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
12973
|
+
const context = await rolloutContext(channel);
|
|
12974
|
+
if (!context)
|
|
12975
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
12976
|
+
const report = await inspectUpdateRollout(input);
|
|
12977
|
+
if (!report)
|
|
12978
|
+
throw new MobileUpdateRegistryError("Mobile update rollout report is unavailable");
|
|
12979
|
+
const nextStage = context.plan.stages[context.currentStage + 1];
|
|
12980
|
+
if (!nextStage)
|
|
12981
|
+
return report;
|
|
12982
|
+
if (input.rollout !== undefined && input.rollout !== nextStage.rollout)
|
|
12983
|
+
throw new MobileUpdateRegistryError("Mobile update rollout can advance only to the next configured stage");
|
|
12984
|
+
if (report.status !== "active") {
|
|
12985
|
+
if (strict)
|
|
12986
|
+
throw new MobileUpdateRegistryError(`Mobile update rollout cannot advance while ${report.status}`);
|
|
12987
|
+
return report;
|
|
12988
|
+
}
|
|
12989
|
+
const gate = context.plan.stages[context.currentStage];
|
|
12990
|
+
const observedMs = clock().getTime() - Date.parse(context.enteredAt);
|
|
12991
|
+
const blocked = report.terminalReports < gate.minimumReports || report.failureRate > gate.maximumFailureRate || observedMs < gate.observationMs;
|
|
12992
|
+
if (blocked) {
|
|
12993
|
+
if (strict)
|
|
12994
|
+
throw new MobileUpdateRegistryError(`Mobile update rollout needs ${gate.minimumReports} terminal reports, at most ${(gate.maximumFailureRate * 100).toFixed(1)}% failures, and ${gate.observationMs}ms observation at the current stage`);
|
|
12995
|
+
return report;
|
|
12996
|
+
}
|
|
12997
|
+
const stage = context.currentStage + 1;
|
|
12998
|
+
const event = {
|
|
12999
|
+
createdAt: clock().toISOString(),
|
|
13000
|
+
failureRate: report.failureRate,
|
|
13001
|
+
format: 1,
|
|
13002
|
+
promotionId: context.promotionId,
|
|
13003
|
+
releaseId: channel.releaseId,
|
|
13004
|
+
rollout: nextStage.rollout,
|
|
13005
|
+
stage,
|
|
13006
|
+
terminalReports: report.terminalReports
|
|
13007
|
+
};
|
|
13008
|
+
const bytes = json(event);
|
|
13009
|
+
await options.store.put(`${rolloutRoot(input.appId, context.promotionId, channel.releaseId)}/advances/${String(stage).padStart(4, "0")}.json`, bytes, {
|
|
13010
|
+
cacheControl: "no-store",
|
|
13011
|
+
contentType: "application/json",
|
|
13012
|
+
maxBytes: bytes.byteLength,
|
|
13013
|
+
metadata: { releaseid: channel.releaseId, sha256: digest(bytes) },
|
|
13014
|
+
signal: input.signal
|
|
13015
|
+
});
|
|
13016
|
+
return await inspectUpdateRollout(input);
|
|
13017
|
+
};
|
|
13018
|
+
const advanceUpdateRollout = (input) => advanceRollout(input, true);
|
|
13019
|
+
const reconcileUpdateRollout = async (input) => {
|
|
13020
|
+
const report = await inspectUpdateRollout(input);
|
|
13021
|
+
if (!report || !report.automatic)
|
|
13022
|
+
return report;
|
|
13023
|
+
return advanceRollout(input, false);
|
|
13024
|
+
};
|
|
13025
|
+
const rolloutControl = (action) => async (input) => {
|
|
13026
|
+
input.signal?.throwIfAborted();
|
|
13027
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
13028
|
+
if (!channel?.releaseId)
|
|
13029
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
13030
|
+
await writeRolloutControl(channel, action, input.signal);
|
|
13031
|
+
return await inspectUpdateRollout(input);
|
|
13032
|
+
};
|
|
13033
|
+
const pauseUpdateRollout = rolloutControl("pause");
|
|
13034
|
+
const resumeUpdateRollout = rolloutControl("resume");
|
|
13035
|
+
const cancelUpdateRollout = rolloutControl("cancel");
|
|
12748
13036
|
const recordUpdateHealth = async (input) => {
|
|
12749
13037
|
const payload = verifyHealthToken(input.token);
|
|
12750
13038
|
if (payload.appId !== input.appId || payload.channel !== input.channel || payload.installationId !== input.installationId || payload.releaseId !== input.releaseId || payload.runtimeFingerprint !== input.runtimeFingerprint || !HEALTH_KINDS.has(input.kind) || input.reason !== undefined && input.reason !== "boot-interrupted" && input.reason !== "boot-timeout")
|
|
@@ -12799,6 +13087,11 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12799
13087
|
});
|
|
12800
13088
|
report = { ...report, paused: true };
|
|
12801
13089
|
}
|
|
13090
|
+
if (rollout && (input.kind === "activated" || FAILURE_HEALTH_KINDS.has(input.kind)))
|
|
13091
|
+
return await reconcileUpdateRollout({
|
|
13092
|
+
appId: input.appId,
|
|
13093
|
+
channel: input.channel
|
|
13094
|
+
}) ?? report;
|
|
12802
13095
|
return report;
|
|
12803
13096
|
};
|
|
12804
13097
|
const retentionValues = (input) => {
|
|
@@ -13044,6 +13337,14 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
13044
13337
|
};
|
|
13045
13338
|
return {
|
|
13046
13339
|
...health ? { inspectUpdateHealth, issueUpdateHealthToken, recordUpdateHealth } : {},
|
|
13340
|
+
...rollout ? {
|
|
13341
|
+
advanceUpdateRollout,
|
|
13342
|
+
cancelUpdateRollout,
|
|
13343
|
+
inspectUpdateRollout,
|
|
13344
|
+
pauseUpdateRollout,
|
|
13345
|
+
reconcileUpdateRollout,
|
|
13346
|
+
resumeUpdateRollout
|
|
13347
|
+
} : {},
|
|
13047
13348
|
inspectUpdateStorage: async (input) => (await inventory(input)).report,
|
|
13048
13349
|
pruneUpdates,
|
|
13049
13350
|
publishUpdate: async (input) => {
|
|
@@ -13657,6 +13958,11 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13657
13958
|
return;
|
|
13658
13959
|
if (typeof module.registry.inspectUpdateHealth !== "function" || typeof module.registry.issueUpdateHealthToken !== "function" || typeof module.registry.recordUpdateHealth !== "function")
|
|
13659
13960
|
throw new TypeError("Mobile update fleet health is enabled but the registry is not provisioned for it. Run `absolute mobile update provision --force`.");
|
|
13961
|
+
}, verifyRolloutModule = (config, module) => {
|
|
13962
|
+
if (!config.updateServer?.rollout)
|
|
13963
|
+
return;
|
|
13964
|
+
if (typeof module.registry.advanceUpdateRollout !== "function" || typeof module.registry.cancelUpdateRollout !== "function" || typeof module.registry.inspectUpdateRollout !== "function" || typeof module.registry.pauseUpdateRollout !== "function" || typeof module.registry.reconcileUpdateRollout !== "function" || typeof module.registry.resumeUpdateRollout !== "function")
|
|
13965
|
+
throw new TypeError("Mobile update rollout orchestration is enabled but the registry is not provisioned for it. Run `absolute mobile update provision --force`.");
|
|
13660
13966
|
}, expoSigningOptions = (config) => {
|
|
13661
13967
|
if (!config.updates?.expoCodeSigning)
|
|
13662
13968
|
return;
|
|
@@ -13691,6 +13997,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13691
13997
|
if (options.production) {
|
|
13692
13998
|
await verifyDurableModule(module);
|
|
13693
13999
|
verifyHealthModule(config, module);
|
|
14000
|
+
verifyRolloutModule(config, module);
|
|
13694
14001
|
}
|
|
13695
14002
|
const manifest = new URL(updates.manifestUrl);
|
|
13696
14003
|
if (!manifest.pathname.endsWith("/update.json"))
|
|
@@ -13711,6 +14018,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13711
14018
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
|
|
13712
14019
|
await verifyDurableModule(module);
|
|
13713
14020
|
verifyHealthModule(config, module);
|
|
14021
|
+
verifyRolloutModule(config, module);
|
|
13714
14022
|
if (config.engine === "expo")
|
|
13715
14023
|
expoSigningOptions(config);
|
|
13716
14024
|
return module.metadata;
|
|
@@ -13782,17 +14090,25 @@ export default createMobileUpdateRegistry({
|
|
|
13782
14090
|
`;
|
|
13783
14091
|
}, renderAbsoluteMobileUpdateRegistry = (options) => {
|
|
13784
14092
|
const source = renderAbsoluteMobileUpdateRegistryBase(options);
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
14093
|
+
let generated = "";
|
|
14094
|
+
if (options.health) {
|
|
14095
|
+
const secret = options.storage === "local" ? `process.env.${options.health.secretEnv} ?? 'absolutejs-local-health-secret-not-for-production'` : `required('${options.health.secretEnv}')`;
|
|
14096
|
+
generated += ` health: {
|
|
13789
14097
|
autoPause: { failureRate: ${options.health.failureRate}, minimumReports: ${options.health.minimumReports} },
|
|
13790
14098
|
secret: ${secret}
|
|
13791
14099
|
},
|
|
13792
14100
|
`;
|
|
14101
|
+
}
|
|
14102
|
+
if (options.rollout)
|
|
14103
|
+
generated += ` rollout: ${JSON.stringify(options.rollout, null, "\t").replaceAll(`
|
|
14104
|
+
`, `
|
|
14105
|
+
`)},
|
|
14106
|
+
`;
|
|
14107
|
+
if (!generated)
|
|
14108
|
+
return source;
|
|
13793
14109
|
return source.replace(`export default createMobileUpdateRegistry({
|
|
13794
14110
|
`, `export default createMobileUpdateRegistry({
|
|
13795
|
-
${
|
|
14111
|
+
${generated}`);
|
|
13796
14112
|
}, writeAbsoluteMobileUpdateRegistry = async (options) => {
|
|
13797
14113
|
const path2 = projectPath(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
|
|
13798
14114
|
if (!options.force) {
|
|
@@ -13805,6 +14121,7 @@ ${health}`);
|
|
|
13805
14121
|
await mkdir6(dirname12(path2), { recursive: true });
|
|
13806
14122
|
await Bun.write(path2, renderAbsoluteMobileUpdateRegistry({
|
|
13807
14123
|
...options.health ? { health: options.health } : {},
|
|
14124
|
+
...options.rollout ? { rollout: options.rollout } : {},
|
|
13808
14125
|
publicKeys: options.publicKeys,
|
|
13809
14126
|
storage: options.storage
|
|
13810
14127
|
}));
|
|
@@ -45217,5 +45534,5 @@ export {
|
|
|
45217
45534
|
wrapPageHandlerWithStreamingSlots
|
|
45218
45535
|
};
|
|
45219
45536
|
|
|
45220
|
-
//# debugId=
|
|
45537
|
+
//# debugId=95E12D596BC2404464756E2164756E21
|
|
45221
45538
|
//# sourceMappingURL=index.js.map
|