@absolutejs/absolute 0.20.0-beta.49 → 0.20.0-beta.50

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.
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-gW9hwe/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-fjProX/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-gW9hwe/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-fjProX/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
48
48
  getWarningController()?.maybeWarn(primitiveName);
49
49
  };
50
50
 
51
- // .angular-partial-tmp-gW9hwe/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-fjProX/src/core/streamingSlotRegistry.ts
52
52
  var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
53
53
  var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
54
54
  var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
package/dist/cli/index.js CHANGED
@@ -4791,6 +4791,45 @@ var developmentTeamArgument = (value) => {
4791
4791
  return entry.isDirectory() ? findByExtension(path, extension) : undefined;
4792
4792
  }));
4793
4793
  return matches.find((match) => match !== undefined);
4794
+ }, findAllByExtension = async (root, extension, options = {}) => {
4795
+ if (!await pathExists3(root))
4796
+ return [];
4797
+ const entries = await readdir3(root, { withFileTypes: true });
4798
+ const matches = await Promise.all(entries.map(async (entry) => {
4799
+ const path = join13(root, entry.name);
4800
+ if (entry.name.endsWith(extension))
4801
+ return [path];
4802
+ if (!entry.isDirectory() || options.excludedDirectories?.has(entry.name))
4803
+ return [];
4804
+ return findAllByExtension(path, extension, options);
4805
+ }));
4806
+ return matches.flat().sort();
4807
+ }, resolveAbsoluteIosXcodeProject = async (nativeDirectory, options = {}) => {
4808
+ const requestedWorkspace = options.workspacePath ? resolve9(nativeDirectory, options.workspacePath) : undefined;
4809
+ if (requestedWorkspace) {
4810
+ const requestedRelative = relative5(nativeDirectory, requestedWorkspace);
4811
+ if (requestedRelative === ".." || requestedRelative.startsWith(`..${sep3}`) || isAbsolute2(requestedRelative))
4812
+ throw new TypeError("iOS release workspacePath must remain inside the native iOS project.");
4813
+ }
4814
+ const workspaces = requestedWorkspace ? [requestedWorkspace] : await findAllByExtension(nativeDirectory, ".xcworkspace", {
4815
+ excludedDirectories: new Set(["Pods", "DerivedData", "build"])
4816
+ });
4817
+ if (workspaces.length !== 1)
4818
+ throw new TypeError(workspaces.length > 1 ? `iOS release found multiple Xcode workspaces: ${workspaces.map((path) => relative5(nativeDirectory, path)).join(", ")}. Pass an explicit workspacePath.` : "iOS release could not find an Xcode workspace.");
4819
+ const [workspacePath] = workspaces;
4820
+ if (!workspacePath || !await pathExists3(workspacePath))
4821
+ throw new TypeError("iOS release could not find an Xcode workspace.");
4822
+ if (options.scheme)
4823
+ return { scheme: options.scheme, workspacePath };
4824
+ const schemeFiles = await findAllByExtension(nativeDirectory, ".xcscheme", {
4825
+ excludedDirectories: new Set(["Pods", "DerivedData", "build"])
4826
+ });
4827
+ const schemes = [
4828
+ ...new Set(schemeFiles.map((path) => path.slice(path.lastIndexOf(sep3) + 1, -".xcscheme".length)))
4829
+ ];
4830
+ if (schemes.length !== 1 || !schemes[0])
4831
+ throw new TypeError(schemes.length > 1 ? `iOS release found multiple shared Xcode schemes: ${schemes.join(", ")}. Pass an explicit scheme.` : "iOS release could not find a shared Xcode scheme.");
4832
+ return { scheme: schemes[0], workspacePath };
4794
4833
  }, exportOptions = () => `<?xml version="1.0" encoding="UTF-8"?>
4795
4834
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4796
4835
  <plist version="1.0"><dict>
@@ -4857,6 +4896,10 @@ var developmentTeamArgument = (value) => {
4857
4896
  if (manifest.appId !== options.config.appId)
4858
4897
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
4859
4898
  const nativeDirectory = join13(options.config.nativeProjectDirectory, "ios");
4899
+ const xcode = await resolveAbsoluteIosXcodeProject(nativeDirectory, {
4900
+ scheme: options.scheme,
4901
+ workspacePath: options.workspacePath
4902
+ });
4860
4903
  let buildNumber = requireBuildNumber(options.buildNumber);
4861
4904
  if (options.prepareBuildNumber) {
4862
4905
  const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
@@ -4882,9 +4925,9 @@ var developmentTeamArgument = (value) => {
4882
4925
  const archiveExit = await run([
4883
4926
  "xcodebuild",
4884
4927
  "-workspace",
4885
- join13(nativeDirectory, "App", "App.xcworkspace"),
4928
+ xcode.workspacePath,
4886
4929
  "-scheme",
4887
- "App",
4930
+ xcode.scheme,
4888
4931
  "-configuration",
4889
4932
  "Release",
4890
4933
  "-destination",
@@ -4893,7 +4936,7 @@ var developmentTeamArgument = (value) => {
4893
4936
  archivePath,
4894
4937
  ...versionArguments,
4895
4938
  "archive"
4896
- ], { cwd: nativeDirectory });
4939
+ ], { cwd: nativeDirectory, env: options.env });
4897
4940
  if (archiveExit !== 0)
4898
4941
  throw new TypeError("Xcode failed to archive the iOS app.");
4899
4942
  const archivedApp = await findByExtension(join13(archivePath, "Products", "Applications"), ".app");
@@ -4916,7 +4959,7 @@ var developmentTeamArgument = (value) => {
4916
4959
  exportPath,
4917
4960
  "-exportOptionsPlist",
4918
4961
  exportPlist
4919
- ], { cwd: nativeDirectory });
4962
+ ], { cwd: nativeDirectory, env: options.env });
4920
4963
  if (exportExit !== 0)
4921
4964
  throw new TypeError("Xcode failed to export the App Store IPA.");
4922
4965
  const artifactPath = await findByExtension(exportPath, ".ipa");
@@ -4932,7 +4975,7 @@ var developmentTeamArgument = (value) => {
4932
4975
  appId: manifest.appId,
4933
4976
  ...buildNumber === undefined ? {} : { buildNumber },
4934
4977
  bytes,
4935
- engine: "capacitor",
4978
+ engine: options.config.engine,
4936
4979
  format: 1,
4937
4980
  marketingVersion,
4938
4981
  platform: "ios",
@@ -19888,8 +19931,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
19888
19931
  if (!isRecord14(root.expo))
19889
19932
  throw new TypeError("Generated Expo application config is invalid.");
19890
19933
  const { expo } = root;
19891
- if (expo.name !== config.appName || !isRecord14(expo.android) || expo.android.package !== config.appId) {
19892
- throw new TypeError("Generated Expo Android identity does not match mobile config.");
19934
+ if (expo.name !== config.appName || config.platforms.includes("android") && (!isRecord14(expo.android) || expo.android.package !== config.appId) || config.platforms.includes("ios") && (!isRecord14(expo.ios) || expo.ios.bundleIdentifier !== config.appId)) {
19935
+ throw new TypeError("Generated Expo application identity does not match mobile config.");
19893
19936
  }
19894
19937
  if (!isRecord14(expo.runtimeVersion) || expo.runtimeVersion.policy !== "appVersion") {
19895
19938
  throw new TypeError("Generated Expo runtimeVersion must follow the native app version.");
@@ -19899,7 +19942,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
19899
19942
  }
19900
19943
  return pass("expo.app-config", "Expo application identity and runtime policy match the production mobile config.", path);
19901
19944
  } catch (error) {
19902
- return fail5("expo.app-config", error instanceof Error ? error.message : "Generated Expo application config could not be validated.", path, "Run `absolute mobile build android`; do not edit the generated Expo project.");
19945
+ return fail5("expo.app-config", error instanceof Error ? error.message : "Generated Expo application config could not be validated.", path, "Run `absolute mobile build <platform>`; do not edit the generated Expo project.");
19903
19946
  }
19904
19947
  }, expoEmbeddedAssetsCheck = async (config) => {
19905
19948
  const generated = join52(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
@@ -19936,7 +19979,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
19936
19979
  throw new TypeError("Generated Expo asset bytes differ from the prepared mobile bundle.");
19937
19980
  return pass("expo.bundle-projection", `Expo embeds the complete signed mobile bundle as ${embeddedFiles.length} opaque asset(s).`, generated);
19938
19981
  } catch (error) {
19939
- return fail5("expo.bundle-projection", error instanceof Error ? error.message : "Generated Expo assets could not be validated.", generated, "Run `absolute mobile build android` to regenerate and verify the production asset projection.");
19982
+ return fail5("expo.bundle-projection", error instanceof Error ? error.message : "Generated Expo assets could not be validated.", generated, "Run `absolute mobile build <platform>` to regenerate and verify the production asset projection.");
19940
19983
  }
19941
19984
  }, sourceFiles = async (root, extensions) => {
19942
19985
  if (!await pathExists6(root))
@@ -19978,15 +20021,16 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
19978
20021
  return fail5("android.deep-links", error instanceof Error ? error.message : "Android deep-link projection could not be validated.", manifestPath, "Run `absolute mobile sync android` and review the AbsoluteJS-owned deep-link region.");
19979
20022
  }
19980
20023
  }, iosNativeSecurityCheck = async (iosRoot) => {
19981
- const entitlementsPath = join52(iosRoot, "App/AbsoluteJS.entitlements");
19982
- const entitlements = await readFile18(entitlementsPath, "utf8").catch(() => "");
19983
- if (/<key>get-task-allow<\/key>\s*<true\s*\/>/u.test(entitlements) || /<key>com\.apple\.security\.get-task-allow<\/key>\s*<true\s*\/>/u.test(entitlements))
19984
- return fail5("ios.native-debugging", "iOS source entitlements explicitly permit debugger attachment.", entitlementsPath, "Remove get-task-allow from source entitlements; Xcode supplies development entitlements only to debug builds.");
19985
- const sources = await sourceFiles(join52(iosRoot, "App"), new Set([".m", ".mm", ".swift"]));
20024
+ const applicationFiles = async (extensions) => (await sourceFiles(iosRoot, extensions)).filter((path) => !relative26(iosRoot, path).split(/[\\/]/u).some((part) => ["Pods", "DerivedData", "build"].includes(part)));
20025
+ const entitlementPaths = await applicationFiles(new Set([".entitlements"]));
20026
+ const unsafeEntitlements = await containsPattern(entitlementPaths, /<key>(?:com\.apple\.security\.)?get-task-allow<\/key>\s*<true\s*\/>/u);
20027
+ if (unsafeEntitlements)
20028
+ return fail5("ios.native-debugging", "iOS source entitlements explicitly permit debugger attachment.", unsafeEntitlements, "Remove get-task-allow from source entitlements; Xcode supplies development entitlements only to debug builds.");
20029
+ const sources = await applicationFiles(new Set([".m", ".mm", ".swift"]));
19986
20030
  const debugSource = await containsPattern(sources, /\.isInspectable\s*=\s*true|setInspectable\s*\(\s*true\s*\)/u);
19987
20031
  if (debugSource)
19988
20032
  return fail5("ios.native-debugging", "iOS application source unconditionally enables WebView inspection.", debugSource, "Remove unconditional WebView inspection from release source.");
19989
- return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementsPath);
20033
+ return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementPaths[0] ?? iosRoot);
19990
20034
  }, iosDeepLinkProjectionCheck = async (config, iosRoot) => {
19991
20035
  const infoPath = join52(iosRoot, "App/App/Info.plist");
19992
20036
  const entitlementsPath = join52(iosRoot, "App/AbsoluteJS.entitlements");
@@ -20041,15 +20085,43 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
20041
20085
  }, iosDevicePermissionCheck = async (config, purposes) => {
20042
20086
  if (!config.platforms.includes("ios") || purposes.length === 0)
20043
20087
  return;
20044
- const path = join52(config.nativeProjectDirectory, "ios/App/App/Info.plist");
20088
+ const iosRoot = join52(config.nativeProjectDirectory, "ios");
20089
+ const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join52(iosRoot, "App/App/Info.plist");
20045
20090
  const source = await readFile18(path, "utf8");
20046
20091
  const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
20047
20092
  if (missing.length === 0)
20048
20093
  return;
20049
20094
  return fail5("mobile.device-capabilities", `iOS is missing usage descriptions for: ${missing.join(", ")}.`, path, "Run `absolute mobile sync ios` to regenerate detected device usage descriptions.");
20095
+ }, expoIosPrivacyCapabilityCheck = async (iosRoot, project, requirements) => {
20096
+ if (requirements.iosPrivacyAccessedApis.length === 0)
20097
+ return;
20098
+ const privacyPath = await uniqueExpoIosFile(iosRoot, "**/PrivacyInfo.xcprivacy", "privacy manifest");
20099
+ const privacy = await readFile18(privacyPath, "utf8");
20100
+ const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
20101
+ if (!missing && project.includes(privacyPath.split(/[\\/]/u).at(-1) ?? ""))
20102
+ return;
20103
+ return fail5("mobile.device-capabilities", "Expo iOS privacy manifest or target membership does not match detected capabilities.", privacyPath, "Run `absolute mobile build ios` to regenerate detected Expo privacy declarations.");
20104
+ }, expoIosPushCapabilityCheck = async (iosRoot, requirements) => {
20105
+ if (!requirements.iosPushNotifications)
20106
+ return;
20107
+ const entitlementsPath = await uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements");
20108
+ const entitlements = await readFile18(entitlementsPath, "utf8");
20109
+ if (entitlements.includes("<key>aps-environment</key>"))
20110
+ return;
20111
+ return fail5("mobile.device-capabilities", "Expo iOS push entitlement does not match detected capabilities.", entitlementsPath, "Run `absolute mobile build ios` to regenerate native push integration.");
20112
+ }, expoIosCapabilityProjectionCheck = async (config, requirements) => {
20113
+ const iosRoot = join52(config.nativeProjectDirectory, "ios");
20114
+ const projectPath = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
20115
+ const project = await readFile18(projectPath, "utf8");
20116
+ const privacy = await expoIosPrivacyCapabilityCheck(iosRoot, project, requirements);
20117
+ if (privacy)
20118
+ return privacy;
20119
+ return expoIosPushCapabilityCheck(iosRoot, requirements);
20050
20120
  }, iosCapabilityProjectionCheck = async (config, requirements) => {
20051
20121
  if (!config.platforms.includes("ios"))
20052
20122
  return;
20123
+ if (config.engine === "expo")
20124
+ return expoIosCapabilityProjectionCheck(config, requirements);
20053
20125
  const appRoot = join52(config.nativeProjectDirectory, "ios/App/App");
20054
20126
  const infoPath = join52(appRoot, "Info.plist");
20055
20127
  const info2 = await readFile18(infoPath, "utf8").catch(() => "");
@@ -20151,6 +20223,58 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
20151
20223
  ...check2,
20152
20224
  path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
20153
20225
  }));
20226
+ }, uniqueExpoIosFile = async (iosRoot, pattern, label) => {
20227
+ const paths = (await Array.fromAsync(new Bun.Glob(pattern).scan({ cwd: iosRoot, onlyFiles: true }))).filter((path2) => !path2.split(/[\\/]/u).some((part) => ["Pods", "DerivedData", "build"].includes(part)));
20228
+ if (paths.length !== 1)
20229
+ throw new TypeError(paths.length === 0 ? `Generated Expo iOS project is missing its ${label}.` : `Generated Expo iOS project contains ambiguous ${label} files.`);
20230
+ const [path] = paths;
20231
+ if (!path)
20232
+ throw new TypeError(`Generated Expo iOS project is missing its ${label}.`);
20233
+ return join52(iosRoot, path);
20234
+ }, expoIosNativeProjectionCheck = async (config, iosRoot) => {
20235
+ try {
20236
+ const [infoPath, entitlementsPath, projectPath] = await Promise.all([
20237
+ uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist"),
20238
+ uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements"),
20239
+ uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project")
20240
+ ]);
20241
+ const [info2, entitlements, project] = await Promise.all([
20242
+ readFile18(infoPath, "utf8"),
20243
+ readFile18(entitlementsPath, "utf8"),
20244
+ readFile18(projectPath, "utf8")
20245
+ ]);
20246
+ if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2))
20247
+ throw new TypeError("Expo iOS App Transport Security permits arbitrary network loads.");
20248
+ if (config.deepLinkScheme && (!info2.includes("<key>CFBundleURLTypes</key>") || !info2.includes(`<string>${config.deepLinkScheme}</string>`)))
20249
+ throw new TypeError("Expo iOS custom URL scheme does not match mobile config.");
20250
+ if (config.deepLinkHosts.some((host2) => !entitlements.includes(`<string>applinks:${host2}</string>`)))
20251
+ throw new TypeError("Expo iOS associated domains do not match mobile config.");
20252
+ if (!project.includes(entitlementsPath.split(/[\\/]/u).at(-1) ?? ""))
20253
+ throw new TypeError("Expo iOS target does not sign its generated entitlements file.");
20254
+ return pass("ios.expo-native-projection", "Expo iOS transport security, URL schemes, universal links, and signed entitlements match mobile config.", entitlementsPath);
20255
+ } catch (error) {
20256
+ return fail5("ios.expo-native-projection", error instanceof Error ? error.message : "Expo iOS native projection could not be validated.", iosRoot, "Run `absolute mobile build ios` to regenerate the production Expo CNG project.");
20257
+ }
20258
+ }, inspectExpoIosRelease = async (config, projectRoot) => {
20259
+ const iosRoot = join52(config.nativeProjectDirectory, "ios");
20260
+ const journalPath = join52(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
20261
+ const checks = [
20262
+ await journalReleaseCheck(journalPath, "ios")
20263
+ ];
20264
+ checks.push(config.iosVersion ? pass("ios.marketing-version", `The iOS marketing version is ${config.iosVersion}.`) : fail5("ios.marketing-version", "iOS has no explicit App Store marketing version.", projectRoot, "Add mobile.ios.version to absolutejs.config.ts, for example 1.0.0."));
20265
+ checks.push(...await Promise.all([
20266
+ expoApplicationConfigCheck(config),
20267
+ expoEmbeddedAssetsCheck(config),
20268
+ hmrAssetsReleaseCheck(config.bundleDirectory),
20269
+ embeddedBundleReleaseCheck(config, projectRoot, "ios", config.bundleDirectory),
20270
+ contentSecurityPolicyCheck(config, "ios", config.bundleDirectory),
20271
+ expoIosNativeProjectionCheck(config, iosRoot),
20272
+ iosNativeSecurityCheck(iosRoot)
20273
+ ]));
20274
+ return checks.map((check2) => ({
20275
+ ...check2,
20276
+ path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
20277
+ }));
20154
20278
  }, inspectIosRelease = async (config, projectRoot) => {
20155
20279
  const iosAppRoot = join52(config.nativeProjectDirectory, "ios", "App", "App");
20156
20280
  const nativeConfigPath = join52(iosAppRoot, "capacitor.config.json");
@@ -20219,9 +20343,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
20219
20343
  if (config.platforms.includes("android"))
20220
20344
  checks.push(...config.engine === "expo" ? await inspectExpoAndroidRelease(config, projectRoot) : await inspectAndroidRelease(config, projectRoot));
20221
20345
  if (config.platforms.includes("ios"))
20222
- checks.push(...config.engine === "expo" ? [
20223
- fail5("expo.ios-release", "Expo iOS production release automation is not implemented yet.", config.nativeProjectDirectory, "Build Android independently or wait for the Expo iOS signing checkpoint.")
20224
- ] : await inspectIosRelease(config, projectRoot));
20346
+ checks.push(...config.engine === "expo" ? await inspectExpoIosRelease(config, projectRoot) : await inspectIosRelease(config, projectRoot));
20225
20347
  const syncSchema = syncSchemaReleaseCheck(projectRoot);
20226
20348
  if (syncSchema) {
20227
20349
  checks.push({
@@ -21545,10 +21667,8 @@ ${releaseAuditSteps("ios")}
21545
21667
  ...custom
21546
21668
  ], createAbsoluteMobileGithubWorkflow = (options) => {
21547
21669
  const platforms = [
21548
- ...options.config.engine === "expo" ? options.config.platforms.filter((platform6) => platform6 === "android") : options.config.platforms
21670
+ ...options.config.platforms
21549
21671
  ].sort();
21550
- if (platforms.length === 0)
21551
- throw new TypeError("Generated Expo production CI currently requires android in mobile.platforms; Expo iOS release automation is the next checkpoint.");
21552
21672
  const includePublishing = options.includePublishing === true;
21553
21673
  const customSecrets = normalizeSecretEnvironment(options.secretEnvironment);
21554
21674
  const serverEntry = projectPath(options.projectRoot, options.serverEntry ?? "server.ts", "mobile ci github server entry");
@@ -21746,13 +21866,13 @@ var NOT_FOUND4 = -1, isRecord17 = (value) => typeof value === "object" && value
21746
21866
  }
21747
21867
  }, runCapacitor = async (projectRoot, args) => {
21748
21868
  const executable = await capacitorExecutable(projectRoot);
21749
- const subprocess = Bun.spawn([executable, ...args], {
21869
+ const process2 = Bun.spawn([executable, ...args], {
21750
21870
  cwd: projectRoot,
21751
21871
  stderr: "inherit",
21752
21872
  stdin: "inherit",
21753
21873
  stdout: "inherit"
21754
21874
  });
21755
- const exitCode = await subprocess.exited;
21875
+ const exitCode = await process2.exited;
21756
21876
  if (exitCode !== 0) {
21757
21877
  throw new TypeError(`Capacitor exited with status ${exitCode}.`);
21758
21878
  }
@@ -22233,7 +22353,18 @@ Mobile release security and compliance checks failed.`);
22233
22353
  await ensureExpoPackages(mobile.nativeProjectDirectory, [...args, "--yes"]);
22234
22354
  await syncAbsoluteExpoWebAssets(mobile);
22235
22355
  await runExpo(mobile.nativeProjectDirectory, ["prebuild", "--clean", "--no-install", "--platform", "android"], { production: true });
22236
- }, prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
22356
+ }, prepareExpoIosReleaseProject = async (mobile, projectRoot, args) => {
22357
+ await writeAbsoluteExpoProject(mobile, { projectRoot });
22358
+ await ensureExpoPackages(mobile.nativeProjectDirectory, [...args, "--yes"]);
22359
+ await syncAbsoluteExpoWebAssets(mobile);
22360
+ await runExpo(mobile.nativeProjectDirectory, ["prebuild", "--clean", "--no-install", "--platform", "ios"], { production: true });
22361
+ }, prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
22362
+ await writeAbsoluteCapacitorConfig(mobile, { projectRoot });
22363
+ await runCapacitorForPlatforms(projectRoot, "sync", ["ios"]);
22364
+ await applyAbsoluteNativeDeepLinks(mobile, ["ios"]);
22365
+ await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile, ["ios"]);
22366
+ await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
22367
+ }, prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot), prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
22237
22368
  await writeAbsoluteCapacitorConfig(mobile, { projectRoot });
22238
22369
  await runCapacitorForPlatforms(projectRoot, "sync", ["android"]);
22239
22370
  await applyAbsoluteNativeDeepLinks(mobile, ["android"]);
@@ -22319,7 +22450,7 @@ Mobile release security and compliance checks failed.`);
22319
22450
  } finally {
22320
22451
  sendTelemetryEvent("mobile:android-release-publish", {
22321
22452
  durationMs: Math.round(performance.now() - startedAt),
22322
- engine: "capacitor",
22453
+ engine: mobile.engine,
22323
22454
  platform: "android",
22324
22455
  provider: googlePlay ? "google-play" : "registry-module",
22325
22456
  reused,
@@ -22344,27 +22475,22 @@ Mobile release security and compliance checks failed.`);
22344
22475
  }, buildIos = async (args, prepareBuildNumber) => {
22345
22476
  const configPath2 = valueAfter(args, "--config");
22346
22477
  const { mobile, projectRoot } = await loadMobile(configPath2);
22347
- requireCapacitorEngine(mobile, "mobile build ios");
22348
22478
  if (!mobile.platforms.includes("ios")) {
22349
22479
  throw new TypeError("mobile build ios requires ios in mobile.platforms.");
22350
22480
  }
22351
22481
  const startedAt = performance.now();
22352
22482
  let success = false;
22353
22483
  try {
22354
- await repairAbsoluteIosDevSession(projectRoot);
22484
+ if (mobile.engine === "capacitor")
22485
+ await repairAbsoluteIosDevSession(projectRoot);
22355
22486
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
22356
- await writeAbsoluteCapacitorConfig(mobile, { projectRoot });
22357
- await runCapacitorForPlatforms(projectRoot, "sync", ["ios"]);
22358
- await applyAbsoluteNativeDeepLinks(mobile, ["ios"]);
22359
- await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile, [
22360
- "ios"
22361
- ]);
22362
- await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
22487
+ await prepareIosReleaseProject(mobile, projectRoot, args);
22363
22488
  await requireIosReleaseReady(mobile, projectRoot);
22364
22489
  const release = await buildAbsoluteIosRelease({
22365
22490
  allowUnsigned: args.includes("--unsigned"),
22366
22491
  config: mobile,
22367
22492
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
22493
+ ...mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
22368
22494
  outputDirectory: valueAfter(args, "--outdir"),
22369
22495
  ...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
22370
22496
  projectRoot
@@ -22378,7 +22504,7 @@ Mobile release security and compliance checks failed.`);
22378
22504
  } finally {
22379
22505
  sendTelemetryEvent("mobile:ios-release-build", {
22380
22506
  durationMs: Math.round(performance.now() - startedAt),
22381
- engine: "capacitor",
22507
+ engine: mobile.engine,
22382
22508
  platform: "ios",
22383
22509
  success,
22384
22510
  type: "ipa",
@@ -22391,7 +22517,6 @@ Mobile release security and compliance checks failed.`);
22391
22517
  const configPath2 = valueAfter(args, "--config");
22392
22518
  const appStoreConnect = appStoreConnectTarget(args);
22393
22519
  const { mobile, projectRoot } = await loadMobile(configPath2);
22394
- requireCapacitorEngine(mobile, "mobile publish ios");
22395
22520
  const startedAt = performance.now();
22396
22521
  let reused = false;
22397
22522
  let success = false;
@@ -22427,7 +22552,7 @@ Mobile release security and compliance checks failed.`);
22427
22552
  } finally {
22428
22553
  sendTelemetryEvent("mobile:ios-release-publish", {
22429
22554
  durationMs: Math.round(performance.now() - startedAt),
22430
- engine: "capacitor",
22555
+ engine: mobile.engine,
22431
22556
  platform: "ios",
22432
22557
  provider: appStoreConnect ? "app-store-connect" : "registry-module",
22433
22558
  reused,
@@ -2649,6 +2649,47 @@ var findByExtension = async (root, extension) => {
2649
2649
  }));
2650
2650
  return matches.find((match) => match !== undefined);
2651
2651
  };
2652
+ var findAllByExtension = async (root, extension, options = {}) => {
2653
+ if (!await pathExists4(root))
2654
+ return [];
2655
+ const entries = await readdir3(root, { withFileTypes: true });
2656
+ const matches = await Promise.all(entries.map(async (entry) => {
2657
+ const path = join5(root, entry.name);
2658
+ if (entry.name.endsWith(extension))
2659
+ return [path];
2660
+ if (!entry.isDirectory() || options.excludedDirectories?.has(entry.name))
2661
+ return [];
2662
+ return findAllByExtension(path, extension, options);
2663
+ }));
2664
+ return matches.flat().sort();
2665
+ };
2666
+ var resolveAbsoluteIosXcodeProject = async (nativeDirectory, options = {}) => {
2667
+ const requestedWorkspace = options.workspacePath ? resolve4(nativeDirectory, options.workspacePath) : undefined;
2668
+ if (requestedWorkspace) {
2669
+ const requestedRelative = relative4(nativeDirectory, requestedWorkspace);
2670
+ if (requestedRelative === ".." || requestedRelative.startsWith(`..${sep3}`) || isAbsolute3(requestedRelative))
2671
+ throw new TypeError("iOS release workspacePath must remain inside the native iOS project.");
2672
+ }
2673
+ const workspaces = requestedWorkspace ? [requestedWorkspace] : await findAllByExtension(nativeDirectory, ".xcworkspace", {
2674
+ excludedDirectories: new Set(["Pods", "DerivedData", "build"])
2675
+ });
2676
+ if (workspaces.length !== 1)
2677
+ throw new TypeError(workspaces.length > 1 ? `iOS release found multiple Xcode workspaces: ${workspaces.map((path) => relative4(nativeDirectory, path)).join(", ")}. Pass an explicit workspacePath.` : "iOS release could not find an Xcode workspace.");
2678
+ const [workspacePath] = workspaces;
2679
+ if (!workspacePath || !await pathExists4(workspacePath))
2680
+ throw new TypeError("iOS release could not find an Xcode workspace.");
2681
+ if (options.scheme)
2682
+ return { scheme: options.scheme, workspacePath };
2683
+ const schemeFiles = await findAllByExtension(nativeDirectory, ".xcscheme", {
2684
+ excludedDirectories: new Set(["Pods", "DerivedData", "build"])
2685
+ });
2686
+ const schemes = [
2687
+ ...new Set(schemeFiles.map((path) => path.slice(path.lastIndexOf(sep3) + 1, -".xcscheme".length)))
2688
+ ];
2689
+ if (schemes.length !== 1 || !schemes[0])
2690
+ throw new TypeError(schemes.length > 1 ? `iOS release found multiple shared Xcode schemes: ${schemes.join(", ")}. Pass an explicit scheme.` : "iOS release could not find a shared Xcode scheme.");
2691
+ return { scheme: schemes[0], workspacePath };
2692
+ };
2652
2693
  var exportOptions = () => `<?xml version="1.0" encoding="UTF-8"?>
2653
2694
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2654
2695
  <plist version="1.0"><dict>
@@ -2718,6 +2759,10 @@ var buildAbsoluteIosRelease = async (options) => {
2718
2759
  if (manifest.appId !== options.config.appId)
2719
2760
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
2720
2761
  const nativeDirectory = join5(options.config.nativeProjectDirectory, "ios");
2762
+ const xcode = await resolveAbsoluteIosXcodeProject(nativeDirectory, {
2763
+ scheme: options.scheme,
2764
+ workspacePath: options.workspacePath
2765
+ });
2721
2766
  let buildNumber = requireBuildNumber(options.buildNumber);
2722
2767
  if (options.prepareBuildNumber) {
2723
2768
  const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
@@ -2743,9 +2788,9 @@ var buildAbsoluteIosRelease = async (options) => {
2743
2788
  const archiveExit = await run([
2744
2789
  "xcodebuild",
2745
2790
  "-workspace",
2746
- join5(nativeDirectory, "App", "App.xcworkspace"),
2791
+ xcode.workspacePath,
2747
2792
  "-scheme",
2748
- "App",
2793
+ xcode.scheme,
2749
2794
  "-configuration",
2750
2795
  "Release",
2751
2796
  "-destination",
@@ -2754,7 +2799,7 @@ var buildAbsoluteIosRelease = async (options) => {
2754
2799
  archivePath,
2755
2800
  ...versionArguments,
2756
2801
  "archive"
2757
- ], { cwd: nativeDirectory });
2802
+ ], { cwd: nativeDirectory, env: options.env });
2758
2803
  if (archiveExit !== 0)
2759
2804
  throw new TypeError("Xcode failed to archive the iOS app.");
2760
2805
  const archivedApp = await findByExtension(join5(archivePath, "Products", "Applications"), ".app");
@@ -2777,7 +2822,7 @@ var buildAbsoluteIosRelease = async (options) => {
2777
2822
  exportPath,
2778
2823
  "-exportOptionsPlist",
2779
2824
  exportPlist
2780
- ], { cwd: nativeDirectory });
2825
+ ], { cwd: nativeDirectory, env: options.env });
2781
2826
  if (exportExit !== 0)
2782
2827
  throw new TypeError("Xcode failed to export the App Store IPA.");
2783
2828
  const artifactPath = await findByExtension(exportPath, ".ipa");
@@ -2793,7 +2838,7 @@ var buildAbsoluteIosRelease = async (options) => {
2793
2838
  appId: manifest.appId,
2794
2839
  ...buildNumber === undefined ? {} : { buildNumber },
2795
2840
  bytes,
2796
- engine: "capacitor",
2841
+ engine: options.config.engine,
2797
2842
  format: 1,
2798
2843
  marketingVersion,
2799
2844
  platform: "ios",
@@ -8706,10 +8751,8 @@ var requiredSecrets = (platforms, includePublishing, custom) => [
8706
8751
  ];
8707
8752
  var createAbsoluteMobileGithubWorkflow = (options) => {
8708
8753
  const platforms = [
8709
- ...options.config.engine === "expo" ? options.config.platforms.filter((platform) => platform === "android") : options.config.platforms
8754
+ ...options.config.platforms
8710
8755
  ].sort();
8711
- if (platforms.length === 0)
8712
- throw new TypeError("Generated Expo production CI currently requires android in mobile.platforms; Expo iOS release automation is the next checkpoint.");
8713
8756
  const includePublishing = options.includePublishing === true;
8714
8757
  const customSecrets = normalizeSecretEnvironment(options.secretEnvironment);
8715
8758
  const serverEntry = projectPath(options.projectRoot, options.serverEntry ?? "server.ts", "mobile ci github server entry");
@@ -10487,6 +10530,7 @@ export {
10487
10530
  removeAbsoluteRemoteMacProfile,
10488
10531
  repairAbsoluteIosDevSession,
10489
10532
  resolveAbsoluteDeviceCapabilityPlan,
10533
+ resolveAbsoluteIosXcodeProject,
10490
10534
  resolveAbsoluteMobileAuthManifest,
10491
10535
  resolveAbsoluteMobileCompatibilityRelease,
10492
10536
  resolveAbsoluteMobileDeepLink,
@@ -10515,5 +10559,5 @@ export {
10515
10559
  writeAbsoluteMobileGithubWorkflow
10516
10560
  };
10517
10561
 
10518
- //# debugId=9EFFDF4D91097A2464756E2164756E21
10562
+ //# debugId=25517EB0CBB1387F64756E2164756E21
10519
10563
  //# sourceMappingURL=index.js.map