catalyst-core 1.1.0 → 1.2.0-canary.2

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.
Files changed (69) hide show
  1. package/bin/catalyst.js +54 -6
  2. package/changelog.md +27 -1
  3. package/dist/errors/registry.js +306 -18
  4. package/dist/errors-index.json +273 -70
  5. package/dist/native/androidProject/app/build.gradle.kts +53 -79
  6. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +1 -1
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CatalystConstants.kt +3 -1
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +32 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +5 -1
  10. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ConfigMappingTest.kt +25 -0
  11. package/dist/native/androidSetup.js +76 -170
  12. package/dist/native/androidToolchain.js +642 -0
  13. package/dist/native/appIcon.js +100 -0
  14. package/dist/native/buildAndroid/assets.js +29 -5
  15. package/dist/native/buildAndroid/build.js +110 -223
  16. package/dist/native/buildAndroid/config.js +57 -65
  17. package/dist/native/buildAndroid/devices.js +176 -0
  18. package/dist/native/buildAndroid/index.js +271 -46
  19. package/dist/native/buildAndroid/output.js +43 -0
  20. package/dist/native/buildAndroid/signing.js +456 -0
  21. package/dist/native/buildAppAndroid.js +27 -7
  22. package/dist/native/buildAppIos.js +139 -20
  23. package/dist/native/buildIos/assets.js +76 -4
  24. package/dist/native/buildIos/build.js +31 -181
  25. package/dist/native/buildIos/config.js +126 -11
  26. package/dist/native/buildIos/index.js +354 -34
  27. package/dist/native/buildIos/localhostCert.js +87 -0
  28. package/dist/native/buildIos/output.js +28 -0
  29. package/dist/native/buildIos/signing.js +596 -0
  30. package/dist/native/credentials.js +309 -0
  31. package/dist/native/exportCredentials.js +51 -0
  32. package/dist/native/iosToolchain.js +583 -0
  33. package/dist/native/iosnativeWebView/Configurations/Shared.xcconfig +1 -3
  34. package/dist/native/iosnativeWebView/iosnativeWebView/Info-Release.plist +2 -0
  35. package/dist/native/iosnativeWebView/iosnativeWebView/PrivacyInfo.xcprivacy +40 -0
  36. package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebView.entitlements +0 -2
  37. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +4 -4
  38. package/dist/native/localState.js +148 -0
  39. package/dist/native/nativeConfig.js +621 -0
  40. package/dist/native/releaseOutput.js +53 -0
  41. package/dist/native/renameAndroidProject.js +154 -369
  42. package/dist/native/setupEmulatorIos.js +58 -275
  43. package/dist/native/setupServer.js +520 -124
  44. package/dist/native/utils.js +418 -115
  45. package/dist/vite/resolveDevServerConfig.js +21 -0
  46. package/mcp_v2/knowledge-base.json +13 -11
  47. package/mcp_v2/test/config.test.ts +40 -0
  48. package/mcp_v2/tools/build.js +80 -104
  49. package/mcp_v2/tools/config.js +181 -174
  50. package/package.json +1 -1
  51. package/test/cli/nativeFlags.test.ts +97 -0
  52. package/test/native/androidDevices.test.ts +230 -0
  53. package/test/native/androidElfAlignment.test.ts +148 -0
  54. package/test/native/androidSigning.test.ts +646 -0
  55. package/test/native/androidToolchain.test.ts +766 -0
  56. package/test/native/appIcon.test.ts +124 -0
  57. package/test/native/credentials.test.ts +241 -0
  58. package/test/native/interaction.test.ts +179 -0
  59. package/test/native/iosConfigPhase.test.ts +611 -0
  60. package/test/native/iosSigning.test.ts +1060 -0
  61. package/test/native/iosToolchain.test.ts +821 -0
  62. package/test/native/localState.test.ts +80 -0
  63. package/test/native/nativeConfig.test.ts +602 -0
  64. package/test/native/setupServer.test.ts +529 -0
  65. package/test/native/utils.test.ts +102 -127
  66. package/test/vite/resolveDevServerConfig.test.js +25 -0
  67. package/dist/native/androidProject/keystore/Untitled +0 -0
  68. package/dist/native/androidProject/keystore/release-key.jks +0 -0
  69. package/dist/native/iosnativeWebView/iosnativeWebView/localhost.p12 +0 -0
@@ -75,6 +75,46 @@ function generateSwiftProperty(key, value, indent = " ") {
75
75
  return `${indent}public static let ${key} = "${value}"`;
76
76
  }
77
77
 
78
+ // ios.* keys that only steer the build and never reach ConfigConstants.
79
+ // ascApiKey is no longer read (the key lives in credentials/ios/), but an
80
+ // older config that still has it must not ship it in the binary either.
81
+ const BUILD_ONLY_IOS_KEYS = new Set(["ascApiKey", "exportMethod"]);
82
+
83
+ // WEBVIEW_CONFIG keys the generic emitter skips: the platform blocks, and
84
+ // nativeConfig.js RESOLVER_ONLY_KEYS, which only the resolver reads (it turns
85
+ // url, appId and appName into the legacy keys, so ConfigConstants gets one
86
+ // url). Copied, not required: this CJS file is loaded with plain require in
87
+ // tests, which cannot load the ESM resolver.
88
+ const SKIPPED_TOP_LEVEL_KEYS = new Set(["ios", "android", "url", "appId", "appName", "allowPrivateHost"]);
89
+
90
+ /**
91
+ * The xcconfig signing block. Manual (an explicit profile) keeps the
92
+ * settings existing ios.provisioningProfile users had; everything else is
93
+ * Automatic for DEVELOPMENT_TEAM, with no identity or profile pinned, so
94
+ * Xcode (or -allowProvisioningUpdates) picks them.
95
+ *
96
+ * @param {object} signing
97
+ * @param {"manual"|"automatic"|"none"} [signing.style]
98
+ * @param {string} [signing.provisioningProfile]
99
+ * @param {string} [signing.identity] - CODE_SIGN_IDENTITY for device builds
100
+ */
101
+ function xcconfigSigningBlock({
102
+ style,
103
+ provisioningProfile,
104
+ identity
105
+ } = {}) {
106
+ if (style === "manual" && provisioningProfile) {
107
+ return ["CODE_SIGN_STYLE = Manual", "CODE_SIGN_IDENTITY = Apple Development", `CODE_SIGN_IDENTITY[sdk=iphoneos*] = ${identity || "iPhone Developer"}`, "PROVISIONING_PROFILE_SPECIFIER =", `PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = ${provisioningProfile}`].join("\n");
108
+ }
109
+ return "CODE_SIGN_STYLE = Automatic";
110
+ }
111
+
112
+ /** MARKETING_VERSION must be one to three dot-separated integers; "2.5.0-beta.1" becomes "2.5.0". */
113
+ function marketingVersionOf(versionName) {
114
+ const match = /^\d+(?:\.\d+){0,2}/.exec(String(versionName || ""));
115
+ return match ? match[0] : null;
116
+ }
117
+
78
118
  // ─── Module factory ───────────────────────────────────────────────────────────
79
119
 
80
120
  module.exports = function createConfigPhase(ctx) {
@@ -89,6 +129,20 @@ module.exports = function createConfigPhase(ctx) {
89
129
  readPlistObject,
90
130
  writePlistObject
91
131
  } = ctx;
132
+
133
+ /**
134
+ * CFBundleShortVersionString and CFBundleVersion: package.json's version
135
+ * and the build number (nativeConfig.js resolveAppVersion), falling back
136
+ * to ios.version as shipped.
137
+ */
138
+ function appVersion(version) {
139
+ const resolvedVersion = version || ctx.version;
140
+ const configuredVersion = iosConfig.version ? `${iosConfig.version.major || 1}.${iosConfig.version.minor || 0}.${iosConfig.version.patch || 0}` : "1.0.0";
141
+ return {
142
+ marketingVersion: marketingVersionOf(resolvedVersion?.versionName) || configuredVersion,
143
+ buildNumber: resolvedVersion?.buildNumber || iosConfig.version?.buildNumber || 1
144
+ };
145
+ }
92
146
  function mergeIntoTopLevelObject(target, source, fieldName) {
93
147
  for (const [key, value] of Object.entries(source || {})) {
94
148
  target[key] = mergeStructuredValues(target[key], value, `${fieldName}.${key}`);
@@ -151,7 +205,7 @@ public enum ConfigConstants {
151
205
  const addedKeys = new Set();
152
206
  if (WEBVIEW_CONFIG && typeof WEBVIEW_CONFIG === "object") {
153
207
  for (const [key, value] of Object.entries(WEBVIEW_CONFIG)) {
154
- if (key === "ios" || key === "android") continue;
208
+ if (SKIPPED_TOP_LEVEL_KEYS.has(key)) continue;
155
209
  if (key === "notifications") progress.log(`Processing notifications config: ${JSON.stringify(value)}`, "info");
156
210
  configContent += "\n" + generateSwiftProperty(key, value);
157
211
  addedKeys.add(key);
@@ -161,6 +215,9 @@ public enum ConfigConstants {
161
215
  configContent += "\n \n // iOS-specific configuration";
162
216
  for (const [key, value] of Object.entries(iosConfig)) {
163
217
  if (addedKeys.has(key)) continue;
218
+ // Build-machine signing settings, not app config: key paths
219
+ // and IDs have no business in the shipped binary.
220
+ if (BUILD_ONLY_IOS_KEYS.has(key)) continue;
164
221
  configContent += "\n" + generateSwiftProperty(key, value);
165
222
  addedKeys.add(key);
166
223
  }
@@ -255,7 +312,16 @@ public enum ConfigConstants {
255
312
  throw error;
256
313
  }
257
314
  }
258
- async function generateXCConfig() {
315
+
316
+ /**
317
+ * @param {object} [options]
318
+ * @param {object} [options.signing] - from planIosSigning (plus the CI profile and identity); defaults to ios.provisioningProfile or Automatic
319
+ * @param {{ versionName?: string, buildNumber?: number }} [options.version] - from resolveAppVersion
320
+ */
321
+ async function generateXCConfig({
322
+ signing,
323
+ version
324
+ } = {}) {
259
325
  try {
260
326
  const xconfigPath = path.join(PROJECT_DIR, "Configurations", "Shared.xcconfig"); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
261
327
  const configDir = path.dirname(xconfigPath);
@@ -264,12 +330,17 @@ public enum ConfigConstants {
264
330
  });
265
331
  const bundleId = iosConfig.bundleIdentifier || iosConfig.appBundleId || "com.catalyst.framework.app";
266
332
  const appName = iosConfig.appName || "Catalyst App";
267
- const teamId = iosConfig.developmentTeam || "";
333
+ const signingPlan = signing || {
334
+ style: iosConfig.provisioningProfile ? "manual" : "automatic",
335
+ provisioningProfile: iosConfig.provisioningProfile
336
+ };
337
+ const teamId = signingPlan.teamId || iosConfig.developmentTeam || "";
268
338
  const swiftVersion = iosConfig.deployment?.swiftVersion || "5.9";
269
339
  const deploymentTarget = iosConfig.deployment?.target || "17.0";
270
- const marketingVersion = iosConfig.version ? `${iosConfig.version.major || 1}.${iosConfig.version.minor || 0}.${iosConfig.version.patch || 0}` : "1.0.0";
271
- const buildNumber = iosConfig.version?.buildNumber || 1;
272
- const provisioningProfile = iosConfig.provisioningProfile || "";
340
+ const {
341
+ marketingVersion,
342
+ buildNumber
343
+ } = appVersion(version);
273
344
  const xconfigContent = `// This file is auto-generated by buildAppIos.js. Do not edit.
274
345
  //
275
346
  // Shared configuration for both Debug and Release builds
@@ -301,11 +372,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
301
372
  ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor
302
373
 
303
374
  // Code signing
304
- CODE_SIGN_IDENTITY = Apple Development
305
- CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
306
- CODE_SIGN_STYLE = Manual
307
- PROVISIONING_PROFILE_SPECIFIER =
308
- PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = ${provisioningProfile}
375
+ ${xcconfigSigningBlock(signingPlan)}
309
376
 
310
377
  // Module settings
311
378
  CLANG_ENABLE_MODULES = NO
@@ -357,6 +424,10 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPo
357
424
  throw new Error("Google Sign-In enabled but no valid clientId found");
358
425
  }
359
426
  const plistTargets = [infoPlistPath, infoReleasePlistPath];
427
+ const {
428
+ marketingVersion,
429
+ buildNumber
430
+ } = appVersion();
360
431
  const pluginUrlSchemes = pluginComposition.urlSchemes || [];
361
432
  const pluginQuerySchemes = pluginComposition.querySchemes || [];
362
433
  const pluginInfoPlist = pluginComposition.infoPlist || {};
@@ -375,16 +446,60 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPo
375
446
  }
376
447
  mergeUrlSchemes(plistObject, pluginUrlSchemes);
377
448
  mergeQuerySchemes(plistObject, pluginQuerySchemes);
449
+ // Literal values, so the plist says what the IPA carries.
450
+ plistObject.CFBundleShortVersionString = String(marketingVersion);
451
+ plistObject.CFBundleVersion = String(buildNumber);
452
+ if (plistPath === infoReleasePlistPath) {
453
+ // Without it App Store Connect asks the export compliance
454
+ // questions for every upload. A plugin that ships non-exempt
455
+ // encryption sets it to true through its infoPlist.
456
+ if (plistObject.ITSAppUsesNonExemptEncryption === undefined) {
457
+ plistObject.ITSAppUsesNonExemptEncryption = false;
458
+ }
459
+ } else {
460
+ applyDebugTransportSecurity(plistObject);
461
+ }
378
462
  writePlistObject(plistPath, plistObject);
379
463
  });
380
464
  }
465
+
466
+ /**
467
+ * Info.plist is the Debug configuration's plist. When a debug build loads
468
+ * the dev server (ctx.localNetworking), NSAllowsLocalNetworking lets it
469
+ * reach `<mac>.local`, a LAN IP or localhost over plain HTTP. On iOS 10
470
+ * and later its presence makes the OS ignore NSAllowsArbitraryLoads, so
471
+ * it is added only when the plist does not already allow arbitrary loads
472
+ * (the template's debug plist does, and that already covers local hosts).
473
+ * Release builds use Info-Release.plist and never get it.
474
+ */
475
+ function applyDebugTransportSecurity(plistObject) {
476
+ const ats = isPlainObject(plistObject.NSAppTransportSecurity) ? {
477
+ ...plistObject.NSAppTransportSecurity
478
+ } : {};
479
+ if (ctx.localNetworking && ats.NSAllowsArbitraryLoads !== true) ats.NSAllowsLocalNetworking = true;else delete ats.NSAllowsLocalNetworking;
480
+ plistObject.NSAppTransportSecurity = ats;
481
+ }
482
+
483
+ /**
484
+ * The app's entitlements: the template, the plugins' additions, and
485
+ * aps-environment only when notifications are on. A push entitlement
486
+ * nobody uses makes App Store Connect warn and needs a push-enabled
487
+ * profile for nothing. The value follows the signing profile (see
488
+ * planIosSigning): "development", or "production" for a manually signed
489
+ * release.
490
+ */
381
491
  async function updateEntitlements(pluginComposition = {}) {
382
492
  const PROJECT_NAME = ctx.PROJECT_NAME;
383
493
  const entitlementsPath = path.join(PROJECT_DIR, PROJECT_NAME, `${PROJECT_NAME}.entitlements`); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
384
494
  if (!fs.existsSync(entitlementsPath)) return;
385
495
  restoreManagedFileFromBaseline(entitlementsPath);
386
496
  const entitlementsObject = readPlistObject(entitlementsPath);
497
+ // A baseline saved by an older Catalyst still has the key.
498
+ delete entitlementsObject["aps-environment"];
387
499
  mergeIntoTopLevelObject(entitlementsObject, pluginComposition.entitlements || {}, "ios.entitlements");
500
+ if (WEBVIEW_CONFIG.notifications?.enabled) {
501
+ entitlementsObject["aps-environment"] = ctx.apsEnvironment || "development";
502
+ }
388
503
  writePlistObject(entitlementsPath, entitlementsObject);
389
504
  }
390
505
  return {