catalyst-core 1.2.0-canary.4 → 1.2.0-canary.5

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.
@@ -330,9 +330,9 @@ export const ERROR_DEFINITIONS = {
330
330
  category: "IOS",
331
331
  defaultMessage: "iOS code signing is not set up",
332
332
  defaultDetails:
333
- "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
333
+ "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. A debug build for an iPhone signs with WEBVIEW_CONFIG.ios.developmentTeam from config/config.json, and with the installed development profile ios.provisioningProfile names when that is set. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
334
334
  suggestedAction:
335
- "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, and for a device build, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
335
+ "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
336
336
  },
337
337
  [ERROR_CODES.IOS_ASC_API_KEY_INVALID]: {
338
338
  category: "IOS",
@@ -226,8 +226,8 @@
226
226
  "IOS-010": {
227
227
  "category": "IOS",
228
228
  "message": "iOS code signing is not set up",
229
- "details": "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
230
- "suggestedAction": "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, and for a device build, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
229
+ "details": "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. A debug build for an iPhone signs with WEBVIEW_CONFIG.ios.developmentTeam from config/config.json, and with the installed development profile ios.provisioningProfile names when that is set. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
230
+ "suggestedAction": "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
231
231
  "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-010.md"
232
232
  },
233
233
  "IOS-011": {
@@ -11,6 +11,7 @@ const {
11
11
  runInteractiveCommand
12
12
  } = require("../utils.js");
13
13
  const {
14
+ DEBUG_BUILD_SETTINGS,
14
15
  xcodeAuthArgs
15
16
  } = require("./signing.js");
16
17
  module.exports = function createBuildPhase(ctx) {
@@ -138,6 +139,7 @@ module.exports = function createBuildPhase(ctx) {
138
139
  CODE_SIGNING_REQUIRED=NO \
139
140
  CODE_SIGNING_ALLOWED=NO \
140
141
  ONLY_ACTIVE_ARCH=YES \
142
+ ${DEBUG_BUILD_SETTINGS.join(" ")} \
141
143
  BUILD_DIR="${derivedDataPath}/${projectName}-Build/Build/Products" \
142
144
  CONFIGURATION_BUILD_DIR="${derivedDataPath}/${projectName}-Build/Build/Products/Debug-iphonesimulator" \
143
145
  OS_ACTIVITY_MODE=debug \
@@ -319,7 +321,7 @@ module.exports = function createBuildPhase(ctx) {
319
321
  const signingArgs = ctx.signingPlan?.style === "manual" ? [] : xcodeAuthArgs(ctx.apiKey);
320
322
  // The streamed output is what tells you how it is going; argv, not a
321
323
  // shell string, so a key path with spaces needs no quoting.
322
- return runCommandStreaming("xcodebuild", ["-scheme", scheme, "-sdk", "iphoneos", "-configuration", "Debug", "-destination", `platform=iOS,id=${device.udid}`, `PRODUCT_BUNDLE_IDENTIFIER=${bundleId}`, "ONLY_ACTIVE_ARCH=YES", ...signingArgs, "build"]);
324
+ return runCommandStreaming("xcodebuild", ["-scheme", scheme, "-sdk", "iphoneos", "-configuration", "Debug", "-destination", `platform=iOS,id=${device.udid}`, `PRODUCT_BUNDLE_IDENTIFIER=${bundleId}`, "ONLY_ACTIVE_ARCH=YES", ...DEBUG_BUILD_SETTINGS, ...signingArgs, "build"]);
323
325
  }
324
326
  async function findPhysicalDeviceAppPath() {
325
327
  const DERIVED_DATA_DIR = path.join(os.homedir(), "Library/Developer/Xcode/DerivedData");
@@ -35,6 +35,7 @@ const {
35
35
  createProfileScanner,
36
36
  pickInstalledProfile,
37
37
  XCODE_TEAM_HINT,
38
+ deviceSigningHint,
38
39
  missingProfileError,
39
40
  buildExportOptions,
40
41
  writeExportOptionsPlist,
@@ -469,10 +470,12 @@ function createIosBuild(config, {
469
470
  progress.complete("build");
470
471
  } catch (error) {
471
472
  // The diagnostic in the catch below shows what xcodebuild
472
- // said. A signing failure gets IOS-010, which names both
473
- // ways to set signing up. Not checked up front: an Apple
474
- // account signed in to Xcode signs fine without either.
475
- const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the device build", error) : error;
473
+ // said. A signing failure gets IOS-010 with the
474
+ // development hint (ios.developmentTeam, or what to check
475
+ // in ios.provisioningProfile), not the App Store one.
476
+ const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the device build", error, {
477
+ hint: deviceSigningHint(signingPlan)
478
+ }) : error;
476
479
  failure.stage = failure.stage || "Building for the device";
477
480
  throw failure;
478
481
  }
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.XCODE_TEAM_HINT = exports.SIGNING_PROPERTIES = exports.EXPORT_METHODS = void 0;
6
+ exports.XCODE_TEAM_HINT = exports.SIGNING_PROPERTIES = exports.EXPORT_METHODS = exports.DEVICE_TEAM_HINT = exports.DEBUG_BUILD_SETTINGS = void 0;
7
7
  exports.buildExportOptions = buildExportOptions;
8
8
  exports.chooseXcodeTeam = chooseXcodeTeam;
9
9
  exports.createProfileScanner = createProfileScanner;
10
+ exports.deviceSigningHint = deviceSigningHint;
10
11
  exports.findInstalledProfileIdentity = findInstalledProfileIdentity;
11
12
  exports.isReleaseBuildType = isReleaseBuildType;
12
13
  exports.isSigningFailure = isSigningFailure;
@@ -222,6 +223,15 @@ function readIosCredentials(dir, {
222
223
  };
223
224
  }
224
225
 
226
+ /**
227
+ * xcodebuild build settings for every debug build, simulator and iPhone.
228
+ * ENABLE_DEBUG_DYLIB=NO: for Xcode's Previews a Debug app is split into the
229
+ * app, <app>.debug.dylib and __preview.dylib, each signed on its own, so an
230
+ * iPhone build asked for the Keychain password three times. A command line
231
+ * build has no use for Previews. The release archive does not use them.
232
+ */
233
+ const DEBUG_BUILD_SETTINGS = exports.DEBUG_BUILD_SETTINGS = ["ENABLE_DEBUG_DYLIB=NO"];
234
+
225
235
  /**
226
236
  * xcodebuild flags that let automatic signing create and fetch certificates
227
237
  * and profiles: with the API key when there is one (cloud signing),
@@ -701,6 +711,25 @@ function findInstalledProfileIdentity({
701
711
 
702
712
  const XCODE_TEAM_HINT = exports.XCODE_TEAM_HINT = "Open Xcode > Settings > Accounts and sign in with the Apple account that has your company's team, or set WEBVIEW_CONFIG.ios.developmentTeam.";
703
713
 
714
+ /**
715
+ * IOS-010's hint when Xcode could not sign a debug build for an iPhone. That
716
+ * build signs with what config/config.json sets, nothing else: the team, and
717
+ * an installed development profile when ios.provisioningProfile names one.
718
+ */
719
+ const DEVICE_TEAM_HINT = exports.DEVICE_TEAM_HINT = "Set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID. With Xcode signed in to that team, Xcode signs automatically. Or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone.";
720
+
721
+ /**
722
+ * DEVICE_TEAM_HINT, or for a plan that signs with ios.provisioningProfile
723
+ * what to check in that profile.
724
+ *
725
+ * @param {ReturnType<typeof planIosSigning>} [plan]
726
+ * @returns {string}
727
+ */
728
+ function deviceSigningHint(plan) {
729
+ if (!plan?.provisioningProfile) return DEVICE_TEAM_HINT;
730
+ return `Check that the profile "${plan.provisioningProfile}" set in WEBVIEW_CONFIG.ios.provisioningProfile includes this iPhone, matches the bundle id, and that its certificate is in your keychain.`;
731
+ }
732
+
704
733
  /**
705
734
  * IOS-010's detail and hint for an App Store release that found no installed
706
735
  * App Store profile for its bundle id (plan.missingProfileFor) and has no
@@ -1188,17 +1217,21 @@ function signingFailureLine(output) {
1188
1217
  * @param {any} [cause]
1189
1218
  * @param {object} [options]
1190
1219
  * @param {string} [options.missingProfileFor] - plan.missingProfileFor
1220
+ * @param {string} [options.hint] - over the registry's hint, e.g. deviceSigningHint(plan)
1191
1221
  * @returns {any}
1192
1222
  */
1193
1223
  function signingSetupError(detail, cause, {
1194
- missingProfileFor
1224
+ missingProfileFor,
1225
+ hint
1195
1226
  } = {}) {
1196
1227
  const said = signingFailureLine(cause?.output);
1197
1228
  const failed = said ? `${detail}: ${said}` : detail;
1198
1229
  const missing = missingProfileError(missingProfileFor, failed);
1199
1230
  const error = missing ? (0, _utils.nativeError)("IOS-010", missing.detail, {
1200
1231
  hint: missing.hint
1201
- }) : (0, _utils.nativeError)("IOS-010", failed);
1232
+ }) : (0, _utils.nativeError)("IOS-010", failed, {
1233
+ hint
1234
+ });
1202
1235
  if (cause) {
1203
1236
  error.cause = cause;
1204
1237
  if (cause.output) error.output = cause.output;
@@ -489,7 +489,8 @@ async function chooseIosTarget({
489
489
  /**
490
490
  * The host a debug build reaches the dev server on. The simulator shares the
491
491
  * Mac's network, so localhost. An iPhone uses the Mac's Bonjour name
492
- * (`<LocalHostName>.local`), else its LAN IP; IOS-015 when there is neither.
492
+ * (`<localhostname>.local`, lowercased), else its LAN IP; IOS-015 when there
493
+ * is neither.
493
494
  * It goes into the build only, never into config.json.
494
495
  *
495
496
  * @param {{ target: string, name?: string } | null} target
@@ -72,8 +72,8 @@ function getLocalIPAddress(interfaces = _os.default.networkInterfaces()) {
72
72
 
73
73
  /**
74
74
  * The host a physical device on the same network reaches this Mac on:
75
- * `<LocalHostName>.local` (Bonjour, survives DHCP changes), else the LAN IP.
76
- * Null when neither is available.
75
+ * `<localhostname>.local` (Bonjour, survives DHCP changes, lowercased), else
76
+ * the LAN IP. Null when neither is available.
77
77
  *
78
78
  * @param {object} [options]
79
79
  * @param {() => string} [options.localHostName] - returns `scutil --get LocalHostName`
@@ -90,8 +90,10 @@ function getDeviceReachableHost({
90
90
  } catch (error) {
91
91
  // Not macOS, or scutil failed: fall back to the LAN IP.
92
92
  }
93
+ // Lowercase: WebKit sends the Host header lowercased, and Vite's
94
+ // server.allowedHosts compares it exactly.
93
95
  if (/^[A-Za-z0-9-]+$/.test(name)) return {
94
- host: `${name}.local`,
96
+ host: `${name.toLowerCase()}.local`,
95
97
  source: "bonjour"
96
98
  };
97
99
  const ip = getLocalIPAddress(interfaces);
@@ -508,7 +508,7 @@ const NATIVE_ERRORS = exports.NATIVE_ERRORS = {
508
508
  },
509
509
  "IOS-010": {
510
510
  message: "iOS code signing is not set up",
511
- hint: "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, and for a device build, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios."
511
+ hint: "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios."
512
512
  },
513
513
  "IOS-011": {
514
514
  message: "App Store Connect API key settings are incomplete or wrong",
@@ -38,11 +38,13 @@ export function resolveDevFsAllow(devFs, frameworkPaths) {
38
38
  * CATALYST_DEV_ALLOWED_HOSTS. `catalyst buildApp:ios` sets it to the Mac's
39
39
  * `<name>.local` when a physical iPhone loads the dev server by that name:
40
40
  * Vite answers 403 to any Host header that is not localhost or an IP.
41
+ * Hosts are lowercased: browsers send the Host header lowercased and Vite
42
+ * compares it exactly.
41
43
  */
42
44
  export function withExtraAllowedHosts(allowedHosts, extra) {
43
45
  const hosts = String(extra || "")
44
46
  .split(",")
45
- .map((host) => host.trim())
47
+ .map((host) => host.trim().toLowerCase())
46
48
  .filter(Boolean)
47
49
  if (allowedHosts === true || hosts.length === 0) return allowedHosts
48
50
  const base = Array.isArray(allowedHosts) ? allowedHosts : []
@@ -170,7 +170,7 @@
170
170
  {
171
171
  "section": "build_system",
172
172
  "title": "iOS Build Pipeline",
173
- "content": "npm run ios (catalyst buildApp:ios) checks full Xcode and offers the licence and first-launch steps, installs a simulator runtime, uses an existing iPhone simulator (ios.simulatorName, else the last pick, a booted one or the newest; 'Catalyst iPhone' is created after asking only when there is none), picks the target (simulator by default; a terminal run asks when an iPhone is connected; --device uses the iPhone; the pick is remembered in .catalyst/local.json), starts catalyst start when nothing answers and waits for it, generates ConfigConstants.swift and plugin metadata from WEBVIEW_CONFIG, syncs public/ios assets into the package-owned Xcode project, compiles with the Debug configuration, and installs and launches the .app. The simulator loads the dev server on localhost; an iPhone on the Mac's <LocalHostName>.local (else the LAN IP), with NSAllowsLocalNetworking in debug, and needs ios.developmentTeam plus an Apple account signed in to Xcode once (a free account works). config.json is never written. With --release (or the deprecated ios.buildType release) it archives and exports the signed store IPA instead. The generated project is located under the resolved catalyst-core package at dist/native/iosnativeWebView/iosnativeWebView.xcodeproj.",
173
+ "content": "npm run ios (catalyst buildApp:ios) checks full Xcode and offers the licence and first-launch steps, installs a simulator runtime, uses an existing iPhone simulator (ios.simulatorName, else the last pick, a booted one or the newest; 'Catalyst iPhone' is created after asking only when there is none), picks the target (simulator by default; a terminal run asks when an iPhone is connected; --device uses the iPhone; the pick is remembered in .catalyst/local.json), starts catalyst start when nothing answers and waits for it, generates ConfigConstants.swift and plugin metadata from WEBVIEW_CONFIG, syncs public/ios assets into the package-owned Xcode project, compiles with the Debug configuration, and installs and launches the .app. The simulator loads the dev server on localhost; an iPhone on the Mac's <localhostname>.local, lowercased (else the LAN IP), with NSAllowsLocalNetworking in debug. An iPhone build signs with what config.json sets and looks nothing up: ios.developmentTeam (with Xcode signed in to that team, Xcode signs automatically; a free account works), plus ios.provisioningProfile to sign with an installed development profile that includes the iPhone (and ios.bundleIdentifier when the profile is for another id); a signing failure is IOS-010. A debug build never writes config.json. With --release (or the deprecated ios.buildType release) it archives and exports the signed store IPA instead. The generated project is located under the resolved catalyst-core package at dist/native/iosnativeWebView/iosnativeWebView.xcodeproj.",
174
174
  "layer": "Build",
175
175
  "source": "static",
176
176
  "tags": ["ios", "build", "buildApp:ios", "debug", "xcode"],
@@ -484,7 +484,7 @@
484
484
  {
485
485
  "section": "config_structure",
486
486
  "title": "WEBVIEW_CONFIG Required Fields",
487
- "content": "WEBVIEW_CONFIG field reference (catalyst-core 1.2.0+, from nativeConfig.js):\n\nTOP-LEVEL (both platforms):\n- appId (string, required for release): Android application id and iOS bundle id, e.g. com.company.app. android.packageName / ios.bundleIdentifier / ios.appBundleId win over it when set. Release refuses com.example.* (NATIVE-003); a release in a terminal asks for it once and saves it. An iOS bundle id allows only letters, digits, - and ., so an appId with an underscore fails every iOS build (NATIVE-010): set ios.bundleIdentifier\n- appName (string, optional): name under the icon; wins over android.appName / ios.appName\n- url (string, required for release): https origin the release app loads, no path. --url overrides. Debug builds ignore it\n- allowPrivateHost (boolean, optional): lets a release load a private host\n- LOCAL_IP (string, optional): 'localhost' makes debug builds use the dev server (they work out its address). A real host (public, or a private VPN/LAN staging server, not this machine's own address) is loaded by debug builds instead; apps without url still use LOCAL_IP/port/useHttps as the release host\n- port (string, optional): dev server port when NODE_SERVER_PORT is not set\n- appInfo (string, optional): free-form text useDeviceInfo() reports, e.g. \"android-5Feb2026-v2.1.0\". The iOS build writes \"\" when it is missing\n- cachePattern (optional): set it per platform instead. A top-level cachePattern wins over android.cachePattern and ios.cachePattern\n- accessControl.enabled / allowedUrls (optional): URL allowlist, off when the block is missing; debug builds add the dev server origin\n\nANDROID (WEBVIEW_CONFIG.android, all optional):\n- sdkPath: else ANDROID_HOME, ANDROID_SDK_ROOT, default location; installed when missing\n- emulatorName: else the last used or catalyst-api36, created when missing\n- buildType: deprecated; 'release' builds a release with a warning\n- outputPath: release folder instead of build-output/android\n- buildOptimisation\n- cachePattern: comma-separated string of globs, e.g. \"*.css,*.js,*.png\"\n\nIOS (WEBVIEW_CONFIG.ios, all optional):\n- developmentTeam: Apple team ID, needed for an iPhone and for cloud or Xcode-account signing (IOS-010)\n- simulatorName: simulator to use when it exists; else the last pick, a booted iPhone simulator or the newest one. 'Catalyst iPhone' is created only when there is no iPhone simulator\n- deviceUDID: which iPhone --device uses without a terminal\n- provisioningProfile: installed profile name (or UUID) for manual signing; a terminal release saves the installed App Store profile it picked here, pinning later releases to it (remove the key to let the build pick again)\n- exportMethod: app-store-connect (default), release-testing, enterprise or debugging\n- buildType: deprecated; 'release' builds the store IPA with a warning\n- cachePattern: array of globs, e.g. [\"*.css\", \"*.js\"]; a string is read as one pattern, not split on commas\n\nVersion comes from package.json; signing files live in credentials/android/ and credentials/ios/, never in config.json.",
487
+ "content": "WEBVIEW_CONFIG field reference (catalyst-core 1.2.0+, from nativeConfig.js):\n\nTOP-LEVEL (both platforms):\n- appId (string, required for release): Android application id and iOS bundle id, e.g. com.company.app. android.packageName / ios.bundleIdentifier / ios.appBundleId win over it when set. Release refuses com.example.* (NATIVE-003); a release in a terminal asks for it once and saves it. An iOS bundle id allows only letters, digits, - and ., so an appId with an underscore fails every iOS build (NATIVE-010): set ios.bundleIdentifier\n- appName (string, optional): name under the icon; wins over android.appName / ios.appName\n- url (string, required for release): https origin the release app loads, no path. --url overrides. Debug builds ignore it\n- allowPrivateHost (boolean, optional): lets a release load a private host\n- LOCAL_IP (string, optional): 'localhost' makes debug builds use the dev server (they work out its address). A real host (public, or a private VPN/LAN staging server, not this machine's own address) is loaded by debug builds instead; apps without url still use LOCAL_IP/port/useHttps as the release host\n- port (string, optional): dev server port when NODE_SERVER_PORT is not set\n- appInfo (string, optional): free-form text useDeviceInfo() reports, e.g. \"android-5Feb2026-v2.1.0\". The iOS build writes \"\" when it is missing\n- cachePattern (optional): set it per platform instead. A top-level cachePattern wins over android.cachePattern and ios.cachePattern\n- accessControl.enabled / allowedUrls (optional): URL allowlist, off when the block is missing; debug builds add the dev server origin\n\nANDROID (WEBVIEW_CONFIG.android, all optional):\n- sdkPath: else ANDROID_HOME, ANDROID_SDK_ROOT, default location; installed when missing\n- emulatorName: else the last used or catalyst-api36, created when missing\n- buildType: deprecated; 'release' builds a release with a warning\n- outputPath: release folder instead of build-output/android\n- buildOptimisation\n- cachePattern: comma-separated string of globs, e.g. \"*.css,*.js,*.png\"\n\nIOS (WEBVIEW_CONFIG.ios, all optional):\n- developmentTeam: Apple team ID, needed for an iPhone and for cloud or Xcode-account signing (IOS-010); an iPhone debug build reads it from here only, a release in a terminal reads it from Xcode and saves it here when unset\n- simulatorName: simulator to use when it exists; else the last pick, a booted iPhone simulator or the newest one. 'Catalyst iPhone' is created only when there is no iPhone simulator\n- deviceUDID: which iPhone --device uses without a terminal\n- provisioningProfile: installed profile name (or UUID) for manual signing; an iPhone debug build signs with the development profile named here, which must include the iPhone; a terminal release saves the installed App Store profile it picked here, pinning later releases to it (remove the key to let the build pick again)\n- exportMethod: app-store-connect (default), release-testing, enterprise or debugging\n- buildType: deprecated; 'release' builds the store IPA with a warning\n- cachePattern: array of globs, e.g. [\"*.css\", \"*.js\"]; a string is read as one pattern, not split on commas\n\nVersion comes from package.json; signing files live in credentials/android/ and credentials/ios/, never in config.json.",
488
488
  "layer": "Config",
489
489
  "source": "static",
490
490
  "tags": ["config", "WEBVIEW_CONFIG", "android", "ios", "required", "fields"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "1.2.0-canary.4",
3
+ "version": "1.2.0-canary.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.jsx",
6
6
  "description": "Web framework that provides great performance out of the box",
@@ -5,8 +5,10 @@ import path from "path"
5
5
  import { afterEach, describe, expect, it } from "vitest"
6
6
 
7
7
  import {
8
+ DEVICE_TEAM_HINT,
8
9
  XCODE_TEAM_HINT,
9
10
  chooseXcodeTeam,
11
+ deviceSigningHint,
10
12
  missingProfileError,
11
13
  createProfileScanner,
12
14
  findInstalledProfileIdentity,
@@ -743,4 +745,26 @@ describe("signing failure detail", () => {
743
745
  /^For an App Store release/
744
746
  )
745
747
  })
748
+
749
+ it("gives a device build the config hint: the team, or what to check in the profile", () => {
750
+ const output = 'error: Signing for "iosnativeWebView" requires a development team.'
751
+ const automatic = planIosSigning({ iosConfig: {} })
752
+ const error = signingSetupError(
753
+ "Xcode could not sign the device build",
754
+ { output },
755
+ {
756
+ hint: deviceSigningHint(automatic),
757
+ }
758
+ )
759
+ expect(error.hint).toBe(DEVICE_TEAM_HINT)
760
+ expect(error.hint).toMatch(/WEBVIEW_CONFIG\.ios\.developmentTeam in config\/config\.json/)
761
+ expect(error.hint).not.toMatch(/App Store/)
762
+ const manual = planIosSigning({
763
+ iosConfig: { developmentTeam: "ABCDE12345", provisioningProfile: "Shop Dev" },
764
+ })
765
+ expect(deviceSigningHint(manual)).toBe(
766
+ 'Check that the profile "Shop Dev" set in WEBVIEW_CONFIG.ios.provisioningProfile includes this iPhone, matches the bundle id, and that its certificate is in your keychain.'
767
+ )
768
+ expect(deviceSigningHint(undefined)).toBe(DEVICE_TEAM_HINT)
769
+ })
746
770
  })
@@ -8,6 +8,7 @@ import { fileURLToPath } from "url"
8
8
  import { afterEach, describe, expect, it } from "vitest"
9
9
 
10
10
  import {
11
+ DEBUG_BUILD_SETTINGS,
11
12
  buildExportOptions,
12
13
  findInstalledProfileIdentity,
13
14
  isReleaseBuildType,
@@ -784,6 +785,20 @@ describe("signing failures", () => {
784
785
  })
785
786
  })
786
787
 
788
+ describe("debug xcodebuild settings", () => {
789
+ it("turn off Xcode's debug dylib, so a debug build signs only the app", () => {
790
+ expect(DEBUG_BUILD_SETTINGS).toEqual(["ENABLE_DEBUG_DYLIB=NO"])
791
+ })
792
+
793
+ it("go into the simulator command and the iPhone arguments, not the release archive", () => {
794
+ const build = fs.readFileSync(path.join(here, "../../src/native/buildIos/build.js"), "utf8")
795
+ expect(build).toContain('${DEBUG_BUILD_SETTINGS.join(" ")} \\')
796
+ expect(build).toMatch(/"ONLY_ACTIVE_ARCH=YES",\s+\.\.\.DEBUG_BUILD_SETTINGS,/)
797
+ const index = fs.readFileSync(path.join(here, "../../src/native/buildIos/index.js"), "utf8")
798
+ expect(index).not.toContain("DEBUG_BUILD_SETTINGS")
799
+ })
800
+ })
801
+
787
802
  describe("generateXCConfig signing and version", () => {
788
803
  function phase(iosConfig: Record<string, any>, extra: Record<string, any> = {}) {
789
804
  const projectDir = tempDir()
@@ -56,11 +56,17 @@ describe("getDeviceReachableHost", () => {
56
56
  expect(
57
57
  getDeviceReachableHost({ localHostName: () => "Janes-MacBook-Pro\n", interfaces: {} as any })
58
58
  ).toEqual({
59
- host: "Janes-MacBook-Pro.local",
59
+ host: "janes-macbook-pro.local",
60
60
  source: "bonjour",
61
61
  })
62
62
  })
63
63
 
64
+ it("lowercases the Bonjour name, since WebKit sends the Host header lowercased", () => {
65
+ expect(
66
+ getDeviceReachableHost({ localHostName: () => "G206F1WHVF", interfaces: {} as any })?.host
67
+ ).toBe("g206f1whvf.local")
68
+ })
69
+
64
70
  it("falls back to the LAN IP, then null", () => {
65
71
  const fails = () => {
66
72
  throw new Error("no scutil")
@@ -125,6 +125,14 @@ test("withExtraAllowedHosts: adds the env hosts to the app's list", () => {
125
125
  assert.equal(withExtraAllowedHosts(undefined, ""), undefined)
126
126
  })
127
127
 
128
+ test("withExtraAllowedHosts: lowercases the env hosts and compares them exactly, as Vite does", () => {
129
+ assert.deepEqual(withExtraAllowedHosts(undefined, "G206F1WHVF.local"), ["g206f1whvf.local"])
130
+ assert.deepEqual(withExtraAllowedHosts(["My-Mac.local"], "my-mac.local"), [
131
+ "My-Mac.local",
132
+ "my-mac.local",
133
+ ])
134
+ })
135
+
128
136
  test("buildDevServer: CATALYST_DEV_ALLOWED_HOSTS reaches server.allowedHosts", () => {
129
137
  const before = process.env.CATALYST_DEV_ALLOWED_HOSTS
130
138
  process.env.CATALYST_DEV_ALLOWED_HOSTS = "my-mac.local"