@absolutejs/absolute 0.20.0-beta.109 → 0.20.0-beta.110

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.
@@ -20092,7 +20092,7 @@ import {
20092
20092
  verify as verify2,
20093
20093
  X509Certificate as X509Certificate3
20094
20094
  } from "crypto";
20095
- import { readFile as readFile28, stat as stat7 } from "fs/promises";
20095
+ import { readFile as readFile29, stat as stat8 } 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)
@@ -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 readFile28(path.join(localRoot, "update.json"), "utf8")));
21128
+ const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile29(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);
@@ -21143,7 +21143,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21143
21143
  if (!existing) {
21144
21144
  for (const file of manifest.files) {
21145
21145
  const local = path.join(localRoot, "files", file.path);
21146
- const metadata = await stat7(local).catch(() => null);
21146
+ const metadata = await stat8(local).catch(() => null);
21147
21147
  if (!metadata?.isFile() || metadata.size !== file.bytes)
21148
21148
  throw new MobileUpdateRegistryError(`Mobile update file ${file.path} size changed`);
21149
21149
  if (await fileDigest(Bun.file(local)) !== file.sha256)
@@ -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 relative21 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21415
- if (request.method === "POST" && relative21 === "health") {
21414
+ const relative22 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21415
+ if (request.method === "POST" && relative22 === "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 (relative21 === "update.json") {
21458
+ if (relative22 === "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(relative21);
21576
+ const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative22);
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 access20, mkdir as mkdir22 } from "fs/promises";
21670
- import { dirname as dirname22, isAbsolute as isAbsolute12, relative as relative21, resolve as resolve23, sep as sep14 } from "path";
21669
+ import { access as access21, mkdir as mkdir23 } from "fs/promises";
21670
+ import { dirname as dirname23, isAbsolute as isAbsolute13, relative as relative22, resolve as resolve24, sep as sep15 } 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), projectPath3 = (projectRoot, requested) => {
21674
- const root = resolve23(projectRoot);
21675
- const path2 = resolve23(root, requested);
21676
- const projectRelative = relative21(root, path2);
21677
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep14}`) || isAbsolute12(projectRelative))
21674
+ const root = resolve24(projectRoot);
21675
+ const path2 = resolve24(root, requested);
21676
+ const projectRelative = relative22(root, path2);
21677
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep15}`) || isAbsolute13(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 = projectPath3(projectRoot, requestedModulePath);
21700
- await access20(modulePath).catch(() => {
21700
+ await access21(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 = projectPath3(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
21888
21888
  if (!options.force) {
21889
- await access20(path2).then(() => {
21889
+ await access21(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 mkdir22(dirname22(path2), { recursive: true });
21895
+ await mkdir23(dirname23(path2), { recursive: true });
21896
21896
  await Bun.write(path2, renderAbsoluteMobileUpdateRegistry({
21897
21897
  ...options.health ? { health: options.health } : {},
21898
21898
  ...options.rollout ? { rollout: options.rollout } : {},
@@ -33033,7 +33033,7 @@ init_config();
33033
33033
  import { existsSync as existsSync4 } from "fs";
33034
33034
  import { access as access16, mkdir as mkdir16, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
33035
33035
  import { dirname as dirname15, extname as extname5, relative as relative15, resolve as resolve17, sep as sep10 } from "path";
33036
- var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
33036
+ var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 2;
33037
33037
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
33038
33038
  var CI_ENV_INDENTATION = 6;
33039
33039
  var RESERVED_SECRET_NAMES = new Set([
@@ -33171,6 +33171,14 @@ var publishingInputs = (platforms, includePublishing) => {
33171
33171
  if (!includePublishing)
33172
33172
  return "";
33173
33173
  const fields = [
33174
+ ` operation:
33175
+ description: Build a release or promote an exact artifact from an earlier run
33176
+ required: true
33177
+ type: choice
33178
+ default: build
33179
+ options:
33180
+ - build
33181
+ - promote`,
33174
33182
  ` publish:
33175
33183
  description: Publish through mobile.release.ts after the signed build
33176
33184
  required: true
@@ -33179,6 +33187,14 @@ var publishingInputs = (platforms, includePublishing) => {
33179
33187
  ` channel:
33180
33188
  description: Optional AbsoluteJS immutable release channel
33181
33189
  required: false
33190
+ type: string`,
33191
+ ` source_run_id:
33192
+ description: Source workflow run ID for promotion without rebuilding
33193
+ required: false
33194
+ type: string`,
33195
+ ` certification_base64:
33196
+ description: AbsoluteJS certification supplied by the promotion CLI
33197
+ required: false
33182
33198
  type: string`
33183
33199
  ];
33184
33200
  if (platforms.includes("android"))
@@ -33208,7 +33224,7 @@ var publishingInputs = (platforms, includePublishing) => {
33208
33224
  ${fields.join(`
33209
33225
  `)}`;
33210
33226
  };
33211
- var jobCondition = (platform3) => `github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == '${platform3}')`;
33227
+ var jobCondition = (platform3, includePublishing) => `github.event_name == 'workflow_dispatch' && ${includePublishing ? "inputs.operation == 'build' && " : ""}(inputs.platform == 'all' || inputs.platform == '${platform3}')`;
33212
33228
  var androidJob = (options) => {
33213
33229
  const custom = customSecretEnvironment(options.customSecrets);
33214
33230
  const publishEnvironment = options.includePublishing ? `
@@ -33235,6 +33251,11 @@ var androidJob = (options) => {
33235
33251
  chmod 600 "$GOOGLE_APPLICATION_CREDENTIALS"
33236
33252
  fi` : "";
33237
33253
  const certificationSteps = options.includePublishing ? `
33254
+ - name: Install pinned Cosign for Android certification
33255
+ if: inputs.publish
33256
+ uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
33257
+ with:
33258
+ cosign-release: v3.1.2
33238
33259
  - name: Install-test and certify exact Android release
33239
33260
  if: inputs.publish
33240
33261
  shell: bash
@@ -33254,6 +33275,12 @@ var androidJob = (options) => {
33254
33275
  "\${args[@]}"
33255
33276
  args=(bunx absolute mobile certify "$RELEASE_DIRECTORY" --evidence .absolutejs/mobile-ci/acceptance/android --require installed --outdir .absolutejs/mobile-ci/certifications/android --json)
33256
33277
  "\${args[@]}" > .absolutejs/mobile-ci/android-certification.json
33278
+ shopt -s nullglob
33279
+ certifications=(.absolutejs/mobile-ci/certifications/android/*)
33280
+ if [[ "\${#certifications[@]}" -ne 1 ]]; then echo "Expected exactly one Android certification." >&2; exit 1; fi
33281
+ CERTIFICATION_PATH="\${certifications[0]}/certification.json"
33282
+ bunx @absolutejs/attest@0.2.0 sign-blobs "$CERTIFICATION_PATH"
33283
+ bunx absolute mobile ci verification --bundle "$CERTIFICATION_PATH.sigstore.json" --out "\${certifications[0]}/verification.json"
33257
33284
  - name: Publish certified Android release
33258
33285
  if: inputs.publish
33259
33286
  shell: bash
@@ -33265,7 +33292,7 @@ var androidJob = (options) => {
33265
33292
  echo "Expected exactly one Android release and certification." >&2
33266
33293
  exit 1
33267
33294
  fi
33268
- args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification "\${certifications[0]}" --registry "$ABSOLUTE_REGISTRY_MODULE")
33295
+ args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification "\${certifications[0]}" --certification-attestation "\${certifications[0]}/verification.json" --registry "$ABSOLUTE_REGISTRY_MODULE")
33269
33296
  if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then
33270
33297
  args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL")
33271
33298
  fi
@@ -33290,7 +33317,7 @@ var androidJob = (options) => {
33290
33317
  android:
33291
33318
  name: Signed Android release
33292
33319
  needs: validate
33293
- if: \${{ ${jobCondition("android")} }}
33320
+ if: \${{ ${jobCondition("android", options.includePublishing)} }}
33294
33321
  runs-on: ubuntu-latest
33295
33322
  environment: absolute-mobile-release
33296
33323
  permissions:
@@ -33381,6 +33408,11 @@ var iosJob = (options) => {
33381
33408
  chmod 600 "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
33382
33409
  fi` : "";
33383
33410
  const certificationSteps = options.includePublishing ? `
33411
+ - name: Install pinned Cosign for iOS certification
33412
+ if: inputs.publish
33413
+ uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
33414
+ with:
33415
+ cosign-release: v3.1.2
33384
33416
  - name: Simulator-test and certify exact iOS release
33385
33417
  if: inputs.publish
33386
33418
  shell: bash
@@ -33397,6 +33429,12 @@ var iosJob = (options) => {
33397
33429
  "\${args[@]}"
33398
33430
  args=(bunx absolute mobile certify "$RELEASE_DIRECTORY" --evidence .absolutejs/mobile-ci/acceptance/ios --require simulator --outdir .absolutejs/mobile-ci/certifications/ios --json)
33399
33431
  "\${args[@]}" > .absolutejs/mobile-ci/ios-certification.json
33432
+ shopt -s nullglob
33433
+ certifications=(.absolutejs/mobile-ci/certifications/ios/*)
33434
+ if [[ "\${#certifications[@]}" -ne 1 ]]; then echo "Expected exactly one iOS certification." >&2; exit 1; fi
33435
+ CERTIFICATION_PATH="\${certifications[0]}/certification.json"
33436
+ bunx @absolutejs/attest@0.2.0 sign-blobs "$CERTIFICATION_PATH"
33437
+ bunx absolute mobile ci verification --bundle "$CERTIFICATION_PATH.sigstore.json" --out "\${certifications[0]}/verification.json"
33400
33438
  - name: Publish certified iOS release
33401
33439
  if: inputs.publish
33402
33440
  shell: bash
@@ -33408,7 +33446,7 @@ var iosJob = (options) => {
33408
33446
  echo "Expected exactly one iOS release and certification." >&2
33409
33447
  exit 1
33410
33448
  fi
33411
- args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification "\${certifications[0]}" --registry "$ABSOLUTE_REGISTRY_MODULE")
33449
+ args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification "\${certifications[0]}" --certification-attestation "\${certifications[0]}/verification.json" --registry "$ABSOLUTE_REGISTRY_MODULE")
33412
33450
  if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then
33413
33451
  args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL")
33414
33452
  fi
@@ -33436,7 +33474,7 @@ var iosJob = (options) => {
33436
33474
  ios:
33437
33475
  name: Signed iOS release
33438
33476
  needs: validate
33439
- if: \${{ ${jobCondition("ios")} }}
33477
+ if: \${{ ${jobCondition("ios", options.includePublishing)} }}
33440
33478
  runs-on: macos-latest
33441
33479
  environment: absolute-mobile-release
33442
33480
  permissions:
@@ -33514,6 +33552,179 @@ ${certificationSteps}
33514
33552
  rm -f "\${{ runner.temp }}/absolute.mobileprovision"
33515
33553
  rm -f "\${{ runner.temp }}/AuthKey_AbsoluteJS.p8"`;
33516
33554
  };
33555
+ var promotionValidationSteps = ` - name: Validate promotion handoff
33556
+ if: github.event_name == 'workflow_dispatch' && inputs.operation == 'promote'
33557
+ shell: bash
33558
+ run: |
33559
+ if [[ "$ABSOLUTE_PROMOTION_PLATFORM" == "all" ]]; then
33560
+ echo "Promotion requires one exact platform." >&2
33561
+ exit 1
33562
+ fi
33563
+ if [[ ! "$ABSOLUTE_SOURCE_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then
33564
+ echo "Promotion requires a numeric source workflow run ID." >&2
33565
+ exit 1
33566
+ fi
33567
+ if [[ -z "$ABSOLUTE_CERTIFICATION_BASE64" ]]; then
33568
+ echo "Promotion requires certification supplied by absolute mobile ci promote." >&2
33569
+ exit 1
33570
+ fi`;
33571
+ var promotionSharedSteps = (platform3) => ` - name: Restore exact ${platform3 === "android" ? "Android" : "iOS"} release from source run
33572
+ uses: actions/download-artifact@v8
33573
+ with:
33574
+ name: absolute-mobile-${platform3}
33575
+ path: .absolutejs/mobile/releases/${platform3}
33576
+ run-id: \${{ inputs.source_run_id }}
33577
+ github-token: \${{ github.token }}
33578
+ - name: Import and verify exact certification
33579
+ shell: bash
33580
+ run: |
33581
+ mkdir -p .absolutejs/mobile-ci/imported-certification
33582
+ printf '%s' "$ABSOLUTE_CERTIFICATION_BASE64" | base64 --decode > .absolutejs/mobile-ci/imported-certification/certification.json
33583
+ shopt -s nullglob
33584
+ releases=(.absolutejs/mobile/releases/${platform3}/*)
33585
+ if [[ "\${#releases[@]}" -ne 1 || ! -d "\${releases[0]}" ]]; then
33586
+ echo "Expected exactly one immutable ${platform3} release directory from the source run." >&2
33587
+ exit 1
33588
+ fi
33589
+ args=(bunx absolute mobile certify "\${releases[0]}" --verify .absolutejs/mobile-ci/imported-certification/certification.json --json)
33590
+ "\${args[@]}" > .absolutejs/mobile-ci/imported-certification/verified.json
33591
+ - name: Install pinned Cosign
33592
+ uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
33593
+ with:
33594
+ cosign-release: v3.1.2
33595
+ - name: Sign certification with GitHub OIDC
33596
+ shell: bash
33597
+ run: |
33598
+ bunx @absolutejs/attest@0.2.0 sign-blobs .absolutejs/mobile-ci/imported-certification/certification.json
33599
+ bunx absolute mobile ci verification --bundle .absolutejs/mobile-ci/imported-certification/certification.json.sigstore.json --out .absolutejs/mobile-ci/imported-certification/verification.json`;
33600
+ var androidPromotionJob = (options) => {
33601
+ const custom = customSecretEnvironment(options.customSecrets);
33602
+ return `
33603
+ promote_android:
33604
+ name: Promote exact certified Android release
33605
+ needs: validate
33606
+ if: \${{ github.event_name == 'workflow_dispatch' && inputs.operation == 'promote' && inputs.platform == 'android' }}
33607
+ runs-on: ubuntu-latest
33608
+ environment: absolute-mobile-release
33609
+ permissions:
33610
+ actions: read
33611
+ contents: read
33612
+ id-token: write
33613
+ env:
33614
+ ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
33615
+ ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
33616
+ ABSOLUTE_PLAY_TRACK: \${{ inputs.play_track }}
33617
+ ABSOLUTE_GOOGLE_CREDENTIALS_BASE64: \${{ secrets.ABSOLUTE_GOOGLE_CREDENTIALS_BASE64 }}
33618
+ GOOGLE_APPLICATION_CREDENTIALS: \${{ runner.temp }}/absolute-google-credentials.json${custom ? `
33619
+ ${custom}` : ""}
33620
+ ${commandEnvironment2({ configPath: undefined, registryModule: "", serverEntry: "" })}
33621
+ steps:
33622
+ ${installSteps}
33623
+ ${promotionSharedSteps("android")}
33624
+ - name: Publish restored Android release
33625
+ shell: bash
33626
+ run: |
33627
+ if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" || "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then
33628
+ true
33629
+ else
33630
+ echo "Promotion requires a release channel or Google Play track." >&2
33631
+ exit 1
33632
+ fi
33633
+ if [[ "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then
33634
+ if [[ -z "$ABSOLUTE_GOOGLE_CREDENTIALS_BASE64" ]]; then
33635
+ echo "ABSOLUTE_GOOGLE_CREDENTIALS_BASE64 is required for Google Play publication." >&2
33636
+ exit 1
33637
+ fi
33638
+ printf '%s' "$ABSOLUTE_GOOGLE_CREDENTIALS_BASE64" | base64 --decode > "$GOOGLE_APPLICATION_CREDENTIALS"
33639
+ chmod 600 "$GOOGLE_APPLICATION_CREDENTIALS"
33640
+ fi
33641
+ shopt -s nullglob
33642
+ releases=(.absolutejs/mobile/releases/android/*)
33643
+ args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification .absolutejs/mobile-ci/imported-certification/certification.json --certification-attestation .absolutejs/mobile-ci/imported-certification/verification.json --registry "$ABSOLUTE_REGISTRY_MODULE" --json)
33644
+ if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL"); fi
33645
+ if [[ "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then args+=(--play-track "$ABSOLUTE_PLAY_TRACK"); fi
33646
+ ${appendConfigArgument}
33647
+ "\${args[@]}" > .absolutejs/mobile-ci/promotion-receipt.json
33648
+ - name: Upload Android promotion audit
33649
+ if: always()
33650
+ uses: actions/upload-artifact@v7
33651
+ with:
33652
+ name: absolute-mobile-android-promotion-\${{ github.run_id }}
33653
+ path: |
33654
+ .absolutejs/mobile-ci/imported-certification/
33655
+ .absolutejs/mobile-ci/promotion-receipt.json
33656
+ if-no-files-found: error
33657
+ retention-days: 90
33658
+ include-hidden-files: true
33659
+ - name: Remove Google credentials
33660
+ if: always()
33661
+ run: rm -f "\${{ runner.temp }}/absolute-google-credentials.json"`;
33662
+ };
33663
+ var iosPromotionJob = (options) => {
33664
+ const custom = customSecretEnvironment(options.customSecrets);
33665
+ return `
33666
+ promote_ios:
33667
+ name: Promote exact certified iOS release
33668
+ needs: validate
33669
+ if: \${{ github.event_name == 'workflow_dispatch' && inputs.operation == 'promote' && inputs.platform == 'ios' }}
33670
+ runs-on: ubuntu-latest
33671
+ environment: absolute-mobile-release
33672
+ permissions:
33673
+ actions: read
33674
+ contents: read
33675
+ id-token: write
33676
+ env:
33677
+ ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
33678
+ ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
33679
+ ABSOLUTE_TESTFLIGHT_GROUP: \${{ inputs.testflight_group }}
33680
+ ABSOLUTE_TESTFLIGHT_SUBMIT_REVIEW: \${{ inputs.submit_testflight_review }}
33681
+ APP_STORE_CONNECT_ISSUER_ID: \${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
33682
+ APP_STORE_CONNECT_KEY_ID: \${{ secrets.APP_STORE_CONNECT_KEY_ID }}
33683
+ APP_STORE_CONNECT_PRIVATE_KEY_BASE64: \${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY_BASE64 }}
33684
+ APP_STORE_CONNECT_PRIVATE_KEY_PATH: \${{ runner.temp }}/AuthKey_AbsoluteJS.p8${custom ? `
33685
+ ${custom}` : ""}
33686
+ ${commandEnvironment2({ configPath: undefined, registryModule: "", serverEntry: "" })}
33687
+ steps:
33688
+ ${installSteps}
33689
+ ${promotionSharedSteps("ios")}
33690
+ - name: Publish restored iOS release
33691
+ shell: bash
33692
+ run: |
33693
+ if [[ -z "$ABSOLUTE_RELEASE_CHANNEL" && -z "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then
33694
+ echo "Promotion requires a release channel or TestFlight group." >&2
33695
+ exit 1
33696
+ fi
33697
+ if [[ -n "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then
33698
+ required=(APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_KEY_ID APP_STORE_CONNECT_PRIVATE_KEY_BASE64)
33699
+ for name in "\${required[@]}"; do
33700
+ if [[ -z "\${!name}" ]]; then echo "$name is required for TestFlight publication." >&2; exit 1; fi
33701
+ done
33702
+ printf '%s' "$APP_STORE_CONNECT_PRIVATE_KEY_BASE64" | base64 --decode > "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
33703
+ chmod 600 "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
33704
+ fi
33705
+ shopt -s nullglob
33706
+ releases=(.absolutejs/mobile/releases/ios/*)
33707
+ args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification .absolutejs/mobile-ci/imported-certification/certification.json --certification-attestation .absolutejs/mobile-ci/imported-certification/verification.json --registry "$ABSOLUTE_REGISTRY_MODULE" --json)
33708
+ if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL"); fi
33709
+ if [[ -n "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then args+=(--testflight-group "$ABSOLUTE_TESTFLIGHT_GROUP"); fi
33710
+ if [[ "$ABSOLUTE_TESTFLIGHT_SUBMIT_REVIEW" == "true" ]]; then args+=(--testflight-submit-review); fi
33711
+ ${appendConfigArgument}
33712
+ "\${args[@]}" > .absolutejs/mobile-ci/promotion-receipt.json
33713
+ - name: Upload iOS promotion audit
33714
+ if: always()
33715
+ uses: actions/upload-artifact@v7
33716
+ with:
33717
+ name: absolute-mobile-ios-promotion-\${{ github.run_id }}
33718
+ path: |
33719
+ .absolutejs/mobile-ci/imported-certification/
33720
+ .absolutejs/mobile-ci/promotion-receipt.json
33721
+ if-no-files-found: error
33722
+ retention-days: 90
33723
+ include-hidden-files: true
33724
+ - name: Remove App Store Connect credentials
33725
+ if: always()
33726
+ run: rm -f "\${{ runner.temp }}/AuthKey_AbsoluteJS.p8"`;
33727
+ };
33517
33728
  var requiredSecrets = (platforms, includePublishing, custom) => [
33518
33729
  ...platforms.includes("android") ? [
33519
33730
  "ABSOLUTE_ANDROID_KEYSTORE_BASE64",
@@ -33575,10 +33786,14 @@ jobs:
33575
33786
  permissions:
33576
33787
  contents: read
33577
33788
  env:
33578
- ${environment}
33789
+ ${environment}${includePublishing ? `
33790
+ ABSOLUTE_PROMOTION_PLATFORM: \${{ inputs.platform }}
33791
+ ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
33792
+ ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}` : ""}
33579
33793
  steps:
33580
- ${installSteps}
33581
- ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets, includePublishing }) : ""}${platforms.includes("ios") ? iosJob({ customSecrets, includePublishing }) : ""}
33794
+ ${installSteps}${includePublishing ? `
33795
+ ${promotionValidationSteps}` : ""}
33796
+ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets, includePublishing }) : ""}${platforms.includes("ios") ? iosJob({ customSecrets, includePublishing }) : ""}${includePublishing && platforms.includes("android") ? androidPromotionJob({ customSecrets, includePublishing }) : ""}${includePublishing && platforms.includes("ios") ? iosPromotionJob({ customSecrets, includePublishing }) : ""}
33582
33797
  `;
33583
33798
  const replacements = new Map([
33584
33799
  [
@@ -35745,6 +35960,8 @@ This certification is local and contains no signing credentials, device identifi
35745
35960
  ${evidence}
35746
35961
  `;
35747
35962
  };
35963
+ var serializeAbsoluteMobileReleaseCertification = (certification) => `${JSON.stringify(certification, null, 2)}
35964
+ `;
35748
35965
  var verifyAbsoluteMobileReleaseCertification = (certification, release, requirement) => {
35749
35966
  const parsed = parseCertification(certification);
35750
35967
  const expected = releaseIdentity(release);
@@ -35769,8 +35986,7 @@ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification,
35769
35986
  jsonPath: join30(destination, "certification.json"),
35770
35987
  markdownPath: join30(destination, "certification.md")
35771
35988
  };
35772
- const json = `${JSON.stringify(certification, null, 2)}
35773
- `;
35989
+ const json = serializeAbsoluteMobileReleaseCertification(certification);
35774
35990
  const markdown = renderAbsoluteMobileReleaseCertification(certification);
35775
35991
  const verifyExisting = async () => {
35776
35992
  const [existingJson, existingMarkdown] = await Promise.all([
@@ -35805,9 +36021,110 @@ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification,
35805
36021
  await verifyExisting();
35806
36022
  return paths;
35807
36023
  };
36024
+ // src/mobile/certificationVerification.ts
36025
+ import { access as access18, mkdir as mkdir21, readFile as readFile27, stat as stat6, writeFile as writeFile23 } from "fs/promises";
36026
+ import { dirname as dirname20, isAbsolute as isAbsolute10, relative as relative17, resolve as resolve19, sep as sep12 } from "path";
36027
+ var ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT = 1;
36028
+ var MAX_VERIFICATION_BYTES = 1048576;
36029
+ var SHA_PATTERN = /^[a-f0-9]{40}$/u;
36030
+ var REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
36031
+ var REF_PATTERN = /^refs\/(?:heads|tags)\/[^@\s]+$/u;
36032
+ var WORKFLOW_PATTERN = /^\.github\/workflows\/[^/@]+\.ya?ml$/u;
36033
+ var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36034
+ var required = (value, name) => {
36035
+ if (typeof value !== "string" || value.trim() === "")
36036
+ throw new TypeError(`${name} is required.`);
36037
+ return value.trim();
36038
+ };
36039
+ var requireMatch = (value, pattern, name) => {
36040
+ if (!pattern.test(value))
36041
+ throw new TypeError(`${name} is invalid.`);
36042
+ return value;
36043
+ };
36044
+ var createAbsoluteMobileCertificationVerification = (bundle, environment) => {
36045
+ const repository = required(environment.GITHUB_REPOSITORY, "GITHUB_REPOSITORY");
36046
+ const workflowReference = required(environment.GITHUB_WORKFLOW_REF, "GITHUB_WORKFLOW_REF");
36047
+ const prefix = `${repository}/`;
36048
+ const separator = workflowReference.lastIndexOf("@");
36049
+ if (!workflowReference.startsWith(prefix) || separator <= prefix.length)
36050
+ throw new TypeError("GITHUB_WORKFLOW_REF is invalid.");
36051
+ return parseAbsoluteMobileCertificationVerification({
36052
+ bundle,
36053
+ format: ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
36054
+ identity: {
36055
+ issuer: "https://token.actions.githubusercontent.com",
36056
+ ref: environment.GITHUB_REF,
36057
+ repository,
36058
+ sha: environment.GITHUB_SHA,
36059
+ workflowPath: workflowReference.slice(prefix.length, separator)
36060
+ },
36061
+ kind: "sigstore-bundle"
36062
+ });
36063
+ };
36064
+ var parseAbsoluteMobileCertificationVerification = (value) => {
36065
+ if (!isRecord15(value) || value.format !== ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT || value.kind !== "sigstore-bundle" || !isRecord15(value.bundle) || !isRecord15(value.identity))
36066
+ throw new TypeError("Mobile certification verification contract is invalid.");
36067
+ const issuer = required(value.identity.issuer, "verification issuer");
36068
+ if (issuer !== "https://token.actions.githubusercontent.com")
36069
+ throw new TypeError("verification issuer is invalid.");
36070
+ const verification = {
36071
+ bundle: value.bundle,
36072
+ format: ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
36073
+ identity: {
36074
+ issuer,
36075
+ ref: requireMatch(required(value.identity.ref, "verification ref"), REF_PATTERN, "verification ref"),
36076
+ repository: requireMatch(required(value.identity.repository, "verification repository"), REPOSITORY_PATTERN, "verification repository"),
36077
+ sha: requireMatch(required(value.identity.sha, "verification sha"), SHA_PATTERN, "verification sha"),
36078
+ workflowPath: requireMatch(required(value.identity.workflowPath, "verification workflowPath"), WORKFLOW_PATTERN, "verification workflowPath")
36079
+ },
36080
+ kind: "sigstore-bundle"
36081
+ };
36082
+ const bytes = new TextEncoder().encode(JSON.stringify(verification));
36083
+ if (bytes.byteLength > MAX_VERIFICATION_BYTES)
36084
+ throw new TypeError("Mobile certification verification exceeds the 1 MiB limit.");
36085
+ return verification;
36086
+ };
36087
+ var projectFile = (projectRoot, requested) => {
36088
+ const root = resolve19(projectRoot);
36089
+ const path = resolve19(root, requested);
36090
+ const portable = relative17(root, path);
36091
+ if (portable === ".." || portable.startsWith(`..${sep12}`) || isAbsolute10(portable))
36092
+ throw new TypeError("Mobile certification verification path must remain inside the project.");
36093
+ return path;
36094
+ };
36095
+ var readAbsoluteMobileCertificationVerification = async (projectRoot, requested) => {
36096
+ const path = projectFile(projectRoot, requested);
36097
+ await access18(path).catch(() => {
36098
+ throw new TypeError(`Mobile certification verification does not exist: ${path}`);
36099
+ });
36100
+ const metadata = await stat6(path);
36101
+ if (!metadata.isFile() || metadata.size > MAX_VERIFICATION_BYTES)
36102
+ throw new TypeError("Mobile certification verification file is invalid.");
36103
+ return parseAbsoluteMobileCertificationVerification(JSON.parse(await readFile27(path, "utf8")));
36104
+ };
36105
+ var readAbsoluteSigstoreBundle = async (projectRoot, requested) => {
36106
+ const path = projectFile(projectRoot, requested);
36107
+ const metadata = await stat6(path).catch(() => null);
36108
+ if (!metadata?.isFile() || metadata.size > MAX_VERIFICATION_BYTES)
36109
+ throw new TypeError("Sigstore bundle file is invalid.");
36110
+ const value = JSON.parse(await readFile27(path, "utf8"));
36111
+ if (!isRecord15(value))
36112
+ throw new TypeError("Sigstore bundle is invalid.");
36113
+ return value;
36114
+ };
36115
+ var writeAbsoluteMobileCertificationVerification = async (projectRoot, requested, verification) => {
36116
+ const path = projectFile(projectRoot, requested);
36117
+ const parsed = parseAbsoluteMobileCertificationVerification(verification);
36118
+ await mkdir21(dirname20(path), { recursive: true });
36119
+ await writeFile23(path, `${JSON.stringify(parsed, null, 2)}
36120
+ `, {
36121
+ mode: 384
36122
+ });
36123
+ return path;
36124
+ };
35808
36125
  // src/mobile/releasePublisher.ts
35809
- import { access as access18 } from "fs/promises";
35810
- import { isAbsolute as isAbsolute10, relative as relative17, resolve as resolve19, sep as sep12 } from "path";
36126
+ import { access as access19 } from "fs/promises";
36127
+ import { isAbsolute as isAbsolute11, relative as relative18, resolve as resolve20, sep as sep13 } from "path";
35811
36128
  import { pathToFileURL as pathToFileURL3 } from "url";
35812
36129
  var prepareAbsoluteIosRelease = async (publisher, options) => {
35813
36130
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -35830,33 +36147,35 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
35830
36147
  }
35831
36148
  return versionCode;
35832
36149
  };
35833
- var requireCertificationReceipt = (publication, certification, requirement, releaseId) => {
35834
- if (!certification && !requirement)
36150
+ var requireCertificationReceipt = (publication, certification, requirement, releaseId, verification) => {
36151
+ if (!certification && !requirement && !verification)
35835
36152
  return;
35836
36153
  if (!certification || !requirement)
35837
36154
  throw new TypeError("Native release publication certification contract is incomplete.");
35838
36155
  const receipt = publication.certification;
35839
36156
  if (!receipt || receipt.certificationId !== certification.certificationId || receipt.releaseId !== releaseId || receipt.requirement !== requirement || receipt.strength !== certification.strength)
35840
36157
  throw new TypeError("Native release registry did not retain the required release certification.");
36158
+ if (verification && (!receipt.provenance || receipt.provenance.subject !== releaseId))
36159
+ throw new TypeError("Native release registry did not retain trusted certification provenance.");
35841
36160
  };
35842
- var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35843
- var isPublisher = (value) => isRecord15(value) && typeof value.publish === "function";
36161
+ var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36162
+ var isPublisher = (value) => isRecord16(value) && typeof value.publish === "function";
35844
36163
  var publisherModulePath = (projectRoot, requested) => {
35845
- const root = resolve19(projectRoot);
35846
- const path = resolve19(root, requested);
35847
- const projectRelative = relative17(root, path);
35848
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep12}`) || isAbsolute10(projectRelative)) {
36164
+ const root = resolve20(projectRoot);
36165
+ const path = resolve20(root, requested);
36166
+ const projectRelative = relative18(root, path);
36167
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep13}`) || isAbsolute11(projectRelative)) {
35849
36168
  throw new TypeError("mobile publish --registry must remain inside the project.");
35850
36169
  }
35851
36170
  return path;
35852
36171
  };
35853
36172
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
35854
36173
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
35855
- await access18(modulePath).catch(() => {
36174
+ await access19(modulePath).catch(() => {
35856
36175
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
35857
36176
  });
35858
36177
  const loaded = await import(pathToFileURL3(modulePath).href);
35859
- const publisher = isRecord15(loaded) ? loaded.default ?? loaded.registry : undefined;
36178
+ const publisher = isRecord16(loaded) ? loaded.default ?? loaded.registry : undefined;
35860
36179
  if (!isPublisher(publisher)) {
35861
36180
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
35862
36181
  }
@@ -35864,6 +36183,8 @@ var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath
35864
36183
  };
35865
36184
  var publishAbsoluteAndroidRelease = async (options) => {
35866
36185
  const certificationRequirement = options.certificationRequirement ?? options.certification?.requirement;
36186
+ if (options.certificationVerification && !options.certification)
36187
+ throw new TypeError("Android certification verification requires certification.");
35867
36188
  if (options.certification)
35868
36189
  verifyAbsoluteMobileReleaseCertification(options.certification, options.release, certificationRequirement);
35869
36190
  else if (certificationRequirement)
@@ -35873,6 +36194,7 @@ var publishAbsoluteAndroidRelease = async (options) => {
35873
36194
  allowUnsigned: options.allowUnsigned,
35874
36195
  certification: options.certification,
35875
36196
  certificationRequirement,
36197
+ certificationVerification: options.certificationVerification,
35876
36198
  channel: options.channel,
35877
36199
  googlePlay: options.googlePlay,
35878
36200
  releaseRoot: options.release.releaseRoot,
@@ -35883,7 +36205,7 @@ var publishAbsoluteAndroidRelease = async (options) => {
35883
36205
  if (!actual || actual.appId !== expected.appId || actual.platform !== "android" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.versionCode !== expected.versionCode || typeof publication.reused !== "boolean") {
35884
36206
  throw new TypeError("Native release registry returned a different Android release identity.");
35885
36207
  }
35886
- requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId);
36208
+ requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId, options.certificationVerification);
35887
36209
  if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
35888
36210
  throw new TypeError("Native release registry did not promote the requested channel.");
35889
36211
  }
@@ -35897,6 +36219,8 @@ var publishAbsoluteAndroidRelease = async (options) => {
35897
36219
  };
35898
36220
  var publishAbsoluteIosRelease = async (options) => {
35899
36221
  const certificationRequirement = options.certificationRequirement ?? options.certification?.requirement;
36222
+ if (options.certificationVerification && !options.certification)
36223
+ throw new TypeError("iOS certification verification requires certification.");
35900
36224
  if (options.certification)
35901
36225
  verifyAbsoluteMobileReleaseCertification(options.certification, options.release, certificationRequirement);
35902
36226
  else if (certificationRequirement)
@@ -35907,6 +36231,7 @@ var publishAbsoluteIosRelease = async (options) => {
35907
36231
  appStoreConnect: options.appStoreConnect,
35908
36232
  certification: options.certification,
35909
36233
  certificationRequirement,
36234
+ certificationVerification: options.certificationVerification,
35910
36235
  channel: options.channel,
35911
36236
  releaseRoot: options.release.releaseRoot,
35912
36237
  signal: options.signal
@@ -35916,7 +36241,7 @@ var publishAbsoluteIosRelease = async (options) => {
35916
36241
  if (!actual || actual.appId !== expected.appId || actual.platform !== "ios" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.buildNumber !== expected.buildNumber || actual.marketingVersion !== expected.marketingVersion || typeof publication.reused !== "boolean") {
35917
36242
  throw new TypeError("Native release registry returned a different iOS release identity.");
35918
36243
  }
35919
- requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId);
36244
+ requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId, options.certificationVerification);
35920
36245
  if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
35921
36246
  throw new TypeError("Native release registry did not promote the requested channel.");
35922
36247
  }
@@ -35932,7 +36257,7 @@ var publishAbsoluteIosRelease = async (options) => {
35932
36257
  init_pageProtocol();
35933
36258
  init_releaseArtifact();
35934
36259
  import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
35935
- import { dirname as dirname20, extname as extname6, relative as relative18, resolve as resolve20 } from "path";
36260
+ import { dirname as dirname21, extname as extname6, relative as relative19, resolve as resolve21 } from "path";
35936
36261
  import ts2 from "typescript";
35937
36262
  var ROUTE_METHODS = new Set(["get", "head"]);
35938
36263
  var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
@@ -35983,7 +36308,7 @@ var PAGE_HANDLERS = new Map([
35983
36308
  ]
35984
36309
  ]);
35985
36310
  var posixPath = (value) => value.replace(/\\/g, "/");
35986
- var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname20(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
36311
+ var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname21(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
35987
36312
  var createProgram = (entry, projectRoot) => {
35988
36313
  const configPath2 = findTsconfig(entry, projectRoot);
35989
36314
  if (!configPath2) {
@@ -35995,7 +36320,7 @@ var createProgram = (entry, projectRoot) => {
35995
36320
  target: ts2.ScriptTarget.ESNext
35996
36321
  });
35997
36322
  }
35998
- const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync8(path, "utf8")).config, ts2.sys, dirname20(configPath2));
36323
+ const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync8(path, "utf8")).config, ts2.sys, dirname21(configPath2));
35999
36324
  if (!parsed.fileNames.includes(entry))
36000
36325
  parsed.fileNames.push(entry);
36001
36326
  return ts2.createProgram(parsed.fileNames, parsed.options);
@@ -36101,7 +36426,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
36101
36426
  const declaration = symbol?.declarations?.[0];
36102
36427
  const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
36103
36428
  const exportedName = symbol?.name ?? expression.getText(sourceFile);
36104
- const source = posixPath(relative18(projectRoot, file));
36429
+ const source = posixPath(relative19(projectRoot, file));
36105
36430
  return `${source}#${exportedName}`;
36106
36431
  };
36107
36432
  var resolveAlias = (symbol, checker) => {
@@ -36340,7 +36665,7 @@ var analyzeProgram = (program, projectRoot) => {
36340
36665
  const checker = program.getTypeChecker();
36341
36666
  const analyzed = new Map;
36342
36667
  for (const sourceFile of program.getSourceFiles()) {
36343
- const resolvedFile = resolve20(sourceFile.fileName);
36668
+ const resolvedFile = resolve21(sourceFile.fileName);
36344
36669
  if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
36345
36670
  continue;
36346
36671
  const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
@@ -36433,14 +36758,14 @@ var transformFile = (source, fileName, analysis) => {
36433
36758
  };
36434
36759
  var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
36435
36760
  var createAbsoluteMobileRouteMetadataPlugin = (options) => {
36436
- const projectRoot = resolve20(options.projectRoot ?? process.cwd());
36437
- const entry = resolve20(options.entry);
36761
+ const projectRoot = resolve21(options.projectRoot ?? process.cwd());
36762
+ const entry = resolve21(options.entry);
36438
36763
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
36439
36764
  return {
36440
36765
  name: "absolute-mobile-route-metadata",
36441
36766
  setup(build) {
36442
36767
  build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
36443
- const analysis = analyzed.get(resolve20(path));
36768
+ const analysis = analyzed.get(resolve21(path));
36444
36769
  if (!analysis)
36445
36770
  return;
36446
36771
  const source = await Bun.file(path).text();
@@ -36453,11 +36778,11 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
36453
36778
  };
36454
36779
  };
36455
36780
  var inspectAbsoluteMobileRouteMetadata = (options) => {
36456
- const projectRoot = resolve20(options.projectRoot ?? process.cwd());
36457
- const entry = resolve20(options.entry);
36781
+ const projectRoot = resolve21(options.projectRoot ?? process.cwd());
36782
+ const entry = resolve21(options.entry);
36458
36783
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
36459
36784
  return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
36460
- file: posixPath(relative18(projectRoot, file)),
36785
+ file: posixPath(relative19(projectRoot, file)),
36461
36786
  metadata
36462
36787
  })));
36463
36788
  };
@@ -36791,7 +37116,7 @@ var createCapacitor = (win) => {
36791
37116
  callbackId
36792
37117
  }, callback);
36793
37118
  };
36794
- const p = new Promise((resolve21) => call.then(() => resolve21({ remove })));
37119
+ const p = new Promise((resolve22) => call.then(() => resolve22({ remove })));
36795
37120
  p.remove = async () => {
36796
37121
  console.warn(`Using addListener() without 'await' is deprecated.`);
36797
37122
  await remove();
@@ -36996,11 +37321,11 @@ class CapacitorCookiesPluginWeb extends WebPlugin {
36996
37321
  var CapacitorCookies = registerPlugin("CapacitorCookies", {
36997
37322
  web: () => new CapacitorCookiesPluginWeb
36998
37323
  });
36999
- var readBlobAsBase64 = async (blob) => new Promise((resolve21, reject) => {
37324
+ var readBlobAsBase64 = async (blob) => new Promise((resolve22, reject) => {
37000
37325
  const reader = new FileReader;
37001
37326
  reader.onload = () => {
37002
37327
  const base64String = reader.result;
37003
- resolve21(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
37328
+ resolve22(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
37004
37329
  };
37005
37330
  reader.onerror = (error) => reject(error);
37006
37331
  reader.readAsDataURL(blob);
@@ -37176,7 +37501,7 @@ var REPORT_KINDS = new Set([
37176
37501
  "signal"
37177
37502
  ]);
37178
37503
  var nativeObservability = registerPlugin("AbsoluteMobileObservability");
37179
- var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37504
+ var isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37180
37505
  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]");
37181
37506
  var redactValue = (value, depth = 0) => {
37182
37507
  if (depth >= MAX_REDACTION_DEPTH)
@@ -37187,7 +37512,7 @@ var redactValue = (value, depth = 0) => {
37187
37512
  return value;
37188
37513
  if (Array.isArray(value))
37189
37514
  return value.slice(0, 64).map((entry) => redactValue(entry, depth + 1));
37190
- if (!isRecord16(value))
37515
+ if (!isRecord17(value))
37191
37516
  return String(value).slice(0, MAX_STRING_LENGTH);
37192
37517
  return Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [
37193
37518
  key,
@@ -37195,7 +37520,7 @@ var redactValue = (value, depth = 0) => {
37195
37520
  ]));
37196
37521
  };
37197
37522
  var parseReport = (value) => {
37198
- if (!isRecord16(value) || !isRecord16(value.details))
37523
+ if (!isRecord17(value) || !isRecord17(value.details))
37199
37524
  return;
37200
37525
  if (typeof value.id !== "string" || !REPORT_ID.test(value.id))
37201
37526
  return;
@@ -37206,7 +37531,7 @@ var parseReport = (value) => {
37206
37531
  if (value.platform !== "android" && value.platform !== "ios")
37207
37532
  return;
37208
37533
  const details = redactValue(value.details);
37209
- if (!isRecord16(details))
37534
+ if (!isRecord17(details))
37210
37535
  return;
37211
37536
  if (new TextEncoder().encode(JSON.stringify(details)).byteLength > MAX_DETAIL_BYTES)
37212
37537
  return;
@@ -41660,8 +41985,8 @@ var createBeacon = (options) => {
41660
41985
  if (await fontAvailable(element))
41661
41986
  return;
41662
41987
  if (confirmationMs > 0) {
41663
- await new Promise((resolve21) => {
41664
- const timer2 = window.setTimeout(resolve21, confirmationMs);
41988
+ await new Promise((resolve22) => {
41989
+ const timer2 = window.setTimeout(resolve22, confirmationMs);
41665
41990
  cleanups.push(() => window.clearTimeout(timer2));
41666
41991
  });
41667
41992
  if (document.visibilityState === "hidden")
@@ -42649,24 +42974,24 @@ var createAbsoluteMobileUpdateClient = (options) => {
42649
42974
  };
42650
42975
  };
42651
42976
  // src/mobile/updatePublisher.ts
42652
- import { access as access19 } from "fs/promises";
42653
- import { isAbsolute as isAbsolute11, relative as relative20, resolve as resolve22, sep as sep13 } from "path";
42977
+ import { access as access20 } from "fs/promises";
42978
+ import { isAbsolute as isAbsolute12, relative as relative21, resolve as resolve23, sep as sep14 } from "path";
42654
42979
  import { pathToFileURL as pathToFileURL4 } from "url";
42655
42980
 
42656
42981
  // src/mobile/updateSigning.ts
42657
42982
  import { createHash as createHash17, sign, verify } from "crypto";
42658
42983
  import {
42659
42984
  cp as cp2,
42660
- mkdir as mkdir21,
42985
+ mkdir as mkdir22,
42661
42986
  mkdtemp as mkdtemp11,
42662
42987
  readdir as readdir10,
42663
- readFile as readFile27,
42988
+ readFile as readFile28,
42664
42989
  rename as rename19,
42665
42990
  rm as rm16,
42666
- stat as stat6,
42667
- writeFile as writeFile23
42991
+ stat as stat7,
42992
+ writeFile as writeFile24
42668
42993
  } from "fs/promises";
42669
- import { dirname as dirname21, join as join31, relative as relative19, resolve as resolve21 } from "path";
42994
+ import { dirname as dirname22, join as join31, relative as relative20, resolve as resolve22 } from "path";
42670
42995
  var UPDATE_MANIFEST_FILE = "update.json";
42671
42996
  var UPDATE_FILES_DIRECTORY = "files";
42672
42997
  var sha2565 = (value) => createHash17("sha256").update(value).digest("hex");
@@ -42678,22 +43003,22 @@ var listFiles = async (root, directory = root) => {
42678
43003
  return listFiles(root, path);
42679
43004
  if (!entry.isFile())
42680
43005
  throw new TypeError("Mobile updates cannot contain links or special files.");
42681
- return [relative19(root, path).replaceAll("\\", "/")];
43006
+ return [relative20(root, path).replaceAll("\\", "/")];
42682
43007
  }));
42683
43008
  return paths.flat().sort((left, right) => left.localeCompare(right));
42684
43009
  };
42685
43010
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
42686
43011
  const source = join31(root, path);
42687
43012
  const [metadata, contents] = await Promise.all([
42688
- stat6(source),
42689
- readFile27(source)
43013
+ stat7(source),
43014
+ readFile28(source)
42690
43015
  ]);
42691
43016
  return { bytes: metadata.size, path, sha256: sha2565(contents) };
42692
43017
  }));
42693
43018
  var releaseIdFor = (value) => `amu_${sha2565(canonicalizeAbsoluteMobileUpdate(value))}`;
42694
43019
  var buildAbsoluteMobileUpdate = async (options) => {
42695
- const bundleDirectory = resolve21(options.bundleDirectory);
42696
- const outputRoot = resolve21(options.outputDirectory);
43020
+ const bundleDirectory = resolve22(options.bundleDirectory);
43021
+ const outputRoot = resolve22(options.outputDirectory);
42697
43022
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
42698
43023
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
42699
43024
  const paths = await listFiles(bundleDirectory);
@@ -42721,7 +43046,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
42721
43046
  value: signature.toString("base64")
42722
43047
  }
42723
43048
  });
42724
- await mkdir21(outputRoot, { recursive: true });
43049
+ await mkdir22(outputRoot, { recursive: true });
42725
43050
  const outputDirectory = join31(outputRoot, manifest.releaseId);
42726
43051
  const staging = await mkdtemp11(join31(outputRoot, ".stage-"));
42727
43052
  try {
@@ -42729,7 +43054,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
42729
43054
  force: true,
42730
43055
  recursive: true
42731
43056
  });
42732
- await writeFile23(join31(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
43057
+ await writeFile24(join31(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
42733
43058
  `);
42734
43059
  await rename19(staging, outputDirectory);
42735
43060
  } catch (error) {
@@ -42745,14 +43070,14 @@ var buildAbsoluteMobileUpdate = async (options) => {
42745
43070
  };
42746
43071
  };
42747
43072
  var copyAbsoluteMobileUpdateFile = async (updateDirectory, path, destination) => {
42748
- const source = resolve21(updateDirectory, UPDATE_FILES_DIRECTORY, path);
42749
- const root = resolve21(updateDirectory, UPDATE_FILES_DIRECTORY);
43073
+ const source = resolve22(updateDirectory, UPDATE_FILES_DIRECTORY, path);
43074
+ const root = resolve22(updateDirectory, UPDATE_FILES_DIRECTORY);
42750
43075
  if (!source.startsWith(`${root}/`))
42751
43076
  throw new TypeError("Update file escaped its release.");
42752
- await mkdir21(dirname21(destination), { recursive: true });
43077
+ await mkdir22(dirname22(destination), { recursive: true });
42753
43078
  await cp2(source, destination, { force: true });
42754
43079
  };
42755
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile27(join31(resolve21(directory), UPDATE_MANIFEST_FILE), "utf8")));
43080
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile28(join31(resolve22(directory), UPDATE_MANIFEST_FILE), "utf8")));
42756
43081
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
42757
43082
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
42758
43083
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -42869,16 +43194,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
42869
43194
  var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
42870
43195
  var isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
42871
43196
  var projectPath2 = (projectRoot, requested, label) => {
42872
- const root = resolve22(projectRoot);
42873
- const path = resolve22(root, requested);
42874
- const projectRelative = relative20(root, path);
42875
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep13}`) || isAbsolute11(projectRelative))
43197
+ const root = resolve23(projectRoot);
43198
+ const path = resolve23(root, requested);
43199
+ const projectRelative = relative21(root, path);
43200
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep14}`) || isAbsolute12(projectRelative))
42876
43201
  throw new TypeError(`${label} must remain inside the project.`);
42877
43202
  return path;
42878
43203
  };
42879
43204
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
42880
43205
  const modulePath = projectPath2(projectRoot, requestedModulePath, "mobile update registry");
42881
- await access19(modulePath).catch(() => {
43206
+ await access20(modulePath).catch(() => {
42882
43207
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
42883
43208
  });
42884
43209
  const loaded = await import(pathToFileURL4(modulePath).href);
@@ -42984,6 +43309,7 @@ export {
42984
43309
  ABSOLUTE_EXPO_UPDATE_FORMAT,
42985
43310
  ABSOLUTE_IOS_RELEASE_FORMAT,
42986
43311
  ABSOLUTE_IOS_SIMULATOR_NAME,
43312
+ ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
42987
43313
  ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
42988
43314
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
42989
43315
  ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
@@ -43069,6 +43395,7 @@ export {
43069
43395
  createAbsoluteMobileAssociationPlugin,
43070
43396
  createAbsoluteMobileAuthManifest,
43071
43397
  createAbsoluteMobileBlobArtifactStore,
43398
+ createAbsoluteMobileCertificationVerification,
43072
43399
  createAbsoluteMobileCompatibilityArtifact,
43073
43400
  createAbsoluteMobileCompatibilityDispatcher,
43074
43401
  createAbsoluteMobileFileArtifactStore,
@@ -43160,6 +43487,7 @@ export {
43160
43487
  parseAbsoluteIosInstalledAppInfo,
43161
43488
  parseAbsoluteIosLogLine,
43162
43489
  parseAbsoluteMobileBuildPageMetadata,
43490
+ parseAbsoluteMobileCertificationVerification,
43163
43491
  parseAbsoluteMobileCompatibilityArtifact,
43164
43492
  parseAbsoluteMobilePageEnvelope,
43165
43493
  parseAbsoluteMobilePageRequest,
@@ -43185,10 +43513,12 @@ export {
43185
43513
  publishAbsoluteMobileUpdate,
43186
43514
  readAbsoluteAndroidRelease,
43187
43515
  readAbsoluteIosRelease,
43516
+ readAbsoluteMobileCertificationVerification,
43188
43517
  readAbsoluteMobileLinkIntent,
43189
43518
  readAbsoluteMobileMaterializedReleases,
43190
43519
  readAbsoluteMobileReleaseCertification,
43191
43520
  readAbsoluteMobileUpdate,
43521
+ readAbsoluteSigstoreBundle,
43192
43522
  reconcileAbsoluteMobileUpdateRollout,
43193
43523
  redactAbsoluteIosLog,
43194
43524
  removeAbsoluteRemoteMacProfile,
@@ -43217,6 +43547,7 @@ export {
43217
43547
  runAbsoluteRemoteIosReleaseAcceptance,
43218
43548
  runWithAbsoluteMobileProducer,
43219
43549
  serializeAbsoluteMobileAuthEnvironment,
43550
+ serializeAbsoluteMobileReleaseCertification,
43220
43551
  startAbsoluteExpoDevSession,
43221
43552
  startAbsoluteIosCaEnrollmentServer,
43222
43553
  startAbsoluteIosDevSession,
@@ -43238,10 +43569,11 @@ export {
43238
43569
  waitForAbsoluteIosHmrLog,
43239
43570
  writeAbsoluteCapacitorConfig,
43240
43571
  writeAbsoluteExpoProject,
43572
+ writeAbsoluteMobileCertificationVerification,
43241
43573
  writeAbsoluteMobileGithubWorkflow,
43242
43574
  writeAbsoluteMobileReleaseCertification,
43243
43575
  writeAbsoluteMobileUpdateRegistry
43244
43576
  };
43245
43577
 
43246
- //# debugId=F8C3D6BAA9852D3F64756E2164756E21
43578
+ //# debugId=F05777F8BA048C6164756E2164756E21
43247
43579
  //# sourceMappingURL=index.js.map