catalyst-core-internal 0.1.2 → 0.1.4

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 (53) hide show
  1. package/README.md +4 -4
  2. package/bin/catalyst.js +8 -1
  3. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +3 -11
  4. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +12 -1
  5. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +18 -3
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +7 -0
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +253 -0
  9. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/SecurityBridgeTest.kt +199 -0
  10. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +139 -0
  11. package/dist/native/bridge/hooks.js +4 -4
  12. package/dist/native/bridge/useBaseHook.js +5 -4
  13. package/dist/native/bridge/utils/NativeBridge.js +4 -4
  14. package/dist/native/buildAppAndroid.js +2 -2
  15. package/dist/native/buildAppIos.js +10 -17
  16. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  17. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  18. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  19. package/dist/native/internalPluginUtils.js +1 -0
  20. package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
  21. package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
  22. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
  23. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +13 -2
  24. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +13 -2
  25. package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
  26. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +6 -0
  27. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +4 -0
  28. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +36 -0
  29. package/dist/native/iosnativeWebView/iosnativeWebView.xctestplan +1 -0
  30. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift +212 -0
  31. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FrameworkServerUtilsTests.swift +14 -4
  32. package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
  33. package/dist/native/iosnativeWebView/iosnativeWebViewTests/ScreenSecureManagerTests.swift +121 -0
  34. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +9 -21
  35. package/dist/native/plugin-bridge/PluginBridge.js +1 -0
  36. package/dist/native/pluginComposerAndroid.js +9 -0
  37. package/dist/native/pluginComposerIos.js +7 -0
  38. package/dist/scripts/plugins.js +1 -0
  39. package/package.json +3 -2
  40. package/mcp_v2/conversion-tasks.json +0 -371
  41. package/mcp_v2/knowledge-base.json +0 -1450
  42. package/mcp_v2/lib/helpers.js +0 -145
  43. package/mcp_v2/mcp.js +0 -366
  44. package/mcp_v2/package.json +0 -13
  45. package/mcp_v2/schema.sql +0 -88
  46. package/mcp_v2/setup.js +0 -262
  47. package/mcp_v2/tools/build.js +0 -449
  48. package/mcp_v2/tools/config.js +0 -262
  49. package/mcp_v2/tools/conversion.js +0 -492
  50. package/mcp_v2/tools/debug.js +0 -62
  51. package/mcp_v2/tools/knowledge.js +0 -213
  52. package/mcp_v2/tools/sync.js +0 -21
  53. package/mcp_v2/tools/tasks.js +0 -844
@@ -1,262 +0,0 @@
1
- 'use strict';
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- let _projectInfo;
6
-
7
- function init(projectInfo) {
8
- _projectInfo = projectInfo;
9
- }
10
-
11
- function handle_check_config({ project_path, platform = 'both' } = {}) {
12
- const root = project_path || _projectInfo.dir;
13
- const configPath = path.join(root, 'config', 'config.json');
14
- const issues = [];
15
- const warnings = [];
16
- const passed = [];
17
-
18
- if (!fs.existsSync(configPath)) {
19
- return {
20
- project_path: root, platform, config_found: false, config_path: configPath,
21
- issues: [{ field: 'config/config.json', severity: 'error', message: 'config/config.json not found. catalyst-core requires this file at project root/config/config.json.' }],
22
- warnings: [], passed: [],
23
- summary: { errors: 1, warnings: 0, passed: 0, valid: false },
24
- };
25
- }
26
-
27
- let config;
28
- try {
29
- config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
30
- } catch (e) {
31
- return {
32
- project_path: root, platform, config_found: true, config_path: configPath,
33
- issues: [{ field: 'config/config.json', severity: 'error', message: `config/config.json is not valid JSON: ${e.message}` }],
34
- warnings: [], passed: [],
35
- summary: { errors: 1, warnings: 0, passed: 0, valid: false },
36
- };
37
- }
38
-
39
- // ── Top-level required fields ──────────────────────────────────────────────
40
- if (config.NODE_SERVER_PORT === undefined) {
41
- issues.push({ field: 'NODE_SERVER_PORT', severity: 'error', message: 'NODE_SERVER_PORT is required. Set to the port your web server runs on (e.g. 3000).' });
42
- } else if (typeof config.NODE_SERVER_PORT !== 'number') {
43
- issues.push({ field: 'NODE_SERVER_PORT', severity: 'error', message: `NODE_SERVER_PORT must be a number, got ${typeof config.NODE_SERVER_PORT}.` });
44
- } else {
45
- passed.push({ field: 'NODE_SERVER_PORT', value: config.NODE_SERVER_PORT });
46
- }
47
-
48
- if (!config.API_URL) {
49
- issues.push({ field: 'API_URL', severity: 'error', message: 'API_URL is required. Set to your backend API base URL.' });
50
- } else if (typeof config.API_URL !== 'string') {
51
- issues.push({ field: 'API_URL', severity: 'error', message: `API_URL must be a string, got ${typeof config.API_URL}.` });
52
- } else {
53
- passed.push({ field: 'API_URL', value: config.API_URL });
54
- }
55
-
56
- if (!config.WEBVIEW_CONFIG) {
57
- issues.push({ field: 'WEBVIEW_CONFIG', severity: 'error', message: 'WEBVIEW_CONFIG object is required. Missing this will prevent native builds.' });
58
- return _buildResult({ root, configPath, platform, config, issues, warnings, passed });
59
- } else {
60
- passed.push({ field: 'WEBVIEW_CONFIG', note: 'block present' });
61
- }
62
-
63
- const wc = config.WEBVIEW_CONFIG;
64
-
65
- // ── WEBVIEW_CONFIG.LOCAL_IP ───────────────────────────────────────────────
66
- if (!wc.LOCAL_IP) {
67
- issues.push({ field: 'WEBVIEW_CONFIG.LOCAL_IP', severity: 'error', message: 'WEBVIEW_CONFIG.LOCAL_IP is required. Set to your machine LAN IP (e.g. "192.168.0.11"). Do NOT use "localhost" — the native emulator/simulator cannot resolve localhost and will fail to connect to the dev server.' });
68
- } else if (wc.LOCAL_IP === 'localhost' || wc.LOCAL_IP === '127.0.0.1') {
69
- issues.push({ field: 'WEBVIEW_CONFIG.LOCAL_IP', severity: 'error', message: `WEBVIEW_CONFIG.LOCAL_IP is set to "${wc.LOCAL_IP}" — this will not work. The native emulator runs in a separate network namespace and cannot resolve localhost. Use your machine LAN IP (run "ifconfig | grep inet" to find it).` });
70
- } else {
71
- passed.push({ field: 'WEBVIEW_CONFIG.LOCAL_IP', value: wc.LOCAL_IP });
72
- }
73
-
74
- // ── WEBVIEW_CONFIG.appInfo ────────────────────────────────────────────────
75
- if (!wc.appInfo) {
76
- issues.push({ field: 'WEBVIEW_CONFIG.appInfo', severity: 'error', message: 'WEBVIEW_CONFIG.appInfo is required. Missing appInfo breaks iOS build at compile time. Set to a build identifier string e.g. "android-5Feb2026-v2.1.0". Place at top level of WEBVIEW_CONFIG (not inside android/ios blocks).' });
77
- } else {
78
- passed.push({ field: 'WEBVIEW_CONFIG.appInfo', value: wc.appInfo });
79
- }
80
-
81
- // ── WEBVIEW_CONFIG.port ────────────────────────────────────────────────────
82
- if (wc.port === undefined) {
83
- issues.push({ field: 'WEBVIEW_CONFIG.port', severity: 'error', message: 'WEBVIEW_CONFIG.port is required. Must match NODE_SERVER_PORT (e.g. 3000). The native WebView connects to this port.' });
84
- } else if (typeof wc.port !== 'number') {
85
- issues.push({ field: 'WEBVIEW_CONFIG.port', severity: 'error', message: `WEBVIEW_CONFIG.port must be a number, got ${typeof wc.port}.` });
86
- } else {
87
- if (config.NODE_SERVER_PORT !== undefined && wc.port !== config.NODE_SERVER_PORT) {
88
- warnings.push({ field: 'WEBVIEW_CONFIG.port', message: `WEBVIEW_CONFIG.port (${wc.port}) does not match NODE_SERVER_PORT (${config.NODE_SERVER_PORT}). WebView may connect to wrong port.` });
89
- } else {
90
- passed.push({ field: 'WEBVIEW_CONFIG.port', value: wc.port });
91
- }
92
- }
93
-
94
- // ── accessControl ──────────────────────────────────────────────────────────
95
- if (!wc.accessControl) {
96
- issues.push({ field: 'WEBVIEW_CONFIG.accessControl', severity: 'error', message: 'accessControl block missing. Without it, WebView navigation is unconstrained — deep links can take users outside the app.' });
97
- } else {
98
- if (!wc.accessControl.enabled) {
99
- warnings.push({ field: 'WEBVIEW_CONFIG.accessControl.enabled', message: 'accessControl.enabled is not true. URL allowlist is ignored — all navigation is permitted.' });
100
- } else {
101
- passed.push({ field: 'WEBVIEW_CONFIG.accessControl.enabled', value: true });
102
- }
103
- if (!Array.isArray(wc.accessControl.allowedUrls) || wc.accessControl.allowedUrls.length === 0) {
104
- issues.push({ field: 'WEBVIEW_CONFIG.accessControl.allowedUrls', severity: 'error', message: 'allowedUrls is empty. When accessControl is enabled and allowedUrls is empty, ALL URLs are blocked — including your own API calls. App will hang on every network request.' });
105
- } else {
106
- const hasLocalhost = wc.accessControl.allowedUrls.some(u => u.includes('localhost'));
107
- if (!hasLocalhost) {
108
- warnings.push({ field: 'WEBVIEW_CONFIG.accessControl.allowedUrls', message: 'No localhost entry in allowedUrls. If your app uses the localhost server transport (file downloads, progress tracking), add "http://localhost:*" to allowedUrls.' });
109
- } else {
110
- passed.push({ field: 'WEBVIEW_CONFIG.accessControl.allowedUrls', value: `${wc.accessControl.allowedUrls.length} entries (localhost included)` });
111
- }
112
- if (!hasLocalhost) {
113
- passed.push({ field: 'WEBVIEW_CONFIG.accessControl.allowedUrls', value: `${wc.accessControl.allowedUrls.length} entries` });
114
- }
115
- }
116
- }
117
-
118
- // ── Android fields ─────────────────────────────────────────────────────────
119
- if (platform === 'android' || platform === 'both') {
120
- if (!wc.android) {
121
- issues.push({ field: 'WEBVIEW_CONFIG.android', severity: 'error', message: 'WEBVIEW_CONFIG.android block missing. Android build cannot start without it.' });
122
- } else {
123
- const a = wc.android;
124
- // sdkPath and emulatorName are hard required — build exits immediately without them
125
- if (!a.sdkPath) {
126
- issues.push({ field: 'WEBVIEW_CONFIG.android.sdkPath', severity: 'error', message: 'android.sdkPath is required. Set to absolute path of Android SDK (e.g. /Users/you/Android/). Build exits immediately without it.' });
127
- } else if (!fs.existsSync(a.sdkPath)) {
128
- issues.push({ field: 'WEBVIEW_CONFIG.android.sdkPath', severity: 'error', message: `android.sdkPath "${a.sdkPath}" does not exist on this machine. Android build will fail immediately.` });
129
- } else {
130
- passed.push({ field: 'WEBVIEW_CONFIG.android.sdkPath', value: a.sdkPath });
131
- }
132
- if (!a.emulatorName) {
133
- issues.push({ field: 'WEBVIEW_CONFIG.android.emulatorName', severity: 'error', message: 'android.emulatorName is required for debug builds. Set to AVD name (run: emulator -list-avds). Build fails if emulator not found.' });
134
- } else {
135
- passed.push({ field: 'WEBVIEW_CONFIG.android.emulatorName', value: a.emulatorName });
136
- }
137
- // appName and packageName — optional but strongly recommended
138
- if (!a.appName) {
139
- warnings.push({ field: 'WEBVIEW_CONFIG.android.appName', message: 'android.appName not set. App will show "Catalyst Application" as display name on device.' });
140
- } else {
141
- passed.push({ field: 'WEBVIEW_CONFIG.android.appName', value: a.appName });
142
- }
143
- if (!a.packageName) {
144
- warnings.push({ field: 'WEBVIEW_CONFIG.android.packageName', message: 'android.packageName not set (e.g. com.company.app). Will be derived from appName — may produce unexpected package ID.' });
145
- } else {
146
- passed.push({ field: 'WEBVIEW_CONFIG.android.packageName', value: a.packageName });
147
- }
148
- // buildType
149
- if (a.buildType && !['debug', 'release'].includes(a.buildType)) {
150
- issues.push({ field: 'WEBVIEW_CONFIG.android.buildType', severity: 'error', message: `android.buildType must be "debug" or "release", got "${a.buildType}".` });
151
- } else {
152
- passed.push({ field: 'WEBVIEW_CONFIG.android.buildType', value: a.buildType || 'debug (default)' });
153
- }
154
- // release keystore check
155
- if (a.buildType === 'release' && !a.keystore && !a.keystoreConfig) {
156
- issues.push({ field: 'WEBVIEW_CONFIG.android.keystore', severity: 'error', message: 'android.buildType is "release" but no keystore or keystoreConfig provided. Release build cannot sign the APK.' });
157
- }
158
- if (a.cachePattern !== undefined && typeof a.cachePattern !== 'string') {
159
- warnings.push({ field: 'WEBVIEW_CONFIG.android.cachePattern', message: `android.cachePattern should be a comma-separated glob string (e.g. "*.css,*.js,*.png"), got ${typeof a.cachePattern}.` });
160
- } else if (a.cachePattern) {
161
- passed.push({ field: 'WEBVIEW_CONFIG.android.cachePattern', value: a.cachePattern });
162
- }
163
- if (a.security && typeof a.security.allowBackup !== 'boolean') {
164
- warnings.push({ field: 'WEBVIEW_CONFIG.android.security.allowBackup', message: 'android.security.allowBackup should be a boolean. Controls Google cloud backup of app data.' });
165
- }
166
- }
167
- }
168
-
169
- // ── iOS fields ─────────────────────────────────────────────────────────────
170
- if (platform === 'ios' || platform === 'both') {
171
- if (!wc.ios) {
172
- issues.push({ field: 'WEBVIEW_CONFIG.ios', severity: 'error', message: 'WEBVIEW_CONFIG.ios block missing. iOS build cannot start without it.' });
173
- } else {
174
- const ios = wc.ios;
175
- // appBundleId — optional but strongly recommended (defaults to com.debug.webview)
176
- if (!ios.appBundleId) {
177
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.appBundleId', message: 'ios.appBundleId not set. App will use "com.debug.webview" as bundle ID — fine for testing, must be set for distribution.' });
178
- } else {
179
- if (!/^[a-z0-9][a-z0-9]*(\.[a-z0-9][a-z0-9]*){1,}$/i.test(ios.appBundleId)) {
180
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.appBundleId', message: `appBundleId "${ios.appBundleId}" may not be valid — expected reverse-DNS format like "com.company.app".` });
181
- } else {
182
- passed.push({ field: 'WEBVIEW_CONFIG.ios.appBundleId', value: ios.appBundleId });
183
- }
184
- }
185
- // simulatorName — optional, auto-detected if missing
186
- if (!ios.simulatorName) {
187
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.simulatorName', message: 'ios.simulatorName not set. Will use auto-detected booted simulator. Recommend setting explicitly (e.g. "iPhone 17 Pro"). Run: xcrun simctl list devices.' });
188
- } else {
189
- passed.push({ field: 'WEBVIEW_CONFIG.ios.simulatorName', value: ios.simulatorName });
190
- }
191
- // appName — optional, defaults to "Catalyst Application"
192
- if (!ios.appName) {
193
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.appName', message: 'ios.appName not set. App will show "Catalyst Application" as display name on device.' });
194
- } else {
195
- passed.push({ field: 'WEBVIEW_CONFIG.ios.appName', value: ios.appName });
196
- }
197
- // buildType — case-sensitive (Debug/Release not debug/release)
198
- if (ios.buildType && !['Debug', 'Release'].includes(ios.buildType)) {
199
- issues.push({ field: 'WEBVIEW_CONFIG.ios.buildType', severity: 'error', message: `ios.buildType must be "Debug" or "Release" (case-sensitive). Got: "${ios.buildType}". Common mistake: using lowercase "debug" breaks Xcode build.` });
200
- } else {
201
- passed.push({ field: 'WEBVIEW_CONFIG.ios.buildType', value: ios.buildType || 'Debug (default)' });
202
- }
203
- // physical device fields
204
- if (ios.deviceUDID && !ios.developmentTeam) {
205
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.developmentTeam', message: 'ios.deviceUDID is set (physical device build) but ios.developmentTeam is missing. Physical device builds require Apple Developer Team ID for code signing.' });
206
- }
207
- if (ios.cachePattern !== undefined && typeof ios.cachePattern !== 'string') {
208
- warnings.push({ field: 'WEBVIEW_CONFIG.ios.cachePattern', message: `ios.cachePattern should be a comma-separated glob string, got ${typeof ios.cachePattern}.` });
209
- } else if (ios.cachePattern) {
210
- passed.push({ field: 'WEBVIEW_CONFIG.ios.cachePattern', value: ios.cachePattern });
211
- }
212
- }
213
- }
214
-
215
- // ── splashScreen (top-level, not inside WEBVIEW_CONFIG) ────────────────────
216
- if (!config.splashScreen) {
217
- warnings.push({ field: 'splashScreen', message: 'splashScreen key missing from top level of config/config.json. Native app will show a blank white screen during JS load. Add splashScreen: { android: { path: "public/android/splashscreen.png" }, ios: { path: "public/ios/splashscreen.png" } }.' });
218
- } else {
219
- const missingSplash = ['public/android/splashscreen.png', 'public/ios/splashscreen.png'].filter(f => !fs.existsSync(path.join(root, f)));
220
- if (missingSplash.length) {
221
- issues.push({ field: 'splashScreen', severity: 'error', message: `splashScreen configured but asset files missing: ${missingSplash.join(', ')}. Native build will fail when packaging.` });
222
- } else {
223
- passed.push({ field: 'splashScreen', note: 'config and both asset files present' });
224
- }
225
- }
226
-
227
- // ── notifications (optional, validate if present) ─────────────────────────
228
- if (wc.notifications && wc.notifications.enabled) {
229
- const missingFirebase = ['google-services.json', 'GoogleService-Info.plist'].filter(f => !fs.existsSync(path.join(root, f)));
230
- if (missingFirebase.length) {
231
- issues.push({ field: 'WEBVIEW_CONFIG.notifications', severity: 'error', message: `notifications.enabled=true but Firebase config files missing: ${missingFirebase.join(', ')}. Native build will fail at compile time.` });
232
- } else {
233
- passed.push({ field: 'WEBVIEW_CONFIG.notifications', note: 'enabled=true and Firebase files present' });
234
- }
235
- }
236
-
237
- return _buildResult({ root, configPath, platform, config, issues, warnings, passed });
238
- }
239
-
240
- function _buildResult({ root, configPath, platform, config, issues, warnings, passed }) {
241
- const errorCount = issues.filter(i => i.severity === 'error').length;
242
- return {
243
- project_path: root,
244
- platform,
245
- config_found: true,
246
- config_path: configPath,
247
- issues,
248
- warnings,
249
- passed,
250
- summary: {
251
- errors: errorCount,
252
- warnings: warnings.length,
253
- passed: passed.length,
254
- valid: errorCount === 0,
255
- verdict: errorCount === 0
256
- ? (warnings.length === 0 ? 'Config is valid.' : `Config is valid with ${warnings.length} warning(s).`)
257
- : `Config has ${errorCount} error(s) that will cause build or runtime failures.`,
258
- },
259
- };
260
- }
261
-
262
- module.exports = { init, handle_check_config };