@absolutejs/absolute 0.20.0-beta.84 → 0.20.0-beta.86

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.
@@ -12,7 +12,7 @@ import {
12
12
  createIslandRegistryDefinitionPlugin,
13
13
  finalizeAbsoluteMobileCompatibilityBuild,
14
14
  resolveServerBundleExternals
15
- } from "./index-8gksmws3.js";
15
+ } from "./index-x48brywr.js";
16
16
  import {
17
17
  loadIslandRegistryBuildInfo
18
18
  } from "./index-bzs19w3r.js";
@@ -35,8 +35,8 @@ import {
35
35
  import"./index-w5vswatm.js";
36
36
  import {
37
37
  normalizeAbsoluteMobileConfig
38
- } from "./index-czw3jd8f.js";
39
- import"./index-p8ezgd5e.js";
38
+ } from "./index-3tsmbyze.js";
39
+ import"./index-tame6e2c.js";
40
40
  import {
41
41
  ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV,
42
42
  installAbsoluteMobileAuthEnvironment
@@ -1093,7 +1093,7 @@ var compileUnlocked = async (serverEntry, resolvedOutdir, outfile, configPath) =
1093
1093
  force: true,
1094
1094
  recursive: true
1095
1095
  });
1096
- const configuredPrerenderPort = env.COMPILE_PORT === undefined ? Number(env.PORT) : Number(env.COMPILE_PORT);
1096
+ const configuredPrerenderPort = Number(env.COMPILE_PORT);
1097
1097
  const prerenderPort = configuredPrerenderPort > 0 ? configuredPrerenderPort : await findFreePort();
1098
1098
  killStaleProcesses(prerenderPort);
1099
1099
  const entryName = basename2(serverEntry).replace(/\.[^.]+$/, "");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  normalizeAbsoluteMobileConfig
4
- } from "./index-czw3jd8f.js";
4
+ } from "./index-3tsmbyze.js";
5
5
  import"./index-mywsk201.js";
6
6
  export {
7
7
  normalizeAbsoluteMobileConfig
@@ -1281,8 +1281,8 @@ var loadAbsoluteMobileDevModules = async () => {
1281
1281
  remoteMacProtocol,
1282
1282
  iosNativeWatcher
1283
1283
  ] = await Promise.all([
1284
- import("./config-naw5fer4.js"),
1285
- import("./expoProject-3mmwtemn.js"),
1284
+ import("./config-z2kf085h.js"),
1285
+ import("./expoProject-dac8fqhq.js"),
1286
1286
  import("./expoDevController-n19kb5fr.js"),
1287
1287
  import("./nativeAuth-zx4tsb1f.js"),
1288
1288
  import("./androidEmulatorController-nybhzc6c.js"),
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  syncAbsoluteExpoWebAssets,
4
4
  writeAbsoluteExpoProject
5
- } from "./index-p8ezgd5e.js";
5
+ } from "./index-tame6e2c.js";
6
6
  import"./index-ar4gz15x.js";
7
7
  import"./index-mywsk201.js";
8
8
  export {
@@ -11,6 +11,8 @@ var UPDATE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
11
11
  var UPDATE_PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/u;
12
12
  var ENVIRONMENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
13
13
  var DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000;
14
+ var DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2;
15
+ var DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20;
14
16
  var MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000;
15
17
  var MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000;
16
18
  var 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])?))*$/;
@@ -230,6 +232,20 @@ var normalizeUpdateServer = (config, productionOrigin, projectRoot, updates) =>
230
232
  if (!ENVIRONMENT_NAME_PATTERN.test(expoPrivateKeyEnv))
231
233
  throw new TypeError("mobile.updates.server.expoPrivateKeyEnv must be a valid environment variable name.");
232
234
  const autoMount = config.updates.server?.autoMount ?? true;
235
+ const configuredHealth = config.updates.server?.health;
236
+ let health;
237
+ if (configuredHealth !== false) {
238
+ const failureRate = configuredHealth?.failureRate ?? DEFAULT_UPDATE_HEALTH_FAILURE_RATE;
239
+ const minimumReports = configuredHealth?.minimumReports ?? DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS;
240
+ const secretEnv = requireText(configuredHealth?.secretEnv ?? "ABSOLUTE_MOBILE_UPDATE_HEALTH_SECRET", "mobile.updates.server.health.secretEnv");
241
+ if (!Number.isFinite(failureRate) || failureRate <= 0 || failureRate > 1)
242
+ throw new TypeError("mobile.updates.server.health.failureRate must be greater than 0 and at most 1.");
243
+ if (!Number.isSafeInteger(minimumReports) || minimumReports < 1)
244
+ throw new TypeError("mobile.updates.server.health.minimumReports must be a positive integer.");
245
+ if (!ENVIRONMENT_NAME_PATTERN.test(secretEnv))
246
+ throw new TypeError("mobile.updates.server.health.secretEnv must be a valid environment variable name.");
247
+ health = { failureRate, minimumReports, secretEnv };
248
+ }
233
249
  if (autoMount) {
234
250
  const manifest = new URL(updates.manifestUrl);
235
251
  if (manifest.origin !== productionOrigin)
@@ -262,7 +278,12 @@ var normalizeUpdateServer = (config, productionOrigin, projectRoot, updates) =>
262
278
  throw new TypeError(`mobile.updates.server.expoCodeSigningKeys.${keyId} certificate is not currently valid.`);
263
279
  expoCodeSigningKeys[keyId] = { certificatePem, privateKeyEnv };
264
280
  }
265
- return { autoMount, expoCodeSigningKeys, registryModule };
281
+ return {
282
+ autoMount,
283
+ expoCodeSigningKeys,
284
+ ...health ? { health } : {},
285
+ registryModule
286
+ };
266
287
  };
267
288
  var validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
268
289
  if (segment === "*" && (index !== count - 1 || count === 1)) {
@@ -6,7 +6,7 @@ import {
6
6
  createIslandRegistryDefinitionPlugin,
7
7
  finalizeAbsoluteMobileCompatibilityBuild,
8
8
  resolveServerBundleExternals
9
- } from "./index-8gksmws3.js";
9
+ } from "./index-x48brywr.js";
10
10
  import {
11
11
  loadIslandRegistryBuildInfo
12
12
  } from "./index-bzs19w3r.js";
@@ -26,7 +26,7 @@ import {
26
26
  } from "./index-9kwmb3c0.js";
27
27
  import {
28
28
  normalizeAbsoluteMobileConfig
29
- } from "./index-czw3jd8f.js";
29
+ } from "./index-3tsmbyze.js";
30
30
  import {
31
31
  installAbsoluteMobileAuthEnvironment
32
32
  } from "./index-ar4gz15x.js";
@@ -1728,11 +1728,48 @@ export default function AbsoluteLayout() {
1728
1728
  return <Stack screenOptions={{ headerShown: false }} />;
1729
1729
  }
1730
1730
  `;
1731
- var updatesRuntimeSource = () => `${EXPO_GENERATED_HEADER}import { randomUUID } from 'expo-crypto';
1731
+ var updatesRuntimeSource = (config) => `${EXPO_GENERATED_HEADER}import { randomUUID } from 'expo-crypto';
1732
1732
  import * as SecureStore from 'expo-secure-store';
1733
1733
  import * as Updates from 'expo-updates';
1734
1734
 
1735
1735
  const INSTALLATION_KEY = 'absolutejs.mobile.update.installation.v1';
1736
+ const PENDING_HEALTH_KEY = 'absolutejs.mobile.update.pending-health.v1';
1737
+ const APP_ID = ${JSON.stringify(config.appId)};
1738
+ const CHANNEL = ${JSON.stringify(config.updates?.channel)};
1739
+ const MANIFEST_URL = ${JSON.stringify(config.updates?.manifestUrl)};
1740
+
1741
+ const updateIdentity = (value: unknown) => {
1742
+ if (!value || typeof value !== 'object') return undefined;
1743
+ const extra = Reflect.get(value, 'extra');
1744
+ if (!extra || typeof extra !== 'object') return undefined;
1745
+ const absolute = Reflect.get(extra, 'absolutejs');
1746
+ if (!absolute || typeof absolute !== 'object') return undefined;
1747
+ const healthToken = Reflect.get(absolute, 'healthToken');
1748
+ const releaseId = Reflect.get(absolute, 'releaseId');
1749
+ return typeof healthToken === 'string' && typeof releaseId === 'string'
1750
+ ? { healthToken, releaseId }
1751
+ : undefined;
1752
+ };
1753
+
1754
+ const report = async (installationId: string, evidence: { healthToken: string; kind: string; releaseId: string }) => {
1755
+ const endpoint = new URL('./health', MANIFEST_URL);
1756
+ await fetch(endpoint.href, {
1757
+ body: JSON.stringify({ kind: evidence.kind, releaseId: evidence.releaseId }),
1758
+ cache: 'no-store',
1759
+ credentials: 'omit',
1760
+ headers: {
1761
+ 'content-type': 'application/json',
1762
+ 'x-absolute-mobile-app': APP_ID,
1763
+ 'x-absolute-mobile-channel': CHANNEL,
1764
+ 'x-absolute-mobile-health-token': evidence.healthToken,
1765
+ 'x-absolute-mobile-installation': installationId,
1766
+ 'x-absolute-mobile-release': updateIdentity(Updates.manifest)?.releaseId ?? 'embedded',
1767
+ 'x-absolute-mobile-runtime': Updates.runtimeVersion ?? ''
1768
+ },
1769
+ method: 'POST',
1770
+ redirect: 'error'
1771
+ });
1772
+ };
1736
1773
 
1737
1774
  let startPromise: Promise<void> | undefined;
1738
1775
  export const startAbsoluteExpoUpdates = () => {
@@ -1744,9 +1781,34 @@ export const startAbsoluteExpoUpdates = () => {
1744
1781
  await SecureStore.setItemAsync(INSTALLATION_KEY, installationId);
1745
1782
  }
1746
1783
  await Updates.setExtraParamAsync('absolute-installation', installationId);
1784
+ const pendingSource = await SecureStore.getItemAsync(PENDING_HEALTH_KEY);
1785
+ if (pendingSource) {
1786
+ try {
1787
+ const pending = JSON.parse(pendingSource);
1788
+ const active = updateIdentity(Updates.manifest);
1789
+ if (typeof pending?.healthToken === 'string' && typeof pending?.releaseId === 'string')
1790
+ await report(installationId, {
1791
+ healthToken: pending.healthToken,
1792
+ kind: active?.releaseId === pending.releaseId ? 'activated' : 'rolled-back',
1793
+ releaseId: pending.releaseId
1794
+ });
1795
+ } catch {}
1796
+ await SecureStore.deleteItemAsync(PENDING_HEALTH_KEY);
1797
+ }
1747
1798
  const result = await Updates.checkForUpdateAsync();
1748
1799
  if (result.isAvailable || result.isRollBackToEmbedded) {
1749
- await Updates.fetchUpdateAsync();
1800
+ const available = updateIdentity(Reflect.get(result, 'manifest'));
1801
+ try {
1802
+ const fetched = await Updates.fetchUpdateAsync();
1803
+ const identity = updateIdentity(Reflect.get(fetched, 'manifest')) ?? available;
1804
+ if (identity) {
1805
+ await SecureStore.setItemAsync(PENDING_HEALTH_KEY, JSON.stringify(identity));
1806
+ await report(installationId, { ...identity, kind: 'downloaded' });
1807
+ }
1808
+ } catch (error) {
1809
+ if (available) await report(installationId, { ...available, kind: 'download-failed' }).catch(() => undefined);
1810
+ throw error;
1811
+ }
1750
1812
  if (result.isRollBackToEmbedded) {
1751
1813
  await Updates.reloadAsync();
1752
1814
  }
@@ -2691,7 +2753,7 @@ node_modules/
2691
2753
  files.set(path, source);
2692
2754
  }
2693
2755
  if (config.updates) {
2694
- files.set(join4(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource());
2756
+ files.set(join4(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource(config));
2695
2757
  }
2696
2758
  const expoCodeSigning = config.updates?.expoCodeSigning;
2697
2759
  if (expoCodeSigning)
@@ -4,13 +4,13 @@ import {
4
4
  } from "./index-s2tazax7.js";
5
5
  import {
6
6
  normalizeAbsoluteMobileConfig
7
- } from "./index-czw3jd8f.js";
7
+ } from "./index-3tsmbyze.js";
8
8
  import {
9
9
  assertAbsoluteDeviceCapabilityPackages,
10
10
  resolveAbsoluteMobileUpdateRuntime,
11
11
  syncAbsoluteExpoWebAssets,
12
12
  writeAbsoluteExpoProject
13
- } from "./index-p8ezgd5e.js";
13
+ } from "./index-tame6e2c.js";
14
14
 
15
15
  // src/build/islandRegistryTransform.ts
16
16
  import { basename } from "path";
package/dist/cli/index.js CHANGED
@@ -58,7 +58,7 @@ if (command === "dev") {
58
58
  }
59
59
  const positionalArgs = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile" && arg !== "--eager");
60
60
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
61
- const { dev } = await import("./dev-skb8dz33.js");
61
+ const { dev } = await import("./dev-3hweza3s.js");
62
62
  await dev(serverEntry, configPath, {
63
63
  androidDevice,
64
64
  eager: args.includes("--eager"),
@@ -71,7 +71,7 @@ if (command === "dev") {
71
71
  const configPath = parseNamedArg("--config");
72
72
  const positionalArgs = stripNamedArgs("--outdir", "--config").filter((arg) => arg !== "--prebuilt");
73
73
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
74
- const { start } = await import("./start-jbgfw27x.js");
74
+ const { start } = await import("./start-7t186mhg.js");
75
75
  await start(serverEntry, outdir, configPath, {
76
76
  prebuilt: args.includes("--prebuilt")
77
77
  });
@@ -81,7 +81,7 @@ if (command === "dev") {
81
81
  const configPath = parseNamedArg("--config");
82
82
  const positionalArgs = stripNamedArgs("--outdir", "--config");
83
83
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
84
- const { start } = await import("./start-jbgfw27x.js");
84
+ const { start } = await import("./start-7t186mhg.js");
85
85
  await start(serverEntry, outdir, configPath, { prepareOnly: true });
86
86
  } else if (command === "build") {
87
87
  sendTelemetryEvent("cli:command", { command });
@@ -197,13 +197,13 @@ if (command === "dev") {
197
197
  const configPath = parseNamedArg("--config");
198
198
  const positionalArgs = stripNamedArgs("--outdir", "--outfile", "--config");
199
199
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
200
- const { compile } = await import("./compile-t73ac1zb.js");
200
+ const { compile } = await import("./compile-e9cn3xpx.js");
201
201
  await compile(serverEntry, outdir, outfile, configPath);
202
202
  } else if (command === "mobile") {
203
203
  sendTelemetryEvent("cli:command", {
204
204
  command: `mobile:${workspaceCommand ?? "unknown"}`
205
205
  });
206
- const { runMobile } = await import("./mobile-pdck6a35.js");
206
+ const { runMobile } = await import("./mobile-2p3z33t1.js");
207
207
  try {
208
208
  await runMobile(args);
209
209
  } catch (error) {
@@ -7,12 +7,12 @@ import {
7
7
  } from "./index-9r7n9dqp.js";
8
8
  import {
9
9
  start
10
- } from "./index-q78x1k9q.js";
10
+ } from "./index-9rk7v35t.js";
11
11
  import {
12
12
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
13
13
  ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
14
14
  resolveAbsoluteMobileRoute
15
- } from "./index-8gksmws3.js";
15
+ } from "./index-x48brywr.js";
16
16
  import"./index-bzs19w3r.js";
17
17
  import"./index-s2tazax7.js";
18
18
  import {
@@ -52,7 +52,7 @@ import {
52
52
  import"./index-w5vswatm.js";
53
53
  import {
54
54
  normalizeAbsoluteMobileConfig
55
- } from "./index-czw3jd8f.js";
55
+ } from "./index-3tsmbyze.js";
56
56
  import {
57
57
  ABSOLUTE_MOBILE_UPDATE_FORMAT,
58
58
  absoluteDeviceNativeRequirements,
@@ -66,7 +66,7 @@ import {
66
66
  syncAbsoluteExpoWebAssets,
67
67
  unsignedAbsoluteMobileUpdate,
68
68
  writeAbsoluteExpoProject
69
- } from "./index-p8ezgd5e.js";
69
+ } from "./index-tame6e2c.js";
70
70
  import {
71
71
  normalizeAbsoluteIosDeviceIdentifier
72
72
  } from "./index-j66p8hn5.js";
@@ -19318,6 +19318,12 @@ var verifyDurableModule = async (module) => {
19318
19318
  throw new TypeError(`Durable mobile update storage verification failed for ${module.metadata.provider}. Check the bucket, endpoint, credentials, and read/write/delete permissions.`, { cause: error });
19319
19319
  }
19320
19320
  };
19321
+ var verifyHealthModule = (config, module) => {
19322
+ if (!config.updateServer?.health)
19323
+ return;
19324
+ if (typeof module.registry.inspectUpdateHealth !== "function" || typeof module.registry.issueUpdateHealthToken !== "function" || typeof module.registry.recordUpdateHealth !== "function")
19325
+ throw new TypeError("Mobile update fleet health is enabled but the registry is not provisioned for it. Run `absolute mobile update provision --force`.");
19326
+ };
19321
19327
  var expoSigningOptions = (config) => {
19322
19328
  if (!config.updates?.expoCodeSigning)
19323
19329
  return;
@@ -19350,12 +19356,13 @@ var inspectAbsoluteMobileUpdateServer = async (config, projectRoot) => {
19350
19356
  return;
19351
19357
  const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
19352
19358
  await verifyDurableModule(module);
19359
+ verifyHealthModule(config, module);
19353
19360
  if (config.engine === "expo")
19354
19361
  expoSigningOptions(config);
19355
19362
  return module.metadata;
19356
19363
  };
19357
19364
  var publicKeysSource = (publicKeys) => JSON.stringify(publicKeys, null, "\t");
19358
- var renderAbsoluteMobileUpdateRegistry = (options) => {
19365
+ var renderAbsoluteMobileUpdateRegistryBase = (options) => {
19359
19366
  const metadata = `export const absoluteMobileUpdateServer = {
19360
19367
  format: 1,
19361
19368
  provider: '${options.storage}',
@@ -19422,6 +19429,20 @@ export default createMobileUpdateRegistry({
19422
19429
  });
19423
19430
  `;
19424
19431
  };
19432
+ var renderAbsoluteMobileUpdateRegistry = (options) => {
19433
+ const source = renderAbsoluteMobileUpdateRegistryBase(options);
19434
+ if (!options.health)
19435
+ return source;
19436
+ const secret = options.storage === "local" ? `process.env.${options.health.secretEnv} ?? 'absolutejs-local-health-secret-not-for-production'` : `required('${options.health.secretEnv}')`;
19437
+ const health = ` health: {
19438
+ autoPause: { failureRate: ${options.health.failureRate}, minimumReports: ${options.health.minimumReports} },
19439
+ secret: ${secret}
19440
+ },
19441
+ `;
19442
+ return source.replace(`export default createMobileUpdateRegistry({
19443
+ `, `export default createMobileUpdateRegistry({
19444
+ ${health}`);
19445
+ };
19425
19446
  var writeAbsoluteMobileUpdateRegistry = async (options) => {
19426
19447
  const path = projectPath(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
19427
19448
  if (!options.force) {
@@ -19433,6 +19454,7 @@ var writeAbsoluteMobileUpdateRegistry = async (options) => {
19433
19454
  }
19434
19455
  await mkdir5(dirname4(path), { recursive: true });
19435
19456
  await Bun.write(path, renderAbsoluteMobileUpdateRegistry({
19457
+ ...options.health ? { health: options.health } : {},
19436
19458
  publicKeys: options.publicKeys,
19437
19459
  storage: options.storage
19438
19460
  }));
@@ -21994,6 +22016,34 @@ var lifecycleMethod = (publisher, name) => {
21994
22016
  throw new TypeError(`Mobile update registry does not support ${name}. Re-run \`absolute mobile update provision --force\` after upgrading @absolutejs/deploy.`);
21995
22017
  return method;
21996
22018
  };
22019
+ var inspectAbsoluteMobileUpdateHealth = async (options) => {
22020
+ const report = await lifecycleMethod(options.publisher, "inspectUpdateHealth")({
22021
+ appId: options.appId,
22022
+ channel: options.channel,
22023
+ ...options.releaseId ? { releaseId: options.releaseId } : {}
22024
+ });
22025
+ if (report === null)
22026
+ return null;
22027
+ if (!object3(report) || report.appId !== options.appId || report.channel !== options.channel || options.releaseId !== undefined && report.releaseId !== options.releaseId || typeof report.paused !== "boolean" || ![
22028
+ report.activated,
22029
+ report.downloaded,
22030
+ report.downloadFailed,
22031
+ report.failures,
22032
+ report.quarantined,
22033
+ report.reportedInstallations,
22034
+ report.rolledBack,
22035
+ report.terminalReports
22036
+ ].every((value) => Number.isSafeInteger(value) && value >= 0) || !Number.isFinite(report.failureRate) || report.failureRate < 0 || report.failureRate > 1 || !Number.isFinite(report.rollout) || report.rollout < 0 || report.rollout > 1 || !object3(report.transfer) || ![
22037
+ report.transfer.avoidedBytes,
22038
+ report.transfer.downloadedBytes,
22039
+ report.transfer.durationMs,
22040
+ report.transfer.resumedBytes,
22041
+ report.transfer.reusedBytes,
22042
+ report.transfer.throughputBytesPerSecond
22043
+ ].every((value) => Number.isFinite(value) && value >= 0))
22044
+ throw new TypeError("Mobile update registry returned an invalid health report.");
22045
+ return report;
22046
+ };
21997
22047
  var validOptionalCounters = (values) => values.every((value) => value === undefined) || values.every((value) => Number.isSafeInteger(value) && (value ?? -1) >= 0);
21998
22048
  var validateStorageIdentity = (result, appId) => {
21999
22049
  if (!object3(result) || result.appId !== appId || !Array.isArray(result.releases) || ![
@@ -22793,7 +22843,7 @@ var provisionMobileUpdate = async (args) => {
22793
22843
  throw new TypeError("--storage must be local or s3.");
22794
22844
  const modulePath = valueAfter(args, "--registry") ?? mobile.updateServer.registryModule;
22795
22845
  const packages = [
22796
- "@absolutejs/deploy@0.25.7",
22846
+ "@absolutejs/deploy@0.25.11",
22797
22847
  "@absolutejs/blob@0.5.2",
22798
22848
  ...requestedStorage === "s3" ? [
22799
22849
  "@aws-sdk/client-s3@3.1095.0",
@@ -22806,6 +22856,7 @@ var provisionMobileUpdate = async (args) => {
22806
22856
  await installApprovedPackages(projectRoot, args, `Mobile update serving needs ${missing.map(packageNameFromSpec).join(", ")}. Install them now?`, missing);
22807
22857
  const path = await writeAbsoluteMobileUpdateRegistry({
22808
22858
  force: args.includes("--force"),
22859
+ ...mobile.updateServer.health ? { health: mobile.updateServer.health } : {},
22809
22860
  modulePath,
22810
22861
  projectRoot,
22811
22862
  publicKeys: mobile.updates.publicKeys,
@@ -22815,7 +22866,7 @@ var provisionMobileUpdate = async (args) => {
22815
22866
  if (requestedStorage === "local")
22816
22867
  console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
22817
22868
  else
22818
- console.log("Set ABSOLUTE_MOBILE_UPDATE_S3_BUCKET and standard AWS credentials on the trusted server; endpoint and region overrides are optional.");
22869
+ console.log(`Set ABSOLUTE_MOBILE_UPDATE_S3_BUCKET${mobile.updateServer.health ? `, ${mobile.updateServer.health.secretEnv},` : ""} and standard AWS credentials on the trusted server; endpoint and region overrides are optional.`);
22819
22870
  const expoSigning = mobile.updates.expoCodeSigning;
22820
22871
  if (expoSigning) {
22821
22872
  const signingKey = mobile.updateServer.expoCodeSigningKeys[expoSigning.keyId];
@@ -22938,6 +22989,28 @@ var inspectMobileUpdateStorage = async (args) => {
22938
22989
  });
22939
22990
  return report;
22940
22991
  };
22992
+ var inspectMobileUpdateHealth = async (args) => {
22993
+ const { mobile } = await loadMobile(valueAfter(args, "--config"));
22994
+ if (!mobile.updates)
22995
+ throw new TypeError("mobile update status requires mobile.updates config.");
22996
+ const { publisher } = await mobileUpdatePublisher(args);
22997
+ const report = await inspectAbsoluteMobileUpdateHealth({
22998
+ appId: mobile.appId,
22999
+ channel: mobile.updates.channel,
23000
+ publisher,
23001
+ ...valueAfter(args, "--release") ? { releaseId: valueAfter(args, "--release") } : {}
23002
+ });
23003
+ if (args.includes("--json"))
23004
+ console.log(JSON.stringify(report, null, 2));
23005
+ else if (!report)
23006
+ console.log(`No active mobile update exists on ${mobile.updates.channel}.`);
23007
+ else {
23008
+ console.log(`${report.releaseId} is ${report.paused ? "PAUSED" : "active"} at ${Math.round(report.rollout * 100)}%: ${report.terminalReports} terminal reports, ${report.failures} failures (${(report.failureRate * 100).toFixed(1)}%).`);
23009
+ console.log(` ${report.activated} activated, ${report.rolledBack} rolled back, ${report.quarantined} quarantined, ${report.downloaded} downloaded, ${report.downloadFailed} download failures.`);
23010
+ console.log(` Transfer: ${formatBytes(report.transfer.downloadedBytes)} downloaded, ${formatBytes(report.transfer.avoidedBytes)} avoided (${formatBytes(report.transfer.resumedBytes)} resumed, ${formatBytes(report.transfer.reusedBytes)} reused).`);
23011
+ }
23012
+ return report;
23013
+ };
22941
23014
  var collectMobileUpdates = async (args) => {
22942
23015
  const startedAt = performance.now();
22943
23016
  const { mobile } = await loadMobile(valueAfter(args, "--config"));
@@ -24316,6 +24389,10 @@ var runMobile = async (args) => {
24316
24389
  await inspectMobileUpdateStorage(args.slice(2));
24317
24390
  return;
24318
24391
  }
24392
+ if (command === "update" && args[1] === "status") {
24393
+ await inspectMobileUpdateHealth(args.slice(2));
24394
+ return;
24395
+ }
24319
24396
  if (command === "update" && args[1] === "gc") {
24320
24397
  await collectMobileUpdates(args.slice(2));
24321
24398
  return;
@@ -24328,7 +24405,7 @@ var runMobile = async (args) => {
24328
24405
  await publishIos(args.slice(2));
24329
24406
  return;
24330
24407
  }
24331
- throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
24408
+ throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--release id] [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json]> [--config path]");
24332
24409
  };
24333
24410
  export {
24334
24411
  runMobile
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  import {
3
3
  start
4
- } from "./index-q78x1k9q.js";
5
- import"./index-8gksmws3.js";
4
+ } from "./index-9rk7v35t.js";
5
+ import"./index-x48brywr.js";
6
6
  import"./index-bzs19w3r.js";
7
7
  import"./index-s2tazax7.js";
8
8
  import"./index-fc9vxw56.js";
@@ -10,8 +10,8 @@ import"./index-r8x3839e.js";
10
10
  import"./index-06cmrcdq.js";
11
11
  import"./index-9kwmb3c0.js";
12
12
  import"./index-w5vswatm.js";
13
- import"./index-czw3jd8f.js";
14
- import"./index-p8ezgd5e.js";
13
+ import"./index-3tsmbyze.js";
14
+ import"./index-tame6e2c.js";
15
15
  import"./index-ar4gz15x.js";
16
16
  import"./index-t84wxk1p.js";
17
17
  import"./index-wtfnv3n2.js";