@absolutejs/absolute 0.20.0-beta.80 → 0.20.0-beta.82

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.
@@ -4,12 +4,12 @@ import {
4
4
  } from "./index-zh6hhrwy.js";
5
5
  import {
6
6
  start
7
- } from "./index-d4ar3mwd.js";
7
+ } from "./index-q78x1k9q.js";
8
8
  import {
9
9
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
10
10
  ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
11
11
  resolveAbsoluteMobileRoute
12
- } from "./index-w5y7wxn9.js";
12
+ } from "./index-8gksmws3.js";
13
13
  import"./index-bzs19w3r.js";
14
14
  import"./index-s2tazax7.js";
15
15
  import {
@@ -49,7 +49,7 @@ import {
49
49
  import"./index-w5vswatm.js";
50
50
  import {
51
51
  normalizeAbsoluteMobileConfig
52
- } from "./index-6fb7k6dg.js";
52
+ } from "./index-czw3jd8f.js";
53
53
  import {
54
54
  ABSOLUTE_MOBILE_UPDATE_FORMAT,
55
55
  absoluteDeviceNativeRequirements,
@@ -16768,15 +16768,15 @@ var require_main = __commonJS(function(exports) {
16768
16768
 
16769
16769
  // src/cli/scripts/mobile.ts
16770
16770
  import {
16771
- access as access11,
16772
- mkdir as mkdir11,
16771
+ access as access12,
16772
+ mkdir as mkdir12,
16773
16773
  mkdtemp as mkdtemp3,
16774
16774
  readFile as readFile16,
16775
16775
  rm as rm6,
16776
16776
  writeFile as writeFile14
16777
16777
  } from "fs/promises";
16778
- import { createPublicKey } from "crypto";
16779
- import { join as join13, relative as relative10, resolve as resolve11 } from "path";
16778
+ import { createPublicKey as createPublicKey2 } from "crypto";
16779
+ import { join as join13, relative as relative11, resolve as resolve12 } from "path";
16780
16780
  import { createInterface } from "readline/promises";
16781
16781
 
16782
16782
  // src/mobile/nativeDeepLinks.ts
@@ -19083,8 +19083,8 @@ var waitForAbsoluteAndroidHmrApply = async (session, options) => {
19083
19083
  };
19084
19084
 
19085
19085
  // src/mobile/releaseDoctor.ts
19086
- import { access as access3, readFile as readFile8, readdir as readdir3 } from "fs/promises";
19087
- import { dirname as dirname4, extname, join as join7, relative as relative2 } from "path";
19086
+ import { access as access4, readFile as readFile8, readdir as readdir3 } from "fs/promises";
19087
+ import { dirname as dirname5, extname, join as join7, relative as relative3 } from "path";
19088
19088
 
19089
19089
  // src/mobile/mobileBundleInspection.ts
19090
19090
  import { createHash } from "crypto";
@@ -19240,6 +19240,202 @@ var inspectAbsoluteMobileBundle = async (config, projectRoot) => {
19240
19240
  }
19241
19241
  };
19242
19242
 
19243
+ // src/mobile/updateServer.ts
19244
+ import {
19245
+ createPrivateKey,
19246
+ createPublicKey,
19247
+ X509Certificate
19248
+ } from "crypto";
19249
+ import { access as access3, mkdir as mkdir5 } from "fs/promises";
19250
+ import { dirname as dirname4, isAbsolute, relative as relative2, resolve as resolve4, sep } from "path";
19251
+ import { pathToFileURL } from "url";
19252
+ import { Elysia as Elysia2 } from "elysia";
19253
+ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1;
19254
+ var DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE = "mobile.update.ts";
19255
+ var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19256
+ var projectPath = (projectRoot, requested) => {
19257
+ const root = resolve4(projectRoot);
19258
+ const path = resolve4(root, requested);
19259
+ const projectRelative = relative2(root, path);
19260
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep}`) || isAbsolute(projectRelative))
19261
+ throw new TypeError("mobile.updates.server.registry must remain inside the project.");
19262
+ return path;
19263
+ };
19264
+ var isRegistry = (value) => object(value) && [
19265
+ "publishUpdate",
19266
+ "promoteUpdate",
19267
+ "rollbackUpdate",
19268
+ "resolveUpdate",
19269
+ "readUpdateFile"
19270
+ ].every((method) => typeof value[method] === "function");
19271
+ var serverMetadata = (value) => {
19272
+ if (!object(value))
19273
+ throw new TypeError("Mobile update registry must export valid absoluteMobileUpdateServer metadata. Run `absolute mobile update provision`.");
19274
+ const { format, provider, storage } = value;
19275
+ if (format !== ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT || storage !== "local" && storage !== "durable" || typeof provider !== "string" || !provider)
19276
+ throw new TypeError("Mobile update registry must export valid absoluteMobileUpdateServer metadata. Run `absolute mobile update provision`.");
19277
+ const metadata = {
19278
+ format: ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT,
19279
+ provider,
19280
+ storage
19281
+ };
19282
+ return metadata;
19283
+ };
19284
+ var loadAbsoluteMobileUpdateServerModule = async (projectRoot, requestedModulePath = DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE) => {
19285
+ const modulePath = projectPath(projectRoot, requestedModulePath);
19286
+ await access3(modulePath).catch(() => {
19287
+ throw new TypeError(`Mobile update server registry does not exist: ${modulePath}. Run \`absolute mobile update provision\`.`);
19288
+ });
19289
+ const loaded = await import(pathToFileURL(modulePath).href);
19290
+ if (!object(loaded))
19291
+ throw new TypeError("Mobile update registry module has no exports.");
19292
+ const registry = loaded.default ?? loaded.registry;
19293
+ if (!isRegistry(registry))
19294
+ throw new TypeError("Mobile update registry must implement publication, promotion, rollback, resolution, and file reads.");
19295
+ const metadata = serverMetadata(loaded.absoluteMobileUpdateServer);
19296
+ const verifier = loaded.verifyAbsoluteMobileUpdateServer;
19297
+ if (metadata.storage === "durable" && typeof verifier !== "function")
19298
+ throw new TypeError("Mobile update registries marked durable must export verifyAbsoluteMobileUpdateServer(). Re-run `absolute mobile update provision --storage s3 --force` or provide an active durability check.");
19299
+ return {
19300
+ metadata,
19301
+ registry,
19302
+ ...typeof verifier === "function" ? {
19303
+ verifyDurability: async () => {
19304
+ await verifier();
19305
+ }
19306
+ } : {}
19307
+ };
19308
+ };
19309
+ var verifyDurableModule = async (module) => {
19310
+ if (module.metadata.storage !== "durable")
19311
+ throw new TypeError("Mobile production updates require durable object storage. Re-run `absolute mobile update provision --storage s3 --force` or configure a durable adapter.");
19312
+ try {
19313
+ await module.verifyDurability?.();
19314
+ } catch (error) {
19315
+ 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 });
19316
+ }
19317
+ };
19318
+ var expoSigningOptions = (config) => {
19319
+ if (!config.updates?.expoCodeSigning)
19320
+ return;
19321
+ const entries = Object.entries(config.updateServer?.expoCodeSigningKeys ?? {});
19322
+ const keys = Object.fromEntries(entries.map(([keyId, key]) => {
19323
+ const privateKey = process.env[key.privateKeyEnv];
19324
+ if (!privateKey)
19325
+ throw new TypeError(`Expo update serving requires ${key.privateKeyEnv} on the trusted server.`);
19326
+ try {
19327
+ const certificate = new X509Certificate(key.certificatePem);
19328
+ const expected = certificate.publicKey.export({
19329
+ format: "der",
19330
+ type: "spki"
19331
+ });
19332
+ const actual = createPublicKey(createPrivateKey(privateKey)).export({
19333
+ format: "der",
19334
+ type: "spki"
19335
+ });
19336
+ if (!expected.equals(actual))
19337
+ throw new Error("key mismatch");
19338
+ } catch (error) {
19339
+ throw new TypeError(`${key.privateKeyEnv} must contain the RSA private key matching Expo update key ${keyId}.`, { cause: error });
19340
+ }
19341
+ return [keyId, { certificate: key.certificatePem, privateKey }];
19342
+ }));
19343
+ return { keys };
19344
+ };
19345
+ var inspectAbsoluteMobileUpdateServer = async (config, projectRoot) => {
19346
+ if (!config.updates)
19347
+ return;
19348
+ const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
19349
+ await verifyDurableModule(module);
19350
+ if (config.engine === "expo")
19351
+ expoSigningOptions(config);
19352
+ return module.metadata;
19353
+ };
19354
+ var publicKeysSource = (publicKeys) => JSON.stringify(publicKeys, null, "\t");
19355
+ var renderAbsoluteMobileUpdateRegistry = (options) => {
19356
+ const metadata = `export const absoluteMobileUpdateServer = {
19357
+ format: 1,
19358
+ provider: '${options.storage}',
19359
+ storage: '${options.storage === "local" ? "local" : "durable"}'
19360
+ } as const;`;
19361
+ if (options.storage === "local")
19362
+ return `import { fileURLToPath } from 'node:url';
19363
+ import { localBlobStore } from '@absolutejs/blob/local';
19364
+ import { createMobileUpdateRegistry } from '@absolutejs/deploy/mobile-update';
19365
+
19366
+ ${metadata}
19367
+
19368
+ const store = localBlobStore({
19369
+ root: process.env.ABSOLUTE_MOBILE_UPDATE_LOCAL_ROOT ??
19370
+ fileURLToPath(new URL('./.absolutejs/mobile/update-registry/', import.meta.url))
19371
+ });
19372
+
19373
+ export default createMobileUpdateRegistry({
19374
+ publicKeys: ${publicKeysSource(options.publicKeys)},
19375
+ store
19376
+ });
19377
+ `;
19378
+ return `import { randomUUID } from 'node:crypto';
19379
+ import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
19380
+ import { awsS3BlobStore } from '@absolutejs/blob/aws-s3';
19381
+ import { createMobileUpdateRegistry } from '@absolutejs/deploy/mobile-update';
19382
+
19383
+ ${metadata}
19384
+
19385
+ const required = (name: string) => {
19386
+ const value = process.env[name];
19387
+ if (!value) throw new Error(\`Missing \${name}\`);
19388
+ return value;
19389
+ };
19390
+
19391
+ const bucket = required('ABSOLUTE_MOBILE_UPDATE_S3_BUCKET');
19392
+ const client = new S3Client({
19393
+ region: process.env.ABSOLUTE_MOBILE_UPDATE_S3_REGION ?? 'auto',
19394
+ forcePathStyle: process.env.ABSOLUTE_MOBILE_UPDATE_S3_FORCE_PATH_STYLE === '1',
19395
+ ...(process.env.ABSOLUTE_MOBILE_UPDATE_S3_ENDPOINT
19396
+ ? { endpoint: process.env.ABSOLUTE_MOBILE_UPDATE_S3_ENDPOINT }
19397
+ : {})
19398
+ });
19399
+ const store = awsS3BlobStore({ bucket, client });
19400
+
19401
+ export const verifyAbsoluteMobileUpdateServer = async () => {
19402
+ const key = \`absolutejs/mobile-updates/_health/\${randomUUID()}\`;
19403
+ const expected = randomUUID();
19404
+ let stored = false;
19405
+ try {
19406
+ await client.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: expected }));
19407
+ stored = true;
19408
+ const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
19409
+ if ((await response.Body?.transformToString()) !== expected)
19410
+ throw new Error('Durability probe read did not match its write.');
19411
+ } finally {
19412
+ if (stored) await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
19413
+ }
19414
+ };
19415
+
19416
+ export default createMobileUpdateRegistry({
19417
+ publicKeys: ${publicKeysSource(options.publicKeys)},
19418
+ store
19419
+ });
19420
+ `;
19421
+ };
19422
+ var writeAbsoluteMobileUpdateRegistry = async (options) => {
19423
+ const path = projectPath(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
19424
+ if (!options.force) {
19425
+ await access3(path).then(() => {
19426
+ throw new TypeError(`Mobile update registry already exists: ${path}. Pass --force to replace it.`);
19427
+ }, () => {
19428
+ return;
19429
+ });
19430
+ }
19431
+ await mkdir5(dirname4(path), { recursive: true });
19432
+ await Bun.write(path, renderAbsoluteMobileUpdateRegistry({
19433
+ publicKeys: options.publicKeys,
19434
+ storage: options.storage
19435
+ }));
19436
+ return path;
19437
+ };
19438
+
19243
19439
  // src/mobile/releaseDoctor.ts
19244
19440
  var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1;
19245
19441
  var HMR_ASSET_PATTERN = /(?:__HMR_WS__|hmr-timing|__absolute_target|absolutejs-error-overlay)/u;
@@ -19262,7 +19458,7 @@ var MANUAL_REVIEW = [
19262
19458
  ];
19263
19459
  var pathExists2 = async (path) => {
19264
19460
  try {
19265
- await access3(path);
19461
+ await access4(path);
19266
19462
  return true;
19267
19463
  } catch {
19268
19464
  return false;
@@ -19298,6 +19494,16 @@ var warn = (id, detail, path, remediation) => ({
19298
19494
  remediation,
19299
19495
  status: "warn"
19300
19496
  });
19497
+ var mobileUpdateServerCheck = async (config, projectRoot) => {
19498
+ if (!config.updates)
19499
+ return;
19500
+ try {
19501
+ const metadata = await inspectAbsoluteMobileUpdateServer(config, projectRoot);
19502
+ return pass("updates.trusted-server", `The trusted update server proved durable ${metadata?.provider ?? "object"} storage read/write/delete access, supports publish/promote/rollback, and has valid server-only signing material.`, config.updateServer?.registryModule);
19503
+ } catch (error) {
19504
+ return fail("updates.trusted-server", error instanceof Error ? error.message : "The trusted mobile update server is not release-ready.", config.updateServer?.registryModule ?? "mobile.update.ts", "Run `absolute mobile update provision --storage s3 --force`, provision its environment variables on the trusted server, and rerun the release doctor.");
19505
+ }
19506
+ };
19301
19507
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19302
19508
  var readJsonObject = async (path) => {
19303
19509
  const value = JSON.parse(await readFile8(path, "utf8"));
@@ -19445,7 +19651,7 @@ var manifestReleaseCheck = async (manifestPath) => {
19445
19651
  const source = await readFile8(manifestPath, "utf8");
19446
19652
  const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
19447
19653
  const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
19448
- const networkConfigPath = networkConfigName ? join7(dirname4(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
19654
+ const networkConfigPath = networkConfigName ? join7(dirname5(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
19449
19655
  const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
19450
19656
  const developmentTrustContents = networkConfigPath ? await readFile8(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
19451
19657
  const developmentTrust = developmentTrustReference || developmentTrustContents;
@@ -19657,7 +19863,7 @@ var nativeObservabilityProjectionCheck = async (config, platform) => {
19657
19863
  return registration && plugin ? pass("android.native-observability", "The Android process-exit collector is projected and registered.", plugin) : fail("android.native-observability", "The Android process-exit collector is missing or unregistered.", plugin ?? sourceRoot, "Run `absolute mobile sync android` before building the release.");
19658
19864
  };
19659
19865
  var iosNativeSecurityCheck = async (iosRoot) => {
19660
- const applicationFiles = async (extensions) => (await sourceFiles(iosRoot, extensions)).filter((path) => !relative2(iosRoot, path).split(/[\\/]/u).some((part) => ["Pods", "DerivedData", "build"].includes(part)));
19866
+ const applicationFiles = async (extensions) => (await sourceFiles(iosRoot, extensions)).filter((path) => !relative3(iosRoot, path).split(/[\\/]/u).some((part) => ["Pods", "DerivedData", "build"].includes(part)));
19661
19867
  const entitlementPaths = await applicationFiles(new Set([".entitlements"]));
19662
19868
  const unsafeEntitlements = await containsPattern(entitlementPaths, /<key>(?:com\.apple\.security\.)?get-task-allow<\/key>\s*<true\s*\/>/u);
19663
19869
  if (unsafeEntitlements)
@@ -19671,12 +19877,12 @@ var iosNativeSecurityCheck = async (iosRoot) => {
19671
19877
  var iosDeepLinkProjectionCheck = async (config, iosRoot) => {
19672
19878
  const infoPath = join7(iosRoot, "App/App/Info.plist");
19673
19879
  const entitlementsPath = join7(iosRoot, "App/AbsoluteJS.entitlements");
19674
- const projectPath = join7(iosRoot, "App/App.xcodeproj/project.pbxproj");
19880
+ const projectPath2 = join7(iosRoot, "App/App.xcodeproj/project.pbxproj");
19675
19881
  try {
19676
19882
  const [info, entitlements, project] = await Promise.all([
19677
19883
  readFile8(infoPath, "utf8"),
19678
19884
  readFile8(entitlementsPath, "utf8"),
19679
- readFile8(projectPath, "utf8")
19885
+ readFile8(projectPath2, "utf8")
19680
19886
  ]);
19681
19887
  if (config.deepLinkScheme && (!info.includes("<key>CFBundleURLTypes</key>") || !info.includes(`<string>${config.deepLinkScheme}</string>`)))
19682
19888
  throw new TypeError("iOS custom URL scheme does not match mobile config.");
@@ -19761,8 +19967,8 @@ var expoIosPushCapabilityCheck = async (iosRoot, requirements) => {
19761
19967
  };
19762
19968
  var expoIosCapabilityProjectionCheck = async (config, requirements) => {
19763
19969
  const iosRoot = join7(config.nativeProjectDirectory, "ios");
19764
- const projectPath = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
19765
- const project = await readFile8(projectPath, "utf8");
19970
+ const projectPath2 = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
19971
+ const project = await readFile8(projectPath2, "utf8");
19766
19972
  const privacy = await expoIosPrivacyCapabilityCheck(iosRoot, project, requirements);
19767
19973
  if (privacy)
19768
19974
  return privacy;
@@ -19780,10 +19986,10 @@ var iosCapabilityProjectionCheck = async (config, requirements) => {
19780
19986
  return fail("mobile.device-capabilities", "iOS system-bar capability is missing its required view-controller setting.", infoPath, "Run `absolute mobile sync ios` to regenerate native capability settings.");
19781
19987
  if (requirements.iosPrivacyAccessedApis.length > 0) {
19782
19988
  const privacyPath = join7(appRoot, "PrivacyInfo.xcprivacy");
19783
- const projectPath = join7(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
19989
+ const projectPath2 = join7(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
19784
19990
  const [privacy, project] = await Promise.all([
19785
19991
  readFile8(privacyPath, "utf8").catch(() => ""),
19786
- readFile8(projectPath, "utf8").catch(() => "")
19992
+ readFile8(projectPath2, "utf8").catch(() => "")
19787
19993
  ]);
19788
19994
  const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
19789
19995
  if (missing || !project.includes("PrivacyInfo.xcprivacy in Resources"))
@@ -19856,7 +20062,7 @@ var inspectAndroidRelease = async (config, projectRoot) => {
19856
20062
  ]);
19857
20063
  return checks.filter((check) => check !== undefined).map((check) => ({
19858
20064
  ...check,
19859
- path: check.path ? relative2(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20065
+ path: check.path ? relative3(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
19860
20066
  }));
19861
20067
  };
19862
20068
  var inspectExpoAndroidRelease = async (config, projectRoot) => {
@@ -19878,7 +20084,7 @@ var inspectExpoAndroidRelease = async (config, projectRoot) => {
19878
20084
  ]);
19879
20085
  return checks.filter((check) => check !== undefined).map((check) => ({
19880
20086
  ...check,
19881
- path: check.path ? relative2(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20087
+ path: check.path ? relative3(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
19882
20088
  }));
19883
20089
  };
19884
20090
  var uniqueExpoIosFile = async (iosRoot, pattern, label) => {
@@ -19892,7 +20098,7 @@ var uniqueExpoIosFile = async (iosRoot, pattern, label) => {
19892
20098
  };
19893
20099
  var expoIosNativeProjectionCheck = async (config, iosRoot) => {
19894
20100
  try {
19895
- const [infoPath, entitlementsPath, projectPath] = await Promise.all([
20101
+ const [infoPath, entitlementsPath, projectPath2] = await Promise.all([
19896
20102
  uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist"),
19897
20103
  uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements"),
19898
20104
  uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project")
@@ -19900,7 +20106,7 @@ var expoIosNativeProjectionCheck = async (config, iosRoot) => {
19900
20106
  const [info, entitlements, project] = await Promise.all([
19901
20107
  readFile8(infoPath, "utf8"),
19902
20108
  readFile8(entitlementsPath, "utf8"),
19903
- readFile8(projectPath, "utf8")
20109
+ readFile8(projectPath2, "utf8")
19904
20110
  ]);
19905
20111
  if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info))
19906
20112
  throw new TypeError("Expo iOS App Transport Security permits arbitrary network loads.");
@@ -19936,7 +20142,7 @@ var inspectExpoIosRelease = async (config, projectRoot) => {
19936
20142
  checks.push(nativeObservability);
19937
20143
  return checks.map((check) => ({
19938
20144
  ...check,
19939
- path: check.path ? relative2(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20145
+ path: check.path ? relative3(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
19940
20146
  }));
19941
20147
  };
19942
20148
  var inspectIosRelease = async (config, projectRoot) => {
@@ -19978,7 +20184,7 @@ var inspectIosRelease = async (config, projectRoot) => {
19978
20184
  checks.push(updateWatchdog);
19979
20185
  return checks.map((check) => ({
19980
20186
  ...check,
19981
- path: check.path ? relative2(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20187
+ path: check.path ? relative3(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
19982
20188
  }));
19983
20189
  };
19984
20190
  var createAbsoluteMobileComplianceReport = (config, result) => {
@@ -20006,11 +20212,12 @@ var inspectAbsoluteMobileRelease = async (config, projectRoot) => {
20006
20212
  Promise.resolve(productionOriginCheck(config, projectRoot)),
20007
20213
  Promise.resolve(associationIdentityCheck(config, projectRoot)),
20008
20214
  dependencyLockCheck(projectRoot),
20009
- config.engine === "expo" ? expoVersionCheck(config) : capacitorVersionCheck(config, projectRoot)
20215
+ config.engine === "expo" ? expoVersionCheck(config) : capacitorVersionCheck(config, projectRoot),
20216
+ mobileUpdateServerCheck(config, projectRoot)
20010
20217
  ]);
20011
- const checks = globalChecks.map((check) => ({
20218
+ const checks = globalChecks.filter((check) => check !== undefined).map((check) => ({
20012
20219
  ...check,
20013
- path: check.path ? relative2(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20220
+ path: check.path ? relative3(projectRoot, check.path).replaceAll("\\", "/") || "." : undefined
20014
20221
  }));
20015
20222
  if (config.platforms.includes("android"))
20016
20223
  checks.push(...config.engine === "expo" ? await inspectExpoAndroidRelease(config, projectRoot) : await inspectAndroidRelease(config, projectRoot));
@@ -20020,13 +20227,13 @@ var inspectAbsoluteMobileRelease = async (config, projectRoot) => {
20020
20227
  if (syncSchema) {
20021
20228
  checks.push({
20022
20229
  ...syncSchema,
20023
- path: syncSchema.path ? relative2(projectRoot, syncSchema.path).replaceAll("\\", "/") || "." : undefined
20230
+ path: syncSchema.path ? relative3(projectRoot, syncSchema.path).replaceAll("\\", "/") || "." : undefined
20024
20231
  });
20025
20232
  }
20026
20233
  const deviceCapabilities = await deviceCapabilityReleaseCheck(config, projectRoot);
20027
20234
  checks.push({
20028
20235
  ...deviceCapabilities,
20029
- path: deviceCapabilities.path ? relative2(projectRoot, deviceCapabilities.path).replaceAll("\\", "/") || "." : undefined
20236
+ path: deviceCapabilities.path ? relative3(projectRoot, deviceCapabilities.path).replaceAll("\\", "/") || "." : undefined
20030
20237
  });
20031
20238
  return {
20032
20239
  checks,
@@ -20037,9 +20244,9 @@ var inspectAbsoluteMobileRelease = async (config, projectRoot) => {
20037
20244
  // src/mobile/androidRelease.ts
20038
20245
  import { createHash as createHash2 } from "crypto";
20039
20246
  import {
20040
- access as access4,
20247
+ access as access5,
20041
20248
  copyFile,
20042
- mkdir as mkdir5,
20249
+ mkdir as mkdir6,
20043
20250
  mkdtemp,
20044
20251
  readFile as readFile9,
20045
20252
  realpath,
@@ -20048,7 +20255,7 @@ import {
20048
20255
  stat as stat2,
20049
20256
  writeFile as writeFile8
20050
20257
  } from "fs/promises";
20051
- import { dirname as dirname5, isAbsolute, join as join8, relative as relative3, resolve as resolve4, sep } from "path";
20258
+ import { dirname as dirname6, isAbsolute as isAbsolute2, join as join8, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
20052
20259
  var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1;
20053
20260
  var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20054
20261
  var requireManifest = (value) => {
@@ -20063,7 +20270,7 @@ var requireManifest = (value) => {
20063
20270
  };
20064
20271
  var pathExists3 = async (path) => {
20065
20272
  try {
20066
- await access4(path);
20273
+ await access5(path);
20067
20274
  return true;
20068
20275
  } catch {
20069
20276
  return false;
@@ -20133,10 +20340,10 @@ var fingerprintExpoAndroidProject = async (nativeDirectory) => {
20133
20340
  return createHash2("sha256").update(records.join("")).digest("hex");
20134
20341
  };
20135
20342
  var safeOutputDirectory = (projectRoot, requested) => {
20136
- const root = resolve4(projectRoot);
20137
- const output = resolve4(root, requested ?? ".absolutejs/mobile/releases/android");
20138
- const projectRelative = relative3(root, output);
20139
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep}`) || isAbsolute(projectRelative)) {
20343
+ const root = resolve5(projectRoot);
20344
+ const output = resolve5(root, requested ?? ".absolutejs/mobile/releases/android");
20345
+ const projectRelative = relative4(root, output);
20346
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
20140
20347
  throw new TypeError("mobile build --outdir must remain inside the project.");
20141
20348
  }
20142
20349
  return output;
@@ -20156,8 +20363,8 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
20156
20363
  }
20157
20364
  return { artifactPath: destination, metadata: existing, releaseRoot };
20158
20365
  }
20159
- await mkdir5(dirname5(releaseRoot), { recursive: true });
20160
- const staging = await mkdtemp(join8(dirname5(releaseRoot), ".android-stage-"));
20366
+ await mkdir6(dirname6(releaseRoot), { recursive: true });
20367
+ const staging = await mkdtemp(join8(dirname6(releaseRoot), ".android-stage-"));
20161
20368
  try {
20162
20369
  await copyFile(artifactPath, join8(staging, artifactName));
20163
20370
  const complete = {
@@ -20191,7 +20398,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
20191
20398
  if (options.versionCode !== undefined && (!Number.isSafeInteger(options.versionCode) || options.versionCode < 1 || options.versionCode > 2100000000)) {
20192
20399
  throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
20193
20400
  }
20194
- const projectRoot = resolve4(options.projectRoot);
20401
+ const projectRoot = resolve5(options.projectRoot);
20195
20402
  const host = options.host ?? detectAbsoluteMobileHost();
20196
20403
  if (options.config.engine === "expo" && host === "wsl") {
20197
20404
  throw new TypeError("Expo Android production builds from WSL are not available yet. Run the generated CI workflow on Linux or build from native Windows while the WSL projection is completed.");
@@ -20369,7 +20576,7 @@ var waitForAbsoluteIosHmrLog = async (options) => {
20369
20576
  };
20370
20577
 
20371
20578
  // src/mobile/nativeTestReport.ts
20372
- import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
20579
+ import { mkdir as mkdir7, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
20373
20580
  import { join as join9 } from "path";
20374
20581
  var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
20375
20582
  var bearerPattern = /bearer\s+[^\s,;]+/giu;
@@ -20518,7 +20725,7 @@ ${table(report.manualChecks)}
20518
20725
  `;
20519
20726
  };
20520
20727
  var writeAbsoluteNativeTestReport = async (directory, report) => {
20521
- await mkdir6(directory, { recursive: true });
20728
+ await mkdir7(directory, { recursive: true });
20522
20729
  const jsonPath = join9(directory, "report.json");
20523
20730
  const markdownPath = join9(directory, "report.md");
20524
20731
  await Promise.all([
@@ -20774,9 +20981,9 @@ var createAbsoluteAndroidTestReport = (options) => {
20774
20981
  };
20775
20982
 
20776
20983
  // src/mobile/releasePublisher.ts
20777
- import { access as access5 } from "fs/promises";
20778
- import { isAbsolute as isAbsolute2, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
20779
- import { pathToFileURL } from "url";
20984
+ import { access as access6 } from "fs/promises";
20985
+ import { isAbsolute as isAbsolute3, relative as relative5, resolve as resolve6, sep as sep3 } from "path";
20986
+ import { pathToFileURL as pathToFileURL2 } from "url";
20780
20987
  var prepareAbsoluteIosRelease = async (publisher, options) => {
20781
20988
  if (typeof publisher.prepareIosRelease !== "function") {
20782
20989
  throw new TypeError("App Store Connect publishing requires a registry module created with @absolutejs/deploy/app-store-connect.");
@@ -20801,20 +21008,20 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
20801
21008
  var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20802
21009
  var isPublisher = (value) => isRecord5(value) && typeof value.publish === "function";
20803
21010
  var publisherModulePath = (projectRoot, requested) => {
20804
- const root = resolve5(projectRoot);
20805
- const path = resolve5(root, requested);
20806
- const projectRelative = relative4(root, path);
20807
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
21011
+ const root = resolve6(projectRoot);
21012
+ const path = resolve6(root, requested);
21013
+ const projectRelative = relative5(root, path);
21014
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep3}`) || isAbsolute3(projectRelative)) {
20808
21015
  throw new TypeError("mobile publish --registry must remain inside the project.");
20809
21016
  }
20810
21017
  return path;
20811
21018
  };
20812
21019
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
20813
21020
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
20814
- await access5(modulePath).catch(() => {
21021
+ await access6(modulePath).catch(() => {
20815
21022
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
20816
21023
  });
20817
- const loaded = await import(pathToFileURL(modulePath).href);
21024
+ const loaded = await import(pathToFileURL2(modulePath).href);
20818
21025
  const publisher = isRecord5(loaded) ? loaded.default ?? loaded.registry : undefined;
20819
21026
  if (!isPublisher(publisher)) {
20820
21027
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
@@ -20873,8 +21080,8 @@ var publishAbsoluteIosRelease = async (options) => {
20873
21080
  };
20874
21081
 
20875
21082
  // src/mobile/mobileInspect.ts
20876
- import { access as access6, readFile as readFile12 } from "fs/promises";
20877
- import { join as join10, relative as relative5, resolve as resolve6 } from "path";
21083
+ import { access as access7, readFile as readFile12 } from "fs/promises";
21084
+ import { join as join10, relative as relative6, resolve as resolve7 } from "path";
20878
21085
  var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
20879
21086
  var MOBILE_PACKAGE_NAMES = new Set([
20880
21087
  "@absolutejs/absolute",
@@ -20889,12 +21096,12 @@ var MOBILE_PACKAGE_NAMES = new Set([
20889
21096
  ]);
20890
21097
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20891
21098
  var portablePath2 = (projectRoot, path) => {
20892
- const value = relative5(resolve6(projectRoot), resolve6(path)).replaceAll("\\", "/");
21099
+ const value = relative6(resolve7(projectRoot), resolve7(path)).replaceAll("\\", "/");
20893
21100
  return value || ".";
20894
21101
  };
20895
21102
  var pathExists4 = async (path) => {
20896
21103
  try {
20897
- await access6(path);
21104
+ await access7(path);
20898
21105
  return true;
20899
21106
  } catch {
20900
21107
  return false;
@@ -21035,8 +21242,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
21035
21242
 
21036
21243
  // src/mobile/ciWorkflow.ts
21037
21244
  import { existsSync } from "fs";
21038
- import { access as access7, mkdir as mkdir7, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
21039
- import { dirname as dirname6, extname as extname2, relative as relative6, resolve as resolve7, sep as sep3 } from "path";
21245
+ import { access as access8, mkdir as mkdir8, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
21246
+ import { dirname as dirname7, extname as extname2, relative as relative7, resolve as resolve8, sep as sep4 } from "path";
21040
21247
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
21041
21248
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
21042
21249
  var CI_ENV_INDENTATION = 6;
@@ -21057,18 +21264,18 @@ var RESERVED_SECRET_NAMES = new Set([
21057
21264
  ]);
21058
21265
  var exists2 = async (path) => {
21059
21266
  try {
21060
- await access7(path);
21267
+ await access8(path);
21061
21268
  return true;
21062
21269
  } catch {
21063
21270
  return false;
21064
21271
  }
21065
21272
  };
21066
21273
  var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
21067
- var projectPath = (projectRoot, value, field, options = {}) => {
21068
- const root = resolve7(projectRoot);
21069
- const path = resolve7(root, value);
21070
- const portable = relative6(root, path).replaceAll("\\", "/");
21071
- if (portable === ".." || portable.startsWith(`..${sep3}`) || portable.startsWith("../") || portable === "") {
21274
+ var projectPath2 = (projectRoot, value, field, options = {}) => {
21275
+ const root = resolve8(projectRoot);
21276
+ const path = resolve8(root, value);
21277
+ const portable = relative7(root, path).replaceAll("\\", "/");
21278
+ if (portable === ".." || portable.startsWith(`..${sep4}`) || portable.startsWith("../") || portable === "") {
21072
21279
  throw new TypeError(`${field} must remain inside the project root.`);
21073
21280
  }
21074
21281
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -21078,11 +21285,11 @@ var projectPath = (projectRoot, value, field, options = {}) => {
21078
21285
  return portable;
21079
21286
  };
21080
21287
  var workflowOutputPath = (projectRoot, value) => {
21081
- const root = resolve7(projectRoot);
21082
- const workflows = resolve7(root, ".github/workflows");
21083
- const path = resolve7(root, value ?? ".github/workflows/absolute-mobile.yml");
21084
- const portable = relative6(workflows, path);
21085
- if (portable === ".." || portable.startsWith(`..${sep3}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
21288
+ const root = resolve8(projectRoot);
21289
+ const workflows = resolve8(root, ".github/workflows");
21290
+ const path = resolve8(root, value ?? ".github/workflows/absolute-mobile.yml");
21291
+ const portable = relative7(workflows, path);
21292
+ if (portable === ".." || portable.startsWith(`..${sep4}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
21086
21293
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
21087
21294
  }
21088
21295
  return path;
@@ -21449,9 +21656,9 @@ var createAbsoluteMobileGithubWorkflow = (options) => {
21449
21656
  ].sort();
21450
21657
  const includePublishing = options.includePublishing === true;
21451
21658
  const customSecrets = normalizeSecretEnvironment(options.secretEnvironment);
21452
- const serverEntry = projectPath(options.projectRoot, options.serverEntry ?? "server.ts", "mobile ci github server entry");
21453
- const configPath = options.configPath ? projectPath(options.projectRoot, options.configPath, "mobile ci github --config") : undefined;
21454
- const registryModule = projectPath(options.projectRoot, options.registryModule ?? "mobile.release.ts", "mobile ci github --registry", { allowMissing: !includePublishing });
21659
+ const serverEntry = projectPath2(options.projectRoot, options.serverEntry ?? "server.ts", "mobile ci github server entry");
21660
+ const configPath = options.configPath ? projectPath2(options.projectRoot, options.configPath, "mobile ci github --config") : undefined;
21661
+ const registryModule = projectPath2(options.projectRoot, options.registryModule ?? "mobile.release.ts", "mobile ci github --registry", { allowMissing: !includePublishing });
21455
21662
  const environment = commandEnvironment({
21456
21663
  configPath,
21457
21664
  registryModule,
@@ -21513,9 +21720,9 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
21513
21720
  const generated = createAbsoluteMobileGithubWorkflow(options);
21514
21721
  const previous = await exists2(path) ? await readFile13(path, "utf8") : undefined;
21515
21722
  if (previous !== undefined && previous !== generated.workflow && !options.force)
21516
- throw new TypeError(`${relative6(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
21723
+ throw new TypeError(`${relative7(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
21517
21724
  if (previous !== generated.workflow) {
21518
- await mkdir7(dirname6(path), { recursive: true });
21725
+ await mkdir8(dirname7(path), { recursive: true });
21519
21726
  await writeFile10(path, generated.workflow);
21520
21727
  }
21521
21728
  return {
@@ -21532,7 +21739,7 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
21532
21739
  import { createHash as createHash3, sign, verify } from "crypto";
21533
21740
  import {
21534
21741
  cp,
21535
- mkdir as mkdir8,
21742
+ mkdir as mkdir9,
21536
21743
  mkdtemp as mkdtemp2,
21537
21744
  readdir as readdir4,
21538
21745
  readFile as readFile14,
@@ -21541,7 +21748,7 @@ import {
21541
21748
  stat as stat4,
21542
21749
  writeFile as writeFile11
21543
21750
  } from "fs/promises";
21544
- import { dirname as dirname7, join as join11, relative as relative7, resolve as resolve8 } from "path";
21751
+ import { dirname as dirname8, join as join11, relative as relative8, resolve as resolve9 } from "path";
21545
21752
  var UPDATE_MANIFEST_FILE = "update.json";
21546
21753
  var UPDATE_FILES_DIRECTORY = "files";
21547
21754
  var sha256 = (value) => createHash3("sha256").update(value).digest("hex");
@@ -21553,7 +21760,7 @@ var listFiles = async (root, directory = root) => {
21553
21760
  return listFiles(root, path);
21554
21761
  if (!entry.isFile())
21555
21762
  throw new TypeError("Mobile updates cannot contain links or special files.");
21556
- return [relative7(root, path).replaceAll("\\", "/")];
21763
+ return [relative8(root, path).replaceAll("\\", "/")];
21557
21764
  }));
21558
21765
  return paths.flat().sort((left, right) => left.localeCompare(right));
21559
21766
  };
@@ -21567,8 +21774,8 @@ var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) =>
21567
21774
  }));
21568
21775
  var releaseIdFor = (value) => `amu_${sha256(canonicalizeAbsoluteMobileUpdate(value))}`;
21569
21776
  var buildAbsoluteMobileUpdate = async (options) => {
21570
- const bundleDirectory = resolve8(options.bundleDirectory);
21571
- const outputRoot = resolve8(options.outputDirectory);
21777
+ const bundleDirectory = resolve9(options.bundleDirectory);
21778
+ const outputRoot = resolve9(options.outputDirectory);
21572
21779
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
21573
21780
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
21574
21781
  const paths = await listFiles(bundleDirectory);
@@ -21596,7 +21803,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
21596
21803
  value: signature.toString("base64")
21597
21804
  }
21598
21805
  });
21599
- await mkdir8(outputRoot, { recursive: true });
21806
+ await mkdir9(outputRoot, { recursive: true });
21600
21807
  const outputDirectory = join11(outputRoot, manifest.releaseId);
21601
21808
  const staging = await mkdtemp2(join11(outputRoot, ".stage-"));
21602
21809
  try {
@@ -21619,7 +21826,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
21619
21826
  outputDirectory
21620
21827
  };
21621
21828
  };
21622
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile14(join11(resolve8(directory), UPDATE_MANIFEST_FILE), "utf8")));
21829
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile14(join11(resolve9(directory), UPDATE_MANIFEST_FILE), "utf8")));
21623
21830
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
21624
21831
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
21625
21832
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -21629,11 +21836,11 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
21629
21836
  };
21630
21837
 
21631
21838
  // src/mobile/expoUpdate.ts
21632
- import { access as access8, mkdir as mkdir9, readFile as readFile15, writeFile as writeFile12 } from "fs/promises";
21633
- import { dirname as dirname8, extname as extname3, join as join12 } from "path";
21839
+ import { access as access9, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile12 } from "fs/promises";
21840
+ import { dirname as dirname9, extname as extname3, join as join12 } from "path";
21634
21841
  var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
21635
21842
  var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
21636
- var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21843
+ var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21637
21844
  var safePath = (value, field) => {
21638
21845
  if (typeof value !== "string" || value.length === 0)
21639
21846
  throw new TypeError(`Expo update ${field} is invalid.`);
@@ -21652,7 +21859,7 @@ var extension = (value, path) => {
21652
21859
  return value;
21653
21860
  };
21654
21861
  var asset = (value) => {
21655
- if (!object(value))
21862
+ if (!object2(value))
21656
21863
  throw new TypeError("Expo update asset is invalid.");
21657
21864
  const path = safePath(value.path, "asset path");
21658
21865
  const normalizedExtension = extension(value.extension ?? value.ext, path);
@@ -21662,9 +21869,9 @@ var asset = (value) => {
21662
21869
  };
21663
21870
  };
21664
21871
  var platform = (value) => {
21665
- if (!object(value) || !Array.isArray(value.assets))
21872
+ if (!object2(value) || !Array.isArray(value.assets))
21666
21873
  throw new TypeError("Expo update platform metadata is invalid.");
21667
- const launchValue = object(value.launchAsset) ? value.launchAsset : { path: value.bundle };
21874
+ const launchValue = object2(value.launchAsset) ? value.launchAsset : { path: value.bundle };
21668
21875
  const launchPath = safePath(launchValue.path, "launch bundle");
21669
21876
  const launchExtension = extension(launchValue.extension ?? null, launchPath);
21670
21877
  const assets = value.assets.map(asset);
@@ -21677,7 +21884,7 @@ var platform = (value) => {
21677
21884
  };
21678
21885
  };
21679
21886
  var parseAbsoluteExpoUpdateDescriptor = (value) => {
21680
- if (!object(value) || value.engine !== "expo" || value.format !== ABSOLUTE_EXPO_UPDATE_FORMAT || !object(value.expoConfig) || !object(value.platforms) || typeof value.runtimeVersion !== "string" || !/^[a-f0-9]{64}$/u.test(value.runtimeVersion))
21887
+ if (!object2(value) || value.engine !== "expo" || value.format !== ABSOLUTE_EXPO_UPDATE_FORMAT || !object2(value.expoConfig) || !object2(value.platforms) || typeof value.runtimeVersion !== "string" || !/^[a-f0-9]{64}$/u.test(value.runtimeVersion))
21681
21888
  throw new TypeError("AbsoluteJS Expo update descriptor is invalid.");
21682
21889
  const platforms = {};
21683
21890
  for (const name of ["android", "ios"]) {
@@ -21697,7 +21904,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
21697
21904
  };
21698
21905
  var finalizeAbsoluteExpoUpdateExport = async (options) => {
21699
21906
  const metadata = JSON.parse(await readFile15(join12(options.exportDirectory, "metadata.json"), "utf8"));
21700
- if (!object(metadata) || !object(metadata.fileMetadata))
21907
+ if (!object2(metadata) || !object2(metadata.fileMetadata))
21701
21908
  throw new TypeError("Expo export metadata.json is invalid.");
21702
21909
  const descriptor = parseAbsoluteExpoUpdateDescriptor({
21703
21910
  engine: "expo",
@@ -21707,11 +21914,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
21707
21914
  runtimeVersion: options.runtimeVersion
21708
21915
  });
21709
21916
  const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
21710
- await Promise.all(referenced.map(({ path }) => access8(join12(options.exportDirectory, path)).catch(() => {
21917
+ await Promise.all(referenced.map(({ path }) => access9(join12(options.exportDirectory, path)).catch(() => {
21711
21918
  throw new TypeError(`Expo export metadata references missing asset ${path}.`);
21712
21919
  })));
21713
21920
  const destination = join12(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
21714
- await mkdir9(dirname8(destination), { recursive: true });
21921
+ await mkdir10(dirname9(destination), { recursive: true });
21715
21922
  await writeFile12(destination, `${JSON.stringify(descriptor, null, "\t")}
21716
21923
  `);
21717
21924
  return { descriptor, path: destination };
@@ -21719,18 +21926,18 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
21719
21926
 
21720
21927
  // src/mobile/expoCodeSigning.ts
21721
21928
  var import_code_signing_certificates = __toESM(require_main(), 1);
21722
- import { access as access9, mkdir as mkdir10, writeFile as writeFile13 } from "fs/promises";
21723
- import { dirname as dirname9, isAbsolute as isAbsolute3, relative as relative8, resolve as resolve9, sep as sep4 } from "path";
21724
- var exists3 = async (path) => access9(path).then(() => true).catch(() => false);
21929
+ import { access as access10, mkdir as mkdir11, writeFile as writeFile13 } from "fs/promises";
21930
+ import { dirname as dirname10, isAbsolute as isAbsolute4, relative as relative9, resolve as resolve10, sep as sep5 } from "path";
21931
+ var exists3 = async (path) => access10(path).then(() => true).catch(() => false);
21725
21932
  var inside = (root, path) => {
21726
- const location = relative8(root, path);
21727
- return location === "" || location !== ".." && !location.startsWith(`..${sep4}`) && !isAbsolute3(location);
21933
+ const location = relative9(root, path);
21934
+ return location === "" || location !== ".." && !location.startsWith(`..${sep5}`) && !isAbsolute4(location);
21728
21935
  };
21729
21936
  var generateAbsoluteExpoCodeSigning = async (options) => {
21730
- const root = resolve9(options.projectRoot);
21731
- const certificatePath = resolve9(root, options.certificatePath);
21732
- const privateKeyPath = resolve9(root, options.privateKeyPath);
21733
- const publicKeyPath = resolve9(root, options.publicKeyPath ?? resolve9(dirname9(privateKeyPath), "public-key.pem"));
21937
+ const root = resolve10(options.projectRoot);
21938
+ const certificatePath = resolve10(root, options.certificatePath);
21939
+ const privateKeyPath = resolve10(root, options.privateKeyPath);
21940
+ const publicKeyPath = resolve10(root, options.publicKeyPath ?? resolve10(dirname10(privateKeyPath), "public-key.pem"));
21734
21941
  if (!inside(root, certificatePath))
21735
21942
  throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
21736
21943
  if (inside(root, privateKeyPath) || inside(root, publicKeyPath))
@@ -21760,7 +21967,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
21760
21967
  import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
21761
21968
  const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
21762
21969
  const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
21763
- await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir10(dirname9(path), { recursive: true })));
21970
+ await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir11(dirname10(path), { recursive: true })));
21764
21971
  await Promise.all([
21765
21972
  writeFile13(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
21766
21973
  writeFile13(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
@@ -21775,26 +21982,26 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
21775
21982
  };
21776
21983
 
21777
21984
  // src/mobile/updatePublisher.ts
21778
- import { access as access10 } from "fs/promises";
21779
- import { isAbsolute as isAbsolute4, relative as relative9, resolve as resolve10, sep as sep5 } from "path";
21780
- import { pathToFileURL as pathToFileURL2 } from "url";
21781
- var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21782
- var isPublisher2 = (value) => object2(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
21783
- var projectPath2 = (projectRoot, requested, label) => {
21784
- const root = resolve10(projectRoot);
21785
- const path = resolve10(root, requested);
21786
- const projectRelative = relative9(root, path);
21787
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute4(projectRelative))
21985
+ import { access as access11 } from "fs/promises";
21986
+ import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
21987
+ import { pathToFileURL as pathToFileURL3 } from "url";
21988
+ var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21989
+ var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
21990
+ var projectPath3 = (projectRoot, requested, label) => {
21991
+ const root = resolve11(projectRoot);
21992
+ const path = resolve11(root, requested);
21993
+ const projectRelative = relative10(root, path);
21994
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute5(projectRelative))
21788
21995
  throw new TypeError(`${label} must remain inside the project.`);
21789
21996
  return path;
21790
21997
  };
21791
21998
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
21792
- const modulePath = projectPath2(projectRoot, requestedModulePath, "mobile update registry");
21793
- await access10(modulePath).catch(() => {
21999
+ const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
22000
+ await access11(modulePath).catch(() => {
21794
22001
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
21795
22002
  });
21796
- const loaded = await import(pathToFileURL2(modulePath).href);
21797
- const publisher = object2(loaded) ? loaded.default ?? loaded.registry : undefined;
22003
+ const loaded = await import(pathToFileURL3(modulePath).href);
22004
+ const publisher = object3(loaded) ? loaded.default ?? loaded.registry : undefined;
21798
22005
  if (!isPublisher2(publisher))
21799
22006
  throw new TypeError("Mobile update registry must implement publishUpdate, promoteUpdate, and rollbackUpdate.");
21800
22007
  return publisher;
@@ -21812,7 +22019,7 @@ var promoteAbsoluteMobileUpdate = async (options) => {
21812
22019
  return result;
21813
22020
  };
21814
22021
  var publishAbsoluteMobileUpdate = async (options) => {
21815
- const releaseDirectory = projectPath2(options.projectRoot, options.releaseDirectory, "mobile update release directory");
22022
+ const releaseDirectory = projectPath3(options.projectRoot, options.releaseDirectory, "mobile update release directory");
21816
22023
  const manifest = await readAbsoluteMobileUpdate(releaseDirectory);
21817
22024
  const result = await options.publisher.publishUpdate({
21818
22025
  manifest,
@@ -21948,7 +22155,7 @@ var requireMobileConfig = (value) => {
21948
22155
  var capacitorExecutable = async (projectRoot) => {
21949
22156
  const executable = join13(projectRoot, "node_modules", ".bin", "cap");
21950
22157
  try {
21951
- await access11(executable);
22158
+ await access12(executable);
21952
22159
  return executable;
21953
22160
  } catch {
21954
22161
  throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
@@ -21971,7 +22178,7 @@ var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.re
21971
22178
  var expoExecutable = async (project) => {
21972
22179
  const executable = join13(project, "node_modules", ".bin", "expo");
21973
22180
  try {
21974
- await access11(executable);
22181
+ await access12(executable);
21975
22182
  return executable;
21976
22183
  } catch {
21977
22184
  throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
@@ -22206,7 +22413,7 @@ var sync = async (args) => {
22206
22413
  };
22207
22414
  var associations = async (args) => {
22208
22415
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
22209
- const outputDirectory = resolve11(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
22416
+ const outputDirectory = resolve12(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
22210
22417
  if (args.includes("--verify")) {
22211
22418
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
22212
22419
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -22258,7 +22465,7 @@ var generateGithubCi = async (args) => {
22258
22465
  const publicResult = {
22259
22466
  changed: result.changed,
22260
22467
  format: result.format,
22261
- path: relative10(projectRoot, result.path).replaceAll("\\", "/"),
22468
+ path: relative11(projectRoot, result.path).replaceAll("\\", "/"),
22262
22469
  platforms: result.platforms,
22263
22470
  publishing: result.publishing,
22264
22471
  requiredSecrets: result.requiredSecrets
@@ -22370,7 +22577,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
22370
22577
  };
22371
22578
  await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
22372
22579
  const temporaryParent = join13(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
22373
- await mkdir11(temporaryParent, { recursive: true });
22580
+ await mkdir12(temporaryParent, { recursive: true });
22374
22581
  const temporaryDirectory = await mkdtemp3(join13(temporaryParent, ".stage-"));
22375
22582
  try {
22376
22583
  await runExpo(mobile.nativeProjectDirectory, [
@@ -22430,9 +22637,9 @@ var buildMobileUpdate = async (args) => {
22430
22637
  const runtimeFingerprint = isRecord6(embedded) ? embedded.nativeRuntime : undefined;
22431
22638
  if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
22432
22639
  throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
22433
- const privateKey = await readFile16(resolve11(projectRoot, signingKeyPath));
22640
+ const privateKey = await readFile16(resolve12(projectRoot, signingKeyPath));
22434
22641
  const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
22435
- const derivedPublicKey = createPublicKey(privateKey).export({
22642
+ const derivedPublicKey = createPublicKey2(privateKey).export({
22436
22643
  format: "der",
22437
22644
  type: "spki"
22438
22645
  });
@@ -22493,7 +22700,7 @@ var generateExpoUpdateSigning = async (args) => {
22493
22700
  ...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
22494
22701
  ...validityYears === undefined ? {} : { validityYears }
22495
22702
  });
22496
- const certificatePath = relative10(projectRoot, result.certificatePath).replaceAll("\\", "/");
22703
+ const certificatePath = relative11(projectRoot, result.certificatePath).replaceAll("\\", "/");
22497
22704
  sendTelemetryEvent("mobile:update-code-signing-generated", {
22498
22705
  engine: "expo",
22499
22706
  validityYears: validityYears ?? 10
@@ -22516,13 +22723,56 @@ var updateRollout = (args, fallback) => {
22516
22723
  return rollout;
22517
22724
  };
22518
22725
  var mobileUpdatePublisher = async (args) => {
22519
- const { projectRoot } = await loadMobile(valueAfter(args, "--config"));
22520
- const modulePath = valueAfter(args, "--registry") ?? "mobile.release.ts";
22726
+ const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
22727
+ if (!mobile.updates || !mobile.updateServer)
22728
+ throw new TypeError("Mobile updates are not configured.");
22729
+ const modulePath = valueAfter(args, "--registry") ?? mobile.updateServer.registryModule;
22521
22730
  return {
22522
22731
  projectRoot,
22523
22732
  publisher: await loadAbsoluteMobileUpdatePublisher(projectRoot, modulePath)
22524
22733
  };
22525
22734
  };
22735
+ var provisionMobileUpdate = async (args) => {
22736
+ const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
22737
+ if (!mobile.updates || !mobile.updateServer)
22738
+ throw new TypeError("mobile update provision requires mobile.updates.publicKeys in absolute.config.ts.");
22739
+ const requestedStorage = valueAfter(args, "--storage") ?? "local";
22740
+ if (requestedStorage !== "local" && requestedStorage !== "s3")
22741
+ throw new TypeError("--storage must be local or s3.");
22742
+ const modulePath = valueAfter(args, "--registry") ?? mobile.updateServer.registryModule;
22743
+ const packages = [
22744
+ "@absolutejs/deploy@0.25.6",
22745
+ "@absolutejs/blob@0.5.2",
22746
+ ...requestedStorage === "s3" ? [
22747
+ "@aws-sdk/client-s3@3.1095.0",
22748
+ "@aws-sdk/lib-storage@3.1095.0",
22749
+ "@aws-sdk/s3-request-presigner@3.1095.0"
22750
+ ] : []
22751
+ ];
22752
+ const installed = await directProjectPackages(projectRoot);
22753
+ const missing = packages.filter((spec) => !installed.has(packageNameFromSpec(spec)));
22754
+ await installApprovedPackages(projectRoot, args, `Mobile update serving needs ${missing.map(packageNameFromSpec).join(", ")}. Install them now?`, missing);
22755
+ const path = await writeAbsoluteMobileUpdateRegistry({
22756
+ force: args.includes("--force"),
22757
+ modulePath,
22758
+ projectRoot,
22759
+ publicKeys: mobile.updates.publicKeys,
22760
+ storage: requestedStorage
22761
+ });
22762
+ console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative11(projectRoot, path).replaceAll("\\", "/")}.`);
22763
+ if (requestedStorage === "local")
22764
+ console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
22765
+ else
22766
+ console.log("Set ABSOLUTE_MOBILE_UPDATE_S3_BUCKET and standard AWS credentials on the trusted server; endpoint and region overrides are optional.");
22767
+ const expoSigning = mobile.updates.expoCodeSigning;
22768
+ if (expoSigning) {
22769
+ const signingKey = mobile.updateServer.expoCodeSigningKeys[expoSigning.keyId];
22770
+ if (!signingKey)
22771
+ throw new TypeError("The active Expo server signing key is missing.");
22772
+ console.log(`Set ${signingKey.privateKeyEnv} only on the trusted server.`);
22773
+ }
22774
+ return path;
22775
+ };
22526
22776
  var publishMobileUpdate = async (args) => {
22527
22777
  const releaseDirectory = args.find((value, index) => {
22528
22778
  if (value.startsWith("-"))
@@ -23144,7 +23394,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
23144
23394
  }
23145
23395
  return { https: args.includes("--https"), port };
23146
23396
  }
23147
- const instances = listLiveInstances().filter((instance2) => resolve11(instance2.cwd) === resolve11(projectRoot) && instance2.source === "dev" && instance2.port !== null);
23397
+ const instances = listLiveInstances().filter((instance2) => resolve12(instance2.cwd) === resolve12(projectRoot) && instance2.source === "dev" && instance2.port !== null);
23148
23398
  if (instances.length !== 1) {
23149
23399
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
23150
23400
  }
@@ -23191,8 +23441,8 @@ var selectAndroidSerial = (adb, explicitSerial) => {
23191
23441
  return selected;
23192
23442
  };
23193
23443
  var safeArtifactRoot = (projectRoot, value) => {
23194
- const root = resolve11(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
23195
- if (root !== projectRoot && !root.startsWith(`${resolve11(projectRoot)}/`)) {
23444
+ const root = resolve12(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
23445
+ if (root !== projectRoot && !root.startsWith(`${resolve12(projectRoot)}/`)) {
23196
23446
  throw new TypeError("mobile test --artifacts must remain inside the project.");
23197
23447
  }
23198
23448
  return root;
@@ -23220,7 +23470,7 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
23220
23470
  });
23221
23471
  };
23222
23472
  var writeAndroidFailureArtifacts = async (options) => {
23223
- await mkdir11(options.artifactRoot, { recursive: true });
23473
+ await mkdir12(options.artifactRoot, { recursive: true });
23224
23474
  const screenshot = options.session ? await options.session.screenshot(join13(options.artifactRoot, "android-failure.png")).catch(() => {
23225
23475
  return;
23226
23476
  }) : undefined;
@@ -23359,14 +23609,14 @@ var requireIosTestContext = (args, projectRoot) => {
23359
23609
  const port = Number(explicit);
23360
23610
  if (!Number.isInteger(port) || port < 1 || port > 65535)
23361
23611
  throw new TypeError("mobile test --port must be a valid TCP port.");
23362
- const instance2 = listLiveInstances().find((candidate) => resolve11(candidate.cwd) === resolve11(projectRoot) && candidate.source === "dev" && candidate.port === port);
23612
+ const instance2 = listLiveInstances().find((candidate) => resolve12(candidate.cwd) === resolve12(projectRoot) && candidate.source === "dev" && candidate.port === port);
23363
23613
  return {
23364
23614
  https: instance2?.https ?? args.includes("--https"),
23365
23615
  instance: instance2,
23366
23616
  port
23367
23617
  };
23368
23618
  }
23369
- const instances = listLiveInstances().filter((instance2) => resolve11(instance2.cwd) === resolve11(projectRoot) && instance2.source === "dev" && instance2.port !== null);
23619
+ const instances = listLiveInstances().filter((instance2) => resolve12(instance2.cwd) === resolve12(projectRoot) && instance2.source === "dev" && instance2.port !== null);
23370
23620
  if (instances.length !== 1)
23371
23621
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
23372
23622
  const [instance] = instances;
@@ -23489,7 +23739,7 @@ var requireCapturedCommand = (command, label) => {
23489
23739
  return result;
23490
23740
  };
23491
23741
  var writeIosFailureArtifacts = async (options) => {
23492
- await mkdir11(options.artifactRoot, { recursive: true });
23742
+ await mkdir12(options.artifactRoot, { recursive: true });
23493
23743
  const screenshot = join13(options.artifactRoot, "ios-failure.png");
23494
23744
  const screenshotResult = captureCommand2([
23495
23745
  options.xcrun,
@@ -23527,8 +23777,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
23527
23777
  var absolutejsVersionForReport = async () => {
23528
23778
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
23529
23779
  const versions = await Promise.all([
23530
- resolve11(import.meta.dir, "..", "..", "package.json"),
23531
- resolve11(import.meta.dir, "..", "..", "..", "package.json")
23780
+ resolve12(import.meta.dir, "..", "..", "package.json"),
23781
+ resolve12(import.meta.dir, "..", "..", "..", "package.json")
23532
23782
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
23533
23783
  for (const version of versions) {
23534
23784
  if (version === "unknown")
@@ -23752,7 +24002,7 @@ var testIos = async (args) => {
23752
24002
  mobile.appId
23753
24003
  ], "iOS app launch");
23754
24004
  await waitForIosHmrClient({ https, port, timeoutMs });
23755
- await mkdir11(artifactRoot, { recursive: true });
24005
+ await mkdir12(artifactRoot, { recursive: true });
23756
24006
  const screenshot = join13(artifactRoot, "ios-simulator.png");
23757
24007
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
23758
24008
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
@@ -23895,6 +24145,10 @@ var runMobile = async (args) => {
23895
24145
  await buildMobileUpdate(args.slice(2));
23896
24146
  return;
23897
24147
  }
24148
+ if (command === "update" && args[1] === "provision") {
24149
+ await provisionMobileUpdate(args.slice(2));
24150
+ return;
24151
+ }
23898
24152
  if (command === "update" && args[1] === "signing" && args[2] === "generate") {
23899
24153
  await generateExpoUpdateSigning(args.slice(3));
23900
24154
  return;
@@ -23919,7 +24173,7 @@ var runMobile = async (args) => {
23919
24173
  await publishIos(args.slice(2));
23920
24174
  return;
23921
24175
  }
23922
- 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 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] | 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]");
24176
+ 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] | 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]");
23923
24177
  };
23924
24178
  export {
23925
24179
  runMobile