@absolutejs/absolute 0.20.0-beta.86 → 0.20.0-beta.87
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-c94a5y9k.js} +141 -23
- 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 +396 -41
- package/dist/mobile/index.js.map +6 -6
- package/dist/mobile/remoteMacAgentEntry.js +167 -167
- package/dist/src/mobile/config.d.ts +9 -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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
-
// .angular-partial-tmp-
|
|
4
|
+
// .angular-partial-tmp-Fz4wlA/src/core/streamingSlotRegistrar.ts
|
|
5
5
|
var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
|
|
6
6
|
var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
|
|
7
7
|
var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
-
// .angular-partial-tmp-
|
|
4
|
+
// .angular-partial-tmp-Fz4wlA/src/core/streamingSlotRegistrar.ts
|
|
5
5
|
var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
|
|
6
6
|
var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
|
|
7
7
|
var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
|
|
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
|
|
|
48
48
|
getWarningController()?.maybeWarn(primitiveName);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
// .angular-partial-tmp-
|
|
51
|
+
// .angular-partial-tmp-Fz4wlA/src/core/streamingSlotRegistry.ts
|
|
52
52
|
var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
|
|
53
53
|
var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
|
|
54
54
|
var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
|
package/dist/build.js
CHANGED
|
@@ -27625,7 +27625,7 @@ __export(exports_config, {
|
|
|
27625
27625
|
import { readFileSync as readFileSync35 } from "fs";
|
|
27626
27626
|
import { resolve as resolve44 } from "path";
|
|
27627
27627
|
import { createHash as createHash5, createPublicKey, X509Certificate } from "crypto";
|
|
27628
|
-
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) => {
|
|
27628
|
+
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) => {
|
|
27629
27629
|
const root = resolve44(projectRoot);
|
|
27630
27630
|
const path = resolve44(root, value);
|
|
27631
27631
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
@@ -27833,6 +27833,42 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
27833
27833
|
throw new TypeError("mobile.updates.server.health.secretEnv must be a valid environment variable name.");
|
|
27834
27834
|
health = { failureRate, minimumReports, secretEnv };
|
|
27835
27835
|
}
|
|
27836
|
+
const configuredRollout = config.updates.server?.rollout;
|
|
27837
|
+
let rollout;
|
|
27838
|
+
if (configuredRollout !== undefined && configuredRollout !== false) {
|
|
27839
|
+
if (!health)
|
|
27840
|
+
throw new TypeError("mobile.updates.server.rollout requires fleet health to be enabled.");
|
|
27841
|
+
if (configuredRollout.automatic !== undefined && typeof configuredRollout.automatic !== "boolean")
|
|
27842
|
+
throw new TypeError("mobile.updates.server.rollout.automatic must be boolean.");
|
|
27843
|
+
const configuredStages = configuredRollout.stages ?? DEFAULT_UPDATE_ROLLOUT_STAGES;
|
|
27844
|
+
const stages = configuredStages.map((stage, index) => {
|
|
27845
|
+
const previousStage = configuredStages[index - 1];
|
|
27846
|
+
const maximumFailureRate = stage.maximumFailureRate ?? DEFAULT_UPDATE_ROLLOUT_FAILURE_RATE;
|
|
27847
|
+
const minimumReports = stage.minimumReports ?? DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS;
|
|
27848
|
+
const observationMinutes = stage.observationMinutes ?? DEFAULT_UPDATE_ROLLOUT_OBSERVATION_MINUTES;
|
|
27849
|
+
const observationMs = observationMinutes * MINUTE_MS;
|
|
27850
|
+
if (!Number.isFinite(stage.rollout) || stage.rollout <= 0 || stage.rollout > 1 || previousStage !== undefined && stage.rollout <= previousStage.rollout)
|
|
27851
|
+
throw new TypeError("mobile.updates.server.rollout stages must be strictly increasing fractions greater than 0 and at most 1.");
|
|
27852
|
+
if (!Number.isFinite(maximumFailureRate) || maximumFailureRate < 0 || maximumFailureRate >= health.failureRate)
|
|
27853
|
+
throw new TypeError("mobile.updates.server.rollout maximumFailureRate must be non-negative and lower than the fleet-health pause rate.");
|
|
27854
|
+
if (!Number.isSafeInteger(minimumReports) || minimumReports < health.minimumReports)
|
|
27855
|
+
throw new TypeError("mobile.updates.server.rollout minimumReports must be an integer at least as large as the fleet-health minimumReports.");
|
|
27856
|
+
if (!Number.isFinite(observationMinutes) || observationMinutes < 0 || !Number.isSafeInteger(observationMs))
|
|
27857
|
+
throw new TypeError("mobile.updates.server.rollout observationMinutes must produce a non-negative whole number of milliseconds.");
|
|
27858
|
+
return {
|
|
27859
|
+
maximumFailureRate,
|
|
27860
|
+
minimumReports,
|
|
27861
|
+
observationMs,
|
|
27862
|
+
rollout: stage.rollout
|
|
27863
|
+
};
|
|
27864
|
+
});
|
|
27865
|
+
if (stages.length === 0 || stages.at(-1)?.rollout !== 1)
|
|
27866
|
+
throw new TypeError("mobile.updates.server.rollout stages must end at rollout 1.");
|
|
27867
|
+
rollout = {
|
|
27868
|
+
automatic: configuredRollout.automatic ?? false,
|
|
27869
|
+
stages
|
|
27870
|
+
};
|
|
27871
|
+
}
|
|
27836
27872
|
if (autoMount) {
|
|
27837
27873
|
const manifest = new URL(updates.manifestUrl);
|
|
27838
27874
|
if (manifest.origin !== productionOrigin)
|
|
@@ -27869,6 +27905,7 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
27869
27905
|
autoMount,
|
|
27870
27906
|
expoCodeSigningKeys,
|
|
27871
27907
|
...health ? { health } : {},
|
|
27908
|
+
...rollout ? { rollout } : {},
|
|
27872
27909
|
registryModule
|
|
27873
27910
|
};
|
|
27874
27911
|
}, validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
|
|
@@ -27962,6 +27999,12 @@ var init_config = __esm(() => {
|
|
|
27962
27999
|
UPDATE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
27963
28000
|
UPDATE_PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/u;
|
|
27964
28001
|
ENVIRONMENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
28002
|
+
MINUTE_MS = 60 * 1000;
|
|
28003
|
+
DEFAULT_UPDATE_ROLLOUT_STAGES = [
|
|
28004
|
+
{ minimumReports: 20, observationMinutes: 60, rollout: 0.05 },
|
|
28005
|
+
{ minimumReports: 100, observationMinutes: 360, rollout: 0.25 },
|
|
28006
|
+
{ minimumReports: 100, observationMinutes: 0, rollout: 1 }
|
|
28007
|
+
];
|
|
27965
28008
|
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])?))*$/;
|
|
27966
28009
|
EXPO_RESERVED_ROUTE_PREFIXES = new Set([
|
|
27967
28010
|
"_expo",
|
|
@@ -28494,6 +28537,7 @@ import {
|
|
|
28494
28537
|
createHmac,
|
|
28495
28538
|
createPrivateKey,
|
|
28496
28539
|
createPublicKey as createPublicKey2,
|
|
28540
|
+
randomUUID,
|
|
28497
28541
|
sign,
|
|
28498
28542
|
timingSafeEqual,
|
|
28499
28543
|
verify,
|
|
@@ -28630,7 +28674,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28630
28674
|
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
28631
28675
|
const appId = text2(value.appId, "channel appId");
|
|
28632
28676
|
const channel = text2(value.channel, "channel");
|
|
28633
|
-
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))
|
|
28677
|
+
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))
|
|
28634
28678
|
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
28635
28679
|
return {
|
|
28636
28680
|
...value.activationId ? { activationId: value.activationId } : {},
|
|
@@ -28639,6 +28683,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28639
28683
|
channel,
|
|
28640
28684
|
...value.fallbackReleaseId ? { fallbackReleaseId: value.fallbackReleaseId } : {},
|
|
28641
28685
|
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
28686
|
+
...value.promotionId ? { promotionId: value.promotionId } : {},
|
|
28642
28687
|
promotedAt: value.promotedAt,
|
|
28643
28688
|
...value.releaseId ? { releaseId: value.releaseId } : {},
|
|
28644
28689
|
rollout: value.rollout
|
|
@@ -28661,7 +28706,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28661
28706
|
return true;
|
|
28662
28707
|
const value = createHash6("sha256").update(`${input.appId}\x00${input.channel}\x00${input.releaseId}\x00${input.installationId}`).digest().readUInt32BE(0);
|
|
28663
28708
|
return value / 4294967296 < input.rollout;
|
|
28664
|
-
}, 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) => {
|
|
28709
|
+
}, 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) => {
|
|
28665
28710
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
28666
28711
|
throw new MobileUpdateRegistryError(`Mobile update health ${field} is invalid`);
|
|
28667
28712
|
return value;
|
|
@@ -28682,10 +28727,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28682
28727
|
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
28683
28728
|
const clock = options.clock ?? (() => new Date);
|
|
28684
28729
|
const health = options.health;
|
|
28730
|
+
const rollout = options.rollout;
|
|
28685
28731
|
if (health && health.secret.length < 32)
|
|
28686
28732
|
throw new MobileUpdateRegistryError("Mobile update health secret must contain at least 32 characters");
|
|
28687
28733
|
if (health && !options.store.list)
|
|
28688
28734
|
throw new MobileUpdateRegistryError("Mobile update health requires storage lifecycle listing");
|
|
28735
|
+
if (rollout && (!health || !options.store.list))
|
|
28736
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration requires fleet health and storage lifecycle listing");
|
|
28737
|
+
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)))
|
|
28738
|
+
throw new MobileUpdateRegistryError("Mobile update rollout stages are invalid");
|
|
28689
28739
|
const minimumReports = health?.autoPause?.minimumReports ?? 20;
|
|
28690
28740
|
const failureThreshold = health?.autoPause?.failureRate ?? 0.2;
|
|
28691
28741
|
if (health && (!Number.isSafeInteger(minimumReports) || minimumReports < 1 || failureThreshold <= 0 || failureThreshold > 1))
|
|
@@ -28698,6 +28748,8 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28698
28748
|
const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
|
|
28699
28749
|
const healthRoot = (appId, promotionId, releaseId) => `${root(appId)}/health/${promotionId}/${releaseId}`;
|
|
28700
28750
|
const pauseKey = (appId, promotionId, releaseId) => `${healthRoot(appId, promotionId, releaseId)}/paused.json`;
|
|
28751
|
+
const rolloutRoot = (appId, promotionId, releaseId) => `${healthRoot(appId, promotionId, releaseId)}/rollout`;
|
|
28752
|
+
const rolloutPlanKey = (appId, promotionId, releaseId) => `${rolloutRoot(appId, promotionId, releaseId)}/plan.json`;
|
|
28701
28753
|
const channelKey = (appId, channel) => {
|
|
28702
28754
|
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
28703
28755
|
throw new MobileUpdateRegistryError("Mobile update channel identity is invalid");
|
|
@@ -28726,6 +28778,81 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28726
28778
|
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
28727
28779
|
return value;
|
|
28728
28780
|
};
|
|
28781
|
+
const listPrefix = async (value) => {
|
|
28782
|
+
const objects = [];
|
|
28783
|
+
const cursors = new Set;
|
|
28784
|
+
let cursor;
|
|
28785
|
+
do {
|
|
28786
|
+
const page = await options.store.list({
|
|
28787
|
+
...cursor ? { cursor } : {},
|
|
28788
|
+
prefix: value
|
|
28789
|
+
});
|
|
28790
|
+
objects.push(...page.objects);
|
|
28791
|
+
if (!page.truncated)
|
|
28792
|
+
break;
|
|
28793
|
+
if (!page.cursor || cursors.has(page.cursor))
|
|
28794
|
+
throw new MobileUpdateRegistryError("Mobile update storage returned an invalid cursor");
|
|
28795
|
+
cursors.add(page.cursor);
|
|
28796
|
+
cursor = page.cursor;
|
|
28797
|
+
} while (true);
|
|
28798
|
+
return objects;
|
|
28799
|
+
};
|
|
28800
|
+
const readVerifiedObject = async (key, label) => {
|
|
28801
|
+
const bytes = await options.store.get(key);
|
|
28802
|
+
if (!bytes)
|
|
28803
|
+
return null;
|
|
28804
|
+
const head = await options.store.head(key);
|
|
28805
|
+
if (!head || head.size !== bytes.byteLength || head.metadata?.sha256 !== digest(bytes))
|
|
28806
|
+
throw new MobileUpdateRegistryError(`Stored mobile update ${label} integrity failed`);
|
|
28807
|
+
return decode(bytes);
|
|
28808
|
+
};
|
|
28809
|
+
const parseRolloutPlan = (value, promotionId, releaseId) => {
|
|
28810
|
+
if (!object3(value) || value.format !== 1 || value.promotionId !== promotionId || value.releaseId !== releaseId || typeof value.automatic !== "boolean" || !iso(value.createdAt) || !Array.isArray(value.stages))
|
|
28811
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
28812
|
+
const stages = value.stages.map((stage) => {
|
|
28813
|
+
if (!object3(stage) || typeof stage.rollout !== "number" || typeof stage.maximumFailureRate !== "number" || !Number.isSafeInteger(stage.minimumReports) || !Number.isSafeInteger(stage.observationMs))
|
|
28814
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
28815
|
+
return {
|
|
28816
|
+
maximumFailureRate: stage.maximumFailureRate,
|
|
28817
|
+
minimumReports: stage.minimumReports,
|
|
28818
|
+
observationMs: stage.observationMs,
|
|
28819
|
+
rollout: stage.rollout
|
|
28820
|
+
};
|
|
28821
|
+
});
|
|
28822
|
+
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))
|
|
28823
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout plan is invalid");
|
|
28824
|
+
return {
|
|
28825
|
+
automatic: value.automatic,
|
|
28826
|
+
createdAt: value.createdAt,
|
|
28827
|
+
format: 1,
|
|
28828
|
+
promotionId,
|
|
28829
|
+
releaseId,
|
|
28830
|
+
stages
|
|
28831
|
+
};
|
|
28832
|
+
};
|
|
28833
|
+
const initializeRollout = async (channel, signal) => {
|
|
28834
|
+
if (!rollout || !channel.releaseId)
|
|
28835
|
+
return;
|
|
28836
|
+
if (!rollout.stages.some((stage) => stage.rollout === channel.rollout))
|
|
28837
|
+
throw new MobileUpdateRegistryError("Mobile update promotion rollout must match a configured rollout stage");
|
|
28838
|
+
const promotionId = healthPromotionId(channel);
|
|
28839
|
+
const plan = {
|
|
28840
|
+
automatic: rollout.automatic ?? false,
|
|
28841
|
+
createdAt: channel.promotedAt,
|
|
28842
|
+
format: 1,
|
|
28843
|
+
promotionId,
|
|
28844
|
+
releaseId: channel.releaseId,
|
|
28845
|
+
stages: rollout.stages.map((stage) => ({ ...stage }))
|
|
28846
|
+
};
|
|
28847
|
+
const bytes = json(plan);
|
|
28848
|
+
await options.store.put(rolloutPlanKey(channel.appId, promotionId, channel.releaseId), bytes, {
|
|
28849
|
+
cacheControl: "no-store",
|
|
28850
|
+
contentType: "application/json",
|
|
28851
|
+
maxBytes: bytes.byteLength,
|
|
28852
|
+
metadata: { releaseid: channel.releaseId, sha256: digest(bytes) },
|
|
28853
|
+
signal
|
|
28854
|
+
});
|
|
28855
|
+
};
|
|
28729
28856
|
const signHealthToken = (payload) => {
|
|
28730
28857
|
if (!health)
|
|
28731
28858
|
return null;
|
|
@@ -28764,12 +28891,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28764
28891
|
if (await options.store.head(tombstoneKey(appId, releaseId)))
|
|
28765
28892
|
throw new MobileUpdateRegistryError("Mobile update release is marked for collection. Increase retention and apply garbage collection to restore it before promotion");
|
|
28766
28893
|
};
|
|
28767
|
-
const writeChannel = async (input, signal) => {
|
|
28894
|
+
const writeChannel = async (input, signal, beforeWrite) => {
|
|
28895
|
+
const promotedAt = clock().toISOString();
|
|
28768
28896
|
const value = {
|
|
28769
28897
|
...input,
|
|
28770
28898
|
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
28771
|
-
promotedAt
|
|
28899
|
+
promotedAt,
|
|
28900
|
+
promotionId: digest(new TextEncoder().encode(`${input.appId}\x00${input.channel}\x00${input.releaseId ?? "embedded"}\x00${promotedAt}\x00${randomUUID()}`))
|
|
28772
28901
|
};
|
|
28902
|
+
await beforeWrite?.(value);
|
|
28773
28903
|
const bytes = json(value);
|
|
28774
28904
|
await options.store.put(channelKey(value.appId, value.channel), bytes, {
|
|
28775
28905
|
cacheControl: "no-cache",
|
|
@@ -28784,10 +28914,92 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28784
28914
|
});
|
|
28785
28915
|
return value;
|
|
28786
28916
|
};
|
|
28917
|
+
const rolloutContext = async (channel) => {
|
|
28918
|
+
if (!options.store.list || !channel.releaseId)
|
|
28919
|
+
return null;
|
|
28920
|
+
const promotionId = healthPromotionId(channel);
|
|
28921
|
+
const storedPlan = await readVerifiedObject(rolloutPlanKey(channel.appId, promotionId, channel.releaseId), "rollout plan");
|
|
28922
|
+
if (storedPlan === null)
|
|
28923
|
+
return null;
|
|
28924
|
+
const plan = parseRolloutPlan(storedPlan, promotionId, channel.releaseId);
|
|
28925
|
+
const initialStage = plan.stages.findIndex((stage) => stage.rollout === channel.rollout);
|
|
28926
|
+
if (initialStage < 0)
|
|
28927
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout does not match its plan");
|
|
28928
|
+
let currentStage = initialStage;
|
|
28929
|
+
let enteredAt = plan.createdAt;
|
|
28930
|
+
const advances = await listPrefix(`${rolloutRoot(channel.appId, promotionId, channel.releaseId)}/advances/`);
|
|
28931
|
+
for (const item of advances) {
|
|
28932
|
+
const value = await readVerifiedObject(item.key, "rollout advancement");
|
|
28933
|
+
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))
|
|
28934
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout advancement is invalid");
|
|
28935
|
+
if (Number(value.stage) >= currentStage) {
|
|
28936
|
+
currentStage = Number(value.stage);
|
|
28937
|
+
enteredAt = value.createdAt;
|
|
28938
|
+
}
|
|
28939
|
+
}
|
|
28940
|
+
const controls = await listPrefix(`${rolloutRoot(channel.appId, promotionId, channel.releaseId)}/controls/`);
|
|
28941
|
+
const parsedControls = [];
|
|
28942
|
+
for (const item of controls) {
|
|
28943
|
+
const value = await readVerifiedObject(item.key, "rollout control");
|
|
28944
|
+
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")))
|
|
28945
|
+
throw new MobileUpdateRegistryError("Stored mobile update rollout control is invalid");
|
|
28946
|
+
parsedControls.push(value);
|
|
28947
|
+
}
|
|
28948
|
+
const cancelled = parsedControls.some(({ action }) => action === "cancel");
|
|
28949
|
+
const resumedPauseIds = new Set(parsedControls.flatMap((control) => control.resumedPauseIds ?? []));
|
|
28950
|
+
const activePauseIds = parsedControls.filter(({ action, id }) => action === "pause" && !resumedPauseIds.has(id)).map(({ id }) => id);
|
|
28951
|
+
const operatorPaused = activePauseIds.length > 0;
|
|
28952
|
+
const fleetPaused = Boolean(await options.store.head(pauseKey(channel.appId, promotionId, channel.releaseId)));
|
|
28953
|
+
return {
|
|
28954
|
+
cancelled,
|
|
28955
|
+
activePauseIds,
|
|
28956
|
+
channel,
|
|
28957
|
+
currentStage,
|
|
28958
|
+
enteredAt,
|
|
28959
|
+
fleetPaused,
|
|
28960
|
+
operatorPaused,
|
|
28961
|
+
plan,
|
|
28962
|
+
promotionId,
|
|
28963
|
+
rollout: plan.stages[currentStage].rollout
|
|
28964
|
+
};
|
|
28965
|
+
};
|
|
28966
|
+
const writeRolloutControl = async (channel, action, signal) => {
|
|
28967
|
+
const context = await rolloutContext(channel);
|
|
28968
|
+
if (!context)
|
|
28969
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
28970
|
+
if (context.cancelled)
|
|
28971
|
+
throw new MobileUpdateRegistryError("Mobile update rollout was already cancelled");
|
|
28972
|
+
if (action === "resume" && context.fleetPaused)
|
|
28973
|
+
throw new MobileUpdateRegistryError("A fleet-health pause requires an explicit re-promotion");
|
|
28974
|
+
const releaseId = channel.releaseId;
|
|
28975
|
+
if (!releaseId)
|
|
28976
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
28977
|
+
const createdAt = clock().toISOString();
|
|
28978
|
+
const id = randomUUID();
|
|
28979
|
+
const event = {
|
|
28980
|
+
action,
|
|
28981
|
+
createdAt,
|
|
28982
|
+
format: 1,
|
|
28983
|
+
id,
|
|
28984
|
+
promotionId: context.promotionId,
|
|
28985
|
+
releaseId,
|
|
28986
|
+
...action === "resume" ? { resumedPauseIds: context.activePauseIds } : {}
|
|
28987
|
+
};
|
|
28988
|
+
const bytes = json(event);
|
|
28989
|
+
await options.store.put(`${rolloutRoot(channel.appId, context.promotionId, releaseId)}/controls/${createdAt}-${id}-${action}.json`, bytes, {
|
|
28990
|
+
cacheControl: "no-store",
|
|
28991
|
+
contentType: "application/json",
|
|
28992
|
+
maxBytes: bytes.byteLength,
|
|
28993
|
+
metadata: { action, sha256: digest(bytes) },
|
|
28994
|
+
signal
|
|
28995
|
+
});
|
|
28996
|
+
};
|
|
28787
28997
|
const promoteUpdate = async (input) => {
|
|
28788
28998
|
input.signal?.throwIfAborted();
|
|
28789
28999
|
if (input.rollout <= 0 || input.rollout > 1)
|
|
28790
29000
|
throw new MobileUpdateRegistryError("Mobile update rollout is invalid");
|
|
29001
|
+
if (rollout && !rollout.stages.some((stage) => stage.rollout === input.rollout))
|
|
29002
|
+
throw new MobileUpdateRegistryError("Mobile update promotion rollout must match a configured rollout stage");
|
|
28791
29003
|
await assertNotMarked(input.appId, input.releaseId);
|
|
28792
29004
|
const release = await readManifest2(input.appId, input.releaseId);
|
|
28793
29005
|
if (!release || release.manifest.channel !== input.channel)
|
|
@@ -28799,7 +29011,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28799
29011
|
...existing?.releaseId && existing.releaseId !== input.releaseId ? { fallbackReleaseId: existing.releaseId } : existing?.fallbackReleaseId ? { fallbackReleaseId: existing.fallbackReleaseId } : {},
|
|
28800
29012
|
releaseId: input.releaseId,
|
|
28801
29013
|
rollout: input.rollout
|
|
28802
|
-
}, input.signal);
|
|
29014
|
+
}, input.signal, (channel) => initializeRollout(channel, input.signal));
|
|
28803
29015
|
return {
|
|
28804
29016
|
appId: input.appId,
|
|
28805
29017
|
channel: input.channel,
|
|
@@ -28812,14 +29024,15 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28812
29024
|
const channel = await readChannel(input.appId, input.channel);
|
|
28813
29025
|
if (!channel?.releaseId)
|
|
28814
29026
|
return { status: "empty" };
|
|
29027
|
+
const rolloutState = await rolloutContext(channel);
|
|
28815
29028
|
let selected = rolloutMember({
|
|
28816
29029
|
appId: input.appId,
|
|
28817
29030
|
channel: input.channel,
|
|
28818
29031
|
installationId: input.installationId,
|
|
28819
29032
|
releaseId: channel.releaseId,
|
|
28820
|
-
rollout: channel.rollout
|
|
29033
|
+
rollout: rolloutState?.rollout ?? channel.rollout
|
|
28821
29034
|
}) ? channel.releaseId : channel.fallbackReleaseId;
|
|
28822
|
-
if (
|
|
29035
|
+
if (selected === channel.releaseId && (rolloutState?.cancelled || rolloutState?.fleetPaused || rolloutState?.operatorPaused || health && await options.store.head(pauseKey(input.appId, healthPromotionId(channel), channel.releaseId))))
|
|
28823
29036
|
selected = channel.fallbackReleaseId;
|
|
28824
29037
|
if (!selected)
|
|
28825
29038
|
return { status: "empty" };
|
|
@@ -28863,23 +29076,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28863
29076
|
if (!channel || !releaseId || channel.releaseId !== releaseId)
|
|
28864
29077
|
return null;
|
|
28865
29078
|
const promotionId = healthPromotionId(channel);
|
|
28866
|
-
const
|
|
28867
|
-
const objects = [];
|
|
28868
|
-
const cursors = new Set;
|
|
28869
|
-
let cursor;
|
|
28870
|
-
do {
|
|
28871
|
-
const page = await options.store.list({
|
|
28872
|
-
...cursor ? { cursor } : {},
|
|
28873
|
-
prefix: prefix2
|
|
28874
|
-
});
|
|
28875
|
-
objects.push(...page.objects);
|
|
28876
|
-
if (!page.truncated)
|
|
28877
|
-
break;
|
|
28878
|
-
if (!page.cursor || cursors.has(page.cursor))
|
|
28879
|
-
throw new MobileUpdateRegistryError("Mobile update health storage returned an invalid cursor");
|
|
28880
|
-
cursors.add(page.cursor);
|
|
28881
|
-
cursor = page.cursor;
|
|
28882
|
-
} while (true);
|
|
29079
|
+
const objects = await listPrefix(`${healthRoot(input.appId, promotionId, releaseId)}/events/`);
|
|
28883
29080
|
const installations = new Set;
|
|
28884
29081
|
const byKind = new Map([...HEALTH_KINDS].map((kind) => [
|
|
28885
29082
|
kind,
|
|
@@ -28918,6 +29115,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28918
29115
|
]);
|
|
28919
29116
|
const terminals = new Set([...byKind.get("activated"), ...failures]);
|
|
28920
29117
|
const failureRate = terminals.size === 0 ? 0 : failures.size / terminals.size;
|
|
29118
|
+
const rolloutState = await rolloutContext(channel);
|
|
28921
29119
|
return {
|
|
28922
29120
|
activated: byKind.get("activated").size,
|
|
28923
29121
|
appId: input.appId,
|
|
@@ -28926,17 +29124,107 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28926
29124
|
downloadFailed: byKind.get("download-failed").size,
|
|
28927
29125
|
failureRate,
|
|
28928
29126
|
failures: failures.size,
|
|
28929
|
-
paused: Boolean(await options.store.head(pauseKey(input.appId, promotionId, releaseId))),
|
|
29127
|
+
paused: Boolean(rolloutState?.cancelled || rolloutState?.fleetPaused || rolloutState?.operatorPaused || await options.store.head(pauseKey(input.appId, promotionId, releaseId))),
|
|
28930
29128
|
promotionId,
|
|
28931
29129
|
quarantined: byKind.get("quarantined").size,
|
|
28932
29130
|
releaseId,
|
|
28933
29131
|
reportedInstallations: installations.size,
|
|
28934
29132
|
rolledBack: byKind.get("rolled-back").size,
|
|
28935
|
-
rollout: channel.rollout,
|
|
29133
|
+
rollout: rolloutState?.rollout ?? channel.rollout,
|
|
28936
29134
|
terminalReports: terminals.size,
|
|
28937
29135
|
transfer
|
|
28938
29136
|
};
|
|
28939
29137
|
};
|
|
29138
|
+
const inspectUpdateRollout = async (input) => {
|
|
29139
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
29140
|
+
if (!channel?.releaseId)
|
|
29141
|
+
return null;
|
|
29142
|
+
const context = await rolloutContext(channel);
|
|
29143
|
+
if (!context)
|
|
29144
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
29145
|
+
const healthReport = await inspectUpdateHealth(input);
|
|
29146
|
+
if (!healthReport)
|
|
29147
|
+
return null;
|
|
29148
|
+
const paused = context.fleetPaused || context.operatorPaused;
|
|
29149
|
+
const complete = context.currentStage === context.plan.stages.length - 1;
|
|
29150
|
+
return {
|
|
29151
|
+
...healthReport,
|
|
29152
|
+
automatic: context.plan.automatic,
|
|
29153
|
+
currentStage: context.currentStage,
|
|
29154
|
+
enteredAt: context.enteredAt,
|
|
29155
|
+
...!complete ? { nextStage: context.plan.stages[context.currentStage + 1] } : {},
|
|
29156
|
+
...context.fleetPaused ? { pausedBy: "fleet-health" } : context.operatorPaused ? { pausedBy: "operator" } : {},
|
|
29157
|
+
status: context.cancelled ? "cancelled" : paused ? "paused" : complete ? "complete" : "active"
|
|
29158
|
+
};
|
|
29159
|
+
};
|
|
29160
|
+
const advanceRollout = async (input, strict) => {
|
|
29161
|
+
input.signal?.throwIfAborted();
|
|
29162
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
29163
|
+
if (!channel?.releaseId)
|
|
29164
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
29165
|
+
const context = await rolloutContext(channel);
|
|
29166
|
+
if (!context)
|
|
29167
|
+
throw new MobileUpdateRegistryError("Mobile update rollout orchestration is not configured");
|
|
29168
|
+
const report = await inspectUpdateRollout(input);
|
|
29169
|
+
if (!report)
|
|
29170
|
+
throw new MobileUpdateRegistryError("Mobile update rollout report is unavailable");
|
|
29171
|
+
const nextStage = context.plan.stages[context.currentStage + 1];
|
|
29172
|
+
if (!nextStage)
|
|
29173
|
+
return report;
|
|
29174
|
+
if (input.rollout !== undefined && input.rollout !== nextStage.rollout)
|
|
29175
|
+
throw new MobileUpdateRegistryError("Mobile update rollout can advance only to the next configured stage");
|
|
29176
|
+
if (report.status !== "active") {
|
|
29177
|
+
if (strict)
|
|
29178
|
+
throw new MobileUpdateRegistryError(`Mobile update rollout cannot advance while ${report.status}`);
|
|
29179
|
+
return report;
|
|
29180
|
+
}
|
|
29181
|
+
const gate = context.plan.stages[context.currentStage];
|
|
29182
|
+
const observedMs = clock().getTime() - Date.parse(context.enteredAt);
|
|
29183
|
+
const blocked = report.terminalReports < gate.minimumReports || report.failureRate > gate.maximumFailureRate || observedMs < gate.observationMs;
|
|
29184
|
+
if (blocked) {
|
|
29185
|
+
if (strict)
|
|
29186
|
+
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`);
|
|
29187
|
+
return report;
|
|
29188
|
+
}
|
|
29189
|
+
const stage = context.currentStage + 1;
|
|
29190
|
+
const event = {
|
|
29191
|
+
createdAt: clock().toISOString(),
|
|
29192
|
+
failureRate: report.failureRate,
|
|
29193
|
+
format: 1,
|
|
29194
|
+
promotionId: context.promotionId,
|
|
29195
|
+
releaseId: channel.releaseId,
|
|
29196
|
+
rollout: nextStage.rollout,
|
|
29197
|
+
stage,
|
|
29198
|
+
terminalReports: report.terminalReports
|
|
29199
|
+
};
|
|
29200
|
+
const bytes = json(event);
|
|
29201
|
+
await options.store.put(`${rolloutRoot(input.appId, context.promotionId, channel.releaseId)}/advances/${String(stage).padStart(4, "0")}.json`, bytes, {
|
|
29202
|
+
cacheControl: "no-store",
|
|
29203
|
+
contentType: "application/json",
|
|
29204
|
+
maxBytes: bytes.byteLength,
|
|
29205
|
+
metadata: { releaseid: channel.releaseId, sha256: digest(bytes) },
|
|
29206
|
+
signal: input.signal
|
|
29207
|
+
});
|
|
29208
|
+
return await inspectUpdateRollout(input);
|
|
29209
|
+
};
|
|
29210
|
+
const advanceUpdateRollout = (input) => advanceRollout(input, true);
|
|
29211
|
+
const reconcileUpdateRollout = async (input) => {
|
|
29212
|
+
const report = await inspectUpdateRollout(input);
|
|
29213
|
+
if (!report || !report.automatic)
|
|
29214
|
+
return report;
|
|
29215
|
+
return advanceRollout(input, false);
|
|
29216
|
+
};
|
|
29217
|
+
const rolloutControl = (action) => async (input) => {
|
|
29218
|
+
input.signal?.throwIfAborted();
|
|
29219
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
29220
|
+
if (!channel?.releaseId)
|
|
29221
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
|
|
29222
|
+
await writeRolloutControl(channel, action, input.signal);
|
|
29223
|
+
return await inspectUpdateRollout(input);
|
|
29224
|
+
};
|
|
29225
|
+
const pauseUpdateRollout = rolloutControl("pause");
|
|
29226
|
+
const resumeUpdateRollout = rolloutControl("resume");
|
|
29227
|
+
const cancelUpdateRollout = rolloutControl("cancel");
|
|
28940
29228
|
const recordUpdateHealth = async (input) => {
|
|
28941
29229
|
const payload = verifyHealthToken(input.token);
|
|
28942
29230
|
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")
|
|
@@ -28991,6 +29279,11 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
28991
29279
|
});
|
|
28992
29280
|
report = { ...report, paused: true };
|
|
28993
29281
|
}
|
|
29282
|
+
if (rollout && (input.kind === "activated" || FAILURE_HEALTH_KINDS.has(input.kind)))
|
|
29283
|
+
return await reconcileUpdateRollout({
|
|
29284
|
+
appId: input.appId,
|
|
29285
|
+
channel: input.channel
|
|
29286
|
+
}) ?? report;
|
|
28994
29287
|
return report;
|
|
28995
29288
|
};
|
|
28996
29289
|
const retentionValues = (input) => {
|
|
@@ -29236,6 +29529,14 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
29236
29529
|
};
|
|
29237
29530
|
return {
|
|
29238
29531
|
...health ? { inspectUpdateHealth, issueUpdateHealthToken, recordUpdateHealth } : {},
|
|
29532
|
+
...rollout ? {
|
|
29533
|
+
advanceUpdateRollout,
|
|
29534
|
+
cancelUpdateRollout,
|
|
29535
|
+
inspectUpdateRollout,
|
|
29536
|
+
pauseUpdateRollout,
|
|
29537
|
+
reconcileUpdateRollout,
|
|
29538
|
+
resumeUpdateRollout
|
|
29539
|
+
} : {},
|
|
29239
29540
|
inspectUpdateStorage: async (input) => (await inventory(input)).report,
|
|
29240
29541
|
pruneUpdates,
|
|
29241
29542
|
publishUpdate: async (input) => {
|
|
@@ -29849,6 +30150,11 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
29849
30150
|
return;
|
|
29850
30151
|
if (typeof module.registry.inspectUpdateHealth !== "function" || typeof module.registry.issueUpdateHealthToken !== "function" || typeof module.registry.recordUpdateHealth !== "function")
|
|
29851
30152
|
throw new TypeError("Mobile update fleet health is enabled but the registry is not provisioned for it. Run `absolute mobile update provision --force`.");
|
|
30153
|
+
}, verifyRolloutModule = (config, module) => {
|
|
30154
|
+
if (!config.updateServer?.rollout)
|
|
30155
|
+
return;
|
|
30156
|
+
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")
|
|
30157
|
+
throw new TypeError("Mobile update rollout orchestration is enabled but the registry is not provisioned for it. Run `absolute mobile update provision --force`.");
|
|
29852
30158
|
}, expoSigningOptions = (config) => {
|
|
29853
30159
|
if (!config.updates?.expoCodeSigning)
|
|
29854
30160
|
return;
|
|
@@ -29883,6 +30189,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
29883
30189
|
if (options.production) {
|
|
29884
30190
|
await verifyDurableModule(module);
|
|
29885
30191
|
verifyHealthModule(config, module);
|
|
30192
|
+
verifyRolloutModule(config, module);
|
|
29886
30193
|
}
|
|
29887
30194
|
const manifest = new URL(updates.manifestUrl);
|
|
29888
30195
|
if (!manifest.pathname.endsWith("/update.json"))
|
|
@@ -29903,6 +30210,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
29903
30210
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
|
|
29904
30211
|
await verifyDurableModule(module);
|
|
29905
30212
|
verifyHealthModule(config, module);
|
|
30213
|
+
verifyRolloutModule(config, module);
|
|
29906
30214
|
if (config.engine === "expo")
|
|
29907
30215
|
expoSigningOptions(config);
|
|
29908
30216
|
return module.metadata;
|
|
@@ -29974,17 +30282,25 @@ export default createMobileUpdateRegistry({
|
|
|
29974
30282
|
`;
|
|
29975
30283
|
}, renderAbsoluteMobileUpdateRegistry = (options) => {
|
|
29976
30284
|
const source = renderAbsoluteMobileUpdateRegistryBase(options);
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
30285
|
+
let generated = "";
|
|
30286
|
+
if (options.health) {
|
|
30287
|
+
const secret = options.storage === "local" ? `process.env.${options.health.secretEnv} ?? 'absolutejs-local-health-secret-not-for-production'` : `required('${options.health.secretEnv}')`;
|
|
30288
|
+
generated += ` health: {
|
|
29981
30289
|
autoPause: { failureRate: ${options.health.failureRate}, minimumReports: ${options.health.minimumReports} },
|
|
29982
30290
|
secret: ${secret}
|
|
29983
30291
|
},
|
|
29984
30292
|
`;
|
|
30293
|
+
}
|
|
30294
|
+
if (options.rollout)
|
|
30295
|
+
generated += ` rollout: ${JSON.stringify(options.rollout, null, "\t").replaceAll(`
|
|
30296
|
+
`, `
|
|
30297
|
+
`)},
|
|
30298
|
+
`;
|
|
30299
|
+
if (!generated)
|
|
30300
|
+
return source;
|
|
29985
30301
|
return source.replace(`export default createMobileUpdateRegistry({
|
|
29986
30302
|
`, `export default createMobileUpdateRegistry({
|
|
29987
|
-
${
|
|
30303
|
+
${generated}`);
|
|
29988
30304
|
}, writeAbsoluteMobileUpdateRegistry = async (options) => {
|
|
29989
30305
|
const path2 = projectPath(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
|
|
29990
30306
|
if (!options.force) {
|
|
@@ -29997,6 +30313,7 @@ ${health}`);
|
|
|
29997
30313
|
await mkdir13(dirname31(path2), { recursive: true });
|
|
29998
30314
|
await Bun.write(path2, renderAbsoluteMobileUpdateRegistry({
|
|
29999
30315
|
...options.health ? { health: options.health } : {},
|
|
30316
|
+
...options.rollout ? { rollout: options.rollout } : {},
|
|
30000
30317
|
publicKeys: options.publicKeys,
|
|
30001
30318
|
storage: options.storage
|
|
30002
30319
|
}));
|
|
@@ -37599,5 +37916,5 @@ export {
|
|
|
37599
37916
|
devBuild
|
|
37600
37917
|
};
|
|
37601
37918
|
|
|
37602
|
-
//# debugId=
|
|
37919
|
+
//# debugId=EAFB7DB04FFF880464756E2164756E21
|
|
37603
37920
|
//# sourceMappingURL=build.js.map
|