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,315 +1,98 @@
1
1
  "use strict";
2
2
 
3
- var _child_process = require("child_process");
4
3
  var _fs = _interopRequireDefault(require("fs"));
4
+ var _path = _interopRequireDefault(require("path"));
5
5
  var _utils = require("./utils.js");
6
+ var _iosToolchain = require("./iosToolchain.js");
7
+ var _localState = require("./localState.js");
6
8
  var _terminalProgress = _interopRequireDefault(require("./terminalProgress.js"));
9
+ var _diagnostic = require("../cli/diagnostic.js");
7
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
- const configPath = `${process.env.PWD}/config/config.json`;
9
- const {
10
- setupServer
11
- } = require("./setupServer.js");
12
- const ITEMS_PER_PAGE = 10;
11
+ // `catalyst setupEmulator:ios`: install what an iOS debug build needs on this
12
+ // Mac (Xcode's licence and first-launch components, a simulator runtime, the
13
+ // simulator) and nothing else. It reads ios.simulatorName from config.json
14
+ // but never writes config.json, and starts no server. buildApp:ios runs the
15
+ // same checks itself, so this is only for setting a machine up ahead of time.
16
+
13
17
  const steps = {
14
18
  platform: "Check platform compatibility",
15
- config: "Initialize configuration",
16
- simulator: "Configure iOS simulator",
17
- launch: "Launch iOS simulator",
18
- saveConfig: "Save configuration",
19
- setupServer: "Set up server"
19
+ xcode: "Check Xcode",
20
+ runtime: "Check iOS simulator runtime",
21
+ simulator: "Check iOS simulator"
20
22
  };
21
23
  const progress = new _terminalProgress.default(steps, "catalyst setupEmulator", {
22
24
  subject: "ios"
23
25
  });
24
- async function initializeConfig() {
25
- const configFile = _fs.default.readFileSync(configPath, "utf8");
26
- const config = JSON.parse(configFile);
27
- const {
28
- WEBVIEW_CONFIG
29
- } = config;
30
- if (!WEBVIEW_CONFIG || Object.keys(WEBVIEW_CONFIG).length === 0) {
31
- progress.log("WebView Config missing in " + configPath, "error");
32
- process.exit(1);
33
- }
34
- if (!WEBVIEW_CONFIG.ios) {
35
- WEBVIEW_CONFIG.ios = {};
36
- }
37
- return {
38
- WEBVIEW_CONFIG
39
- };
40
- }
41
- async function saveConfig(newConfig) {
42
- try {
43
- // Read the existing config
44
- const existingConfigFile = _fs.default.readFileSync(configPath, "utf8");
45
- const existingConfig = JSON.parse(existingConfigFile);
46
-
47
- // Merge the new WEBVIEW_CONFIG with existing config
48
- const updatedConfig = {
49
- ...existingConfig,
50
- // Preserve all existing keys
51
- WEBVIEW_CONFIG: {
52
- ...existingConfig.WEBVIEW_CONFIG,
53
- ios: newConfig.WEBVIEW_CONFIG.ios
54
- } // Update only WEBVIEW_CONFIG
55
- };
26
+ const log = (message, type = "info") => progress.log(message, type);
56
27
 
57
- // Save the merged config
58
- _fs.default.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2));
59
- progress.log("Configuration saved successfully", "success");
28
+ /** ios.simulatorName from config/config.json, when the app sets one. */
29
+ function configuredSimulatorName(cwd = process.cwd()) {
30
+ try {
31
+ // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal - cwd is the app root; the file name is fixed.
32
+ const config = JSON.parse(_fs.default.readFileSync(_path.default.join(cwd, "config", "config.json"), "utf8"));
33
+ const name = config?.WEBVIEW_CONFIG?.ios?.simulatorName;
34
+ return typeof name === "string" && name.trim() ? name.trim() : null;
60
35
  } catch (error) {
61
- progress.log("Failed to save configuration: " + error.message, "error");
62
- process.exit(1);
36
+ return null;
63
37
  }
64
38
  }
65
39
  async function setupIOSEnvironment() {
66
40
  try {
41
+ const flags = (0, _utils.parseNativeArgs)(process.argv.slice(2));
67
42
  progress.start("platform");
68
43
  if (process.platform !== "darwin") {
69
44
  progress.log("iOS Simulator is only available on macOS.", "error");
70
45
  process.exit(1);
71
46
  }
72
47
  progress.complete("platform");
73
- progress.start("config");
74
- const {
75
- WEBVIEW_CONFIG
76
- } = await initializeConfig();
77
- progress.complete("config");
48
+ progress.start("xcode");
49
+ const developerDir = await (0, _iosToolchain.ensureXcodeReady)({
50
+ flags,
51
+ log
52
+ });
53
+ progress.log(`Using ${developerDir}`, "info");
54
+ progress.complete("xcode");
55
+ progress.start("runtime");
56
+ const runtime = await (0, _iosToolchain.ensureIosRuntime)({
57
+ flags,
58
+ log
59
+ });
60
+ progress.log(`Runtime ${runtime.name}`, "info");
61
+ progress.complete("runtime");
78
62
  progress.start("simulator");
79
- await configureSimulator(WEBVIEW_CONFIG);
63
+ const simulator = await (0, _iosToolchain.ensureSimulator)({
64
+ runtime,
65
+ name: configuredSimulatorName() || undefined,
66
+ last: (0, _localState.getLastTarget)("ios"),
67
+ flags,
68
+ log
69
+ });
70
+ if (!simulator.created) progress.log(`Simulator "${simulator.name}" is ready`, "info");
80
71
  progress.complete("simulator");
81
- progress.start("launch");
82
- await launchIOSSimulator(WEBVIEW_CONFIG.ios.simulatorName);
83
- progress.complete("launch");
84
- progress.start("saveConfig");
85
- const config = await (0, _utils.validateAndCompleteConfig)("ios", configPath);
86
- progress.complete("saveConfig");
87
- progress.start("setupServer");
88
- await setupServer(configPath, message => progress.log(message, "info"));
89
- progress.complete("setupServer");
90
- const iosCfg = config.ios || {};
91
- progress.printTreeContent("Config config/config.json", [{
92
- text: `port ${config.port ?? ""}`,
72
+ progress.printTreeContent("iOS", [{
73
+ text: `xcode ${developerDir}`,
93
74
  color: "gray"
94
75
  }, {
95
- text: `appBundleId ${iosCfg.appBundleId ?? ""}`,
76
+ text: `runtime ${runtime.name}`,
96
77
  color: "gray"
97
78
  }, {
98
- text: `simulatorName ${iosCfg.simulatorName ?? ""}`,
79
+ text: `simulator ${simulator.name}`,
99
80
  color: "gray"
100
81
  }]);
101
- progress.summary("Ready", "Run catalyst buildApp:ios to install the app");
82
+ progress.summary("Ready", "Run catalyst buildApp:ios to build and install the app");
102
83
  process.exit(0);
103
84
  } catch (error) {
85
+ // A coded error (IOS-003, NATIVE-005, ...) gets the full diagnostic:
86
+ // code, hint and doc link. Anything else keeps the one-line failure.
87
+ const coded = /^[A-Z]+-\d{3}$/.test(String(error?.code || ""));
104
88
  if (progress.currentStep) {
105
- progress.fail(progress.currentStep.id, error.message);
106
- }
107
- progress.hint(error.hint);
108
- process.exit(1);
109
- }
110
- }
111
- async function displayPaginatedList(items, startIndex) {
112
- const endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.length);
113
- for (let i = startIndex; i < endIndex; i++) {
114
- console.log(`${i + 1}. ${items[i]}`);
115
- }
116
- if (endIndex < items.length) {
117
- console.log(`\nShowing ${startIndex + 1}-${endIndex} of ${items.length} items`);
118
- console.log('Type "more" to see more items, or enter your selection: ');
119
- return true;
120
- }
121
- return false;
122
- }
123
- async function getRuntime() {
124
- const runtimesOutput = (0, _child_process.execSync)("xcrun simctl list runtimes -j").toString();
125
- const parsedRuntimes = JSON.parse(runtimesOutput).runtimes;
126
- const availableRuntime = parsedRuntimes.find(runtime => runtime.isAvailable || runtime.availability === "(available)" || runtime.availability === "available");
127
- if (!availableRuntime) {
128
- const runningSimulators = (0, _child_process.execSync)("xcrun simctl list devices booted").toString();
129
- if (runningSimulators.includes("(Booted)") && parsedRuntimes.length > 0) {
130
- progress.status(`runtime ${parsedRuntimes[0].name}`);
131
- return parsedRuntimes[0];
89
+ progress.fail(progress.currentStep.id, coded ? undefined : error.message);
132
90
  }
133
- progress.log("No available iOS runtime found. Install one through Xcode.", "error");
134
- process.exit(1);
135
- }
136
- progress.status(`runtime ${availableRuntime.name}`);
137
- return availableRuntime;
138
- }
139
- async function configureSimulator(config) {
140
- // If a simulator is already configured, skip the interactive picker
141
- if (config.ios.simulatorName) {
142
- progress.log(`Using configured simulator: ${config.ios.simulatorName}`);
143
- return;
144
- }
145
- progress.pause();
146
-
147
- // Get the current runtime first
148
- const runtime = await getRuntime();
149
-
150
- // Get simulators specifically for this runtime
151
- const simulatorsJson = JSON.parse((0, _child_process.execSync)("xcrun simctl list devices -j").toString());
152
- const runtimeSimulators = simulatorsJson.devices[runtime.identifier] || [];
153
-
154
- // Filter for available simulators only
155
- const availableSimulators = runtimeSimulators.filter(sim => sim.availability === "(available)" || sim.isAvailable).map(sim => `${sim.name} (${sim.udid})`);
156
- if (availableSimulators.length === 0) {
157
- progress.log(`No available simulators found for runtime: ${runtime.name}`, "error");
158
- progress.resume();
91
+ if (coded) process.stderr.write((0, _diagnostic.buildFailure)({
92
+ error,
93
+ stage: "iOS setup"
94
+ }));else progress.hint(error.hint);
159
95
  process.exit(1);
160
- return;
161
96
  }
162
- let startIndex = 0;
163
- console.log(`\nAvailable simulators for ${runtime.name} (or type 'new' to create a new one):`);
164
-
165
- // eslint-disable-next-line no-constant-condition
166
- while (true) {
167
- const hasMore = await displayPaginatedList(availableSimulators, startIndex);
168
- const simulatorChoice = await (0, _utils.promptUser)(hasMore ? "" : "\nEnter selection: ");
169
- if (simulatorChoice.toLowerCase() === "more" && hasMore) {
170
- startIndex += ITEMS_PER_PAGE;
171
- continue;
172
- }
173
- if (simulatorChoice.toLowerCase() === "new") {
174
- const deviceTypes = JSON.parse((0, _child_process.execSync)("xcrun simctl list devicetypes --json").toString()).devicetypes;
175
- const simulatorName = await (0, _utils.promptUser)("Enter a name for the new simulator: ");
176
-
177
- // Use iPhone 15 Pro as default device type
178
- const defaultDevice = deviceTypes.find(dt => dt.name.includes("iPhone 15 Pro")) || deviceTypes[0];
179
- try {
180
- (0, _utils.runCommand)(`xcrun simctl create "${simulatorName}" "${defaultDevice.identifier}" "${runtime.identifier}"`);
181
- console.log(`Simulator "${simulatorName}" created successfully.`);
182
- config.ios.simulatorName = simulatorName;
183
- } catch (error) {
184
- console.log(`Failed to create simulator "${simulatorName}". Error: ${error.message}`);
185
- process.exit(1);
186
- }
187
- break;
188
- } else {
189
- const index = parseInt(simulatorChoice) - 1;
190
- if (index >= 0 && index < availableSimulators.length) {
191
- const selectedSimulator = availableSimulators[index];
192
- // Extract only the simulator name without ID and state
193
- const simulatorName = selectedSimulator.split(" (")[0];
194
- config.ios.simulatorName = simulatorName;
195
- break;
196
- } else {
197
- console.log("Invalid selection. Please try again.");
198
- }
199
- }
200
- }
201
- progress.resume();
202
- await saveConfig({
203
- WEBVIEW_CONFIG: config
204
- });
205
- }
206
-
207
- // async function createDefaultSimulator(config, runtime) {
208
- // const deviceTypes = JSON.parse(execSync("xcrun simctl list devicetypes --json").toString()).devicetypes
209
- // const defaultDevice = deviceTypes.find((dt) => dt.name.includes("iPhone 15 Pro")) || deviceTypes[0]
210
- // const simulatorName = `Default ${defaultDevice.name.split(" (")[0]}`
211
-
212
- // try {
213
- // runCommand(
214
- // `xcrun simctl create "${simulatorName}" "${defaultDevice.identifier}" "${runtime.identifier}"`
215
- // )
216
- // console.log(`Default simulator "${simulatorName}" created successfully.`)
217
- // config.ios.simulatorName = simulatorName
218
- // await saveConfig({ WEBVIEW_CONFIG: config })
219
- // } catch (error) {
220
- // console.log(`Failed to create default simulator. Error: ${error.message}`)
221
- // process.exit(1)
222
- // }
223
- // }
224
-
225
- // Xcode 27 replaced the standalone Simulator.app (com.apple.iphonesimulator)
226
- // with DeviceHub.app (com.apple.dt.Devices); older Xcode installs still ship
227
- // the former. Resolve whichever is actually installed instead of hardcoding
228
- // the app name, which no longer resolves via `open -a` on either app.
229
- function resolveSimulatorAppBundleId() {
230
- const candidates = ["com.apple.dt.Devices", "com.apple.iphonesimulator"];
231
- for (const bundleId of candidates) {
232
- try {
233
- (0, _child_process.execSync)(`osascript -e 'id of app id "${bundleId}"'`, {
234
- stdio: "ignore"
235
- });
236
- return bundleId;
237
- } catch {
238
- // Not installed under this bundle id; try the next candidate.
239
- }
240
- }
241
- return null;
242
- }
243
-
244
- // Text mirrors errors/registry.js ERROR_DEFINITIONS[IOS-001] (see
245
- // errors/IOS/IOS-001.md) — this CJS subtree can't import the ESM error
246
- // registry (see native/buildErrorFormat.js), so the message is duplicated
247
- // here rather than imported. This condition is recoverable (the simulator
248
- // can still boot headlessly via simctl) so callers log it as a warning
249
- // instead of throwing.
250
- const SIMULATOR_APP_NOT_FOUND_WARNING = "[IOS-001] Could not find an installed iOS Simulator app (checked DeviceHub.app and the legacy Simulator.app). " + "The simulator is booted but its window may not be visible. Suggested action: confirm a full Xcode.app is installed and selected via xcode-select -p, then open it once from Spotlight.";
251
- async function launchIOSSimulator(simulatorName) {
252
- progress.status("launching simulator");
253
- try {
254
- // Get all simulators including both available and booted ones
255
- const allSimulatorInfo = (0, _child_process.execSync)("xcrun simctl list devices -j").toString();
256
- const simulatorsJson = JSON.parse(allSimulatorInfo);
257
- const runtimesOutput = (0, _child_process.execSync)("xcrun simctl list runtimes -j").toString();
258
- const availableRuntimeIds = new Set(JSON.parse(runtimesOutput).runtimes.filter(r => r.isAvailable || r.availability === "(available)" || r.availability === "available").map(r => r.identifier));
259
-
260
- // Search through all runtimes and their devices
261
- let foundSimulator = null;
262
- let foundSimulatorId = null;
263
- let isBooted = false;
264
-
265
- // Prefer devices under an available runtime; fall back to already-booted ones
266
- Object.entries(simulatorsJson.devices).forEach(([runtimeId, devices]) => {
267
- const isAvailableRuntime = availableRuntimeIds.has(runtimeId);
268
- devices.forEach(device => {
269
- if (device.name === simulatorName) {
270
- const alreadyBooted = device.state === "Booted";
271
- // Take this match if: no match yet, or this runtime is available and current isn't, or device is already booted
272
- if (!foundSimulator || isAvailableRuntime && !foundSimulator._fromAvailableRuntime || alreadyBooted) {
273
- foundSimulator = {
274
- ...device,
275
- _fromAvailableRuntime: isAvailableRuntime
276
- };
277
- foundSimulatorId = device.udid;
278
- isBooted = alreadyBooted;
279
- }
280
- }
281
- });
282
- });
283
- if (!foundSimulator) {
284
- progress.log(`Configured simulator "${simulatorName}" not found`, "warning");
285
- return;
286
- }
287
- if (!isBooted) {
288
- progress.status(`booting ${simulatorName}`);
289
- (0, _utils.runCommand)(`xcrun simctl boot ${foundSimulatorId}`);
290
- } else {
291
- progress.status("already booted");
292
- }
293
-
294
- // Open the simulator UI (DeviceHub on Xcode 27+, Simulator.app on older Xcode) and focus it
295
- const simulatorAppBundleId = resolveSimulatorAppBundleId();
296
- if (simulatorAppBundleId) {
297
- progress.status("opening simulator");
298
- (0, _utils.runCommand)(`open -b ${simulatorAppBundleId}`);
299
-
300
- // Give the simulator a moment to open/focus
301
- await new Promise(resolve => setTimeout(resolve, 1000));
302
-
303
- // Activate the simulator UI window to bring it to front
304
- (0, _utils.runCommand)(`osascript -e 'tell application id "${simulatorAppBundleId}" to activate'`);
305
- } else {
306
- progress.log(SIMULATOR_APP_NOT_FOUND_WARNING, "warning");
307
- }
308
- } catch (error) {
309
- progress.log(`Failed to launch iOS Simulator: ${error.message}`, "error");
310
- }
311
- // process.exit(0);
312
97
  }
313
-
314
- // Execute the main setup
315
98
  setupIOSEnvironment();