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
@@ -1,74 +1,4 @@
1
1
  {
2
- "CCA-000": {
3
- "category": "CCA",
4
- "message": "An upstream command failed",
5
- "details": "This wraps an error from npm, tar, or another upstream tool invoked by create-catalyst-app. See the printed upstream message for the actual cause.",
6
- "suggestedAction": "Read the upstream error message printed above and fix the underlying issue.",
7
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-000.md"
8
- },
9
- "CCA-001": {
10
- "category": "CCA",
11
- "message": "Invalid project name",
12
- "details": "The project name is not a valid npm package name.",
13
- "suggestedAction": "Choose a project name that is a valid npm package name and try again.",
14
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-001.md"
15
- },
16
- "CCA-002": {
17
- "category": "CCA",
18
- "message": "Target directory already exists",
19
- "details": "A file or directory with the project name already exists in the current directory.",
20
- "suggestedAction": "Choose a different project name, or remove/rename the existing directory.",
21
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-002.md"
22
- },
23
- "CCA-003": {
24
- "category": "CCA",
25
- "message": "Invalid language option",
26
- "details": "The --lang option must be \"js\" or \"ts\".",
27
- "suggestedAction": "Pass --lang js or --lang ts.",
28
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-003.md"
29
- },
30
- "CCA-004": {
31
- "category": "CCA",
32
- "message": "Invalid state management option",
33
- "details": "The --state-management option must be \"rtk\", \"redux\", or \"none\".",
34
- "suggestedAction": "Pass a supported --state-management value.",
35
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-004.md"
36
- },
37
- "CCA-005": {
38
- "category": "CCA",
39
- "message": "Invalid --yes option",
40
- "details": "The -y/--yes flag does not accept a non-boolean value.",
41
- "suggestedAction": "Use -y or --yes with no value to accept defaults.",
42
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-005.md"
43
- },
44
- "CCA-006": {
45
- "category": "CCA",
46
- "message": "Failed to pack create-catalyst-app",
47
- "details": "npm pack could not produce the tarball used to scaffold the new project.",
48
- "suggestedAction": "Check your npm registry access/network connection and try again.",
49
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-006.md"
50
- },
51
- "CCA-007": {
52
- "category": "CCA",
53
- "message": "Failed to extract template files",
54
- "details": "The packed tarball could not be extracted into the new project directory.",
55
- "suggestedAction": "Ensure the target directory is writable and try again.",
56
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-007.md"
57
- },
58
- "CCA-008": {
59
- "category": "CCA",
60
- "message": "MCP server setup failed",
61
- "details": "Downloading, installing, or configuring the MCP server failed.",
62
- "suggestedAction": "Check your network connection and re-run `catalyst-mcp` inside the project.",
63
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-008.md"
64
- },
65
- "CCA-009": {
66
- "category": "CCA",
67
- "message": ".gitignore already exists",
68
- "details": "The scaffolded project already contains a .gitignore file.",
69
- "suggestedAction": "Remove or rename the existing .gitignore before running again, or ignore this warning.",
70
- "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-009.md"
71
- },
72
2
  "PREFLIGHT-001": {
73
3
  "category": "PREFLIGHT",
74
4
  "message": "config not found in config folder",
@@ -258,6 +188,83 @@
258
188
  "suggestedAction": "Confirm a full Xcode.app (not just Command Line Tools) is installed and selected via xcode-select -p, then reopen it once from Spotlight so macOS registers its app bundle.",
259
189
  "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-001.md"
260
190
  },
191
+ "IOS-002": {
192
+ "category": "IOS",
193
+ "message": "Full Xcode is not installed or selected",
194
+ "details": "setupEmulator:ios and buildApp:ios need Xcode.app, not only the Command Line Tools. xcode-select -p does not point inside an Xcode app bundle, so there is no simulator, iOS SDK or app-building xcodebuild.",
195
+ "suggestedAction": "Install Xcode from the Mac App Store, then run sudo xcode-select -s /Applications/Xcode.app/Contents/Developer.",
196
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-002.md"
197
+ },
198
+ "IOS-003": {
199
+ "category": "IOS",
200
+ "message": "Xcode setup is not finished",
201
+ "details": "The Xcode licence has not been accepted or its first-launch components are not installed, so xcodebuild and simctl refuse to run. Both steps need an administrator password: in a terminal they run after a yes, and without one --accept-licenses (the licence) and --yes (first launch) run them with sudo -n, which works only where sudo needs no password.",
202
+ "suggestedAction": "Run sudo xcodebuild -license accept and sudo xcodebuild -runFirstLaunch, then retry.",
203
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-003.md"
204
+ },
205
+ "IOS-004": {
206
+ "category": "IOS",
207
+ "message": "No iOS simulator runtime installed",
208
+ "details": "Xcode has no available iOS simulator runtime, so there is no simulator to create or boot. The runtime is a download of several gigabytes and is only fetched after a yes in an interactive terminal or with --yes.",
209
+ "suggestedAction": "Run catalyst setupEmulator:ios in a terminal or with --yes, or run xcodebuild -downloadPlatform iOS yourself.",
210
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-004.md"
211
+ },
212
+ "IOS-005": {
213
+ "category": "IOS",
214
+ "message": "App icon is not usable",
215
+ "details": "public/icon.png becomes the app's single 1024x1024 universal AppIcon, so it must be a PNG file, square, and at least 1024 by 1024 pixels.",
216
+ "suggestedAction": "Replace public/icon.png with a square PNG of at least 1024x1024 pixels, or add the sized icons in public/ios/appIcons.",
217
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-005.md"
218
+ },
219
+ "IOS-006": {
220
+ "category": "IOS",
221
+ "message": "App icon has transparency",
222
+ "details": "App Store Connect rejects app icons with an alpha channel (ITMS-90717). public/icon.png has an alpha channel or a transparency chunk, which fails an iOS release build. Debug builds only warn.",
223
+ "suggestedAction": "Export public/icon.png without an alpha channel (flatten it onto a solid background), then rebuild.",
224
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-006.md"
225
+ },
226
+ "IOS-010": {
227
+ "category": "IOS",
228
+ "message": "iOS code signing is not set up",
229
+ "details": "A device or release build needs a signing identity and provisioning profile. Automatic signing needs WEBVIEW_CONFIG.ios.developmentTeam plus either an App Store Connect API key in credentials/ios/ (cloud signing) or an Apple account signed in to Xcode; manual signing needs a .p12 and its provisioning profile in credentials/ios/, or an installed ios.provisioningProfile. xcodebuild reported a signing failure, or a release build has no team to sign for.",
230
+ "suggestedAction": "For cloud signing, put an App Store Connect API key (its AuthKey_*.p8 file, with an ascIssuerId line in signing.properties) in credentials/ios/ and set WEBVIEW_CONFIG.ios.developmentTeam; for local signing, put a distribution .p12 and its .mobileprovision there instead. In CI, set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
231
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-010.md"
232
+ },
233
+ "IOS-011": {
234
+ "category": "IOS",
235
+ "message": "App Store Connect API key settings are incomplete or wrong",
236
+ "details": "Cloud signing uses an App Store Connect API key from credentials/ios/ (in CI, unpacked from CATALYST_IOS_CREDENTIALS), which takes three values: the AuthKey_*.p8 file, its key ID and the issuer ID. Some of them are there but not all, there is more than one .p8 file, or the key ID is not letters and digits.",
237
+ "suggestedAction": "Put the AuthKey_*.p8 file in credentials/ios/, and an ascIssuerId line (plus ascKeyId when the file is renamed) in credentials/ios/signing.properties. In CI, run catalyst credentials:export ios again and paste the value into CATALYST_IOS_CREDENTIALS.",
238
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-011.md"
239
+ },
240
+ "IOS-012": {
241
+ "category": "IOS",
242
+ "message": "Manual signing inputs are incomplete or wrong",
243
+ "details": "Local signing imports the .p12 from credentials/ios/ into a temporary keychain and installs its provisioning profile for the build. Only one of the .p12 and the .mobileprovision is there, there are several of either, the p12Password in signing.properties is wrong, the p12 has no code signing identity, or the profile is unreadable, expired or for another bundle ID.",
244
+ "suggestedAction": "Keep one distribution .p12 and the .mobileprovision for WEBVIEW_CONFIG.appId in credentials/ios/, with a p12Password line in credentials/ios/signing.properties, and check that they belong together. In CI, run catalyst credentials:export ios again and paste the value into CATALYST_IOS_CREDENTIALS.",
245
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-012.md"
246
+ },
247
+ "IOS-013": {
248
+ "category": "IOS",
249
+ "message": "Unknown iOS export method",
250
+ "details": "WEBVIEW_CONFIG.ios.exportMethod picks how the release archive is exported to an IPA. Xcode 26 accepts app-store-connect, release-testing, enterprise and debugging; the older names app-store, ad-hoc and development are mapped to them with a warning.",
251
+ "suggestedAction": "Set WEBVIEW_CONFIG.ios.exportMethod to app-store-connect (the default), release-testing, enterprise or debugging.",
252
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-013.md"
253
+ },
254
+ "IOS-014": {
255
+ "category": "IOS",
256
+ "message": "No connected iPhone found",
257
+ "details": "buildApp:ios --device builds for a connected iPhone, and xcrun devicectl lists none that is paired and reachable. An iPhone shows up once it is unlocked, trusts this Mac and has Developer Mode on.",
258
+ "suggestedAction": "Connect the iPhone with a cable, unlock it, accept Trust This Computer and turn on Developer Mode (Settings > Privacy & Security), then retry; or leave out --device to use the simulator.",
259
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-014.md"
260
+ },
261
+ "IOS-015": {
262
+ "category": "IOS",
263
+ "message": "No address the iPhone can reach this Mac on",
264
+ "details": "A debug build on a physical iPhone loads the dev server from this Mac over the local network, at the Mac's Bonjour name (its Local Hostname plus .local) or its LAN IP address. This Mac has neither a Local Hostname nor a private IPv4 address on an active interface.",
265
+ "suggestedAction": "Join the Mac and the iPhone to the same Wi-Fi network and check the Local Hostname in System Settings > General > Sharing, then retry.",
266
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-015.md"
267
+ },
261
268
  "ANDROID-000": {
262
269
  "category": "ANDROID",
263
270
  "message": "Android build failed in an upstream toolchain step",
@@ -272,6 +279,132 @@
272
279
  "suggestedAction": "Try booting the AVD manually first (emulator -avd <name>) to see the actual startup error, check available disk space/RAM, or increase the emulator's cold-boot allowance if the device is unusually slow.",
273
280
  "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-001.md"
274
281
  },
282
+ "ANDROID-005": {
283
+ "category": "ANDROID",
284
+ "message": "No upload key for the release build",
285
+ "details": "A release build signs the app bundle with the app's upload key. None was found in credentials/android/ or in the CATALYST_ANDROID_CREDENTIALS secret. A new key is only created after a yes in an interactive terminal, and never for an app that is already on Google Play.",
286
+ "suggestedAction": "Run catalyst buildApp:android --release once in a terminal to create credentials/android/upload.jks, then back it up with catalyst credentials:export android. In CI, set the CATALYST_ANDROID_CREDENTIALS secret to the output of catalyst credentials:export android.",
287
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-005.md"
288
+ },
289
+ "ANDROID-006": {
290
+ "category": "ANDROID",
291
+ "message": "Upload key settings are incomplete or wrong",
292
+ "details": "An upload key was found but cannot be used: the keystore or its keystore.properties is missing, no store password was given, or keytool rejected the password or alias.",
293
+ "suggestedAction": "Check credentials/android/keystore.properties against credentials/android/upload.jks. In CI, run catalyst credentials:export android again and update the CATALYST_ANDROID_CREDENTIALS secret.",
294
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-006.md"
295
+ },
296
+ "ANDROID-007": {
297
+ "category": "ANDROID",
298
+ "message": "No JDK 17 to 23 found",
299
+ "details": "Gradle 8.13 and the Android SDK tools need JDK 17 to 23. None was found in JAVA_HOME, /usr/libexec/java_home or Android Studio's bundled runtime (a JDK 24 or newer is skipped), and none could be installed: this is not macOS, Homebrew is missing, or the install was declined.",
300
+ "suggestedAction": "Install JDK 17 or 21 (macOS: brew install --cask zulu@17; Linux: your distribution's openjdk-17-jdk package) and set JAVA_HOME, or run catalyst buildApp:android in a terminal, or with --yes, to install it.",
301
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-007.md"
302
+ },
303
+ "ANDROID-008": {
304
+ "category": "ANDROID",
305
+ "message": "Android SDK setup was declined",
306
+ "details": "buildApp:android or setupEmulator:android found Android SDK pieces to download (command-line tools, packages or a system image), an emulator to create, or SDK licences to accept, and the answer was no. Without them the app cannot be built or run.",
307
+ "suggestedAction": "Run the command again and answer yes, or pass --yes to allow the downloads and --accept-licenses to accept the Android SDK licences.",
308
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-008.md"
309
+ },
310
+ "ANDROID-009": {
311
+ "category": "ANDROID",
312
+ "message": "Could not install the Android command-line tools",
313
+ "details": "The pinned Android command-line tools archive could not be downloaded, did not match its published SHA-256, or could not be unpacked into <sdk>/cmdline-tools/latest.",
314
+ "suggestedAction": "Check your network or proxy and retry, or download the command-line tools from https://developer.android.com/studio#command-tools and unzip them so that <sdk>/cmdline-tools/latest/bin/sdkmanager exists.",
315
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-009.md"
316
+ },
317
+ "ANDROID-010": {
318
+ "category": "ANDROID",
319
+ "message": "App icon is not usable",
320
+ "details": "public/icon.png becomes the Android launcher icon when public/android/appIcons has no per-density icons, so it must be a PNG file, square, and at least 1024 by 1024 pixels.",
321
+ "suggestedAction": "Replace public/icon.png with a square PNG of at least 1024x1024 pixels, or add per-density icons in public/android/appIcons.",
322
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-010.md"
323
+ },
324
+ "ANDROID-011": {
325
+ "category": "ANDROID",
326
+ "message": "No Android phone connected",
327
+ "details": "buildApp:android --device installs the app on a phone that adb can reach, but adb lists none that is ready: no phone is connected, USB debugging is off, the phone has not allowed this computer yet (adb shows it as unauthorized), or ANDROID_SERIAL names a phone that is not connected.",
328
+ "suggestedAction": "Connect the phone, turn on USB debugging in Developer options, accept the Allow USB debugging prompt on the phone and check that adb devices lists it. Or drop --device to use the emulator.",
329
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/ANDROID/ANDROID-011.md"
330
+ },
331
+ "NATIVE-001": {
332
+ "category": "NATIVE",
333
+ "message": "Release build points at a local or private host",
334
+ "details": "A release build (buildApp:android --release or buildApp:ios --release) would open a start URL on localhost, a loopback, private (RFC 1918), link-local or .local address, this machine's own address, or a reserved placeholder domain such as example.com. Users' devices cannot reach it, so the store build would open on an error page. The start URL comes from --url, then WEBVIEW_CONFIG.url, then the older LOCAL_IP, port and useHttps keys.",
335
+ "suggestedAction": "Set WEBVIEW_CONFIG.url to your production origin (e.g. https://shop.acme.com) or pass --url, or set WEBVIEW_CONFIG.allowPrivateHost to true for an internal app on a private network.",
336
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-001.md"
337
+ },
338
+ "NATIVE-002": {
339
+ "category": "NATIVE",
340
+ "message": "Invalid build number",
341
+ "details": "The build number becomes the Android versionCode and the iOS CFBundleVersion. It is the whole minutes since 2024-01-01T00:00:00Z unless CATALYST_BUILD_NUMBER is set, and CATALYST_BUILD_NUMBER is not a whole number from 1 to 2100000000 (the largest value Google Play accepts).",
342
+ "suggestedAction": "Unset CATALYST_BUILD_NUMBER to use the automatic build number, or set it to a whole number from 1 to 2100000000 that is higher than the last one you uploaded.",
343
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-002.md"
344
+ },
345
+ "NATIVE-003": {
346
+ "category": "NATIVE",
347
+ "message": "Release build uses a placeholder app id",
348
+ "details": "The app id becomes the Android application id and the iOS bundle id, and a store listing is tied to it for good. It is a com.example.* id or one of the template defaults (io.yourname.androidproject, com.catalyst.framework.app, com.debug.webview), or none is set, so the template's default would ship.",
349
+ "suggestedAction": "Set WEBVIEW_CONFIG.appId in config/config.json to your own reverse-DNS id, e.g. com.acme.shop.",
350
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-003.md"
351
+ },
352
+ "NATIVE-004": {
353
+ "category": "NATIVE",
354
+ "message": "Invalid production URL",
355
+ "details": "WEBVIEW_CONFIG.url and --url take an origin: an http:// or https:// scheme, a host and an optional port, with no path, query, fragment or user name. HTTPS must use port 443, because the iOS app always loads HTTPS on 443.",
356
+ "suggestedAction": "Use an origin such as https://shop.acme.com, without a path or a port other than 443.",
357
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-004.md"
358
+ },
359
+ "NATIVE-005": {
360
+ "category": "NATIVE",
361
+ "message": "A step needs your answer and there is no terminal to ask on",
362
+ "details": "Native commands only ask questions when stdin and stdout are terminals and CI is not set. This step needs an answer: --yes confirms installs and downloads, --accept-licenses accepts licences, and creating a signing key always needs a person at a terminal.",
363
+ "suggestedAction": "Pass the flag named in the message, or run the command in an interactive terminal.",
364
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-005.md"
365
+ },
366
+ "NATIVE-006": {
367
+ "category": "NATIVE",
368
+ "message": "The dev server did not answer",
369
+ "details": "A debug build loads the app from the dev server. The CLI started it with catalyst start and waited for it to answer, but it exited, did not answer in time, or is running where the device cannot reach it.",
370
+ "suggestedAction": "Run npm start on its own to see why the server does not start, then run the build again.",
371
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-006.md"
372
+ },
373
+ "NATIVE-007": {
374
+ "category": "NATIVE",
375
+ "message": "The credentials secret could not be unpacked",
376
+ "details": "CATALYST_ANDROID_CREDENTIALS and CATALYST_IOS_CREDENTIALS hold a base64 gzipped tar of credentials/<platform>/, as printed by catalyst credentials:export. The value is not one, is cut short, or holds links or paths outside the folder.",
377
+ "suggestedAction": "Run catalyst credentials:export <android|ios> where credentials/<platform>/ exists and paste the whole value into the secret again.",
378
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-007.md"
379
+ },
380
+ "NATIVE-008": {
381
+ "category": "NATIVE",
382
+ "message": "No credentials to export",
383
+ "details": "catalyst credentials:export packs credentials/<platform>/ in the app root, and that folder is missing or empty. A first release build creates it (the Android upload key), or you add the iOS signing files to it.",
384
+ "suggestedAction": "Run the command from the app root after a release build has created credentials/<platform>/.",
385
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-008.md"
386
+ },
387
+ "NATIVE-009": {
388
+ "category": "NATIVE",
389
+ "message": "Credentials are too large for one secret",
390
+ "details": "A GitHub Actions secret holds at most 48 KB, and credentials/<platform>/ packs to more than that.",
391
+ "suggestedAction": "Remove files the build does not need from credentials/<platform>/ (only signing keys, profiles and their settings belong there), then export again.",
392
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-009.md"
393
+ },
394
+ "NATIVE-010": {
395
+ "category": "NATIVE",
396
+ "message": "Invalid app id",
397
+ "details": "The app id (WEBVIEW_CONFIG.appId, android.packageName or ios.bundleIdentifier / appBundleId) is not a valid id for the platform. Android needs a Java package name: two or more dot-separated parts, each starting with a letter and using only letters, digits and _, none a Java keyword. iOS needs two or more parts of letters, digits and -.",
398
+ "suggestedAction": "Set WEBVIEW_CONFIG.appId in config/config.json to a reverse-DNS id such as com.acme.shop: letters and digits only, each part starting with a letter.",
399
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-010.md"
400
+ },
401
+ "NATIVE-011": {
402
+ "category": "NATIVE",
403
+ "message": "A release with this version and build number already exists",
404
+ "details": "build-output/<platform>/<version>-<build>/ already holds a store artifact. The build number counts whole minutes, so two releases in the same minute get the same number, and a store refuses a build number it has already seen.",
405
+ "suggestedAction": "Wait a minute and run the release again, or set CATALYST_BUILD_NUMBER to a higher number. To rebuild the same build on purpose, delete that folder first.",
406
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/NATIVE/NATIVE-011.md"
407
+ },
275
408
  "RUNTIME-NATIVE-001": {
276
409
  "category": "RUNTIME-NATIVE",
277
410
  "message": "Permission denied",
@@ -523,5 +656,75 @@
523
656
  "details": "An unhandled error reached the top level of the SSR request handler. This may originate from app code, a third-party dependency, or catalyst-core itself — see the cause above for the actual error.",
524
657
  "suggestedAction": "See the cause above for the underlying error.",
525
658
  "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/RUNTIME-WEB/RUNTIME-WEB-004.md"
659
+ },
660
+ "CCA-000": {
661
+ "category": "CCA",
662
+ "message": "An upstream command failed",
663
+ "details": "This wraps an error from npm, tar, or another upstream tool invoked by create-catalyst-app. See the printed upstream message for the actual cause.",
664
+ "suggestedAction": "Read the upstream error message printed above and fix the underlying issue.",
665
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-000.md"
666
+ },
667
+ "CCA-001": {
668
+ "category": "CCA",
669
+ "message": "Invalid project name",
670
+ "details": "The project name is not a valid npm package name.",
671
+ "suggestedAction": "Choose a project name that is a valid npm package name and try again.",
672
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-001.md"
673
+ },
674
+ "CCA-002": {
675
+ "category": "CCA",
676
+ "message": "Target directory already exists",
677
+ "details": "A file or directory with the project name already exists in the current directory.",
678
+ "suggestedAction": "Choose a different project name, or remove/rename the existing directory.",
679
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-002.md"
680
+ },
681
+ "CCA-003": {
682
+ "category": "CCA",
683
+ "message": "Invalid language option",
684
+ "details": "The --lang option must be \"js\" or \"ts\".",
685
+ "suggestedAction": "Pass --lang js or --lang ts.",
686
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-003.md"
687
+ },
688
+ "CCA-004": {
689
+ "category": "CCA",
690
+ "message": "Invalid state management option",
691
+ "details": "The --state-management option must be \"rtk\", \"redux\", or \"none\".",
692
+ "suggestedAction": "Pass a supported --state-management value.",
693
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-004.md"
694
+ },
695
+ "CCA-005": {
696
+ "category": "CCA",
697
+ "message": "Invalid --yes option",
698
+ "details": "The -y/--yes flag does not accept a non-boolean value.",
699
+ "suggestedAction": "Use -y or --yes with no value to accept defaults.",
700
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-005.md"
701
+ },
702
+ "CCA-006": {
703
+ "category": "CCA",
704
+ "message": "Failed to pack create-catalyst-app",
705
+ "details": "npm pack could not produce the tarball used to scaffold the new project.",
706
+ "suggestedAction": "Check your npm registry access/network connection and try again.",
707
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-006.md"
708
+ },
709
+ "CCA-007": {
710
+ "category": "CCA",
711
+ "message": "Failed to extract template files",
712
+ "details": "The packed tarball could not be extracted into the new project directory.",
713
+ "suggestedAction": "Ensure the target directory is writable and try again.",
714
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-007.md"
715
+ },
716
+ "CCA-008": {
717
+ "category": "CCA",
718
+ "message": "MCP server setup failed",
719
+ "details": "Downloading, installing, or configuring the MCP server failed.",
720
+ "suggestedAction": "Check your network connection and re-run `catalyst-mcp` inside the project.",
721
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-008.md"
722
+ },
723
+ "CCA-009": {
724
+ "category": "CCA",
725
+ "message": ".gitignore already exists",
726
+ "details": "The scaffolded project already contains a .gitignore file.",
727
+ "suggestedAction": "Remove or rename the existing .gitignore before running again, or ignore this warning.",
728
+ "docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/CCA/CCA-009.md"
526
729
  }
527
730
  }
@@ -11,9 +11,19 @@ buildscript {
11
11
  }
12
12
 
13
13
  val configPath = findProperty("configPath") as? String
14
- val keystorePassword: String? by project.properties // Changed from keyStorePassword to keystorePassword
15
- val keyAlias: String? by project.properties
16
- val keyPassword: String? by project.properties
14
+
15
+ // Set by the Catalyst CLI (buildApp:android). -PcatalystBuildType decides the
16
+ // build type for generateWebViewConfig; the task-name check is only the
17
+ // fallback for a hand-run `./gradlew bundleRelease`. Version, application id
18
+ // and signing values arrive as ORG_GRADLE_PROJECT_* environment variables so
19
+ // that passwords never appear on a command line.
20
+ val isCatalystReleaseBuild: Boolean = (findProperty("catalystBuildType") as? String)?.let { it == "release" }
21
+ ?: gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }
22
+ val catalystApplicationId = (findProperty("catalystApplicationId") as? String)?.takeIf { it.isNotBlank() }
23
+ ?: "io.yourname.androidproject"
24
+ val catalystVersionCode = (findProperty("catalystVersionCode") as? String)?.toIntOrNull() ?: 1
25
+ val catalystVersionName = (findProperty("catalystVersionName") as? String)?.takeIf { it.isNotBlank() } ?: "1.1"
26
+ val catalystStoreFile = findProperty("catalystStoreFile") as? String
17
27
 
18
28
  fun isAllowBackupEnabled(): Boolean {
19
29
  return try {
@@ -96,32 +106,28 @@ jacoco {
96
106
 
97
107
  android {
98
108
  namespace = "io.yourname.androidproject"
99
- compileSdk = 34
109
+ compileSdk = 36
100
110
 
101
111
  defaultConfig {
102
- applicationId = "io.yourname.androidproject"
112
+ applicationId = catalystApplicationId
103
113
  minSdk = 24
104
- targetSdk = 34
105
- versionCode = 1
106
- versionName = "1.1"
114
+ targetSdk = 36
115
+ versionCode = catalystVersionCode
116
+ versionName = catalystVersionName
107
117
  testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
108
118
  buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
109
119
  manifestPlaceholders["allowBackup"] = isAllowBackupEnabled()
110
120
  }
111
121
 
112
- // Add signing configuration for app bundle
122
+ // The app's upload key, passed in by the Catalyst CLI. Without it a
123
+ // release build is unsigned.
113
124
  signingConfigs {
114
- create("release") {
115
- // Make sure the keystore file exists before referencing it
116
- val keystoreFile = file("../keystore/release-key.jks")
117
- if (keystoreFile.exists()) {
118
- storeFile = file("../keystore/release-key.jks")
119
- storePassword = "test@123"
120
- keyAlias = "release"
121
- keyPassword = "test@123"
122
- } else {
123
- // Log a warning if the keystore doesn't exist yet
124
- logger.warn("Keystore file not found at ${keystoreFile.absolutePath}. Run the generateKeystore task first.")
125
+ if (catalystStoreFile != null) {
126
+ create("release") {
127
+ storeFile = file(catalystStoreFile)
128
+ storePassword = findProperty("catalystStorePassword") as? String
129
+ keyAlias = findProperty("catalystKeyAlias") as? String
130
+ keyPassword = findProperty("catalystKeyPassword") as? String
125
131
  }
126
132
  }
127
133
  }
@@ -145,10 +151,7 @@ android {
145
151
  buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "false")
146
152
  buildConfigField("String", "LOCAL_IP", "\"127.0.0.1\"")
147
153
 
148
- // Only apply signing config if the keystore exists
149
- if (file("../keystore/release-key.jks").exists()) {
150
- signingConfig = signingConfigs.getByName("release")
151
- }
154
+ signingConfigs.findByName("release")?.let { signingConfig = it }
152
155
  }
153
156
  }
154
157
 
@@ -289,19 +292,27 @@ dependencies {
289
292
  }
290
293
 
291
294
  // Security detection dependencies
292
- implementation("com.scottyab:rootbeer-lib:0.1.1") // Root detection
295
+ // 0.1.2 ships libtoolChecker.so 16 KB aligned on every ABI (0.1.1 did on
296
+ // 64-bit only). #302; https://github.com/scottyab/rootbeer/releases
297
+ implementation("com.scottyab:rootbeer-lib:0.1.2") // Root detection
293
298
  implementation("com.google.android.play:integrity:1.3.0") // Play Integrity API
294
299
 
295
300
  // SLF4J simple logger for Ktor (optional, can be excluded if needed)
296
301
  implementation("org.slf4j:slf4j-simple:2.0.9")
297
302
 
298
- // CameraX
299
- implementation("androidx.camera:camera-core:1.3.4")
300
- implementation("androidx.camera:camera-camera2:1.3.4")
301
- implementation("androidx.camera:camera-lifecycle:1.3.4")
302
- implementation("androidx.camera:camera-view:1.3.4")
303
-
304
- // ML Kit Barcode Scanning
303
+ // CameraX. 1.3.4's libimage_processing_util_jni.so has 4 KB LOAD
304
+ // segments; 1.4.0-alpha05 and later ship it 16 KB aligned (#302, checked
305
+ // in the AARs from Google Maven). 1.4.2 is the newest line that needs
306
+ // neither minSdk 23 nor Kotlin 2 (1.5.0 raised both; see
307
+ // https://developer.android.com/jetpack/androidx/releases/camera).
308
+ implementation("androidx.camera:camera-core:1.4.2")
309
+ implementation("androidx.camera:camera-camera2:1.4.2")
310
+ implementation("androidx.camera:camera-lifecycle:1.4.2")
311
+ implementation("androidx.camera:camera-view:1.4.2")
312
+
313
+ // ML Kit Barcode Scanning. 17.3.0 is 16 KB aligned on 64-bit ABIs, which
314
+ // is what Play requires: https://developers.google.com/ml-kit/release-notes
315
+ // (August 7, 2024).
305
316
  implementation("com.google.mlkit:barcode-scanning:17.3.0")
306
317
 
307
318
  // Notification dependencies - conditional based on config
@@ -483,11 +494,16 @@ tasks.register("generateWebViewConfig") {
483
494
 
484
495
  val properties = Properties()
485
496
 
486
- // Set different IP based on build type
487
- if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
488
- properties.setProperty("LOCAL_IP", "127.0.0.1") // Production
497
+ // Set different IP based on build type. A release takes LOCAL_IP from
498
+ // the config (the CLI resolves WEBVIEW_CONFIG.url into it first) and
499
+ // honours android.buildOptimisation instead of forcing it on.
500
+ if (isCatalystReleaseBuild) {
501
+ properties.setProperty("LOCAL_IP", "127.0.0.1") // Overridden by the config's LOCAL_IP
489
502
  properties.setProperty("buildType", "release")
490
- properties.setProperty("buildOptimisation", "true")
503
+ val optimise = (findProperty("buildOptimisation") as? String)?.toBoolean()
504
+ ?: webviewConfig.optJSONObject("android")?.optBoolean("buildOptimisation", false)
505
+ ?: false
506
+ properties.setProperty("buildOptimisation", optimise.toString())
491
507
  } else {
492
508
  properties.setProperty("LOCAL_IP", getLocalIpAddress()) // Debug
493
509
  properties.setProperty("buildType", "debug")
@@ -568,12 +584,6 @@ tasks.register("generateWebViewConfig") {
568
584
  properties.setProperty("splashScreen.enabled", "false")
569
585
  }
570
586
 
571
- // Set production-specific properties for release builds
572
- if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
573
- properties.setProperty("PRODUCTION_URL", "https://yourwebapp.com") // Replace with your domain
574
- properties.setProperty("apiBaseUrl", "https://api.yourdomain.com/") // Replace with your API URL
575
- }
576
-
577
587
  // Create the assets directory if it doesn't exist
578
588
  val assetsDir = File("${project.projectDir}/src/main/assets")
579
589
  if (!assetsDir.exists()) {
@@ -591,45 +601,9 @@ if (isNotificationsEnabled()) {
591
601
  apply(plugin = "com.google.gms.google-services")
592
602
  }
593
603
 
594
- // Task to create key store if it doesn't exist
595
- // Add this task to your build.gradle.kts
596
- tasks.register("generateKeystore") {
597
- doLast {
598
- val keystoreDir = File(project.rootDir, "keystore")
599
- if (!keystoreDir.exists()) {
600
- keystoreDir.mkdirs()
601
- }
602
-
603
- val keystoreFile = File(keystoreDir, "release-key.jks")
604
- if (!keystoreFile.exists()) {
605
- val storePass = project.properties["keystorePassword"] as? String ?: System.getenv("KEYSTORE_PASSWORD") ?: "android"
606
- val keyPass = project.properties["keyPassword"] as? String ?: System.getenv("KEY_PASSWORD") ?: "android"
607
- val alias = project.properties["keyAlias"] as? String ?: System.getenv("KEY_ALIAS") ?: "release"
608
-
609
- project.exec {
610
- commandLine = listOf(
611
- "keytool",
612
- "-genkey",
613
- "-v",
614
- "-keystore", keystoreFile.absolutePath,
615
- "-alias", alias,
616
- "-keyalg", "RSA",
617
- "-keysize", "2048",
618
- "-validity", "10000",
619
- "-storepass", storePass,
620
- "-keypass", keyPass,
621
- "-dname", "CN=YourCompany, OU=YourDepartment, O=YourOrganization, L=YourCity, ST=YourState, C=US"
622
- )
623
- }
624
- println("Generated keystore at: ${keystoreFile.absolutePath}")
625
- } else {
626
- println("Keystore already exists at: ${keystoreFile.absolutePath}")
627
- }
628
- }
629
- }
630
604
  // Task to build app bundle
631
605
  tasks.register("createAppBundle") {
632
- dependsOn("generateKeystore", "bundleRelease")
606
+ dependsOn("bundleRelease")
633
607
  doLast {
634
608
  println("""
635
609
  =======================================================
@@ -47,7 +47,7 @@
47
47
  </activity>
48
48
  <provider
49
49
  android:name="androidx.core.content.FileProvider"
50
- android:authorities="io.yourname.androidproject.fileprovider"
50
+ android:authorities="${applicationId}.fileprovider"
51
51
  android:exported="false"
52
52
  android:grantUriPermissions="true">
53
53
  <meta-data
@@ -90,7 +90,9 @@ object CatalystConstants {
90
90
 
91
91
  // MARK: - File Provider
92
92
  object FileProvider {
93
- const val AUTHORITY: String = "io.yourname.androidproject.fileprovider"
93
+ // Follows the application id (manifest: ${applicationId}.fileprovider), so two
94
+ // Catalyst apps, or an app's debug and release builds, can be installed together.
95
+ const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".fileprovider"
94
96
  }
95
97
 
96
98
  // MARK: - Logging Configuration