@absolutejs/absolute 0.20.0-beta.111 → 0.20.0-beta.113

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.
@@ -19401,7 +19401,7 @@ var init_compatibilityDispatcher = __esm(() => {
19401
19401
  // src/cli/scripts/telemetry.ts
19402
19402
  import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync6, writeFileSync } from "fs";
19403
19403
  import { homedir as homedir4 } from "os";
19404
- import { join as join25 } from "path";
19404
+ import { join as join26 } from "path";
19405
19405
  var configDir, configPath, getTelemetryConfig = () => {
19406
19406
  try {
19407
19407
  if (!existsSync5(configPath))
@@ -19414,14 +19414,14 @@ var configDir, configPath, getTelemetryConfig = () => {
19414
19414
  }
19415
19415
  };
19416
19416
  var init_telemetry = __esm(() => {
19417
- configDir = join25(homedir4(), ".absolutejs");
19418
- configPath = join25(configDir, "telemetry.json");
19417
+ configDir = join26(homedir4(), ".absolutejs");
19418
+ configPath = join26(configDir, "telemetry.json");
19419
19419
  });
19420
19420
 
19421
19421
  // src/cli/telemetryEvent.ts
19422
19422
  import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
19423
19423
  import { arch, platform as platform3 } from "os";
19424
- import { dirname as dirname18, join as join26, parse } from "path";
19424
+ import { dirname as dirname19, join as join27, parse } from "path";
19425
19425
  var checkCandidate = (candidate) => {
19426
19426
  if (!existsSync6(candidate)) {
19427
19427
  return null;
@@ -19441,12 +19441,12 @@ var checkCandidate = (candidate) => {
19441
19441
  }, findPackageVersion = () => {
19442
19442
  let { dir } = import.meta;
19443
19443
  while (dir !== parse(dir).root) {
19444
- const candidate = join26(dir, "package.json");
19444
+ const candidate = join27(dir, "package.json");
19445
19445
  const version = checkCandidate(candidate);
19446
19446
  if (version) {
19447
19447
  return version;
19448
19448
  }
19449
- dir = dirname18(dir);
19449
+ dir = dirname19(dir);
19450
19450
  }
19451
19451
  return "unknown";
19452
19452
  }, sendTelemetryEvent = (event, payload) => {
@@ -19487,7 +19487,7 @@ __export(exports_mobilePreview, {
19487
19487
  createAbsoluteMobilePreviewPlugin: () => createAbsoluteMobilePreviewPlugin
19488
19488
  });
19489
19489
  import { Elysia as Elysia3 } from "elysia";
19490
- var ABSOLUTE_MOBILE_PREVIEW_PATH = "/__absolute/mobile-preview", escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;"), isRecord14 = (value) => typeof value === "object" && value !== null, normalizeEntry2 = (entry) => {
19490
+ var ABSOLUTE_MOBILE_PREVIEW_PATH = "/__absolute/mobile-preview", escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;"), isRecord15 = (value) => typeof value === "object" && value !== null, normalizeEntry2 = (entry) => {
19491
19491
  const parsed = new URL(entry ?? "/", "https://absolute.invalid");
19492
19492
  return `${parsed.pathname}${parsed.search}${parsed.hash}`;
19493
19493
  }, absoluteMobilePreviewDocument = (mobile) => {
@@ -19535,7 +19535,7 @@ var ABSOLUTE_MOBILE_PREVIEW_PATH = "/__absolute/mobile-preview", escapeHtml2 = (
19535
19535
  "X-Robots-Tag": "noindex, nofollow"
19536
19536
  }
19537
19537
  })).post("/__absolute/mobile-preview-telemetry", ({ body, status }) => {
19538
- const value = isRecord14(body) ? body : undefined;
19538
+ const value = isRecord15(body) ? body : undefined;
19539
19539
  const durationMs = value?.durationMs;
19540
19540
  const platform4 = value?.platform;
19541
19541
  if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 || platform4 !== "android" && platform4 !== "ios") {
@@ -20086,13 +20086,13 @@ import {
20086
20086
  createHmac,
20087
20087
  createPrivateKey,
20088
20088
  createPublicKey as createPublicKey2,
20089
- randomUUID as randomUUID5,
20089
+ randomUUID as randomUUID6,
20090
20090
  sign as sign2,
20091
20091
  timingSafeEqual,
20092
20092
  verify as verify2,
20093
20093
  X509Certificate as X509Certificate3
20094
20094
  } from "crypto";
20095
- import { readFile as readFile30, stat as stat9 } from "fs/promises";
20095
+ import { readFile as readFile31, stat as stat9 } from "fs/promises";
20096
20096
  import path from "path";
20097
20097
  var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updates", MAX_FILE_BYTES, MAX_TOTAL_BYTES, DAY_MS, DEFAULT_MIN_AGE_MS, DEFAULT_GRACE_PERIOD_MS, DEFAULT_RETAIN_RECENT = 5, HASH, RELEASE, APP_ID, NAME, EXPO_DESCRIPTOR = "_absolute/expo-update.json", EXPO_CODE_SIGNING_ALGORITHM = "rsa-v1_5-sha256", HEALTH_TOKEN_VERSION = 1, HEALTH_KINDS, FAILURE_HEALTH_KINDS, MobileUpdateRegistryError, object6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field2) => {
20098
20098
  if (typeof value !== "string" || value.length === 0)
@@ -20446,7 +20446,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20446
20446
  ...input,
20447
20447
  format: MOBILE_UPDATE_REGISTRY_FORMAT,
20448
20448
  promotedAt,
20449
- promotionId: digest(new TextEncoder().encode(`${input.appId}\x00${input.channel}\x00${input.releaseId ?? "embedded"}\x00${promotedAt}\x00${randomUUID5()}`))
20449
+ promotionId: digest(new TextEncoder().encode(`${input.appId}\x00${input.channel}\x00${input.releaseId ?? "embedded"}\x00${promotedAt}\x00${randomUUID6()}`))
20450
20450
  };
20451
20451
  await beforeWrite?.(value);
20452
20452
  const bytes = json(value);
@@ -20548,7 +20548,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20548
20548
  if (!releaseId)
20549
20549
  throw new MobileUpdateRegistryError("Mobile update channel does not have an active release");
20550
20550
  const createdAt = clock().toISOString();
20551
- const id = randomUUID5();
20551
+ const id = randomUUID6();
20552
20552
  const event = {
20553
20553
  action,
20554
20554
  createdAt,
@@ -21125,7 +21125,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21125
21125
  verifyManifestSignature(manifest, options.publicKeys);
21126
21126
  await assertNotMarked(manifest.appId, manifest.releaseId);
21127
21127
  const localRoot = path.resolve(input.releaseDirectory);
21128
- const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile30(path.join(localRoot, "update.json"), "utf8")));
21128
+ const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile31(path.join(localRoot, "update.json"), "utf8")));
21129
21129
  if (JSON.stringify(localManifest) !== JSON.stringify(manifest))
21130
21130
  throw new MobileUpdateRegistryError("Local mobile update manifest changed");
21131
21131
  const existing = await readManifest(manifest.appId, manifest.releaseId);
@@ -21411,8 +21411,8 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21411
21411
  });
21412
21412
  }
21413
21413
  const pathname = new URL(request.url).pathname.replace(/^\/+/, "");
21414
- const relative23 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21415
- if (request.method === "POST" && relative23 === "health") {
21414
+ const relative24 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21415
+ if (request.method === "POST" && relative24 === "health") {
21416
21416
  if (!options.registry.recordUpdateHealth)
21417
21417
  return new Response(null, { status: 404 });
21418
21418
  const appId = request.headers.get("x-absolute-mobile-app");
@@ -21455,7 +21455,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21455
21455
  }
21456
21456
  if (request.method !== "GET")
21457
21457
  return new Response(null, { status: 405 });
21458
- if (relative23 === "update.json") {
21458
+ if (relative24 === "update.json") {
21459
21459
  const expoProtocolVersion = request.headers.get("expo-protocol-version");
21460
21460
  const expoProtocol = expoProtocolVersion !== null;
21461
21461
  if (expoProtocol && expoProtocolVersion !== "1")
@@ -21573,7 +21573,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21573
21573
  }
21574
21574
  });
21575
21575
  }
21576
- const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative23);
21576
+ const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative24);
21577
21577
  if (!match?.[1] || !match[2])
21578
21578
  return new Response(null, { status: 404 });
21579
21579
  const file = await options.registry.readUpdateFile({
@@ -21666,15 +21666,15 @@ import {
21666
21666
  createPublicKey as createPublicKey3,
21667
21667
  X509Certificate as X509Certificate4
21668
21668
  } from "crypto";
21669
- import { access as access22, mkdir as mkdir24 } from "fs/promises";
21670
- import { dirname as dirname24, isAbsolute as isAbsolute14, relative as relative23, resolve as resolve25, sep as sep16 } from "path";
21669
+ import { access as access23, mkdir as mkdir25 } from "fs/promises";
21670
+ import { dirname as dirname25, isAbsolute as isAbsolute15, relative as relative24, resolve as resolve26, sep as sep17 } from "path";
21671
21671
  import { pathToFileURL as pathToFileURL5 } from "url";
21672
21672
  import { Elysia as Elysia4 } from "elysia";
21673
21673
  var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE = "mobile.update.ts", object7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectPath4 = (projectRoot, requested) => {
21674
- const root = resolve25(projectRoot);
21675
- const path2 = resolve25(root, requested);
21676
- const projectRelative = relative23(root, path2);
21677
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep16}`) || isAbsolute14(projectRelative))
21674
+ const root = resolve26(projectRoot);
21675
+ const path2 = resolve26(root, requested);
21676
+ const projectRelative = relative24(root, path2);
21677
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep17}`) || isAbsolute15(projectRelative))
21678
21678
  throw new TypeError("mobile.updates.server.registry must remain inside the project.");
21679
21679
  return path2;
21680
21680
  }, isRegistry4 = (value) => object7(value) && [
@@ -21697,7 +21697,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
21697
21697
  return metadata;
21698
21698
  }, loadAbsoluteMobileUpdateServerModule = async (projectRoot, requestedModulePath = DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE) => {
21699
21699
  const modulePath = projectPath4(projectRoot, requestedModulePath);
21700
- await access22(modulePath).catch(() => {
21700
+ await access23(modulePath).catch(() => {
21701
21701
  throw new TypeError(`Mobile update server registry does not exist: ${modulePath}. Run \`absolute mobile update provision\`.`);
21702
21702
  });
21703
21703
  const loaded = await import(pathToFileURL5(modulePath).href);
@@ -21886,13 +21886,13 @@ ${generated}`);
21886
21886
  }, writeAbsoluteMobileUpdateRegistry = async (options) => {
21887
21887
  const path2 = projectPath4(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
21888
21888
  if (!options.force) {
21889
- await access22(path2).then(() => {
21889
+ await access23(path2).then(() => {
21890
21890
  throw new TypeError(`Mobile update registry already exists: ${path2}. Pass --force to replace it.`);
21891
21891
  }, () => {
21892
21892
  return;
21893
21893
  });
21894
21894
  }
21895
- await mkdir24(dirname24(path2), { recursive: true });
21895
+ await mkdir25(dirname25(path2), { recursive: true });
21896
21896
  await Bun.write(path2, renderAbsoluteMobileUpdateRegistry({
21897
21897
  ...options.health ? { health: options.health } : {},
21898
21898
  ...options.rollout ? { rollout: options.rollout } : {},
@@ -33560,12 +33560,15 @@ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification,
33560
33560
 
33561
33561
  // src/mobile/ciPromotionAudit.ts
33562
33562
  var ABSOLUTE_MOBILE_CI_PROMOTION_AUDIT_FORMAT = 1;
33563
- var ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT = 1;
33563
+ var ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT = 2;
33564
33564
  var MAX_GITHUB_OUTPUT_BYTES = 1048576;
33565
33565
  var MAX_AUDIT_JSON_BYTES = 1048576;
33566
33566
  var MAX_SEARCH_FILES = 20000;
33567
33567
  var RUN_ID_PATTERN = /^[1-9][0-9]*$/u;
33568
33568
  var REPOSITORY_PATTERN2 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
33569
+ var DISPATCH_ID_PATTERN = /^amp_[a-f0-9]{32}$/u;
33570
+ var PROMOTION_DISCOVERY_ATTEMPTS = 60;
33571
+ var PROMOTION_DISCOVERY_INTERVAL_MS = 1000;
33569
33572
  var isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
33570
33573
  var requiredString = (value, name) => {
33571
33574
  if (typeof value !== "string" || value.trim() === "")
@@ -33582,6 +33585,26 @@ var runId = (value, name) => {
33582
33585
  throw new TypeError(`${name} is invalid.`);
33583
33586
  return value;
33584
33587
  };
33588
+ var dispatchId = (value) => {
33589
+ if (!DISPATCH_ID_PATTERN.test(value))
33590
+ throw new TypeError("Mobile promotion dispatch ID is invalid.");
33591
+ return value;
33592
+ };
33593
+ var promotionRunTitle = (value) => `AbsoluteJS mobile promotion [${dispatchId(value)}]`;
33594
+ var promotionDiscoveryCommand = (workflow, repository) => [
33595
+ "gh",
33596
+ "run",
33597
+ "list",
33598
+ "--workflow",
33599
+ workflow,
33600
+ "--event",
33601
+ "workflow_dispatch",
33602
+ "--limit",
33603
+ "50",
33604
+ ...repositoryArgs(repository),
33605
+ "--json",
33606
+ "databaseId,displayTitle,event,url"
33607
+ ];
33585
33608
  var repositoryArgs = (repository) => {
33586
33609
  if (repository === undefined)
33587
33610
  return [];
@@ -33629,6 +33652,27 @@ var parseJob = (value) => {
33629
33652
  status: requiredString(value.status, "GitHub job status")
33630
33653
  };
33631
33654
  };
33655
+ var discoverAbsoluteMobilePromotionRun = async (options) => {
33656
+ const requestedDispatchId = dispatchId(options.dispatchId);
33657
+ const attempts = options.attempts ?? PROMOTION_DISCOVERY_ATTEMPTS;
33658
+ if (!Number.isSafeInteger(attempts) || attempts < 1 || attempts > 300)
33659
+ throw new TypeError("Mobile promotion discovery attempts are invalid.");
33660
+ const pollIntervalMs = options.pollIntervalMs ?? PROMOTION_DISCOVERY_INTERVAL_MS;
33661
+ if (!Number.isSafeInteger(pollIntervalMs) || pollIntervalMs < 0 || pollIntervalMs > 1e4)
33662
+ throw new TypeError("Mobile promotion discovery interval is invalid.");
33663
+ const sleep = options.sleep ?? Bun.sleep;
33664
+ const discover = async (attempt) => {
33665
+ const result = await requireCommand(options.run ?? defaultRunner, promotionDiscoveryCommand(options.workflow, options.repository), { cwd: options.projectRoot }, "GitHub promotion run discovery");
33666
+ const discovered = parseAbsoluteMobilePromotionRunList(JSON.parse(result.stdout), requestedDispatchId);
33667
+ if (discovered)
33668
+ return discovered;
33669
+ if (attempt >= attempts)
33670
+ throw new TypeError(`GitHub did not expose promotion dispatch ${requestedDispatchId} within ${attempts} attempts.`);
33671
+ await sleep(pollIntervalMs);
33672
+ return discover(attempt + 1);
33673
+ };
33674
+ return discover(1);
33675
+ };
33632
33676
  var inspectAbsoluteMobileCiRun = async (options) => {
33633
33677
  const requestedRunId = runId(options.runId, "GitHub run ID");
33634
33678
  const result = await requireCommand(options.run ?? defaultRunner, [
@@ -33638,7 +33682,7 @@ var inspectAbsoluteMobileCiRun = async (options) => {
33638
33682
  requestedRunId,
33639
33683
  ...repositoryArgs(options.repository),
33640
33684
  "--json",
33641
- "conclusion,createdAt,databaseId,headBranch,headSha,jobs,startedAt,status,updatedAt,url,workflowName"
33685
+ "conclusion,createdAt,databaseId,displayTitle,headBranch,headSha,jobs,startedAt,status,updatedAt,url,workflowName"
33642
33686
  ], { cwd: options.projectRoot }, "GitHub workflow inspection");
33643
33687
  return parseAbsoluteMobileCiRunStatus(JSON.parse(result.stdout), requestedRunId);
33644
33688
  };
@@ -33651,6 +33695,7 @@ var parseAbsoluteMobileCiRunStatus = (value, requestedRunId) => {
33651
33695
  return {
33652
33696
  conclusion: optionalString(value.conclusion, "GitHub run conclusion"),
33653
33697
  createdAt: requiredString(value.createdAt, "GitHub run createdAt"),
33698
+ displayTitle: requiredString(value.displayTitle, "GitHub run displayTitle"),
33654
33699
  headBranch: requiredString(value.headBranch, "GitHub run headBranch"),
33655
33700
  headSha: requiredString(value.headSha, "GitHub run headSha"),
33656
33701
  jobs: value.jobs.map(parseJob),
@@ -33663,20 +33708,44 @@ var parseAbsoluteMobileCiRunStatus = (value, requestedRunId) => {
33663
33708
  };
33664
33709
  };
33665
33710
  var parseAbsoluteMobilePromotionContext = (value) => {
33666
- if (!isRecord13(value) || value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT || value.platform !== "android" && value.platform !== "ios")
33711
+ if (!isRecord13(value) || value.format !== 1 && value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT || value.platform !== "android" && value.platform !== "ios")
33667
33712
  throw new TypeError("Mobile promotion context is invalid.");
33668
33713
  const { platform: platform3 } = value;
33669
33714
  const sourceArtifact = requiredString(value.sourceArtifact, "promotion source artifact");
33670
33715
  if (sourceArtifact !== `absolute-mobile-${platform3}`)
33671
33716
  throw new TypeError("Mobile promotion source artifact is invalid.");
33672
33717
  return {
33673
- format: ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT,
33718
+ dispatchId: value.format === 1 ? null : dispatchId(requiredString(value.dispatchId, "promotion dispatch ID")),
33719
+ format: value.format,
33674
33720
  platform: platform3,
33675
33721
  promotionRunId: runId(requiredString(value.promotionRunId, "promotion run ID"), "promotion run ID"),
33676
33722
  sourceArtifact,
33677
33723
  sourceRunId: runId(requiredString(value.sourceRunId, "source run ID"), "source run ID")
33678
33724
  };
33679
33725
  };
33726
+ var parseAbsoluteMobilePromotionRunList = (value, requestedDispatchId) => {
33727
+ if (!Array.isArray(value))
33728
+ throw new TypeError("GitHub workflow run list response is invalid.");
33729
+ const expectedTitle = promotionRunTitle(requestedDispatchId);
33730
+ const matches2 = value.filter((candidate) => isRecord13(candidate) && candidate.displayTitle === expectedTitle);
33731
+ if (matches2.length === 0)
33732
+ return null;
33733
+ if (matches2.length !== 1)
33734
+ throw new TypeError("GitHub returned multiple runs for one promotion dispatch identity.");
33735
+ const [match] = matches2;
33736
+ if (!isRecord13(match) || match.event !== "workflow_dispatch")
33737
+ throw new TypeError("GitHub promotion run identity is invalid.");
33738
+ const databaseId = typeof match.databaseId === "number" || typeof match.databaseId === "string" ? String(match.databaseId) : "";
33739
+ const requestedRunId = runId(databaseId, "GitHub promotion run ID");
33740
+ const url = requiredString(match.url, "GitHub promotion run URL");
33741
+ if (!url.endsWith(`/actions/runs/${requestedRunId}`))
33742
+ throw new TypeError("GitHub promotion run URL does not match its ID.");
33743
+ return {
33744
+ dispatchId: requestedDispatchId,
33745
+ runId: requestedRunId,
33746
+ url
33747
+ };
33748
+ };
33680
33749
  var watchAbsoluteMobileCiRun = async (options) => {
33681
33750
  const requestedRunId = runId(options.runId, "GitHub run ID");
33682
33751
  await requireCommand(options.run ?? defaultRunner, [
@@ -33795,6 +33864,7 @@ var renderAuditMarkdown = (audit) => `# AbsoluteJS mobile promotion audit
33795
33864
  - Artifact SHA-256: \`${audit.release.sha256}\`
33796
33865
  - Source workflow run: ${audit.github.sourceRunId}
33797
33866
  - Promotion workflow run: ${audit.github.promotionRunId}
33867
+ - Dispatch correlation: ${audit.github.dispatchId ?? "legacy workflow (not recorded)"}
33798
33868
  - Workflow: ${audit.github.workflowName}
33799
33869
  - GitHub identity: \`${audit.provenance.repository}/${audit.provenance.workflowPath}@${audit.provenance.ref}\`
33800
33870
  - Certification: \`${audit.certification.certificationId}\` (${audit.certification.strength})
@@ -33844,6 +33914,8 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
33844
33914
  throw new TypeError("Promotion context run identity does not match.");
33845
33915
  if (context && context.platform !== platform3)
33846
33916
  throw new TypeError("Promotion context platform does not match.");
33917
+ if (context?.dispatchId && status.displayTitle !== promotionRunTitle(context.dispatchId))
33918
+ throw new TypeError("Promotion dispatch identity does not match the GitHub run.");
33847
33919
  const sourceRunId = options.sourceRunId ? runId(options.sourceRunId, "GitHub source run ID") : context?.sourceRunId;
33848
33920
  if (!sourceRunId)
33849
33921
  throw new TypeError("Promotion artifact predates source-run context; pass --source-run-id.");
@@ -33912,6 +33984,7 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
33912
33984
  format: ABSOLUTE_MOBILE_CI_PROMOTION_AUDIT_FORMAT,
33913
33985
  github: {
33914
33986
  conclusion: status.conclusion,
33987
+ dispatchId: context?.dispatchId ?? null,
33915
33988
  promotionRunId,
33916
33989
  repository: verification.identity.repository,
33917
33990
  sourceRunId,
@@ -33946,11 +34019,229 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
33946
34019
  throw error;
33947
34020
  }
33948
34021
  };
34022
+ // src/mobile/ciPromotionLedger.ts
34023
+ import {
34024
+ access as access19,
34025
+ link,
34026
+ lstat as lstat2,
34027
+ mkdir as mkdir19,
34028
+ open,
34029
+ readFile as readFile23,
34030
+ readdir as readdir8,
34031
+ rename as rename15,
34032
+ rm as rm15
34033
+ } from "fs/promises";
34034
+ import { dirname as dirname17, isAbsolute as isAbsolute12, join as join25, relative as relative18, resolve as resolve20, sep as sep13 } from "path";
34035
+ import { randomUUID as randomUUID5 } from "crypto";
34036
+ var ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT = 1;
34037
+ var MAX_OPERATION_BYTES = 65536;
34038
+ var MAX_OPERATIONS = 1000;
34039
+ var DISPATCH_ID_PATTERN2 = /^amp_[a-f0-9]{32}$/u;
34040
+ var RUN_ID_PATTERN2 = /^[1-9][0-9]*$/u;
34041
+ var SHA256_PATTERN = /^[a-f0-9]{64}$/u;
34042
+ var CERTIFICATION_ID_PATTERN = /^amobile_cert_[a-f0-9]{64}$/u;
34043
+ var RELEASE_ID_PATTERN2 = /^amobile_(?:android|ios)_[a-f0-9]{64}$/u;
34044
+ var REPOSITORY_PATTERN3 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
34045
+ var LAST_ASCII_CONTROL = 31;
34046
+ var DELETE_CHARACTER = 127;
34047
+ var PHASES = [
34048
+ "dispatching",
34049
+ "dispatched",
34050
+ "discovered",
34051
+ "completed",
34052
+ "audited"
34053
+ ];
34054
+ var isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
34055
+ var requiredString2 = (value, label, max = 512) => {
34056
+ const hasControlCharacter = typeof value === "string" && Array.from(value).some((character) => {
34057
+ const code = character.codePointAt(0);
34058
+ return code !== undefined && (code <= LAST_ASCII_CONTROL || code === DELETE_CHARACTER);
34059
+ });
34060
+ if (typeof value !== "string" || value.trim() === "" || value !== value.trim() || value.length > max || hasControlCharacter)
34061
+ throw new TypeError(`${label} is invalid.`);
34062
+ return value;
34063
+ };
34064
+ var optionalString2 = (value, label, max = 512) => {
34065
+ if (value === null)
34066
+ return null;
34067
+ return requiredString2(value, label, max);
34068
+ };
34069
+ var boolean = (value, label) => {
34070
+ if (typeof value !== "boolean")
34071
+ throw new TypeError(`${label} is invalid.`);
34072
+ return value;
34073
+ };
34074
+ var timestamp = (value, label) => {
34075
+ const parsed = requiredString2(value, label, 64);
34076
+ if (!Number.isFinite(Date.parse(parsed)))
34077
+ throw new TypeError(`${label} is invalid.`);
34078
+ return parsed;
34079
+ };
34080
+ var projectRelativePath = (value, label) => {
34081
+ const parsed = requiredString2(value, label, 512);
34082
+ if (isAbsolute12(parsed) || parsed === ".." || parsed.startsWith(`..${sep13}`) || parsed.split(/[\\/]/u).includes(".."))
34083
+ throw new TypeError(`${label} must be project-relative.`);
34084
+ return parsed.replaceAll("\\", "/");
34085
+ };
34086
+ var nullableProjectRelativePath = (value, label) => value === null ? null : projectRelativePath(value, label);
34087
+ var dispatchId2 = (value) => {
34088
+ const parsed = requiredString2(value, "Promotion dispatch ID", 36);
34089
+ if (!DISPATCH_ID_PATTERN2.test(parsed))
34090
+ throw new TypeError("Promotion dispatch ID is invalid.");
34091
+ return parsed;
34092
+ };
34093
+ var runId2 = (value, label) => {
34094
+ const parsed = requiredString2(value, label, 32);
34095
+ if (!RUN_ID_PATTERN2.test(parsed))
34096
+ throw new TypeError(`${label} is invalid.`);
34097
+ return parsed;
34098
+ };
34099
+ var nullableRunId = (value, label) => value === null ? null : runId2(value, label);
34100
+ var operationRoot = (projectRoot) => join25(resolve20(projectRoot), ".absolutejs", "mobile-ci", "promotions");
34101
+ var absoluteMobilePromotionOperationPath = (projectRoot, requestedDispatchId) => join25(operationRoot(projectRoot), dispatchId2(requestedDispatchId), "operation.json");
34102
+ var operationPhase = (value) => {
34103
+ const phase = PHASES.find((candidate) => candidate === value);
34104
+ if (!phase)
34105
+ throw new TypeError("Mobile promotion operation phase is invalid.");
34106
+ return phase;
34107
+ };
34108
+ var parseAbsoluteMobilePromotionOperation = (value) => {
34109
+ if (!isRecord14(value) || value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT || value.platform !== "android" && value.platform !== "ios")
34110
+ throw new TypeError("Mobile promotion operation is invalid.");
34111
+ const parsed = {
34112
+ auditDirectory: nullableProjectRelativePath(value.auditDirectory, "Promotion audit directory"),
34113
+ auditOutputDirectory: nullableProjectRelativePath(value.auditOutputDirectory, "Promotion requested audit directory"),
34114
+ auditRequested: boolean(value.auditRequested, "Promotion audit request"),
34115
+ certificationId: requiredString2(value.certificationId, "Promotion certification ID", 256),
34116
+ certificationSha256: requiredString2(value.certificationSha256, "Promotion certification digest", 64),
34117
+ channel: optionalString2(value.channel, "Promotion channel", 128),
34118
+ conclusion: optionalString2(value.conclusion, "Promotion conclusion", 64),
34119
+ createdAt: timestamp(value.createdAt, "Promotion creation time"),
34120
+ dispatchId: dispatchId2(value.dispatchId),
34121
+ format: ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT,
34122
+ githubStatus: optionalString2(value.githubStatus, "Promotion GitHub status", 64),
34123
+ phase: operationPhase(value.phase),
34124
+ platform: value.platform,
34125
+ playTrack: optionalString2(value.playTrack, "Promotion Play track", 128),
34126
+ ref: optionalString2(value.ref, "Promotion ref", 256),
34127
+ releaseId: requiredString2(value.releaseId, "Promotion release ID", 256),
34128
+ repository: optionalString2(value.repository, "Promotion repository", 256),
34129
+ runId: nullableRunId(value.runId, "Promotion run ID"),
34130
+ sourceRunId: runId2(value.sourceRunId, "Promotion source run ID"),
34131
+ testflightGroup: optionalString2(value.testflightGroup, "Promotion TestFlight group", 128),
34132
+ testflightSubmitReview: boolean(value.testflightSubmitReview, "Promotion TestFlight review setting"),
34133
+ updatedAt: timestamp(value.updatedAt, "Promotion update time"),
34134
+ url: optionalString2(value.url, "Promotion run URL", 1024),
34135
+ watchRequested: boolean(value.watchRequested, "Promotion watch request"),
34136
+ workflow: projectRelativePath(value.workflow, "Promotion workflow")
34137
+ };
34138
+ if (!SHA256_PATTERN.test(parsed.certificationSha256))
34139
+ throw new TypeError("Promotion certification digest is invalid.");
34140
+ if (!CERTIFICATION_ID_PATTERN.test(parsed.certificationId))
34141
+ throw new TypeError("Promotion certification ID is invalid.");
34142
+ if (!RELEASE_ID_PATTERN2.test(parsed.releaseId))
34143
+ throw new TypeError("Promotion release ID is invalid.");
34144
+ if (parsed.repository !== null && !REPOSITORY_PATTERN3.test(parsed.repository))
34145
+ throw new TypeError("Promotion repository is invalid.");
34146
+ const discovered = PHASES.indexOf(parsed.phase) >= PHASES.indexOf("discovered");
34147
+ const completed = PHASES.indexOf(parsed.phase) >= PHASES.indexOf("completed");
34148
+ if (discovered && (!parsed.runId || !parsed.url) || !discovered && (parsed.runId || parsed.url) || completed && (parsed.githubStatus !== "completed" || !parsed.conclusion) || !completed && (parsed.githubStatus || parsed.conclusion) || parsed.phase === "audited" && !parsed.auditDirectory || parsed.phase !== "audited" && parsed.auditDirectory)
34149
+ throw new TypeError("Mobile promotion operation phase is incomplete.");
34150
+ if (parsed.url && parsed.runId) {
34151
+ const url = new URL(parsed.url);
34152
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !url.pathname.endsWith(`/actions/runs/${parsed.runId}`))
34153
+ throw new TypeError("Promotion run URL is invalid.");
34154
+ }
34155
+ if (Date.parse(parsed.updatedAt) < Date.parse(parsed.createdAt))
34156
+ throw new TypeError("Promotion operation time moved backwards.");
34157
+ return parsed;
34158
+ };
34159
+ var serialize = (operation2) => `${JSON.stringify(parseAbsoluteMobilePromotionOperation(operation2), null, 2)}
34160
+ `;
34161
+ var writeSynced = async (path, source) => {
34162
+ const handle = await open(path, "wx", 384);
34163
+ try {
34164
+ await handle.writeFile(source);
34165
+ await handle.sync();
34166
+ } finally {
34167
+ await handle.close();
34168
+ }
34169
+ };
34170
+ var atomicWrite = async (path, source, exclusive) => {
34171
+ await mkdir19(dirname17(path), { recursive: true });
34172
+ const temporary = join25(dirname17(path), `.operation-${randomUUID5()}.tmp`);
34173
+ try {
34174
+ await writeSynced(temporary, source);
34175
+ if (exclusive)
34176
+ await link(temporary, path);
34177
+ else
34178
+ await rename15(temporary, path);
34179
+ } finally {
34180
+ await rm15(temporary, { force: true });
34181
+ }
34182
+ };
34183
+ var advanceAbsoluteMobilePromotionOperation = async (projectRoot, requestedDispatchId, options, now = () => new Date) => {
34184
+ const current = await readAbsoluteMobilePromotionOperation(projectRoot, requestedDispatchId);
34185
+ if (PHASES.indexOf(options.phase) < PHASES.indexOf(current.phase))
34186
+ throw new TypeError("Mobile promotion operation cannot move backwards.");
34187
+ if (current.runId && options.runId && current.runId !== options.runId || current.url && options.url && current.url !== options.url || current.auditDirectory && options.auditDirectory && current.auditDirectory !== options.auditDirectory)
34188
+ throw new TypeError("Mobile promotion operation identity is immutable.");
34189
+ const next = parseAbsoluteMobilePromotionOperation({
34190
+ ...current,
34191
+ auditDirectory: options.auditDirectory === undefined ? current.auditDirectory : options.auditDirectory,
34192
+ conclusion: options.conclusion === undefined ? current.conclusion : options.conclusion,
34193
+ githubStatus: options.githubStatus === undefined ? current.githubStatus : options.githubStatus,
34194
+ phase: options.phase,
34195
+ runId: options.runId === undefined ? current.runId : options.runId,
34196
+ updatedAt: now().toISOString(),
34197
+ url: options.url === undefined ? current.url : options.url
34198
+ });
34199
+ await atomicWrite(absoluteMobilePromotionOperationPath(projectRoot, requestedDispatchId), serialize(next), false);
34200
+ return next;
34201
+ };
34202
+ var createAbsoluteMobilePromotionOperation = async (projectRoot, options) => {
34203
+ const now = (options.now ?? (() => new Date))().toISOString();
34204
+ const operation2 = parseAbsoluteMobilePromotionOperation({
34205
+ ...options,
34206
+ auditDirectory: null,
34207
+ conclusion: null,
34208
+ createdAt: now,
34209
+ format: ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT,
34210
+ githubStatus: null,
34211
+ phase: "dispatching",
34212
+ runId: null,
34213
+ updatedAt: now,
34214
+ url: null
34215
+ });
34216
+ await atomicWrite(absoluteMobilePromotionOperationPath(projectRoot, operation2.dispatchId), serialize(operation2), true);
34217
+ return operation2;
34218
+ };
34219
+ var listAbsoluteMobilePromotionOperations = async (projectRoot) => {
34220
+ const root = operationRoot(projectRoot);
34221
+ if (!await access19(root).then(() => true).catch(() => false))
34222
+ return [];
34223
+ const entries = await readdir8(root, { withFileTypes: true });
34224
+ if (entries.length > MAX_OPERATIONS)
34225
+ throw new TypeError("Mobile promotion operation count exceeds the limit.");
34226
+ const operations = await Promise.all(entries.filter((entry) => entry.isDirectory() && DISPATCH_ID_PATTERN2.test(entry.name)).map((entry) => readAbsoluteMobilePromotionOperation(projectRoot, entry.name)));
34227
+ return operations.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
34228
+ };
34229
+ var readAbsoluteMobilePromotionOperation = async (projectRoot, requestedDispatchId) => {
34230
+ const path = absoluteMobilePromotionOperationPath(projectRoot, requestedDispatchId);
34231
+ const metadata = await lstat2(path).catch(() => null);
34232
+ if (!metadata?.isFile() || metadata.size > MAX_OPERATION_BYTES)
34233
+ throw new TypeError(`Mobile promotion operation ${requestedDispatchId} was not found or is invalid.`);
34234
+ const operation2 = parseAbsoluteMobilePromotionOperation(JSON.parse(await readFile23(path, "utf8")));
34235
+ if (operation2.dispatchId !== requestedDispatchId)
34236
+ throw new TypeError("Mobile promotion operation identity does not match.");
34237
+ return operation2;
34238
+ };
34239
+ var relativeAbsoluteMobilePromotionOperationPath = (projectRoot, dispatchIdValue) => relative18(resolve20(projectRoot), absoluteMobilePromotionOperationPath(projectRoot, dispatchIdValue)).replaceAll("\\", "/");
33949
34240
  // src/mobile/ciWorkflow.ts
33950
34241
  import { existsSync as existsSync4 } from "fs";
33951
- import { access as access19, mkdir as mkdir19, readFile as readFile23, writeFile as writeFile19 } from "fs/promises";
33952
- import { dirname as dirname17, extname as extname5, relative as relative18, resolve as resolve20, sep as sep13 } from "path";
33953
- var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 3;
34242
+ import { access as access20, mkdir as mkdir20, readFile as readFile24, writeFile as writeFile19 } from "fs/promises";
34243
+ import { dirname as dirname18, extname as extname5, relative as relative19, resolve as resolve21, sep as sep14 } from "path";
34244
+ var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 4;
33954
34245
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
33955
34246
  var CI_ENV_INDENTATION = 6;
33956
34247
  var RESERVED_SECRET_NAMES = new Set([
@@ -33970,7 +34261,7 @@ var RESERVED_SECRET_NAMES = new Set([
33970
34261
  ]);
33971
34262
  var exists9 = async (path) => {
33972
34263
  try {
33973
- await access19(path);
34264
+ await access20(path);
33974
34265
  return true;
33975
34266
  } catch {
33976
34267
  return false;
@@ -33978,10 +34269,10 @@ var exists9 = async (path) => {
33978
34269
  };
33979
34270
  var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
33980
34271
  var projectPath2 = (projectRoot, value, field2, options = {}) => {
33981
- const root = resolve20(projectRoot);
33982
- const path = resolve20(root, value);
33983
- const portable = relative18(root, path).replaceAll("\\", "/");
33984
- if (portable === ".." || portable.startsWith(`..${sep13}`) || portable.startsWith("../") || portable === "") {
34272
+ const root = resolve21(projectRoot);
34273
+ const path = resolve21(root, value);
34274
+ const portable = relative19(root, path).replaceAll("\\", "/");
34275
+ if (portable === ".." || portable.startsWith(`..${sep14}`) || portable.startsWith("../") || portable === "") {
33985
34276
  throw new TypeError(`${field2} must remain inside the project root.`);
33986
34277
  }
33987
34278
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -33991,11 +34282,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
33991
34282
  return portable;
33992
34283
  };
33993
34284
  var workflowOutputPath = (projectRoot, value) => {
33994
- const root = resolve20(projectRoot);
33995
- const workflows = resolve20(root, ".github/workflows");
33996
- const path = resolve20(root, value ?? ".github/workflows/absolute-mobile.yml");
33997
- const portable = relative18(workflows, path);
33998
- if (portable === ".." || portable.startsWith(`..${sep13}`) || extname5(path) !== ".yml" && extname5(path) !== ".yaml") {
34285
+ const root = resolve21(projectRoot);
34286
+ const workflows = resolve21(root, ".github/workflows");
34287
+ const path = resolve21(root, value ?? ".github/workflows/absolute-mobile.yml");
34288
+ const portable = relative19(workflows, path);
34289
+ if (portable === ".." || portable.startsWith(`..${sep14}`) || extname5(path) !== ".yml" && extname5(path) !== ".yaml") {
33999
34290
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
34000
34291
  }
34001
34292
  return path;
@@ -34109,6 +34400,10 @@ var publishingInputs = (platforms, includePublishing) => {
34109
34400
  description: Source workflow run ID for promotion without rebuilding
34110
34401
  required: false
34111
34402
  type: string`,
34403
+ ` dispatch_id:
34404
+ description: Unique promotion correlation ID supplied by the AbsoluteJS CLI
34405
+ required: false
34406
+ type: string`,
34112
34407
  ` certification_base64:
34113
34408
  description: AbsoluteJS certification supplied by the promotion CLI
34114
34409
  required: false
@@ -34481,6 +34776,10 @@ var promotionValidationSteps = ` - name: Validate promotion handoff
34481
34776
  echo "Promotion requires a numeric source workflow run ID." >&2
34482
34777
  exit 1
34483
34778
  fi
34779
+ if [[ ! "$ABSOLUTE_PROMOTION_DISPATCH_ID" =~ ^amp_[a-f0-9]{32}$ ]]; then
34780
+ echo "Promotion requires a valid AbsoluteJS dispatch correlation ID." >&2
34781
+ exit 1
34782
+ fi
34484
34783
  if [[ -z "$ABSOLUTE_CERTIFICATION_BASE64" ]]; then
34485
34784
  echo "Promotion requires certification supplied by absolute mobile ci promote." >&2
34486
34785
  exit 1
@@ -34495,7 +34794,7 @@ var promotionSharedSteps = (platform3) => ` - name: Restore exact ${platfor
34495
34794
  - name: Record bounded promotion context
34496
34795
  shell: bash
34497
34796
  run: |
34498
- bun -e 'const sourceRunId = process.env.ABSOLUTE_SOURCE_RUN_ID; const promotionRunId = process.env.GITHUB_RUN_ID; if (!sourceRunId?.match(/^[1-9][0-9]*$/) || !promotionRunId?.match(/^[1-9][0-9]*$/)) throw new Error("Invalid promotion run identity"); await Bun.write(".absolutejs/mobile-ci/promotion-context.json", JSON.stringify({ format: 1, platform: ${JSON.stringify(platform3)}, promotionRunId, sourceArtifact: ${JSON.stringify(`absolute-mobile-${platform3}`)}, sourceRunId }, null, 2) + "\\n");'
34797
+ bun -e 'const dispatchId = process.env.ABSOLUTE_PROMOTION_DISPATCH_ID; const sourceRunId = process.env.ABSOLUTE_SOURCE_RUN_ID; const promotionRunId = process.env.GITHUB_RUN_ID; if (!dispatchId?.match(/^amp_[a-f0-9]{32}$/) || !sourceRunId?.match(/^[1-9][0-9]*$/) || !promotionRunId?.match(/^[1-9][0-9]*$/)) throw new Error("Invalid promotion run identity"); await Bun.write(".absolutejs/mobile-ci/promotion-context.json", JSON.stringify({ dispatchId, format: 2, platform: ${JSON.stringify(platform3)}, promotionRunId, sourceArtifact: ${JSON.stringify(`absolute-mobile-${platform3}`)}, sourceRunId }, null, 2) + "\\n");'
34499
34798
  - name: Import and verify exact certification
34500
34799
  shell: bash
34501
34800
  run: |
@@ -34533,6 +34832,7 @@ var androidPromotionJob = (options) => {
34533
34832
  id-token: write
34534
34833
  env:
34535
34834
  ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
34835
+ ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
34536
34836
  ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
34537
34837
  ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
34538
34838
  ABSOLUTE_PLAY_TRACK: \${{ inputs.play_track }}
@@ -34598,6 +34898,7 @@ var iosPromotionJob = (options) => {
34598
34898
  id-token: write
34599
34899
  env:
34600
34900
  ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
34901
+ ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
34601
34902
  ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
34602
34903
  ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
34603
34904
  ABSOLUTE_TESTFLIGHT_GROUP: \${{ inputs.testflight_group }}
@@ -34688,6 +34989,7 @@ var createAbsoluteMobileGithubWorkflow = (options) => {
34688
34989
  });
34689
34990
  let workflow = `# Generated by AbsoluteJS. Regenerate with: absolute mobile ci github${includePublishing ? " --publish" : ""}
34690
34991
  name: AbsoluteJS Mobile
34992
+ run-name: \${{ inputs.operation == 'promote' && format('AbsoluteJS mobile promotion [{0}]', inputs.dispatch_id) || format('AbsoluteJS mobile {0}', github.event_name) }}
34691
34993
 
34692
34994
  on:
34693
34995
  pull_request:
@@ -34713,6 +35015,7 @@ jobs:
34713
35015
  env:
34714
35016
  ${environment}${includePublishing ? `
34715
35017
  ABSOLUTE_PROMOTION_PLATFORM: \${{ inputs.platform }}
35018
+ ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
34716
35019
  ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
34717
35020
  ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}` : ""}
34718
35021
  steps:
@@ -34744,11 +35047,11 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
34744
35047
  var writeAbsoluteMobileGithubWorkflow = async (options) => {
34745
35048
  const path = workflowOutputPath(options.projectRoot, options.outputPath);
34746
35049
  const generated = createAbsoluteMobileGithubWorkflow(options);
34747
- const previous = await exists9(path) ? await readFile23(path, "utf8") : undefined;
35050
+ const previous = await exists9(path) ? await readFile24(path, "utf8") : undefined;
34748
35051
  if (previous !== undefined && previous !== generated.workflow && !options.force)
34749
- throw new TypeError(`${relative18(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
35052
+ throw new TypeError(`${relative19(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
34750
35053
  if (previous !== generated.workflow) {
34751
- await mkdir19(dirname17(path), { recursive: true });
35054
+ await mkdir20(dirname18(path), { recursive: true });
34752
35055
  await writeFile19(path, generated.workflow);
34753
35056
  }
34754
35057
  return {
@@ -34767,20 +35070,20 @@ init_materializedBundle();
34767
35070
  init_mobilePreview();
34768
35071
 
34769
35072
  // src/mobile/nativeDeepLinks.ts
34770
- import { readFile as readFile24, rename as rename15, writeFile as writeFile20 } from "fs/promises";
34771
- import { join as join27 } from "path";
35073
+ import { readFile as readFile25, rename as rename16, writeFile as writeFile20 } from "fs/promises";
35074
+ import { join as join28 } from "path";
34772
35075
  var START_MARKER = "<!-- absolutejs:deep-links:start -->";
34773
35076
  var END_MARKER = "<!-- absolutejs:deep-links:end -->";
34774
35077
  var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
34775
35078
  var NOT_FOUND = -1;
34776
35079
  var escapeXml = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
34777
35080
  var writeChangedFile = async (path, source) => {
34778
- const current = await readFile24(path, "utf8");
35081
+ const current = await readFile25(path, "utf8");
34779
35082
  if (current === source)
34780
35083
  return false;
34781
35084
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
34782
35085
  await writeFile20(temporary, source, { flag: "wx" });
34783
- await rename15(temporary, path);
35086
+ await rename16(temporary, path);
34784
35087
  return true;
34785
35088
  };
34786
35089
  var replaceManagedRegion = (source, region, insertAt) => {
@@ -34825,8 +35128,8 @@ ${hosts}
34825
35128
  `;
34826
35129
  };
34827
35130
  var configureAndroid = async (config) => {
34828
- const path = join27(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
34829
- const source = await readFile24(path, "utf8");
35131
+ const path = join28(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
35132
+ const source = await readFile25(path, "utf8");
34830
35133
  const mainActivity = source.indexOf('android:name=".MainActivity"');
34831
35134
  if (mainActivity === NOT_FOUND) {
34832
35135
  throw new TypeError("Android MainActivity was not found.");
@@ -34851,8 +35154,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
34851
35154
  ${END_MARKER}
34852
35155
  `;
34853
35156
  var configureIosInfo = async (config) => {
34854
- const path = join27(config.nativeProjectDirectory, "ios/App/App/Info.plist");
34855
- const source = await readFile24(path, "utf8");
35157
+ const path = join28(config.nativeProjectDirectory, "ios/App/App/Info.plist");
35158
+ const source = await readFile25(path, "utf8");
34856
35159
  const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
34857
35160
  ${END_MARKER}
34858
35161
  `;
@@ -34875,10 +35178,10 @@ ${domains}
34875
35178
  `;
34876
35179
  };
34877
35180
  var configureIosEntitlements = async (config) => {
34878
- const path = join27(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
35181
+ const path = join28(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
34879
35182
  let current = "";
34880
35183
  try {
34881
- current = await readFile24(path, "utf8");
35184
+ current = await readFile25(path, "utf8");
34882
35185
  } catch (error) {
34883
35186
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
34884
35187
  throw error;
@@ -34889,12 +35192,12 @@ var configureIosEntitlements = async (config) => {
34889
35192
  return false;
34890
35193
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
34891
35194
  await writeFile20(temporary, source, { flag: "wx" });
34892
- await rename15(temporary, path);
35195
+ await rename16(temporary, path);
34893
35196
  return true;
34894
35197
  };
34895
35198
  var configureIosProject = async (config) => {
34896
- const path = join27(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
34897
- const source = await readFile24(path, "utf8");
35199
+ const path = join28(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
35200
+ const source = await readFile25(path, "utf8");
34898
35201
  const declarations = [
34899
35202
  ...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
34900
35203
  ].map((match) => match[1]);
@@ -34933,8 +35236,8 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
34933
35236
  };
34934
35237
  // src/mobile/nativeDeviceCapabilities.ts
34935
35238
  init_deviceCapabilities();
34936
- import { readFile as readFile25, rename as rename16, writeFile as writeFile21 } from "fs/promises";
34937
- import { join as join28 } from "path";
35239
+ import { readFile as readFile26, rename as rename17, writeFile as writeFile21 } from "fs/promises";
35240
+ import { join as join29 } from "path";
34938
35241
  var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
34939
35242
  var END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->";
34940
35243
  var NOT_FOUND2 = -1;
@@ -34944,12 +35247,12 @@ var PUSH_START_MARKER = "absolutejs:push-notifications:start";
34944
35247
  var PUSH_END_MARKER = "absolutejs:push-notifications:end";
34945
35248
  var escapeXml2 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
34946
35249
  var writeChangedFile2 = async (path, source) => {
34947
- const current = await readFile25(path, "utf8");
35250
+ const current = await readFile26(path, "utf8");
34948
35251
  if (current === source)
34949
35252
  return false;
34950
35253
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
34951
35254
  await writeFile21(temporary, source, { flag: "wx" });
34952
- await rename16(temporary, path);
35255
+ await rename17(temporary, path);
34953
35256
  return true;
34954
35257
  };
34955
35258
  var writeOptionalChangedFile = async (path, source) => {
@@ -34964,7 +35267,7 @@ var writeOptionalChangedFile = async (path, source) => {
34964
35267
  };
34965
35268
  var optionalSource = async (path) => {
34966
35269
  try {
34967
- return await readFile25(path, "utf8");
35270
+ return await readFile26(path, "utf8");
34968
35271
  } catch (error) {
34969
35272
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
34970
35273
  return null;
@@ -35084,8 +35387,8 @@ var writeIosPrivacyManifest = async (path, current, source) => {
35084
35387
  var configureIosPrivacyProject = async (config, requirements) => {
35085
35388
  if (requirements.iosPrivacyAccessedApis.length === 0)
35086
35389
  return false;
35087
- const projectPath3 = join28(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
35088
- const project = await readFile25(projectPath3, "utf8");
35390
+ const projectPath3 = join29(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
35391
+ const project = await readFile26(projectPath3, "utf8");
35089
35392
  return writeChangedFile2(projectPath3, addIosPrivacyProjectReference(project));
35090
35393
  };
35091
35394
  var addIosPrivacyProjectReference = (source) => {
@@ -35137,8 +35440,8 @@ ${next.slice(index)}`;
35137
35440
  return next;
35138
35441
  };
35139
35442
  var configureIos2 = async (config, plan) => {
35140
- const path = join28(config.nativeProjectDirectory, "ios/App/App/Info.plist");
35141
- const source = await readFile25(path, "utf8");
35443
+ const path = join29(config.nativeProjectDirectory, "ios/App/App/Info.plist");
35444
+ const source = await readFile26(path, "utf8");
35142
35445
  const requirements = absoluteDeviceNativeRequirements(plan);
35143
35446
  const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
35144
35447
  const ownedStart = source.indexOf(START_MARKER2);
@@ -35158,7 +35461,7 @@ ${content}
35158
35461
  ${END_MARKER2}
35159
35462
  ` : "";
35160
35463
  const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
35161
- const privacyPath = join28(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
35464
+ const privacyPath = join29(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
35162
35465
  const privacyCurrent = await optionalSource(privacyPath);
35163
35466
  const privacySource = privacyManifestSource(privacyCurrent, requirements);
35164
35467
  const [privacyChanged, projectChanged, pushChanged] = await Promise.all([
@@ -35169,7 +35472,7 @@ ${content}
35169
35472
  return infoChanged || privacyChanged || projectChanged || pushChanged;
35170
35473
  };
35171
35474
  var configureIosPushNotifications = async (config, enabled) => {
35172
- const entitlementsPath = join28(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
35475
+ const entitlementsPath = join29(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
35173
35476
  const entitlements = await optionalSource(entitlementsPath);
35174
35477
  if (entitlements === null && !enabled)
35175
35478
  return false;
@@ -35181,7 +35484,7 @@ var configureIosPushNotifications = async (config, enabled) => {
35181
35484
  <!-- ${PUSH_END_MARKER} -->
35182
35485
  ` : "";
35183
35486
  const nextEntitlements = replacePushRegion(entitlements, entitlementRegion, entitlements.lastIndexOf("</dict>"));
35184
- const delegatePath = join28(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
35487
+ const delegatePath = join29(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
35185
35488
  const delegate = await optionalSource(delegatePath);
35186
35489
  if (delegate === null && !enabled)
35187
35490
  return false;
@@ -35230,8 +35533,8 @@ var replacePushRegion = (source, region, insertion) => {
35230
35533
  return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
35231
35534
  };
35232
35535
  var configureAndroid2 = async (config, plan) => {
35233
- const path = join28(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
35234
- const source = await readFile25(path, "utf8");
35536
+ const path = join29(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
35537
+ const source = await readFile26(path, "utf8");
35235
35538
  const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
35236
35539
  const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
35237
35540
  `);
@@ -35266,7 +35569,7 @@ ${content}
35266
35569
  throw new TypeError(`Android google-services.json does not contain package ${config.appId}.`);
35267
35570
  const [manifestChanged, firebaseChanged] = await Promise.all([
35268
35571
  writeChangedFile2(path, nextManifest),
35269
- writeOptionalChangedFile(join28(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
35572
+ writeOptionalChangedFile(join29(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
35270
35573
  ]);
35271
35574
  return manifestChanged || firebaseChanged;
35272
35575
  };
@@ -35285,14 +35588,14 @@ init_nativeAuth();
35285
35588
 
35286
35589
  // src/mobile/nativeUpdates.ts
35287
35590
  import {
35288
- mkdir as mkdir20,
35289
- readFile as readFile26,
35290
- readdir as readdir8,
35291
- rename as rename17,
35292
- rm as rm15,
35591
+ mkdir as mkdir21,
35592
+ readFile as readFile27,
35593
+ readdir as readdir9,
35594
+ rename as rename18,
35595
+ rm as rm16,
35293
35596
  writeFile as writeFile22
35294
35597
  } from "fs/promises";
35295
- import { dirname as dirname19, join as join29 } from "path";
35598
+ import { dirname as dirname20, join as join30 } from "path";
35296
35599
  var START = "// absolutejs:mobile-updates:start";
35297
35600
  var END = "// absolutejs:mobile-updates:end";
35298
35601
  var PLUGIN_START = "// absolutejs:mobile-update-plugin:start";
@@ -35300,32 +35603,32 @@ var PLUGIN_END = "// absolutejs:mobile-update-plugin:end";
35300
35603
  var ANDROID_PLUGIN = "AbsoluteMobileUpdateWatchdogPlugin.java";
35301
35604
  var optionalSource2 = async (path) => {
35302
35605
  try {
35303
- return await readFile26(path, "utf8");
35606
+ return await readFile27(path, "utf8");
35304
35607
  } catch (error) {
35305
35608
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
35306
35609
  return null;
35307
35610
  throw error;
35308
35611
  }
35309
35612
  };
35310
- var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35613
+ var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35311
35614
  var writeChanged = async (path, source) => {
35312
35615
  const current = await optionalSource2(path);
35313
35616
  if (current === source)
35314
35617
  return false;
35315
- await mkdir20(dirname19(path), { recursive: true });
35618
+ await mkdir21(dirname20(path), { recursive: true });
35316
35619
  if (current === null) {
35317
35620
  await writeFile22(path, source, { flag: "wx" });
35318
35621
  return true;
35319
35622
  }
35320
35623
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
35321
35624
  await writeFile22(temporary, source, { flag: "wx" });
35322
- await rename17(temporary, path);
35625
+ await rename18(temporary, path);
35323
35626
  return true;
35324
35627
  };
35325
35628
  var removeOwnedFile = async (path) => {
35326
35629
  if (await optionalSource2(path) === null)
35327
35630
  return false;
35328
- await rm15(path);
35631
+ await rm16(path);
35329
35632
  return true;
35330
35633
  };
35331
35634
  var replaceMarkedRegion = (source, start, end, region, insertion, error) => {
@@ -35462,9 +35765,9 @@ public final class AbsoluteMobileUpdateWatchdogPlugin: CAPPlugin, CAPBridgedPlug
35462
35765
  ${PLUGIN_END}
35463
35766
  `;
35464
35767
  var configureIos3 = async (config, enabled) => {
35465
- const path = join29(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
35466
- const capacitorConfigPath = join29(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
35467
- let source = await readFile26(path, "utf8");
35768
+ const path = join30(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
35769
+ const capacitorConfigPath = join30(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
35770
+ let source = await readFile27(path, "utf8");
35468
35771
  const launch2 = source.indexOf("didFinishLaunchingWithOptions");
35469
35772
  const brace = launch2 < 0 ? -1 : source.indexOf("{", launch2);
35470
35773
  const launchRegion = enabled ? ` ${START}
@@ -35475,11 +35778,11 @@ var configureIos3 = async (config, enabled) => {
35475
35778
  `, brace) + 1, "Could not find a safe iOS location for mobile update recovery.");
35476
35779
  const plugin = enabled ? iosPluginRegion(config.updates?.bootTimeoutMs ?? 20000) : "";
35477
35780
  source = replaceMarkedRegion(source, PLUGIN_START, PLUGIN_END, plugin, source.length, "Could not find a safe iOS location for the mobile update watchdog.");
35478
- const capacitorConfigSource2 = await readFile26(capacitorConfigPath, "utf8");
35781
+ const capacitorConfigSource2 = await readFile27(capacitorConfigPath, "utf8");
35479
35782
  let capacitorConfig;
35480
35783
  try {
35481
35784
  const value = JSON.parse(capacitorConfigSource2);
35482
- if (!isRecord15(value))
35785
+ if (!isRecord16(value))
35483
35786
  throw new TypeError;
35484
35787
  capacitorConfig = value;
35485
35788
  } catch {
@@ -35648,19 +35951,19 @@ public final class AbsoluteMobileUpdateWatchdogPlugin extends Plugin {
35648
35951
  var androidActivityInRoot = async (root) => {
35649
35952
  let entries;
35650
35953
  try {
35651
- entries = await readdir8(root, { recursive: true });
35954
+ entries = await readdir9(root, { recursive: true });
35652
35955
  } catch (error) {
35653
35956
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
35654
35957
  return;
35655
35958
  throw error;
35656
35959
  }
35657
35960
  const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
35658
- return candidate ? join29(root, candidate) : undefined;
35961
+ return candidate ? join30(root, candidate) : undefined;
35659
35962
  };
35660
35963
  var androidActivity = async (config) => {
35661
35964
  const roots = [
35662
- join29(config.nativeProjectDirectory, "android/app/src/main/java"),
35663
- join29(config.nativeProjectDirectory, "android/app/src/main/kotlin")
35965
+ join30(config.nativeProjectDirectory, "android/app/src/main/java"),
35966
+ join30(config.nativeProjectDirectory, "android/app/src/main/kotlin")
35664
35967
  ];
35665
35968
  const candidates = await Promise.all(roots.map(androidActivityInRoot));
35666
35969
  const candidate = candidates.find((value) => value !== undefined);
@@ -35759,12 +36062,12 @@ var injectAndroidActivity = (source, enabled) => {
35759
36062
  };
35760
36063
  var configureAndroid3 = async (config, enabled) => {
35761
36064
  const activityPath = await androidActivity(config);
35762
- const activity = await readFile26(activityPath, "utf8");
36065
+ const activity = await readFile27(activityPath, "utf8");
35763
36066
  const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
35764
36067
  if (!packageName)
35765
36068
  throw new TypeError("Android MainActivity package declaration was not found.");
35766
36069
  const packagePath = packageName.replaceAll(".", "/");
35767
- const pluginPath = join29(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
36070
+ const pluginPath = join30(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
35768
36071
  const changed = await Promise.all([
35769
36072
  writeChanged(activityPath, injectAndroidActivity(activity, enabled)),
35770
36073
  enabled ? writeChanged(pluginPath, androidPluginSource(packageName, config.updates?.bootTimeoutMs ?? 20000)) : removeOwnedFile(pluginPath)
@@ -35784,14 +36087,14 @@ var applyAbsoluteNativeUpdates = async (config, platforms = config.platforms) =>
35784
36087
  };
35785
36088
  // src/mobile/nativeObservability.ts
35786
36089
  import {
35787
- mkdir as mkdir21,
35788
- readFile as readFile27,
35789
- readdir as readdir9,
35790
- rename as rename18,
35791
- rm as rm16,
36090
+ mkdir as mkdir22,
36091
+ readFile as readFile28,
36092
+ readdir as readdir10,
36093
+ rename as rename19,
36094
+ rm as rm17,
35792
36095
  writeFile as writeFile23
35793
36096
  } from "fs/promises";
35794
- import { dirname as dirname20, join as join30 } from "path";
36097
+ import { dirname as dirname21, join as join31 } from "path";
35795
36098
  var START2 = "// absolutejs:native-observability:start";
35796
36099
  var END2 = "// absolutejs:native-observability:end";
35797
36100
  var PLUGIN_START2 = "// absolutejs:native-observability-plugin:start";
@@ -35799,7 +36102,7 @@ var PLUGIN_END2 = "// absolutejs:native-observability-plugin:end";
35799
36102
  var ANDROID_PLUGIN2 = "AbsoluteMobileObservabilityPlugin.java";
35800
36103
  var optionalSource3 = async (path) => {
35801
36104
  try {
35802
- return await readFile27(path, "utf8");
36105
+ return await readFile28(path, "utf8");
35803
36106
  } catch (error) {
35804
36107
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
35805
36108
  return null;
@@ -35810,23 +36113,23 @@ var writeChanged2 = async (path, source) => {
35810
36113
  const current = await optionalSource3(path);
35811
36114
  if (current === source)
35812
36115
  return false;
35813
- await mkdir21(dirname20(path), { recursive: true });
36116
+ await mkdir22(dirname21(path), { recursive: true });
35814
36117
  if (current === null) {
35815
36118
  await writeFile23(path, source, { flag: "wx" });
35816
36119
  return true;
35817
36120
  }
35818
36121
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
35819
36122
  await writeFile23(temporary, source, { flag: "wx" });
35820
- await rename18(temporary, path);
36123
+ await rename19(temporary, path);
35821
36124
  return true;
35822
36125
  };
35823
36126
  var removeOwnedFile2 = async (path) => {
35824
36127
  if (await optionalSource3(path) === null)
35825
36128
  return false;
35826
- await rm16(path);
36129
+ await rm17(path);
35827
36130
  return true;
35828
36131
  };
35829
- var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36132
+ var isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35830
36133
  var replaceMarkedRegion2 = (source, start, end, region, insertion, error) => {
35831
36134
  const existingStart = source.indexOf(start);
35832
36135
  const existingEnd = source.indexOf(end);
@@ -35959,9 +36262,9 @@ public final class AbsoluteMobileObservabilityPlugin: CAPPlugin, CAPBridgedPlugi
35959
36262
  ${PLUGIN_END2}
35960
36263
  `;
35961
36264
  var configureIos4 = async (config, enabled) => {
35962
- const path = join30(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
35963
- const capacitorConfigPath = join30(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
35964
- let source = await readFile27(path, "utf8");
36265
+ const path = join31(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
36266
+ const capacitorConfigPath = join31(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
36267
+ let source = await readFile28(path, "utf8");
35965
36268
  if (enabled && !source.includes("import MetricKit")) {
35966
36269
  const capacitorImport = source.lastIndexOf("import Capacitor");
35967
36270
  if (capacitorImport < 0)
@@ -35975,11 +36278,11 @@ ${source.slice(newline + 1)}`;
35975
36278
  source = source.replace(`import MetricKit
35976
36279
  `, "");
35977
36280
  source = replaceMarkedRegion2(source, PLUGIN_START2, PLUGIN_END2, enabled ? iosPluginRegion2 : "", source.length, "Could not find a safe iOS location for native observability.");
35978
- const capacitorConfigSource2 = await readFile27(capacitorConfigPath, "utf8");
36281
+ const capacitorConfigSource2 = await readFile28(capacitorConfigPath, "utf8");
35979
36282
  let capacitorConfig;
35980
36283
  try {
35981
36284
  const value = JSON.parse(capacitorConfigSource2);
35982
- if (!isRecord16(value))
36285
+ if (!isRecord17(value))
35983
36286
  throw new TypeError;
35984
36287
  capacitorConfig = value;
35985
36288
  } catch {
@@ -36213,19 +36516,19 @@ public final class AbsoluteMobileObservabilityPlugin extends Plugin {
36213
36516
  var androidActivityInRoot2 = async (root) => {
36214
36517
  let entries;
36215
36518
  try {
36216
- entries = await readdir9(root, { recursive: true });
36519
+ entries = await readdir10(root, { recursive: true });
36217
36520
  } catch (error) {
36218
36521
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
36219
36522
  return;
36220
36523
  throw error;
36221
36524
  }
36222
36525
  const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
36223
- return candidate ? join30(root, candidate) : undefined;
36526
+ return candidate ? join31(root, candidate) : undefined;
36224
36527
  };
36225
36528
  var androidActivity2 = async (config) => {
36226
36529
  const roots = [
36227
- join30(config.nativeProjectDirectory, "android/app/src/main/java"),
36228
- join30(config.nativeProjectDirectory, "android/app/src/main/kotlin")
36530
+ join31(config.nativeProjectDirectory, "android/app/src/main/java"),
36531
+ join31(config.nativeProjectDirectory, "android/app/src/main/kotlin")
36229
36532
  ];
36230
36533
  const candidates = await Promise.all(roots.map(androidActivityInRoot2));
36231
36534
  const candidate = candidates.find((value) => value !== undefined);
@@ -36293,11 +36596,11 @@ var injectAndroidActivity2 = (source, enabled) => {
36293
36596
  };
36294
36597
  var configureAndroid4 = async (config, enabled) => {
36295
36598
  const activityPath = await androidActivity2(config);
36296
- const activity = await readFile27(activityPath, "utf8");
36599
+ const activity = await readFile28(activityPath, "utf8");
36297
36600
  const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
36298
36601
  if (!packageName)
36299
36602
  throw new TypeError("Android MainActivity package declaration was not found.");
36300
- const pluginPath = join30(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN2);
36603
+ const pluginPath = join31(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN2);
36301
36604
  const changed = await Promise.all([
36302
36605
  writeChanged2(activityPath, injectAndroidActivity2(activity, enabled)),
36303
36606
  enabled ? writeChanged2(pluginPath, androidPluginSource2(packageName)) : removeOwnedFile2(pluginPath)
@@ -36316,8 +36619,8 @@ var applyAbsoluteNativeObservability = async (config, platforms = config.platfor
36316
36619
  return { changed: changed.some(Boolean) };
36317
36620
  };
36318
36621
  // src/mobile/nativeReleaseReadiness.ts
36319
- import { mkdir as mkdir22, readFile as readFile28, readdir as readdir10, rename as rename19, writeFile as writeFile24 } from "fs/promises";
36320
- import { dirname as dirname21, join as join31 } from "path";
36622
+ import { mkdir as mkdir23, readFile as readFile29, readdir as readdir11, rename as rename20, writeFile as writeFile24 } from "fs/promises";
36623
+ import { dirname as dirname22, join as join32 } from "path";
36321
36624
  var START3 = "// absolutejs:release-readiness:start";
36322
36625
  var END3 = "// absolutejs:release-readiness:end";
36323
36626
  var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
@@ -36325,7 +36628,7 @@ var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
36325
36628
  var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
36326
36629
  var optionalSource4 = async (path) => {
36327
36630
  try {
36328
- return await readFile28(path, "utf8");
36631
+ return await readFile29(path, "utf8");
36329
36632
  } catch (error) {
36330
36633
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
36331
36634
  return null;
@@ -36336,32 +36639,32 @@ var writeChanged3 = async (path, source) => {
36336
36639
  const current = await optionalSource4(path);
36337
36640
  if (current === source)
36338
36641
  return false;
36339
- await mkdir22(dirname21(path), { recursive: true });
36642
+ await mkdir23(dirname22(path), { recursive: true });
36340
36643
  if (current === null) {
36341
36644
  await writeFile24(path, source, { flag: "wx" });
36342
36645
  return true;
36343
36646
  }
36344
36647
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
36345
36648
  await writeFile24(temporary, source, { flag: "wx" });
36346
- await rename19(temporary, path);
36649
+ await rename20(temporary, path);
36347
36650
  return true;
36348
36651
  };
36349
36652
  var activityInRoot = async (root) => {
36350
36653
  let entries;
36351
36654
  try {
36352
- entries = await readdir10(root, { recursive: true });
36655
+ entries = await readdir11(root, { recursive: true });
36353
36656
  } catch (error) {
36354
36657
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
36355
36658
  return;
36356
36659
  throw error;
36357
36660
  }
36358
36661
  const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
36359
- return candidate ? join31(root, candidate) : undefined;
36662
+ return candidate ? join32(root, candidate) : undefined;
36360
36663
  };
36361
36664
  var activityPath = async (config) => {
36362
36665
  const roots = [
36363
- join31(config.nativeProjectDirectory, "android/app/src/main/java"),
36364
- join31(config.nativeProjectDirectory, "android/app/src/main/kotlin")
36666
+ join32(config.nativeProjectDirectory, "android/app/src/main/java"),
36667
+ join32(config.nativeProjectDirectory, "android/app/src/main/kotlin")
36365
36668
  ];
36366
36669
  const candidates = await Promise.all(roots.map(activityInRoot));
36367
36670
  const candidate = candidates.find((value) => value !== undefined);
@@ -36471,9 +36774,9 @@ var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
36471
36774
  return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
36472
36775
  };
36473
36776
  var configureIos5 = async (config) => {
36474
- const appDelegatePath = join31(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
36475
- const capacitorConfigPath = join31(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
36476
- const appDelegate = await readFile28(appDelegatePath, "utf8");
36777
+ const appDelegatePath = join32(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
36778
+ const capacitorConfigPath = join32(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
36779
+ const appDelegate = await readFile29(appDelegatePath, "utf8");
36477
36780
  const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
36478
36781
  const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
36479
36782
  if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
@@ -36483,7 +36786,7 @@ var configureIos5 = async (config) => {
36483
36786
  ${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
36484
36787
  let capacitorConfig;
36485
36788
  try {
36486
- capacitorConfig = JSON.parse(await readFile28(capacitorConfigPath, "utf8"));
36789
+ capacitorConfig = JSON.parse(await readFile29(capacitorConfigPath, "utf8"));
36487
36790
  } catch {
36488
36791
  throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
36489
36792
  }
@@ -36508,11 +36811,11 @@ ${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existi
36508
36811
  };
36509
36812
  var configureAndroid5 = async (config) => {
36510
36813
  const mainActivityPath = await activityPath(config);
36511
- const activity = await readFile28(mainActivityPath, "utf8");
36814
+ const activity = await readFile29(mainActivityPath, "utf8");
36512
36815
  const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
36513
36816
  if (!packageName)
36514
36817
  throw new TypeError("Android MainActivity package declaration was not found.");
36515
- const pluginPath = join31(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN3);
36818
+ const pluginPath = join32(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN3);
36516
36819
  const changed = await Promise.all([
36517
36820
  writeChanged3(mainActivityPath, injectRegistration(activity)),
36518
36821
  writeChanged3(pluginPath, pluginSource(packageName))
@@ -36535,8 +36838,8 @@ init_producerContext();
36535
36838
  init_releaseArtifact();
36536
36839
 
36537
36840
  // src/mobile/releasePublisher.ts
36538
- import { access as access20 } from "fs/promises";
36539
- import { isAbsolute as isAbsolute12, relative as relative19, resolve as resolve21, sep as sep14 } from "path";
36841
+ import { access as access21 } from "fs/promises";
36842
+ import { isAbsolute as isAbsolute13, relative as relative20, resolve as resolve22, sep as sep15 } from "path";
36540
36843
  import { pathToFileURL as pathToFileURL3 } from "url";
36541
36844
  var prepareAbsoluteIosRelease = async (publisher, options) => {
36542
36845
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -36570,24 +36873,24 @@ var requireCertificationReceipt = (publication, certification, requirement, rele
36570
36873
  if (verification && (!receipt.provenance || receipt.provenance.subject !== releaseId))
36571
36874
  throw new TypeError("Native release registry did not retain trusted certification provenance.");
36572
36875
  };
36573
- var isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36574
- var isPublisher = (value) => isRecord17(value) && typeof value.publish === "function";
36876
+ var isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36877
+ var isPublisher = (value) => isRecord18(value) && typeof value.publish === "function";
36575
36878
  var publisherModulePath = (projectRoot, requested) => {
36576
- const root = resolve21(projectRoot);
36577
- const path = resolve21(root, requested);
36578
- const projectRelative = relative19(root, path);
36579
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep14}`) || isAbsolute12(projectRelative)) {
36879
+ const root = resolve22(projectRoot);
36880
+ const path = resolve22(root, requested);
36881
+ const projectRelative = relative20(root, path);
36882
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep15}`) || isAbsolute13(projectRelative)) {
36580
36883
  throw new TypeError("mobile publish --registry must remain inside the project.");
36581
36884
  }
36582
36885
  return path;
36583
36886
  };
36584
36887
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
36585
36888
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
36586
- await access20(modulePath).catch(() => {
36889
+ await access21(modulePath).catch(() => {
36587
36890
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
36588
36891
  });
36589
36892
  const loaded = await import(pathToFileURL3(modulePath).href);
36590
- const publisher = isRecord17(loaded) ? loaded.default ?? loaded.registry : undefined;
36893
+ const publisher = isRecord18(loaded) ? loaded.default ?? loaded.registry : undefined;
36591
36894
  if (!isPublisher(publisher)) {
36592
36895
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
36593
36896
  }
@@ -36669,7 +36972,7 @@ var publishAbsoluteIosRelease = async (options) => {
36669
36972
  init_pageProtocol();
36670
36973
  init_releaseArtifact();
36671
36974
  import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
36672
- import { dirname as dirname22, extname as extname6, relative as relative20, resolve as resolve22 } from "path";
36975
+ import { dirname as dirname23, extname as extname6, relative as relative21, resolve as resolve23 } from "path";
36673
36976
  import ts2 from "typescript";
36674
36977
  var ROUTE_METHODS = new Set(["get", "head"]);
36675
36978
  var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
@@ -36720,7 +37023,7 @@ var PAGE_HANDLERS = new Map([
36720
37023
  ]
36721
37024
  ]);
36722
37025
  var posixPath = (value) => value.replace(/\\/g, "/");
36723
- var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname22(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
37026
+ var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname23(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
36724
37027
  var createProgram = (entry, projectRoot) => {
36725
37028
  const configPath2 = findTsconfig(entry, projectRoot);
36726
37029
  if (!configPath2) {
@@ -36732,7 +37035,7 @@ var createProgram = (entry, projectRoot) => {
36732
37035
  target: ts2.ScriptTarget.ESNext
36733
37036
  });
36734
37037
  }
36735
- const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync8(path, "utf8")).config, ts2.sys, dirname22(configPath2));
37038
+ const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync8(path, "utf8")).config, ts2.sys, dirname23(configPath2));
36736
37039
  if (!parsed.fileNames.includes(entry))
36737
37040
  parsed.fileNames.push(entry);
36738
37041
  return ts2.createProgram(parsed.fileNames, parsed.options);
@@ -36838,7 +37141,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
36838
37141
  const declaration = symbol?.declarations?.[0];
36839
37142
  const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
36840
37143
  const exportedName = symbol?.name ?? expression.getText(sourceFile);
36841
- const source = posixPath(relative20(projectRoot, file));
37144
+ const source = posixPath(relative21(projectRoot, file));
36842
37145
  return `${source}#${exportedName}`;
36843
37146
  };
36844
37147
  var resolveAlias = (symbol, checker) => {
@@ -37077,7 +37380,7 @@ var analyzeProgram = (program, projectRoot) => {
37077
37380
  const checker = program.getTypeChecker();
37078
37381
  const analyzed = new Map;
37079
37382
  for (const sourceFile of program.getSourceFiles()) {
37080
- const resolvedFile = resolve22(sourceFile.fileName);
37383
+ const resolvedFile = resolve23(sourceFile.fileName);
37081
37384
  if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
37082
37385
  continue;
37083
37386
  const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
@@ -37170,14 +37473,14 @@ var transformFile = (source, fileName, analysis) => {
37170
37473
  };
37171
37474
  var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
37172
37475
  var createAbsoluteMobileRouteMetadataPlugin = (options) => {
37173
- const projectRoot = resolve22(options.projectRoot ?? process.cwd());
37174
- const entry = resolve22(options.entry);
37476
+ const projectRoot = resolve23(options.projectRoot ?? process.cwd());
37477
+ const entry = resolve23(options.entry);
37175
37478
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
37176
37479
  return {
37177
37480
  name: "absolute-mobile-route-metadata",
37178
37481
  setup(build) {
37179
37482
  build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
37180
- const analysis = analyzed.get(resolve22(path));
37483
+ const analysis = analyzed.get(resolve23(path));
37181
37484
  if (!analysis)
37182
37485
  return;
37183
37486
  const source = await Bun.file(path).text();
@@ -37190,11 +37493,11 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
37190
37493
  };
37191
37494
  };
37192
37495
  var inspectAbsoluteMobileRouteMetadata = (options) => {
37193
- const projectRoot = resolve22(options.projectRoot ?? process.cwd());
37194
- const entry = resolve22(options.entry);
37496
+ const projectRoot = resolve23(options.projectRoot ?? process.cwd());
37497
+ const entry = resolve23(options.entry);
37195
37498
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
37196
37499
  return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
37197
- file: posixPath(relative20(projectRoot, file)),
37500
+ file: posixPath(relative21(projectRoot, file)),
37198
37501
  metadata
37199
37502
  })));
37200
37503
  };
@@ -37528,7 +37831,7 @@ var createCapacitor = (win) => {
37528
37831
  callbackId
37529
37832
  }, callback);
37530
37833
  };
37531
- const p = new Promise((resolve23) => call.then(() => resolve23({ remove })));
37834
+ const p = new Promise((resolve24) => call.then(() => resolve24({ remove })));
37532
37835
  p.remove = async () => {
37533
37836
  console.warn(`Using addListener() without 'await' is deprecated.`);
37534
37837
  await remove();
@@ -37733,11 +38036,11 @@ class CapacitorCookiesPluginWeb extends WebPlugin {
37733
38036
  var CapacitorCookies = registerPlugin("CapacitorCookies", {
37734
38037
  web: () => new CapacitorCookiesPluginWeb
37735
38038
  });
37736
- var readBlobAsBase64 = async (blob) => new Promise((resolve23, reject) => {
38039
+ var readBlobAsBase64 = async (blob) => new Promise((resolve24, reject) => {
37737
38040
  const reader = new FileReader;
37738
38041
  reader.onload = () => {
37739
38042
  const base64String = reader.result;
37740
- resolve23(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
38043
+ resolve24(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
37741
38044
  };
37742
38045
  reader.onerror = (error) => reject(error);
37743
38046
  reader.readAsDataURL(blob);
@@ -37913,7 +38216,7 @@ var REPORT_KINDS = new Set([
37913
38216
  "signal"
37914
38217
  ]);
37915
38218
  var nativeObservability = registerPlugin("AbsoluteMobileObservability");
37916
- var isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
38219
+ var isRecord19 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37917
38220
  var redactText = (value) => value.slice(0, MAX_STRING_LENGTH).replace(/\b(Bearer)\s+[A-Za-z0-9._~+/-]+=*/giu, "$1 [REDACTED]").replace(/([?&](?:access_token|api_?key|authorization|code|id_token|password|refresh_token|secret|token)=)[^&#\s]*/giu, "$1[REDACTED]").replace(/\b((?:access_?token|api_?key|authorization|client_?secret|cookie|id_?token|password|passwd|refresh_?token|secret|token)\s*[:=]\s*)[^,\s;]+/giu, "$1[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]");
37918
38221
  var redactValue = (value, depth = 0) => {
37919
38222
  if (depth >= MAX_REDACTION_DEPTH)
@@ -37924,7 +38227,7 @@ var redactValue = (value, depth = 0) => {
37924
38227
  return value;
37925
38228
  if (Array.isArray(value))
37926
38229
  return value.slice(0, 64).map((entry) => redactValue(entry, depth + 1));
37927
- if (!isRecord18(value))
38230
+ if (!isRecord19(value))
37928
38231
  return String(value).slice(0, MAX_STRING_LENGTH);
37929
38232
  return Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [
37930
38233
  key,
@@ -37932,7 +38235,7 @@ var redactValue = (value, depth = 0) => {
37932
38235
  ]));
37933
38236
  };
37934
38237
  var parseReport = (value) => {
37935
- if (!isRecord18(value) || !isRecord18(value.details))
38238
+ if (!isRecord19(value) || !isRecord19(value.details))
37936
38239
  return;
37937
38240
  if (typeof value.id !== "string" || !REPORT_ID.test(value.id))
37938
38241
  return;
@@ -37943,7 +38246,7 @@ var parseReport = (value) => {
37943
38246
  if (value.platform !== "android" && value.platform !== "ios")
37944
38247
  return;
37945
38248
  const details = redactValue(value.details);
37946
- if (!isRecord18(details))
38249
+ if (!isRecord19(details))
37947
38250
  return;
37948
38251
  if (new TextEncoder().encode(JSON.stringify(details)).byteLength > MAX_DETAIL_BYTES)
37949
38252
  return;
@@ -40755,13 +41058,13 @@ var createBeacon = (options) => {
40755
41058
  }
40756
41059
  };
40757
41060
  const documentStylesAreReady = () => {
40758
- const stylesheets = Array.from(document.querySelectorAll('link[rel~="stylesheet"]')).filter((link) => {
40759
- if (link.disabled)
41061
+ const stylesheets = Array.from(document.querySelectorAll('link[rel~="stylesheet"]')).filter((link2) => {
41062
+ if (link2.disabled)
40760
41063
  return false;
40761
- const media = link.media.trim();
41064
+ const media = link2.media.trim();
40762
41065
  return media === "" || typeof window.matchMedia !== "function" || window.matchMedia(media).matches;
40763
41066
  });
40764
- if (!stylesheets.every((link) => link.sheet !== null))
41067
+ if (!stylesheets.every((link2) => link2.sheet !== null))
40765
41068
  return false;
40766
41069
  if (stylesheets.length === 0)
40767
41070
  return true;
@@ -42397,8 +42700,8 @@ var createBeacon = (options) => {
42397
42700
  if (await fontAvailable(element))
42398
42701
  return;
42399
42702
  if (confirmationMs > 0) {
42400
- await new Promise((resolve23) => {
42401
- const timer2 = window.setTimeout(resolve23, confirmationMs);
42703
+ await new Promise((resolve24) => {
42704
+ const timer2 = window.setTimeout(resolve24, confirmationMs);
42402
42705
  cleanups.push(() => window.clearTimeout(timer2));
42403
42706
  });
42404
42707
  if (document.visibilityState === "hidden")
@@ -42891,8 +43194,8 @@ var sheetById = (id) => {
42891
43194
  const target = document.getElementById(id);
42892
43195
  return target instanceof HTMLDialogElement && target.dataset.absoluteSheet !== undefined ? target : undefined;
42893
43196
  };
42894
- var dispatchSheet = (sheet, open) => dispatchEvent(new CustomEvent(SHEET_EVENT, {
42895
- detail: { id: sheet.id, open }
43197
+ var dispatchSheet = (sheet, open2) => dispatchEvent(new CustomEvent(SHEET_EVENT, {
43198
+ detail: { id: sheet.id, open: open2 }
42896
43199
  }));
42897
43200
  var focusSheet = (sheet) => {
42898
43201
  const target = sheet.querySelector('[autofocus], button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
@@ -43386,51 +43689,51 @@ var createAbsoluteMobileUpdateClient = (options) => {
43386
43689
  };
43387
43690
  };
43388
43691
  // src/mobile/updatePublisher.ts
43389
- import { access as access21 } from "fs/promises";
43390
- import { isAbsolute as isAbsolute13, relative as relative22, resolve as resolve24, sep as sep15 } from "path";
43692
+ import { access as access22 } from "fs/promises";
43693
+ import { isAbsolute as isAbsolute14, relative as relative23, resolve as resolve25, sep as sep16 } from "path";
43391
43694
  import { pathToFileURL as pathToFileURL4 } from "url";
43392
43695
 
43393
43696
  // src/mobile/updateSigning.ts
43394
43697
  import { createHash as createHash17, sign, verify } from "crypto";
43395
43698
  import {
43396
43699
  cp as cp2,
43397
- mkdir as mkdir23,
43700
+ mkdir as mkdir24,
43398
43701
  mkdtemp as mkdtemp12,
43399
- readdir as readdir11,
43400
- readFile as readFile29,
43401
- rename as rename20,
43402
- rm as rm17,
43702
+ readdir as readdir12,
43703
+ readFile as readFile30,
43704
+ rename as rename21,
43705
+ rm as rm18,
43403
43706
  stat as stat8,
43404
43707
  writeFile as writeFile25
43405
43708
  } from "fs/promises";
43406
- import { dirname as dirname23, join as join32, relative as relative21, resolve as resolve23 } from "path";
43709
+ import { dirname as dirname24, join as join33, relative as relative22, resolve as resolve24 } from "path";
43407
43710
  var UPDATE_MANIFEST_FILE = "update.json";
43408
43711
  var UPDATE_FILES_DIRECTORY = "files";
43409
43712
  var sha2565 = (value) => createHash17("sha256").update(value).digest("hex");
43410
43713
  var listFiles = async (root, directory = root) => {
43411
- const entries = await readdir11(directory, { withFileTypes: true });
43714
+ const entries = await readdir12(directory, { withFileTypes: true });
43412
43715
  const paths = await Promise.all(entries.map(async (entry) => {
43413
- const path = join32(directory, entry.name);
43716
+ const path = join33(directory, entry.name);
43414
43717
  if (entry.isDirectory())
43415
43718
  return listFiles(root, path);
43416
43719
  if (!entry.isFile())
43417
43720
  throw new TypeError("Mobile updates cannot contain links or special files.");
43418
- return [relative21(root, path).replaceAll("\\", "/")];
43721
+ return [relative22(root, path).replaceAll("\\", "/")];
43419
43722
  }));
43420
43723
  return paths.flat().sort((left, right) => left.localeCompare(right));
43421
43724
  };
43422
43725
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
43423
- const source = join32(root, path);
43726
+ const source = join33(root, path);
43424
43727
  const [metadata, contents] = await Promise.all([
43425
43728
  stat8(source),
43426
- readFile29(source)
43729
+ readFile30(source)
43427
43730
  ]);
43428
43731
  return { bytes: metadata.size, path, sha256: sha2565(contents) };
43429
43732
  }));
43430
43733
  var releaseIdFor = (value) => `amu_${sha2565(canonicalizeAbsoluteMobileUpdate(value))}`;
43431
43734
  var buildAbsoluteMobileUpdate = async (options) => {
43432
- const bundleDirectory = resolve23(options.bundleDirectory);
43433
- const outputRoot = resolve23(options.outputDirectory);
43735
+ const bundleDirectory = resolve24(options.bundleDirectory);
43736
+ const outputRoot = resolve24(options.outputDirectory);
43434
43737
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
43435
43738
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
43436
43739
  const paths = await listFiles(bundleDirectory);
@@ -43458,38 +43761,38 @@ var buildAbsoluteMobileUpdate = async (options) => {
43458
43761
  value: signature.toString("base64")
43459
43762
  }
43460
43763
  });
43461
- await mkdir23(outputRoot, { recursive: true });
43462
- const outputDirectory = join32(outputRoot, manifest.releaseId);
43463
- const staging = await mkdtemp12(join32(outputRoot, ".stage-"));
43764
+ await mkdir24(outputRoot, { recursive: true });
43765
+ const outputDirectory = join33(outputRoot, manifest.releaseId);
43766
+ const staging = await mkdtemp12(join33(outputRoot, ".stage-"));
43464
43767
  try {
43465
- await cp2(bundleDirectory, join32(staging, UPDATE_FILES_DIRECTORY), {
43768
+ await cp2(bundleDirectory, join33(staging, UPDATE_FILES_DIRECTORY), {
43466
43769
  force: true,
43467
43770
  recursive: true
43468
43771
  });
43469
- await writeFile25(join32(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
43772
+ await writeFile25(join33(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
43470
43773
  `);
43471
- await rename20(staging, outputDirectory);
43774
+ await rename21(staging, outputDirectory);
43472
43775
  } catch (error) {
43473
- await rm17(staging, { force: true, recursive: true });
43776
+ await rm18(staging, { force: true, recursive: true });
43474
43777
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
43475
43778
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
43476
43779
  throw error;
43477
43780
  }
43478
43781
  return {
43479
43782
  manifest,
43480
- manifestPath: join32(outputDirectory, UPDATE_MANIFEST_FILE),
43783
+ manifestPath: join33(outputDirectory, UPDATE_MANIFEST_FILE),
43481
43784
  outputDirectory
43482
43785
  };
43483
43786
  };
43484
43787
  var copyAbsoluteMobileUpdateFile = async (updateDirectory, path, destination) => {
43485
- const source = resolve23(updateDirectory, UPDATE_FILES_DIRECTORY, path);
43486
- const root = resolve23(updateDirectory, UPDATE_FILES_DIRECTORY);
43788
+ const source = resolve24(updateDirectory, UPDATE_FILES_DIRECTORY, path);
43789
+ const root = resolve24(updateDirectory, UPDATE_FILES_DIRECTORY);
43487
43790
  if (!source.startsWith(`${root}/`))
43488
43791
  throw new TypeError("Update file escaped its release.");
43489
- await mkdir23(dirname23(destination), { recursive: true });
43792
+ await mkdir24(dirname24(destination), { recursive: true });
43490
43793
  await cp2(source, destination, { force: true });
43491
43794
  };
43492
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile29(join32(resolve23(directory), UPDATE_MANIFEST_FILE), "utf8")));
43795
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile30(join33(resolve24(directory), UPDATE_MANIFEST_FILE), "utf8")));
43493
43796
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
43494
43797
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
43495
43798
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -43606,16 +43909,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
43606
43909
  var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
43607
43910
  var isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
43608
43911
  var projectPath3 = (projectRoot, requested, label) => {
43609
- const root = resolve24(projectRoot);
43610
- const path = resolve24(root, requested);
43611
- const projectRelative = relative22(root, path);
43612
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep15}`) || isAbsolute13(projectRelative))
43912
+ const root = resolve25(projectRoot);
43913
+ const path = resolve25(root, requested);
43914
+ const projectRelative = relative23(root, path);
43915
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep16}`) || isAbsolute14(projectRelative))
43613
43916
  throw new TypeError(`${label} must remain inside the project.`);
43614
43917
  return path;
43615
43918
  };
43616
43919
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
43617
43920
  const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
43618
- await access21(modulePath).catch(() => {
43921
+ await access22(modulePath).catch(() => {
43619
43922
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
43620
43923
  });
43621
43924
  const loaded = await import(pathToFileURL4(modulePath).href);
@@ -43724,6 +44027,7 @@ export {
43724
44027
  ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
43725
44028
  ABSOLUTE_MOBILE_CI_PROMOTION_AUDIT_FORMAT,
43726
44029
  ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT,
44030
+ ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT,
43727
44031
  ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
43728
44032
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
43729
44033
  ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
@@ -43767,6 +44071,7 @@ export {
43767
44071
  absoluteExpoNativeObservabilityPaths,
43768
44072
  absoluteIosDeviceAcceptanceCommands,
43769
44073
  absoluteMobilePreviewDocument,
44074
+ absoluteMobilePromotionOperationPath,
43770
44075
  absoluteMobileUpdateSigningPayload,
43771
44076
  absolutePercentile,
43772
44077
  absolutePssMiB,
@@ -43778,6 +44083,7 @@ export {
43778
44083
  acceptsAbsoluteRouteData,
43779
44084
  acquireAbsoluteRemoteMacReleaseLease,
43780
44085
  activateAbsoluteMobilePage,
44086
+ advanceAbsoluteMobilePromotionOperation,
43781
44087
  advanceAbsoluteMobileUpdateRollout,
43782
44088
  applyAbsoluteNativeDeepLinks,
43783
44089
  applyAbsoluteNativeDeviceCapabilities,
@@ -43819,6 +44125,7 @@ export {
43819
44125
  createAbsoluteMobilePageErrorResponse,
43820
44126
  createAbsoluteMobilePageRequest,
43821
44127
  createAbsoluteMobilePreviewPlugin,
44128
+ createAbsoluteMobilePromotionOperation,
43822
44129
  createAbsoluteMobileReleaseCertification,
43823
44130
  createAbsoluteMobileRouteMetadataPlugin,
43824
44131
  createAbsoluteMobileUpdateClient,
@@ -43829,6 +44136,7 @@ export {
43829
44136
  createAbsoluteRemoteIosDevProject,
43830
44137
  directAbsoluteProjectPackages,
43831
44138
  discoverAbsoluteDeviceCapabilities,
44139
+ discoverAbsoluteMobilePromotionRun,
43832
44140
  discoverAbsoluteSyncSchema,
43833
44141
  disposeAbsoluteMobilePage,
43834
44142
  drainAbsoluteMobileNativeObservability,
@@ -43873,6 +44181,7 @@ export {
43873
44181
  isAbsoluteExpoNativeRootInput,
43874
44182
  isAbsoluteIosNativeRootInput,
43875
44183
  isAbsoluteMobileUpdateRolloutMember,
44184
+ listAbsoluteMobilePromotionOperations,
43876
44185
  listAbsoluteRemoteMacProfiles,
43877
44186
  loadAbsoluteDeviceCapabilityProviders,
43878
44187
  loadAbsoluteMobileMaterializedBundle,
@@ -43909,6 +44218,8 @@ export {
43909
44218
  parseAbsoluteMobilePageEnvelope,
43910
44219
  parseAbsoluteMobilePageRequest,
43911
44220
  parseAbsoluteMobilePromotionContext,
44221
+ parseAbsoluteMobilePromotionOperation,
44222
+ parseAbsoluteMobilePromotionRunList,
43912
44223
  parseAbsoluteMobileUnsignedUpdateManifest,
43913
44224
  parseAbsoluteMobileUpdateManifest,
43914
44225
  parseAbsoluteMobileUpdateRequest,
@@ -43934,11 +44245,13 @@ export {
43934
44245
  readAbsoluteMobileCertificationVerification,
43935
44246
  readAbsoluteMobileLinkIntent,
43936
44247
  readAbsoluteMobileMaterializedReleases,
44248
+ readAbsoluteMobilePromotionOperation,
43937
44249
  readAbsoluteMobileReleaseCertification,
43938
44250
  readAbsoluteMobileUpdate,
43939
44251
  readAbsoluteSigstoreBundle,
43940
44252
  reconcileAbsoluteMobileUpdateRollout,
43941
44253
  redactAbsoluteIosLog,
44254
+ relativeAbsoluteMobilePromotionOperationPath,
43942
44255
  removeAbsoluteRemoteMacProfile,
43943
44256
  renderAbsoluteMobileReleaseCertification,
43944
44257
  renderAbsoluteMobileUpdateRegistry,
@@ -43994,5 +44307,5 @@ export {
43994
44307
  writeAbsoluteMobileUpdateRegistry
43995
44308
  };
43996
44309
 
43997
- //# debugId=1FBA634844700C1464756E2164756E21
44310
+ //# debugId=8439B99573B3AFB964756E2164756E21
43998
44311
  //# sourceMappingURL=index.js.map